added opensimpad-2.4.27-vrs1-pxa1-jpm1 kernel. This contains the
authorFrederic Bompart <frederic@unknown.openembedded.org>
Mon, 19 Sep 2005 15:28:24 +0000 (15:28 +0000)
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>
Mon, 19 Sep 2005 15:28:24 +0000 (15:28 +0000)
following new patches over 2.4.25:
- simpad-proc-sys-board.patch (/proc/sys/board patch from Till Harbaum)
- simpad-serial.patch (DTR/RTS/CTS support patch from Till Harbaum)
- mppe-20040216.patch (add MPPE encryption for PPP)
- 2.4.27-mh1.patch (bluetooth patches from Marcel Holtmann)
Common patches were placed in the "opensimpad" directory.

33 files changed:
packages/linux/files/mipv6-1.1-v2.4.27.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/disable-pcmcia-probe.patch [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/mkdep.patch [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-backlight-if.diff [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input.diff [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input2.diff [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-ts-noninput.diff [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/sound-volume-reversed.patch [deleted file]
packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/support-128mb-flash.patch [deleted file]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/.mtn2git_empty [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-mh1.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1-jpm1.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/defconfig-simpad [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-apm.patch [new file with mode: 0644]
packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-pm-updates.patch [new file with mode: 0644]
packages/linux/opensimpad-64+0_2.4.27-vrs1-pxa1-jpm1.bb [new file with mode: 0644]
packages/linux/opensimpad/.mtn2git_empty [new file with mode: 0644]
packages/linux/opensimpad/disable-pcmcia-probe.patch [new file with mode: 0644]
packages/linux/opensimpad/mkdep.patch [new file with mode: 0644]
packages/linux/opensimpad/mppe-20040216.patch [new file with mode: 0644]
packages/linux/opensimpad/sa1100-usb-tcl1.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-backlight-if.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-proc-sys-board.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-serial.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-switches-input.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-switches-input2.patch [new file with mode: 0644]
packages/linux/opensimpad/simpad-ts-noninput.patch [new file with mode: 0644]
packages/linux/opensimpad/sound-volume-reversed.patch [new file with mode: 0644]
packages/linux/opensimpad/support-128mb-flash.patch [new file with mode: 0644]
packages/linux/opensimpad_2.4.25-vrs2-pxa1-jpm1.bb
packages/linux/opensimpad_2.4.27-vrs1-pxa1-jpm1.bb [new file with mode: 0644]

diff --git a/packages/linux/files/mipv6-1.1-v2.4.27.patch b/packages/linux/files/mipv6-1.1-v2.4.27.patch
new file mode 100644 (file)
index 0000000..b8fb071
--- /dev/null
@@ -0,0 +1,19736 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/Documentation/Configure.help~mipv6-1.1-v2.4.26
++++ linux-2.4.27/Documentation/Configure.help
+@@ -6308,6 +6308,57 @@
+   It is safe to say N here for now.
++IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)
++CONFIG_IPV6_TUNNEL
++  Experimental IP6-IP6 tunneling.  You must select this, if you want
++  to use CONFIG_IPV6_MOBILITY.  More information in MIPL Mobile IPv6
++  instructions.
++
++  If you don't want IP6-IP6 tunnels and Mobile IPv6, say N.
++
++IPv6: Mobility Support (EXPERIMENTAL)
++CONFIG_IPV6_MOBILITY
++  This is experimental support for the upcoming specification of
++  Mobile IPv6. Mobile IPv6 allows nodes to seamlessly move between
++  networks without changing their IP addresses, thus allowing them to
++  maintain upper layer connections (e.g. TCP).  Selecting this option
++  allows your computer to act as a Correspondent Node (CN).  A MIPv6
++  Mobile Node will be able to communicate with the CN and use route
++  optimization.
++
++  For more information and configuration details, see
++  http://www.mipl.mediapoli.com/.
++
++  If unsure, say N.
++
++MIPv6: Mobile Node Support
++CONFIG_IPV6_MOBILITY_MN
++  If you want your computer to be a MIPv6 Mobile Node (MN), select
++  this option.  You must configure MN using the userspace tools
++  available at http://www.mipl.mediapoli.com/download/mipv6-tools/.
++
++  If your computer is stationary, or you are unsure if you need this,
++  say N.  Note that you will need a properly configured MIPv6 Home
++  Agent to use any Mobile Nodes.
++
++MIPv6: Home Agent Support
++CONFIG_IPV6_MOBILITY_HA
++  If you want your router to serve as a MIPv6 Home Agent (HA), select
++  this option.  You must configure HA using the userspace tools
++  available at http://www.mipl.mediapoli.com/download/mipv6-tools/.
++
++  If your computer is not a router, or you are unsure if you need
++  this, say N.
++
++MIPv6: Debug messages
++CONFIG_IPV6_MOBILITY_DEBUG
++  MIPL Mobile IPv6 can produce a lot of debugging messages. There are
++  eight debug levels (0 through 7) and the level is controlled via
++  /proc/sys/net/ipv6/mobility/debuglevel. Since MIPL is still
++  experimental, you might want to say Y here.
++
++  Be sure to say Y and record debug messages when submitting a bug
++  report.
+ The SCTP Protocol (EXPERIMENTAL)
+ CONFIG_IP_SCTP
+   Stream Control Transmission Protocol
+--- linux-2.4.27/Documentation/DocBook/Makefile~mipv6-1.1-v2.4.26
++++ linux-2.4.27/Documentation/DocBook/Makefile
+@@ -2,7 +2,7 @@
+          kernel-api.sgml parportbook.sgml kernel-hacking.sgml \
+          kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \
+          deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \
+-         journal-api.sgml libata.sgml
++         journal-api.sgml libata.sgml mip6-func.sgml
+ PS    :=      $(patsubst %.sgml, %.ps, $(BOOKS))
+ PDF   :=      $(patsubst %.sgml, %.pdf, $(BOOKS))
+@@ -96,6 +96,9 @@
+ procfs-guide.sgml:  procfs-guide.tmpl procfs_example.sgml
+       $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@
++mip6-func.sgml: mip6-func.tmpl
++      $(TOPDIR)/scripts/docgen <$< >$@
++
+ APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \
+               $(TOPDIR)/arch/i386/kernel/irq.c \
+               $(TOPDIR)/arch/i386/kernel/mca.c \
+--- /dev/null
++++ linux-2.4.27/Documentation/DocBook/mip6-func.tmpl
+@@ -0,0 +1,756 @@
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
++<book id="LinuxMobileIPv6">
++ <bookinfo>
++  <title>MIPL Mobile IPv6 Function Reference Guide</title>
++
++  <authorgroup>
++    <author>
++      <othername>MIPL Mobile IPv6 for Linux Team</othername>
++      <affiliation>
++       <orgname>Helsinki University of Technology</orgname>
++       <orgdiv>Telecommunications Software and Multimedia Lab</orgdiv>
++       <address>
++        <pob>PO BOX 9201</pob>
++      <postcode>FIN-02015 HUT</postcode>
++      <country>Finland</country>
++        <email>mipl@list.mipl.mediapoli.com</email>
++       </address>
++      </affiliation>
++     </author>
++  </authorgroup>
++
++  <copyright>
++   <year>2000-2001</year>
++   <holder>Helsinki University of Technology</holder>
++  </copyright>
++ 
++  <legalnotice>
++   <para>
++     Copyright (c) 2001, 2002 MIPL Mobile IPv6 for Linux Team.
++   </para>
++   <para>
++     Permission is granted to copy, distribute and/or modify this
++     document under the terms of the GNU Free Documentation License,
++     Version 1.1 published by the Free Software Foundation; with the
++     Invariant Sections being "Introduction", with the Front-Cover
++     Texts being "MIPL Mobile IPv6 Function Reference Guide", "MIPL
++     Mobile IPv6 for Linux Team" and "Helsinki University of
++     Technology".  A copy of the license is included in <xref
++     linkend="gfdl">.
++   </para>
++      
++  </legalnotice>
++ </bookinfo>
++
++<toc></toc>
++
++  <preface id="intro">
++     <title>Introduction</title>
++
++     <para>
++       MIPL Mobile IPv6 for Linux is an implementation of Mobility
++       Support in IPv6 IETF mobile-ip working groups Internet-Draft
++       (draft-ietf-mobileip-ipv6).  This implementation has been
++       developed in the Telecommunications Software and Multimedia
++       Laboratory at Helsinki University of Technology.
++     </para>
++
++     <para>
++       MIPL is fully open source, licensed under the GNU General
++       Public License.  Latest source for MIPL can be downloaded from
++       the MIPL website at:
++     </para>
++     <programlisting>
++       http://www.mipl.mediapoli.com/.
++     </programlisting>
++     <para>
++       Developers and users interested in MIPL can subscribe to the
++       MIPL mailing list by sending e-mail to
++       <email>majordomo@list.mipl.mediapoli.com</email> with
++     </para>
++     <programlisting>
++       subscribe mipl
++     </programlisting>
++     <para>
++       in the body of the message.
++     </para>
++
++     <para>
++       This document is a reference guide to MIPL functions.  Intended
++       audience is developers wishing to contribute to the project.
++       Hopefully this document will make it easier and quicker to
++       understand and adopt the inner workings of MIPL Mobile IPv6.
++     </para>
++
++     <para>
++       MIPL Mobile IPv6 for Linux Team members (past and present):
++
++       <itemizedlist>
++        <listitem>
++      <address>
++      Sami Kivisaari <email>Sami.Kivisaari@hut.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Niklas Kampe <email>Niklas.Kampe@hut.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Juha Mynttinen <email>Juha.Mynttinen@hut.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Toni Nykanen <email>Toni.Nykanen@iki.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Henrik Petander <email>Henrik.Petander@hut.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Antti Tuominen <email>ajtuomin@tml.hut.fi</email>
++      </address>
++        </listitem>
++       </itemizedlist>
++
++       <itemizedlist>
++        <listitem>
++      <address>
++      Marko Myllynen
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Ville Nuorvala <email>vnuorval@tcs.hut.fi</email>
++      </address>
++        </listitem>
++        <listitem>
++      <address>
++      Jaakko Laine <email>Jaakko.Laine@hut.fi</email>
++      </address>
++        </listitem>
++       </itemizedlist>
++     </para>
++
++  </preface>
++
++  <chapter id="common">
++     <title>Common functions for all entities</title>
++
++     <sect1><title>Low-level functions</title>
++     <para>
++       These functions implement memory allocation used by others.
++       Hashlist functions implement a linked list with hash lookup,
++       which is used with Binding Update List, Binding Cache, Home
++       Agents List etc.
++     </para>
++!Inet/ipv6/mobile_ip6/mempool.h
++!Inet/ipv6/mobile_ip6/hashlist.h
++     </sect1>
++
++     <sect1><title>Debug functions</title>
++     <para>
++       Debug and utility functions.  These functions are available if
++       <constant>CONFIG_IPV6_MOBILITY_DEBUG</constant> is set.
++       Otherwise macros expand to no operation.
++     </para>
++!Inet/ipv6/mobile_ip6/debug.h
++!Inet/ipv6/mobile_ip6/mipv6.c
++     </sect1>
++
++     <sect1><title>Extension Header functions</title>
++     <para>
++       These functions create and handle extension headers that are
++       specific to MIPv6.
++     </para>
++!Inet/ipv6/mobile_ip6/exthdrs.c
++     </sect1>
++
++     <sect1><title>Mobility Header functions</title>
++     <para>
++       MIPv6 specifies a new protocol called Mobility Header.
++       Mobility Header has several message types.  Messages may also
++       carry Mobility Options.  These functions are used to create and
++       handle Mobility Headers and Mobility Options.
++     </para>
++!Inet/ipv6/mobile_ip6/sendopts.c
++!Inet/ipv6/mobile_ip6/mh_recv.c
++!Inet/ipv6/mobile_ip6/auth_subopt.c
++     </sect1>
++
++     <sect1><title>Binding Cache</title>
++     <para>
++       All Mobile IPv6 entities have a binding cache.  These functions
++       provide easy manipulation of the binding cache.
++     </para>
++!Inet/ipv6/mobile_ip6/bcache.c
++     </sect1>
++
++     <sect1><title>Security</title>
++
++     <para>
++       These functions are common authentication functions and
++       implement Draft 13 style IPSec AH support for Binding Updates.
++     </para>
++!Inet/ipv6/mobile_ip6/ah_algo.c
++!Inet/ipv6/mobile_ip6/sadb.c
++!Inet/ipv6/mobile_ip6/ah.c
++     </sect1>
++
++     <sect1><title>Utility functions</title>
++
++     <para>
++       These functions are general utility functions commonly used by
++       all entities.
++     </para>
++!Inet/ipv6/mobile_ip6/util.c
++     </sect1>
++
++  </chapter>
++
++  <chapter id="mn">
++     <title>Mobile Node functions</title>
++     <sect1><title>General functions</title>
++     <para>
++     </para>
++!Inet/ipv6/mobile_ip6/mn.c
++     </sect1>
++
++     <sect1><title>Binding Update List</title>
++     <para>
++       Mobile Node keeps track of sent binding updates in Binding
++       Update List.
++     </para>
++!Inet/ipv6/mobile_ip6/bul.c
++     </sect1>
++
++     <sect1><title>Movement detection</title>
++
++     <para>
++       These functions are used by the mobile node for movement
++       detection.
++     </para>
++!Inet/ipv6/mobile_ip6/mdetect.c
++     </sect1>
++  </chapter>
++
++  <chapter id="ha">
++     <title>Home Agent functions</title>
++     <sect1><title>General functions</title>
++     <para>
++     </para>
++!Inet/ipv6/mobile_ip6/ha.c
++     </sect1>
++
++     <sect1><title>Duplicate Address Detection functions</title>
++     <para>
++       Home Agent does Duplicate Address Detection for Mobile Nodes'
++       addresses.  These functions implement MIPv6 specific DAD
++       functionality.
++     </para>
++!Inet/ipv6/mobile_ip6/dad.c
++     </sect1>
++
++  </chapter>
++  <appendix id="gfdl">
++     <title>GNU Free Documentation License</title>
++
++     <para>
++       Version 1.1, March 2000
++     </para>
++
++     <programlisting>
++       Copyright (C) 2000  Free Software Foundation, Inc.
++       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++       Everyone is permitted to copy and distribute verbatim copies
++       of this license document, but changing it is not allowed.
++     </programlisting>
++
++     <sect1><title>0. PREAMBLE</title>
++
++     <para>
++       The purpose of this License is to make a manual, textbook, or
++       other written document "free" in the sense of freedom: to
++       assure everyone the effective freedom to copy and redistribute
++       it, with or without modifying it, either commercially or
++       noncommercially. Secondarily, this License preserves for the
++       author and publisher a way to get credit for their work, while
++       not being considered responsible for modifications made by
++       others.
++     </para>
++
++     <para>
++       This License is a kind of "copyleft", which means that
++       derivative works of the document must themselves be free in the
++       same sense. It complements the GNU General Public License,
++       which is a copyleft license designed for free software.
++     </para>
++
++     <para>
++       We have designed this License in order to use it for manuals
++       for free software, because free software needs free
++       documentation: a free program should come with manuals
++       providing the same freedoms that the software does. But this
++       License is not limited to software manuals; it can be used for
++       any textual work, regardless of subject matter or whether it is
++       published as a printed book. We recommend this License
++       principally for works whose purpose is instruction or
++       reference.
++     </para>
++
++     </sect1>
++     <sect1><title>1. APPLICABILITY AND DEFINITIONS</title>
++
++     <para>
++       This License applies to any manual or other work that contains
++       a notice placed by the copyright holder saying it can be
++       distributed under the terms of this License. The "Document",
++       below, refers to any such manual or work. Any member of the
++       public is a licensee, and is addressed as "you".
++     </para>
++
++     <para>
++       A "Modified Version" of the Document means any work containing
++       the Document or a portion of it, either copied verbatim, or
++       with modifications and/or translated into another language.
++     </para>
++
++     <para>
++       A "Secondary Section" is a named appendix or a front-matter
++       section of the Document that deals exclusively with the
++       relationship of the publishers or authors of the Document to
++       the Document's overall subject (or to related matters) and
++       contains nothing that could fall directly within that overall
++       subject. (For example, if the Document is in part a textbook of
++       mathematics, a Secondary Section may not explain any
++       mathematics.) The relationship could be a matter of historical
++       connection with the subject or with related matters, or of
++       legal, commercial, philosophical, ethical or political position
++       regarding them.
++     </para>
++
++     <para>
++       The "Invariant Sections" are certain Secondary Sections whose
++       titles are designated, as being those of Invariant Sections, in
++       the notice that says that the Document is released under this
++       License.
++     </para>
++
++     <para>
++       The "Cover Texts" are certain short passages of text that are
++       listed, as Front-Cover Texts or Back-Cover Texts, in the notice
++       that says that the Document is released under this License.
++     </para>
++
++     <para>
++       A "Transparent" copy of the Document means a machine-readable
++       copy, represented in a format whose specification is available
++       to the general public, whose contents can be viewed and edited
++       directly and straightforwardly with generic text editors or
++       (for images composed of pixels) generic paint programs or (for
++       drawings) some widely available drawing editor, and that is
++       suitable for input to text formatters or for automatic
++       translation to a variety of formats suitable for input to text
++       formatters. A copy made in an otherwise Transparent file format
++       whose markup has been designed to thwart or discourage
++       subsequent modification by readers is not Transparent. A copy
++       that is not "Transparent" is called "Opaque".
++     </para>
++
++     <para>
++       Examples of suitable formats for Transparent copies include
++       plain ASCII without markup, Texinfo input format, LaTeX input
++       format, SGML or XML using a publicly available DTD, and
++       standard-conforming simple HTML designed for human
++       modification. Opaque formats include PostScript, PDF,
++       proprietary formats that can be read and edited only by
++       proprietary word processors, SGML or XML for which the DTD
++       and/or processing tools are not generally available, and the
++       machine-generated HTML produced by some word processors for
++       output purposes only.
++     </para>
++
++     <para>
++       The "Title Page" means, for a printed book, the title page
++       itself, plus such following pages as are needed to hold,
++       legibly, the material this License requires to appear in the
++       title page. For works in formats which do not have any title
++       page as such, "Title Page" means the text near the most
++       prominent appearance of the work's title, preceding the
++       beginning of the body of the text.
++     </para>
++
++     </sect1>
++     <sect1><title>2. VERBATIM COPYING</title>
++
++     <para>
++       You may copy and distribute the Document in any medium, either
++       commercially or noncommercially, provided that this License,
++       the copyright notices, and the license notice saying this
++       License applies to the Document are reproduced in all copies,
++       and that you add no other conditions whatsoever to those of
++       this License. You may not use technical measures to obstruct or
++       control the reading or further copying of the copies you make
++       or distribute. However, you may accept compensation in exchange
++       for copies. If you distribute a large enough number of copies
++       you must also follow the conditions in section 3.
++     </para>
++
++     <para>
++       You may also lend copies, under the same conditions stated
++       above, and you may publicly display copies.
++     </para>
++
++     </sect1>
++     <sect1><title>3. COPYING IN QUANTITY</title>
++
++     <para>
++       If you publish printed copies of the Document numbering more
++       than 100, and the Document's license notice requires Cover
++       Texts, you must enclose the copies in covers that carry,
++       clearly and legibly, all these Cover Texts: Front-Cover Texts
++       on the front cover, and Back-Cover Texts on the back
++       cover. Both covers must also clearly and legibly identify you
++       as the publisher of these copies. The front cover must present
++       the full title with all words of the title equally prominent
++       and visible. You may add other material on the covers in
++       addition. Copying with changes limited to the covers, as long
++       as they preserve the title of the Document and satisfy these
++       conditions, can be treated as verbatim copying in other
++       respects.
++     </para>
++
++     <para>
++       If the required texts for either cover are too voluminous to
++       fit legibly, you should put the first ones listed (as many as
++       fit reasonably) on the actual cover, and continue the rest onto
++       adjacent pages.
++     </para>
++
++     <para>
++       If you publish or distribute Opaque copies of the Document
++       numbering more than 100, you must either include a
++       machine-readable Transparent copy along with each Opaque copy,
++       or state in or with each Opaque copy a publicly-accessible
++       computer-network location containing a complete Transparent
++       copy of the Document, free of added material, which the general
++       network-using public has access to download anonymously at no
++       charge using public-standard network protocols. If you use the
++       latter option, you must take reasonably prudent steps, when you
++       begin distribution of Opaque copies in quantity, to ensure that
++       this Transparent copy will remain thus accessible at the stated
++       location until at least one year after the last time you
++       distribute an Opaque copy (directly or through your agents or
++       retailers) of that edition to the public.
++     </para>
++
++     <para>
++       It is requested, but not required, that you contact the authors
++       of the Document well before redistributing any large number of
++       copies, to give them a chance to provide you with an updated
++       version of the Document.
++     </para>
++
++     </sect1>
++     <sect1><title>4. MODIFICATIONS</title>
++
++     <para>
++       You may copy and distribute a Modified Version of the Document
++       under the conditions of sections 2 and 3 above, provided that
++       you release the Modified Version under precisely this License,
++       with the Modified Version filling the role of the Document,
++       thus licensing distribution and modification of the Modified
++       Version to whoever possesses a copy of it. In addition, you
++       must do these things in the Modified Version:
++     </para>
++
++     <para>
++      <itemizedlist spacing=compact>
++       <listitem>
++        <para>
++        A. Use in the Title Page (and on the covers, if any) a title
++        distinct from that of the Document, and from those of previous
++        versions (which should, if there were any, be listed in the
++        History section of the Document). You may use the same title
++        as a previous version if the original publisher of that
++        version gives permission.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      B. List on the Title Page, as authors, one or more persons
++      or entities responsible for authorship of the modifications in
++      the Modified Version, together with at least five of the
++      principal authors of the Document (all of its principal
++      authors, if it has less than five).
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      C. State on the Title page the name of the publisher of the
++      Modified Version, as the publisher.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      D. Preserve all the copyright notices of the Document.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      E. Add an appropriate copyright notice for your
++      modifications adjacent to the other copyright notices.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      F. Include, immediately after the copyright notices, a
++      license notice giving the public permission to use the
++      Modified Version under the terms of this License, in the form
++      shown in the Addendum below.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      G. Preserve in that license notice the full lists of
++      Invariant Sections and required Cover Texts given in the
++      Document's license notice.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      H. Include an unaltered copy of this License.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      I. Preserve the section entitled "History", and its title,
++      and add to it an item stating at least the title, year, new
++      authors, and publisher of the Modified Version as given on the
++      Title Page. If there is no section entitled "History" in the
++      Document, create one stating the title, year, authors, and
++      publisher of the Document as given on its Title Page, then add
++      an item describing the Modified Version as stated in the
++      previous sentence.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++        J. Preserve the network location, if any, given in the
++        Document for public access to a Transparent copy of the
++        Document, and likewise the network locations given in the
++        Document for previous versions it was based on. These may be
++        placed in the "History" section. You may omit a network
++        location for a work that was published at least four years
++        before the Document itself, or if the original publisher of
++        the version it refers to gives permission.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++        K. In any section entitled "Acknowledgements" or
++        "Dedications", preserve the section's title, and preserve in
++        the section all the substance and tone of each of the
++        contributor acknowledgements and/or dedications given therein.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      L. Preserve all the Invariant Sections of the Document,
++      unaltered in their text and in their titles. Section numbers
++      or the equivalent are not considered part of the section
++      titles.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      M. Delete any section entitled "Endorsements". Such a
++      section may not be included in the Modified Version.
++      </para>
++       </listitem>
++       <listitem>
++        <para>
++      N. Do not retitle any existing section as "Endorsements" or
++      to conflict in title with any Invariant Section.
++      </para>
++       </listitem>
++      </itemizedlist>
++     </para>
++
++     <para>
++       If the Modified Version includes new front-matter sections or
++       appendices that qualify as Secondary Sections and contain no
++       material copied from the Document, you may at your option
++       designate some or all of these sections as invariant. To do
++       this, add their titles to the list of Invariant Sections in the
++       Modified Version's license notice. These titles must be
++       distinct from any other section titles.
++     </para>
++
++     <para>
++       You may add a section entitled "Endorsements", provided it
++       contains nothing but endorsements of your Modified Version by
++       various parties--for example, statements of peer review or that
++       the text has been approved by an organization as the
++       authoritative definition of a standard.
++     </para>
++
++     <para>
++       You may add a passage of up to five words as a Front-Cover
++       Text, and a passage of up to 25 words as a Back-Cover Text, to
++       the end of the list of Cover Texts in the Modified
++       Version. Only one passage of Front-Cover Text and one of
++       Back-Cover Text may be added by (or through arrangements made
++       by) any one entity. If the Document already includes a cover
++       text for the same cover, previously added by you or by
++       arrangement made by the same entity you are acting on behalf
++       of, you may not add another; but you may replace the old one,
++       on explicit permission from the previous publisher that added
++       the old one.
++     </para>
++
++     <para>
++       The author(s) and publisher(s) of the Document do not by this
++       License give permission to use their names for publicity for or
++       to assert or imply endorsement of any Modified Version.
++     </para>
++
++     </sect1>
++     <sect1><title>5. COMBINING DOCUMENTS</title>
++
++     <para>
++       You may combine the Document with other documents released
++       under this License, under the terms defined in section 4 above
++       for modified versions, provided that you include in the
++       combination all of the Invariant Sections of all of the
++       original documents, unmodified, and list them all as Invariant
++       Sections of your combined work in its license notice.
++     </para>
++
++     <para>
++       The combined work need only contain one copy of this License,
++       and multiple identical Invariant Sections may be replaced with
++       a single copy. If there are multiple Invariant Sections with
++       the same name but different contents, make the title of each
++       such section unique by adding at the end of it, in parentheses,
++       the name of the original author or publisher of that section if
++       known, or else a unique number. Make the same adjustment to the
++       section titles in the list of Invariant Sections in the license
++       notice of the combined work.
++     </para>
++
++     <para>
++       In the combination, you must combine any sections entitled
++       "History" in the various original documents, forming one
++       section entitled "History"; likewise combine any sections
++       entitled "Acknowledgements", and any sections entitled
++       "Dedications". You must delete all sections entitled
++       "Endorsements."
++     </para>
++
++     </sect1>
++     <sect1><title>6. COLLECTIONS OF DOCUMENTS</title>
++
++     <para>
++       You may make a collection consisting of the Document and other
++       documents released under this License, and replace the
++       individual copies of this License in the various documents with
++       a single copy that is included in the collection, provided that
++       you follow the rules of this License for verbatim copying of
++       each of the documents in all other respects.
++     </para>
++
++     <para>
++       You may extract a single document from such a collection, and
++       distribute it individually under this License, provided you
++       insert a copy of this License into the extracted document, and
++       follow this License in all other respects regarding verbatim
++       copying of that document.
++     </para>
++
++     </sect1>
++     <sect1><title>7. AGGREGATION WITH INDEPENDENT WORKS</title>
++
++     <para>
++       A compilation of the Document or its derivatives with other
++       separate and independent documents or works, in or on a volume
++       of a storage or distribution medium, does not as a whole count
++       as a Modified Version of the Document, provided no compilation
++       copyright is claimed for the compilation. Such a compilation is
++       called an "aggregate", and this License does not apply to the
++       other self-contained works thus compiled with the Document, on
++       account of their being thus compiled, if they are not
++       themselves derivative works of the Document.
++     </para>
++
++     <para>
++       If the Cover Text requirement of section 3 is applicable to
++       these copies of the Document, then if the Document is less than
++       one quarter of the entire aggregate, the Document's Cover Texts
++       may be placed on covers that surround only the Document within
++       the aggregate. Otherwise they must appear on covers around the
++       whole aggregate.
++     </para>
++
++     </sect1>
++     <sect1><title>8. TRANSLATION</title>
++
++     <para>
++       Translation is considered a kind of modification, so you may
++       distribute translations of the Document under the terms of
++       section 4. Replacing Invariant Sections with translations
++       requires special permission from their copyright holders, but
++       you may include translations of some or all Invariant Sections
++       in addition to the original versions of these Invariant
++       Sections. You may include a translation of this License
++       provided that you also include the original English version of
++       this License. In case of a disagreement between the translation
++       and the original English version of this License, the original
++       English version will prevail.
++     </para>
++
++     </sect1>
++     <sect1><title>9. TERMINATION</title>
++
++     <para>
++       You may not copy, modify, sublicense, or distribute the
++       Document except as expressly provided for under this
++       License. Any other attempt to copy, modify, sublicense or
++       distribute the Document is void, and will automatically
++       terminate your rights under this License. However, parties who
++       have received copies, or rights, from you under this License
++       will not have their licenses terminated so long as such parties
++       remain in full compliance.
++     </para>
++
++     </sect1>
++     <sect1><title>10. FUTURE REVISIONS OF THIS LICENSE</title>
++
++     <para>
++       The Free Software Foundation may publish new, revised versions
++       of the GNU Free Documentation License from time to time. Such
++       new versions will be similar in spirit to the present version,
++       but may differ in detail to address new problems or
++       concerns. See http://www.gnu.org/copyleft/.
++     </para>
++
++     <para>
++       Each version of the License is given a distinguishing version
++       number. If the Document specifies that a particular numbered
++       version of this License "or any later version" applies to it,
++       you have the option of following the terms and conditions
++       either of that specified version or of any later version that
++       has been published (not as a draft) by the Free Software
++       Foundation. If the Document does not specify a version number
++       of this License, you may choose any version ever published (not
++       as a draft) by the Free Software Foundation.
++     </para>
++
++     </sect1>
++  </appendix>
++</book>
+--- linux-2.4.27/include/linux/icmpv6.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/icmpv6.h
+@@ -40,14 +40,16 @@
+                 struct icmpv6_nd_ra {
+                       __u8            hop_limit;
+ #if defined(__LITTLE_ENDIAN_BITFIELD)
+-                      __u8            reserved:6,
++                      __u8            reserved:5,
++                                      home_agent:1,
+                                       other:1,
+                                       managed:1;
+ #elif defined(__BIG_ENDIAN_BITFIELD)
+                       __u8            managed:1,
+                                       other:1,
+-                                      reserved:6;
++                                      home_agent:1,
++                                      reserved:5;
+ #else
+ #error        "Please fix <asm/byteorder.h>"
+ #endif
+@@ -70,6 +72,7 @@
+ #define icmp6_addrconf_managed        icmp6_dataun.u_nd_ra.managed
+ #define icmp6_addrconf_other  icmp6_dataun.u_nd_ra.other
+ #define icmp6_rt_lifetime     icmp6_dataun.u_nd_ra.rt_lifetime
++#define icmp6_home_agent      icmp6_dataun.u_nd_ra.home_agent
+ };
+--- linux-2.4.27/include/linux/if_arp.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/if_arp.h
+@@ -59,7 +59,7 @@
+ #define ARPHRD_RAWHDLC        518             /* Raw HDLC                     */
+ #define ARPHRD_TUNNEL 768             /* IPIP tunnel                  */
+-#define ARPHRD_TUNNEL6        769             /* IPIP6 tunnel                 */
++#define ARPHRD_TUNNEL6        769             /* IP6IP6 tunnel                */
+ #define ARPHRD_FRAD   770             /* Frame Relay Access Device    */
+ #define ARPHRD_SKIP   771             /* SKIP vif                     */
+ #define ARPHRD_LOOPBACK       772             /* Loopback device              */
+--- linux-2.4.27/include/linux/in6.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/in6.h
+@@ -142,6 +142,11 @@
+ #define IPV6_TLV_JUMBO                194
+ /*
++ *    Mobile IPv6 TLV options.
++ */
++#define MIPV6_TLV_HOMEADDR    201
++
++/*
+  *    IPV6 socket options
+  */
+--- linux-2.4.27/include/linux/ipv6.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/ipv6.h
+@@ -29,6 +29,7 @@
+ #define IPV6_SRCRT_STRICT     0x01    /* this hop must be a neighbor  */
+ #define IPV6_SRCRT_TYPE_0     0       /* IPv6 type 0 Routing Header   */
++#define IPV6_SRCRT_TYPE_2     2       /*      type 2 for Mobile IPv6  */
+ /*
+  *    routing header
+@@ -71,6 +72,19 @@
+       struct in6_addr         addr[0];
+ #define rt0_type              rt_hdr.type
++
++};
++
++/*
++ *    routing header type 2
++ */
++
++struct rt2_hdr {
++      struct ipv6_rt_hdr      rt_hdr;
++      __u32                   reserved;
++      struct in6_addr         addr;
++
++#define rt2_type              rt_hdr.type;
+ };
+ /*
+@@ -156,12 +170,16 @@
+ struct inet6_skb_parm
+ {
+       int                     iif;
++      __u8                    mipv6_flags;
+       __u16                   ra;
+       __u16                   hop;
+       __u16                   auth;
+       __u16                   dst0;
+       __u16                   srcrt;
++      __u16                   srcrt2;
++      __u16                   hao;
+       __u16                   dst1;
++      struct in6_addr         hoa;
+ };
+ #endif
+--- linux-2.4.27/include/linux/ipv6_route.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/ipv6_route.h
+@@ -33,6 +33,7 @@
+ #define RTF_CACHE     0x01000000      /* cache entry                  */
+ #define RTF_FLOW      0x02000000      /* flow significant route       */
+ #define RTF_POLICY    0x04000000      /* policy route                 */
++#define RTF_MOBILENODE  0x10000000      /* for routing to Mobile Node   */
+ #define RTF_LOCAL     0x80000000
+--- /dev/null
++++ linux-2.4.27/include/linux/ipv6_tunnel.h
+@@ -0,0 +1,34 @@
++/*
++ * $Id$
++ */
++
++#ifndef _IPV6_TUNNEL_H
++#define _IPV6_TUNNEL_H
++
++#define IPV6_TLV_TNL_ENCAP_LIMIT 4
++#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4
++
++/* don't add encapsulation limit if one isn't present in inner packet */
++#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1
++/* copy the traffic class field from the inner packet */
++#define IP6_TNL_F_USE_ORIG_TCLASS 0x2
++/* copy the flowlabel from the inner packet */
++#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4
++/* created and maintained from within the kernel */
++#define IP6_TNL_F_KERNEL_DEV 0x8
++/* being used for Mobile IPv6 */
++#define IP6_TNL_F_MIP6_DEV 0x10
++
++struct ip6_tnl_parm {
++      char name[IFNAMSIZ];    /* name of tunnel device */
++      int link;               /* ifindex of underlying L2 interface */
++      __u8 proto;             /* tunnel protocol */
++      __u8 encap_limit;       /* encapsulation limit for tunnel */
++      __u8 hop_limit;         /* hop limit for tunnel */
++      __u32 flowinfo;         /* traffic class and flowlabel for tunnel */
++      __u32 flags;            /* tunnel flags */
++      struct in6_addr laddr;  /* local tunnel end-point address */
++      struct in6_addr raddr;  /* remote tunnel end-point address */
++};
++
++#endif
+--- linux-2.4.27/include/linux/rtnetlink.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/rtnetlink.h
+@@ -315,6 +315,7 @@
+       IFA_BROADCAST,
+       IFA_ANYCAST,
+       IFA_CACHEINFO,
++      IFA_HOMEAGENT,
+       __IFA_MAX
+ };
+@@ -324,6 +325,7 @@
+ #define IFA_F_SECONDARY               0x01
++#define IFA_F_HOMEADDR                0x10
+ #define IFA_F_DEPRECATED      0x20
+ #define IFA_F_TENTATIVE               0x40
+ #define IFA_F_PERMANENT               0x80
+--- linux-2.4.27/include/linux/skbuff.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/skbuff.h
+@@ -177,7 +177,7 @@
+        * want to keep them across layers you have to do a skb_clone()
+        * first. This is owned by whoever has the skb queued ATM.
+        */ 
+-      char            cb[48];  
++      char            cb[64];  
+       unsigned int    len;                    /* Length of actual data                        */
+       unsigned int    data_len;
+--- linux-2.4.27/include/linux/sysctl.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/linux/sysctl.h
+@@ -404,6 +404,23 @@
+       NET_IPV6_ICMP=19,
+       NET_IPV6_BINDV6ONLY=20,
+       NET_IPV6_MLD_MAX_MSF=25,
++      NET_IPV6_MOBILITY=26
++};
++
++/* /proc/sys/net/ipv6/mobility */
++enum {
++      NET_IPV6_MOBILITY_DEBUG=1,
++      NET_IPV6_MOBILITY_TUNNEL_SITELOCAL=2,
++      NET_IPV6_MOBILITY_ROUTER_SOLICITATION_MAX_SENDTIME=3,
++      NET_IPV6_MOBILITY_ROUTER_REACH=4,
++      NET_IPV6_MOBILITY_MDETECT_MECHANISM=5,
++      NET_IPV6_MOBILITY_RETROUT=6,
++      NET_IPV6_MOBILITY_MAX_TNLS=7,
++      NET_IPV6_MOBILITY_MIN_TNLS=8,
++      NET_IPV6_MOBILITY_BINDING_REFRESH=9,
++      NET_IPV6_MOBILITY_BU_F_LLADDR=10,
++      NET_IPV6_MOBILITY_BU_F_KEYMGM=11,
++      NET_IPV6_MOBILITY_BU_F_CN_ACK=12
+ };
+ enum {
+--- linux-2.4.27/include/net/addrconf.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/net/addrconf.h
+@@ -16,9 +16,11 @@
+ #if defined(__BIG_ENDIAN_BITFIELD)
+       __u8                    onlink : 1,
+                               autoconf : 1,
+-                              reserved : 6;
++                              router_address : 1,
++                              reserved : 5;
+ #elif defined(__LITTLE_ENDIAN_BITFIELD)
+-      __u8                    reserved : 6,
++      __u8                    reserved : 5,
++                              router_address : 1,
+                               autoconf : 1,
+                               onlink : 1;
+ #else
+@@ -55,6 +57,7 @@
+                                             struct net_device *dev);
+ extern struct inet6_ifaddr *  ipv6_get_ifaddr(struct in6_addr *addr,
+                                               struct net_device *dev);
++extern void                   ipv6_del_addr(struct inet6_ifaddr *ifp);
+ extern int                    ipv6_get_saddr(struct dst_entry *dst, 
+                                              struct in6_addr *daddr,
+                                              struct in6_addr *saddr);
+@@ -85,7 +88,9 @@
+ extern void ipv6_mc_down(struct inet6_dev *idev);
+ extern void ipv6_mc_init_dev(struct inet6_dev *idev);
+ extern void ipv6_mc_destroy_dev(struct inet6_dev *idev);
++extern void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags);
+ extern void addrconf_dad_failure(struct inet6_ifaddr *ifp);
++extern void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+ extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
+               struct in6_addr *src_addr);
+@@ -116,6 +121,9 @@
+ extern int register_inet6addr_notifier(struct notifier_block *nb);
+ extern int unregister_inet6addr_notifier(struct notifier_block *nb);
++extern int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
++extern int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev);
++
+ static inline struct inet6_dev *
+ __in6_dev_get(struct net_device *dev)
+ {
+--- linux-2.4.27/include/net/ip6_route.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/net/ip6_route.h
+@@ -2,6 +2,7 @@
+ #define _NET_IP6_ROUTE_H
+ #define IP6_RT_PRIO_FW                16
++#define IP6_RT_PRIO_MIPV6             64
+ #define IP6_RT_PRIO_USER      1024
+ #define IP6_RT_PRIO_ADDRCONF  256
+ #define IP6_RT_PRIO_KERN      512
+@@ -40,6 +41,9 @@
+ extern int                    ip6_route_add(struct in6_rtmsg *rtmsg,
+                                             struct nlmsghdr *);
++
++extern int                    ip6_route_del(struct in6_rtmsg *rtmsg,
++                                            struct nlmsghdr *);
+ extern int                    ip6_del_rt(struct rt6_info *,
+                                          struct nlmsghdr *);
+@@ -99,7 +103,8 @@
+  */
+ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
+-                                   struct in6_addr *daddr)
++                               struct in6_addr *daddr, 
++                               struct in6_addr *saddr)
+ {
+       struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+       struct rt6_info *rt = (struct rt6_info *) dst;
+@@ -107,6 +112,9 @@
+       write_lock(&sk->dst_lock);
+       __sk_dst_set(sk, dst);
+       np->daddr_cache = daddr;
++#ifdef CONFIG_IPV6_SUBTREES
++      np->saddr_cache = saddr;
++#endif
+       np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+       write_unlock(&sk->dst_lock);
+ }
+--- linux-2.4.27/include/net/ipv6.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/net/ipv6.h
+@@ -37,6 +37,7 @@
+ #define NEXTHDR_ICMP          58      /* ICMP for IPv6. */
+ #define NEXTHDR_NONE          59      /* No next header */
+ #define NEXTHDR_DEST          60      /* Destination options header. */
++#define NEXTHDR_MH            135     /* Mobility header, RFC 3775 */
+ #define NEXTHDR_MAX           255
+@@ -146,9 +147,12 @@
+       __u16                   opt_flen;       /* after fragment hdr */
+       __u16                   opt_nflen;      /* before fragment hdr */
++      __u8                    mipv6_flags;    /* flags set by MIPv6 */
++
+       struct ipv6_opt_hdr     *hopopt;
+       struct ipv6_opt_hdr     *dst0opt;
+-      struct ipv6_rt_hdr      *srcrt; /* Routing Header */
++      struct ipv6_rt_hdr      *srcrt; /* Routing Header Type 0 */
++      struct ipv6_rt_hdr      *srcrt2; /* Routing Header Type 2 */
+       struct ipv6_opt_hdr     *auth;
+       struct ipv6_opt_hdr     *dst1opt;
+@@ -257,6 +261,38 @@
+                a->s6_addr32[2] | a->s6_addr32[3] ) == 0); 
+ }
++static inline void ipv6_addr_prefix(struct in6_addr *pfx,
++                                  const struct in6_addr *addr, int plen)
++{
++      /* caller must guarantee 0 <= plen <= 128 */
++      int o = plen >> 3,
++          b = plen & 0x7;
++
++      memcpy(pfx->s6_addr, addr, o);
++      if (b != 0) {
++              pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b);
++              o++;
++      }
++      if (o < 16)
++              memset(pfx->s6_addr + o, 0, 16 - o);
++}
++
++static inline int ipv6_prefix_cmp(const struct in6_addr *p1,
++                                const struct in6_addr *p2, int plen)
++{
++      int b = plen&0x7;
++      int o = plen>>3;
++      int res = 0;
++
++      if (o > 0) 
++              res = memcmp(&p1->s6_addr[0], &p2->s6_addr[0], o);
++      if (res == 0 && b > 0) {
++              __u8 m = (0xff00 >> b) & 0xff;
++              res = (p1->s6_addr[o] & m) - (p2->s6_addr[o] & m);  
++      }
++      return res;
++}
++
+ /*
+  *    Prototypes exported by ipv6
+  */
+--- /dev/null
++++ linux-2.4.27/include/net/ipv6_tunnel.h
+@@ -0,0 +1,92 @@
++/*
++ * $Id$
++ */
++
++#ifndef _NET_IPV6_TUNNEL_H
++#define _NET_IPV6_TUNNEL_H
++
++#include <linux/ipv6.h>
++#include <linux/netdevice.h>
++#include <linux/ipv6_tunnel.h>
++#include <linux/skbuff.h>
++#include <asm/atomic.h>
++
++/* capable of sending packets */
++#define IP6_TNL_F_CAP_XMIT 0x10000
++/* capable of receiving packets */
++#define IP6_TNL_F_CAP_RCV 0x20000
++
++#define IP6_TNL_MAX 128
++
++/* IPv6 tunnel */
++
++struct ip6_tnl {
++      struct ip6_tnl *next;   /* next tunnel in list */
++      struct net_device *dev; /* virtual device associated with tunnel */
++      struct net_device_stats stat;   /* statistics for tunnel device */
++      int recursion;          /* depth of hard_start_xmit recursion */
++      struct ip6_tnl_parm parms;      /* tunnel configuration paramters */
++      struct flowi fl;        /* flowi template for xmit */
++      atomic_t refcnt;        /* nr of identical tunnels used by kernel */
++      struct socket *sock;
++};
++
++#define IP6_TNL_PRE_ENCAP 0
++#define IP6_TNL_PRE_DECAP 1
++#define IP6_TNL_MAXHOOKS 2
++
++#define IP6_TNL_DROP 0
++#define IP6_TNL_ACCEPT 1
++
++typedef int ip6_tnl_hookfn(struct ip6_tnl *t, struct sk_buff *skb);
++
++struct ip6_tnl_hook_ops {
++      struct list_head list;
++      unsigned int hooknum;
++      int priority;
++      ip6_tnl_hookfn *hook;
++};
++
++enum ip6_tnl_hook_priorities {
++      IP6_TNL_PRI_FIRST = INT_MIN,
++      IP6_TNL_PRI_LAST = INT_MAX
++};
++
++/* Tunnel encapsulation limit destination sub-option */
++
++struct ipv6_tlv_tnl_enc_lim {
++      __u8 type;              /* type-code for option         */
++      __u8 length;            /* option length                */
++      __u8 encap_limit;       /* tunnel encapsulation limit   */
++} __attribute__ ((packed));
++
++#ifdef __KERNEL__
++extern int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt);
++
++extern struct ip6_tnl *ip6ip6_tnl_lookup(struct in6_addr *remote,
++                                       struct in6_addr *local);
++
++void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p);
++
++extern int ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p);
++
++extern int ip6ip6_kernel_tnl_del(struct ip6_tnl *t);
++
++extern unsigned int ip6ip6_tnl_inc_max_kdev_count(unsigned int n);
++
++extern unsigned int ip6ip6_tnl_dec_max_kdev_count(unsigned int n);
++
++extern unsigned int ip6ip6_tnl_inc_min_kdev_count(unsigned int n);
++
++extern unsigned int ip6ip6_tnl_dec_min_kdev_count(unsigned int n);
++
++extern void ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg);
++
++extern void ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg);
++
++#ifdef CONFIG_IPV6_TUNNEL
++extern int __init ip6_tunnel_init(void);
++extern void ip6_tunnel_cleanup(void);
++#endif
++#endif
++#endif
+--- /dev/null
++++ linux-2.4.27/include/net/mipglue.h
+@@ -0,0 +1,266 @@
++/*
++ *    Glue for Mobility support integration to IPv6
++ *
++ *    Authors:
++ *    Antti Tuominen          <ajtuomin@cc.hut.fi>    
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#ifndef _NET_MIPGLUE_H
++#define _NET_MIPGLUE_H
++
++#ifndef USE_IPV6_MOBILITY
++#if defined(CONFIG_IPV6_MOBILITY) || defined(CONFIG_IPV6_MOBILITY_MODULE)
++#define USE_IPV6_MOBILITY
++#endif
++#endif
++
++/* symbols to indicate whether destination options received should take
++ * effect or not (see exthdrs.c, procrcv.c)
++ */
++#define MIPV6_DSTOPTS_ACCEPT 1
++#define MIPV6_DSTOPTS_DISCARD 0
++
++#define MIPV6_IGN_RTR 0
++#define MIPV6_ADD_RTR 1
++#define MIPV6_CHG_RTR 2
++
++/* MIPV6: Approximate maximum for mobile IPv6 options and headers */
++#define MIPV6_HEADERS 48
++
++#ifdef __KERNEL__
++#include <net/mipv6.h>
++#include <linux/slab.h>
++#include <net/ipv6.h>
++
++struct sk_buff;
++struct ndisc_options;
++struct sock;
++struct ipv6_txoptions;
++struct flowi;
++struct dst_entry;
++struct in6_addr;
++struct inet6_ifaddr;
++
++#ifdef USE_IPV6_MOBILITY
++
++/* calls a procedure from mipv6-module */
++#define MIPV6_CALLPROC(X) if(mipv6_functions.X) mipv6_functions.X
++
++/* calls a function from mipv6-module, default-value if function not defined
++ */
++#define MIPV6_CALLFUNC(X,Y) (!mipv6_functions.X)?(Y):mipv6_functions.X
++
++/* sets a handler-function to process a call */
++#define MIPV6_SETCALL(X,Y) if(mipv6_functions.X) printk("mipv6: Warning, function assigned twice!\n"); \
++                           mipv6_functions.X = Y
++#define MIPV6_RESETCALL(X) mipv6_functions.X = NULL
++
++/* pointers to mipv6 callable functions */
++struct mipv6_callable_functions {
++      void (*mipv6_initialize_dstopt_rcv) (struct sk_buff *skb);
++      int (*mipv6_finalize_dstopt_rcv) (int process);
++      int (*mipv6_handle_homeaddr) (struct sk_buff *skb, int optoff);
++      int (*mipv6_ra_rcv) (struct sk_buff *skb,
++                           struct ndisc_options *ndopts);
++      void (*mipv6_icmp_rcv) (struct sk_buff *skb);
++      struct ipv6_txoptions * (*mipv6_modify_txoptions) (
++              struct sock *sk,
++              struct sk_buff *skb, 
++              struct ipv6_txoptions *opt,
++              struct flowi *fl,
++              struct dst_entry **dst);
++      void (*mipv6_set_home) (int ifindex, struct in6_addr *homeaddr,
++                              int plen, struct in6_addr *homeagent, 
++                              int plen2);
++      void (*mipv6_get_home_address) (struct in6_addr *home_addr);
++      void (*mipv6_get_care_of_address)(struct in6_addr *homeaddr, 
++                                        struct in6_addr *coa);
++      int (*mipv6_is_home_addr)(struct in6_addr *addr);
++      void (*mipv6_change_router)(void);
++      void (*mipv6_check_dad)(struct in6_addr *home_addr);      
++      void (*mipv6_icmp_swap_addrs)(struct sk_buff *skb);
++      int (*mipv6_forward)(struct sk_buff *skb);
++      int (*mipv6_mn_ha_probe)(struct inet6_ifaddr *ifp, u8 *lladdr);
++};
++
++extern struct mipv6_callable_functions mipv6_functions;
++
++extern void mipv6_invalidate_calls(void);
++
++extern int mipv6_handle_dstopt(struct sk_buff *skb, int optoff);
++
++static inline int
++ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr)
++{
++      return MIPV6_CALLFUNC(mipv6_mn_ha_probe, 0)(ifp, lladdr);
++}
++
++/* Must only be called for HA, no checks here */
++static inline int ip6_mipv6_forward(struct sk_buff *skb)
++{
++      return MIPV6_CALLFUNC(mipv6_forward, 0)(skb);
++}
++
++/* 
++ * Avoid adding new default routers if the old one is still in use 
++ */
++
++static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb,
++                                   struct ndisc_options *ndopts)
++{
++      return MIPV6_CALLFUNC(mipv6_ra_rcv, MIPV6_ADD_RTR)(skb, ndopts);
++}
++
++static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr)
++{
++      return MIPV6_CALLFUNC(mipv6_is_home_addr, 0)(addr);
++}
++
++static inline void ndisc_mipv6_change_router(int change_rtr)
++{
++      if (change_rtr == MIPV6_CHG_RTR)
++              MIPV6_CALLPROC(mipv6_change_router)();
++}
++
++static inline void ndisc_check_mipv6_dad(struct in6_addr *target)
++{
++      MIPV6_CALLPROC(mipv6_check_dad)(target);
++}
++
++static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb)
++{
++      MIPV6_CALLPROC(mipv6_icmp_swap_addrs)(skb);
++}
++
++static inline void mipv6_icmp_rcv(struct sk_buff *skb)
++{
++      MIPV6_CALLPROC(mipv6_icmp_rcv)(skb);
++}
++
++static inline int tcp_v6_get_mipv6_header_len(void)
++{
++      return MIPV6_HEADERS;
++}
++
++static inline struct in6_addr *
++mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr)
++{
++      return daddr;
++}
++
++static inline void 
++addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen,
++                         struct in6_addr *homeagent, int plen2)
++{
++      MIPV6_CALLPROC(mipv6_set_home)(ifindex, homeaddr, plen, homeagent, plen2);
++}
++
++static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr)
++{
++      MIPV6_CALLPROC(mipv6_get_home_address)(saddr);
++}
++
++static inline struct ipv6_txoptions *
++ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, 
++                      struct ipv6_txoptions *opt, struct flowi *fl,
++                      struct dst_entry **dst)
++{
++      return MIPV6_CALLFUNC(mipv6_modify_txoptions, opt)(sk, skb, opt, fl, dst); 
++
++}
++
++static inline void
++ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb)
++{
++      struct inet6_skb_parm *opt;
++      if (txopt) {
++              opt = (struct inet6_skb_parm *)skb->cb;
++              opt->mipv6_flags = txopt->mipv6_flags;
++      }
++}
++
++static inline void 
++ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt,
++                       struct ipv6_txoptions *orig_opt) 
++{
++      if (opt && opt != orig_opt)
++              kfree(opt);
++}
++
++#else /* USE_IPV6_MOBILITY */
++
++#define mipv6_handle_dstopt ip6_tlvopt_unknown
++
++static inline int
++ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr)
++{
++      return 0;
++}
++
++static inline int ip6_mipv6_forward(struct sk_buff *skb)
++{
++      return 0;
++}
++
++static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb,
++                                   struct ndisc_options *ndopts)
++{
++      return MIPV6_ADD_RTR;
++}
++
++static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr)
++{
++      return 0;
++}
++
++static inline void ndisc_mipv6_change_router(int change_rtr) {}
++
++static inline void ndisc_check_mipv6_dad(struct in6_addr *target) {}
++
++static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) {}
++
++static inline void mipv6_icmp_rcv(struct sk_buff *skb) {}
++
++static inline int tcp_v6_get_mipv6_header_len(void)
++{
++      return 0;
++}
++
++static inline struct in6_addr *
++mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr)
++{
++      return hdaddr;
++}
++
++static inline void 
++addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen,
++                         struct in6_addr *homeagent, int plen2) {}
++
++static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) {}
++
++static inline struct ipv6_txoptions *
++ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb,
++                      struct ipv6_txoptions *opt, struct flowi *fl,
++                      struct dst_entry **dst)
++{
++      return opt;
++}
++
++static inline void
++ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) {}
++
++static inline void 
++ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, 
++                       struct ipv6_txoptions *orig_opt) {}
++
++#endif /* USE_IPV6_MOBILITY */
++#endif /* __KERNEL__ */
++#endif /* _NET_MIPGLUE_H */
+--- /dev/null
++++ linux-2.4.27/include/net/mipv6.h
+@@ -0,0 +1,258 @@
++/*
++ *    Mobile IPv6 header-file
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#ifndef _NET_MIPV6_H
++#define _NET_MIPV6_H
++
++#include <linux/types.h>
++#include <asm/byteorder.h>
++#include <linux/in6.h>
++
++/*
++ *
++ * Mobile IPv6 Protocol constants
++ *
++ */
++#define DHAAD_RETRIES                 4       /* transmissions        */
++#define INITIAL_BINDACK_TIMEOUT               1       /* seconds              */
++#define INITIAL_DHAAD_TIMEOUT         3       /* seconds              */
++#define INITIAL_SOLICIT_TIMER         3       /* seconds              */
++#define MAX_BINDACK_TIMEOUT           32      /* seconds              */
++#define MAX_NONCE_LIFE                        240     /* seconds              */
++#define MAX_TOKEN_LIFE                        210     /* seconds              */
++#define MAX_RR_BINDING_LIFE           420     /* seconds              */
++#define MAX_UPDATE_RATE                       3       /* 1/s (min delay=1s)   */
++#define PREFIX_ADV_RETRIES            3       /* transmissions        */
++#define PREFIX_ADV_TIMEOUT            3       /* seconds              */
++
++#define MAX_FAST_UPDATES              5       /* transmissions        */
++#define MAX_PFX_ADV_DELAY             1000    /* seconds              */
++#define SLOW_UPDATE_RATE              10      /* 1/10s (max delay=10s)*/
++#define INITIAL_BINDACK_DAD_TIMEOUT   2       /* seconds              */
++
++/*
++ *
++ * Mobile IPv6 (RFC 3775) Protocol configuration variable defaults
++ *
++ */
++#define DefHomeRtrAdvInterval         1000    /* seconds              */
++#define DefMaxMobPfxAdvInterval               86400   /* seconds              */
++#define DefMinDelayBetweenRAs         3       /* seconds (min 0.03)   */
++#define DefMinMobPfxAdvInterval               600     /* seconds              */
++#define DefInitialBindackTimeoutFirstReg      1.5 /* seconds          */
++
++/* This is not actually specified in the draft, but is needed to avoid
++ * prefix solicitation storm when valid lifetime of a prefix is smaller
++ * than MAX_PFX_ADV_DELAY
++ */
++#define MIN_PFX_SOL_DELAY             5       /* seconds              */
++
++/* Mobile IPv6 ICMP types               */
++/*
++ * Official numbers from RFC 3775
++ */
++#define MIPV6_DHAAD_REQUEST           144
++#define MIPV6_DHAAD_REPLY             145
++#define MIPV6_PREFIX_SOLICIT          146
++#define MIPV6_PREFIX_ADV              147
++
++/* Binding update flag codes              */
++#define MIPV6_BU_F_ACK                        0x80
++#define MIPV6_BU_F_HOME                       0x40
++#define MIPV6_BU_F_LLADDR             0x20
++#define MIPV6_BU_F_KEYMGM             0x10
++
++/* Binding ackknowledgment flag codes */
++#define MIPV6_BA_F_KEYMGM             0x80
++
++/* Binding error status */
++#define MIPV6_BE_HAO_WO_BINDING               1
++#define MIPV6_BE_UNKNOWN_MH_TYPE      2
++
++/* Mobility Header */
++struct mipv6_mh
++{
++      __u8    payload;                /* Payload Protocol             */
++      __u8    length;                 /* MH Length                    */
++      __u8    type;                   /* MH Type                      */
++      __u8    reserved;               /* Reserved                     */
++      __u16   checksum;               /* Checksum                     */
++      __u8    data[0];                /* Message specific data        */
++} __attribute__ ((packed));
++
++/* Mobility Header type */
++#define IPPROTO_MOBILITY                135 /* RFC 3775*/                
++/* Mobility Header Message Types */
++
++#define MIPV6_MH_BRR                  0
++#define MIPV6_MH_HOTI                 1
++#define MIPV6_MH_COTI                 2
++#define MIPV6_MH_HOT                  3
++#define MIPV6_MH_COT                  4
++#define MIPV6_MH_BU                   5
++#define MIPV6_MH_BA                   6
++#define MIPV6_MH_BE                   7
++
++/*
++ * Status codes for Binding Acknowledgements
++ */
++#define SUCCESS                               0
++#define REASON_UNSPECIFIED            128
++#define ADMINISTRATIVELY_PROHIBITED   129
++#define INSUFFICIENT_RESOURCES                130
++#define HOME_REGISTRATION_NOT_SUPPORTED       131
++#define NOT_HOME_SUBNET                       132
++#define NOT_HA_FOR_MN                 133
++#define DUPLICATE_ADDR_DETECT_FAIL    134
++#define SEQUENCE_NUMBER_OUT_OF_WINDOW 135
++#define EXPIRED_HOME_NONCE_INDEX      136
++#define EXPIRED_CAREOF_NONCE_INDEX    137
++#define EXPIRED_NONCES                        138
++#define REG_TYPE_CHANGE_FORBIDDEN       139
++/*
++ * Values for mipv6_flags in struct inet6_skb_parm
++ */
++
++#define MIPV6_RCV_TUNNEL              0x1
++#define MIPV6_SND_HAO                 0x2
++#define MIPV6_SND_BU                    0x4
++
++/*
++ * Mobility Header Message structures
++ */
++
++struct mipv6_mh_brr
++{
++      __u16           reserved;
++      /* Mobility options */
++} __attribute__ ((packed));
++
++struct mipv6_mh_bu
++{
++      __u16           sequence;       /* sequence number of BU        */
++      __u8            flags;          /* flags                        */
++      __u8            reserved;       /* reserved bits                */
++      __u16           lifetime;       /* lifetime of BU               */
++      /* Mobility options */
++} __attribute__ ((packed));
++
++struct mipv6_mh_ba
++{
++      __u8            status;         /* statuscode                   */
++      __u8            reserved;       /* reserved bits                */
++      __u16           sequence;       /* sequence number of BA        */
++      __u16           lifetime;       /* lifetime in CN's bcache      */
++      /* Mobility options */
++} __attribute__ ((packed));
++
++struct mipv6_mh_be
++{
++      __u8            status;
++      __u8            reserved;
++      struct in6_addr home_addr;
++      /* Mobility options */
++} __attribute__ ((packed));
++
++struct mipv6_mh_addr_ti
++{
++      __u16           reserved;       /* Reserved                     */
++      u_int8_t        init_cookie[8]; /* HoT/CoT Init Cookie          */
++      /* Mobility options */
++} __attribute__ ((packed));
++
++struct mipv6_mh_addr_test
++{
++      __u16           nonce_index;    /* Home/Care-of Nonce Index     */
++      u_int8_t        init_cookie[8]; /* HoT/CoT Init Cookie          */
++      u_int8_t        kgen_token[8];  /* Home/Care-of key generation token */
++      /* Mobility options */
++} __attribute__ ((packed));
++
++/*
++ * Mobility Options for various MH types.
++ */
++#define MIPV6_OPT_PAD1                        0x00
++#define MIPV6_OPT_PADN                        0x01
++#define MIPV6_OPT_BIND_REFRESH_ADVICE 0x02
++#define MIPV6_OPT_ALTERNATE_COA               0x03
++#define MIPV6_OPT_NONCE_INDICES               0x04
++#define MIPV6_OPT_AUTH_DATA           0x05
++
++#define MIPV6_SEQ_GT(x,y) \
++        ((short int)(((__u16)(x)) - ((__u16)(y))) > 0)
++
++/*
++ * Mobility Option structures
++ */
++
++struct mipv6_mo
++{
++      __u8            type;
++      __u8            length;
++      __u8            value[0];       /* type specific data */
++} __attribute__ ((packed));
++
++struct mipv6_mo_pad1
++{
++      __u8            type;
++} __attribute__ ((packed));
++
++struct mipv6_mo_padn
++{
++      __u8            type;
++      __u8            length;
++      __u8            data[0];
++} __attribute__ ((packed));
++
++struct mipv6_mo_alt_coa
++{
++      __u8            type;
++      __u8            length;
++      struct in6_addr addr;           /* alternate care-of-address    */
++} __attribute__ ((packed));
++
++struct mipv6_mo_nonce_indices
++{
++      __u8            type;
++      __u8            length;
++      __u16           home_nonce_i;   /* Home Nonce Index             */
++      __u16           careof_nonce_i; /* Careof Nonce Index           */
++} __attribute__ ((packed)); 
++
++struct mipv6_mo_bauth_data
++{
++      __u8            type;
++      __u8            length;
++      __u8            data[0];
++} __attribute__ ((packed)); 
++
++struct mipv6_mo_br_advice
++{
++      __u8            type;
++      __u8            length;
++      __u16           refresh_interval; /* Refresh Interval           */
++} __attribute__ ((packed));
++
++/*
++ * Home Address Destination Option structure
++ */
++struct mipv6_dstopt_homeaddr
++{
++      __u8            type;           /* type-code for option         */
++      __u8            length;         /* option length                */
++      struct in6_addr addr;           /* home address                 */
++} __attribute__ ((packed));
++
++#endif /* _NET_MIPV6_H */
+--- linux-2.4.27/include/net/ndisc.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/net/ndisc.h
+@@ -21,6 +21,10 @@
+ #define ND_OPT_REDIRECT_HDR           4
+ #define ND_OPT_MTU                    5
++/* Mobile IPv6 specific ndisc options */ 
++#define ND_OPT_RTR_ADV_INTERVAL               7 
++#define ND_OPT_HOME_AGENT_INFO                8  
++
+ #define MAX_RTR_SOLICITATION_DELAY    HZ
+ #define ND_REACHABLE_TIME             (30*HZ)
+@@ -57,7 +61,7 @@
+ } __attribute__((__packed__));
+ struct ndisc_options {
+-      struct nd_opt_hdr *nd_opt_array[7];
++      struct nd_opt_hdr *nd_opt_array[10];
+       struct nd_opt_hdr *nd_opt_piend;
+ };
+@@ -67,6 +71,8 @@
+ #define nd_opts_pi_end                nd_opt_piend
+ #define nd_opts_rh            nd_opt_array[ND_OPT_REDIRECT_HDR]
+ #define nd_opts_mtu           nd_opt_array[ND_OPT_MTU]
++#define nd_opts_rai           nd_opt_array[ND_OPT_RTR_ADV_INTERVAL]
++#define nd_opts_hai           nd_opt_array[ND_OPT_HOME_AGENT_INFO]
+ extern struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, struct nd_opt_hdr *end);
+ extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, struct ndisc_options *ndopts);
+@@ -83,6 +89,15 @@
+                                             struct in6_addr *daddr,
+                                             struct in6_addr *saddr);
++extern void                   ndisc_send_na(struct net_device *dev,
++                                            struct neighbour *neigh,
++                                            struct in6_addr *daddr,
++                                            struct in6_addr *solicited_addr,
++                                            int router,
++                                            int solicited,
++                                            int override,
++                                            int inc_opt);
++
+ extern void                   ndisc_send_rs(struct net_device *dev,
+                                             struct in6_addr *saddr,
+                                             struct in6_addr *daddr);
+--- linux-2.4.27/include/net/sock.h~mipv6-1.1-v2.4.26
++++ linux-2.4.27/include/net/sock.h
+@@ -149,7 +149,9 @@
+       struct in6_addr         rcv_saddr;
+       struct in6_addr         daddr;
+       struct in6_addr         *daddr_cache;
+-
++#if defined(CONFIG_IPV6_SUBTREES)
++      struct in6_addr         *saddr_cache;
++#endif
+       __u32                   flow_label;
+       __u32                   frag_size;
+       int                     hop_limit;
+--- linux-2.4.27/net/Makefile~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/Makefile
+@@ -7,7 +7,7 @@
+ O_TARGET :=   network.o
+-mod-subdirs :=        ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802
++mod-subdirs :=        ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802 ipv6
+ export-objs :=        netsyms.o
+ subdir-y :=   core ethernet
+@@ -25,6 +25,7 @@
+ ifneq ($(CONFIG_IPV6),n)
+ ifneq ($(CONFIG_IPV6),)
+ subdir-$(CONFIG_NETFILTER)    += ipv6/netfilter
++subdir-$(CONFIG_IPV6_MOBILITY)        += ipv6/mobile_ip6
+ endif
+ endif
+--- linux-2.4.27/net/core/neighbour.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/core/neighbour.c
+@@ -386,7 +386,7 @@
+       if (!creat)
+               return NULL;
+-      n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
++      n = kmalloc(sizeof(*n) + key_len, GFP_ATOMIC);
+       if (n == NULL)
+               return NULL;
+--- linux-2.4.27/net/ipv6/Config.in~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/Config.in
+@@ -1,10 +1,16 @@
+ #
+ # IPv6 configuration
+ # 
+-
++bool '    IPv6: routing by source address (EXPERIMENTAL)' CONFIG_IPV6_SUBTREES
+ #bool '    IPv6: flow policy support' CONFIG_RT6_POLICY
+ #bool '    IPv6: firewall support' CONFIG_IPV6_FIREWALL
++if [ "$CONFIG_IPV6" != "n" ]; then
++      dep_tristate '    IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)' CONFIG_IPV6_TUNNEL $CONFIG_IPV6
++fi
++
++source net/ipv6/mobile_ip6/Config.in
++
+ if [ "$CONFIG_NETFILTER" != "n" ]; then
+    source net/ipv6/netfilter/Config.in
+ fi
+--- linux-2.4.27/net/ipv6/Makefile~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/Makefile
+@@ -6,18 +6,28 @@
+ # unless it's something special (ie not a .c file).
+ #
++export-objs := ipv6_syms.o ipv6_tunnel.o
+-O_TARGET := ipv6.o
++#list-multi   := ipv6.o ipv6_tunnel.o
+-obj-y :=      af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \
+-              route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
+-              protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+-              exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
+-              ip6_flowlabel.o ipv6_syms.o
++ipv6-objs     := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
++                 sit.o route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o \
++                 raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
++                 exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
++                 ip6_flowlabel.o ipv6_syms.o
+-export-objs := ipv6_syms.o
++ifneq ($(CONFIG_IPV6_MOBILITY),n)
++ifneq ($(CONFIG_IPV6_MOBILITY),)
++ipv6-objs += mipglue.o
++endif
++endif
++
++obj-$(CONFIG_IPV6)  += ipv6.o
++obj-$(CONFIG_IPV6_TUNNEL)  += ipv6_tunnel.o
++
++ipv6.o: $(ipv6-objs)
++      $(LD) -r -o $@ $(ipv6-objs)
+-obj-m  := $(O_TARGET)
+ #obj-$(CONFIG_IPV6_FIREWALL) += ip6_fw.o
+--- linux-2.4.27/net/ipv6/addrconf.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/addrconf.c
+@@ -68,6 +68,8 @@
+ #include <asm/uaccess.h>
++#include <net/mipglue.h>
++
+ #define IPV6_MAX_ADDRESSES 16
+ /* Set to 3 to get tracing... */
+@@ -103,9 +105,9 @@
+ static int addrconf_ifdown(struct net_device *dev, int how);
+-static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags);
++void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags);
+ static void addrconf_dad_timer(unsigned long data);
+-static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
++void addrconf_dad_completed(struct inet6_ifaddr *ifp);
+ static void addrconf_rs_timer(unsigned long data);
+ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
+@@ -330,38 +332,6 @@
+       return idev;
+ }
+-void ipv6_addr_prefix(struct in6_addr *prefix,
+-      struct in6_addr *addr, int prefix_len)
+-{
+-      unsigned long mask;
+-      int ncopy, nbits;
+-
+-      memset(prefix, 0, sizeof(*prefix));
+-
+-      if (prefix_len <= 0)
+-              return;
+-      if (prefix_len > 128)
+-              prefix_len = 128;
+-
+-      ncopy = prefix_len / 32;
+-      switch (ncopy) {
+-      case 4: prefix->s6_addr32[3] = addr->s6_addr32[3];
+-      case 3: prefix->s6_addr32[2] = addr->s6_addr32[2];
+-      case 2: prefix->s6_addr32[1] = addr->s6_addr32[1];
+-      case 1: prefix->s6_addr32[0] = addr->s6_addr32[0];
+-      case 0: break;
+-      }
+-      nbits = prefix_len % 32;
+-      if (nbits == 0)
+-              return;
+-
+-      mask = ~((1 << (32 - nbits)) - 1);
+-      mask = htonl(mask);
+-
+-      prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask;
+-}
+-
+-
+ static void dev_forward_change(struct inet6_dev *idev)
+ {
+       struct net_device *dev;
+@@ -513,7 +483,7 @@
+ /* This function wants to get referenced ifp and releases it before return */
+-static void ipv6_del_addr(struct inet6_ifaddr *ifp)
++void ipv6_del_addr(struct inet6_ifaddr *ifp)
+ {
+       struct inet6_ifaddr *ifa, **ifap;
+       struct inet6_dev *idev = ifp->idev;
+@@ -662,6 +632,12 @@
+       if (match)
+               in6_ifa_put(match);
++      /* The home address is always used as source address in
++       * MIPL mobile IPv6
++       */
++      if (scope != IFA_HOST && scope != IFA_LINK)
++              addrconf_get_mipv6_home_address(saddr);
++
+       return err;
+ }
+@@ -815,7 +791,7 @@
+ }
+-static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
++int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
+ {
+       switch (dev->type) {
+       case ARPHRD_ETHER:
+@@ -840,7 +816,7 @@
+       return -1;
+ }
+-static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
++int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
+ {
+       int err = -1;
+       struct inet6_ifaddr *ifp;
+@@ -1407,6 +1383,24 @@
+               sit_route_add(dev);
+ }
++/**
++ * addrconf_ipv6_tunnel_config - configure IPv6 tunnel device
++ * @dev: tunnel device
++ **/
++
++static void addrconf_ipv6_tunnel_config(struct net_device *dev)
++{
++      struct inet6_dev *idev;
++
++      ASSERT_RTNL();
++
++      /* Assign inet6_dev structure to tunnel device */
++      if ((idev = ipv6_find_idev(dev)) == NULL) {
++              printk(KERN_DEBUG "init ipv6 tunnel: add_dev failed\n");
++              return;
++      }
++}
++
+ int addrconf_notify(struct notifier_block *this, unsigned long event, 
+                   void * data)
+@@ -1421,6 +1415,10 @@
+                       addrconf_sit_config(dev);
+                       break;
++              case ARPHRD_TUNNEL6:
++                      addrconf_ipv6_tunnel_config(dev);
++                      break;
++
+               case ARPHRD_LOOPBACK:
+                       init_loopback(dev);
+                       break;
+@@ -1602,7 +1600,7 @@
+ /*
+  *    Duplicate Address Detection
+  */
+-static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags)
++void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags)
+ {
+       struct net_device *dev;
+       unsigned long rand_num;
+@@ -1667,7 +1665,7 @@
+       in6_ifa_put(ifp);
+ }
+-static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
++void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+ {
+       struct net_device *     dev = ifp->idev->dev;
+@@ -1676,7 +1674,7 @@
+        */
+       ipv6_ifa_notify(RTM_NEWADDR, ifp);
+-
++      notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifp);
+       /* If added prefix is link local and forwarding is off,
+          start sending router solicitations.
+        */
+@@ -1877,8 +1875,20 @@
+       if (rta[IFA_LOCAL-1]) {
+               if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))
+                       return -EINVAL;
++              if (ifm->ifa_flags & IFA_F_HOMEADDR && !rta[IFA_HOMEAGENT-1])
++                      return -EINVAL;
+               pfx = RTA_DATA(rta[IFA_LOCAL-1]);
+       }
++      if (rta[IFA_HOMEAGENT-1]) {
++              struct in6_addr *ha;
++              if (pfx == NULL || !(ifm->ifa_flags & IFA_F_HOMEADDR))
++                      return -EINVAL;
++              if (RTA_PAYLOAD(rta[IFA_HOMEAGENT-1]) < sizeof(*ha))
++                      return -EINVAL;
++              ha = RTA_DATA(rta[IFA_HOMEAGENT-1]);
++              addrconf_set_mipv6_mn_home(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
++                                         ha, ifm->ifa_prefixlen);
++      }
+       if (pfx == NULL)
+               return -EINVAL;
+--- linux-2.4.27/net/ipv6/af_inet6.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/af_inet6.c
+@@ -58,6 +58,9 @@
+ #include <net/transp_v6.h>
+ #include <net/ip6_route.h>
+ #include <net/addrconf.h>
++#ifdef CONFIG_IPV6_TUNNEL
++#include <net/ipv6_tunnel.h>
++#endif
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -646,6 +649,11 @@
+       err = ndisc_init(&inet6_family_ops);
+       if (err)
+               goto ndisc_fail;
++#ifdef CONFIG_IPV6_TUNNEL
++      err = ip6_tunnel_init();
++      if (err)
++              goto ip6_tunnel_fail;
++#endif
+       err = igmp6_init(&inet6_family_ops);
+       if (err)
+               goto igmp_fail;
+@@ -698,6 +706,10 @@
+ #endif
+ igmp_fail:
+       ndisc_cleanup();
++#ifdef CONFIG_IPV6_TUNNEL
++      ip6_tunnel_cleanup();
++ip6_tunnel_fail:
++#endif
+ ndisc_fail:
+       icmpv6_cleanup();
+ icmp_fail:
+@@ -730,6 +742,9 @@
+       ip6_route_cleanup();
+       ipv6_packet_cleanup();
+       igmp6_cleanup();
++#ifdef CONFIG_IPV6_TUNNEL
++      ip6_tunnel_cleanup();
++#endif
+       ndisc_cleanup();
+       icmpv6_cleanup();
+ #ifdef CONFIG_SYSCTL
+--- linux-2.4.27/net/ipv6/exthdrs.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/exthdrs.c
+@@ -41,6 +41,9 @@
+ #include <net/ip6_route.h>
+ #include <net/addrconf.h>
++#include <net/mipglue.h>
++#include <net/mipv6.h>
++
+ #include <asm/uaccess.h>
+ /*
+@@ -160,7 +163,8 @@
+  *****************************/
+ struct tlvtype_proc tlvprocdestopt_lst[] = {
+-      /* No destination options are defined now */
++      /* Mobility Support destination options */
++      {MIPV6_TLV_HOMEADDR,    mipv6_handle_dstopt},
+       {-1,                    NULL}
+ };
+@@ -210,6 +214,7 @@
+       struct ipv6_rt_hdr *hdr;
+       struct rt0_hdr *rthdr;
++      struct rt2_hdr *rt2hdr;
+       if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
+           !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
+@@ -225,17 +230,25 @@
+               kfree_skb(skb);
+               return -1;
+       }
+-
++      /* Silently discard invalid packets containing RTH type 2 */ 
++      if (hdr->type == IPV6_SRCRT_TYPE_2 && 
++          (hdr->hdrlen != 2 || hdr->segments_left != 1)) {
++              kfree_skb(skb);
++              return -1;
++      }
+ looped_back:
+       if (hdr->segments_left == 0) {
+-              opt->srcrt = skb->h.raw - skb->nh.raw;
++              if (hdr->type == IPV6_SRCRT_TYPE_0)
++                      opt->srcrt = skb->h.raw - skb->nh.raw;
++              else if (hdr->type == IPV6_SRCRT_TYPE_2)
++                      opt->srcrt2 = skb->h.raw - skb->nh.raw;
+               skb->h.raw += (hdr->hdrlen + 1) << 3;
+               opt->dst0 = opt->dst1;
+               opt->dst1 = 0;
+               return (&hdr->nexthdr) - skb->nh.raw;
+       }
+-      if (hdr->type != IPV6_SRCRT_TYPE_0) {
++      if (hdr->type != IPV6_SRCRT_TYPE_0 && hdr->type != IPV6_SRCRT_TYPE_2) {
+               icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
+               return -1;
+       }
+@@ -275,9 +288,20 @@
+       i = n - --hdr->segments_left;
+-      rthdr = (struct rt0_hdr *) hdr;
+-      addr = rthdr->addr;
+-      addr += i - 1;
++      if (hdr->type == IPV6_SRCRT_TYPE_0) {
++              rthdr = (struct rt0_hdr *) hdr;
++              addr = rthdr->addr;
++              addr += i - 1;
++      } else {
++              /* check that address is this node's home address */
++              rt2hdr = (struct rt2_hdr *) hdr;
++              addr = &rt2hdr->addr;
++              if (!ipv6_chk_addr(addr, NULL) || 
++                  !ipv6_chk_mip_home_addr(addr)) {
++                      kfree_skb(skb);
++                      return -1;
++              }
++      }
+       addr_type = ipv6_addr_type(addr);
+@@ -330,6 +354,10 @@
+    temporary (or permanent) backdoor.
+    If listening socket set IPV6_RTHDR to 2, then we invert header.
+                                                    --ANK (980729)
++
++   By the Mobile IPv6 specification Type 2 routing header MUST NOT be
++   inverted.
++                                                   --AJT (20020917)
+  */
+ struct ipv6_txoptions *
+@@ -352,6 +380,18 @@
+       struct ipv6_txoptions *opt;
+       int hdrlen = ipv6_optlen(hdr);
++      if (hdr->type == IPV6_SRCRT_TYPE_2) {
++              opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC);
++              if (opt == NULL)
++                      return NULL;
++              memset(opt, 0, sizeof(*opt));
++              opt->tot_len = sizeof(*opt) + hdrlen;
++              opt->srcrt = (void*)(opt+1);
++              opt->opt_nflen = hdrlen;
++              memcpy(opt->srcrt, hdr, sizeof(struct rt2_hdr));
++              return opt;
++      }
++
+       if (hdr->segments_left ||
+           hdr->type != IPV6_SRCRT_TYPE_0 ||
+           hdr->hdrlen & 0x01)
+@@ -622,8 +662,18 @@
+       if (opt) {
+               if (opt->dst0opt)
+                       prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
+-              if (opt->srcrt)
+-                      prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
++              if (opt->srcrt) {
++                      if (opt->srcrt2) {
++                              struct in6_addr *rt2_hop = &((struct rt2_hdr *)opt->srcrt2)->addr;
++                              prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, rt2_hop);
++                      } else
++                              prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
++              }
++              if (opt->srcrt2) {
++                      struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb;
++                      ipv6_addr_copy(&parm->hoa, daddr);
++                      prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt2, daddr);
++              }
+       }
+       return prev_hdr;
+ }
+@@ -684,6 +734,11 @@
+                         u8 *proto,
+                         struct in6_addr **daddr)
+ {
++      if (opt->srcrt2) {
++              struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb;
++              ipv6_addr_copy(&parm->hoa, *daddr);
++              ipv6_push_rthdr(skb, proto, opt->srcrt2, daddr);
++      }
+       if (opt->srcrt)
+               ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
+       if (opt->dst0opt)
+@@ -719,6 +774,8 @@
+                       *((char**)&opt2->auth) += dif;
+               if (opt2->srcrt)
+                       *((char**)&opt2->srcrt) += dif;
++              if (opt2->srcrt2)
++                      *((char**)&opt2->srcrt2) += dif;
+       }
+       return opt2;
+ }
+--- linux-2.4.27/net/ipv6/icmp.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/icmp.c
+@@ -61,6 +61,8 @@
+ #include <net/addrconf.h>
+ #include <net/icmp.h>
++#include <net/mipglue.h>
++
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+@@ -364,6 +366,8 @@
+       msg.len = len;
++      icmpv6_swap_mipv6_addrs(skb);
++
+       ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+                      MSG_DONTWAIT);
+       if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB)
+@@ -562,13 +566,13 @@
+               rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,
+                                  ntohl(hdr->icmp6_mtu));
+-              /*
+-               *      Drop through to notify
+-               */
++              icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
++              break;
+       case ICMPV6_DEST_UNREACH:
+-      case ICMPV6_TIME_EXCEED:
+       case ICMPV6_PARAMPROB:
++              mipv6_icmp_rcv(skb);
++      case ICMPV6_TIME_EXCEED:
+               icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
+               break;
+@@ -598,6 +602,13 @@
+       case ICMPV6_MLD2_REPORT:
+               break;
++      case MIPV6_DHAAD_REQUEST:
++      case MIPV6_DHAAD_REPLY:
++      case MIPV6_PREFIX_SOLICIT:
++      case MIPV6_PREFIX_ADV:
++              mipv6_icmp_rcv(skb);
++              break;
++
+       default:
+               if (net_ratelimit())
+                       printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
+--- linux-2.4.27/net/ipv6/ip6_fib.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/ip6_fib.c
+@@ -18,6 +18,7 @@
+  *    Yuji SEKIYA @USAGI:     Support default route on router node;
+  *                            remove ip6_null_entry from the top of
+  *                            routing table.
++ *    Ville Nuorvala:         Fixes to source address based routing
+  */
+ #include <linux/config.h>
+ #include <linux/errno.h>
+@@ -40,7 +41,6 @@
+ #include <net/ip6_route.h>
+ #define RT6_DEBUG 2
+-#undef CONFIG_IPV6_SUBTREES
+ #if RT6_DEBUG >= 3
+ #define RT6_TRACE(x...) printk(KERN_DEBUG x)
+@@ -500,6 +500,8 @@
+               mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);
+ }
++static struct rt6_info * fib6_find_prefix(struct fib6_node *fn);
++
+ /*
+  *    Add routing information to the routing tree.
+  *    <destination addr>/<source addr>
+@@ -508,17 +510,19 @@
+ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh)
+ {
+-      struct fib6_node *fn;
++      struct fib6_node *fn = root;
+       int err = -ENOMEM;
+-      fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
+-                      rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt);
++#ifdef CONFIG_IPV6_SUBTREES
++      struct fib6_node *pn = NULL;
++      fn = fib6_add_1(root, &rt->rt6i_src.addr, sizeof(struct in6_addr),
++                      rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt);
++      
+       if (fn == NULL)
+               goto out;
+-#ifdef CONFIG_IPV6_SUBTREES
+-      if (rt->rt6i_src.plen) {
++      if (rt->rt6i_dst.plen) {
+               struct fib6_node *sn;
+               if (fn->subtree == NULL) {
+@@ -546,9 +550,9 @@
+                       /* Now add the first leaf node to new subtree */
+-                      sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
+-                                      sizeof(struct in6_addr), rt->rt6i_src.plen,
+-                                      (u8*) &rt->rt6i_src - (u8*) rt);
++                      sn = fib6_add_1(sfn, &rt->rt6i_dst.addr,
++                                      sizeof(struct in6_addr), rt->rt6i_dst.plen,
++                                      (u8*) &rt->rt6i_dst - (u8*) rt);
+                       if (sn == NULL) {
+                               /* If it is failed, discard just allocated
+@@ -562,21 +566,30 @@
+                       /* Now link new subtree to main tree */
+                       sfn->parent = fn;
+                       fn->subtree = sfn;
+-                      if (fn->leaf == NULL) {
+-                              fn->leaf = rt;
+-                              atomic_inc(&rt->rt6i_ref);
+-                      }
+               } else {
+-                      sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+-                                      sizeof(struct in6_addr), rt->rt6i_src.plen,
+-                                      (u8*) &rt->rt6i_src - (u8*) rt);
++                      sn = fib6_add_1(fn->subtree, &rt->rt6i_dst.addr,
++                                      sizeof(struct in6_addr), rt->rt6i_dst.plen,
++                                      (u8*) &rt->rt6i_dst - (u8*) rt);
+                       if (sn == NULL)
+                               goto st_failure;
+               }
++              /* fib6_add_1 might have cleared the old leaf pointer */
++              if (fn->leaf == NULL) {
++                      fn->leaf = rt;
++                      atomic_inc(&rt->rt6i_ref);
++              }
++
++              pn = fn;
+               fn = sn;
+       }
++#else 
++      fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
++                      rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt);
++
++      if (fn == NULL)
++              goto out;
+ #endif
+       err = fib6_add_rt2node(fn, rt, nlh);
+@@ -588,8 +601,30 @@
+       }
+ out:
+-      if (err)
++      if (err) {
++#ifdef CONFIG_IPV6_SUBTREES
++
++              /* If fib6_add_1 has cleared the old leaf pointer in the 
++                 super-tree leaf node, we have to find a new one for it. 
++                 
++                 This situation will never arise in the sub-tree since 
++                 the node will at least have the route that caused 
++                 fib6_add_rt2node to fail.
++              */
++
++              if (pn && !(pn->fn_flags & RTN_RTINFO)) {
++                      pn->leaf = fib6_find_prefix(pn);
++#if RT6_DEBUG >= 2
++                      if (!pn->leaf) {
++                              BUG_TRAP(pn->leaf);
++                              pn->leaf = &ip6_null_entry;
++                      }
++#endif
++                      atomic_inc(&pn->leaf->rt6i_ref);
++              }
++#endif
+               dst_free(&rt->u.dst);
++      }
+       return err;
+ #ifdef CONFIG_IPV6_SUBTREES
+@@ -597,8 +632,8 @@
+          is orphan. If it is, shoot it.
+        */
+ st_failure:
+-      if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT))
+-              fib_repair_tree(fn);
++      if (fn && !(fn->fn_flags & (RTN_RTINFO | RTN_ROOT)))
++              fib6_repair_tree(fn);
+       dst_free(&rt->u.dst);
+       return err;
+ #endif
+@@ -641,22 +676,28 @@
+               break;
+       }
+-      while ((fn->fn_flags & RTN_ROOT) == 0) {
++      for (;;) {
+ #ifdef CONFIG_IPV6_SUBTREES
+               if (fn->subtree) {
+-                      struct fib6_node *st;
+-                      struct lookup_args *narg;
+-
+-                      narg = args + 1;
+-
+-                      if (narg->addr) {
+-                              st = fib6_lookup_1(fn->subtree, narg);
++                      struct rt6key *key;
+-                              if (st && !(st->fn_flags & RTN_ROOT))
+-                                      return st;
++                      key = (struct rt6key *) ((u8 *) fn->leaf +
++                                               args->offset);
++                      
++                      if (addr_match(&key->addr, args->addr, key->plen)) {
++                              struct fib6_node *st;
++                              struct lookup_args *narg = args + 1;
++                              if (!ipv6_addr_any(narg->addr)) {
++                                      st = fib6_lookup_1(fn->subtree, narg);
++                                      
++                                      if (st && !(st->fn_flags & RTN_ROOT))
++                                              return st;
++                              } 
+                       }
+               }
+ #endif
++              if (fn->fn_flags & RTN_ROOT)
++                      break;
+               if (fn->fn_flags & RTN_RTINFO) {
+                       struct rt6key *key;
+@@ -680,13 +721,22 @@
+       struct lookup_args args[2];
+       struct rt6_info *rt = NULL;
+       struct fib6_node *fn;
++#ifdef CONFIG_IPV6_SUBTREES
++      struct in6_addr saddr_buf;
++#endif
++#ifdef CONFIG_IPV6_SUBTREES
++      if (saddr == NULL) {
++              memset(&saddr_buf, 0, sizeof(struct in6_addr));
++              saddr = &saddr_buf;
++      }
++      args[0].offset = (u8*) &rt->rt6i_src - (u8*) rt;
++      args[0].addr = saddr;
++      args[1].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
++      args[1].addr = daddr;
++#else 
+       args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+       args[0].addr = daddr;
+-
+-#ifdef CONFIG_IPV6_SUBTREES
+-      args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
+-      args[1].addr = saddr;
+ #endif
+       fn = fib6_lookup_1(root, args);
+@@ -739,19 +789,25 @@
+ {
+       struct rt6_info *rt = NULL;
+       struct fib6_node *fn;
+-
+-      fn = fib6_locate_1(root, daddr, dst_len,
+-                         (u8*) &rt->rt6i_dst - (u8*) rt);
+-
+ #ifdef CONFIG_IPV6_SUBTREES
+-      if (src_len) {
+-              BUG_TRAP(saddr!=NULL);
+-              if (fn == NULL)
+-                      fn = fn->subtree;
++      struct in6_addr saddr_buf;
++
++      if (saddr == NULL) {
++              memset(&saddr_buf, 0, sizeof(struct in6_addr));
++              saddr = &saddr_buf;
++      }
++      fn = fib6_locate_1(root, saddr, src_len, 
++                         (u8*) &rt->rt6i_src - (u8*) rt);
++      if (dst_len) {
+               if (fn)
+-                      fn = fib6_locate_1(fn, saddr, src_len,
+-                                         (u8*) &rt->rt6i_src - (u8*) rt);
++                      fn = fib6_locate_1(fn->subtree, daddr, dst_len,
++                                         (u8*) &rt->rt6i_dst - (u8*) rt);
++              else 
++                      return NULL;
+       }
++#else
++      fn = fib6_locate_1(root, daddr, dst_len,
++                         (u8*) &rt->rt6i_dst - (u8*) rt);
+ #endif
+       if (fn && fn->fn_flags&RTN_RTINFO)
+@@ -939,7 +995,7 @@
+                       }
+                       fn = fn->parent;
+               }
+-              /* No more references are possiible at this point. */
++              /* No more references are possible at this point. */
+               if (atomic_read(&rt->rt6i_ref) != 1) BUG();
+       }
+--- linux-2.4.27/net/ipv6/ip6_input.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/ip6_input.c
+@@ -40,13 +40,42 @@
+ #include <net/ip6_route.h>
+ #include <net/addrconf.h>
++static inline int ip6_proxy_chk(struct sk_buff *skb)
++{
++      struct ipv6hdr *hdr = skb->nh.ipv6h;
++
++      if (ipv6_addr_type(&hdr->daddr)&IPV6_ADDR_UNICAST &&
++          pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) {
++              u8 nexthdr = hdr->nexthdr;
++              int offset;
++              struct icmp6hdr msg;
++              if (ipv6_ext_hdr(nexthdr)) {
++                      offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, 
++                                                skb->len - sizeof(*hdr));
++                      if (offset < 0)
++                              return 0;
++              } else
++                      offset = sizeof(*hdr);
++
++              /* capture unicast NUD probes on behalf of the proxied node */
++              if (nexthdr == IPPROTO_ICMPV6 &&
++                  !skb_copy_bits(skb, offset, &msg, sizeof(msg)) &&
++                  msg.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
++                      return 1;
++              }
++      }
++      return 0;
++}
++ 
+ static inline int ip6_rcv_finish( struct sk_buff *skb) 
+ {
+-      if (skb->dst == NULL)
+-              ip6_route_input(skb);
+-
++      if (skb->dst == NULL) {
++              if (ip6_proxy_chk(skb)) 
++                      return ip6_input(skb);
++              ip6_route_input(skb);
++      }
+       return skb->dst->input(skb);
+ }
+--- linux-2.4.27/net/ipv6/ip6_output.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/ip6_output.c
+@@ -50,6 +50,8 @@
+ #include <net/rawv6.h>
+ #include <net/icmp.h>
++#include <net/mipglue.h>
++
+ static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
+ {
+       static u32 ipv6_fragmentation_id = 1;
+@@ -194,7 +196,14 @@
+       u8  proto = fl->proto;
+       int seg_len = skb->len;
+       int hlimit;
++      int retval;
++      struct ipv6_txoptions *orig_opt = opt;
++
++      opt = ip6_add_mipv6_txoptions(sk, skb, orig_opt, fl, &dst);
++      if(orig_opt && !opt)
++              return -ENOMEM;
++              
+       if (opt) {
+               int head_room;
+@@ -209,8 +218,11 @@
+                       struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
+                       kfree_skb(skb);
+                       skb = skb2;
+-                      if (skb == NULL)
++                      if (skb == NULL) {
++                              ip6_free_mipv6_txoptions(opt, orig_opt);
++
+                               return -ENOBUFS;
++                      }
+                       if (sk)
+                               skb_set_owner_w(skb, sk);
+               }
+@@ -242,7 +254,10 @@
+       if (skb->len <= dst->pmtu) {
+               IP6_INC_STATS(Ip6OutRequests);
+-              return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
++              ip6_mark_mipv6_packet(opt, skb);
++              retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
++              ip6_free_mipv6_txoptions(opt, orig_opt);
++              return retval; 
+       }
+       if (net_ratelimit())
+@@ -250,6 +265,9 @@
+       skb->dev = dst->dev;
+       icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+       kfree_skb(skb);
++
++      ip6_free_mipv6_txoptions(opt, orig_opt);
++
+       return -EMSGSIZE;
+ }
+@@ -473,6 +491,7 @@
+                       IP6_INC_STATS(Ip6FragCreates);
+                       IP6_INC_STATS(Ip6OutRequests);
++                      ip6_mark_mipv6_packet(opt, skb);
+                       err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
+                       if (err) {
+                               kfree_skb(last_skb);
+@@ -499,6 +518,7 @@
+       IP6_INC_STATS(Ip6FragCreates);
+       IP6_INC_STATS(Ip6FragOKs);
+       IP6_INC_STATS(Ip6OutRequests);
++      ip6_mark_mipv6_packet(opt, last_skb);
+       return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute);
+ }
+@@ -509,26 +529,43 @@
+       struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+       struct in6_addr *final_dst = NULL;
+       struct dst_entry *dst;
++      struct rt6_info *rt;
+       int err = 0;
+       unsigned int pktlength, jumbolen, mtu;
+       struct in6_addr saddr;
++      struct ipv6_txoptions *orig_opt = opt; 
++#ifdef CONFIG_IPV6_SUBTREES
++      struct dst_entry *org_dst;
++#endif
++
++      opt = ip6_add_mipv6_txoptions(sk, NULL, orig_opt, fl, NULL);
++
++      if(orig_opt && !opt)
++              return -ENOMEM;
+       if (opt && opt->srcrt) {
+               struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+               final_dst = fl->fl6_dst;
+               fl->fl6_dst = rt0->addr;
+-      }
++      } else if (opt && opt->srcrt2) {
++                struct rt2_hdr *rt2 = (struct rt2_hdr *) opt->srcrt2;
++                final_dst = fl->fl6_dst;
++                fl->fl6_dst = &rt2->addr;
++        }
+       if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
+               fl->oif = np->mcast_oif;
+       dst = __sk_dst_check(sk, np->dst_cookie);
++#ifdef CONFIG_IPV6_SUBTREES
++      org_dst = dst;
++#endif
+       if (dst) {
+-              struct rt6_info *rt = (struct rt6_info*)dst;
++              rt = (struct rt6_info*)dst;
+                       /* Yes, checking route validity in not connected
+                          case is not very simple. Take into account,
+-                         that we do not support routing by source, TOS,
++                         that we do not support routing by TOS,
+                          and MSG_DONTROUTE            --ANK (980726)
+                          1. If route was host route, check that
+@@ -548,6 +585,13 @@
+                     ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr))
+                    && (np->daddr_cache == NULL ||
+                        ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache)))
++#ifdef CONFIG_IPV6_SUBTREES
++                  || (fl->fl6_src != NULL
++                      && (rt->rt6i_src.plen != 128 ||
++                          ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr))
++                      && (np->saddr_cache == NULL ||
++                          ipv6_addr_cmp(fl->fl6_src, np->saddr_cache)))
++#endif
+                   || (fl->oif && fl->oif != dst->dev->ifindex)) {
+                       dst = NULL;
+               } else
+@@ -560,21 +604,42 @@
+       if (dst->error) {
+               IP6_INC_STATS(Ip6OutNoRoutes);
+               dst_release(dst);
++              ip6_free_mipv6_txoptions(opt, orig_opt);
+               return -ENETUNREACH;
+       }
+       if (fl->fl6_src == NULL) {
+               err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr);
+-
+               if (err) {
+ #if IP6_DEBUG >= 2
+                       printk(KERN_DEBUG "ip6_build_xmit: "
+                              "no available source address\n");
+ #endif
++
++#ifdef CONFIG_IPV6_SUBTREES
++                      if (dst != org_dst) {
++                              dst_release(dst);
++                              dst = org_dst;
++                      }
++#endif                
+                       goto out;
+               }
+               fl->fl6_src = &saddr;
+       }
++#ifdef CONFIG_IPV6_SUBTREES
++      rt = (struct rt6_info*)dst;
++      if (dst != org_dst || rt->rt6i_src.plen != 128 ||
++          ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) {
++              dst_release(dst);
++              dst = ip6_route_output(sk, fl);
++              if (dst->error) {
++                      IP6_INC_STATS(Ip6OutNoRoutes);
++                      dst_release(dst);
++                      ip6_free_mipv6_txoptions(opt, orig_opt);
++                      return -ENETUNREACH;
++              }
++      }
++#endif
+       pktlength = length;
+       if (hlimit < 0) {
+@@ -667,6 +732,7 @@
+               if (!err) {
+                       IP6_INC_STATS(Ip6OutRequests);
++                      ip6_mark_mipv6_packet(opt, skb);
+                       err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
+               } else {
+                       err = -EFAULT;
+@@ -688,9 +754,14 @@
+        *      cleanup
+        */
+ out:
+-      ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
++      ip6_dst_store(sk, dst, 
++                    fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL,
++                    fl->nl_u.ip6_u.saddr == &np->saddr ? &np->saddr : NULL);
+       if (err > 0)
+               err = np->recverr ? net_xmit_errno(err) : 0;
++
++      ip6_free_mipv6_txoptions(opt, orig_opt);
++
+       return err;
+ }
+@@ -769,6 +840,15 @@
+               return -ETIMEDOUT;
+       }
++      /* The proxying router can't forward traffic sent to a link-local
++         address, so signal the sender and discard the packet. This
++         behavior is required by the MIPv6 specification. */
++
++      if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL && 
++          skb->dev && pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) {
++              dst_link_failure(skb);
++              goto drop;
++      }
+       /* IPv6 specs say nothing about it, but it is clear that we cannot
+          send redirects to source routed frames.
+        */
+--- linux-2.4.27/net/ipv6/ipv6_syms.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/ipv6_syms.c
+@@ -6,6 +6,8 @@
+ #include <net/ipv6.h>
+ #include <net/addrconf.h>
+ #include <net/ip6_route.h>
++#include <net/ndisc.h>
++#include <net/mipglue.h>
+ EXPORT_SYMBOL(ipv6_addr_type);
+ EXPORT_SYMBOL(icmpv6_send);
+@@ -35,3 +37,47 @@
+ EXPORT_SYMBOL(in6_dev_finish_destroy);
+ EXPORT_SYMBOL(ipv6_skip_exthdr);
++#if defined(CONFIG_IPV6_TUNNEL_MODULE) || defined(CONFIG_IPV6_MOBILITY_MODULE)
++EXPORT_SYMBOL(ip6_build_xmit);
++EXPORT_SYMBOL(rt6_lookup);
++EXPORT_SYMBOL(ipv6_ext_hdr);
++#endif
++#ifdef CONFIG_IPV6_MOBILITY_MODULE
++EXPORT_SYMBOL(mipv6_functions);
++EXPORT_SYMBOL(mipv6_invalidate_calls);
++#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE)
++EXPORT_SYMBOL(ip6_route_add);
++EXPORT_SYMBOL(ip6_route_del);
++EXPORT_SYMBOL(ipv6_get_lladdr);
++EXPORT_SYMBOL(ipv6_get_ifaddr);
++EXPORT_SYMBOL(nd_tbl);
++EXPORT_SYMBOL(ndisc_send_ns);
++EXPORT_SYMBOL(ndisc_send_na);
++EXPORT_SYMBOL(ndisc_next_option);
++EXPORT_SYMBOL(inet6_ifa_finish_destroy);
++#endif
++#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE
++EXPORT_SYMBOL(ipv6_dev_ac_dec);
++EXPORT_SYMBOL(ipv6_dev_ac_inc);
++EXPORT_SYMBOL(ipv6_dev_mc_dec);
++EXPORT_SYMBOL(ipv6_dev_mc_inc);
++EXPORT_SYMBOL(ip6_forward);
++EXPORT_SYMBOL(ip6_input);
++EXPORT_SYMBOL(ipv6_chk_acast_addr);
++#endif
++#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE
++#endif
++EXPORT_SYMBOL(addrconf_add_ifaddr);
++EXPORT_SYMBOL(addrconf_del_ifaddr);
++EXPORT_SYMBOL(addrconf_dad_start);
++EXPORT_SYMBOL(ip6_del_rt);
++EXPORT_SYMBOL(ip6_routing_table);
++EXPORT_SYMBOL(rt6_get_dflt_router);
++EXPORT_SYMBOL(rt6_purge_dflt_routers);
++EXPORT_SYMBOL(rt6_lock);
++EXPORT_SYMBOL(ndisc_send_rs);
++EXPORT_SYMBOL(fib6_clean_tree);
++EXPORT_SYMBOL(ipv6_del_addr);
++EXPORT_SYMBOL(ipv6_generate_eui64);
++EXPORT_SYMBOL(ipv6_inherit_eui64);
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/ipv6_tunnel.c
+@@ -0,0 +1,1604 @@
++/*
++ *    IPv6 over IPv6 tunnel device
++ *    Linux INET6 implementation
++ *
++ *    Authors:
++ *    Ville Nuorvala          <vnuorval@tcs.hut.fi>   
++ *
++ *    $Id$
++ *
++ *      Based on:
++ *      linux/net/ipv6/sit.c
++ *
++ *      RFC 2473
++ *
++ *    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/errno.h>
++#include <linux/types.h>
++#include <linux/socket.h>
++#include <linux/sockios.h>
++#include <linux/if.h>
++#include <linux/in.h>
++#include <linux/ip.h>
++#include <linux/if_tunnel.h>
++#include <linux/net.h>
++#include <linux/in6.h>
++#include <linux/netdevice.h>
++#include <linux/if_arp.h>
++#include <linux/icmpv6.h>
++#include <linux/init.h>
++#include <linux/route.h>
++#include <linux/rtnetlink.h>
++#include <linux/tqueue.h>
++
++#include <asm/uaccess.h>
++#include <asm/atomic.h>
++
++#include <net/sock.h>
++#include <net/ipv6.h>
++#include <net/protocol.h>
++#include <net/ip6_route.h>
++#include <net/addrconf.h>
++#include <net/ipv6_tunnel.h>
++
++MODULE_AUTHOR("Ville Nuorvala");
++MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel");
++MODULE_LICENSE("GPL");
++
++#define IPV6_TLV_TEL_DST_SIZE 8
++
++#ifdef IP6_TNL_DEBUG
++#define IP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __FUNCTION__)
++#else
++#define IP6_TNL_TRACE(x...) do {;} while(0)
++#endif
++
++#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
++
++#define HASH_SIZE  32
++
++#define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \
++                   (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \
++                    (HASH_SIZE - 1))
++
++static int ip6ip6_fb_tnl_dev_init(struct net_device *dev);
++static int ip6ip6_tnl_dev_init(struct net_device *dev);
++
++/* the IPv6 IPv6 tunnel fallback device */
++static struct net_device ip6ip6_fb_tnl_dev = {
++      name: "ip6tnl0",
++      init: ip6ip6_fb_tnl_dev_init
++};
++
++/* the IPv6 IPv6 fallback tunnel */
++static struct ip6_tnl ip6ip6_fb_tnl = {
++      dev: &ip6ip6_fb_tnl_dev,
++      parms:{name: "ip6tnl0", proto: IPPROTO_IPV6}
++};
++
++/* lists for storing tunnels in use */
++static struct ip6_tnl *tnls_r_l[HASH_SIZE];
++static struct ip6_tnl *tnls_wc[1];
++static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l };
++
++/* list for unused cached kernel tunnels */
++static struct ip6_tnl *tnls_kernel[1];
++/* maximum number of cached kernel tunnels */
++static unsigned int max_kdev_count = 0;
++/* minimum number of cached kernel tunnels */
++static unsigned int min_kdev_count = 0;
++/* current number of cached kernel tunnels */
++static unsigned int kdev_count = 0;
++
++/* lists for tunnel hook functions */
++static struct list_head hooks[IP6_TNL_MAXHOOKS];
++
++/* locks for the different lists */
++static rwlock_t ip6ip6_lock = RW_LOCK_UNLOCKED;
++static rwlock_t ip6ip6_kernel_lock = RW_LOCK_UNLOCKED;
++static rwlock_t ip6ip6_hook_lock = RW_LOCK_UNLOCKED;
++
++/* flag indicating if the module is being removed */
++static int shutdown = 0;
++
++/**
++ * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses
++ *   @remote: the address of the tunnel exit-point 
++ *   @local: the address of the tunnel entry-point 
++ *
++ * Return:  
++ *   tunnel matching given end-points if found,
++ *   else fallback tunnel if its device is up, 
++ *   else %NULL
++ **/
++
++struct ip6_tnl *
++ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local)
++{
++      unsigned h0 = HASH(remote);
++      unsigned h1 = HASH(local);
++      struct ip6_tnl *t;
++
++      for (t = tnls_r_l[h0 ^ h1]; t; t = t->next) {
++              if (!ipv6_addr_cmp(local, &t->parms.laddr) &&
++                  !ipv6_addr_cmp(remote, &t->parms.raddr) &&
++                  (t->dev->flags & IFF_UP))
++                      return t;
++      }
++      if ((t = tnls_wc[0]) != NULL && (t->dev->flags & IFF_UP))
++              return t;
++
++      return NULL;
++}
++
++/**
++ * ip6ip6_bucket - get head of list matching given tunnel parameters
++ *   @p: parameters containing tunnel end-points 
++ *
++ * Description:
++ *   ip6ip6_bucket() returns the head of the list matching the 
++ *   &struct in6_addr entries laddr and raddr in @p.
++ *
++ * Return: head of IPv6 tunnel list 
++ **/
++
++static struct ip6_tnl **
++ip6ip6_bucket(struct ip6_tnl_parm *p)
++{
++      struct in6_addr *remote = &p->raddr;
++      struct in6_addr *local = &p->laddr;
++      unsigned h = 0;
++      int prio = 0;
++
++      if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) {
++              prio = 1;
++              h = HASH(remote) ^ HASH(local);
++      }
++      return &tnls[prio][h];
++}
++
++/**
++ * ip6ip6_kernel_tnl_link - add new kernel tunnel to cache
++ *   @t: kernel tunnel
++ *
++ * Note:
++ *   %IP6_TNL_F_KERNEL_DEV is assumed to be raised in t->parms.flags. 
++ *   See the comments on ip6ip6_kernel_tnl_add() for more information.
++ **/
++
++static inline void
++ip6ip6_kernel_tnl_link(struct ip6_tnl *t)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      t->next = tnls_kernel[0];
++      tnls_kernel[0] = t;
++      kdev_count++;
++      write_unlock_bh(&ip6ip6_kernel_lock);
++}
++
++/**
++ * ip6ip6_kernel_tnl_unlink - remove first kernel tunnel from cache
++ *
++ * Return: first free kernel tunnel
++ *
++ * Note:
++ *   See the comments on ip6ip6_kernel_tnl_add() for more information.
++ **/
++
++static inline struct ip6_tnl *
++ip6ip6_kernel_tnl_unlink(void)
++{
++      struct ip6_tnl *t;
++
++      write_lock_bh(&ip6ip6_kernel_lock);
++      if ((t = tnls_kernel[0]) != NULL) {
++              tnls_kernel[0] = t->next;
++              kdev_count--;
++      }
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      return t;
++}
++
++/**
++ * ip6ip6_tnl_link - add tunnel to hash table
++ *   @t: tunnel to be added
++ **/
++
++static void
++ip6ip6_tnl_link(struct ip6_tnl *t)
++{
++      struct ip6_tnl **tp = ip6ip6_bucket(&t->parms);
++
++      write_lock_bh(&ip6ip6_lock);
++      t->next = *tp;
++      *tp = t;
++      write_unlock_bh(&ip6ip6_lock);
++}
++
++/**
++ * ip6ip6_tnl_unlink - remove tunnel from hash table
++ *   @t: tunnel to be removed
++ **/
++
++static void
++ip6ip6_tnl_unlink(struct ip6_tnl *t)
++{
++      struct ip6_tnl **tp;
++      
++      write_lock_bh(&ip6ip6_lock);
++      for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) {
++              if (t == *tp) {
++                      *tp = t->next;
++                      break;
++              }
++      }
++      write_unlock_bh(&ip6ip6_lock);
++}
++
++/**
++ * ip6ip6_tnl_create() - create a new tunnel
++ *   @p: tunnel parameters
++ *   @pt: pointer to new tunnel
++ *
++ * Description:
++ *   Create tunnel matching given parameters. New kernel managed devices are 
++ *   not put in the normal hash structure, but are instead cached for later
++ *   use.
++ * 
++ * Return: 
++ *   0 on success
++ **/
++
++
++static int __ip6ip6_tnl_create(struct ip6_tnl_parm *p,
++                             struct ip6_tnl **pt,
++                             int kernel_list)
++{
++      struct net_device *dev;
++      int err = -ENOBUFS;
++      struct ip6_tnl *t;
++
++      MOD_INC_USE_COUNT;
++      dev = kmalloc(sizeof (*dev) + sizeof (*t), GFP_KERNEL);
++      if (!dev) {
++              MOD_DEC_USE_COUNT;
++              return err;
++      }
++      memset(dev, 0, sizeof (*dev) + sizeof (*t));
++      dev->priv = (void *) (dev + 1);
++      t = (struct ip6_tnl *) dev->priv;
++      t->dev = dev;
++      dev->init = ip6ip6_tnl_dev_init;
++      dev->features |= NETIF_F_DYNALLOC;
++      if (kernel_list) {
++              memcpy(t->parms.name, p->name, IFNAMSIZ - 1);
++              t->parms.proto = IPPROTO_IPV6;
++              t->parms.flags = IP6_TNL_F_KERNEL_DEV;
++      } else {
++              memcpy(&t->parms, p, sizeof (*p));
++      }
++      t->parms.name[IFNAMSIZ - 1] = '\0';
++      strcpy(dev->name, t->parms.name);
++      if (!dev->name[0]) {
++              int i;
++              for (i = 0; i < IP6_TNL_MAX; i++) {
++                      sprintf(dev->name, "ip6tnl%d", i);
++                      if (__dev_get_by_name(dev->name) == NULL)
++                              break;
++              }
++
++              if (i == IP6_TNL_MAX) {
++                      goto failed;
++              }
++              memcpy(t->parms.name, dev->name, IFNAMSIZ);
++      }
++      if ((err = register_netdevice(dev)) < 0) {
++              goto failed;
++      }
++      dev_hold(dev);
++      if (kernel_list) {
++              ip6ip6_kernel_tnl_link(t);
++      } else {
++              ip6ip6_tnl_link(t);
++      }
++      *pt = t;
++      return 0;
++failed:
++      kfree(dev);
++      MOD_DEC_USE_COUNT;
++      return err;
++}
++
++
++int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt)
++{
++      return __ip6ip6_tnl_create(p, pt, 0);
++}
++
++
++static void manage_kernel_tnls(void *foo);
++
++static struct tq_struct manager_task = {
++      routine:manage_kernel_tnls,
++      data:NULL
++};
++
++/**
++ * manage_kernel_tnls() - create and destroy kernel tunnels
++ *
++ * Description:
++ *   manage_kernel_tnls() creates new kernel devices if there
++ *   are less than $min_kdev_count of them and deletes old ones if
++ *   there are less than $max_kdev_count of them in the cache
++ *
++ * Note:
++ *   Schedules itself to be run later in process context if called from 
++ *   interrupt. Therefore only works synchronously when called from process 
++ *   context.
++ **/
++
++static void
++manage_kernel_tnls(void *foo)
++{
++      struct ip6_tnl *t = NULL;
++      struct ip6_tnl_parm parm;
++
++      /* We can't do this processing in interrupt 
++         context so schedule it for later */
++      if (in_interrupt()) {
++              read_lock(&ip6ip6_kernel_lock);
++              if (!shutdown &&
++                  (kdev_count < min_kdev_count ||
++                   kdev_count > max_kdev_count)) {
++                      schedule_task(&manager_task);
++              }
++              read_unlock(&ip6ip6_kernel_lock);
++              return;
++      }
++
++      rtnl_lock();
++      read_lock_bh(&ip6ip6_kernel_lock);
++      memset(&parm, 0, sizeof (parm));
++      parm.flags = IP6_TNL_F_KERNEL_DEV;
++      /* Create tunnels until there are at least min_kdev_count */
++      while (kdev_count < min_kdev_count) {
++              read_unlock_bh(&ip6ip6_kernel_lock);
++              if (!__ip6ip6_tnl_create(&parm, &t, 1)) {
++                      dev_open(t->dev);
++              } else {
++                      goto err;
++              }
++              read_lock_bh(&ip6ip6_kernel_lock);
++      }
++
++      /* Destroy tunnels until there are at most max_kdev_count */
++      while (kdev_count > max_kdev_count) {
++              read_unlock_bh(&ip6ip6_kernel_lock);
++              if ((t = ip6ip6_kernel_tnl_unlink()) != NULL) {
++                      unregister_netdevice(t->dev);
++              } else {
++                      goto err;
++              }
++              read_lock_bh(&ip6ip6_kernel_lock);
++      }
++      read_unlock_bh(&ip6ip6_kernel_lock);
++err:
++      rtnl_unlock();
++}
++
++/**
++ * ip6ip6_tnl_inc_max_kdev_count() - increase max kernel dev cache size
++ *   @n: size increase
++ * Description:
++ *   Increase the upper limit for the number of kernel devices allowed in the 
++ *   cache at any on time.
++ **/
++
++unsigned int
++ip6ip6_tnl_inc_max_kdev_count(unsigned int n)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      max_kdev_count += n;
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      manage_kernel_tnls(NULL);
++      return max_kdev_count;
++}
++
++/**
++ * ip6ip6_tnl_dec_max_kdev_count() -  decrease max kernel dev cache size
++ *   @n: size decrement
++ * Description:
++ *   Decrease the upper limit for the number of kernel devices allowed in the 
++ *   cache at any on time.
++ **/
++
++unsigned int
++ip6ip6_tnl_dec_max_kdev_count(unsigned int n)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      max_kdev_count -= min(max_kdev_count, n);
++      if (max_kdev_count < min_kdev_count)
++              min_kdev_count = max_kdev_count;
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      manage_kernel_tnls(NULL);
++      return max_kdev_count;
++}
++
++/**
++ * ip6ip6_tnl_inc_min_kdev_count() - increase min kernel dev cache size
++ *   @n: size increase
++ * Description:
++ *   Increase the lower limit for the number of kernel devices allowed in the 
++ *   cache at any on time.
++ **/
++
++unsigned int
++ip6ip6_tnl_inc_min_kdev_count(unsigned int n)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      min_kdev_count += n;
++      if (min_kdev_count > max_kdev_count)
++              max_kdev_count = min_kdev_count;
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      manage_kernel_tnls(NULL);
++      return min_kdev_count;
++}
++
++/**
++ * ip6ip6_tnl_dec_min_kdev_count() -  decrease min kernel dev cache size
++ *   @n: size decrement
++ * Description:
++ *   Decrease the lower limit for the number of kernel devices allowed in the 
++ *   cache at any on time.
++ **/
++
++unsigned int
++ip6ip6_tnl_dec_min_kdev_count(unsigned int n)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      min_kdev_count -= min(min_kdev_count, n);
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      manage_kernel_tnls(NULL);
++      return min_kdev_count;
++}
++
++/**
++ * ip6ip6_tnl_locate - find or create tunnel matching given parameters
++ *   @p: tunnel parameters 
++ *   @create: != 0 if allowed to create new tunnel if no match found
++ *
++ * Description:
++ *   ip6ip6_tnl_locate() first tries to locate an existing tunnel
++ *   based on @parms. If this is unsuccessful, but @create is set a new
++ *   tunnel device is created and registered for use.
++ *
++ * Return:
++ *   0 if tunnel located or created,
++ *   -EINVAL if parameters incorrect,
++ *   -ENODEV if no matching tunnel available
++ **/
++
++int ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create)
++{
++      struct in6_addr *remote = &p->raddr;
++      struct in6_addr *local = &p->laddr;
++      struct ip6_tnl *t;
++
++      if (p->proto != IPPROTO_IPV6)
++              return -EINVAL;
++
++      for (t = *ip6ip6_bucket(p); t; t = t->next) {
++              if (!ipv6_addr_cmp(local, &t->parms.laddr) &&
++                  !ipv6_addr_cmp(remote, &t->parms.raddr)) {
++                      *pt = t;
++                      return (create ? -EEXIST : 0);
++              }
++      }
++      return ip6ip6_tnl_create(p, pt);
++}
++
++/**
++ * ip6ip6_tnl_dev_destructor - tunnel device destructor
++ *   @dev: the device to be destroyed
++ **/
++
++static void
++ip6ip6_tnl_dev_destructor(struct net_device *dev)
++{
++      if (dev != &ip6ip6_fb_tnl_dev) {
++              MOD_DEC_USE_COUNT;
++      }
++}
++
++/**
++ * ip6ip6_tnl_dev_uninit - tunnel device uninitializer
++ *   @dev: the device to be destroyed
++ *   
++ * Description:
++ *   ip6ip6_tnl_dev_uninit() removes tunnel from its list
++ **/
++
++static void
++ip6ip6_tnl_dev_uninit(struct net_device *dev)
++{
++      struct ip6_tnl *t = (struct ip6_tnl *) dev->priv;
++
++      if (dev == &ip6ip6_fb_tnl_dev) {
++              write_lock_bh(&ip6ip6_lock);
++              tnls_wc[0] = NULL;
++              write_unlock_bh(&ip6ip6_lock);
++      } else {
++              ip6ip6_tnl_unlink(t);
++      }
++      sock_release(t->sock);
++      dev_put(dev);
++}
++
++/**
++ * parse_tvl_tnl_enc_lim - handle encapsulation limit option
++ *   @skb: received socket buffer
++ *
++ * Return: 
++ *   0 if none was found, 
++ *   else index to encapsulation limit
++ **/
++
++static __u16
++parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
++{
++      struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw;
++      __u8 nexthdr = ipv6h->nexthdr;
++      __u16 off = sizeof (*ipv6h);
++
++      while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) {
++              __u16 optlen = 0;
++              struct ipv6_opt_hdr *hdr;
++              if (raw + off + sizeof (*hdr) > skb->data &&
++                  !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr)))
++                      break;
++
++              hdr = (struct ipv6_opt_hdr *) (raw + off);
++              if (nexthdr == NEXTHDR_FRAGMENT) {
++                      struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr;
++                      if (frag_hdr->frag_off)
++                              break;
++                      optlen = 8;
++              } else if (nexthdr == NEXTHDR_AUTH) {
++                      optlen = (hdr->hdrlen + 2) << 2;
++              } else {
++                      optlen = ipv6_optlen(hdr);
++              }
++              if (nexthdr == NEXTHDR_DEST) {
++                      __u16 i = off + 2;
++                      while (1) {
++                              struct ipv6_tlv_tnl_enc_lim *tel;
++
++                              /* No more room for encapsulation limit */
++                              if (i + sizeof (*tel) > off + optlen)
++                                      break;
++
++                              tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i];
++                              /* return index of option if found and valid */
++                              if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT &&
++                                  tel->length == 1)
++                                      return i;
++                              /* else jump to next option */
++                              if (tel->type)
++                                      i += tel->length + 2;
++                              else
++                                      i++;
++                      }
++              }
++              nexthdr = hdr->nexthdr;
++              off += optlen;
++      }
++      return 0;
++}
++
++/**
++ * ip6ip6_err - tunnel error handler
++ *
++ * Description:
++ *   ip6ip6_err() should handle errors in the tunnel according
++ *   to the specifications in RFC 2473.
++ **/
++
++void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
++                 int type, int code, int offset, __u32 info)
++{
++      struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data;
++      struct ip6_tnl *t;
++      int rel_msg = 0;
++      int rel_type = ICMPV6_DEST_UNREACH;
++      int rel_code = ICMPV6_ADDR_UNREACH;
++      __u32 rel_info = 0;
++      __u16 len;
++
++      /* If the packet doesn't contain the original IPv6 header we are 
++         in trouble since we might need the source address for furter 
++         processing of the error. */
++
++      read_lock(&ip6ip6_lock);
++      if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL)
++              goto out;
++
++      switch (type) {
++              __u32 teli;
++              struct ipv6_tlv_tnl_enc_lim *tel;
++              __u32 mtu;
++      case ICMPV6_DEST_UNREACH:
++              if (net_ratelimit())
++                      printk(KERN_WARNING
++                             "%s: Path to destination invalid "
++                             "or inactive!\n", t->parms.name);
++              rel_msg = 1;
++              break;
++      case ICMPV6_TIME_EXCEED:
++              if (code == ICMPV6_EXC_HOPLIMIT) {
++                      if (net_ratelimit())
++                              printk(KERN_WARNING
++                                     "%s: Too small hop limit or "
++                                     "routing loop in tunnel!\n", 
++                                     t->parms.name);
++                      rel_msg = 1;
++              }
++              break;
++      case ICMPV6_PARAMPROB:
++              /* ignore if parameter problem not caused by a tunnel
++                 encapsulation limit sub-option */
++              if (code != ICMPV6_HDR_FIELD) {
++                      break;
++              }
++              teli = parse_tlv_tnl_enc_lim(skb, skb->data);
++
++              if (teli && teli == ntohl(info) - 2) {
++                      tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
++                      if (tel->encap_limit == 0) {
++                              if (net_ratelimit())
++                                      printk(KERN_WARNING
++                                             "%s: Too small encapsulation "
++                                             "limit or routing loop in "
++                                             "tunnel!\n", t->parms.name);
++                              rel_msg = 1;
++                      }
++              }
++              break;
++      case ICMPV6_PKT_TOOBIG:
++              mtu = ntohl(info) - offset;
++              if (mtu < IPV6_MIN_MTU)
++                      mtu = IPV6_MIN_MTU;
++              t->dev->mtu = mtu;
++
++              if ((len = sizeof (*ipv6h) + ipv6h->payload_len) > mtu) {
++                      rel_type = ICMPV6_PKT_TOOBIG;
++                      rel_code = 0;
++                      rel_info = mtu;
++                      rel_msg = 1;
++              }
++              break;
++      }
++      if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) {
++              struct rt6_info *rt;
++              struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
++              if (!skb2)
++                      goto out;
++
++              dst_release(skb2->dst);
++              skb2->dst = NULL;
++              skb_pull(skb2, offset);
++              skb2->nh.raw = skb2->data;
++
++              /* Try to guess incoming interface */
++              rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0);
++
++              if (rt && rt->rt6i_dev)
++                      skb2->dev = rt->rt6i_dev;
++
++              icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);
++
++              if (rt)
++                      dst_release(&rt->u.dst);
++
++              kfree_skb(skb2);
++      }
++out:
++      read_unlock(&ip6ip6_lock);
++}
++
++/**
++ * call_hooks - call ipv6 tunnel hooks
++ *   @hooknum: hook number, either %IP6_TNL_PRE_ENCAP, or 
++ *   %IP6_TNL_PRE_DECAP
++ *   @t: the current tunnel
++ *   @skb: the tunneled packet
++ *
++ * Description:
++ *   Pass packet to all the hook functions until %IP6_TNL_DROP
++ *
++ * Return:
++ *   %IP6_TNL_ACCEPT or %IP6_TNL_DROP
++ **/
++
++static inline int
++call_hooks(unsigned int hooknum, struct ip6_tnl *t, struct sk_buff *skb)
++{
++      struct ip6_tnl_hook_ops *h;
++      int accept = IP6_TNL_ACCEPT;
++
++      if (hooknum < IP6_TNL_MAXHOOKS) {
++              struct list_head *i;
++              read_lock(&ip6ip6_hook_lock);
++              for (i = hooks[hooknum].next; i != &hooks[hooknum]; i = i->next) {
++                      h = (struct ip6_tnl_hook_ops *) i;
++
++                      if (h->hook) {
++                              accept = h->hook(t, skb);
++
++                              if (accept != IP6_TNL_ACCEPT)
++                                      break;
++                      }
++              }
++              read_unlock(&ip6ip6_hook_lock);
++      }
++      return accept;
++}
++
++/**
++ * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally
++ *   @skb: received socket buffer
++ *
++ * Return: 0
++ **/
++
++int ip6ip6_rcv(struct sk_buff *skb)
++{
++      struct ipv6hdr *ipv6h;
++      struct ip6_tnl *t;
++
++      if (!pskb_may_pull(skb, sizeof (*ipv6h)))
++              goto discard;
++
++      ipv6h = skb->nh.ipv6h;
++
++      read_lock(&ip6ip6_lock);
++
++      if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) {
++              if (!(t->parms.flags & IP6_TNL_F_CAP_RCV) ||
++                  call_hooks(IP6_TNL_PRE_DECAP, t, skb) != IP6_TNL_ACCEPT) {
++                      t->stat.rx_dropped++;
++                      read_unlock(&ip6ip6_lock);
++                      goto discard;
++              }
++              skb->mac.raw = skb->nh.raw;
++              skb->nh.raw = skb->data;
++              skb->protocol = htons(ETH_P_IPV6);
++              skb->pkt_type = PACKET_HOST;
++              memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
++              skb->dev = t->dev;
++              dst_release(skb->dst);
++              skb->dst = NULL;
++              t->stat.rx_packets++;
++              t->stat.rx_bytes += skb->len;
++              netif_rx(skb);
++              read_unlock(&ip6ip6_lock);
++              return 0;
++      }
++      read_unlock(&ip6ip6_lock);
++      icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
++discard:
++      kfree_skb(skb);
++      return 0;
++}
++
++static inline struct ipv6_txoptions *create_tel(__u8 encap_limit)
++{
++      struct ipv6_tlv_tnl_enc_lim *tel;
++      struct ipv6_txoptions *opt;
++      __u8 *raw;
++
++      int opt_len = sizeof(*opt) + IPV6_TLV_TEL_DST_SIZE;
++
++      if (!(opt = kmalloc(opt_len, GFP_ATOMIC))) {
++              return NULL;
++      }
++      memset(opt, 0, opt_len);
++      opt->tot_len = opt_len;
++      opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1);
++      opt->opt_nflen = 8;
++
++      tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1);
++      tel->type = IPV6_TLV_TNL_ENCAP_LIMIT;
++      tel->length = 1;
++      tel->encap_limit = encap_limit;
++
++      raw = (__u8 *) opt->dst0opt;
++      raw[5] = IPV6_TLV_PADN;
++      raw[6] = 1;
++
++      return opt;
++}
++
++static int
++ip6ip6_getfrag(const void *data, struct in6_addr *addr,
++                char *buff, unsigned int offset, unsigned int len)
++{
++      memcpy(buff, data + offset, len);
++      return 0;
++}
++
++/**
++ * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
++ *   @t: the outgoing tunnel device
++ *   @hdr: IPv6 header from the incoming packet 
++ *
++ * Description:
++ *   Avoid trivial tunneling loop by checking that tunnel exit-point 
++ *   doesn't match source of incoming packet.
++ *
++ * Return: 
++ *   1 if conflict,
++ *   0 else
++ **/
++
++static inline int
++ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr)
++{
++      return !ipv6_addr_cmp(&t->parms.raddr, &hdr->saddr);
++}
++
++/**
++ * ip6ip6_tnl_xmit - encapsulate packet and send 
++ *   @skb: the outgoing socket buffer
++ *   @dev: the outgoing tunnel device 
++ *
++ * Description:
++ *   Build new header and do some sanity checks on the packet before sending
++ *   it to ip6_build_xmit().
++ *
++ * Return: 
++ *   0
++ **/
++
++int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      struct ip6_tnl *t = (struct ip6_tnl *) dev->priv;
++      struct net_device_stats *stats = &t->stat;
++      struct ipv6hdr *ipv6h = skb->nh.ipv6h;
++      struct ipv6_txoptions *opt = NULL;
++      int encap_limit = -1;
++      __u16 offset;
++      struct flowi fl;
++      int err = 0;
++      struct dst_entry *dst;
++      struct sock *sk = t->sock->sk;
++      struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
++      int mtu;
++
++      if (t->recursion++) {
++              stats->collisions++;
++              goto tx_err;
++      }
++      if (skb->protocol != htons(ETH_P_IPV6) ||
++          !(t->parms.flags & IP6_TNL_F_CAP_XMIT) ||
++          ip6ip6_tnl_addr_conflict(t, ipv6h)) {
++              goto tx_err;
++      }
++      if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) {
++              struct ipv6_tlv_tnl_enc_lim *tel;
++              tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset];
++              if (tel->encap_limit == 0) {
++                      icmpv6_send(skb, ICMPV6_PARAMPROB,
++                                  ICMPV6_HDR_FIELD, offset + 2, skb->dev);
++                      goto tx_err;
++              }
++              encap_limit = tel->encap_limit - 1;
++      } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) {
++              encap_limit = t->parms.encap_limit;
++      }
++      if (call_hooks(IP6_TNL_PRE_ENCAP, t, skb) != IP6_TNL_ACCEPT)
++              goto discard;
++      memcpy(&fl, &t->fl, sizeof (fl));
++
++      if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS))
++              fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK);
++      if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL))
++              fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK);
++
++      if (encap_limit >= 0 && (opt = create_tel(encap_limit)) == NULL)
++              goto tx_err;
++
++      dst = __sk_dst_check(sk, np->dst_cookie);
++
++      if (dst) {
++              if (np->daddr_cache == NULL ||
++                  ipv6_addr_cmp(fl.fl6_dst, np->daddr_cache) ||
++#ifdef CONFIG_IPV6_SUBTREES
++                  np->saddr_cache == NULL ||
++                  ipv6_addr_cmp(fl.fl6_src, np->saddr_cache) ||
++#endif
++                  (fl.oif && fl.oif != dst->dev->ifindex)) {
++                      dst = NULL;
++              } else {
++                      dst_hold(dst);
++              }
++      }
++      if (dst == NULL) {
++              dst = ip6_route_output(sk, &fl);
++              if (dst->error) {
++                      stats->tx_carrier_errors++;
++                      dst_link_failure(skb);
++                      goto tx_err_dst_release;
++              }
++              /* local routing loop */
++              if (dst->dev == dev) {
++                      stats->collisions++;
++                      if (net_ratelimit())
++                              printk(KERN_WARNING 
++                                     "%s: Local routing loop detected!\n",
++                                     t->parms.name);
++                      goto tx_err_dst_release;
++              }
++      }
++      mtu = dst->pmtu - sizeof (*ipv6h);
++      if (opt) {
++              mtu -= (opt->opt_nflen + opt->opt_flen);
++      }
++      if (mtu < IPV6_MIN_MTU)
++              mtu = IPV6_MIN_MTU;
++      if (skb->dst && mtu < skb->dst->pmtu) {
++              struct rt6_info *rt = (struct rt6_info *) skb->dst;
++              rt->rt6i_flags |= RTF_MODIFIED;
++              rt->u.dst.pmtu = mtu;
++      }
++      if (skb->len > mtu) {
++              icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
++              goto tx_err_dst_release;
++      }
++      ip6_dst_store(sk, dst, &np->daddr, &np->saddr);
++      err = ip6_build_xmit(sk, ip6ip6_getfrag, (void *) skb->nh.raw,
++                           &fl, skb->len, opt, t->parms.hop_limit,
++                           MSG_DONTWAIT);
++
++      if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) {
++              stats->tx_bytes += skb->len;
++              stats->tx_packets++;
++      } else {
++              stats->tx_errors++;
++              stats->tx_aborted_errors++;
++      }
++      if (opt)
++              kfree(opt);
++      kfree_skb(skb);
++      t->recursion--;
++      return 0;
++tx_err_dst_release:
++      dst_release(dst);
++      if (opt)
++              kfree(opt);
++tx_err:
++      stats->tx_errors++;
++discard:
++      stats->tx_dropped++;
++      kfree_skb(skb);
++      t->recursion--;
++      return 0;
++}
++
++static void ip6_tnl_set_cap(struct ip6_tnl *t)
++{
++      struct ip6_tnl_parm *p = &t->parms;
++      struct in6_addr *laddr = &p->laddr;
++      struct in6_addr *raddr = &p->raddr;
++      int ltype = ipv6_addr_type(laddr);
++      int rtype = ipv6_addr_type(raddr);
++
++      p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV);
++
++      if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY &&
++          ((ltype|rtype) &
++           (IPV6_ADDR_UNICAST|
++            IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL|
++            IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) {
++              struct net_device *ldev = NULL;
++              int l_ok = 1;
++              int r_ok = 1;
++
++              if (p->link)
++                      ldev = dev_get_by_index(p->link);
++
++              if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev))
++                      l_ok = 0;
++
++              if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL))
++                      r_ok = 0;
++
++              if (l_ok && r_ok) {
++                      if (ltype&IPV6_ADDR_UNICAST)
++                              p->flags |= IP6_TNL_F_CAP_XMIT;
++                      if (rtype&IPV6_ADDR_UNICAST)
++                              p->flags |= IP6_TNL_F_CAP_RCV;
++              }
++              if (ldev)
++                      dev_put(ldev);
++      }
++}
++
++static void ip6ip6_tnl_link_config(struct ip6_tnl *t)
++{
++      struct net_device *dev = t->dev;
++      struct ip6_tnl_parm *p = &t->parms;
++      struct flowi *fl = &t->fl;
++
++      /* Set up flowi template */
++      fl->fl6_src = &p->laddr;
++      fl->fl6_dst = &p->raddr;
++      fl->oif = p->link;
++      fl->fl6_flowlabel = 0;
++
++      if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
++              fl->fl6_flowlabel |= IPV6_TCLASS_MASK & htonl(p->flowinfo);
++      if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
++              fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & htonl(p->flowinfo);
++
++      ip6_tnl_set_cap(t);
++
++      if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV)
++              dev->flags |= IFF_POINTOPOINT;
++      else
++              dev->flags &= ~IFF_POINTOPOINT;
++
++      if (p->flags & IP6_TNL_F_CAP_XMIT) {
++              struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr,
++                                               p->link, 0);
++              
++              if (rt == NULL)
++                      return;
++              
++              if (rt->rt6i_dev) {
++                      dev->iflink = rt->rt6i_dev->ifindex;
++
++                      dev->hard_header_len = rt->rt6i_dev->hard_header_len +
++                              sizeof (struct ipv6hdr);
++
++                      dev->mtu = rt->rt6i_dev->mtu - sizeof (struct ipv6hdr);
++
++                      if (dev->mtu < IPV6_MIN_MTU)
++                              dev->mtu = IPV6_MIN_MTU;
++              }
++              dst_release(&rt->u.dst);
++      }
++}
++
++/**
++ * __ip6ip6_tnl_change - update the tunnel parameters
++ *   @t: tunnel to be changed
++ *   @p: tunnel configuration parameters
++ *
++ * Description:
++ *   __ip6ip6_tnl_change() updates the tunnel parameters
++ **/
++
++static void
++__ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
++{
++      ipv6_addr_copy(&t->parms.laddr, &p->laddr);
++      ipv6_addr_copy(&t->parms.raddr, &p->raddr);
++      t->parms.flags = p->flags;
++      t->parms.hop_limit = p->hop_limit;
++      t->parms.encap_limit = p->encap_limit;
++      t->parms.flowinfo = p->flowinfo;
++      ip6ip6_tnl_link_config(t);
++}
++
++void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
++{
++      ip6ip6_tnl_unlink(t);
++      __ip6ip6_tnl_change(t, p);
++      ip6ip6_tnl_link(t);
++}
++
++/**
++ * ip6ip6_kernel_tnl_add - configure and add kernel tunnel to hash 
++ *   @p: kernel tunnel configuration parameters
++ *
++ * Description:
++ *   ip6ip6_kernel_tnl_add() fetches an unused kernel tunnel configures
++ *   it according to @p and places it among the active tunnels.
++ * 
++ * Return:
++ *   number of references to tunnel on success,
++ *   %-EEXIST if there is already a device matching description
++ *   %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised,
++ *   %-ENODEV if there are no unused kernel tunnels available 
++ * 
++ * Note:
++ *   The code for creating, opening, closing and destroying network devices
++ *   must be called from process context, while the Mobile IP code, which 
++ *   needs the tunnel devices, unfortunately runs in interrupt context. 
++ *   
++ *   The devices must be created and opened in advance, then placed in a 
++ *   list where the kernel can fetch and ready them for use at a later time.
++ *
++ **/
++
++int
++ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p)
++{
++      struct ip6_tnl *t;
++
++      if (!(p->flags & IP6_TNL_F_KERNEL_DEV))
++              return -EINVAL;
++      if ((t = ip6ip6_tnl_lookup(&p->raddr, &p->laddr)) != NULL &&
++          t != &ip6ip6_fb_tnl) {
++              /* Handle duplicate tunnels by incrementing 
++                 reference count */
++              atomic_inc(&t->refcnt);
++              goto out;
++      }
++      if ((t = ip6ip6_kernel_tnl_unlink()) == NULL)
++              return -ENODEV;
++      __ip6ip6_tnl_change(t, p);
++
++      atomic_inc(&t->refcnt);
++
++      ip6ip6_tnl_link(t);
++
++      manage_kernel_tnls(NULL);
++out:
++      return atomic_read(&t->refcnt);
++}
++
++/**
++ * ip6ip6_kernel_tnl_del - delete no longer needed kernel tunnel 
++ *   @t: kernel tunnel to be removed from hash
++ *
++ * Description:
++ *   ip6ip6_kernel_tnl_del() removes and deconfigures the tunnel @t
++ *   and places it among the unused kernel devices.
++ * 
++ * Return:
++ *   number of references on success,
++ *   %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised,
++ * 
++ * Note:
++ *   See the comments on ip6ip6_kernel_tnl_add() for more information.
++ **/
++
++int
++ip6ip6_kernel_tnl_del(struct ip6_tnl *t)
++{
++      if (!t)
++              return -ENODEV;
++
++      if (!(t->parms.flags & IP6_TNL_F_KERNEL_DEV))
++              return -EINVAL;
++
++      if (atomic_dec_and_test(&t->refcnt)) {
++              struct ip6_tnl_parm p;
++              ip6ip6_tnl_unlink(t);
++              memset(&p, 0, sizeof (p));
++              p.flags = IP6_TNL_F_KERNEL_DEV;
++
++              __ip6ip6_tnl_change(t, &p);
++
++              ip6ip6_kernel_tnl_link(t);
++
++              manage_kernel_tnls(NULL);
++      }
++      return atomic_read(&t->refcnt);
++}
++
++/**
++ * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace 
++ *   @dev: virtual device associated with tunnel
++ *   @ifr: parameters passed from userspace
++ *   @cmd: command to be performed
++ *
++ * Description:
++ *   ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels 
++ *   from userspace. 
++ *
++ *   The possible commands are the following:
++ *     %SIOCGETTUNNEL: get tunnel parameters for device
++ *     %SIOCADDTUNNEL: add tunnel matching given tunnel parameters
++ *     %SIOCCHGTUNNEL: change tunnel parameters to those given
++ *     %SIOCDELTUNNEL: delete tunnel
++ *
++ *   The fallback device "ip6tnl0", created during module 
++ *   initialization, can be used for creating other tunnel devices.
++ *
++ * Return:
++ *   0 on success,
++ *   %-EFAULT if unable to copy data to or from userspace,
++ *   %-EPERM if current process hasn't %CAP_NET_ADMIN set or attempting
++ *   to configure kernel devices from userspace, 
++ *   %-EINVAL if passed tunnel parameters are invalid,
++ *   %-EEXIST if changing a tunnel's parameters would cause a conflict
++ *   %-ENODEV if attempting to change or delete a nonexisting device
++ *
++ * Note:
++ *   See the comments on ip6ip6_kernel_tnl_add() for more information 
++ *   about kernel tunnels.
++ * **/
++
++static int
++ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
++{
++      int err = 0;
++      int create;
++      struct ip6_tnl_parm p;
++      struct ip6_tnl *t = NULL;
++
++      MOD_INC_USE_COUNT;
++
++      switch (cmd) {
++      case SIOCGETTUNNEL:
++              if (dev == &ip6ip6_fb_tnl_dev) {
++                      if (copy_from_user(&p,
++                                         ifr->ifr_ifru.ifru_data,
++                                         sizeof (p))) {
++                              err = -EFAULT;
++                              break;
++                      }
++                      if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV)
++                              t = (struct ip6_tnl *) dev->priv;
++                      else if (err)
++                              break;
++              } else
++                      t = (struct ip6_tnl *) dev->priv;
++
++              memcpy(&p, &t->parms, sizeof (p));
++              if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
++                      err = -EFAULT;
++              }
++              break;
++      case SIOCADDTUNNEL:
++      case SIOCCHGTUNNEL:
++              err = -EPERM;
++              create = (cmd == SIOCADDTUNNEL);
++              if (!capable(CAP_NET_ADMIN))
++                      break;
++              if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) {
++                      err = -EFAULT;
++                      break;
++              }
++              if (p.flags & IP6_TNL_F_KERNEL_DEV) {
++                      break;
++              }
++              if (!create && dev != &ip6ip6_fb_tnl_dev) {
++                      t = (struct ip6_tnl *) dev->priv;
++              }
++              if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) {
++                      break;
++              }
++              if (cmd == SIOCCHGTUNNEL) {
++                      if (t->dev != dev) {
++                              err = -EEXIST;
++                              break;
++                      }
++                      if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) {
++                              err = -EPERM;
++                              break;
++                      }
++                      ip6ip6_tnl_change(t, &p);
++                      netdev_state_change(dev);
++              }
++              if (copy_to_user(ifr->ifr_ifru.ifru_data,
++                               &t->parms, sizeof (p))) {
++                      err = -EFAULT;
++              } else {
++                      err = 0;
++              }
++              break;
++      case SIOCDELTUNNEL:
++              err = -EPERM;
++              if (!capable(CAP_NET_ADMIN))
++                      break;
++
++              if (dev == &ip6ip6_fb_tnl_dev) {
++                      if (copy_from_user(&p, ifr->ifr_ifru.ifru_data,
++                                         sizeof (p))) {
++                              err = -EFAULT;
++                              break;
++                      }
++                      err = ip6ip6_tnl_locate(&p, &t, 0);
++                      if (err)
++                              break;
++                      if (t == &ip6ip6_fb_tnl) {
++                              err = -EPERM;
++                              break;
++                      }
++              } else {
++                      t = (struct ip6_tnl *) dev->priv;
++              }
++              if (t->parms.flags & IP6_TNL_F_KERNEL_DEV)
++                      err = -EPERM;
++              else
++                      err = unregister_netdevice(t->dev);
++              break;
++      default:
++              err = -EINVAL;
++      }
++      MOD_DEC_USE_COUNT;
++      return err;
++}
++
++/**
++ * ip6ip6_tnl_get_stats - return the stats for tunnel device 
++ *   @dev: virtual device associated with tunnel
++ *
++ * Return: stats for device
++ **/
++
++static struct net_device_stats *
++ip6ip6_tnl_get_stats(struct net_device *dev)
++{
++      return &(((struct ip6_tnl *) dev->priv)->stat);
++}
++
++/**
++ * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device
++ *   @dev: virtual device associated with tunnel
++ *   @new_mtu: the new mtu
++ *
++ * Return:
++ *   0 on success,
++ *   %-EINVAL if mtu too small
++ **/
++
++static int
++ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
++{
++      if (new_mtu < IPV6_MIN_MTU) {
++              return -EINVAL;
++      }
++      dev->mtu = new_mtu;
++      return 0;
++}
++
++/**
++ * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices
++ *   @dev: virtual device associated with tunnel
++ *
++ * Description:
++ *   Set function pointers and initialize the &struct flowi template used
++ *   by the tunnel.
++ **/
++
++static int
++ip6ip6_tnl_dev_init_gen(struct net_device *dev)
++{
++      struct ip6_tnl *t = (struct ip6_tnl *) dev->priv;
++      struct flowi *fl = &t->fl;
++      int err;
++      struct sock *sk;
++
++      if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_IPV6, &t->sock))) {
++              printk(KERN_ERR
++                     "Failed to create IPv6 tunnel socket (err %d).\n", err);
++              return err;
++      }
++      t->sock->inode->i_uid = 0;
++      t->sock->inode->i_gid = 0;
++
++      sk = t->sock->sk;
++      sk->allocation = GFP_ATOMIC;
++      sk->net_pinfo.af_inet6.hop_limit = 254;
++      sk->net_pinfo.af_inet6.mc_loop = 0;
++      sk->prot->unhash(sk);
++
++      memset(fl, 0, sizeof (*fl));
++      fl->proto = IPPROTO_IPV6;
++
++      dev->destructor = ip6ip6_tnl_dev_destructor;
++      dev->uninit = ip6ip6_tnl_dev_uninit;
++      dev->hard_start_xmit = ip6ip6_tnl_xmit;
++      dev->get_stats = ip6ip6_tnl_get_stats;
++      dev->do_ioctl = ip6ip6_tnl_ioctl;
++      dev->change_mtu = ip6ip6_tnl_change_mtu;
++
++      dev->type = ARPHRD_TUNNEL6;
++      dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr);
++      dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr);
++      dev->flags |= IFF_NOARP;
++      dev->iflink = 0;
++      /* Hmm... MAX_ADDR_LEN is 8, so the ipv6 addresses can't be 
++         copied to dev->dev_addr and dev->broadcast, like the ipv4
++         addresses were in ipip.c, ip_gre.c and sit.c. */
++      dev->addr_len = 0;
++      return 0;
++}
++
++/**
++ * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices
++ *   @dev: virtual device associated with tunnel
++ **/
++
++static int
++ip6ip6_tnl_dev_init(struct net_device *dev)
++{
++      struct ip6_tnl *t = (struct ip6_tnl *) dev->priv;
++      ip6ip6_tnl_dev_init_gen(dev);
++      ip6ip6_tnl_link_config(t);
++      return 0;
++}
++
++#ifdef MODULE
++
++/**
++ * ip6ip6_fb_tnl_open - function called when fallback device opened
++ *   @dev: fallback device
++ *
++ * Return: 0 
++ **/
++
++static int
++ip6ip6_fb_tnl_open(struct net_device *dev)
++{
++      MOD_INC_USE_COUNT;
++      return 0;
++}
++
++/**
++ * ip6ip6_fb_tnl_close - function called when fallback device closed
++ *   @dev: fallback device
++ *
++ * Return: 0 
++ **/
++
++static int
++ip6ip6_fb_tnl_close(struct net_device *dev)
++{
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++#endif
++
++/**
++ * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device
++ *   @dev: fallback device
++ *
++ * Return: 0
++ **/
++
++int __init
++ip6ip6_fb_tnl_dev_init(struct net_device *dev)
++{
++      ip6ip6_tnl_dev_init_gen(dev);
++#ifdef MODULE
++      dev->open = ip6ip6_fb_tnl_open;
++      dev->stop = ip6ip6_fb_tnl_close;
++#endif
++      dev_hold(dev);
++      tnls_wc[0] = &ip6ip6_fb_tnl;
++      return 0;
++}
++
++/**
++ * ip6ip6_tnl_register_hook - add hook for processing of tunneled packets
++ *   @reg: hook function and its parameters
++ * 
++ * Description:
++ *   Add a netfilter like hook function for special handling of tunneled 
++ *   packets. The hook functions are called before encapsulation 
++ *   (%IP6_TNL_PRE_ENCAP) and before decapsulation 
++ *   (%IP6_TNL_PRE_DECAP). The possible return values by the hook 
++ *   functions are %IP6_TNL_DROP, %IP6_TNL_ACCEPT and 
++ *   %IP6_TNL_STOLEN (in case the hook function took care of the packet
++ *   and it doesn't have to be processed any further).
++ **/
++
++void
++ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg)
++{
++      if (reg->hooknum < IP6_TNL_MAXHOOKS) {
++              struct list_head *i;
++
++              write_lock_bh(&ip6ip6_hook_lock);
++              for (i = hooks[reg->hooknum].next;
++                   i != &hooks[reg->hooknum]; i = i->next) {
++                      if (reg->priority <
++                          ((struct ip6_tnl_hook_ops *) i)->priority) {
++                              break;
++                      }
++              }
++              list_add(&reg->list, i->prev);
++              write_unlock_bh(&ip6ip6_hook_lock);
++      }
++}
++
++/**
++ * ip6ip6_tnl_unregister_hook - remove tunnel hook
++ *   @reg: hook function and its parameters
++ **/
++
++void
++ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg)
++{
++      if (reg->hooknum < IP6_TNL_MAXHOOKS) {
++              write_lock_bh(&ip6ip6_hook_lock);
++              list_del(&reg->list);
++              write_unlock_bh(&ip6ip6_hook_lock);
++      }
++}
++
++
++/* the IPv6 over IPv6 protocol structure */
++static struct inet6_protocol ip6ip6_protocol = {
++      ip6ip6_rcv,             /* IPv6 handler         */
++      ip6ip6_err,             /* IPv6 error control   */
++      NULL,                   /* next                 */
++      IPPROTO_IPV6,           /* protocol ID          */
++      0,                      /* copy                 */
++      NULL,                   /* data                 */
++      "IPv6 over IPv6"        /* name                 */
++};
++
++/**
++ * ip6_tunnel_init - register protocol and reserve needed resources
++ *
++ * Return: 0 on success
++ **/
++
++int __init ip6_tunnel_init(void)
++{
++      int i, err;
++
++      ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl;
++
++      for (i = 0; i < IP6_TNL_MAXHOOKS; i++) {
++              INIT_LIST_HEAD(&hooks[i]);
++      }
++      if ((err = register_netdev(&ip6ip6_fb_tnl_dev)))
++              return err;
++
++      inet6_add_protocol(&ip6ip6_protocol);
++      return 0;
++}
++
++/**
++ * ip6_tunnel_cleanup - free resources and unregister protocol
++ **/
++
++void ip6_tunnel_cleanup(void)
++{
++      write_lock_bh(&ip6ip6_kernel_lock);
++      shutdown = 1;
++      write_unlock_bh(&ip6ip6_kernel_lock);
++      flush_scheduled_tasks();
++      manage_kernel_tnls(NULL);
++      inet6_del_protocol(&ip6ip6_protocol);
++      unregister_netdev(&ip6ip6_fb_tnl_dev);
++}
++
++#ifdef MODULE
++module_init(ip6_tunnel_init);
++module_exit(ip6_tunnel_cleanup);
++#endif
++
++#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE)
++EXPORT_SYMBOL(ip6ip6_tnl_register_hook);
++EXPORT_SYMBOL(ip6ip6_tnl_unregister_hook);
++#endif
++#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE
++EXPORT_SYMBOL(ip6ip6_tnl_dec_max_kdev_count);
++EXPORT_SYMBOL(ip6ip6_tnl_inc_max_kdev_count);
++EXPORT_SYMBOL(ip6ip6_tnl_dec_min_kdev_count);
++EXPORT_SYMBOL(ip6ip6_tnl_inc_min_kdev_count);
++EXPORT_SYMBOL(ip6ip6_kernel_tnl_add);
++EXPORT_SYMBOL(ip6ip6_kernel_tnl_del);
++EXPORT_SYMBOL(ip6ip6_tnl_lookup);
++#endif
++#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE
++EXPORT_SYMBOL(ip6ip6_tnl_create);
++EXPORT_SYMBOL(ip6ip6_tnl_change);
++#endif
++
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mipglue.c
+@@ -0,0 +1,63 @@
++/*
++ *    Glue for Mobility support integration to IPv6
++ *
++ *    Authors:
++ *    Antti Tuominen          <ajtuomin@cc.hut.fi>    
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/sched.h>
++
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/neighbour.h>
++#include <net/mipglue.h>
++
++extern int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff);
++
++/*  Initialize all zero  */
++struct mipv6_callable_functions mipv6_functions = { NULL };
++
++/* Sets mipv6_functions struct to zero to invalidate all successive
++ * calls to mipv6 functions. Used on module unload. */
++
++void mipv6_invalidate_calls(void)
++{
++      memset(&mipv6_functions, 0, sizeof(mipv6_functions));
++}
++
++
++/* Selects correct handler for tlv encoded destination option. Called
++ * by ip6_parse_tlv. Checks if mipv6 calls are valid before calling. */
++
++int mipv6_handle_dstopt(struct sk_buff *skb, int optoff)
++{
++      int ret;
++
++        switch (skb->nh.raw[optoff]) {
++      case MIPV6_TLV_HOMEADDR: 
++              ret = MIPV6_CALLFUNC(mipv6_handle_homeaddr, 0)(skb, optoff);
++              break;
++      default:
++              /* Should never happen */
++              printk(KERN_ERR __FILE__ ": Invalid destination option code (%d)\n",
++                     skb->nh.raw[optoff]);
++              ret = 1;
++              break;
++      }
++
++      /* If mipv6 handlers are not valid, pass the packet to
++         * ip6_tlvopt_unknown() for correct handling. */
++      if (!ret)
++              return ip6_tlvopt_unknown(skb, optoff);
++
++      return ret;
++}
++
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/Config.in
+@@ -0,0 +1,12 @@
++#
++# Mobile IPv6 Configuration
++#
++dep_tristate '    IPv6: Mobility Support (Correspondent Node)' CONFIG_IPV6_MOBILITY $CONFIG_IPV6
++if [ "$CONFIG_IPV6_IPV6_TUNNEL" != "n" ]; then
++   dep_tristate '      MIPv6: Mobile Node Support' CONFIG_IPV6_MOBILITY_MN $CONFIG_IPV6_MOBILITY
++
++   dep_tristate '      MIPv6: Home Agent Support' CONFIG_IPV6_MOBILITY_HA $CONFIG_IPV6_MOBILITY
++fi
++if [ "$CONFIG_IPV6_MOBILITY" != "n" ]; then
++   bool '      MIPv6: Debug messages' CONFIG_IPV6_MOBILITY_DEBUG
++fi
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/Makefile
+@@ -0,0 +1,35 @@
++#
++# Makefile for the MIPL Mobile IPv6 for Linux.
++#
++# 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).
++#
++
++
++O_TARGET := mip6_base.o
++
++list-multi := mip6_ha.o mip6_mn.o
++
++obj-y := hashlist.o bcache.o mobhdr_common.o stats.o exthdrs.o \
++      rr_crypto.o hmac.o auth_opt.o mipv6_icmp.o module_cn.o
++
++obj-m := $(O_TARGET)
++
++mip6_ha-objs := halist.o mipv6_icmp_ha.o tunnel_ha.o \
++              ndisc_ha.o ha.o module_ha.o
++
++mip6_mn-objs := mipv6_icmp_mn.o ioctl_mn.o tunnel_mn.o \
++              mdetect.o bul.o multiaccess_ctl.o mobhdr_mn.o mn.o \
++              module_mn.o 
++
++obj-$(CONFIG_IPV6_MOBILITY_HA) += mip6_ha.o
++obj-$(CONFIG_IPV6_MOBILITY_MN) += mip6_mn.o
++
++include $(TOPDIR)/Rules.make
++
++mip6_ha.o: $(mip6_ha-objs)
++      $(LD) -r -o $@ $(mip6_ha-objs)
++
++mip6_mn.o: $(mip6_mn-objs)
++      $(LD) -r -o $@ $(mip6_mn-objs)
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/README
+@@ -0,0 +1,15 @@
++MIPL Mobile IPv6 for Linux
++
++More information at http://www.mipl.mediapoli.com/.
++
++To join MIPL Mobile IPv6 for Linux mailing lists go to:
++
++      http://www.mipl.mediapoli.com/cgi-bin/mailman/listinfo
++
++Or send mail with subject "subscribe" for the general list to:
++
++      mipl-request@list.mipl.mediapoli.com
++
++or for the developer list to:
++
++      mipl-devel-request@list.mail.mediapoli.com
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/auth_opt.c
+@@ -0,0 +1,121 @@
++/*
++ *    MIPv6 Binding Authentication Data Option functions
++ *    
++ *      Authors: 
++ *      Henrik Petander         <lpetande@tml.hut.fi>
++ * 
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/autoconf.h>
++#include <linux/icmpv6.h>
++#include <net/mipv6.h>
++
++#include "debug.h"
++#include "hmac.h"
++#include "mobhdr.h"
++
++#define DBG_KEY 5
++
++int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, 
++                   __u8 *mh, __u8 *aud_data, __u8 *k_bu)
++{
++      /* First look up the peer from sadb based on his address */ 
++      struct ah_processing ahp;
++
++      /* Don't add any other options or this system is screwed */
++
++      __u8 buf[MAX_HASH_LENGTH];  
++      
++      
++      if (!k_bu) {
++              DEBUG(DBG_ERROR, "k_bu missing, aborting");
++              return -1;
++      }
++      DEBUG(DBG_KEY, "Key for building authenticator:");
++      debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE);
++
++      if (ah_hmac_sha1_init(&ahp, k_bu,  HMAC_SHA1_KEY_SIZE) < 0) {
++              DEBUG(DBG_ERROR, "Failed to initialize hmac sha1");
++                return -1; 
++        } 
++
++      DEBUG(DBG_KEY, "coa: ");
++      debug_print_buffer(DBG_KEY, coa, 16);
++      DEBUG(DBG_KEY, "cn_addr: ");
++      debug_print_buffer(DBG_KEY, cn_addr, 16);
++      DEBUG(DBG_KEY, "MH contents: ");
++      debug_print_buffer(DBG_KEY, mh, aud_data - mh);
++
++      /* First the common part */
++      ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr));
++      ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr));
++      ah_hmac_sha1_loop(&ahp, mh, aud_data - mh);
++      ah_hmac_sha1_result(&ahp, buf);
++
++      memcpy(aud_data, buf,  MIPV6_RR_MAC_LENGTH);
++
++      return 0;
++}
++
++int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa,
++                   __u8 *opt, __u8 optlen, 
++                   struct mipv6_mo_bauth_data *aud, __u8 *k_bu)
++{
++      int ret = -1;
++      struct ah_processing ahp;
++      __u8 htarget[MAX_HASH_LENGTH];
++
++      /* Look up peer by home address */ 
++      if (!k_bu) {
++              DEBUG(DBG_ERROR, "k_bu missing, aborting"); 
++              return -1;
++      }
++
++      DEBUG(DBG_KEY, "Key for checking authenticator:");
++      debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE);
++
++      if (!aud || !coa) {
++              DEBUG(DBG_INFO, "%s is NULL", aud ? "coa" : "aud");
++              goto out;
++      }
++
++      if (aud->length != MIPV6_RR_MAC_LENGTH) {
++              DEBUG(DBG_ERROR,
++                       ": Incorrect authentication option length %d", aud->length); 
++              goto out; 
++      }
++      
++      if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { 
++                DEBUG(DBG_ERROR,
++                       "internal error in initialization of authentication algorithm");
++              goto out;
++        } 
++      DEBUG(DBG_KEY, "coa: ");
++      debug_print_buffer(DBG_KEY, coa, 16);
++      DEBUG(DBG_KEY, "cn_addr: ");
++      debug_print_buffer(DBG_KEY, cn_addr, 16);
++      DEBUG(DBG_KEY, "MH contents: ");
++      debug_print_buffer(DBG_KEY, opt, (u8*) aud->data - opt);
++
++      ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr));
++      ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr));
++
++      /* 
++       * Process MH + options till the start of the authenticator in
++       * Auth. data option
++       */
++      ah_hmac_sha1_loop(&ahp, opt,  (u8 *)aud->data - opt);
++      ah_hmac_sha1_result(&ahp, htarget);
++      if (memcmp(htarget, aud->data, MIPV6_RR_MAC_LENGTH) == 0)
++              ret = 0;
++
++      DEBUG(DBG_ERROR, "returning %d", ret);
++out:  
++      return ret;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.c
+@@ -0,0 +1,746 @@
++/*
++ *      Binding Cache
++ *
++ *      Authors:
++ *      Juha Mynttinen            <jmynttin@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++/*
++ *    Changes:
++ *
++ *    Nanno Langstraat        :       Timer code cleaned up, active socket
++ *                                    test rewritten
++ */
++
++#include <linux/autoconf.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/in6.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/proc_fs.h>
++#include <linux/ipv6_route.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/tcp.h>
++#include <net/udp.h>
++#include <net/ip6_route.h>
++#include <net/mipv6.h>
++
++#include "bcache.h"
++#include "hashlist.h"
++#include "debug.h"
++#include "mobhdr.h"
++#include "tunnel.h"
++#include "config.h"
++
++#define TIMERDELAY HZ/10
++
++struct mipv6_bcache {
++      struct hashlist *entries;
++      __u32 size;
++      struct timer_list callback_timer;
++};
++
++struct in6_addr_pair {
++      struct in6_addr *a1;
++      struct in6_addr *a2;
++};
++
++static rwlock_t bcache_lock = RW_LOCK_UNLOCKED;
++
++static struct mipv6_bcache bcache;
++
++static int bcache_proc_info(char *buffer, char **start, off_t offset,
++                          int length);
++
++#define MIPV6_BCACHE_HASHSIZE  32
++
++/* Moment of transmission of a BR, in seconds before bcache entry expiry */
++#define BCACHE_BR_SEND_LEAD  3
++
++#define MIPV6_MAX_BRR 3 /* Send 3 BRRs before deleting BC entry */
++#define MIPV6_BRR_RATE HZ /* Send BRRs once per second */
++
++/* 
++ * Internal functions.
++ */
++
++struct cache_entry_iterator_args {
++      struct mipv6_bce **entry;
++};
++
++static int find_first_cache_entry_iterator(void *data, void *args,
++                                         unsigned long *lifetime)
++{
++      struct mipv6_bce *entry =
++          (struct mipv6_bce *) data;
++      struct cache_entry_iterator_args *state =
++          (struct cache_entry_iterator_args *) args;
++
++      ASSERT(entry != NULL);
++
++      if (entry->type == CACHE_ENTRY) {
++              *(state->entry) = entry;
++              return ITERATOR_STOP;   /* stop iteration */
++      } else {
++              return ITERATOR_CONT;   /* continue iteration */
++      }
++}
++
++
++/* 
++ * Get memory for a new bcache entry.  If bcache is full, a cache
++ * entry may be deleted to get space for a home registration, but not
++ * vice versa.
++ */
++static struct mipv6_bce *mipv6_bce_alloc(__u8 type)
++{
++      struct mipv6_bce *entry;
++      struct cache_entry_iterator_args args;
++
++      DEBUG_FUNC();
++
++      entry = (struct mipv6_bce *)
++              hashlist_alloc(bcache.entries, SLAB_ATOMIC);
++
++      /* Cache replacement policy: always replace the CACHE_ENTRY
++           closest to expiration.  Type HOME_REGISTRATION entry may
++           never be deleted before expiration. */
++      if (entry == NULL) {
++              /* cache full, try to delete a CACHE_ENTRY */
++              args.entry = &entry;
++              hashlist_iterate(bcache.entries, &args,
++                               find_first_cache_entry_iterator);
++              if (entry == NULL)
++                      return NULL;
++              hashlist_delete(bcache.entries,
++                              (struct hashlist_entry *)entry);
++              entry = (struct mipv6_bce *)
++                      hashlist_alloc(bcache.entries, SLAB_ATOMIC);
++      }
++      return entry;
++}
++
++/*
++ * Frees entry's memory allocated with mipv6_bce_alloc
++ */
++static void mipv6_bce_free(struct mipv6_bce *entry)
++{
++      hashlist_free(bcache.entries, (void *) entry);
++}
++
++/*
++ * Removes all expired entries 
++ */
++static void expire(void)
++{
++      struct mipv6_bce *entry;
++      struct br_addrs {
++              struct in6_addr daddr;
++              struct in6_addr saddr;
++              struct br_addrs *next;
++      };
++      struct br_addrs *br_info = NULL;
++
++      DEBUG_FUNC();
++
++      write_lock(&bcache_lock);
++
++      while ((entry = (struct mipv6_bce *)
++              hashlist_get_first(bcache.entries)) != NULL) {
++              struct rt6_info *rt;
++              if (time_after_eq(jiffies, entry->callback_time)) {
++
++                      DEBUG(DBG_INFO, "an entry expired");
++
++                      if (entry->type & HOME_REGISTRATION) {
++                              mip6_fn.proxy_del(&entry->home_addr, entry);
++                      }
++                      hashlist_delete(bcache.entries, (void *)entry);
++                      mipv6_bce_free(entry);
++                      entry = NULL;
++              } else if (entry->br_callback_time != 0 &&
++                         time_after_eq(jiffies, entry->br_callback_time) &&
++                         entry->br_count < MIPV6_MAX_BRR &&
++                         (rt = rt6_lookup(&entry->home_addr, &entry->our_addr, 0, 0)) != NULL){
++                      /* Do we have a destination cache entry for the home address */
++                      if (rt->rt6i_flags & RTF_CACHE) {
++                              struct br_addrs *tmp;
++                              tmp = br_info;
++                              DEBUG(DBG_INFO,
++                                    "bcache entry recently used. Sending BR.");
++                              /* queue for sending */
++                              br_info = kmalloc(sizeof(struct br_addrs),
++                                                GFP_ATOMIC);
++                              if (br_info) {
++                                      ipv6_addr_copy(&br_info->saddr,
++                                                     &entry->our_addr);
++                                      ipv6_addr_copy(&br_info->daddr,
++                                                     &entry->home_addr);
++                                      br_info->next = tmp;
++                                      entry->last_br = jiffies;
++                                      entry->br_callback_time = jiffies + MIPV6_BRR_RATE;
++                                      entry->br_count++;
++                              } else {
++                                      br_info = tmp;
++                                      DEBUG(DBG_ERROR, "Out of memory");
++                              }
++                              
++                      } else
++                              entry->br_callback_time = 0;    
++                      dst_release(&rt->u.dst);
++              } else {
++                      entry->br_callback_time = 0;
++                      break;
++              }
++      }
++      write_unlock(&bcache_lock);
++
++      while (br_info) {
++              struct br_addrs *tmp = br_info->next;
++              if (mipv6_send_brr(&br_info->saddr, &br_info->daddr, NULL) < 0)
++                      DEBUG(DBG_WARNING,
++                            "BR send for %x:%x:%x:%x:%x:%x:%x:%x failed",
++                            NIPV6ADDR(&br_info->daddr));
++              kfree(br_info);
++              br_info = tmp;
++      }
++}
++
++static void set_timer(void)
++{
++      struct mipv6_bce *entry;
++      unsigned long callback_time;
++
++      DEBUG_FUNC();
++
++      entry = (struct mipv6_bce *)
++              hashlist_get_first(bcache.entries);
++      if (entry != NULL) {
++              if (entry->br_callback_time > 0 && 
++                  time_after(entry->br_callback_time, jiffies))
++                      callback_time = entry->br_callback_time;
++              else if (time_after(entry->callback_time, jiffies))
++                      callback_time = entry->callback_time;
++              else {
++                      DEBUG(DBG_WARNING, 
++                            "bcache timer attempted to schedule"
++                            " for a historical jiffies count!");
++                      callback_time = jiffies + TIMERDELAY;
++              }
++              
++              DEBUG(DBG_INFO, "setting timer to now");
++              mod_timer(&bcache.callback_timer, callback_time);
++      } else {
++              del_timer(&bcache.callback_timer);
++              DEBUG(DBG_INFO, "BC empty, not setting a new timer");
++      }
++}
++
++/* 
++ * The function that is scheduled to do the callback functions. May be
++ * modified e.g to allow Binding Requests, now only calls expire() and
++ * schedules a new timer.
++ */
++static void timer_handler(unsigned long dummy)
++{
++      expire();
++      write_lock(&bcache_lock);
++      set_timer();
++      write_unlock(&bcache_lock);
++}
++
++/*
++ * Interface functions visible to other modules
++ */
++
++/**
++ * mipv6_bcache_add - add Binding Cache entry
++ * @ifindex: interface index
++ * @our_addr: own address
++ * @home_addr_org: MN's home address
++ * @coa: MN's care-of address
++ * @lifetime: lifetime for this binding
++ * @prefix: prefix length
++ * @seq: sequence number
++ * @flags: flags received in BU
++ * @type: type of entry
++ *
++ * Adds an entry for this @home_addr_org in the Binding Cache.  If entry
++ * already exists, old entry is updated.  @type may be %CACHE_ENTRY or
++ * %HOME_REGISTRATION.
++ **/
++int mipv6_bcache_add(int ifindex,
++                   struct in6_addr *our_addr,
++                   struct in6_addr *home_addr,
++                   struct in6_addr *coa,
++                   __u32 lifetime, __u16 seq, __u8 flags, __u8 type)
++{
++      struct mipv6_bce *entry;
++      int update = 0;
++      int create_tunnel = 0;
++      unsigned long now = jiffies;
++      struct in6_addr_pair hashkey;
++      int ret = -1;
++
++      DEBUG_FUNC();
++
++      hashkey.a1 = home_addr;
++      hashkey.a2 = our_addr;
++
++      write_lock(&bcache_lock);
++
++      if (type == HOME_REGISTRATION && !(mip6node_cnf.capabilities&CAP_HA))
++              return 0;
++
++      if (unlikely(bcache.entries == NULL)) {
++              ret = -ENOMEM;
++              goto err;
++      }
++
++      if ((entry = (struct mipv6_bce *)
++           hashlist_get(bcache.entries, &hashkey)) != NULL) {
++              /* if an entry for this home_addr exists (with smaller
++               * seq than the new seq), update it by removing it
++               * first
++               */
++              if (!MIPV6_SEQ_GT(seq, entry->seq)) {
++                      DEBUG(DBG_INFO, "smaller seq than existing, not updating");
++                      goto out;
++              }
++              DEBUG(DBG_INFO, "updating an existing entry");
++              update = 1;
++
++              /* H-flag is already checked in BU handler. */
++              /* XXX: Should we care about the other flags?*/
++              if (flags != entry->flags) {
++                      DEBUG(DBG_INFO, "entry/BU flag mismatch");
++              }
++
++              if (type == HOME_REGISTRATION) {
++                      create_tunnel = (ipv6_addr_cmp(&entry->coa, coa) ||
++                                       entry->ifindex != ifindex);
++              }
++      } else {
++              /* no entry for this home_addr, try to create a new entry */
++              DEBUG(DBG_INFO, "creating a new entry");
++              update = 0;
++
++              entry = mipv6_bce_alloc(type);
++              if (entry == NULL) {
++                      DEBUG(DBG_INFO, "cache full, entry not added");
++                      goto err;
++              }
++
++              create_tunnel = (type == HOME_REGISTRATION);
++      }
++
++      if (create_tunnel) {
++              if (update)
++                      mip6_fn.proxy_del(&entry->home_addr, entry);
++              if (mip6_fn.proxy_create(flags, ifindex, coa, our_addr, home_addr) < 0) {
++                      goto err_proxy;
++              }
++      }
++
++      ipv6_addr_copy(&(entry->our_addr), our_addr);
++      ipv6_addr_copy(&(entry->home_addr), home_addr);
++      ipv6_addr_copy(&(entry->coa), coa);
++      entry->ifindex = ifindex;
++      entry->seq = seq;
++      entry->type = type;
++      entry->flags = flags;
++      
++      entry->last_br = 0;
++      entry->destunr_count = 0;
++      entry->callback_time = now + lifetime * HZ;
++      if (entry->type & HOME_REGISTRATION)
++              entry->br_callback_time = 0;
++      else
++              entry->br_callback_time = now +
++                      (lifetime - BCACHE_BR_SEND_LEAD) * HZ;
++      
++      if (update) {
++              DEBUG(DBG_INFO, "updating entry : %x", entry);
++              hashlist_reposition(bcache.entries, (void *)entry, 
++                                  entry->callback_time);
++      } else {
++              DEBUG(DBG_INFO, "adding entry: %x", entry);
++              if ((hashlist_add(bcache.entries,
++                                &hashkey,
++                                entry->callback_time, entry)) < 0) {
++                      
++                      DEBUG(DBG_ERROR, "Hash add failed");
++                      goto err_hashlist;
++              }
++      }
++      
++      set_timer();
++      
++out:
++      write_unlock(&bcache_lock);
++      return 0;
++
++err_hashlist:
++      if (create_tunnel) {
++              mip6_fn.proxy_del(home_addr, entry);
++      }
++err_proxy:
++      if (update) {
++              hashlist_delete(bcache.entries, (void *)entry);
++      }
++      mipv6_bce_free(entry);
++err:
++      write_unlock(&bcache_lock);
++      return ret;
++}
++
++int mipv6_bcache_icmp_err(struct in6_addr *home_addr,
++                        struct in6_addr *our_addr, 
++                        int destunr_count)
++{
++      struct mipv6_bce *entry;
++      struct in6_addr_pair hashkey;
++
++      int ret = -ENOENT;
++
++      DEBUG_FUNC();
++
++      hashkey.a1 = home_addr;
++      hashkey.a2 = our_addr;
++
++      write_lock(&bcache_lock);
++      if (unlikely(bcache.entries == NULL)) {
++              ret = -ENOMEM;
++              goto err;
++      }
++
++      if ((entry = (struct mipv6_bce *)
++           hashlist_get(bcache.entries, &hashkey)) != NULL) {
++              entry->last_destunr = jiffies;
++              entry->destunr_count = destunr_count;
++              ret = 0;
++      }
++err:
++      write_unlock(&bcache_lock);
++      return ret;
++}
++
++
++/**
++ * mipv6_bcache_delete - delete Binding Cache entry
++ * @home_addr: MN's home address
++ * @our_addr: our address
++ * @type: type of entry
++ *
++ * Deletes an entry associated with @home_addr from Binding Cache.
++ * Valid values for @type are %CACHE_ENTRY, %HOME_REGISTRATION and
++ * %ANY_ENTRY.  %ANY_ENTRY deletes any type of entry.
++ **/
++int mipv6_bcache_delete(struct in6_addr *home_addr,
++                      struct in6_addr *our_addr, __u8 type)
++{
++      struct mipv6_bce *entry;
++      struct in6_addr_pair hashkey;
++      int err = 0;
++
++      DEBUG_FUNC();
++
++      if (home_addr == NULL || our_addr == NULL) {
++              DEBUG(DBG_INFO, "error in arguments");
++              return -EINVAL;
++      }
++
++      hashkey.a1 = home_addr;
++      hashkey.a2 = our_addr;
++
++      write_lock(&bcache_lock);
++
++      if (unlikely(bcache.entries == NULL) ||
++          (entry = (struct mipv6_bce *)
++           hashlist_get(bcache.entries, &hashkey)) == NULL ||
++          !(entry->type & type)) {
++              DEBUG(DBG_INFO, "No matching entry found");
++              err = -ENOENT;
++              goto out;
++      }
++
++      hashlist_delete(bcache.entries, (void *) entry);
++      mipv6_bce_free(entry);
++
++      set_timer();
++out:
++      write_unlock(&bcache_lock);
++      return err;
++}
++
++/**
++ * mipv6_bcache_exists - check if entry exists
++ * @home_addr: home address to check
++ * @our_addr: our address
++ *
++ * Determines if a binding exists for @home_addr.  Returns type of the
++ * entry or negative if entry does not exist.
++ **/
++int mipv6_bcache_exists(struct in6_addr *home_addr,
++                      struct in6_addr *our_addr)
++{
++      struct mipv6_bce *entry;
++      struct in6_addr_pair hashkey;
++      int type = -ENOENT;
++
++      DEBUG_FUNC();
++
++      if (home_addr == NULL || our_addr == NULL)
++              return -EINVAL;
++
++      hashkey.a1 = home_addr;
++      hashkey.a2 = our_addr;
++
++      read_lock(&bcache_lock);
++      if (likely(bcache.entries != NULL) &&
++          (entry = (struct mipv6_bce *)
++           hashlist_get(bcache.entries, &hashkey)) != NULL) {
++              type = entry->type;
++      }
++      read_unlock(&bcache_lock);
++
++      return type;
++}
++
++/**
++ * mipv6_bcache_get - get entry from Binding Cache
++ * @home_addr: home address to search
++ * @our_addr: our address
++ * @entry: pointer to buffer
++ *
++ * Gets a copy of Binding Cache entry for @home_addr. If entry 
++ * exists entry is copied to @entry and zero is returned.  
++ * Otherwise returns negative.
++ **/
++int mipv6_bcache_get(struct in6_addr *home_addr,
++                   struct in6_addr *our_addr,
++                   struct mipv6_bce *entry)
++{
++      struct mipv6_bce *entry2;
++      struct in6_addr_pair hashkey;
++      int ret = -ENOENT;
++
++      DEBUG_FUNC();
++
++      if (home_addr == NULL || our_addr == NULL || entry == NULL)
++              return -EINVAL;
++
++      hashkey.a1 = home_addr;
++      hashkey.a2 = our_addr;
++
++      read_lock_bh(&bcache_lock);
++
++      entry2 = (struct mipv6_bce *)
++              hashlist_get(bcache.entries, &hashkey);
++      if (entry2 != NULL) {
++              memcpy(entry, entry2, sizeof(struct mipv6_bce));
++              ret = 0;
++      }
++      read_unlock_bh(&bcache_lock);
++      return ret;
++}
++
++int mipv6_bcache_iterate(hashlist_iterator_t func, void *args)
++{
++      int ret;
++
++      read_lock_bh(&bcache_lock);
++      ret = hashlist_iterate(bcache.entries, args, func);
++      read_unlock_bh(&bcache_lock);
++
++      return ret;
++}
++
++/*
++ * Proc-filesystem functions
++ */
++
++#define BC_INFO_LEN 80
++
++struct procinfo_iterator_args {
++      char *buffer;
++      int offset;
++      int length;
++      int skip;
++      int len;
++};
++
++static int procinfo_iterator(void *data, void *args, unsigned long *pref)
++{
++      struct procinfo_iterator_args *arg =
++          (struct procinfo_iterator_args *) args;
++      struct mipv6_bce *entry =
++          (struct mipv6_bce *) data;
++
++      ASSERT(entry != NULL);
++
++      if (arg->skip < arg->offset / BC_INFO_LEN) {
++              arg->skip++;
++              return ITERATOR_CONT;
++      }
++
++      if (arg->len >= arg->length)
++              return ITERATOR_CONT;
++
++      /* HoA CoA CallbackInSecs Type */
++      arg->len += sprintf(arg->buffer + arg->len,
++                          "%08x%08x%08x%08x %08x%08x%08x%08x %010lu %02d\n",
++                          ntohl(entry->home_addr.s6_addr32[0]),
++                          ntohl(entry->home_addr.s6_addr32[1]),
++                          ntohl(entry->home_addr.s6_addr32[2]),
++                          ntohl(entry->home_addr.s6_addr32[3]),
++                          ntohl(entry->coa.s6_addr32[0]),
++                          ntohl(entry->coa.s6_addr32[1]),
++                          ntohl(entry->coa.s6_addr32[2]),
++                          ntohl(entry->coa.s6_addr32[3]),
++                          ((entry->callback_time) - jiffies) / HZ,
++                          (int) entry->type);
++
++      return ITERATOR_CONT;
++}
++
++ /*
++  * Callback function for proc filesystem.
++  */
++static int bcache_proc_info(char *buffer, char **start, off_t offset,
++                          int length)
++{
++      struct procinfo_iterator_args args;
++
++      DEBUG_FUNC();
++
++      args.buffer = buffer;
++      args.offset = offset;
++      args.length = length;
++      args.skip = 0;
++      args.len = 0;
++
++      read_lock_bh(&bcache_lock);
++      hashlist_iterate(bcache.entries, &args, procinfo_iterator);
++      read_unlock_bh(&bcache_lock);
++
++      *start = buffer;
++      if (offset)
++              *start += offset % BC_INFO_LEN;
++
++      args.len -= offset % BC_INFO_LEN;
++
++      if (args.len > length)
++              args.len = length;
++      if (args.len < 0)
++              args.len = 0;
++
++      return args.len;
++}
++
++static int bcache_compare(void *data, void *hashkey)
++{
++      struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey;
++      struct mipv6_bce *e = (struct mipv6_bce *) data;
++
++      if (ipv6_addr_cmp(&e->home_addr, p->a1) == 0
++          && ipv6_addr_cmp(&e->our_addr, p->a2) == 0)
++              return 0;
++      else
++              return -1;
++}
++
++static __u32 bcache_hash(void *hashkey)
++{
++      struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey;
++
++      return p->a1->s6_addr32[0] ^ p->a1->s6_addr32[1] ^
++              p->a2->s6_addr32[2] ^ p->a2->s6_addr32[3];
++}
++
++/* 
++ * Initialization and shutdown functions
++ */
++
++int __init mipv6_bcache_init(__u32 size)
++{
++      if (size < 1) {
++              DEBUG(DBG_ERROR, "Binding cache size must be at least 1");
++              return -EINVAL;
++      }
++      bcache.entries = hashlist_create(MIPV6_BCACHE_HASHSIZE, size,
++                                       sizeof(struct mipv6_bce),
++                                       "mip6_bcache", NULL, NULL,
++                                       bcache_compare, bcache_hash);
++
++      if (bcache.entries == NULL) {
++              DEBUG(DBG_ERROR, "Failed to initialize hashlist");
++              return -ENOMEM;
++      }
++
++      init_timer(&bcache.callback_timer);
++      bcache.callback_timer.data = 0;
++      bcache.callback_timer.function = timer_handler;
++      bcache.size = size;
++
++      proc_net_create("mip6_bcache", 0, bcache_proc_info);
++
++      DEBUG(DBG_INFO, "Binding cache initialized");
++      return 0;
++}
++
++static int 
++bce_cleanup_iterator(void *rawentry, void *args, unsigned long *sortkey)
++{
++      int type = (int) args;
++      struct mipv6_bce *entry = (struct mipv6_bce *) rawentry;
++      if (entry->type == type) {
++              if (entry->type & HOME_REGISTRATION) {
++                      if (unlikely(mip6_fn.proxy_del == NULL))
++                              DEBUG(DBG_ERROR, "proxy_del unitialized");
++                      else
++                              mip6_fn.proxy_del(&entry->home_addr, entry);
++              }
++              return ITERATOR_DELETE_ENTRY;
++      }
++      return ITERATOR_CONT;
++
++}
++
++void mipv6_bcache_cleanup(int type)
++{
++      write_lock_bh(&bcache_lock);
++      hashlist_iterate(bcache.entries,(void *) type, bce_cleanup_iterator);
++      write_unlock_bh(&bcache_lock);
++}
++
++int __exit mipv6_bcache_exit(void)
++{
++      struct hashlist *entries;
++
++      DEBUG_FUNC();
++
++      proc_net_remove("mip6_bcache");
++
++      write_lock_bh(&bcache_lock);
++      DEBUG(DBG_INFO, "Stopping the bcache timer");
++      del_timer(&bcache.callback_timer);
++      hashlist_iterate(bcache.entries,(void *)CACHE_ENTRY, 
++                       bce_cleanup_iterator);
++
++      entries = bcache.entries;
++      bcache.entries = NULL;
++      write_unlock_bh(&bcache_lock);
++
++      hashlist_destroy(entries);
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.h
+@@ -0,0 +1,72 @@
++/*
++ *      MIPL Mobile IPv6 Binding Cache header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _BCACHE_H
++#define _BCACHE_H
++
++#include <linux/in6.h>
++#include <linux/timer.h>
++#include "hashlist.h"
++
++#define CACHE_ENTRY 1 /* this and HOME_REGISTRATION are the entry types */
++#define HOME_REGISTRATION 2
++#define ANY_ENTRY 3
++
++#define MIPV6_MAX_DESTUNREACH 5 /* Delete CN BCEs after 5 destination unreachables */
++#define MIPV6_DEST_UNR_IVAL  10 /* What is the max interval of destination  
++                                 unreacahable error messages for them to be persistent*/
++
++struct mipv6_bce {
++      struct hashlist_entry e;
++      int ifindex;                            /* Interface identifier */
++      struct in6_addr our_addr;               /* our address (as seen by the MN) */
++      struct in6_addr home_addr;              /* MN home address */
++      struct in6_addr coa;                    /* MN care-of address */
++      unsigned long callback_time;            /* time of expiration     (in jiffies) */
++      unsigned long br_callback_time;         /* time for sending a BR  (in jiffies) */
++      int (*callback_function)(struct mipv6_bce *entry);
++      __u8 type;                              /* home registration */
++      __u8 router;                            /* mn is router */
++      __u8 flags;                             /* flags received in BU */
++      __u16 seq;                              /* sequence number */
++      unsigned long last_br;                  /* time when last BR sent */
++      unsigned long last_destunr;             /* time when last ICMP destination unreachable received */
++      int br_count;                           /* How many BRRs have sent */
++      int destunr_count;                      /* Number of destination unreachables received */   
++};
++
++int mipv6_bcache_add(int ifindex, struct in6_addr *our_addr, 
++                   struct in6_addr *home_addr, struct in6_addr *coa,
++                   __u32 lifetime, __u16 seq, __u8 flags, __u8 type);
++
++int mipv6_bcache_icmp_err(struct in6_addr *home_addr,
++                        struct in6_addr *our_addr, 
++                        int destunr_count);
++
++int mipv6_bcache_delete(struct in6_addr *home_addr, struct in6_addr *our_addr,
++                      __u8 type);
++
++int mipv6_bcache_exists(struct in6_addr *home_addr,
++                      struct in6_addr *our_addr);
++
++int mipv6_bcache_get(struct in6_addr *home_addr,
++                   struct in6_addr *our_addr,
++                   struct mipv6_bce *entry);
++
++int mipv6_bcache_iterate(int (*func)(void *, void *, unsigned long *), void *args);
++
++void mipv6_bcache_cleanup(int type);
++
++int mipv6_bcache_init(__u32 size);
++
++int mipv6_bcache_exit(void);
++
++#endif /* _BCACHE_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/bul.c
+@@ -0,0 +1,634 @@
++/*
++ *      Binding update list
++ *
++ *      Authors:
++ *      Juha Mynttinen            <jmynttin@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++/*
++ *    Changes:
++ *
++ *    Nanno Langstraat        :       Timer code cleaned up
++ */
++
++#include <linux/autoconf.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/in6.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <net/ipv6.h>
++#include <net/mipv6.h>
++#include <linux/proc_fs.h>
++
++#include "bul.h"
++#include "debug.h"
++#include "hashlist.h"
++#include "tunnel_mn.h"
++#include "mobhdr.h"
++
++#define MIPV6_BUL_HASHSIZE 32
++
++rwlock_t bul_lock = RW_LOCK_UNLOCKED;
++
++struct mipv6_bul {
++      struct hashlist *entries;
++      struct timer_list callback_timer;
++};
++
++static struct mipv6_bul bul;
++
++struct in6_addr_pair {
++      struct in6_addr *a1;
++      struct in6_addr *a2;
++};
++
++/**********************************************************************
++ *
++ * Private functions
++ *
++ **********************************************************************/
++
++static int bul_compare(void *data, void *hashkey)
++{
++      struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey;
++      struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data;
++
++      if (ipv6_addr_cmp(&e->cn_addr, p->a1) == 0
++          && ipv6_addr_cmp(&e->home_addr, p->a2) == 0)
++              return 0;
++      else
++              return -1;
++}
++
++struct test_keys {
++      struct in6_addr *addr;
++      u8 *cookie;
++};
++
++static int bul_compare_cookie(void *data, void *keys)
++{
++      struct test_keys *p = (struct test_keys *)keys;
++      struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data;
++
++      if (ipv6_addr_cmp(&e->cn_addr, p->addr) == 0 && e->rr
++          && memcmp(&e->rr->cot_cookie, p->cookie, 8) == 0)
++              return 0;
++      else
++              return -1;
++}
++
++static u32 bul_hash(void *hashkey)
++{
++      struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey;
++      
++      return p->a1->s6_addr32[0] ^
++              p->a1->s6_addr32[1] ^
++              p->a1->s6_addr32[2] ^
++              p->a1->s6_addr32[3];
++}
++
++static int bul_proc_info(char *buffer, char **start, off_t offset,
++                          int length);
++
++static struct mipv6_bul_entry *mipv6_bul_get_entry(void)
++{
++      DEBUG_FUNC();
++      return ((struct mipv6_bul_entry *) 
++              hashlist_alloc(bul.entries, SLAB_ATOMIC));
++}
++
++static void mipv6_bul_entry_free(struct mipv6_bul_entry *entry)
++{
++      DEBUG_FUNC();           
++
++      if (entry->rr) {
++              if (entry->rr->kbu)
++                      kfree(entry->rr->kbu);
++              kfree(entry->rr);
++      }
++      if (entry->ops)
++              kfree(entry->ops);
++      hashlist_free(bul.entries, (void *)entry);
++}
++
++static __inline__ int del_bul_entry_tnl(struct mipv6_bul_entry *entry) 
++{
++      if (entry->flags & MIPV6_BU_F_HOME) {
++              return mipv6_mv_tnl_to_ha(&entry->cn_addr, 
++                                          &entry->coa,
++                                          &entry->home_addr);
++      }
++      return 0;
++}
++
++static void timer_update(void)
++{
++      struct mipv6_bul_entry *entry;
++
++      DEBUG_FUNC();
++
++      entry = hashlist_get_first(bul.entries);
++
++      while (entry && time_after_eq(jiffies, entry->callback_time)) {
++              if (time_after_eq(jiffies, entry->expire) ||
++                  entry->callback(entry) != 0) {
++                      /*
++                       * Either the entry has expired, or the callback
++                       * indicated that it should be deleted.
++                       */
++                      hashlist_delete(bul.entries, (void *)entry);
++                      
++                      del_bul_entry_tnl(entry);
++                      mipv6_bul_entry_free(entry);
++                      DEBUG(DBG_INFO, "Entry deleted (was expired) from "
++                            "binding update list");
++              } else {
++                      /* move entry to its right place in the hashlist */
++                      DEBUG(DBG_INFO, "Rescheduling");
++                      hashlist_reposition(bul.entries, (void *)entry,
++                                          entry->callback_time);
++              }
++              entry = (struct mipv6_bul_entry *)
++                      hashlist_get_first(bul.entries);
++      }
++
++      if (entry == NULL) {
++              DEBUG(DBG_INFO, "bul empty, not setting a new timer");
++              del_timer(&bul.callback_timer);
++      } else {
++              mod_timer(&bul.callback_timer, entry->callback_time);
++      }
++}
++
++static void timer_handler(unsigned long dummy)
++{
++      DEBUG_FUNC();
++
++      write_lock(&bul_lock);
++      timer_update();
++      write_unlock(&bul_lock);
++}
++
++/**********************************************************************
++ *
++ * Public interface functions
++ *
++ **********************************************************************/
++
++/**
++ * mipv6_bul_iterate - apply interator function to all entries
++ * @func: function to apply
++ * @args: extra arguments for iterator
++ *
++ * Applies @func for each entry in Binding Update List.  Extra
++ * arguments given in @args are also passed to the iterator function.
++ * Caller must hold @bul_lock.
++ **/
++int mipv6_bul_iterate(hashlist_iterator_t func, void *args)
++{
++      DEBUG_FUNC();
++
++      return hashlist_iterate(bul.entries, args, func);
++}
++
++/**
++ * mipv6_bul_exists - check if Binding Update List entry exists
++ * @cn: address to check
++ *
++ * Checks if Binding Update List has an entry for @cn.  Returns true
++ * if entry exists, false otherwise. Caller may not hold @bul_lock.
++ **/
++int mipv6_bul_exists(struct in6_addr *cn, struct in6_addr *haddr)
++{
++      int exists;
++      struct in6_addr_pair hashkey;
++
++      DEBUG_FUNC();
++
++      hashkey.a1 = cn;
++      hashkey.a2 = haddr;
++      
++      read_lock_bh(&bul_lock);
++
++      if (unlikely(bul.entries == NULL))
++              exists = 0;
++      else
++              exists = (hashlist_get(bul.entries, &hashkey) != NULL);
++
++      read_unlock_bh(&bul_lock);
++      return exists;
++}
++
++/**
++ * mipv6_bul_get - get Binding Update List entry
++ * @cn_addr: CN address to search
++ * @home_addr: home address to search
++ *
++ * Returns Binding Update List entry for @cn_addr if it exists.
++ * Otherwise returns %NULL.  Caller must hold @bul_lock.
++ **/
++struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cn_addr, 
++                                    struct in6_addr *home_addr)
++{
++      struct mipv6_bul_entry *entry;
++      struct in6_addr_pair hashkey;
++      
++      DEBUG_FUNC();
++
++      if (unlikely(bul.entries == NULL)) {
++              return NULL;
++      }
++      hashkey.a1 = cn_addr;
++      hashkey.a2 = home_addr;
++
++      entry = (struct mipv6_bul_entry *) 
++              hashlist_get(bul.entries, &hashkey);
++              
++      return entry;
++}
++
++struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(
++      struct in6_addr *cn_addr, u8 *cookie)
++{
++      struct test_keys key;
++
++      DEBUG_FUNC();
++
++      if (unlikely(bul.entries == NULL))
++              return NULL;
++      key.addr = cn_addr;
++      key.cookie = cookie;
++
++      return (struct mipv6_bul_entry *) 
++              hashlist_get_ex(bul.entries, &key,
++                              bul_compare_cookie);
++}
++
++/**
++ * mipv6_bul_reschedule - reschedule Binding Update List entry
++ * @entry: entry to reschedule
++ *
++ * Reschedules a Binding Update List entry.  Must be called after
++ * modifying entry lifetime.  Caller must hold @bul_lock (write).
++ **/
++void mipv6_bul_reschedule(struct mipv6_bul_entry *entry)
++{
++      DEBUG_FUNC();
++
++      hashlist_reposition(bul.entries,
++                          (void *)entry,
++                          entry->callback_time);
++      timer_update();
++}
++
++/**
++ * mipv6_bul_add - add binding update to Binding Update List
++ * @cn_addr: IPv6 address where BU was sent
++ * @home_addr: Home address for this binding
++ * @coa: Care-of address for this binding
++ * @lifetime: expiration time of the binding in seconds
++ * @seq: sequence number of the BU
++ * @flags: %MIPV6_BU_F_* flags
++ * @callback: callback function called on expiration
++ * @callback_time: expiration time for callback
++ * @state: binding send state
++ * @delay: retransmission delay
++ * @maxdelay: retransmission maximum delay
++ * @ops: Mobility header options for BU
++ * @rr: Return routability information
++ *
++ * Adds a binding update sent to @cn_addr for @home_addr to the
++ * Binding Update List.  If entry already exists, it is updated.
++ * Entry is set to expire in @lifetime seconds.  Entry has a callback
++ * function @callback that is called at @callback_time.  Entry @state
++ * controls resending of this binding update and it can be set to
++ * %ACK_OK, %RESEND_EXP or %ACK_ERROR.  Returns a pointer to the newly
++ * created or updated entry.  Caller must hold @bul_lock (write).
++ **/
++struct mipv6_bul_entry *mipv6_bul_add(
++      struct in6_addr *cn_addr, struct in6_addr *home_addr,
++      struct in6_addr *coa, 
++      __u32 lifetime, __u16 seq, __u8 flags,
++      int (*callback)(struct mipv6_bul_entry *entry),
++      __u32 callback_time, 
++      __u8 state, __u32 delay, __u32 maxdelay,
++      struct mipv6_mh_opt *ops, 
++      struct mipv6_rr_info *rr)
++{
++      struct mipv6_bul_entry *entry;
++      int update = 0;
++      struct in6_addr_pair hashkey;
++
++      DEBUG_FUNC();
++
++      if (unlikely(bul.entries == NULL))
++              return NULL;
++
++      if (cn_addr == NULL || home_addr == NULL || coa == NULL || 
++          lifetime < 0 || callback == NULL || callback_time < 0 || 
++          (state != ACK_OK && state != RESEND_EXP && state != ACK_ERROR) ||
++          delay < 0 || maxdelay < 0) {
++              DEBUG(DBG_ERROR, "invalid arguments");
++              return NULL;
++      }
++      DEBUG(DBG_INFO, "cn_addr: %x:%x:%x:%x:%x:%x:%x:%x, "
++            "home_addr: %x:%x:%x:%x:%x:%x:%x:%x"
++            "coaddr: %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(cn_addr), 
++             NIPV6ADDR(home_addr), NIPV6ADDR(coa));
++      hashkey.a1 = cn_addr;
++      hashkey.a2 = home_addr;
++      
++      /* 
++       * decide whether to add a new entry or update existing, also
++       * check if there's room for a new entry when adding a new
++       * entry (latter is handled by mipv6_bul_get_entry() 
++       */
++      if ((entry = (struct mipv6_bul_entry *)
++           hashlist_get(bul.entries, &hashkey)) != NULL) {
++              /* if an entry for this cn_addr exists (with smaller
++               * seq than the new entry's seq), update it */
++              
++              if (MIPV6_SEQ_GT(seq, entry->seq)) {
++                      DEBUG(DBG_INFO, "updating an existing entry");
++                      update = 1;
++              } else {
++                      DEBUG(DBG_INFO, "smaller seq than existing, not updating");
++                      return NULL;
++              }
++      } else {
++              entry = mipv6_bul_get_entry();
++              if (entry == NULL) {
++                      DEBUG(DBG_WARNING, "binding update list full, can't add!!!");
++                      return NULL;
++              }
++              memset(entry, 0, sizeof(*entry));
++              /* First BU send happens here, save count in the entry */
++              entry->consecutive_sends = 1;
++      }
++
++      if (!update) {
++              ipv6_addr_copy(&(entry->cn_addr), cn_addr);
++              ipv6_addr_copy(&(entry->home_addr), home_addr);
++              entry->ops = ops;
++      }
++      /* Add Return Routability info to bul entry */
++      if (rr) {
++              if(entry->rr)
++                      kfree(entry->rr); 
++              entry->rr = rr;
++      }
++
++      ipv6_addr_copy(&(entry->coa), coa);
++      entry->lifetime = lifetime;
++      if (lifetime)
++              entry->expire = jiffies + lifetime * HZ;
++      else if (flags & MIPV6_BU_F_ACK)
++              entry->expire = jiffies + HOME_RESEND_EXPIRE * HZ;
++      entry->seq = seq;
++      entry->flags = flags;
++      entry->lastsend = jiffies; /* current time = last use of the entry */
++      entry->state = state;
++      entry->delay = delay;
++      entry->maxdelay = maxdelay;
++      entry->callback_time = jiffies + callback_time * HZ;
++      entry->callback = callback;
++
++      if (flags & MIPV6_BU_F_HOME && 
++          mipv6_mv_tnl_to_ha(cn_addr, coa, home_addr)) {
++              DEBUG(DBG_ERROR, "reconfiguration of the tunnel failed");
++      }
++      if (update) {
++              DEBUG(DBG_INFO, "updating entry: %x", entry);
++              hashlist_reposition(bul.entries, (void *)entry,
++                                  entry->callback_time);
++      } else {
++              DEBUG(DBG_INFO, "adding entry: %x", entry);
++
++              hashkey.a1 = &entry->cn_addr;
++              hashkey.a2 = &entry->home_addr;
++
++              if ((hashlist_add(bul.entries, &hashkey,
++                                entry->callback_time,
++                                entry)) < 0) {
++                      DEBUG(DBG_ERROR, "Hash add failed");
++                      mipv6_bul_entry_free(entry);                    
++                      return NULL;
++              }
++      }
++      timer_update(); 
++
++      return entry;
++}
++
++/**
++ * mipv6_bul_delete - delete Binding Update List entry
++ * @cn_addr: address for entry to delete
++ *
++ * Deletes the entry for @cn_addr from the Binding Update List.
++ * Returns zero if entry was deleted succesfully, otherwise returns
++ * negative.  Caller may not hold @bul_lock.
++ **/
++int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr)
++{
++      struct mipv6_bul_entry *entry;
++      struct in6_addr_pair hashkey;
++
++      DEBUG_FUNC();
++
++      hashkey.a1 = cn_addr;
++      hashkey.a2 = home_addr;
++
++      write_lock(&bul_lock);
++
++      if (unlikely(bul.entries == NULL) ||  
++          (entry = (struct mipv6_bul_entry *)
++           hashlist_get(bul.entries, &hashkey)) == NULL) {
++              write_unlock(&bul_lock);
++              DEBUG(DBG_INFO, "No such entry");
++              return -ENOENT;
++      }
++
++      hashlist_delete(bul.entries, (void *)entry);
++
++      del_bul_entry_tnl(entry);
++
++      mipv6_bul_entry_free(entry);
++      timer_update();
++      write_unlock(&bul_lock);
++
++      DEBUG(DBG_INFO, "Binding update list entry deleted");
++
++      return 0;
++}
++
++/**********************************************************************
++ *
++ * Proc interface functions
++ *
++ **********************************************************************/
++
++#define BUL_INFO_LEN 152
++
++struct procinfo_iterator_args {
++      char *buffer;
++      int offset;
++      int length;
++      int skip;
++      int len;
++};
++
++static int procinfo_iterator(void *data, void *args,
++                           unsigned long *sortkey)
++{
++      struct procinfo_iterator_args *arg =
++              (struct procinfo_iterator_args *)args;
++      struct mipv6_bul_entry *entry =
++              (struct mipv6_bul_entry *)data;
++      unsigned long callback_seconds;
++
++      DEBUG_FUNC();
++
++      if (entry == NULL) return ITERATOR_ERR;
++
++      if (time_after(jiffies, entry->callback_time))
++              callback_seconds = 0;
++      else
++              callback_seconds = (entry->callback_time - jiffies) / HZ;
++
++      if (arg->skip < arg->offset / BUL_INFO_LEN) {
++              arg->skip++;
++              return ITERATOR_CONT;
++      }
++
++      if (arg->len >= arg->length)
++              return ITERATOR_CONT;
++
++      /* CN HoA CoA ExpInSecs SeqNum State Delay MaxDelay CallbackInSecs */
++      arg->len += sprintf(arg->buffer + arg->len,
++                          "%08x%08x%08x%08x %08x%08x%08x%08x %08x%08x%08x%08x\n"
++                          "%010lu %05d %02d %010d %010d %010lu\n",
++                          ntohl(entry->cn_addr.s6_addr32[0]),
++                          ntohl(entry->cn_addr.s6_addr32[1]),
++                          ntohl(entry->cn_addr.s6_addr32[2]),
++                          ntohl(entry->cn_addr.s6_addr32[3]),
++                          ntohl(entry->home_addr.s6_addr32[0]),
++                          ntohl(entry->home_addr.s6_addr32[1]),
++                          ntohl(entry->home_addr.s6_addr32[2]),
++                          ntohl(entry->home_addr.s6_addr32[3]),
++                          ntohl(entry->coa.s6_addr32[0]),
++                          ntohl(entry->coa.s6_addr32[1]),
++                          ntohl(entry->coa.s6_addr32[2]),
++                          ntohl(entry->coa.s6_addr32[3]),
++                          (entry->expire - jiffies) / HZ,
++                          entry->seq, entry->state, entry->delay, 
++                          entry->maxdelay, callback_seconds);
++
++      return ITERATOR_CONT;
++}
++
++
++/*
++ * Callback function for proc filesystem.
++ */
++static int bul_proc_info(char *buffer, char **start, off_t offset,
++                            int length)
++{
++      struct procinfo_iterator_args args;
++
++      DEBUG_FUNC();
++
++      args.buffer = buffer;
++      args.offset = offset;
++      args.length = length;
++      args.skip = 0;
++      args.len = 0;
++
++      read_lock_bh(&bul_lock);
++      hashlist_iterate(bul.entries, &args, procinfo_iterator);
++      read_unlock_bh(&bul_lock);
++
++      *start = buffer;
++      if (offset)
++              *start += offset % BUL_INFO_LEN;
++
++      args.len -= offset % BUL_INFO_LEN;
++
++      if (args.len > length)
++              args.len = length;
++      if (args.len < 0)
++              args.len = 0;
++      
++      return args.len;
++}
++
++/**********************************************************************
++ *
++ * Code module init/fini functions
++ *
++ **********************************************************************/
++
++int __init mipv6_bul_init(__u32 size)
++{
++      DEBUG_FUNC();
++
++      if (size < 1) {
++              DEBUG(DBG_CRITICAL, 
++                    "Binding update list size must be at least 1");
++              return -EINVAL;
++      }
++      bul.entries = hashlist_create(MIPV6_BUL_HASHSIZE, size, 
++                                     sizeof(struct mipv6_bul_entry),
++                                     "mip6_bul", NULL, NULL,
++                                     bul_compare, bul_hash);
++
++      if (bul.entries == NULL) {
++              DEBUG(DBG_CRITICAL, "Couldn't allocate memory for "
++                    "hashlist when creating a binding update list");
++              return -ENOMEM;
++      }
++      init_timer(&bul.callback_timer);
++      bul.callback_timer.data = 0;
++      bul.callback_timer.function = timer_handler;
++      proc_net_create("mip6_bul", 0, bul_proc_info);
++      DEBUG(DBG_INFO, "Binding update list initialized");
++      return 0;
++}
++
++void __exit mipv6_bul_exit()
++{
++      struct mipv6_bul_entry *entry;
++      struct hashlist *entries;
++
++      DEBUG_FUNC();
++
++      proc_net_remove("mip6_bul");
++
++      write_lock_bh(&bul_lock);
++
++      DEBUG(DBG_INFO, "Stopping the bul timer");
++      del_timer(&bul.callback_timer);
++
++      while ((entry = (struct mipv6_bul_entry *) 
++              hashlist_get_first(bul.entries)) != NULL) {
++              hashlist_delete(bul.entries, (void *)entry);
++              
++              del_bul_entry_tnl(entry);
++              
++              mipv6_bul_entry_free(entry);
++      }
++      entries = bul.entries;
++      bul.entries = NULL;
++      write_unlock_bh(&bul_lock); 
++
++      hashlist_destroy(entries);
++
++      DEBUG(DBG_INFO, "binding update list destroyed");
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/bul.h
+@@ -0,0 +1,91 @@
++/*
++ *      MIPL Mobile IPv6 Binding Update List header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _BUL_H
++#define _BUL_H
++
++#include "hashlist.h"
++
++#define ACK_OK          0x01
++#define RESEND_EXP      0x02
++#define ACK_ERROR       0x04
++
++#define HOME_RESEND_EXPIRE 3600
++#define MIPV6_COOKIE_LEN 8
++struct mipv6_rr_info {
++      /* RR information */
++      u16 rr_state;                /* State of the RR */
++      u16 rr_flags;                /* Flags for the RR */
++      u8 hot_cookie[MIPV6_COOKIE_LEN];    /* HoT Cookie */
++      u8 cot_cookie[MIPV6_COOKIE_LEN];    /* CoT Cookie */
++      u8 home_cookie[MIPV6_COOKIE_LEN];   /* Home Cookie */
++      u8 careof_cookie[MIPV6_COOKIE_LEN]; /* Careof Cookie */
++      u32 lastsend_hoti;            /* When HoTI was last sent (jiffies) */
++      u32 lastsend_coti;            /* When CoTI was last sent (jiffies) */
++      u32 home_time;                /* when Care-of cookie was received */
++      u32 careof_time;              /* when Home cookie was received */
++      int home_nonce_index;         /* Home cookie nonce index */
++      int careof_nonce_index;       /* Care-of cookie nonce index */
++      u8 *kbu;                      /* Binding authentication key */
++};
++struct mipv6_bul_entry {
++      struct hashlist_entry e;
++      struct in6_addr cn_addr;        /* CN to which BU was sent */
++      struct in6_addr home_addr;      /* home address of this binding */
++      struct in6_addr coa;            /* care-of address of the sent BU */
++
++      unsigned long expire;           /* entry's expiration time (jiffies) */ 
++      __u32 lifetime;                 /* lifetime sent in this BU */
++      __u32 lastsend;                 /* last time when BU sent (jiffies) */
++      __u32 consecutive_sends;        /* Number of consecutive BU's sent */
++      __u16 seq;                      /* sequence number of the latest BU */
++      __u8 flags;                     /* BU send flags */
++      __u8 state;                     /* resend state */
++      __u32 initdelay;                /* initial ack wait */
++      __u32 delay;                    /* current ack wait */
++      __u32 maxdelay;                 /* maximum ack wait */
++
++      struct mipv6_rr_info *rr;
++      struct mipv6_mh_opt *ops;       /* saved option values */
++
++      unsigned long callback_time;
++      int (*callback)(struct mipv6_bul_entry *entry);
++};
++
++extern rwlock_t bul_lock;
++
++int mipv6_bul_init(__u32 size);
++
++void mipv6_bul_exit(void);
++
++struct mipv6_bul_entry *mipv6_bul_add(
++      struct in6_addr *cn_addr, struct in6_addr *home_addr,
++      struct in6_addr *coa, __u32 lifetime, __u16 seq, __u8 flags,
++      int (*callback)(struct mipv6_bul_entry *entry), __u32 callback_time,
++      __u8 state, __u32 delay, __u32 maxdelay, struct mipv6_mh_opt *ops,
++      struct mipv6_rr_info *rr);
++
++int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr);
++
++int mipv6_bul_exists(struct in6_addr *cnaddr, struct in6_addr *home_addr);
++
++struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cnaddr,
++                                    struct in6_addr *home_addr);
++struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(struct in6_addr *cn_addr,
++                                               u8 *cookie);
++
++int bul_entry_expired(struct mipv6_bul_entry *bulentry);
++
++void mipv6_bul_reschedule(struct mipv6_bul_entry *entry);
++
++int mipv6_bul_iterate(int (*func)(void *, void *, unsigned long *), void *args);
++
++#endif /* BUL_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/config.h
+@@ -0,0 +1,72 @@
++/*
++ * Configuration parameters
++ *
++ * $Id$
++ */
++
++#define MIPV6VERSION "D24"
++#define MIPLVERSION "v1.0"
++
++#define CAP_CN        0x01
++#define CAP_HA        0x02
++#define CAP_MN        0x04
++
++struct mip6_conf {
++      int capabilities;
++      int debug_level;
++      int accept_ret_rout;
++      int max_rtr_reachable_time;
++      int eager_cell_switching;
++      int max_num_tunnels;
++      int min_num_tunnels;
++      int binding_refresh_advice;
++      int bu_lladdr;
++      int bu_keymgm;
++      int bu_cn_ack;
++};
++
++extern struct mip6_conf mip6node_cnf;
++
++struct mipv6_bce;
++
++struct mip6_func {
++      void (*bce_home_add) (int ifindex, struct in6_addr *daddr, 
++                            struct in6_addr *haddr, struct in6_addr *coa,
++                            struct in6_addr *rep_coa, __u32 lifetime, 
++                            __u16 sequence, __u8 flags, __u8 *k_bu);
++      void (*bce_cache_add) (int ifindex, struct in6_addr *daddr,
++                             struct in6_addr *haddr, struct in6_addr *coa,
++                             struct in6_addr *rep_coa, __u32 lifetime,
++                             __u16 sequence, __u8 flags, __u8 *k_bu);
++      void (*bce_home_del) (struct in6_addr *daddr, struct in6_addr *haddr, 
++                            struct in6_addr *coa, struct in6_addr *rep_coa,
++                            __u16 sequence, __u8 flags,
++                            __u8 *k_bu);
++      void (*bce_cache_del) (struct in6_addr *daddr, struct in6_addr *haddr, 
++                             struct in6_addr *coa, struct in6_addr *rep_coa,
++                             __u16 sequence, __u8 flags,
++                             __u8 *k_bu);
++      
++      int (*bce_tnl_rt_add) (struct in6_addr *coa, 
++                             struct in6_addr *ha_addr, 
++                             struct in6_addr *home_addr);
++
++      void (*bce_tnl_rt_del) (struct in6_addr *coa, 
++                              struct in6_addr *ha_addr, 
++                              struct in6_addr *home_addr);
++
++      void (*proxy_del) (struct in6_addr *home_addr, struct mipv6_bce *entry);
++      int (*proxy_create) (int flags, int ifindex, struct in6_addr *coa,
++                           struct in6_addr *our_addr, struct in6_addr *home_addr);
++
++      int (*icmpv6_dhaad_rep_rcv) (struct sk_buff *skb);
++      int (*icmpv6_dhaad_req_rcv) (struct sk_buff *skb);
++      int (*icmpv6_pfxadv_rcv) (struct sk_buff *skb);
++      int (*icmpv6_pfxsol_rcv) (struct sk_buff *skb);
++      int (*icmpv6_paramprob_rcv) (struct sk_buff *skb);
++
++      int (*mn_use_hao) (struct in6_addr *daddr, struct in6_addr *saddr);
++      void (*mn_check_tunneled_packet) (struct sk_buff *skb);
++};
++
++extern struct mip6_func mip6_fn;
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/debug.h
+@@ -0,0 +1,112 @@
++/*
++ *      MIPL Mobile IPv6 Debugging macros and functions
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _DEBUG_H
++#define _DEBUG_H
++
++#include <linux/autoconf.h>
++
++/* priorities for different debug conditions */
++
++#define DBG_CRITICAL   0 /* unrecoverable error                     */
++#define DBG_ERROR      1 /* error (recoverable)                     */
++#define DBG_WARNING    2 /* unusual situation but not a real error  */
++#define DBG_INFO       3 /* generally useful information            */
++#define DBG_EXTRA      4 /* extra information                       */
++#define DBG_FUNC_ENTRY 6 /* use to indicate function entry and exit */
++#define DBG_DATADUMP   7 /* packet dumps, etc. lots of flood        */
++
++/**
++ * NIPV6ADDR - macro for IPv6 addresses
++ * @addr: Network byte order IPv6 address
++ *
++ * Macro for printing IPv6 addresses.  Used in conjunction with
++ * printk() or derivatives (such as DEBUG macro).
++ **/
++#define NIPV6ADDR(addr) \
++        ntohs(((u16 *)addr)[0]), \
++        ntohs(((u16 *)addr)[1]), \
++        ntohs(((u16 *)addr)[2]), \
++        ntohs(((u16 *)addr)[3]), \
++        ntohs(((u16 *)addr)[4]), \
++        ntohs(((u16 *)addr)[5]), \
++        ntohs(((u16 *)addr)[6]), \
++        ntohs(((u16 *)addr)[7])
++
++#ifdef CONFIG_IPV6_MOBILITY_DEBUG
++extern int mipv6_debug;
++
++/**
++ * debug_print - print debug message
++ * @debug_level: message priority
++ * @fname: calling function's name
++ * @fmt: printf-style formatting string
++ *
++ * Prints a debug message to system log if @debug_level is less or
++ * equal to @mipv6_debug.  Should always be called using DEBUG()
++ * macro, not directly.
++ **/
++static void debug_print(int debug_level, const char *fname, const char* fmt, ...)
++{
++      char s[1024];
++      va_list args;
++ 
++      if (mipv6_debug < debug_level)
++              return;
++ 
++      va_start(args, fmt);
++      vsprintf(s, fmt, args);
++      printk("mip6[%s]: %s\n", fname, s);
++      va_end(args);
++}
++
++/**
++ * debug_print_buffer - print arbitrary buffer to system log
++ * @debug_level: message priority
++ * @data: pointer to buffer
++ * @len: number of bytes to print
++ *
++ * Prints @len bytes from buffer @data to system log.  @debug_level
++ * tells on which debug level message gets printed.  For
++ * debug_print_buffer() priority %DBG_DATADUMP should be used.
++ **/
++#define debug_print_buffer(debug_level,data,len) { \
++      if (mipv6_debug >= debug_level) { \
++      int i; \
++      for (i=0; i<len; i++) { \
++              if (i%16 == 0) printk("\n%04x: ", i); \
++              printk("%02x ", ((unsigned char *)data)[i]); \
++      } \
++      printk("\n\n"); \
++      } \
++}
++
++#define DEBUG(x,y,z...) debug_print(x,__FUNCTION__,y,##z)
++#define DEBUG_FUNC() \
++DEBUG(DBG_FUNC_ENTRY, "%s(%d)/%s: ", __FILE__,__LINE__,__FUNCTION__)
++
++#else
++#define DEBUG(x,y,z...)
++#define DEBUG_FUNC()
++#define debug_print_buffer(x,y,z)
++#endif
++
++#undef ASSERT
++#define ASSERT(expression) { \
++        if (!(expression)) { \
++                (void)printk(KERN_ERR \
++                 "Assertion \"%s\" failed: file \"%s\", function \"%s\", line %d\n", \
++                 #expression, __FILE__, __FUNCTION__, __LINE__); \
++              BUG(); \
++        } \
++}
++
++#endif /* _DEBUG_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.c
+@@ -0,0 +1,394 @@
++/*
++ *    Extension Header handling and adding code
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>    
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/types.h>
++#include <linux/slab.h>
++
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/addrconf.h>
++#include <net/mipv6.h>
++
++#include "debug.h"
++#include "stats.h"
++#include "mobhdr.h"
++#include "bcache.h"
++#include "config.h"
++
++/**
++ * mipv6_append_home_addr - Add Home Address Option
++ * @opt: buffer for Home Address Option
++ * @offset: offset from beginning of @opt
++ * @addr: address for HAO
++ *
++ * Adds a Home Address Option to a packet.  Option is stored in
++ * @offset from beginning of @opt.  The option is created but the
++ * original source address in IPv6 header is left intact.  The source
++ * address will be changed from home address to CoA after the checksum
++ * has been calculated in getfrag.  Padding is done automatically, and
++ * @opt must have allocated space for both actual option and pad.
++ * Returns offset from @opt to end of options.
++ **/
++int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr)
++{
++      int pad;
++      struct mipv6_dstopt_homeaddr *ho;
++
++      DEBUG(DBG_DATADUMP, "HAO: %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(addr));
++
++      pad = (6 - offset) & 7;
++      mipv6_add_pad(opt + offset, pad);
++
++      ho = (struct mipv6_dstopt_homeaddr *)(opt + offset + pad);
++      ho->type = MIPV6_TLV_HOMEADDR;
++      ho->length = sizeof(*ho) - 2;
++      ipv6_addr_copy(&ho->addr, addr); 
++
++      return offset + pad + sizeof(*ho);
++}
++static inline int check_hao_validity(struct mipv6_dstopt_homeaddr *haopt, 
++                                   u8 *dst1, 
++                                   struct in6_addr *saddr, 
++                                   struct in6_addr *daddr)
++{
++      int addr_type = ipv6_addr_type(&haopt->addr);
++      struct mipv6_bce bc_entry;
++      
++      if (addr_type & IPV6_ADDR_LINKLOCAL || 
++          !(addr_type & IPV6_ADDR_UNICAST)) {
++              DEBUG(DBG_INFO, "HAO with link local or non-unicast HoA, "
++                    "not sending BE to "
++                    "home address " 
++                    "%x:%x:%x:%x:%x:%x:%x:%x ",
++                    "care-of  address %x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(&haopt->addr),
++                    NIPV6ADDR(saddr));
++              return -EINVAL;
++      } else if (dst1[0] != IPPROTO_MOBILITY && 
++          (mipv6_bcache_get(&haopt->addr, 
++                            daddr, &bc_entry) != 0 ||
++           ipv6_addr_cmp(saddr, &bc_entry.coa))) {
++              DEBUG(DBG_INFO, "HAO without binding or incorrect CoA, "
++                    "sending BE code 1: "
++                    "home address %x:%x:%x:%x:%x:%x:%x:%x",
++                    "to care-of address %x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(&haopt->addr),
++                    NIPV6ADDR(saddr));
++              return -ENOENT;
++      }
++      return 0;
++}
++/**
++ * mipv6_handle_homeaddr - Home Address Destination Option handler
++ * @skb: packet buffer
++ * @optoff: offset to where option begins
++ *
++ * Handles Home Address Option in IPv6 Destination Option header.
++ * Packet and offset to option are passed.  If HAO is used without
++ * binding, sends a Binding Error code 1.  When sending BE, notify bit
++ * is cleared to prevent IPv6 error handling from sending ICMP
++ * Parameter Problem.  Returns 1 on success, otherwise zero.
++ **/
++int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff)
++{
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct in6_addr coaddr;
++      struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
++      struct mipv6_dstopt_homeaddr *haopt =
++          (struct mipv6_dstopt_homeaddr *) &skb->nh.raw[optoff];
++      u8 *dst1;
++      int err;
++
++      DEBUG_FUNC();
++      
++      if (haopt->length != sizeof(*haopt) - 2) {
++              DEBUG(DBG_WARNING, "HAO has invalid length");
++              MIPV6_INC_STATS(n_ha_drop.invalid);
++              return 0;
++      }
++      dst1 = (u8 *)skb->h.raw;
++      err = check_hao_validity(haopt, dst1, saddr, &skb->nh.ipv6h->daddr);
++
++      if (err) {
++              haopt->type &= ~(0x80); /* clear notify bit */
++              if (err == -ENOENT)
++                      mipv6_send_be(&skb->nh.ipv6h->daddr, saddr,
++                                    &haopt->addr, MIPV6_BE_HAO_WO_BINDING);
++              MIPV6_INC_STATS(n_ha_drop.misc);
++              return 0;
++      }
++      ipv6_addr_copy(&coaddr, saddr);
++      ipv6_addr_copy(saddr, &haopt->addr);
++      ipv6_addr_copy(&haopt->addr, &coaddr);
++      opt->hao = optoff;
++      if (mip6_fn.mn_check_tunneled_packet != NULL)
++              mip6_fn.mn_check_tunneled_packet(skb);
++
++      MIPV6_INC_STATS(n_ha_rcvd);
++      return 1;
++}
++
++/**
++ * mipv6_icmp_swap_addrs - Switch HAO and src and RT2 and dest for ICMP errors
++ * @skb: packet buffer
++ *
++ * Reset the source address and the Home Address option in skb before
++ * appending it to an ICMP error message, so original packet appears
++ * in the error message rather than mangled.
++ **/
++void mipv6_icmp_swap_addrs(struct sk_buff *skb)
++{
++      struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
++      struct in6_addr tmp;
++      struct in6_addr *hoa;
++      DEBUG_FUNC();
++      if (opt->srcrt2) {
++              struct rt2_hdr *rt2;
++              rt2 = (struct rt2_hdr *)(skb->nh.raw + opt->srcrt2);
++              hoa = &rt2->addr;
++
++              ipv6_addr_copy(&tmp, hoa);
++              ipv6_addr_copy(hoa, &skb->nh.ipv6h->daddr);
++              ipv6_addr_copy(&skb->nh.ipv6h->daddr, &tmp);
++              rt2->rt_hdr.segments_left++;
++              skb->nh.ipv6h->hop_limit++;
++      }
++      if (opt->hao) {
++              struct mipv6_dstopt_homeaddr *hao;
++              hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao);
++              hoa = &hao->addr;
++
++              ipv6_addr_copy(&tmp, hoa);
++              ipv6_addr_copy(hoa, &skb->nh.ipv6h->saddr);
++              ipv6_addr_copy(&skb->nh.ipv6h->saddr, &tmp);
++      }
++}
++
++/**
++ * mipv6_append_rt2hdr - Add Type 2 Routing Header
++ * @rt: buffer for new routing header
++ * @addr: intermediate hop address
++ *
++ * Adds a Routing Header Type 2 in a packet.  Stores newly created
++ * routing header in buffer @rt.  Type 2 RT only carries one address,
++ * so there is no need to process old routing header.  @rt must have
++ * allocated space for 24 bytes.
++ **/
++void mipv6_append_rt2hdr(struct ipv6_rt_hdr *rt, struct in6_addr *addr)
++{
++      struct rt2_hdr *rt2 = (struct rt2_hdr *)rt;
++
++        DEBUG(DBG_DATADUMP, "RT2: %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(addr));
++
++      if (ipv6_addr_type(addr) == IPV6_ADDR_MULTICAST) {
++              DEBUG(DBG_ERROR, "destination address not unicast");
++              return;
++      }
++
++      memset(rt2, 0, sizeof(*rt2));
++      rt2->rt_hdr.type = 2;
++      rt2->rt_hdr.hdrlen = 2;
++      rt2->rt_hdr.segments_left = 1;
++      ipv6_addr_copy(&rt2->addr, addr);
++}
++
++/**
++ * mipv6_append_dst1opts - Add Destination Option (1) Headers
++ * @dst1opt: buffer for new destination options
++ * @saddr: address for Home Address Option
++ * @old_dst1opt: old destination options
++ * @len: length of options
++ *
++ * Adds Destination Option (1) Header to a packet.  New options are
++ * stored in @dst1opt.  If old destination options exist, they are
++ * copied from @old_dst1opt.  Only Home Address Option is destination
++ * option.  @dstopt must have allocated space for @len bytes.  @len
++ * includes Destination Option Header (2 bytes), Home Address Option
++ * (18 bytes) and possible HAO pad (8n+6).
++ **/
++/*
++ * ISSUE: Home Address Destination Option should really be added to a
++ * new destination option header specified in Mobile IPv6 spec which
++ * should be placed after routing header(s), but before fragmentation
++ * header.  Putting HAO in DO1 works for now, but support for the new
++ * placement should be added to the IPv6 stack.
++ */
++void 
++mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr,
++                    struct ipv6_opt_hdr *old_dst1opt, int len)
++{
++      int offset;
++
++      if (old_dst1opt) {
++              memcpy(dst1opt, old_dst1opt, ipv6_optlen(old_dst1opt));
++              offset = ipv6_optlen(old_dst1opt);
++      } else {
++              offset = sizeof (*dst1opt);
++      }
++      dst1opt->hdrlen = (len >> 3) - 1;
++      mipv6_append_home_addr((__u8 *) dst1opt, offset, saddr);
++}
++
++/**
++ * mipv6_modify_txoptions - Modify outgoing packets
++ * @sk: socket
++ * @skb: packet buffer for outgoing packet
++ * @old_opt: transmit options
++ * @fl: packet flow structure
++ * @dst: pointer to destination cache entry
++ *
++ * Adds Home Address Option (for MN packets, when not at home) and
++ * Routing Header Type 2 (for CN packets when sending to an MN) to
++ * data packets.  Old extension headers are copied from @old_opt (if
++ * any).  Extension headers are _explicitly_ added for packets with
++ * Mobility Header.  Returns the new header structure, or old if no
++ * changes.
++ **/
++struct ipv6_txoptions *
++mipv6_modify_txoptions(struct sock *sk, struct sk_buff *skb, 
++                     struct ipv6_txoptions *old_opt, struct flowi *fl, 
++                     struct dst_entry **dst)
++{     
++      struct ipv6_opt_hdr *old_hopopt = NULL;
++      struct ipv6_opt_hdr *old_dst1opt = NULL;
++      struct ipv6_rt_hdr *old_srcrt = NULL;
++
++      int srcrtlen = 0, dst1len = 0;
++      int tot_len, use_hao = 0;
++      struct ipv6_txoptions *opt;
++      struct mipv6_bce bc_entry;
++      struct in6_addr tmpaddr, *saddr, *daddr, coaddr;
++      __u8 *opt_ptr;
++
++      DEBUG_FUNC();
++
++      if (fl->proto == IPPROTO_MOBILITY) return old_opt;
++      /*
++       * we have to be prepared to the fact that saddr might not be present,
++       * if that is the case, we acquire saddr just as kernel does.
++       */
++      saddr = fl ? fl->fl6_src : NULL;
++      daddr = fl ? fl->fl6_dst : NULL;
++
++      if (daddr == NULL)
++              return old_opt;
++      if (saddr == NULL) {
++              int err = ipv6_get_saddr(NULL, daddr, &tmpaddr);
++              if (err)
++                      return old_opt;
++              else
++                      saddr = &tmpaddr;
++      }
++
++      DEBUG(DBG_DATADUMP,
++            "dest. address of packet: %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(daddr));
++      DEBUG(DBG_DATADUMP, " and src. address: %x:%x:%x:%x:%x:%x:%x:%x", 
++            NIPV6ADDR(saddr));
++
++      if (old_opt) {
++              old_hopopt = old_opt->hopopt;
++              old_dst1opt = old_opt->dst1opt;
++              old_srcrt = old_opt->srcrt;
++      } 
++
++      if (mip6_fn.mn_use_hao != NULL)
++              use_hao = mip6_fn.mn_use_hao(daddr, saddr);
++
++      if (use_hao) {
++              if (old_dst1opt)
++                      dst1len = ipv6_optlen(old_dst1opt);
++              dst1len += sizeof(struct mipv6_dstopt_homeaddr) +
++                      ((6 - dst1len) & 7); /* padding */
++      }
++
++      if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0)
++              srcrtlen = sizeof(struct rt2_hdr);
++
++      if ((tot_len = srcrtlen + dst1len) == 0) { 
++              return old_opt;
++      }
++
++      tot_len += sizeof(*opt);
++
++      if (!(opt = kmalloc(tot_len, GFP_ATOMIC))) {
++              return NULL;
++      }
++      memset(opt, 0, tot_len);
++      opt->tot_len = tot_len;
++      opt_ptr = (__u8 *) (opt + 1);
++      
++      if (old_srcrt) {
++              opt->srcrt = old_srcrt;
++              opt->opt_nflen += ipv6_optlen(old_srcrt);
++      }
++
++      if (srcrtlen) {
++              DEBUG(DBG_DATADUMP, "Binding exists. Adding routing header");
++
++              opt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr;
++              opt->opt_nflen += srcrtlen;
++              opt_ptr += srcrtlen;
++              
++              /*
++               * Append care-of-address to routing header (original
++               * destination address is home address, the first
++               * source route segment gets put to the destination
++               * address and the home address gets to the last
++               * segment of source route (just as it should)) 
++               */
++
++              ipv6_addr_copy(&coaddr, &bc_entry.coa);
++
++              mipv6_append_rt2hdr(opt->srcrt2, &coaddr);
++
++              /*
++               * reroute output (we have to do this in case of TCP
++                 * segment) unless a routing header of type 0 is also added
++               */
++              if (dst && !opt->srcrt) {
++                      struct in6_addr *tmp = fl->fl6_dst;
++                      fl->fl6_dst = &coaddr;
++
++                      dst_release(*dst);
++                      *dst = ip6_route_output(sk, fl);
++                      if (skb)
++                              skb->dst = *dst;
++                      fl->fl6_dst = tmp;
++
++                      DEBUG(DBG_DATADUMP, "Rerouted outgoing packet");
++              }
++      }
++
++      /* Only home address option is inserted to first dst opt header */
++      if (dst1len) {
++              opt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr;
++              opt->opt_flen += dst1len;
++              opt_ptr += dst1len;
++              mipv6_append_dst1opts(opt->dst1opt, saddr, 
++                                    old_dst1opt, dst1len);
++              opt->mipv6_flags = MIPV6_SND_HAO;
++      } else if (old_dst1opt) {
++              opt->dst1opt = old_dst1opt;
++              opt->opt_flen += ipv6_optlen(old_dst1opt);
++      }
++      if (old_hopopt) {
++              opt->hopopt = old_hopopt;
++              opt->opt_nflen += ipv6_optlen(old_hopopt);
++      }       
++      
++      return opt;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.h
+@@ -0,0 +1,47 @@
++/*
++ *    MIPL Mobile IPv6 Extension Headers header file
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _MIPV6_EXTHDRS_H
++#define _MIPV6_EXTHDRS_H
++
++struct in6_addr;
++struct sk_buff;
++struct ipv6_rt_hdr;
++struct ipv6_opt_hdr;
++struct ipv6_txoptions;
++struct flowi;
++struct dst_entry;
++/*
++ * Home Address Destination Option function prototypes
++ */
++int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr);
++
++int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff);
++
++void mipv6_icmp_swap_addrs(struct sk_buff *skb);
++
++/*
++ * Creates a routing header of type 2.
++ */
++void mipv6_append_rt2hdr(struct ipv6_rt_hdr *srcrt, struct in6_addr *addr);
++
++/* Function to add the first destination option header, which may
++ * include a home address option.  
++ */
++void mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr,
++                         struct ipv6_opt_hdr *old_dst1opt, int len);
++
++struct ipv6_txoptions *mipv6_modify_txoptions(
++      struct sock *sk, struct sk_buff *skb,
++      struct ipv6_txoptions *old_opt, struct flowi *fl,
++      struct dst_entry **dst);
++
++#endif /* _MIPV6_EXTHDRS_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/ha.c
+@@ -0,0 +1,553 @@
++/*
++ *      Home-agent functionality
++ *
++ *      Authors:
++ *      Sami Kivisaari           <skivisaa@cc.hut.fi>
++ *      Henrik Petander          <lpetande@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *   
++ *      Changes: Venkata Jagana,
++ *               Krishna Kumar     : Statistics fix
++ *               Masahide Nakamura : Use of mipv6_forward  
++ *     
++ */
++
++#include <linux/autoconf.h>
++#include <linux/net.h>
++#include <linux/skbuff.h>
++#include <linux/if_ether.h>
++#include <linux/netdevice.h>
++#include <linux/in6.h>
++#include <linux/init.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv6.h>
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif
++
++#include <net/neighbour.h>
++#include <net/ipv6.h>
++#include <net/ip6_fib.h>
++#include <net/ip6_route.h>
++#include <net/ndisc.h>
++#include <net/addrconf.h>
++#include <net/neighbour.h>
++
++#include "tunnel_ha.h"
++#include "bcache.h"
++#include "stats.h"
++#include "debug.h"
++#include "util.h"
++#include "ha.h"
++#include "config.h"
++#include "mobhdr.h"
++
++static int mipv6_ha_tunnel_sitelocal = 0;
++
++#ifdef CONFIG_SYSCTL
++
++static struct ctl_table_header *mipv6_ha_sysctl_header;
++
++static struct mipv6_ha_sysctl_table
++{
++      struct ctl_table_header *sysctl_header;
++      ctl_table mipv6_vars[3];
++      ctl_table mipv6_mobility_table[2];
++      ctl_table mipv6_proto_table[2];
++      ctl_table mipv6_root_table[2];
++} mipv6_ha_sysctl = {
++      NULL,
++
++        {{NET_IPV6_MOBILITY_TUNNEL_SITELOCAL, "tunnel_sitelocal",
++        &mipv6_ha_tunnel_sitelocal, sizeof(int), 0644, NULL, 
++        &proc_dointvec},
++       {0}},
++
++      {{NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, 
++        mipv6_ha_sysctl.mipv6_vars}, {0}},
++      {{NET_IPV6, "ipv6", NULL, 0, 0555, 
++        mipv6_ha_sysctl.mipv6_mobility_table}, {0}},
++      {{CTL_NET, "net", NULL, 0, 0555, 
++        mipv6_ha_sysctl.mipv6_proto_table}, {0}}
++};
++
++#endif /* CONFIG_SYSCTL */
++
++
++/*  this is defined in kernel IPv6 module (sockglue.c)  */
++extern struct packet_type ipv6_packet_type;
++
++/* mipv6_forward: Intercept NS packets destined to home address of MN */
++int mipv6_forward(struct sk_buff *skb)
++{
++      struct ipv6hdr *ipv6h;
++      struct in6_addr *daddr, *saddr;
++      __u8 nexthdr;
++      int nhoff;
++      
++      if (skb == NULL) return  0;
++      
++      ipv6h = skb->nh.ipv6h;
++      daddr = &ipv6h->daddr;
++      saddr = &ipv6h->saddr;
++
++      nexthdr = ipv6h->nexthdr;
++      nhoff = sizeof(*ipv6h);
++   
++      if (ipv6_ext_hdr(nexthdr))
++              nhoff = ipv6_skip_exthdr(skb, nhoff, &nexthdr,
++                                       skb->len - sizeof(*ipv6h));
++      
++      /* Do not to forward Neighbor Solicitation to Home Address of MN */
++      if (nexthdr == IPPROTO_ICMPV6) {
++              struct icmp6hdr *icmp6h;
++              int dest_type;
++              
++              if (nhoff < 0 || !pskb_may_pull(skb, nhoff + 
++                                              sizeof(struct icmp6hdr))) {
++                      kfree_skb(skb);
++                      return 0;
++                   }
++              
++              dest_type = ipv6_addr_type(daddr);
++              icmp6h = (struct icmp6hdr *)&skb->nh.raw[nhoff];
++              
++              /* Intercepts NS to HoA of MN */
++
++              if ((icmp6h->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) ||
++                  ((dest_type & IPV6_ADDR_MULTICAST) &&
++                   (icmp6h->icmp6_type == NDISC_ROUTER_ADVERTISEMENT))) {
++                      ip6_input(skb);
++              } else {
++                      ip6_forward(skb);
++              }
++      } else {
++              ip6_forward(skb);
++      }
++      return 0;
++}
++
++
++/**
++ * mipv6_proxy_nd_rem - stop acting as a proxy for @home_address
++ * @home_addr: address to remove
++ * @ha_addr: home agent's address on home link
++ * @linklocal: link-local compatibility bit
++ *
++ * When Home Agent acts as a proxy for an address it must leave the
++ * solicited node multicast group for that address and stop responding 
++ * to neighbour solicitations.  
++ **/
++static int mipv6_proxy_nd_rem(struct in6_addr *home_addr,
++                            int ifindex, int linklocal)
++{
++        /* When MN returns home HA leaves the solicited mcast groups
++         * for MNs home addresses 
++       */
++      int err;
++      struct net_device *dev;
++      
++      DEBUG_FUNC();
++      
++        if ((dev = dev_get_by_index(ifindex)) == NULL) {
++              DEBUG(DBG_ERROR, "couldn't get dev");
++              return -ENODEV;
++      }
++#if 1 /* TEST */
++      /* Remove link-local entry */
++      if (linklocal) {
++              struct in6_addr ll_addr;
++              mipv6_generate_ll_addr(&ll_addr, home_addr);
++              if ((err = pneigh_delete(&nd_tbl, &ll_addr, dev)) < 0) {
++                      DEBUG(DBG_INFO,
++                            "peigh_delete failed for "
++                            "%x:%x:%x:%x:%x:%x:%x:%x",
++                            NIPV6ADDR(&ll_addr));     
++              }
++      }
++#endif
++      /* Remove global (or site-local) entry */
++      if ((err = pneigh_delete(&nd_tbl, home_addr, dev)) < 0) {
++              DEBUG(DBG_INFO,
++                    "peigh_delete failed for " 
++                    "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(home_addr));
++      }
++      dev_put(dev);
++      return err;
++}
++
++/**
++ * mipv6_proxy_nd - join multicast group for this address
++ * @home_addr: address to defend
++ * @ha_addr: home agent's address on home link
++ * @linklocal: link-local compatibility bit
++ *
++ * While Mobile Node is away from home, Home Agent acts as a proxy for
++ * @home_address. HA responds to neighbour solicitations for  @home_address 
++ * thus getting all packets destined to home address of MN. 
++ **/
++static int mipv6_proxy_nd(struct in6_addr *home_addr, 
++                        int ifindex, int linklocal)
++{  
++      /* The HA sends a proxy ndisc_na message to all hosts on MN's
++       * home subnet by sending a neighbor advertisement with the
++       * home address or all addresses of the mobile node if the
++       * prefix is not 0. The addresses are formed by combining the
++       * suffix or the host part of the address with each subnet
++       * prefix that exists in the home subnet 
++       */
++      
++        /* Since no previous entry for MN exists a proxy_nd advertisement
++       * is sent to all nodes link local multicast address
++       */     
++      int err = -1;
++
++      struct net_device *dev;
++      struct in6_addr na_saddr;
++      struct in6_addr ll_addr;
++      struct pneigh_entry *ll_pneigh;
++      struct in6_addr mcdest;
++      int send_ll_na = 0;
++      int inc_opt = 1;
++      int solicited = 0;
++      int override = 1;
++      
++      DEBUG_FUNC();
++      
++      if ((dev = dev_get_by_index(ifindex)) == NULL) {
++              DEBUG(DBG_ERROR, "couldn't get dev");
++              return -ENODEV;
++      }
++      
++      if (!pneigh_lookup(&nd_tbl, home_addr, dev, 1)) {
++              DEBUG(DBG_INFO,
++                    "peigh_lookup failed for "
++                    "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(home_addr));
++              goto free_dev;
++      }
++#if 1 /* TEST */
++      if (linklocal) {
++              mipv6_generate_ll_addr(&ll_addr, home_addr);
++              
++              if ((ll_pneigh = pneigh_lookup(&nd_tbl, &ll_addr, 
++                                             dev, 1)) == NULL) {
++                      DEBUG(DBG_INFO,
++                            "peigh_lookup failed for "
++                            "%x:%x:%x:%x:%x:%x:%x:%x",
++                            NIPV6ADDR(&ll_addr));
++                      pneigh_delete(&nd_tbl, home_addr, dev);
++                      goto free_dev;
++              } else {
++                      send_ll_na = 1;
++              }
++      } else {
++              ll_pneigh = NULL;
++      }
++#endif        
++      /* Proxy neighbor advertisement of MN's home address 
++       * to all nodes solicited multicast address 
++       */
++      if (!ipv6_get_lladdr(dev, &na_saddr)) { 
++              ipv6_addr_all_nodes(&mcdest); 
++              ndisc_send_na(dev, NULL, &mcdest, home_addr, 0, 
++                            solicited, override, inc_opt);
++#if 1 /* TEST */
++              if (send_ll_na) {
++                      ndisc_send_na(dev, NULL, &mcdest, &ll_addr, 
++                                    0, solicited, override, inc_opt);
++              }
++#endif
++              err = 0;
++      } else {
++              DEBUG(DBG_ERROR, "failed to get link local address for sending proxy NA");
++      }
++free_dev:
++      dev_put(dev);
++      return err;
++      
++}
++
++struct inet6_ifaddr *is_on_link_ipv6_address(struct in6_addr *mn_haddr,
++                                           struct in6_addr *ha_addr)
++{
++      struct inet6_ifaddr *ifp;
++      struct inet6_dev *in6_dev;
++      struct inet6_ifaddr *oifp = NULL;
++
++      if ((ifp = ipv6_get_ifaddr(ha_addr, 0)) == NULL)
++              return NULL;
++
++      if ((in6_dev = ifp->idev) != NULL) {
++              in6_dev_hold(in6_dev);
++              oifp = in6_dev->addr_list;
++              while (oifp != NULL) {
++                      spin_lock(&oifp->lock);
++                      if (mipv6_prefix_compare(&oifp->addr, mn_haddr,
++                                               oifp->prefix_len) &&
++                          !(oifp->flags & IFA_F_TENTATIVE)) {
++                              spin_unlock(&oifp->lock);
++                              DEBUG(DBG_INFO, "Home Addr Opt: on-link");
++                              in6_ifa_hold(oifp);
++                              break;
++                      }
++                      spin_unlock(&oifp->lock);
++                      oifp = oifp->if_next;
++              }
++              in6_dev_put(in6_dev);
++      }
++      in6_ifa_put(ifp);
++/*      DEBUG(DBG_WARNING, "Home Addr Opt NOT on-link"); */
++      return oifp;
++
++}
++
++/*
++ * Lifetime checks. ifp->valid_lft >= ifp->prefered_lft always (see addrconf.c)
++ * Returned value is in seconds.
++ */
++
++static __u32 get_min_lifetime(struct inet6_ifaddr *ifp, __u32 lifetime)
++{
++      __u32 rem_lifetime = 0;
++      unsigned long now = jiffies;
++
++      if (ifp->valid_lft == 0) {
++              rem_lifetime = lifetime;
++      } else {
++              __u32 valid_lft_left =
++                  ifp->valid_lft - ((now - ifp->tstamp) / HZ);
++              rem_lifetime =
++                  min_t(unsigned long, valid_lft_left, lifetime);
++      }
++
++      return rem_lifetime;
++}
++
++#define MAX_LIFETIME 1000
++
++/**
++ * mipv6_lifetime_check - check maximum lifetime is not exceeded
++ * @lifetime: lifetime to check
++ *
++ * Checks @lifetime does not exceed %MAX_LIFETIME.  Returns @lifetime
++ * if not exceeded, otherwise returns %MAX_LIFETIME.
++ **/
++static int mipv6_lifetime_check(int lifetime)
++{
++      return (lifetime > MAX_LIFETIME) ? MAX_LIFETIME : lifetime;
++}
++
++/* Generic routine handling finish of BU processing */
++void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, __u8 ba_status,
++                   struct in6_addr *daddr, struct in6_addr *haddr,
++                   struct in6_addr *coa, struct in6_addr *rep_coa,
++                   __u32 ba_lifetime, __u16 sequence, __u8 flags, __u8 *k_bu)
++{
++      int err;
++
++      if (ba_status >= REASON_UNSPECIFIED) {
++              /* DAD failed */
++              goto out;
++      }
++      
++      ba_lifetime = get_min_lifetime(ifp, ba_lifetime);
++      ba_lifetime = mipv6_lifetime_check(ba_lifetime);
++
++      if ((err = mipv6_bcache_add(ifindex, daddr, haddr, coa, 
++                                  ba_lifetime, sequence, flags,
++                                  HOME_REGISTRATION)) != 0 ) {
++              DEBUG(DBG_WARNING, "home reg failed.");
++
++              if (err == -ENOMEDIUM)
++                      return;
++
++              ba_status = INSUFFICIENT_RESOURCES;
++      } else {
++              DEBUG(DBG_INFO, "home reg succeeded.");
++      }
++
++      DEBUG(DBG_DATADUMP, "home_addr: %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(haddr));
++      DEBUG(DBG_DATADUMP, "coa: %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(coa));
++      DEBUG(DBG_DATADUMP, "lifet:%d, seq:%d", ba_lifetime, sequence);
++out:
++      mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence,
++                    ba_lifetime, k_bu);
++}
++
++static int ha_proxy_create(int flags, int ifindex, struct in6_addr *coa,
++                         struct in6_addr *our_addr, struct in6_addr *home_addr)
++{
++      int ret;
++
++      if ((ret = mipv6_add_tnl_to_mn(coa, our_addr, home_addr)) <= 0) {
++              if (ret != -ENOMEDIUM) {
++                      DEBUG(DBG_ERROR, "unable to configure tunnel to MN!");
++              }
++              return -1;
++      }
++      if (mipv6_proxy_nd(home_addr, ifindex, 
++                         flags & MIPV6_BU_F_LLADDR) != 0) {
++              DEBUG(DBG_ERROR, "mipv6_proxy_nd failed!");
++              mipv6_del_tnl_to_mn(coa, our_addr, home_addr);
++              return -2;
++      }
++      return 0;
++}
++
++static void ha_proxy_del(struct in6_addr *home_addr, struct mipv6_bce *entry)
++{
++      if (mipv6_proxy_nd_rem(&entry->home_addr, entry->ifindex,
++                             entry->flags & MIPV6_BU_F_LLADDR) == 0) {
++              DEBUG(DBG_INFO, "proxy_nd succ");
++      } else {
++              DEBUG(DBG_INFO, "proxy_nd fail");
++      }
++      mipv6_del_tnl_to_mn(&entry->coa, &entry->our_addr, home_addr);
++}
++
++static void bc_home_add(int ifindex, 
++                      struct in6_addr *daddr, struct in6_addr *haddr, 
++                      struct in6_addr *coa, struct in6_addr *rep_coa,
++                      __u32 lifetime, __u16 sequence, __u8 flags, 
++                      __u8 *k_bu)
++{
++      struct inet6_ifaddr *ifp = NULL;
++      __u8 ba_status = SUCCESS;
++
++      DEBUG_FUNC();
++
++      ifp = is_on_link_ipv6_address(haddr, daddr);
++
++      if (ifp == NULL) {
++              ba_status = NOT_HOME_SUBNET;
++      } else if (((ipv6_addr_type(haddr) & IPV6_ADDR_SITELOCAL) ||
++                  (ipv6_addr_type(coa) & IPV6_ADDR_SITELOCAL))
++                 && !mipv6_ha_tunnel_sitelocal) {
++              /* Site-local home or care-of addresses are not 
++                 accepted by default */
++              ba_status = ADMINISTRATIVELY_PROHIBITED;
++      } else {
++              int ret;
++
++              ifindex = ifp->idev->dev->ifindex;
++
++              if ((ret = mipv6_dad_start(ifp, ifindex, daddr, 
++                                         haddr, coa, rep_coa, lifetime,
++                                         sequence, flags)) < 0) {
++                      /* An error occurred */
++                      ba_status = -ret;
++              } else if (ret) {
++                      /* DAD is needed to be performed. */
++                      in6_ifa_put(ifp);
++                      return;
++              }
++      }
++
++      mipv6_bu_finish(ifp, ifindex, ba_status, daddr, haddr, coa, 
++                      rep_coa, lifetime, sequence, flags, k_bu);
++      if (ifp)
++              in6_ifa_put(ifp);
++}
++
++static void bc_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, 
++                         struct in6_addr *coa, struct in6_addr *rep_coa, 
++                         __u16 sequence, __u8 flags, __u8 *k_bu)
++{
++      __u8 status = SUCCESS;
++      struct mipv6_bce bce;
++
++      /* Primary Care-of Address Deregistration */
++      if (mipv6_bcache_get(haddr, daddr, &bce) < 0) {
++              DEBUG(DBG_INFO, "entry is not in cache");
++              status = NOT_HA_FOR_MN;
++      } else {
++              ha_proxy_del(&bce.home_addr, &bce);
++              mipv6_bcache_delete(haddr, daddr, HOME_REGISTRATION);
++      }
++      mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, 0, k_bu);
++}
++
++extern int mipv6_ra_rcv_ptr(struct sk_buff *skb, struct icmp6hdr *msg);
++
++
++static int
++mipv6_ha_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb)
++{
++      DEBUG_FUNC();
++      if (is_mip6_tnl(t))
++              MIPV6_INC_STATS(n_encapsulations);
++      return IP6_TNL_ACCEPT;
++}
++
++static struct ip6_tnl_hook_ops mipv6_ha_tnl_xmit_stats_ops = {
++      {NULL, NULL},
++      IP6_TNL_PRE_ENCAP,
++      IP6_TNL_PRI_LAST,
++      mipv6_ha_tnl_xmit_stats_hook
++};
++
++static int
++mipv6_ha_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb)
++{
++      DEBUG_FUNC();
++      if (is_mip6_tnl(t))
++              MIPV6_INC_STATS(n_decapsulations);
++      return IP6_TNL_ACCEPT;
++}
++
++static struct ip6_tnl_hook_ops mipv6_ha_tnl_rcv_stats_ops = {
++      {NULL, NULL},
++      IP6_TNL_PRE_DECAP,
++      IP6_TNL_PRI_LAST,
++      mipv6_ha_tnl_rcv_stats_hook
++};
++
++static struct mip6_func old;
++
++int __init mipv6_ha_init(void)
++{
++      DEBUG_FUNC();
++      
++#ifdef CONFIG_SYSCTL
++      if (!(mipv6_ha_sysctl_header = 
++            register_sysctl_table(mipv6_ha_sysctl.mipv6_root_table, 0)))
++              printk(KERN_ERR "Failed to register sysctl handlers!");
++#endif
++      memcpy(&old, &mip6_fn, sizeof(struct mip6_func));
++      mip6_fn.bce_home_add = bc_home_add;
++      mip6_fn.bce_home_del = bc_home_delete;
++      mip6_fn.proxy_del = ha_proxy_del;
++      mip6_fn.proxy_create = ha_proxy_create;
++      /*  register packet interception hooks  */
++      ip6ip6_tnl_register_hook(&mipv6_ha_tnl_xmit_stats_ops);
++      ip6ip6_tnl_register_hook(&mipv6_ha_tnl_rcv_stats_ops);
++      return 0;
++}
++
++void __exit mipv6_ha_exit(void)
++{
++      DEBUG_FUNC();
++
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_ha_sysctl_header);
++#endif
++
++      /*  remove packet interception hooks  */
++      ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_rcv_stats_ops);
++      ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_xmit_stats_ops);
++
++      mip6_fn.bce_home_add = old.bce_home_add;
++      mip6_fn.bce_home_del = old.bce_home_del;
++      mip6_fn.proxy_del = old.proxy_del;
++      mip6_fn.proxy_create = old.proxy_create;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/ha.h
+@@ -0,0 +1,39 @@
++/*
++ *      MIPL Mobile IPv6 Home Agent header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _HA_H
++#define _HA_H
++
++int mipv6_ha_init(void);
++void mipv6_ha_exit(void);
++
++int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex,
++                  struct in6_addr *daddr, struct in6_addr *haddr,
++                  struct in6_addr *coa, struct in6_addr *rep_coa,
++                  __u32 ba_lifetime, __u16 sequence, __u8 flags);
++
++void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, 
++                   __u8 ba_status, struct in6_addr *daddr,
++                   struct in6_addr *haddr, struct in6_addr *coa, 
++                   struct in6_addr *rep_coa, __u32 ba_lifetime,
++                   __u16 sequence, __u8 flags, __u8 *k_bu);
++
++
++static __inline__ void mipv6_generate_ll_addr(struct in6_addr *ll_addr,
++                                            struct in6_addr *addr)
++{
++      ll_addr->s6_addr32[0] = htonl(0xfe800000);
++      ll_addr->s6_addr32[1] = 0;
++      ll_addr->s6_addr32[2] = addr->s6_addr32[2];
++      ll_addr->s6_addr32[3] = addr->s6_addr32[3];
++}
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/halist.c
+@@ -0,0 +1,507 @@
++/*
++ *      Home Agents List
++ *
++ *      Authors:
++ *      Antti Tuominen          <ajtuomin@tml.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#define PREF_BASE 0xffff /* MAX value for u16 field in RA */
++
++#include <linux/autoconf.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/proc_fs.h>
++#include <linux/init.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++
++#include "hashlist.h"
++#include "util.h"
++#include "debug.h"
++
++struct mipv6_halist {
++      struct hashlist *entries;
++      struct timer_list expire_timer;
++};
++
++static rwlock_t home_agents_lock = RW_LOCK_UNLOCKED;
++
++static struct mipv6_halist home_agents;
++
++struct mipv6_halist_entry {
++      struct hashlist_entry e;
++      int ifindex;                     /* Link identifier             */
++      struct in6_addr link_local_addr; /* HA's link-local address     */
++      struct in6_addr global_addr;     /* HA's Global address         */
++      int plen;
++      long preference;                 /* The preference for this HA  */
++      unsigned long expire;            /* expiration time (jiffies)   */
++};
++
++static inline void mipv6_ha_ac_add(struct in6_addr *ll_addr, int ifindex,
++                                 struct in6_addr *glob_addr, int plen)
++{
++      struct net_device *dev;
++
++      if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) {
++              struct in6_addr addr;
++              mipv6_ha_anycast(&addr, glob_addr, plen);
++              ipv6_dev_ac_inc(dev, &addr);
++      }
++}
++
++static inline void mipv6_ha_ac_del(struct in6_addr *ll_addr, int ifindex,
++                                 struct in6_addr *glob_addr, int plen)
++{
++      struct net_device *dev;
++
++      if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) {
++              struct in6_addr addr;
++              mipv6_ha_anycast(&addr, glob_addr, plen);
++              ipv6_dev_ac_dec(dev, &addr);
++      }
++}
++
++struct preflist_iterator_args {
++      int count;
++      int requested;
++      int ifindex;
++      struct in6_addr *list;
++};
++
++static int preflist_iterator(void *data, void *args,
++                           unsigned long *pref)
++{
++      struct preflist_iterator_args *state =
++              (struct preflist_iterator_args *)args;
++      struct mipv6_halist_entry *entry =
++              (struct mipv6_halist_entry *)data;
++      struct in6_addr *newaddr =
++              (struct in6_addr *)state->list + state->count;
++
++      if (state->count >= state->requested)
++              return ITERATOR_STOP;
++
++      if (time_after(jiffies, entry->expire)) {
++              if (!ipv6_addr_any(&entry->link_local_addr)) {
++                      mipv6_ha_ac_del(&entry->link_local_addr, 
++                                      entry->ifindex, 
++                                      &entry->global_addr, entry->plen);
++              }
++              DEBUG(DBG_INFO, "preflist_iterator: Deleting entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr));
++              return ITERATOR_DELETE_ENTRY;
++      }
++      if (state->ifindex != entry->ifindex)
++              return ITERATOR_CONT;
++
++      ipv6_addr_copy(newaddr, &entry->global_addr);
++      DEBUG(DBG_INFO, "preflist_iterator: adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr));
++      state->count++;
++
++      return ITERATOR_CONT;
++}
++
++static int gc_iterator(void *data, void *args,
++                     unsigned long *pref)
++{
++      struct mipv6_halist_entry *entry =
++              (struct mipv6_halist_entry *)data;
++
++      int *type = (int *)args;
++
++      if (*type == 1 || time_after(jiffies, entry->expire)) {
++              if (!ipv6_addr_any(&entry->link_local_addr)) {
++                      mipv6_ha_ac_del(&entry->link_local_addr, 
++                                      entry->ifindex, 
++                                      &entry->global_addr, entry->plen);
++              }
++              return ITERATOR_DELETE_ENTRY;
++      }
++
++      return ITERATOR_CONT;
++}
++
++static int mipv6_halist_gc(int type)
++{
++      DEBUG_FUNC();
++      hashlist_iterate(home_agents.entries, &type, gc_iterator);
++      return 0;
++}
++
++static void mipv6_halist_expire(unsigned long dummy)
++{
++      DEBUG_FUNC();
++
++      write_lock(&home_agents_lock);
++      mipv6_halist_gc(0);
++      write_unlock(&home_agents_lock);
++}
++
++
++static struct mipv6_halist_entry *mipv6_halist_new_entry(void)
++{
++      struct mipv6_halist_entry *entry;
++
++      DEBUG_FUNC();
++
++      entry = hashlist_alloc(home_agents.entries, SLAB_ATOMIC);
++
++      return entry;
++}
++
++
++
++/**
++ * mipv6_halist_add - Add new home agent to the Home Agents List
++ * @ifindex: interface identifier
++ * @glob_addr: home agent's global address
++ * @ll_addr: home agent's link-local address
++ * @pref: relative preference for this home agent
++ * @lifetime: lifetime for the entry
++ *
++ * Adds new home agent to the Home Agents List.  The list is interface
++ * specific and @ifindex tells through which interface the home agent
++ * was heard.  Returns zero on success and negative on failure.
++ **/
++
++int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen,
++                   struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime)
++{
++      int update = 0, ret = 0;
++      unsigned int mpref;
++      struct mipv6_halist_entry *entry = NULL;
++
++      DEBUG_FUNC();
++
++      write_lock(&home_agents_lock);
++
++      if (glob_addr == NULL || lifetime <= 0) {
++              DEBUG(DBG_WARNING, "invalid arguments");
++              ret = -EINVAL;
++              goto out;
++      }
++      mpref = PREF_BASE - pref;
++      if ((entry = (struct mipv6_halist_entry *)
++           hashlist_get(home_agents.entries, glob_addr)) != NULL) {
++              if (entry->ifindex == ifindex) {
++                      DEBUG(DBG_DATADUMP, "updating old entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr));
++                      update = 1;
++              } else {
++                      DEBUG(DBG_INFO, "halist_add : adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr));
++                      update = 0;
++              }
++      }
++      if (update) {
++              entry->expire = jiffies + lifetime * HZ;
++              if (entry->preference != mpref) {
++                      entry->preference = mpref;
++                      ret = hashlist_reposition(home_agents.entries, 
++                                                (void *)entry, mpref);
++              }
++      } else {
++              entry = mipv6_halist_new_entry();
++              if (entry == NULL) {
++                      DEBUG(DBG_INFO, "list full");
++                      ret = -ENOMEM;
++                      goto out;
++              }
++              entry->ifindex = ifindex;
++              if (ll_addr) {
++                      ipv6_addr_copy(&entry->link_local_addr, ll_addr);
++                      mipv6_ha_ac_add(ll_addr, ifindex, glob_addr, plen);
++              } else
++                      ipv6_addr_set(&entry->link_local_addr, 0, 0, 0, 0);
++
++              ipv6_addr_copy(&entry->global_addr, glob_addr);
++              entry->plen = plen;
++              entry->preference = mpref;
++              entry->expire = jiffies + lifetime * HZ;
++              ret = hashlist_add(home_agents.entries, glob_addr, mpref, 
++                                 entry);
++      }
++out:
++      write_unlock(&home_agents_lock);
++      return ret;
++}
++
++/**
++ * mipv6_halist_delete - delete home agent from Home Agents List
++ * @glob_addr: home agent's global address
++ *
++ * Deletes entry for home agent @glob_addr from the Home Agent List.
++ **/
++int mipv6_halist_delete(struct in6_addr *glob_addr)
++{
++      struct hashlist_entry *e;
++      struct mipv6_halist_entry *entry;
++      DEBUG_FUNC();
++
++      if (glob_addr == NULL) {
++              DEBUG(DBG_WARNING, "invalid glob addr");
++              return -EINVAL;
++      }
++      write_lock(&home_agents_lock);
++      if ((e = hashlist_get(home_agents.entries, glob_addr)) == NULL) {
++              write_unlock(&home_agents_lock);
++              return -ENOENT;
++      }
++      hashlist_delete(home_agents.entries, e);
++      entry = (struct mipv6_halist_entry *)e;
++      if (!ipv6_addr_any(&entry->link_local_addr)) {
++              mipv6_ha_ac_del(&entry->link_local_addr, entry->ifindex, 
++                              &entry->global_addr, entry->plen);
++      }
++      hashlist_free(home_agents.entries, e);
++      write_unlock(&home_agents_lock);
++      return 0;
++}
++
++/**
++ * mipv6_ha_get_pref_list - Get list of preferred home agents
++ * @ifindex: interface identifier
++ * @addrs: pointer to a buffer to store the list
++ * @max: maximum number of home agents to return
++ *
++ * Creates a list of @max preferred (or all known if less than @max)
++ * home agents.  Home Agents List is interface specific so you must
++ * supply @ifindex.  Stores list in addrs and returns number of home
++ * agents stored.  On failure, returns a negative value.
++ **/
++int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max)
++{
++      struct preflist_iterator_args args;
++
++      DEBUG_FUNC();
++      if (max <= 0) {
++              *addrs = NULL;
++              return 0;
++      }
++
++      args.count = 0;
++      args.requested = max;
++      args.ifindex = ifindex;
++      args.list = kmalloc(max * sizeof(struct in6_addr), GFP_ATOMIC);
++
++      if (args.list == NULL) return -ENOMEM;
++
++      read_lock(&home_agents_lock);
++      hashlist_iterate(home_agents.entries, &args, preflist_iterator);
++      read_unlock(&home_agents_lock);
++
++      if (args.count >= 0) {
++              *addrs = args.list;
++      } else {
++              kfree(args.list);
++              *addrs = NULL;
++      }
++
++      return args.count;
++}
++
++struct getaddr_iterator_args {
++      struct net_device *dev;
++      struct in6_addr *addr;
++};
++
++static int getaddr_iterator(void *data, void *args,
++           unsigned long *pref)
++{
++      struct mipv6_halist_entry *entry =
++              (struct mipv6_halist_entry *)data;
++      struct getaddr_iterator_args *state =
++              (struct getaddr_iterator_args *)args;
++
++      if (entry->ifindex != state->dev->ifindex)
++              return ITERATOR_CONT;
++
++      if (ipv6_chk_addr(&entry->global_addr, state->dev)) {
++              ipv6_addr_copy(state->addr, &entry->global_addr);
++              return ITERATOR_STOP;
++      }
++      return ITERATOR_CONT;
++}
++
++/*
++ * Get Home Agent Address for given interface.  If node is not serving
++ * as a HA for this interface returns negative error value.
++ */
++int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr)
++{
++      struct getaddr_iterator_args args;
++      struct net_device *dev;
++
++      if (ifindex <= 0)
++              return -EINVAL;
++
++      if ((dev = dev_get_by_index(ifindex)) == NULL)
++              return -ENODEV;
++
++      memset(addr, 0, sizeof(struct in6_addr));
++      args.dev = dev;
++      args.addr = addr;
++      read_lock(&home_agents_lock);
++      hashlist_iterate(home_agents.entries, &args, getaddr_iterator);
++      read_unlock(&home_agents_lock);
++      dev_put(dev);
++
++      if (ipv6_addr_any(addr))
++              return -ENOENT;
++      
++      return 0;
++}
++
++#define HALIST_INFO_LEN 81
++
++struct procinfo_iterator_args {
++      char *buffer;
++      int offset;
++      int length;
++      int skip;
++      int len;
++};
++
++static int procinfo_iterator(void *data, void *args,
++                           unsigned long *pref)
++{
++      struct procinfo_iterator_args *arg =
++              (struct procinfo_iterator_args *)args;
++      struct mipv6_halist_entry *entry =
++              (struct mipv6_halist_entry *)data;
++      unsigned long int expire;
++
++      DEBUG_FUNC();
++
++      if (entry == NULL) return ITERATOR_ERR;
++
++      if (time_after(jiffies, entry->expire)) {
++              if (!ipv6_addr_any(&entry->link_local_addr)) {
++                      mipv6_ha_ac_del(&entry->link_local_addr, 
++                                      entry->ifindex, 
++                                      &entry->global_addr, entry->plen);
++              }
++              return ITERATOR_DELETE_ENTRY;
++      }
++      if (arg->skip < arg->offset / HALIST_INFO_LEN) {
++              arg->skip++;
++              return ITERATOR_CONT;
++      }
++
++      if (arg->len >= arg->length)
++              return ITERATOR_CONT;
++
++      expire = (entry->expire - jiffies) / HZ;
++
++      arg->len += sprintf(arg->buffer + arg->len, 
++                          "%02d %08x%08x%08x%08x %08x%08x%08x%08x %05ld %05ld\n",
++                          entry->ifindex,
++                          ntohl(entry->global_addr.s6_addr32[0]),
++                          ntohl(entry->global_addr.s6_addr32[1]),
++                          ntohl(entry->global_addr.s6_addr32[2]),
++                          ntohl(entry->global_addr.s6_addr32[3]),
++                          ntohl(entry->link_local_addr.s6_addr32[0]),
++                          ntohl(entry->link_local_addr.s6_addr32[1]),
++                          ntohl(entry->link_local_addr.s6_addr32[2]),
++                          ntohl(entry->link_local_addr.s6_addr32[3]),
++                          -(entry->preference - PREF_BASE), expire);
++
++      return ITERATOR_CONT;
++}
++
++static int halist_proc_info(char *buffer, char **start, off_t offset,
++                            int length)
++{
++      struct procinfo_iterator_args args;
++
++      DEBUG_FUNC();
++
++      args.buffer = buffer;
++      args.offset = offset;
++      args.length = length;
++      args.skip = 0;
++      args.len = 0;
++
++      read_lock_bh(&home_agents_lock);
++      hashlist_iterate(home_agents.entries, &args, procinfo_iterator);
++      read_unlock_bh(&home_agents_lock);
++
++      *start = buffer;
++      if (offset)
++              *start += offset % HALIST_INFO_LEN;
++
++      args.len -= offset % HALIST_INFO_LEN;
++
++      if (args.len > length)
++              args.len = length;
++      if (args.len < 0)
++              args.len = 0;
++      
++      return args.len;
++}
++
++static int halist_compare(void *data, void *hashkey)
++{
++      struct mipv6_halist_entry *e = (struct mipv6_halist_entry *)data;
++      struct in6_addr *key = (struct in6_addr *)hashkey;
++
++      return ipv6_addr_cmp(&e->global_addr, key);
++}
++
++static __u32 halist_hash(void *hashkey)
++{
++      struct in6_addr *key = (struct in6_addr *)hashkey;
++      __u32 hash;
++
++      hash = key->s6_addr32[0] ^
++                key->s6_addr32[1] ^
++                key->s6_addr32[2] ^
++                key->s6_addr32[3];
++
++      return hash;
++}
++
++int __init mipv6_halist_init(__u32 size)
++{
++      DEBUG_FUNC();
++
++      if (size <= 0) {
++              DEBUG(DBG_ERROR, "size must be at least 1");
++              return -EINVAL;
++      }
++      init_timer(&home_agents.expire_timer);
++      home_agents.expire_timer.data = 0;
++      home_agents.expire_timer.function = mipv6_halist_expire;
++      home_agents_lock = RW_LOCK_UNLOCKED;
++
++      home_agents.entries = hashlist_create(16, size, sizeof(struct mipv6_halist_entry),
++                                             "mip6_halist", NULL, NULL,
++                                             halist_compare, halist_hash);
++
++      if (home_agents.entries == NULL) {
++              DEBUG(DBG_ERROR, "Failed to initialize hashlist");
++              return -ENOMEM;
++      }
++
++      proc_net_create("mip6_home_agents", 0, halist_proc_info);
++      DEBUG(DBG_INFO, "Home Agents List initialized");
++      return 0;
++}
++
++void __exit mipv6_halist_exit(void)
++{
++      DEBUG_FUNC();
++      proc_net_remove("mip6_home_agents");
++      write_lock_bh(&home_agents_lock);
++      DEBUG(DBG_INFO, "Stopping the halist timer");
++      del_timer(&home_agents.expire_timer);
++      mipv6_halist_gc(1);
++      write_unlock_bh(&home_agents_lock);
++      hashlist_destroy(home_agents.entries);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/halist.h
+@@ -0,0 +1,28 @@
++/*
++ *      MIPL Mobile IPv6 Home Agents List header file      
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _HALIST_H
++#define _HALIST_H
++
++int mipv6_halist_init(__u32 size);
++
++void mipv6_halist_exit(void);
++
++int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen,
++                   struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime);
++
++int mipv6_halist_delete(struct in6_addr *glob_addr);
++
++int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max);
++
++int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr);
++
++#endif /* _HALIST_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.c
+@@ -0,0 +1,351 @@
++/*
++ *    Generic hashtable with chaining.  Supports secodary sort order
++ *    with doubly linked-list.
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Antti Tuominen          <ajtuomin@tml.hut.fi>
++ *
++ *    $Id: s.hashlist.c 1.21 02/10/07 19:31:52+03:00 antti@traci.mipl.mediapoli.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 of
++ *    the License, or (at your option) any later version.
++ */
++
++#include <linux/slab.h>
++#include "hashlist.h"
++#include "debug.h"
++
++struct hashlist {
++      int count;              /* entry count  */
++      int maxcount;           /* max entries  */
++      __u32 bucketnum;        /* hash buckets */
++
++      kmem_cache_t *kmem;
++
++      struct list_head *hashtable;
++      struct list_head sortedlist;
++
++      int (*compare)(void *data, void *hashkey);
++      __u32 (*hash_function)(void *hashkey);
++};
++
++/**
++ * hashlist_create - Create new hashlist
++ * @bucketnum: number of hash buckets
++ * @maxentries: maximum number of entries (0 = no limit)
++ * @size: entry size in bytes
++ * @name: name for kmem_cache_t
++ * @ctor: kmem_cache_t constructor
++ * @dtor: kmem_cache_t destructor
++ * @compare: compare function for key
++ * @hash_function: hash function
++ *
++ * Creates a hashlist structure with @max_entries entries of @size
++ * bytes.  User must supply @hash_function and @compare function for
++ * the hashlist.  User can also supply @ctor and @dtor for kmem_cache.
++ **/
++struct hashlist *hashlist_create(int bucketnum, int max_entries, size_t size,
++                               char *name,
++                               void (*ctor)(void *, kmem_cache_t *, unsigned long),
++                               void (*dtor)(void *, kmem_cache_t *, unsigned long),
++                               int (*compare)(void *data, void *hashkey),
++                               __u32 (*hash_function)(void *hashkey))
++{
++      int i;
++      struct hashlist *hl;
++
++      if (!compare || !hash_function)
++              goto hlfailed;
++
++      hl = kmalloc(sizeof(struct hashlist), GFP_ATOMIC);
++      if (!hl) goto hlfailed;
++
++      hl->kmem = kmem_cache_create(name, size, 0, 0, ctor, dtor);
++      if (!hl->kmem) goto poolfailed;
++
++      hl->hashtable = kmalloc(
++              sizeof(struct list_head) * bucketnum, GFP_ATOMIC);
++      if (!hl->hashtable) goto hashfailed;
++
++      for (i = 0; i < bucketnum; i++)
++              INIT_LIST_HEAD(&hl->hashtable[i]);
++
++      INIT_LIST_HEAD(&hl->sortedlist);
++
++      hl->maxcount = max_entries;
++      hl->count = 0;
++      hl->bucketnum = bucketnum;
++      hl->compare = compare;
++      hl->hash_function = hash_function;
++
++      return hl;
++
++hashfailed:
++      kmem_cache_destroy(hl->kmem);
++      hl->kmem = NULL;
++
++poolfailed:
++      kfree(hl);
++
++hlfailed:
++      DEBUG(DBG_ERROR, "could not create hashlist");
++
++      return NULL;    
++}
++
++/**
++ * hashlist_destroy - Destroy hashlist
++ * @hashlist: hashlist to destroy
++ *
++ * Frees all memory allocated for a hashlist.
++ **/
++void hashlist_destroy(struct hashlist *hashlist)
++{
++      DEBUG_FUNC();
++
++      if (hashlist == NULL) return;
++
++      if (hashlist->hashtable) {
++              kfree(hashlist->hashtable);
++              hashlist->hashtable = NULL;
++      }
++
++      if (hashlist->kmem) {
++              kmem_cache_destroy(hashlist->kmem);
++              hashlist->kmem = NULL;
++      }
++
++      kfree(hashlist);
++
++      return;
++}
++
++/*
++ * Insert a chain of entries to hashlist into correct order.  The
++ * entries are assumed to have valid hashkeys.  We use time_after_eq
++ * for comparing, since it handles wrap-around correctly, and the
++ * sortkey is usually jiffies.
++ */
++static void sorted_insert(struct list_head *lh, struct hashlist_entry *he)
++{
++      struct list_head *p;
++      struct hashlist_entry *hlp = NULL;
++      unsigned long sortkey = he->sortkey;
++
++      if (list_empty(lh)) {
++              list_add(&he->sorted, lh);
++              return;
++      }
++      
++      list_for_each(p, lh) {
++              hlp = list_entry(p, typeof(*hlp), sorted);
++              if (time_after_eq(hlp->sortkey, sortkey)) {
++                      list_add(&he->sorted, hlp->sorted.prev);
++                      return;
++              }
++      }
++      list_add(&he->sorted, &hlp->sorted);
++}
++
++/**
++ * hashlist_iterate - Apply function for all elements in a hash list
++ * @hashlist: pointer to hashlist
++ * @args: data to pass to the function
++ * @func: pointer to a function
++ *
++ * Apply arbitrary function @func to all elements in a hash list.
++ * @func must be a pointer to a function with the following prototype:
++ * int func(void *entry, void *arg, struct in6_addr *hashkey, unsigned
++ * long *sortkey).  Function must return %ITERATOR_STOP,
++ * %ITERATOR_CONT or %ITERATOR_DELETE_ENTRY.  %ITERATOR_STOP stops
++ * iterator and returns last return value from the function.
++ * %ITERATOR_CONT continues with iteration.  %ITERATOR_DELETE_ENTRY
++ * deletes current entry from the hashlist.  If function changes
++ * hashlist element's sortkey, iterator automatically schedules
++ * element to be reinserted after all elements have been processed.
++ */
++int hashlist_iterate(
++      struct hashlist *hashlist, void *args,
++      hashlist_iterator_t func)
++{
++      int res = ITERATOR_CONT;
++      unsigned long skey;
++      struct list_head *p, *n, repos;
++      struct hashlist_entry *he;
++
++      DEBUG_FUNC();
++      INIT_LIST_HEAD(&repos);
++
++      list_for_each_safe(p, n, &hashlist->sortedlist) {
++              he = list_entry(p, typeof(*he), sorted);
++              if (res == ITERATOR_STOP)
++                      break;
++              skey = he->sortkey;
++              res = func(he, args, &he->sortkey);
++              if (res == ITERATOR_DELETE_ENTRY) {
++                      hashlist_delete(hashlist, he);
++                      hashlist_free(hashlist, he);
++              } else if (skey != he->sortkey) {
++                      /* iterator changed the sortkey, schedule for
++                       * repositioning */
++                      list_move(&he->sorted, &repos);
++              }
++      }
++      list_for_each_safe(p, n, &repos) {      
++              he = list_entry(p, typeof(*he), sorted);
++              sorted_insert(&hashlist->sortedlist, he);
++      }
++      return res;
++}
++
++/**
++ * hashlist_alloc - Allocate memory for a hashlist entry
++ * @hashlist: hashlist for allocated entry
++ * @size: size of entry in bytes
++ *
++ * Allocates @size bytes memory from @hashlist->kmem.
++ **/
++void *hashlist_alloc(struct hashlist *hashlist, int type)
++{
++      if (hashlist == NULL) return NULL;
++      return kmem_cache_alloc(hashlist->kmem, type);
++}
++
++/**
++ * hashlist_free - Free hashlist entry
++ * @hashlist: hashlist where @he is
++ * @he: entry to free
++ *
++ * Frees an allocated hashlist entry.
++ **/
++void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he)
++{
++      kmem_cache_free(hashlist->kmem, he);
++}
++
++/**
++ * hashlist_add - Add element to hashlist
++ * @hashlist: pointer to hashlist
++ * @hashkey: hashkey for the element
++ * @sortkey: key for sorting
++ * @data: element data
++ *
++ * Add element to hashlist.  Hashlist is also sorted in a linked list
++ * by @sortkey.
++ */
++int hashlist_add(struct hashlist *hashlist, void *hashkey,
++               unsigned long sortkey, void *entry)
++{
++      struct hashlist_entry *he = (struct hashlist_entry *)entry;
++      unsigned int hash;
++
++      if (hashlist->count >= hashlist->maxcount)
++              return -1;
++
++      hashlist->count++;
++
++      /*  link the entry to sorted order  */ 
++      he->sortkey = sortkey;
++      sorted_insert(&hashlist->sortedlist, he);
++
++      /*  hash the entry  */
++      hash = hashlist->hash_function(hashkey) % hashlist->bucketnum;
++      list_add(&he->hashlist, &hashlist->hashtable[hash]);
++
++      return 0;
++}
++
++/**
++ * hashlist_get_ex - Get element from hashlist
++ * @hashlist: hashlist
++ * @hashkey: hashkey of the desired entry
++ *
++ * Lookup entry with @hashkey from the hash table using @compare
++ * function for entry comparison.  Returns entry on success, otherwise
++ * %NULL.
++ **/
++struct hashlist_entry *hashlist_get_ex(
++      struct hashlist *hashlist, void *hashkey,
++      int (*compare)(void *data, void *hashkey))
++{
++      struct list_head *p, *bkt;
++      __u32 hash;
++
++      hash = hashlist->hash_function(hashkey) % hashlist->bucketnum;
++      bkt = &hashlist->hashtable[hash];
++
++      /*  scan the entries within the same hashbucket  */
++      list_for_each(p, bkt) {
++              struct hashlist_entry *he = list_entry(p, typeof(*he), 
++                                                     hashlist);
++              if (compare(he, hashkey) == 0)
++                      return he;
++      }
++
++      return NULL;
++}
++
++/**
++ * hashlist_get - Get element from hashlist
++ * @hashlist: hashlist
++ * @hashkey: hashkey of the desired entry
++ *
++ * Lookup entry with @hashkey from the hash table.  Returns entry on
++ * success, otherwise %NULL.
++ **/
++struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey)
++{
++      return hashlist_get_ex(hashlist, hashkey, hashlist->compare);
++}
++
++/**
++ * hashlist_reposition - set entry to new position in the list
++ * @hashlist: hashlist
++ * @he: entry to reposition
++ * @sortkey: new sortkey of the entry
++ *
++ * If secondary order sortkey changes, entry must be repositioned in
++ * the sorted list.
++ **/
++int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he,
++                      unsigned long sortkey)
++{
++      list_del(&he->sorted);
++      he->sortkey = sortkey;
++      sorted_insert(&hashlist->sortedlist, he);
++
++      return 0;
++}
++
++/**
++ * hashlist_delete - Delete entry from hashlist
++ * @hashlist: hashlist where entry is
++ * @he: entry to delete
++ *
++ * Deletes an entry from the hashlist and sorted list.
++ **/
++void hashlist_delete(struct hashlist *hashlist,
++                   struct hashlist_entry *he)
++{
++      list_del_init(&he->hashlist);
++      list_del_init(&he->sorted);
++
++      hashlist->count--;
++}
++
++/**
++ * hashlist_get_first - Get first item from sorted list
++ * @hashlist: pointer to hashlist
++ *
++ * Returns first item in the secondary sort order.
++ **/
++void * hashlist_get_first(struct hashlist *hashlist)
++{
++      if (list_empty(&hashlist->sortedlist))
++              return NULL;
++      
++      return list_entry(hashlist->sortedlist.next, struct hashlist_entry, sorted);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.h
+@@ -0,0 +1,63 @@
++/*
++ *    MIPL Mobile IPv6 Hashlist header file
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _HASHLIST_H
++#define _HASHLIST_H
++
++#define ITERATOR_ERR -1
++#define ITERATOR_CONT 0
++#define ITERATOR_STOP 1
++#define ITERATOR_DELETE_ENTRY 2
++
++struct kmem_cache_t;
++
++struct hashlist_entry {
++      unsigned long sortkey;
++      struct list_head sorted;
++      struct list_head hashlist;
++};
++
++struct hashlist * hashlist_create(
++      int bucketnum, int max_entries, size_t size, char *name,
++      void (*ctor)(void *, kmem_cache_t *, unsigned long),
++      void (*dtor)(void *, kmem_cache_t *, unsigned long),
++      int (*compare)(void *data, void *hashkey),
++      __u32 (*hash_function)(void *hashkey));
++
++void hashlist_destroy(struct hashlist *hashlist);
++
++void *hashlist_alloc(struct hashlist *hashlist, int type);
++
++void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he);
++
++struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey);
++
++struct hashlist_entry *hashlist_get_ex(
++      struct hashlist *hashlist, void *hashkey,
++      int (*compare)(void *data, void *hashkey));
++
++int hashlist_add(struct hashlist *hashlist, void *hashkey,
++               unsigned long sortkey, void *data);
++
++void hashlist_delete(struct hashlist *hashlist, struct hashlist_entry *he);
++
++/* iterator function */
++typedef int (*hashlist_iterator_t)(void *, void *, unsigned long *);
++
++int hashlist_iterate(struct hashlist *hashlist, void *args,
++                   hashlist_iterator_t func);
++
++void * hashlist_get_first(struct hashlist *hashlist);
++
++int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he,
++                      unsigned long sortkey);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.c
+@@ -0,0 +1,658 @@
++/*     Authentication algorithms       
++ *    
++ *      Authors: 
++ *       Alexis Olivereau              <Alexis.Olivereau@crm.mot.com>
++ * 
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ *      Changes: 
++ *      Henrik Petander     :     Cleaned up unused parts
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/in6.h>
++
++#include "hmac.h"
++#define LROLL(x, s) (((x) << (s)) | ((x) >> (32 - (s))))
++
++/* MD5 */
++#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
++#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
++#define H(x, y, z) ((x) ^ (y) ^ (z))
++#define I(x, y, z) ((y) ^ ((x) | ~(z)))
++
++#define FF(a, b, c, d, m, s, t) { \
++ (a) += F ((b), (c), (d)) + (m) + (t); \
++ (a) = LROLL((a), (s)); \
++ (a) += (b); \
++ }
++#define GG(a, b, c, d, m, s, t) { \
++ (a) += G ((b), (c), (d)) + (m) + (t); \
++ (a) = LROLL((a), (s)); \
++ (a) += (b); \
++ }
++#define HH(a, b, c, d, m, s, t) { \
++ (a) += H ((b), (c), (d)) + (m) + (t); \
++ (a) = LROLL((a), (s)); \
++ (a) += (b); \
++ }
++#define II(a, b, c, d, m, s, t) { \
++ (a) += I ((b), (c), (d)) + (m) + (t); \
++ (a) = LROLL((a), (s)); \
++ (a) += (b); \
++ }
++
++#define s11  7
++#define s12 12
++#define s13 17
++#define s14 22
++#define s21  5
++#define s22  9
++#define s23 14
++#define s24 20
++#define s31  4
++#define s32 11
++#define s33 16
++#define s34 23
++#define s41  6
++#define s42 10
++#define s43 15
++#define s44 21
++
++/* SHA-1 */
++#define f(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
++#define g(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
++#define h(x, y, z) ((x) ^ (y) ^ (z))
++
++#define K1 0x5a827999
++#define K2 0x6ed9eba1
++#define K3 0x8f1bbcdc
++#define K4 0xca62c1d6
++
++int ah_hmac_md5_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len)
++{
++      int i;
++      int key_up4;
++      uint32_t ipad = 0x36363636;
++      uint8_t extkey[64];
++
++      ahp->key_auth = key;
++      ahp->key_auth_len = key_len;
++      ahp->context = (void *) kmalloc(sizeof(MD5_CTX), GFP_ATOMIC);
++      if (ahp->context == NULL)
++              return -1;
++      md5_init((MD5_CTX *) ahp->context);
++      if ((64 * sizeof(uint8_t)) < ahp->key_auth_len) {
++              printk("buffer overflow!");
++              return -1;
++      }
++      memcpy(extkey, ahp->key_auth, ahp->key_auth_len);
++      if (ahp->key_auth_len % 4) {
++              memset(extkey + ahp->key_auth_len, 0,
++                     4 - (ahp->key_auth_len % 4));
++      }
++      key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4;
++
++      for (i = 0; i < key_up4; i++)
++              ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad;
++      for (i = key_up4; i < 16; i++)
++              ((uint32_t *) extkey)[i] = ipad;
++
++      md5_compute((MD5_CTX *) ahp->context, extkey, 64);
++      return 0;
++}
++
++void ah_hmac_md5_loop(struct ah_processing *ahp, void *str, uint32_t len)
++{
++      md5_compute((MD5_CTX *) ahp->context, str, len);
++}
++
++void ah_hmac_md5_result(struct ah_processing *ahp, char *digest)
++{
++      uint8_t inner[HMAC_MD5_HASH_LEN];
++      int i;
++      int key_up4;
++      uint32_t opad = 0x5c5c5c5c;
++      uint8_t extkey[64];
++
++      md5_final((MD5_CTX *) ahp->context, inner);
++      md5_init((MD5_CTX *) ahp->context);
++
++      memcpy(extkey, ahp->key_auth, ahp->key_auth_len);
++      if (ahp->key_auth_len % 4) {
++              memset(extkey + ahp->key_auth_len, 0,
++                     4 - (ahp->key_auth_len % 4));
++      }
++      key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4;
++
++      for (i = 0; i < key_up4; i++)
++              ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad;
++      for (i = key_up4; i < 16; i++)
++              ((uint32_t *) extkey)[i] = opad;
++
++      md5_compute((MD5_CTX *) ahp->context, extkey, 64);
++      md5_compute((MD5_CTX *) ahp->context, inner, HMAC_MD5_HASH_LEN);
++
++      md5_final((MD5_CTX *) ahp->context, digest);
++
++      kfree(ahp->context);
++}
++
++int ah_hmac_sha1_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len)
++{
++      int i;
++      int key_up4;
++      uint32_t ipad = 0x36363636;
++      uint8_t extkey[64];
++
++      ahp->key_auth = key;
++      ahp->key_auth_len = key_len;
++
++      ahp->context = (void *) kmalloc(sizeof(SHA1_CTX), GFP_ATOMIC);
++      //if (ahp->context == NULL)
++      //      return -1;
++
++      sha1_init((SHA1_CTX *) ahp->context);
++
++      memcpy(extkey, ahp->key_auth, ahp->key_auth_len);
++      if (ahp->key_auth_len % 4) {
++              memset(extkey + ahp->key_auth_len, 0,
++                     4 - (ahp->key_auth_len % 4));
++      }
++      key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4;
++
++      for (i = 0; i < key_up4; i++)
++              ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad;
++      for (i = key_up4; i < 16; i++)
++              ((uint32_t *) extkey)[i] = ipad;
++
++      sha1_compute((SHA1_CTX *) ahp->context, extkey, 64);
++      return 0;
++}
++
++void ah_hmac_sha1_loop(struct ah_processing *ahp, void *str, uint32_t len)
++{
++      if (!ahp)
++              return;
++      sha1_compute((SHA1_CTX *) ahp->context, str, len);
++}
++
++void ah_hmac_sha1_result(struct ah_processing *ahp, char *digest)
++{
++      uint8_t inner[HMAC_SHA1_HASH_LEN];
++      int i;
++      int key_up4;
++      uint32_t opad = 0x5c5c5c5c;
++      uint8_t extkey[64];
++
++      if (!ahp)
++              return;
++      sha1_final((SHA1_CTX *) ahp->context, inner);
++      sha1_init((SHA1_CTX *) ahp->context);
++
++      memcpy(extkey, ahp->key_auth, ahp->key_auth_len);
++      if (ahp->key_auth_len % 4) {
++              memset(extkey + ahp->key_auth_len, 0,
++                     4 - (ahp->key_auth_len % 4));
++      }
++      key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4;
++
++      for (i = 0; i < key_up4; i++)
++              ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad;
++      for (i = key_up4; i < 16; i++)
++              ((uint32_t *) extkey)[i] = opad;
++
++      sha1_compute((SHA1_CTX *) ahp->context, extkey, 64);
++      sha1_compute((SHA1_CTX *) ahp->context, inner,
++                   HMAC_SHA1_HASH_LEN);
++
++      sha1_final((SHA1_CTX *) ahp->context, digest);
++
++      kfree(ahp->context);
++}
++
++void md5_init(MD5_CTX * ctx)
++{
++      ctx->A = 0x67452301;
++      ctx->B = 0xefcdab89;
++      ctx->C = 0x98badcfe;
++      ctx->D = 0x10325476;
++      ctx->buf_cur = ctx->buf;
++      ctx->bitlen[0] = ctx->bitlen[1] = 0;
++      memset(ctx->buf, 0, 64);
++}
++
++void md5_over_block(MD5_CTX * ctx, uint8_t * data)
++{
++      uint32_t M[16];
++      uint32_t a = ctx->A;
++      uint32_t b = ctx->B;
++      uint32_t c = ctx->C;
++      uint32_t d = ctx->D;
++
++      create_M_blocks(M, data);
++
++      /* Round 1 */
++      FF(a, b, c, d, M[0], s11, 0xd76aa478);  /*  1 */
++      FF(d, a, b, c, M[1], s12, 0xe8c7b756);  /*  2 */
++      FF(c, d, a, b, M[2], s13, 0x242070db);  /*  3 */
++      FF(b, c, d, a, M[3], s14, 0xc1bdceee);  /*  4 */
++      FF(a, b, c, d, M[4], s11, 0xf57c0faf);  /*  5 */
++      FF(d, a, b, c, M[5], s12, 0x4787c62a);  /*  6 */
++      FF(c, d, a, b, M[6], s13, 0xa8304613);  /*  7 */
++      FF(b, c, d, a, M[7], s14, 0xfd469501);  /*  8 */
++      FF(a, b, c, d, M[8], s11, 0x698098d8);  /*  9 */
++      FF(d, a, b, c, M[9], s12, 0x8b44f7af);  /* 10 */
++      FF(c, d, a, b, M[10], s13, 0xffff5bb1); /* 11 */
++      FF(b, c, d, a, M[11], s14, 0x895cd7be); /* 12 */
++      FF(a, b, c, d, M[12], s11, 0x6b901122); /* 13 */
++      FF(d, a, b, c, M[13], s12, 0xfd987193); /* 14 */
++      FF(c, d, a, b, M[14], s13, 0xa679438e); /* 15 */
++      FF(b, c, d, a, M[15], s14, 0x49b40821); /* 16 */
++
++      /* Round 2 */
++      GG(a, b, c, d, M[1], s21, 0xf61e2562);  /* 17 */
++      GG(d, a, b, c, M[6], s22, 0xc040b340);  /* 18 */
++      GG(c, d, a, b, M[11], s23, 0x265e5a51); /* 19 */
++      GG(b, c, d, a, M[0], s24, 0xe9b6c7aa);  /* 20 */
++      GG(a, b, c, d, M[5], s21, 0xd62f105d);  /* 21 */
++      GG(d, a, b, c, M[10], s22, 0x02441453); /* 22 */
++      GG(c, d, a, b, M[15], s23, 0xd8a1e681); /* 23 */
++      GG(b, c, d, a, M[4], s24, 0xe7d3fbc8);  /* 24 */
++      GG(a, b, c, d, M[9], s21, 0x21e1cde6);  /* 25 */
++      GG(d, a, b, c, M[14], s22, 0xc33707d6); /* 26 */
++      GG(c, d, a, b, M[3], s23, 0xf4d50d87);  /* 27 */
++      GG(b, c, d, a, M[8], s24, 0x455a14ed);  /* 28 */
++      GG(a, b, c, d, M[13], s21, 0xa9e3e905); /* 29 */
++      GG(d, a, b, c, M[2], s22, 0xfcefa3f8);  /* 30 */
++      GG(c, d, a, b, M[7], s23, 0x676f02d9);  /* 31 */
++      GG(b, c, d, a, M[12], s24, 0x8d2a4c8a); /* 32 */
++
++      /* Round 3 */
++      HH(a, b, c, d, M[5], s31, 0xfffa3942);  /* 33 */
++      HH(d, a, b, c, M[8], s32, 0x8771f681);  /* 34 */
++      HH(c, d, a, b, M[11], s33, 0x6d9d6122); /* 35 */
++      HH(b, c, d, a, M[14], s34, 0xfde5380c); /* 36 */
++      HH(a, b, c, d, M[1], s31, 0xa4beea44);  /* 37 */
++      HH(d, a, b, c, M[4], s32, 0x4bdecfa9);  /* 38 */
++      HH(c, d, a, b, M[7], s33, 0xf6bb4b60);  /* 39 */
++      HH(b, c, d, a, M[10], s34, 0xbebfbc70); /* 40 */
++      HH(a, b, c, d, M[13], s31, 0x289b7ec6); /* 41 */
++      HH(d, a, b, c, M[0], s32, 0xeaa127fa);  /* 42 */
++      HH(c, d, a, b, M[3], s33, 0xd4ef3085);  /* 43 */
++      HH(b, c, d, a, M[6], s34, 0x4881d05);   /* 44 */
++      HH(a, b, c, d, M[9], s31, 0xd9d4d039);  /* 45 */
++      HH(d, a, b, c, M[12], s32, 0xe6db99e5); /* 46 */
++      HH(c, d, a, b, M[15], s33, 0x1fa27cf8); /* 47 */
++      HH(b, c, d, a, M[2], s34, 0xc4ac5665);  /* 48 */
++
++      /* Round 4 */
++      II(a, b, c, d, M[0], s41, 0xf4292244);  /* 49 */
++      II(d, a, b, c, M[7], s42, 0x432aff97);  /* 50 */
++      II(c, d, a, b, M[14], s43, 0xab9423a7); /* 51 */
++      II(b, c, d, a, M[5], s44, 0xfc93a039);  /* 52 */
++      II(a, b, c, d, M[12], s41, 0x655b59c3); /* 53 */
++      II(d, a, b, c, M[3], s42, 0x8f0ccc92);  /* 54 */
++      II(c, d, a, b, M[10], s43, 0xffeff47d); /* 55 */
++      II(b, c, d, a, M[1], s44, 0x85845dd1);  /* 56 */
++      II(a, b, c, d, M[8], s41, 0x6fa87e4f);  /* 57 */
++      II(d, a, b, c, M[15], s42, 0xfe2ce6e0); /* 58 */
++      II(c, d, a, b, M[6], s43, 0xa3014314);  /* 59 */
++      II(b, c, d, a, M[13], s44, 0x4e0811a1); /* 60 */
++      II(a, b, c, d, M[4], s41, 0xf7537e82);  /* 61 */
++      II(d, a, b, c, M[11], s42, 0xbd3af235); /* 62 */
++      II(c, d, a, b, M[2], s43, 0x2ad7d2bb);  /* 63 */
++      II(b, c, d, a, M[9], s44, 0xeb86d391);  /* 64 */
++
++      ctx->A += a;
++      ctx->B += b;
++      ctx->C += c;
++      ctx->D += d;
++}
++
++void create_M_blocks(uint32_t * M, uint8_t * data)
++{
++#ifdef HAVE_LITTLE_ENDIAN
++      memcpy((uint8_t *) M, data, 64);
++#endif                                /* HAVE_LITTLE_ENDIAN */
++
++#ifdef HAVE_BIG_ENDIAN
++      int i;
++      for (i = 0; i < 16; i++, data += 4) {
++              ((uint8_t *) (&M[i]))[0] = data[3];
++              ((uint8_t *) (&M[i]))[1] = data[2];
++              ((uint8_t *) (&M[i]))[2] = data[1];
++              ((uint8_t *) (&M[i]))[3] = data[0];
++      }
++#endif                                /* HAVE_BIG_ENDIAN */
++}
++
++void md5_compute(MD5_CTX * ctx, uint8_t * data, uint32_t len)
++{
++      uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f);
++
++      /* First we update the bit length */
++      if ((ctx->bitlen[0] += (len << 3)) < (len << 3))
++              ctx->bitlen[1]++;
++      ctx->bitlen[1] += (len >> 29);  /* len is expressed in bytes */
++
++      if (pos) {
++              /* Buffer is not empty */
++              if (64 - pos >= len) {
++                      memcpy(ctx->buf_cur, data, len);
++                      ctx->buf_cur += len;
++                      pos += len;
++                      if (pos == 64) {
++                              /* The current block is over */
++                              md5_over_block(ctx, ctx->buf);
++                              ctx->buf_cur = ctx->buf;
++                      }
++                      return;
++              } else {
++                      memcpy(ctx->buf_cur, data, 64 - pos);
++                      md5_over_block(ctx, ctx->buf);
++                      len -= (64 - pos);
++                      data += (64 - pos);
++                      ctx->buf_cur = ctx->buf;
++              }
++      }
++      while (len >= 64) {
++              md5_over_block(ctx, data);
++              len -= 64;
++              data += 64;
++      }
++      if (len) {
++              memcpy(ctx->buf_cur, data, len);
++              ctx->buf_cur += len;
++      }
++}
++
++void md5_final(MD5_CTX * ctx, uint8_t * digest)
++{
++      uint32_t rem_size;
++      uint8_t *buf_cur = ctx->buf_cur;
++      int i;
++
++      rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f);
++      *(buf_cur++) = 0x80;
++
++      if (rem_size > 8 + 1) {
++              /* We have enough room in the current block */
++              for (i = 0; i < rem_size - 8 - 1; i++) {
++                      *(buf_cur++) = 0;
++              }
++      } else {
++              /* We do not have enough room and need therefore to add a new
++                 64-byte block */
++              for (i = 0; i < rem_size - 1; i++) {
++                      *(buf_cur++) = 0;
++              }
++              md5_over_block(ctx, ctx->buf);
++
++              buf_cur = ctx->buf;
++              for (i = 0; i < 64 - 8; i++) {
++                      *(buf_cur++) = 0;
++              }
++      }
++#ifdef HAVE_LITTLE_ENDIAN
++      memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8);
++#endif                                /* HAVE_LITTLE_ENDIAN */
++
++#ifdef HAVE_BIG_ENDIAN
++      *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff;
++#endif                                /* HAVE_BIG_ENDIAN */
++
++      md5_over_block(ctx, ctx->buf);
++
++#ifdef HAVE_LITTLE_ENDIAN
++      memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t));
++      memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t));
++      memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t));
++      memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t));
++#endif                                /* HAVE_LITTLE_ENDIAN */
++
++#ifdef HAVE_BIG_ENDIAN
++      digest[0] = ((ctx->A) >> 24) & 0xff;
++      digest[1] = ((ctx->A) >> 16) & 0xff;
++      digest[2] = ((ctx->A) >> 8) & 0xff;
++      digest[3] = ((ctx->A) >> 0) & 0xff;
++      digest[4] = ((ctx->B) >> 24) & 0xff;
++      digest[5] = ((ctx->B) >> 16) & 0xff;
++      digest[6] = ((ctx->B) >> 8) & 0xff;
++      digest[7] = ((ctx->B) >> 0) & 0xff;
++      digest[8] = ((ctx->C) >> 24) & 0xff;
++      digest[9] = ((ctx->C) >> 16) & 0xff;
++      digest[10] = ((ctx->C) >> 8) & 0xff;
++      digest[11] = ((ctx->C) >> 0) & 0xff;
++      digest[12] = ((ctx->D) >> 24) & 0xff;
++      digest[13] = ((ctx->D) >> 16) & 0xff;
++      digest[14] = ((ctx->D) >> 8) & 0xff;
++      digest[15] = ((ctx->D) >> 0) & 0xff;
++#endif                                /* HAVE_BIG_ENDIAN */
++}
++
++void sha1_init(SHA1_CTX * ctx)
++{
++      ctx->A = 0x67452301;
++      ctx->B = 0xefcdab89;
++      ctx->C = 0x98badcfe;
++      ctx->D = 0x10325476;
++      ctx->E = 0xc3d2e1f0;
++      ctx->buf_cur = ctx->buf;
++      ctx->bitlen[0] = ctx->bitlen[1] = 0;
++      memset(ctx->buf, 0, 64);
++}
++
++void sha1_over_block(SHA1_CTX * ctx, uint8_t * data)
++{
++      int i;
++      uint32_t W[80];
++      uint32_t a = ctx->A;
++      uint32_t b = ctx->B;
++      uint32_t c = ctx->C;
++      uint32_t d = ctx->D;
++      uint32_t e = ctx->E;
++      uint32_t temp;
++
++      create_W_blocks(W, data);
++
++      /* Round 1 */
++      for (i = 0; i < 20; i++) {
++              temp = LROLL(a, 5) + f(b, c, d) + e + W[i] + K1;
++              e = d;
++              d = c;
++              c = LROLL(b, 30);
++              b = a;
++              a = temp;
++      }
++
++      /* Round 2 */
++      for (i = 20; i < 40; i++) {
++              temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K2;
++              e = d;
++              d = c;
++              c = LROLL(b, 30);
++              b = a;
++              a = temp;
++      }
++
++      /* Round 3 */
++      for (i = 40; i < 60; i++) {
++              temp = LROLL(a, 5) + g(b, c, d) + e + W[i] + K3;
++              e = d;
++              d = c;
++              c = LROLL(b, 30);
++              b = a;
++              a = temp;
++      }
++
++      /* Round 4 */
++      for (i = 60; i < 80; i++) {
++              temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K4;
++              e = d;
++              d = c;
++              c = LROLL(b, 30);
++              b = a;
++              a = temp;
++      }
++
++      ctx->A += a;
++      ctx->B += b;
++      ctx->C += c;
++      ctx->D += d;
++      ctx->E += e;
++}
++
++void create_W_blocks(uint32_t * W, uint8_t * data)
++{
++      int i;
++
++#ifdef HAVE_BIG_ENDIAN
++      memcpy((uint8_t *) W, data, 64);
++#endif                                /* HAVE_BIG_ENDIAN */
++
++#ifdef HAVE_LITTLE_ENDIAN
++      for (i = 0; i < 16; i++, data += 4) {
++              ((uint8_t *) (&W[i]))[0] = data[3];
++              ((uint8_t *) (&W[i]))[1] = data[2];
++              ((uint8_t *) (&W[i]))[2] = data[1];
++              ((uint8_t *) (&W[i]))[3] = data[0];
++      }
++#endif                                /* HAVE_LITTLE_ENDIAN */
++      for (i = 16; i < 80; i++) {
++              W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
++              W[i] = LROLL(W[i], 1);
++      }
++}
++
++void sha1_compute(SHA1_CTX * ctx, uint8_t * data, uint32_t len)
++{
++      uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f);
++
++      /* First we update the bit length */
++      if ((ctx->bitlen[0] += (len << 3)) < (len << 3))
++              ctx->bitlen[1]++;
++      ctx->bitlen[1] += (len >> 29);  /* len is expressed in bytes */
++
++      if (pos) {
++              /* Buffer is not empty */
++              if (64 - pos >= len) {
++                      memcpy(ctx->buf_cur, data, len);
++                      ctx->buf_cur += len;
++                      pos += len;
++                      if (pos == 64) {
++                              /* The current block is over */
++                              sha1_over_block(ctx, ctx->buf);
++                              ctx->buf_cur = ctx->buf;
++                      }
++                      return;
++              } else {
++                      memcpy(ctx->buf_cur, data, 64 - pos);
++                      sha1_over_block(ctx, ctx->buf);
++                      len -= (64 - pos);
++                      data += (64 - pos);
++                      ctx->buf_cur = ctx->buf;
++              }
++      }
++      while (len >= 64) {
++              sha1_over_block(ctx, data);
++              len -= 64;
++              data += 64;
++      }
++      if (len) {
++              memcpy(ctx->buf_cur, data, len);
++              ctx->buf_cur += len;
++      }
++}
++
++void sha1_final(SHA1_CTX * ctx, uint8_t * digest)
++{
++      uint32_t rem_size;
++      uint8_t *buf_cur = ctx->buf_cur;
++      int i;
++
++      rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f);
++      *(buf_cur++) = 0x80;
++
++      if (rem_size > 8 + 1) {
++              /* We have enough room in the current block */
++              for (i = 0; i < rem_size - 8 - 1; i++) {
++                      *(buf_cur++) = 0;
++              }
++      } else {
++              /* We do not have enough room and need therefore to add a new
++                 64-byte block */
++              for (i = 0; i < rem_size - 1; i++) {
++                      *(buf_cur++) = 0;
++              }
++              sha1_over_block(ctx, ctx->buf);
++
++              buf_cur = ctx->buf;
++              for (i = 0; i < 64 - 8; i++) {
++                      *(buf_cur++) = 0;
++              }
++      }
++#ifdef HAVE_BIG_ENDIAN
++      memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8);
++#endif                                /* HAVE_BIG_ENDIAN */
++
++#ifdef HAVE_LITTLE_ENDIAN
++      *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff;
++      *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff;
++#endif                                /* HAVE_LITTLE_ENDIAN */
++
++      sha1_over_block(ctx, ctx->buf);
++
++#ifdef HAVE_BIG_ENDIAN
++      memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t));
++      memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t));
++      memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t));
++      memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t));
++      memcpy(digest + 16, (uint8_t *) (&(ctx->E)), sizeof(uint32_t));
++#endif                                /* HAVE_BIG_ENDIAN */
++
++#ifdef HAVE_LITTLE_ENDIAN
++      digest[0] = ((ctx->A) >> 24) & 0xff;
++      digest[1] = ((ctx->A) >> 16) & 0xff;
++      digest[2] = ((ctx->A) >> 8) & 0xff;
++      digest[3] = ((ctx->A) >> 0) & 0xff;
++      digest[4] = ((ctx->B) >> 24) & 0xff;
++      digest[5] = ((ctx->B) >> 16) & 0xff;
++      digest[6] = ((ctx->B) >> 8) & 0xff;
++      digest[7] = ((ctx->B) >> 0) & 0xff;
++      digest[8] = ((ctx->C) >> 24) & 0xff;
++      digest[9] = ((ctx->C) >> 16) & 0xff;
++      digest[10] = ((ctx->C) >> 8) & 0xff;
++      digest[11] = ((ctx->C) >> 0) & 0xff;
++      digest[12] = ((ctx->D) >> 24) & 0xff;
++      digest[13] = ((ctx->D) >> 16) & 0xff;
++      digest[14] = ((ctx->D) >> 8) & 0xff;
++      digest[15] = ((ctx->D) >> 0) & 0xff;
++      digest[16] = ((ctx->E) >> 24) & 0xff;
++      digest[17] = ((ctx->E) >> 16) & 0xff;
++      digest[18] = ((ctx->E) >> 8) & 0xff;
++      digest[19] = ((ctx->E) >> 0) & 0xff;
++#endif                                /* HAVE_LITTLE_ENDIAN */
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.h
+@@ -0,0 +1,94 @@
++/*
++ *      MIPL Mobile IPv6 Message authentication algorithms        
++ * 
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _HMAC_H
++#define _HMAC_H
++
++#include <linux/types.h>
++#include <linux/in6.h>
++
++#define HAVE_LITTLE_ENDIAN
++
++#define NO_EXPIRY 1  /* For sec_as */
++
++#define ALG_AUTH_NONE           0
++#define ALG_AUTH_HMAC_MD5       1
++#define ALG_AUTH_HMAC_SHA1      2
++
++struct sec_as;
++struct ah_processing {
++      void *context;
++      struct sec_as *sas;
++      u_int8_t *key_auth;
++      u_int32_t key_auth_len;
++};
++
++struct antireplay {
++      u_int32_t count;
++      u_int32_t bitmap; 
++};
++
++typedef struct {
++  u_int32_t A, B, C, D;
++  u_int32_t bitlen[2];
++  u_int8_t* buf_cur;
++  u_int8_t buf[64];
++} MD5_CTX;
++
++typedef struct {
++  u_int32_t A, B, C, D, E;
++  u_int32_t bitlen[2];
++  u_int8_t* buf_cur;
++  u_int8_t buf[64];
++} SHA1_CTX;
++
++
++
++int ah_hmac_md5_init (struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len);
++void ah_hmac_md5_loop(struct ah_processing*, void*, u_int32_t);
++void ah_hmac_md5_result(struct ah_processing*, char*);
++int ah_hmac_sha1_init(struct ah_processing*, u_int8_t *key, u_int32_t key_len);
++void ah_hmac_sha1_loop(struct ah_processing*, void*, u_int32_t);
++void ah_hmac_sha1_result(struct ah_processing*, char*);
++
++
++#define AH_HDR_LEN 12   /* # of bytes for Next Header, Payload Length,
++                           RESERVED, Security Parameters Index and
++
++                           Sequence Number Field */
++
++void md5_init(MD5_CTX *ctx);
++void md5_over_block(MD5_CTX *ctx, u_int8_t* data);
++void create_M_blocks(u_int32_t* M, u_int8_t* data);
++void md5_compute(MD5_CTX *ctx, u_int8_t* data, u_int32_t len);
++void md5_final(MD5_CTX *ctx, u_int8_t* digest);
++
++void sha1_init(SHA1_CTX *ctx);
++void sha1_over_block(SHA1_CTX *ctx, u_int8_t* data);
++void create_W_blocks(u_int32_t* W, u_int8_t* data);
++void sha1_compute(SHA1_CTX *ctx, u_int8_t* data, u_int32_t len);
++void sha1_final(SHA1_CTX *ctx, u_int8_t* digest);
++
++struct mipv6_acq {
++      struct in6_addr coa;
++      struct in6_addr haddr;
++      struct in6_addr peer;
++      u_int32_t spi;
++};
++#define MIPV6_MAX_AUTH_DATA 20
++
++#define HMAC_MD5_HASH_LEN   16
++#define HMAC_SHA1_HASH_LEN  20
++#define HMAC_SHA1_KEY_SIZE  20
++#define HMAC_MD5_ICV_LEN   12 /* RFC 2403 */
++#define HMAC_SHA1_ICV_LEN  12 /* RFC 2404 */
++
++#endif /* _HMAC_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/ioctl_mn.c
+@@ -0,0 +1,142 @@
++/*
++ *    Mobile Node IOCTL Control device
++ *
++ *    Authors:
++ *    Henrik Petander         <lpetande@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/ioctl.h> 
++#include <net/ipv6.h>
++#include <asm/uaccess.h>
++
++#include "debug.h"
++#include "mdetect.h"
++#include "multiaccess_ctl.h"
++
++/* Reserved for local / experimental use */
++#define MAJOR_NUM 0xf9
++
++/* Get Care-of address information for Mobile Node */
++#define IOCTL_GET_CAREOFADDR _IOWR(MAJOR_NUM, 9, void *)
++
++#define MA_IOCTL_SET_IFACE_PREFERENCE _IOR (MAJOR_NUM, 13, void *)
++
++/* The name of the device file */
++#define CTLFILE "mipv6_dev"
++
++static int inuse = 0;
++
++static int mipv6_open(struct inode *inode, struct file *file)
++{
++      DEBUG(DBG_INFO, "(%p)\n", file);
++
++      if (inuse)
++              return -EBUSY;
++
++      inuse++;
++
++      MOD_INC_USE_COUNT;
++
++      return 0;
++}
++
++static int mipv6_close(struct inode *inode, struct file *file)
++{
++      DEBUG(DBG_INFO, "(%p,%p)\n", inode, file);
++      inuse--;
++
++      MOD_DEC_USE_COUNT;
++
++      return 0;
++}
++
++int mipv6_ioctl(struct inode *inode, struct file *file, 
++              unsigned int ioctl_num, /* The number of the ioctl */
++              unsigned long arg)      /* The parameter to it */
++{
++      struct in6_addr careofaddr;
++
++      /* Switch according to the ioctl called */
++      switch (ioctl_num) {
++      case IOCTL_GET_CAREOFADDR:
++              DEBUG(DBG_DATADUMP, "IOCTL_GET_CAREOFADDR");
++              /* First get home address from user and then look up 
++               * the care-of address and return it
++               */
++              if (copy_from_user(&careofaddr, (struct in6_addr *)arg, 
++                                 sizeof(struct in6_addr)) < 0) {
++                      DEBUG(DBG_WARNING, "Copy from user failed");
++                      return -EFAULT;
++              }
++              mipv6_get_care_of_address(&careofaddr, &careofaddr);
++              if (copy_to_user((struct in6_addr *)arg, &careofaddr,
++                               sizeof(struct in6_addr)) < 0) {
++                      DEBUG(DBG_WARNING, "copy_to_user failed");
++                      return -EFAULT;
++              }
++              break;
++      case MA_IOCTL_SET_IFACE_PREFERENCE:
++              DEBUG(DBG_INFO, "MA_IOCTL_SET_IFACE_PREFERENCE");
++              ma_ctl_set_preference(arg);
++              break;
++
++      default:
++              DEBUG(DBG_WARNING, "Unknown ioctl cmd (%d)", ioctl_num);
++              return -ENOENT;
++      }
++      return 0;
++}
++
++struct file_operations Fops = {
++      owner: THIS_MODULE,
++      read: NULL,
++      write: NULL,
++      poll: NULL,
++      ioctl: mipv6_ioctl,
++      open: mipv6_open,
++      release: mipv6_close
++};
++
++
++/* Initialize the module - Register the character device */
++int mipv6_ioctl_mn_init(void)
++{
++      int ret_val;
++
++      /* Register the character device (atleast try) */
++      ret_val = register_chrdev(MAJOR_NUM, CTLFILE, &Fops);
++
++      /* Negative values signify an error */
++      if (ret_val < 0) {
++              DEBUG(DBG_ERROR, "failed registering char device (err=%d)",
++                    ret_val);
++              return ret_val;
++      }
++
++      DEBUG(DBG_INFO, "Device number %x, success", MAJOR_NUM);
++      return 0;
++}
++
++
++/* Cleanup - unregister the appropriate file from /proc */
++void mipv6_ioctl_mn_exit(void)
++{
++      int ret;
++      /* Unregister the device */
++      ret = unregister_chrdev(MAJOR_NUM, CTLFILE);
++
++      /* If there's an error, report it */
++      if (ret < 0)
++              DEBUG(DBG_ERROR, "errorcode: %d\n", ret);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.c
+@@ -0,0 +1,1153 @@
++/*
++ *      Movement Detection Module
++ *
++ *      Authors:
++ *      Henrik Petander                <lpetande@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ *      Handles the L3 movement detection of mobile node and also
++ *      changing of its routes.
++ *  
++ */
++
++/*
++ *    Changes:
++ *
++ *    Nanno Langstraat        :       Locking fixes
++ *      Venkata Jagana          :       Locking fix
++ */
++
++#include <linux/autoconf.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/if_arp.h>
++#include <linux/route.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/addrconf.h>
++#include <net/mipglue.h>
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include "util.h"
++#include "mdetect.h"
++#include "mn.h"
++#include "debug.h"
++#include "multiaccess_ctl.h"
++
++#define START 0
++#define CONTINUE 1
++#define OK 2
++#define DEBUG_MDETECT 7
++
++#define DEF_RTR_POLL_IVAL 5 /* In seconds */
++
++#define NO_RTR 0
++#define RTR_SUSPECT 1
++#define CURR_RTR_OK 2
++
++#define RA_RCVD 0
++#define NA_RCVD 1
++#define TIMEOUT 2
++
++#define MIPV6_MDF_NONE 0x0
++#define MIPV6_MDF_HAS_RTR_PREV 0x1
++
++#define ROUTER_REACHABLE 1
++#define RADV_MISSED 2
++#define NOT_REACHABLE 3
++
++/* R_TIME_OUT paramater is used to make the decision when to change the 
++ * default  router, if the current one is unreachable. 2s is pretty aggressive 
++ * and may result in hopping between two routers. OTOH a small value enhances 
++ * the  performance
++ */
++#define R_TIME_OUT 30*HZ
++
++/* maximum RA interval for router unreachability detection */
++#define MAX_RADV_INTERVAL 6*HZ  /* 6000 ms... */
++
++/* Threshold for exponential resending of router solicitations */
++#define RS_RESEND_LINEAR 10*HZ
++
++#define EAGER_CELL_SWITCHING 1
++#define LAZY_CELL_SWITCHING 0
++#define RESPECT_DAD 1
++
++#define ROUTER_ADDRESS 0x20
++
++/* RA flags */
++#define ND_RA_FLAG_MANAGED  0x80
++#define ND_RA_FLAG_OTHER    0x40
++#define ND_RA_FLAG_HA       0x20
++
++/* DAD flags for global and link local addresses */
++
++#define COA_TENTATIVE       0x10
++#define LLADDR_TENTATIVE    0x01
++
++struct router {
++      struct list_head list;
++      struct in6_addr ll_addr;
++      struct in6_addr raddr; /* Also contains prefix */
++      __u8 link_addr[MAX_ADDR_LEN]; /* link layer address */
++      __u8 link_addr_len;
++      __u8 state;
++      __u8 is_current;
++      __u8 reachable;
++      int ifindex;
++      int pfix_len; /* Length of the network prefix */
++      unsigned long lifetime; /* from ra */
++      __u32 last_ns_sent; 
++      __u32 last_ra_rcvd;
++      __u32 interval; /* ra interval in milliseconds, 0 if not set */ 
++      int glob_addr; /*Whether raddr contains also routers global address*/
++      __u8 flags; /* RA flags, for example ha */
++        struct in6_addr CoA;     /* care-off address used with this router */
++      int extra_addr_route;
++};
++
++/* dad could also be RESPECT_DAD for duplicate address detection of
++   new care-of addresses */
++static int dad = 0;
++
++/* Only one choice, nothing else implemented */
++int max_rtr_reach_time = DEF_RTR_POLL_IVAL;
++
++
++int eager_cell_switching = EAGER_CELL_SWITCHING; /* Can be set to 0 via proc */  
++static spinlock_t router_lock; 
++static spinlock_t ho_lock;
++
++static void coa_timer_handler(unsigned long arg);
++static void timer_handler(unsigned long foo);
++static struct router *curr_router = NULL, *next_router = NULL;
++static struct timer_list r_timer = { function: timer_handler };
++static struct timer_list coa_timer = { function: coa_timer_handler };
++#define MAX_ROUTERS 1000
++static LIST_HEAD(rtr_list);
++static int num_routers = 0;
++static struct handoff *_ho = NULL;
++/*
++ * Functions for handling the default router list, which movement
++ * detection uses for avoiding loops etc.
++ */
++
++/* TODO: Send NS to router after MAX interval has passed from last RA */
++static int mipv6_router_state(struct router *rtr) {
++      if (rtr->interval) {
++              if (time_before(jiffies, (rtr->last_ra_rcvd + (rtr->interval * HZ) / 1000)))
++                      return ROUTER_REACHABLE;
++              else
++                      return NOT_REACHABLE;
++      }
++      else
++              if (time_after(jiffies, rtr->last_ra_rcvd + (rtr->lifetime * HZ)))
++                      return NOT_REACHABLE;
++      return ROUTER_REACHABLE;
++}
++
++/* searches for a specific router or any router that is reachable, 
++ * if address is NULL. Also deletes obsolete routers.
++ */
++static void mipv6_router_gc(void)
++{
++      struct router *curr = NULL;
++      struct list_head *lh, *lh_tmp;
++
++      DEBUG_FUNC();
++
++      list_for_each_safe(lh, lh_tmp, &rtr_list) {
++              curr =  list_entry(lh, struct router, list);
++              if (mipv6_router_state(curr) == NOT_REACHABLE && !curr->is_current) {
++                      num_routers--;
++                      list_del_init(&curr->list);
++                      DEBUG(DBG_DATADUMP, "Deleting unreachable router  %x:%x:%x:%x:%x:%x:%x:%x", 
++                            NIPV6ADDR(&curr->raddr));
++                      kfree(curr);
++              }
++              else {
++                      DEBUG(DBG_DATADUMP, "NOT Deleting router  %x:%x:%x:%x:%x:%x:%x:%x", 
++                            NIPV6ADDR(&curr->raddr));
++              }
++      }
++}
++
++static struct router *mipv6_rtr_get(struct in6_addr *search_addr)
++{
++      struct router *rtr = NULL;
++      struct list_head *lh;
++
++      DEBUG_FUNC();
++
++      if (search_addr == NULL)
++              return NULL;
++      list_for_each(lh, &rtr_list) {
++              rtr = list_entry(lh, struct router, list);
++              if(!ipv6_addr_cmp(search_addr, &rtr->raddr)) {
++                      return rtr;
++              }
++      }
++      return NULL;
++}
++
++/*
++ * Adds router to list
++ */
++static struct router *mipv6_rtr_add(struct router *nrt)
++{
++
++      struct router *rptr;
++
++      DEBUG_FUNC();
++
++      /* check if someone is trying DoS attack, or we just have some
++           memory leaks... */
++      if (num_routers > MAX_ROUTERS) {
++              DEBUG(DBG_CRITICAL, 
++                    "failed to add new router, MAX_ROUTERS exceeded");
++              return NULL;
++      }
++      
++      rptr = kmalloc(sizeof(struct router), GFP_ATOMIC);
++      if (rptr) {
++              memcpy(rptr, nrt, sizeof(struct router));
++              list_add(&rptr->list, &rtr_list);
++              num_routers++;
++      }
++      DEBUG(DBG_INFO, "Adding router: %x:%x:%x:%x:%x:%x:%x:%x, "
++            "lifetime : %d sec, adv.interval: %d millisec", 
++            NIPV6ADDR(&rptr->raddr), rptr->lifetime, rptr->interval);
++
++      DEBUG(DBG_INFO, "num_routers after addition: %d", num_routers);
++      return rptr;
++}
++
++/* Cleans up the list */
++static void list_free(struct router **curr_router_p)
++{
++      struct router *tmp;
++      struct list_head *lh, *lh_tmp;
++
++      DEBUG_FUNC();
++
++      DEBUG(DBG_INFO, "Freeing the router list");
++      /* set curr_router->prev_router and curr_router NULL */
++      *curr_router_p = NULL;
++      list_for_each_safe(lh, lh_tmp, &rtr_list) {
++              tmp = list_entry(lh, struct router, list);
++              DEBUG(DBG_INFO, "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(&tmp->ll_addr));
++              list_del(&tmp->list);
++              kfree(tmp);
++              num_routers--;
++      }
++}
++
++int rs_state = START;
++
++/* Sends router solicitations to all valid devices 
++ * source  = link local address (of sending interface)
++ * dstaddr = all routers multicast address
++ * Solicitations are sent at an exponentially decreasing rate
++ *
++ * TODO: send solicitation first at a normal rate (from ipv6) and
++ *       after that use the exponentially increasing intervals 
++ */
++static int rs_send(void)
++{
++      struct net_device *dev;
++      struct in6_addr raddr, lladdr;
++      struct inet6_dev *in6_dev = NULL;
++      static int num_rs;
++
++      if (rs_state == START) {
++              num_rs = 0;
++              rs_state = CONTINUE;
++      } else if (num_rs++ > MAX_RTR_SOLICITATIONS)
++              return HZ;
++
++      ipv6_addr_all_routers(&raddr);
++      read_lock(&dev_base_lock); 
++
++      /*  Send router solicitations to all interfaces  */
++      for (dev = dev_base; dev; dev = dev->next) {
++              if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) {
++                      DEBUG(DBG_DATADUMP, "Sending RS to device %s", 
++                            dev->name);
++                      if (!ipv6_get_lladdr(dev, &lladdr)) {
++                              ndisc_send_rs(dev, &lladdr, &raddr);
++                              in6_dev = in6_dev_get(dev);
++                              in6_dev->if_flags |= IF_RS_SENT;
++                              in6_dev_put(in6_dev);
++                      } else {
++                              DEBUG(DBG_DATADUMP, "%s: device doesn't have link-local address!\n", dev->name);
++                              continue;
++                      }
++              }
++              
++      }
++      read_unlock(&dev_base_lock);
++      return RTR_SOLICITATION_INTERVAL;
++}
++
++/* Create a new CoA for MN and also add a route to it if it is still tentative 
++   to allow MN to get packets to the address immediately
++ */
++static int form_coa(struct in6_addr *coa, struct in6_addr *pfix, 
++                  int plen, int ifindex)
++{
++      struct net_device *dev;
++      struct inet6_dev *in6_dev;
++      int ret = 0;
++
++      if ((dev = dev_get_by_index(ifindex)) == NULL) {
++              DEBUG(DBG_WARNING, "Device is not present");
++              return -1;
++      }
++      if ((in6_dev = in6_dev_get(dev)) == NULL) {
++              DEBUG(DBG_WARNING, "inet6_dev is not present");
++              dev_put(dev);
++              return -1;
++      }
++      coa->s6_addr32[0] = pfix->s6_addr32[0];
++      coa->s6_addr32[1] = pfix->s6_addr32[1];
++
++      if (ipv6_generate_eui64(coa->s6_addr + 8, dev) &&
++          ipv6_inherit_eui64(coa->s6_addr + 8, in6_dev)) {
++              in6_dev_put(in6_dev);
++              dev_put(dev);
++              return -1;
++      }
++      if (ipv6_chk_addr(coa, dev) == 0) { 
++              DEBUG(DBG_WARNING, "care-of address still tentative");
++              ret = 1;
++      }
++      DEBUG(DBG_INFO, "Formed new CoA:  %x:%x:%x:%x:%x:%x:%x:%x",
++            NIPV6ADDR(coa));
++      
++      in6_dev_put(in6_dev);
++      dev_put(dev);
++      return ret;
++}
++
++static inline int rtr_is_gw(struct router *rtr, struct rt6_info *rt) 
++{
++      return ((rt->rt6i_flags & RTF_GATEWAY) && 
++              !ipv6_addr_cmp(&rt->rt6i_gateway, &rtr->ll_addr));
++}
++
++static inline int is_prefix_route(struct router *rtr, struct rt6_info *rt) 
++{
++      return (!(rt->rt6i_flags & RTF_GATEWAY) &&
++              mipv6_prefix_compare(&rt->rt6i_dst.addr, &rtr->raddr, 
++                                   rtr->pfix_len));
++}
++
++/*
++ * Function that determines whether given rt6_info should be destroyed
++ * (negative => destroy rt6_info, zero or positive => do nothing) 
++ */
++static int mn_route_cleaner(struct rt6_info *rt, void *arg)
++{
++      int type;
++
++      struct router *rtr = (struct router *)arg;
++
++      int ret = -1;
++
++      DEBUG_FUNC();
++      
++      if (!rt || !rtr) {
++              DEBUG(DBG_ERROR, "mn_route_cleaner: rt or rtr NULL");
++              return 0;
++      }
++
++      /* Do not delete routes to local addresses or to multicast
++       * addresses, since we need them to get router advertisements
++       * etc. Multicast addresses are more tricky, but we don't
++       * delete them in any case. The routing mechanism is not optimal for 
++       * multihoming.   
++       *
++       * Also keep all new prefix routes, gateway routes through rtr and
++       * all remaining default routes (including those used for reverse
++       * tunneling)
++       */
++      type = ipv6_addr_type(&rt->rt6i_dst.addr);
++      
++      if ((type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) ||
++          rt->rt6i_dev == &loopback_dev || rtr_is_gw(rtr, rt) ||
++          is_prefix_route(rtr, rt) || (rt->rt6i_flags & RTF_DEFAULT))  
++              ret = 0;
++      
++      /*   delete all others */
++
++      if (rt->rt6i_dev != &loopback_dev) {
++              DEBUG(DEBUG_MDETECT, 
++                    "%s route:\n"
++                    "dev: %s,\n"
++                    "gw: %x:%x:%x:%x:%x:%x:%x:%x,\n"
++                    "flags: %x,\n"
++                    "metric: %d,\n"
++                    "src: %x:%x:%x:%x:%x:%x:%x:%x,\n"
++                    "dst: %x:%x:%x:%x:%x:%x:%x:%x,\n"
++                    "plen: %d\n",
++                    (ret ? "Deleting" : "Keeping"),
++                    rt->rt6i_dev->name,              
++                    NIPV6ADDR(&rt->rt6i_gateway),            
++                    rt->rt6i_flags,
++                    rt->rt6i_metric,
++                    NIPV6ADDR(&rt->rt6i_src.addr),
++                    NIPV6ADDR(&rt->rt6i_dst.addr),
++                    rt->rt6i_dst.plen);
++      }
++      return ret;
++}
++
++/* 
++ * Deletes old routes 
++ */
++static __inline__ void delete_routes(struct router *rtr)
++{
++      DEBUG_FUNC();
++
++      /* Routing table is locked to ensure that nobody uses its */  
++      write_lock_bh(&rt6_lock);
++      DEBUG(DBG_INFO, "mipv6: Purging routes");
++      /*  TODO: Does not prune, should it?  */
++      fib6_clean_tree(&ip6_routing_table, 
++                      mn_route_cleaner, 0, rtr);
++      write_unlock_bh(&rt6_lock);
++
++}
++
++
++static __inline__ void delete_coas(struct router *rtr)
++{
++      struct net_device *dev;
++      struct inet6_dev *idev;
++      struct inet6_ifaddr *ifa;
++
++      dev = dev_get_by_index(rtr->ifindex);
++      if (!dev)
++              return;
++
++      idev = in6_dev_get(dev);
++      
++      if (idev) {
++              read_lock_bh(&idev->lock);
++              ifa = idev->addr_list;
++              while (ifa) {
++                      int keep; 
++                      spin_lock(&ifa->lock);
++                      
++                      keep = (ifa->flags&(IFA_F_PERMANENT|IFA_F_HOMEADDR) ||
++                              !ipv6_addr_cmp(&ifa->addr, &rtr->CoA));
++                      
++                      spin_unlock(&ifa->lock);
++                      
++                      if (keep)
++                              ifa = ifa->if_next;
++                      else {
++                              in6_ifa_hold(ifa);
++                              read_unlock_bh(&idev->lock);
++                              
++                              ipv6_del_addr(ifa);
++                              
++                              read_lock_bh(&idev->lock);
++                              ifa = idev->addr_list;
++                      }
++              }
++              read_unlock_bh(&idev->lock);
++              in6_dev_put(idev);
++      }
++      dev_put(dev);
++}
++
++int next_mdet_state[3][3] = {{CURR_RTR_OK, NO_RTR, NO_RTR},
++                           {CURR_RTR_OK, CURR_RTR_OK, NO_RTR},
++                           {CURR_RTR_OK, CURR_RTR_OK, RTR_SUSPECT}};
++ 
++char *states[3] = {"NO_RTR", "RTR_SUSPECT", "CURR_RTR_OK"};
++char *events[3] = {"RA_RCVD", "NA_RCVD", "TIMEOUT"};
++
++/* State transitions
++ * NO_RTR, RA_RCVD -> CURR_RTR_OK
++ * NO_RTR, NA_RCVD -> NO_RTR
++ * NO_RTR, TIMEOUT -> NO_RTR
++
++ * RTR_SUSPECT, RA_RCVD -> CURR_RTR_OK
++ * RTR_SUSPECT, NA_RCVD -> CURR_RTR_OK
++ * RTR_SUSPECT, TIMEOUT -> NO_RTR
++
++ * CURR_RTR_OK, RA_RCVD -> CURR_RTR_OK
++ * CURR_RTR_OK, NA_RCVD -> CURR_RTR_OK
++ * CURR_RTR_OK, TIMEOUT -> RTR_SUSPECT
++ */
++static int _curr_state = NO_RTR;
++
++#if 0
++static int get_mdet_state(void){
++      int state;
++      spin_lock_bh(&router_lock); 
++      state = _curr_state;
++      spin_unlock_bh(&router_lock);
++      return state;
++}
++#endif
++
++/* Needs to be called with router_lock locked */
++static int mdet_statemachine(int event)
++{
++      
++      if (event > 2 || _curr_state > 2) {
++             DEBUG(DBG_ERROR, "Got illegal event or curr_state");
++             return -1;
++      }
++
++      DEBUG(DBG_DATADUMP, "Got event %s and curr_state is %s", 
++            events[event], states[_curr_state]); 
++      
++      _curr_state = next_mdet_state[_curr_state][event];
++      DEBUG(DBG_DATADUMP, "Next state is %s", states[_curr_state]);
++      return _curr_state;
++}
++
++static void mipv6_do_ll_dad(int ifindex)
++{
++      struct net_device *dev = dev_get_by_index(ifindex);
++      if (dev) {
++              struct in6_addr lladdr;
++              struct inet6_ifaddr *ifa;
++              if (!ipv6_get_lladdr(dev, &lladdr) &&
++                  (ifa = ipv6_get_ifaddr(&lladdr, dev)) != NULL) {
++                      spin_lock_bh(&ifa->lock);
++                      if (!(ifa->flags & IFA_F_TENTATIVE)) {
++                              ifa->flags |= IFA_F_TENTATIVE;
++                              spin_unlock_bh(&ifa->lock);
++                              addrconf_dad_start(ifa, 0);
++                      } else
++                              spin_unlock_bh(&ifa->lock);
++
++              }
++              dev_put(dev);
++      }
++}
++/* 
++ * Changes the router, called from ndisc.c if mipv6_router_event 
++ * returns true.
++ */
++
++static void mipv6_change_router(void)
++{
++      struct in6_addr coa;
++      int ret, ifindex;
++      
++      DEBUG_FUNC(); 
++
++      
++      if (next_router == NULL) 
++              return;
++      
++      spin_lock(&router_lock);
++
++
++      if (curr_router != NULL && 
++          !ipv6_addr_cmp(&curr_router->ll_addr, &next_router->ll_addr)) {
++              DEBUG(DBG_INFO,"Trying to handoff from: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(&curr_router->ll_addr));
++              DEBUG(DBG_INFO,"Trying to handoff to: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(&next_router->ll_addr));
++              next_router = NULL; /* Let's not leave dangling pointers */
++              spin_unlock(&router_lock);
++              return;
++        }
++      ret = form_coa(&next_router->CoA, &next_router->raddr, 
++                     next_router->pfix_len, next_router->ifindex);
++      if (ret < 0) {
++              DEBUG(DBG_ERROR, "handoff: Creation of coa failed");
++              spin_unlock(&router_lock);
++              return;
++      } else if (ret > 0)
++              next_router->flags |= COA_TENTATIVE;
++
++      mdet_statemachine(RA_RCVD); /* TODO: What if DAD fails... */
++      if (next_router->interval)
++              mod_timer(&r_timer, jiffies + 
++                        (next_router->interval * HZ)/1000);
++      else
++              mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ);
++      
++
++      if (ret == 0) {
++              ipv6_addr_copy(&coa, &next_router->CoA);
++              ifindex = next_router->ifindex;
++              spin_unlock(&router_lock);
++              mipv6_mdet_finalize_ho(&coa, ifindex);
++              return;
++      }
++      spin_unlock(&router_lock);
++
++}
++static unsigned long ns_send(void)
++{
++      struct neighbour *neigh;
++      struct net_device *dev;
++      struct in6_addr *raddr;
++
++      DEBUG(DBG_DATADUMP, "Sending Neighbour solicitation to default router to verify its reachability");
++      if (!curr_router) 
++              return HZ;
++      if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL)
++              return HZ;
++      if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) {
++              dev_put(dev);
++              return HZ;
++      }
++      if (curr_router->glob_addr)
++              raddr = &curr_router->raddr;
++      else 
++              raddr = &curr_router->ll_addr;
++
++      curr_router->last_ns_sent = jiffies;
++      ndisc_send_ns(dev, neigh, raddr, raddr, NULL);  
++
++      neigh_release(neigh);
++      dev_put(dev);
++      return HZ/5; /* Wait 200ms for a reply */
++}
++
++static int na_rcvd(void)
++{     
++      int neigh_ok = 0;
++      struct neighbour *neigh;
++      struct net_device *dev;
++
++      if (!curr_router) 
++              return 0;
++      if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL)
++              return 0;
++      if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) {
++              dev_put(dev);
++              return 0;
++      }
++      if (neigh->flags & NTF_ROUTER && 
++          (time_after(neigh->confirmed, curr_router->last_ns_sent) || 
++           neigh->confirmed == curr_router->last_ns_sent)) {
++              neigh_ok = 1;
++              DEBUG(DBG_DATADUMP, "Mdetect event: NA rcvd from curr rtr");
++      } else
++              DEBUG(DBG_DATADUMP, "Mdetect event: NA NOT rcvd from curr rtr within time limit");
++      neigh_release(neigh);
++      dev_put(dev);
++      return neigh_ok;
++}
++
++static void coa_timer_handler(unsigned long dummy)
++{
++
++      spin_lock_bh(&ho_lock);
++      if (_ho) {
++              DEBUG(DBG_INFO, "Starting handoff after DAD");
++              mipv6_mobile_node_moved(_ho);
++              kfree(_ho);
++              _ho = NULL;
++      }
++      spin_unlock_bh(&ho_lock);
++}
++static void timer_handler(unsigned long foo)
++{
++      unsigned long timeout;
++      int state;
++      spin_lock_bh(&router_lock);
++      
++      if (_curr_state != NO_RTR)
++              rs_state = START;
++
++      if (_curr_state == RTR_SUSPECT && na_rcvd()) {
++              state = mdet_statemachine(NA_RCVD);
++              timeout = curr_router->interval ? curr_router->interval : max_rtr_reach_time * HZ;
++      } else { 
++              state =  mdet_statemachine(TIMEOUT);
++              if (state == NO_RTR)
++                      timeout = rs_send();
++              else  /* RTR_SUSPECT */
++                      timeout = ns_send();
++      }
++      if (!timeout)
++              timeout = HZ;
++
++      mipv6_router_gc();
++      mod_timer(&r_timer, jiffies + timeout);
++      spin_unlock_bh(&router_lock);
++}
++
++/**
++ * mipv6_get_care_of_address - get node's care-of primary address
++ * @homeaddr: one of node's home addresses
++ * @coaddr: buffer to store care-of address
++ *
++ * Stores the current care-of address in the @coaddr, assumes
++ * addresses in EUI-64 format.  Since node might have several home
++ * addresses caller MUST supply @homeaddr.  If node is at home
++ * @homeaddr is stored in @coaddr.  Returns 0 on success, otherwise a
++ * negative value.
++ **/
++int mipv6_get_care_of_address(
++      struct in6_addr *homeaddr, struct in6_addr *coaddr)
++{
++      
++      DEBUG_FUNC();
++
++      if (homeaddr == NULL)
++              return -1;
++      spin_lock_bh(&router_lock);
++      if (curr_router == NULL || mipv6_mn_is_at_home(homeaddr) || 
++          mipv6_prefix_compare(homeaddr, &curr_router->raddr, 64) || 
++          curr_router->flags&COA_TENTATIVE) {
++              DEBUG(DBG_INFO,
++                    "mipv6_get_care_of_address: returning home address");
++              ipv6_addr_copy(coaddr, homeaddr);
++              spin_unlock_bh(&router_lock);
++              return 0;
++
++      }
++
++      /* At home or address check failure probably due to dad wait */
++      if (mipv6_prefix_compare(&curr_router->raddr, homeaddr, 
++                               curr_router->pfix_len) 
++                               || (dad == RESPECT_DAD && 
++                                   (ipv6_chk_addr(coaddr, NULL) == 0))) { 
++              ipv6_addr_copy(coaddr, homeaddr);
++      } else { 
++              ipv6_addr_copy(coaddr, &curr_router->CoA);
++      }
++
++      spin_unlock_bh(&router_lock);
++      return 0;
++}
++
++int mipv6_mdet_del_if(int ifindex)
++{
++      struct router *curr = NULL;
++      struct list_head *lh, *lh_tmp;
++
++      spin_lock_bh(&router_lock);
++      list_for_each_safe(lh, lh_tmp, &rtr_list) {
++              curr =  list_entry(lh, struct router, list);
++              if (curr->ifindex == ifindex) {
++                      num_routers--;
++                      list_del_init(&curr->list);
++                      DEBUG(DBG_DATADUMP, "Deleting router  %x:%x:%x:%x:%x:%x:%x:%x on interface %d", 
++                            NIPV6ADDR(&curr->raddr), ifindex);
++                      if (curr_router == curr)
++                              curr_router = NULL;
++                      kfree(curr);
++              }
++      }
++      spin_unlock_bh(&router_lock);
++      return 0;
++}
++
++void mipv6_mdet_retrigger_ho(void)
++{
++      struct handoff ho;
++
++      spin_lock_bh(&router_lock);
++      if (curr_router != NULL) {
++              ho.coa = &curr_router->CoA;
++              ho.plen = curr_router->pfix_len;
++              ho.ifindex = curr_router->ifindex;
++              ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr);
++              ho.home_address = (curr_router->glob_addr && 
++                                 curr_router->flags&ND_RA_FLAG_HA);
++      }
++      spin_unlock_bh(&router_lock);
++      mipv6_mobile_node_moved(&ho);
++}
++
++void mipv6_mdet_set_curr_rtr_reachable(int reachable)
++{
++      spin_lock_bh(&router_lock);
++      if (curr_router != NULL) {
++              curr_router->reachable = reachable;
++      }
++      spin_unlock_bh(&router_lock);
++
++}
++
++int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex)
++{
++      int dummy;
++      struct handoff ho;
++      struct router *tmp;
++      struct net_device *dev; 
++      struct in6_addr ll_addr;
++
++      spin_lock_bh(&router_lock);
++
++      if (!next_router) {
++              spin_unlock_bh(&router_lock);
++              return 0;
++      }
++
++      dev = dev_get_by_index(next_router->ifindex);
++
++      if (ipv6_get_lladdr(dev, &ll_addr) == 0) {
++              if (ipv6_addr_cmp(&ll_addr, coa) == 0)
++                      DEBUG(DBG_INFO, "DAD for link local address completed");
++              next_router->flags &= ~LLADDR_TENTATIVE;
++      }
++
++      dev_put(dev);
++
++      if (mipv6_prefix_compare(coa, &next_router->CoA, 
++                               next_router->pfix_len)) {
++              DEBUG(DBG_INFO, "DAD for Care-of address completed");
++              next_router->flags &= ~COA_TENTATIVE;
++      }
++      if (!(next_router->flags&LLADDR_TENTATIVE) && !(next_router->flags&COA_TENTATIVE)) {
++              DEBUG(DBG_INFO, "%s: Proceeding with handoff after DAD\n", __FUNCTION__);
++              tmp = curr_router;
++              curr_router = next_router;
++              curr_router->is_current = 1;
++              next_router = NULL; 
++              curr_router->flags &= ~COA_TENTATIVE; 
++              delete_routes(curr_router);
++              delete_coas(curr_router);
++              if (tmp) {
++                      struct net_device *dev_old = dev_get_by_index(tmp->ifindex);
++                      struct rt6_info *rt = NULL;
++                      if (dev_old) {
++                              rt = rt6_get_dflt_router(&tmp->ll_addr, dev_old);
++                              dev_put(dev_old);
++                      }
++                      if (rt)
++                              ip6_del_rt(rt, NULL);
++                      tmp->is_current = 0;
++              }
++
++              ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy);
++              ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy);
++
++
++              ho.coa = &curr_router->CoA;
++              ho.plen = curr_router->pfix_len;
++              ho.ifindex = curr_router->ifindex;
++              ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr);
++              ho.home_address = (curr_router->glob_addr && 
++                                  curr_router->flags&ND_RA_FLAG_HA);
++              
++              spin_unlock_bh(&router_lock);
++              mipv6_mobile_node_moved(&ho);
++      } else 
++              spin_unlock_bh(&router_lock);
++      return 0;
++}
++/* Decides whether router candidate is the same router as current rtr
++ * based on prefix / global addresses of the routers and their link local 
++ * addresses 
++ */
++static int is_current_rtr(struct router *nrt, struct router *crt)
++{
++      DEBUG_FUNC();
++      
++      DEBUG(DEBUG_MDETECT, "Current router: "
++            "%x:%x:%x:%x:%x:%x:%x:%x and", NIPV6ADDR(&crt->raddr));
++      DEBUG(DEBUG_MDETECT, "Candidate router: "
++            "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&nrt->raddr));
++
++      return (!ipv6_addr_cmp(&nrt->raddr,&crt->raddr) && 
++              !ipv6_addr_cmp(&nrt->ll_addr, &crt->ll_addr));
++}
++
++/* 
++ * Change next router to nrtr
++ * Returns 1, if router has been changed.
++ */ 
++
++static int change_next_rtr(struct router *nrtr, struct router *ortr)
++{
++      int changed = 0;
++      DEBUG_FUNC();
++
++      if (!next_router || ipv6_addr_cmp(&nrtr->raddr, &next_router->raddr)) {
++              changed = 1;
++      }
++      next_router = nrtr;
++      return changed;
++}
++static int clean_ncache(struct router *nrt, struct router *ort, int same_if)
++{
++      struct net_device *ortdev;
++      DEBUG_FUNC();
++
++      /* Always call ifdown after a handoff to ensure proper routing */
++      
++      if (!ort) 
++              return 0;
++      if ((ortdev = dev_get_by_index(ort->ifindex)) == NULL) {
++              DEBUG(DBG_WARNING, "Device is not present");
++              return -1;
++      }
++      neigh_ifdown(&nd_tbl, ortdev);
++      dev_put(ortdev);        
++      return 0;
++}
++
++static int mdet_get_if_preference(int ifi)
++{
++      int pref = 0;
++
++      DEBUG_FUNC();
++
++      pref = ma_ctl_get_preference(ifi);
++
++      DEBUG(DEBUG_MDETECT, "ifi: %d preference %d", ifi, pref);
++
++      return pref;
++}
++
++/*
++ * Called from mipv6_mn_ra_rcv to determine whether to do a handoff. 
++ */
++static int mipv6_router_event(struct router *rptr)
++{
++      struct router *nrt = NULL;
++      int new_router = 0, same_if = 1;
++      int oldstate = _curr_state;
++      int addrtype = ipv6_addr_type(&rptr->raddr);
++
++      DEBUG_FUNC();
++
++      if (rptr->lifetime == 0)
++              return MIPV6_IGN_RTR;
++      DEBUG(DEBUG_MDETECT, "Received a RA from router: "
++            "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&rptr->raddr));
++      spin_lock(&router_lock);
++      
++      /* Add or update router entry */
++      if ((nrt = mipv6_rtr_get(&rptr->raddr)) == NULL) {
++              if (addrtype == IPV6_ADDR_ANY || (nrt = mipv6_rtr_add(rptr)) == NULL) {
++                              spin_unlock(&router_lock);
++                              return MIPV6_IGN_RTR;
++              }
++              DEBUG(DBG_INFO, "Router not on list,adding it to the list"); 
++              new_router = 1;
++      }
++      nrt->last_ra_rcvd = jiffies;
++      nrt->state = ROUTER_REACHABLE;
++      nrt->interval = rptr->interval;
++      nrt->lifetime = rptr->lifetime;
++      nrt->ifindex = rptr->ifindex;
++      nrt->flags = rptr->flags;
++      nrt->glob_addr = rptr->glob_addr;
++
++      /* Whether from current router */
++      if (curr_router && curr_router->reachable && 
++          is_current_rtr(nrt, curr_router)) {
++              if (nrt->interval)
++                      mod_timer(&r_timer, jiffies + (nrt->interval * HZ)/1000);
++              else
++                      mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ);
++              mdet_statemachine(RA_RCVD);
++              spin_unlock(&router_lock);
++              return MIPV6_ADD_RTR;
++      } else if (oldstate == NO_RTR) {
++              rt6_purge_dflt_routers(0); /* For multiple interface case */
++              DEBUG(DBG_INFO, "No router or router not reachable, switching to new one");   
++              goto handoff;
++      }
++      if (!curr_router) { 
++              /* Startup */
++              goto handoff;
++      }
++      /* Router behind same interface as current one ?*/
++      same_if = (nrt->ifindex == curr_router->ifindex);
++      /* Switch to new router behind same interface if eager cell 
++       *  switching is used or if the interface is preferred
++       */
++      if ((new_router && eager_cell_switching && same_if) ||
++          (mdet_get_if_preference(nrt->ifindex) > 
++           mdet_get_if_preference(curr_router->ifindex))) {
++              DEBUG(DBG_INFO, "Switching to new router.");
++              goto handoff;
++      }
++      
++      /* No handoff, don't add default route */
++      DEBUG(DEBUG_MDETECT, "Ignoring RA");
++      spin_unlock(&router_lock);
++      return MIPV6_IGN_RTR;
++handoff:
++      clean_ncache(nrt, curr_router, same_if);
++      nrt->reachable = 1;
++      if (same_if && change_next_rtr(nrt, curr_router)) {
++              mipv6_do_ll_dad(nrt->ifindex);
++              nrt->flags |= LLADDR_TENTATIVE;
++      }
++      spin_unlock(&router_lock);
++
++      return MIPV6_CHG_RTR;
++}     
++
++/* 
++ * Called from ndisc.c's router_discovery.
++ */
++
++static inline int ret_to_ha(struct in6_addr *addr)
++{
++      int res = 0;
++      struct mn_info *minfo;
++      read_lock(&mn_info_lock);
++      minfo = mipv6_mninfo_get_by_ha(addr);
++      if (minfo != NULL) {
++              spin_lock(&minfo->lock);
++              if (minfo->has_home_reg) {
++                      res = 1;
++              }
++              spin_unlock(&minfo->lock);
++      }
++      read_unlock(&mn_info_lock);
++      return res;
++}
++
++static int mipv6_mn_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts)
++{
++      int ifi = ((struct inet6_skb_parm *)skb->cb)->iif;
++      struct ra_msg *ra = (struct ra_msg *) skb->h.raw;
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct router nrt;
++      struct in6_addr *ha = NULL;
++      u8 *lladdr = NULL;
++      int res;
++      DEBUG_FUNC();
++
++      memset(&nrt, 0, sizeof(struct router));
++
++      if (ra->icmph.icmp6_home_agent) {
++              nrt.flags |= ND_RA_FLAG_HA;
++              DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_HA up");
++      }
++
++      if (ra->icmph.icmp6_addrconf_managed) {
++              nrt.flags |= ND_RA_FLAG_MANAGED;
++              DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_MANAGED up");
++      }
++
++      if (ra->icmph.icmp6_addrconf_other) {
++              nrt.flags |= ND_RA_FLAG_OTHER;
++              DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_OTHER up");
++      }
++
++      ipv6_addr_copy(&nrt.ll_addr, saddr);
++      nrt.ifindex = ifi;
++      nrt.lifetime = ntohs(ra->icmph.icmp6_rt_lifetime);
++
++      if (ndopts->nd_opts_src_lladdr) {
++              lladdr = (u8 *) ndopts->nd_opts_src_lladdr+2;
++              nrt.link_addr_len = skb->dev->addr_len;
++              memcpy(nrt.link_addr, lladdr, nrt.link_addr_len);
++      }
++      if (ndopts->nd_opts_pi) {
++              struct nd_opt_hdr *p;
++              for (p = ndopts->nd_opts_pi;
++                   p;
++                   p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) {
++                      struct prefix_info *pinfo;
++                      int update = 0;
++
++                      pinfo = (struct prefix_info *) p;
++
++                      if (!pinfo->autoconf)
++                              continue;
++
++                      if ((pinfo->router_address && 
++                           (update = ret_to_ha(&pinfo->prefix))) ||
++                          ipv6_addr_type(&nrt.raddr) != IPV6_ADDR_UNICAST) {
++                              ipv6_addr_copy(&nrt.raddr, &pinfo->prefix);
++                              nrt.pfix_len = pinfo->prefix_len;
++                              if (pinfo->router_address)
++                                      nrt.glob_addr = 1;
++                              else
++                                      nrt.glob_addr = 0;
++                              if (update)
++                                      ha = &pinfo->prefix;
++                              DEBUG(DBG_DATADUMP, "Address of the received "
++                                    "prefix info option: %x:%x:%x:%x:%x:%x:%x:%x", 
++                                    NIPV6ADDR(&nrt.raddr));
++                              DEBUG(DBG_DATADUMP, "the length of the prefix is %d", 
++                                    nrt.pfix_len);
++                      }
++              }
++      }
++      if (ndopts->nd_opts_rai) {                      
++              nrt.interval = ntohl(*(__u32 *)(ndopts->nd_opts_rai+4));
++              DEBUG(DBG_DATADUMP, 
++                    "received router interval option with interval : %d ",
++                    nrt.interval / HZ);
++
++              if (nrt.interval > MAX_RADV_INTERVAL) {
++                      nrt.interval = 0;
++                      DEBUG(DBG_DATADUMP, "but we are using: %d, "
++                            "because interval>MAX_RADV_INTERVAL",
++                            nrt.interval / HZ);
++              }
++      }
++
++      res = mipv6_router_event(&nrt);
++      
++      if (ha && lladdr) {
++              mipv6_mn_ha_nd_update(__dev_get_by_index(ifi), ha, lladdr);
++      }
++      return res;
++}
++
++int __init mipv6_initialize_mdetect(void)
++{
++
++      DEBUG_FUNC();
++
++      spin_lock_init(&router_lock);
++      spin_lock_init(&ho_lock);
++      init_timer(&coa_timer);
++      init_timer(&r_timer);
++      r_timer.expires = jiffies + HZ;
++      add_timer(&r_timer);
++
++      /* Actual HO, also deletes old routes after the addition of new ones 
++         in ndisc */
++      MIPV6_SETCALL(mipv6_change_router, mipv6_change_router);
++
++      MIPV6_SETCALL(mipv6_ra_rcv, mipv6_mn_ra_rcv);
++
++      return 0;
++}
++
++int __exit mipv6_shutdown_mdetect()
++{
++
++      DEBUG_FUNC();
++
++      MIPV6_RESETCALL(mipv6_ra_rcv);
++      MIPV6_RESETCALL(mipv6_change_router);
++      spin_lock_bh(&router_lock);
++      spin_lock(&ho_lock);
++      del_timer(&coa_timer);
++      del_timer(&r_timer);
++      /* Free the memory allocated by router list */
++      list_free(&curr_router);
++      if (_ho)
++              kfree(_ho);
++      spin_unlock(&ho_lock);
++      spin_unlock_bh(&router_lock);
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.h
+@@ -0,0 +1,37 @@
++/*
++ *      MIPL Mobile IPv6 Movement detection module header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _MDETECT_H
++#define _MDETECT_H
++
++struct handoff {
++      int home_address; /* Is the coa a home address */
++      int ifindex;
++      int plen;
++      struct in6_addr *coa;
++      struct in6_addr rtr_addr; /* Prefix or rtr address if coa is home address */
++};
++
++int mipv6_initialize_mdetect(void);
++
++int mipv6_shutdown_mdetect(void);
++
++int mipv6_get_care_of_address(struct in6_addr *homeaddr, struct in6_addr *coa);
++
++int mipv6_mdet_del_if(int ifindex);
++
++int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex);
++
++void mipv6_mdet_retrigger_ho(void);
++
++void mipv6_mdet_set_curr_rtr_reachable(int reachable);
++
++#endif /* _MDETECT_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.c
+@@ -0,0 +1,342 @@
++/**
++ * Generic icmp routines
++ *
++ * Authors:
++ * Jaakko Laine <medved@iki.fi>,
++ * Ville Nuorvala <vnuorval@tcs.hut.fi> 
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/icmpv6.h>
++#include <net/checksum.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/mipv6.h>
++#include <net/mipglue.h>
++
++#include "debug.h"
++#include "bcache.h"
++#include "mipv6_icmp.h"
++#include "config.h"
++
++struct mipv6_icmpv6_msg {
++      struct icmp6hdr icmph;
++      __u8 *data;
++      struct in6_addr *daddr;
++      int len;
++      __u32 csum;
++};
++
++#define MIPV6_ICMP_HOP_LIMIT 64
++
++static struct socket *mipv6_icmpv6_socket = NULL;
++static __u16 identifier = 0;
++
++int mipv6_icmpv6_no_rcv(struct sk_buff *skb)
++{
++      return 0;
++}
++
++static int mipv6_icmpv6_xmit_holder = -1;
++
++static int mipv6_icmpv6_xmit_lock_bh(void)
++{
++      if (!spin_trylock(&mipv6_icmpv6_socket->sk->lock.slock)) {
++              if (mipv6_icmpv6_xmit_holder == smp_processor_id())
++                      return -EAGAIN;
++              spin_lock(&mipv6_icmpv6_socket->sk->lock.slock);
++      }
++      mipv6_icmpv6_xmit_holder = smp_processor_id();
++      return 0;
++}
++
++static __inline__ int mipv6_icmpv6_xmit_lock(void)
++{
++      int ret;
++      local_bh_disable();
++      ret = mipv6_icmpv6_xmit_lock_bh();
++      if (ret)
++              local_bh_enable();
++      return ret;
++}
++
++static void mipv6_icmpv6_xmit_unlock_bh(void)
++{
++      mipv6_icmpv6_xmit_holder = -1;
++      spin_unlock(&mipv6_icmpv6_socket->sk->lock.slock);
++}
++
++static __inline__ void mipv6_icmpv6_xmit_unlock(void)
++{
++      mipv6_icmpv6_xmit_unlock_bh();
++      local_bh_enable();
++}
++
++
++/**
++ * mipv6_icmpv6_dest_unreach - Destination Unreachable ICMP error message handler
++ * @skb: buffer containing ICMP error message
++ *
++ * Special Mobile IPv6 ICMP handling.  If Correspondent Node receives
++ * persistent ICMP Destination Unreachable messages for a destination
++ * in its Binding Cache, the binding should be deleted.  See draft
++ * section 8.8.
++ **/
++static int mipv6_icmpv6_rcv_dest_unreach(struct sk_buff *skb)
++{
++      struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
++      struct ipv6hdr *ipv6h = (struct ipv6hdr *) (icmph + 1);
++      int left = (skb->tail - skb->h.raw) - sizeof(*icmph)- sizeof(ipv6h);
++      struct ipv6_opt_hdr *eh;
++      struct rt2_hdr *rt2h = NULL;
++      struct in6_addr *daddr = &ipv6h->daddr;
++      struct in6_addr *saddr = &ipv6h->saddr;
++      int hdrlen, nexthdr = ipv6h->nexthdr;
++      struct mipv6_bce bce;
++      DEBUG_FUNC();
++
++      eh = (struct ipv6_opt_hdr *) (ipv6h + 1);
++
++      while (left > 0) {
++              if (nexthdr != NEXTHDR_HOP && nexthdr != NEXTHDR_DEST && 
++                  nexthdr != NEXTHDR_ROUTING)
++                      return 0;
++
++              hdrlen = ipv6_optlen(eh);
++              if (hdrlen > left)
++                      return 0;
++
++              if (nexthdr == NEXTHDR_ROUTING) {
++                      struct ipv6_rt_hdr *rth = (struct ipv6_rt_hdr *) eh;
++
++                      if (rth->type == IPV6_SRCRT_TYPE_2) {
++                              if (hdrlen != sizeof(struct rt2_hdr))
++                                      return 0;
++
++                              rt2h = (struct rt2_hdr *) rth;
++
++                              if (rt2h->rt_hdr.segments_left > 0)
++                                      daddr = &rt2h->addr;
++                              break;
++                      }
++              }
++              /* check for home address option in case this node is a MN */
++              if (nexthdr == NEXTHDR_DEST) {
++                      __u8 *raw = (__u8 *) eh;
++                      __u16 i = 2;
++                      while (1) {
++                              struct mipv6_dstopt_homeaddr *hao;
++                              
++                              if (i + sizeof (*hao) > hdrlen)
++                                      break;
++                              
++                              hao = (struct mipv6_dstopt_homeaddr *) &raw[i];
++                              
++                              if (hao->type == MIPV6_TLV_HOMEADDR &&
++                                  hao->length == sizeof(struct in6_addr)) {
++                                      saddr = &hao->addr;
++                                      break;
++                              }
++                              if (hao->type)
++                                      i += hao->length + 2;
++                              else
++                                      i++;
++                      }
++                      
++              }
++              nexthdr = eh->nexthdr;
++              eh = (struct ipv6_opt_hdr *) ((u8 *) eh + hdrlen);
++              left -= hdrlen;
++      }
++      if (rt2h == NULL) return 0;
++
++      if (mipv6_bcache_get(daddr, saddr, &bce) == 0 && !(bce.flags&HOME_REGISTRATION)) {
++              /* A primitive algorithm for detecting persistent ICMP destination unreachable messages */
++              if (bce.destunr_count &&
++                  time_after(jiffies, 
++                             bce.last_destunr + MIPV6_DEST_UNR_IVAL*HZ)) 
++                      bce.destunr_count = 0;
++
++              bce.destunr_count++;
++
++              mipv6_bcache_icmp_err(daddr, saddr, bce.destunr_count);
++
++              if (bce.destunr_count > MIPV6_MAX_DESTUNREACH && mipv6_bcache_delete(daddr, saddr, CACHE_ENTRY) == 0) {
++                      DEBUG(DBG_INFO, "Deleted bcache entry "
++                            "%x:%x:%x:%x:%x:%x:%x:%x "
++                            "%x:%x:%x:%x:%x:%x:%x:%x (reason: "
++                            "%d dest unreachables) ",
++                            NIPV6ADDR(daddr), NIPV6ADDR(saddr), bce.destunr_count);
++              }
++      }
++      return 0;
++}
++
++static int mipv6_icmpv6_getfrag(const void *data, struct in6_addr *saddr, 
++                              char *buff, unsigned int offset, 
++                              unsigned int len)
++{
++      struct mipv6_icmpv6_msg *msg = (struct mipv6_icmpv6_msg *) data;
++      struct icmp6hdr *icmph;
++      __u32 csum;
++
++      if (offset) {
++              msg->csum = csum_partial_copy_nocheck(msg->data + offset -
++                                                    sizeof(*icmph), buff,
++                                                    len, msg->csum);
++              return 0;
++      }
++      
++      csum = csum_partial_copy_nocheck((__u8 *) &msg->icmph, buff,
++                                       sizeof(*icmph), msg->csum);
++      
++      csum = csum_partial_copy_nocheck(msg->data, buff + sizeof(*icmph),
++                                       len - sizeof(*icmph), csum);
++      
++      icmph = (struct icmp6hdr *) buff;
++      
++      icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
++                                           IPPROTO_ICMPV6, csum);
++      return 0; 
++}
++
++/**
++ * mipv6_icmpv6_send - generic icmpv6 message send
++ * @daddr: destination address
++ * @saddr: source address
++ * @type: icmp type
++ * @code: icmp code
++ * @id: packet identifier. If null, uses internal counter to get new id
++ * @data: packet data
++ * @datalen: length of data in bytes
++ */
++void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, int type,
++                     int code, __u16 *id, __u16 flags, void *data, int datalen)
++{
++      struct sock *sk = mipv6_icmpv6_socket->sk;
++      struct flowi fl;
++      struct mipv6_icmpv6_msg msg;
++
++      DEBUG_FUNC();
++
++      fl.proto = IPPROTO_ICMPV6;
++      fl.fl6_dst = daddr;
++      fl.fl6_src = saddr;
++      fl.fl6_flowlabel = 0;
++      fl.uli_u.icmpt.type = type;
++      fl.uli_u.icmpt.code = code;
++
++      msg.icmph.icmp6_type = type;
++      msg.icmph.icmp6_code = code;
++      msg.icmph.icmp6_cksum = 0;
++
++      if (id)
++              msg.icmph.icmp6_identifier = htons(*id);
++      else
++              msg.icmph.icmp6_identifier = htons(identifier++);
++
++      msg.icmph.icmp6_sequence = htons(flags);
++      msg.data = data;
++      msg.csum = 0;
++      msg.len = datalen + sizeof(struct icmp6hdr);
++      msg.daddr = daddr;
++
++      if (mipv6_icmpv6_xmit_lock())
++              return;
++
++      ip6_build_xmit(sk, mipv6_icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1,
++                     MSG_DONTWAIT);
++
++      ICMP6_INC_STATS_BH(Icmp6OutMsgs);
++      mipv6_icmpv6_xmit_unlock();
++}
++
++/**
++ * icmp6_rcv - ICMPv6 receive and multiplex
++ * @skb: buffer containing ICMP message
++ *
++ * Generic ICMPv6 receive function to multiplex messages to approriate
++ * handlers.  Only used for ICMP messages with special handling in
++ * Mobile IPv6.
++ **/
++static void icmp6_rcv(struct sk_buff *skb)
++{
++      struct icmp6hdr *hdr;
++
++      if (skb_is_nonlinear(skb) &&
++          skb_linearize(skb, GFP_ATOMIC) != 0) {
++              kfree_skb(skb);
++              return;
++      }
++      __skb_push(skb, skb->data-skb->h.raw);
++
++      hdr = (struct icmp6hdr *) skb->h.raw;
++
++      switch (hdr->icmp6_type) {
++      case ICMPV6_DEST_UNREACH:
++              mipv6_icmpv6_rcv_dest_unreach(skb);
++              break;
++
++      case ICMPV6_PARAMPROB:
++              mip6_fn.icmpv6_paramprob_rcv(skb);
++              break;
++
++      case MIPV6_DHAAD_REPLY:
++              mip6_fn.icmpv6_dhaad_rep_rcv(skb);
++              break;
++
++      case MIPV6_PREFIX_ADV:
++              mip6_fn.icmpv6_pfxadv_rcv(skb);
++              break;
++
++      case MIPV6_DHAAD_REQUEST:
++              mip6_fn.icmpv6_dhaad_req_rcv(skb);
++              break;
++
++      case MIPV6_PREFIX_SOLICIT:
++              mip6_fn.icmpv6_pfxsol_rcv(skb);
++              break;
++      }
++}
++
++int mipv6_icmpv6_init(void)
++{
++      struct sock *sk;
++      int err;
++
++      if ((mipv6_icmpv6_socket = sock_alloc()) == NULL) {
++              DEBUG(DBG_ERROR, "Cannot allocate mipv6_icmpv6_socket");
++              return -1;
++      }
++      mipv6_icmpv6_socket->type = SOCK_RAW;
++
++      if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_ICMP, 
++                             &mipv6_icmpv6_socket)) < 0) {
++              DEBUG(DBG_ERROR, "Cannot initialize mipv6_icmpv6_socket");
++              sock_release(mipv6_icmpv6_socket);
++              mipv6_icmpv6_socket = NULL; /* For safety */
++              return err;
++      }
++      sk = mipv6_icmpv6_socket->sk;
++      sk->allocation = GFP_ATOMIC;
++      sk->prot->unhash(sk);
++
++      /* Register our ICMP handler */
++      MIPV6_SETCALL(mipv6_icmp_rcv, icmp6_rcv);
++      return 0;
++}
++
++void mipv6_icmpv6_exit(void)
++{
++      MIPV6_RESETCALL(mipv6_icmp_rcv);
++      if (mipv6_icmpv6_socket)
++              sock_release(mipv6_icmpv6_socket);
++      mipv6_icmpv6_socket = NULL; /* For safety */
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.h
+@@ -0,0 +1,43 @@
++/*
++ *      MIPL Mobile IPv6 ICMP send and receive prototypes
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _MIPV6_ICMP
++#define _MIPV6_ICMP
++
++#include <linux/config.h>
++#include <linux/in6.h>
++
++void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr,
++                     int type, int code, __u16 *id, __u16 flags,
++                     void *data, int datalen);
++
++void mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id);
++
++void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr);
++/* No handling */
++int mipv6_icmpv6_no_rcv(struct sk_buff *skb);
++
++/* Receive DHAAD Reply message */
++int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb);
++/* Receive Parameter Problem message */
++int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb);
++/* Receive prefix advertisements */
++int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb);
++
++/* Receive DHAAD Request message */
++int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb);
++/* Receive prefix solicitations */
++int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb);
++
++int mipv6_icmpv6_init(void);
++void mipv6_icmpv6_exit(void);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_ha.c
+@@ -0,0 +1,158 @@
++/*
++ *    Home Agent specific ICMP routines
++ *
++ *    Authors:
++ *    Antti Tuominen  <ajtuomin@tml.hut.fi>
++ *    Jaakko Laine    <medved@iki.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/autoconf.h>
++#include <linux/sched.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/ip6_route.h>
++#include <net/mipv6.h>
++
++#include "halist.h"
++#include "debug.h"
++#include "mipv6_icmp.h"
++//#include "prefix.h"
++
++/* Is this the easiest way of checking on 
++ *  which interface an anycast address is ?
++ */
++static int find_ac_dev(struct in6_addr *addr)
++{
++      int ifindex = 0;
++      struct net_device *dev;
++      read_lock(&dev_base_lock);
++      for (dev=dev_base; dev; dev=dev->next) {
++              if (ipv6_chk_acast_addr(dev, addr)) {
++                      ifindex = dev->ifindex;
++                      break;
++              }
++      }
++      read_unlock(&dev_base_lock);
++      return ifindex;
++}
++
++/**
++ * mipv6_icmpv6_send_dhaad_rep - Reply to DHAAD Request
++ * @ifindex: index of interface request was received from
++ * @id: request's identification number
++ * @daddr: requester's IPv6 address
++ *
++ * When Home Agent receives Dynamic Home Agent Address Discovery
++ * request, it replies with a list of home agents available on the
++ * home link.
++ */
++void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr)
++{
++      __u8 *data = NULL;
++      struct in6_addr home, *ha_addrs = NULL;
++      int addr_count, max_addrs, size = 0;
++
++      if (daddr == NULL)
++              return;
++
++      if (mipv6_ha_get_addr(ifindex, &home) < 0) {
++              DEBUG(DBG_INFO, "Not Home Agent in this interface");
++              return;
++      }
++
++      /* We send all available HA addresses, not exceeding a maximum
++       * number we can fit in a packet with minimum IPv6 MTU (to
++       * avoid fragmentation).
++       */
++      max_addrs = 76;
++      addr_count = mipv6_ha_get_pref_list(ifindex, &ha_addrs, max_addrs);
++
++      if (addr_count < 0) return;
++
++      if (addr_count != 0 && ha_addrs == NULL) {
++              DEBUG(DBG_ERROR, "addr_count = %d but return no addresses", 
++                    addr_count);
++              return;
++      }
++      data = (u8 *)ha_addrs;
++
++      size = addr_count * sizeof(struct in6_addr);
++
++      mipv6_icmpv6_send(daddr, &home, MIPV6_DHAAD_REPLY, 
++                        0, &id, 0, data, size);
++      if (ha_addrs) {
++              data = NULL;
++              kfree(ha_addrs);
++      }
++}
++
++/** 
++ * mipv6_icmpv6_dhaad_req - Home Agent Address Discovery Request ICMP handler
++ * @skb: buffer containing ICMP information message
++ *
++ * Special Mobile IPv6 ICMP message.  Handles Dynamic Home Agent
++ * Address Discovery Request messages.
++ **/
++int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb)
++{
++      struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw;
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
++      __u16 identifier;
++      int ifindex = 0;
++
++      DEBUG_FUNC();
++
++      /* Invalid packet checks. */
++      if (phdr->icmp6_code != 0)
++              return 0;
++
++      identifier = ntohs(phdr->icmp6_identifier);
++
++      /* 
++       * Make sure we have the right ifindex (if the
++       * req came through another interface. 
++       */
++      ifindex = find_ac_dev(daddr);
++      if (ifindex == 0) { 
++              DEBUG(DBG_WARNING, "received dhaad request to anycast address %x:%x:%x:%x:%x:%x:%x:%x"
++                    " on which prefix we are not HA",
++                    NIPV6ADDR(daddr));
++              return 0;
++      }
++
++      /*
++       * send reply with list
++       */
++      mipv6_icmpv6_send_dhaad_rep(ifindex, identifier, saddr);
++      return 1;
++}
++#if 0
++/**
++ * mipv6_icmpv6_handle_pfx_sol - handle prefix solicitations
++ * @skb: sk_buff including the icmp6 message
++ */
++int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb)
++{
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
++      struct inet6_ifaddr *ifp;
++
++      DEBUG_FUNC();
++
++      if (!(ifp = ipv6_get_ifaddr(daddr, NULL)))
++              return -1;
++
++      in6_ifa_put(ifp);
++      mipv6_pfx_cancel_send(saddr, -1);
++
++      return 0;
++}
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_mn.c
+@@ -0,0 +1,273 @@
++/*
++ *    Mobile Node specific ICMP routines
++ *
++ *    Authors:
++ *    Antti Tuominen  <ajtuomin@tml.hut.fi>
++ *    Jaakko Laine    <medved@iki.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/sched.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/addrconf.h>
++#include <net/mipv6.h>
++
++#include "mn.h"
++#include "bul.h"
++#include "mdetect.h"
++#include "debug.h"
++#include "mipv6_icmp.h"
++#include "util.h"
++//#include "prefix.h"
++
++#define INFINITY 0xffffffff
++
++/**
++ * mipv6_icmpv6_paramprob - Parameter Problem ICMP error message handler
++ * @skb: buffer containing ICMP error message
++ *
++ * Special Mobile IPv6 ICMP handling.  If Mobile Node receives ICMP
++ * Parameter Problem message when using a Home Address Option,
++ * offending node should be logged and error message dropped.  If
++ * error is received because of a Binding Update, offending node
++ * should be recorded in Binding Update List and no more Binding
++ * Updates should be sent to this destination.  See RFC 3775 section
++ * 10.15.
++ **/
++int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb)
++{
++      struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw;
++      struct in6_addr *saddr = skb ? &skb->nh.ipv6h->saddr : NULL;
++      struct in6_addr *daddr = skb ? &skb->nh.ipv6h->daddr : NULL;
++      struct ipv6hdr *hdr = (struct ipv6hdr *) (phdr + 1);
++      int ulen = (skb->tail - (unsigned char *) (phdr + 1));
++
++      int errptr;
++      __u8 *off_octet;
++
++      DEBUG_FUNC();
++
++      /* We only handle code 1 & 2 messages. */
++      if (phdr->icmp6_code != ICMPV6_UNK_NEXTHDR &&
++          phdr->icmp6_code != ICMPV6_UNK_OPTION)
++              return 0;
++
++      /* Find offending octet in the original packet. */
++      errptr = ntohl(phdr->icmp6_pointer);
++
++      /* There is not enough of the original packet left to figure
++       * out what went wrong. Bail out. */
++      if (ulen <= errptr)
++              return 0;
++
++      off_octet = ((__u8 *) hdr + errptr);
++      DEBUG(DBG_INFO, "Parameter problem: offending octet %d [0x%2x]",
++            errptr, *off_octet);
++
++      /* If CN did not understand Mobility Header, set BUL entry to
++       * ACK_ERROR so no further BUs are sumbitted to this CN. */
++      if (phdr->icmp6_code == ICMPV6_UNK_NEXTHDR &&
++          *off_octet == IPPROTO_MOBILITY) {
++              struct bul_inval_args args;
++              args.all_rr_states = 1;
++              args.cn = saddr;
++              args.mn = daddr;
++              write_lock(&bul_lock);
++              mipv6_bul_iterate(mn_bul_invalidate, &args);
++              write_unlock(&bul_lock);
++      }
++
++      /* If CN did not understand Home Address Option, we log an
++       * error and discard the error message. */
++      if (phdr->icmp6_code == ICMPV6_UNK_OPTION &&
++          *off_octet == MIPV6_TLV_HOMEADDR) {
++              DEBUG(DBG_WARNING, "Correspondent node does not "
++                    "implement Home Address Option receipt.");
++              return 1;
++      }
++      return 0;
++}
++
++/**
++ * mipv6_mn_dhaad_send_req - Send DHAAD Request to home network
++ * @home_addr: address to do DHAAD for
++ * @plen: prefix length for @home_addr
++ *
++ * Send Dynamic Home Agent Address Discovery Request to the Home
++ * Agents anycast address in the nodes home network.
++ **/
++void 
++mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id)
++{
++      struct in6_addr ha_anycast;
++      struct in6_addr careofaddr;
++      
++      if (mipv6_get_care_of_address(home_addr, &careofaddr) < 0) {
++              DEBUG(DBG_WARNING, "Could not get node's Care-of Address");
++              return;
++      }
++
++      if (mipv6_ha_anycast(&ha_anycast, home_addr, plen) < 0) {
++              DEBUG(DBG_WARNING, 
++                    "Could not get Home Agent Anycast address for home address %x:%x.%x:%x:%x:%x:%x:%x/%d",
++                    NIPV6ADDR(home_addr), plen);
++              return;
++      }
++
++      mipv6_icmpv6_send(&ha_anycast, &careofaddr, MIPV6_DHAAD_REQUEST, 0, 
++                        &dhaad_id, 0, NULL, 0);
++
++}
++
++/** 
++ * mipv6_icmpv6_dhaad_rep - Home Agent Address Discovery Reply ICMP handler
++ * @skb: buffer containing ICMP information message
++ *
++ * Special Mobile IPv6 ICMP message.  Handles Dynamic Home Agent
++ * Address Discovery Reply messages.
++ **/
++int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb)
++{
++      struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw;
++      struct in6_addr *address;
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      __u16 identifier;
++      int ulen = (skb->tail - (unsigned char *) ((__u32 *) phdr + 2));
++      int i;
++      struct in6_addr home_addr, coa;
++      struct in6_addr *first_ha = NULL;
++      struct mn_info *minfo;
++      int n_addr = ulen / sizeof(struct in6_addr);
++
++      DEBUG_FUNC();
++
++      /* Invalid packet checks. */
++      if (ulen % sizeof(struct in6_addr) != 0)
++              return 0;
++
++      if (phdr->icmp6_code != 0)
++              return 0;
++
++      identifier = ntohs(phdr->icmp6_identifier);
++      if (ulen > 0) {
++              address = (struct in6_addr *) ((__u32 *) phdr + 2);
++      } else {
++              address = saddr;
++              n_addr = 1;
++      }
++
++      /* receive list of home agent addresses
++       * add to home agents list
++       */
++      DEBUG(DBG_INFO, "DHAAD: got %d home agents", n_addr);
++
++      first_ha = address;
++
++      /* lookup H@ with identifier */
++      read_lock(&mn_info_lock);
++      minfo = mipv6_mninfo_get_by_id(identifier);
++      if (!minfo) {
++              read_unlock(&mn_info_lock);
++              DEBUG(DBG_INFO, "no mninfo with id %d", 
++                    identifier);
++              return 0;
++      }
++      spin_lock(&minfo->lock);
++
++      /* Logic:
++       * 1. if old HA on list, prefer it
++       * 2. otherwise first HA on list prefered
++       */
++      for (i = 0; i < n_addr; i++) {
++              DEBUG(DBG_INFO, "HA[%d] %x:%x:%x:%x:%x:%x:%x:%x",
++                    i, NIPV6ADDR(address));
++              if (ipv6_addr_cmp(&minfo->ha, address) == 0) {
++                      spin_unlock(&minfo->lock);
++                      read_unlock(&mn_info_lock);
++                      return 0;
++              }
++              address++;
++      }
++      ipv6_addr_copy(&minfo->ha, first_ha);
++      spin_unlock(&minfo->lock);
++      ipv6_addr_copy(&home_addr, &minfo->home_addr);
++      read_unlock(&mn_info_lock);
++
++      mipv6_get_care_of_address(&home_addr, &coa);
++      init_home_registration(&home_addr, &coa);
++
++      return 1;
++}
++#if 0
++/**
++ * mipv6_icmpv6_handle_pfx_adv - handle prefix advertisements
++ * @skb: sk_buff including the icmp6 message
++ */
++int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb)
++{
++      struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
++      __u8 *opt = (__u8 *) (hdr + 1);
++      int optlen = (skb->tail - opt);
++      unsigned long min_expire = INFINITY;
++      struct inet6_skb_parm *parm = (struct inet6_skb_parm *) skb->cb;
++
++      DEBUG_FUNC();
++
++      while (optlen > 0) {
++              int len = opt[1] << 3;
++              if (len == 0)
++                      goto set_timer;
++
++              if (opt[0] == ND_OPT_PREFIX_INFO) {
++                      int ifindex;
++                      unsigned long expire;
++                      struct prefix_info *pinfo =
++                              (struct prefix_info *) opt;
++                      struct net_device *dev;
++                      struct mn_info *mninfo;
++
++                      read_lock(&mn_info_lock);
++                      mninfo = mipv6_mninfo_get_by_ha(saddr);
++                      if (mninfo == NULL) {
++                              ifindex = 0;
++                      } else {
++                              spin_lock(&mninfo->lock);
++                              ifindex = mninfo->ifindex;
++                              spin_unlock(&mninfo->lock);
++                              mninfo = NULL;
++                      }
++                      read_unlock(&mn_info_lock);
++
++                      if (!(dev = dev_get_by_index(ifindex))) {
++                              DEBUG(DBG_WARNING, "Cannot find device by index %d", parm->iif);
++                              goto nextopt;
++                      }
++
++                      expire = ntohl(pinfo->valid);
++                      expire = expire == 0 ? INFINITY : expire;
++
++                      min_expire = expire < min_expire ? expire : min_expire;
++
++                      dev_put(dev);
++              }
++
++nextopt:
++              optlen -= len;
++              opt += len;
++      }
++
++set_timer:
++
++      mipv6_pfx_add_home(parm->iif, saddr, daddr, min_expire);
++      return 0;
++}
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mn.c
+@@ -0,0 +1,1521 @@
++/*
++ *      Mobile-node functionality
++ *
++ *      Authors:
++ *      Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/autoconf.h>
++#include <linux/sched.h>
++#include <linux/ipv6.h>
++#include <linux/net.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/rtnetlink.h>
++#include <linux/if_arp.h>
++#include <linux/ipsec.h>
++#include <linux/notifier.h>
++#include <linux/list.h>
++#include <linux/route.h>
++#include <linux/netfilter.h>
++#include <linux/netfilter_ipv6.h>
++#include <linux/tqueue.h>
++#include <linux/proc_fs.h>
++
++#include <asm/uaccess.h>
++
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/neighbour.h>
++#include <net/ndisc.h>
++#include <net/ip6_route.h>
++#include <net/mipglue.h>
++
++#include "util.h"
++#include "mdetect.h"
++#include "bul.h"
++#include "mobhdr.h"
++#include "debug.h"
++#include "mn.h"
++#include "mipv6_icmp.h"
++#include "multiaccess_ctl.h"
++//#include "prefix.h"
++#include "tunnel_mn.h"
++#include "stats.h"
++#include "config.h"
++
++#define MIPV6_BUL_SIZE 128
++
++static LIST_HEAD(mn_info_list);
++
++/* Lock for list of MN infos */
++rwlock_t mn_info_lock = RW_LOCK_UNLOCKED;
++
++static spinlock_t ifrh_lock = SPIN_LOCK_UNLOCKED;
++
++struct ifr_holder {
++      struct list_head list;
++      struct in6_ifreq ifr;
++      int old_ifi;
++      struct handoff *ho;
++};
++
++LIST_HEAD(ifrh_list);
++
++static struct tq_struct mv_home_addr_task;
++
++/* Determines whether manually configured home addresses are preferred as 
++ * source addresses over dynamically configured ones
++ */
++int mipv6_use_preconfigured_hoaddr = 1; 
++
++/* Determines whether home addresses, which are at home are preferred as 
++ * source addresses over other home addresses
++ */
++int mipv6_use_topol_corr_hoaddr = 0;
++
++static spinlock_t icmpv6_id_lock = SPIN_LOCK_UNLOCKED;
++static __u16 icmpv6_id = 0;
++
++static inline __u16 mipv6_get_dhaad_id(void)
++{
++      __u16 ret;
++      spin_lock_bh(&icmpv6_id_lock);
++      ret = ++icmpv6_id;
++      spin_unlock_bh(&icmpv6_id_lock);
++      return ret;
++}
++
++/** 
++ * mipv6_mninfo_get_by_home - Returns mn_info for a home address
++ * @haddr: home address of MN
++ *
++ * Returns mn_info on success %NULL otherwise.  Caller MUST hold
++ * @mn_info_lock (read or write).
++ **/
++struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr)
++{
++      struct list_head *lh;
++      struct mn_info *minfo;
++
++      DEBUG_FUNC();
++
++      if (!haddr)
++              return NULL;
++
++      list_for_each(lh, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              spin_lock(&minfo->lock);
++              if (!ipv6_addr_cmp(&minfo->home_addr, haddr)) {
++                      spin_unlock(&minfo->lock);
++                      return minfo;
++              }
++              spin_unlock(&minfo->lock);
++      }
++      return NULL;
++}
++
++/**
++ * mipv6_mninfo_get_by_ha - Lookup mn_info with Home Agent address
++ * @home_agent: Home Agent address
++ *
++ * Searches for a mn_info entry with @ha set to @home_agent.  You MUST
++ * hold @mn_info_lock when calling this function.  Returns pointer to
++ * mn_info entry or %NULL on failure.
++ **/
++struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent)
++{
++      struct list_head *lh;
++      struct mn_info *minfo;
++
++      if (!home_agent)
++              return NULL;
++
++      list_for_each(lh, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              spin_lock(&minfo->lock);
++              if (!ipv6_addr_cmp(&minfo->ha, home_agent)) {
++                      spin_unlock(&minfo->lock);
++                      return minfo;
++              }
++              spin_unlock(&minfo->lock);
++      }
++      return NULL;
++}
++
++/**
++ * mipv6_mninfo_get_by_id - Lookup mn_info with id
++ * @id: DHAAD identifier
++ *
++ * Searches for a mn_info entry with @dhaad_id set to @id.  You MUST
++ * hold @mn_info_lock when calling this function.  Returns pointer to
++ * mn_info entry or %NULL on failure.
++ **/
++struct mn_info *mipv6_mninfo_get_by_id(unsigned short id)
++{
++      struct list_head *lh;
++      struct mn_info *minfo = 0;
++
++      list_for_each(lh, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              spin_lock(&minfo->lock);
++              if (minfo->dhaad_id == id) {
++                      spin_unlock(&minfo->lock);
++                      return minfo;
++              }
++              spin_unlock(&minfo->lock);
++      }
++      return NULL;
++}
++
++/** 
++ * mipv6_mninfo_add - Adds a new home info for MN
++ * @ifindex: Interface for home address
++ * @home_addr:  Home address of MN, must be set
++ * @plen: prefix length of the home address, must be set
++ * @isathome : home address at home
++ * @lifetime: lifetime of the home address, 0 is infinite
++ * @ha: home agent for the home address
++ * @ha_plen: prefix length of home agent's address, can be zero 
++ * @ha_lifetime: Lifetime of the home address, 0 is infinite
++ *
++ * The function adds a new home info entry for MN, allowing it to
++ * register the home address with the home agent.  Starts home
++ * registration process.  If @ha is %ADDRANY, DHAAD is performed to
++ * find a home agent.  Returns 0 on success, a negative value
++ * otherwise.  Caller MUST NOT hold @mn_info_lock or
++ * @addrconf_hash_lock.
++ **/
++void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, 
++                    int isathome, unsigned long lifetime, struct in6_addr *ha, 
++                    int ha_plen, unsigned long ha_lifetime, int man_conf)
++{
++      struct mn_info *minfo;
++      struct in6_addr coa;
++
++      DEBUG_FUNC();
++
++      write_lock_bh(&mn_info_lock);
++      if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL){ 
++            DEBUG(1, "MN info already exists");
++            write_unlock_bh(&mn_info_lock);
++            return;
++      }
++      minfo = kmalloc(sizeof(struct mn_info), GFP_ATOMIC);
++      if (!minfo) {
++             write_unlock_bh(&mn_info_lock);
++             return;
++      }
++      memset(minfo, 0, sizeof(struct mn_info));
++      spin_lock_init(&minfo->lock);
++
++      
++      ipv6_addr_copy(&minfo->home_addr, home_addr);
++
++      if (ha)
++              ipv6_addr_copy(&minfo->ha, ha);
++      if (ha_plen < 128 && ha_plen > 0)
++              minfo->home_plen = ha_plen; 
++      else minfo->home_plen = 64;
++
++      minfo->ifindex_user = ifindex; /* Ifindex for tunnel interface */
++      minfo->ifindex = ifindex; /* Interface on which home address is currently conf'd */
++      /* TODO: we should get home address lifetime from somewhere */
++      /* minfo->home_addr_expires = jiffies + lifetime * HZ; */
++
++      /* manual configuration flag cannot be unset by dynamic updates 
++       *  from prefix advertisements
++       */
++      if (!minfo->man_conf) minfo->man_conf = man_conf; 
++      minfo->is_at_home = isathome;
++
++      list_add(&minfo->list, &mn_info_list);
++      write_unlock_bh(&mn_info_lock);
++
++      if (mipv6_get_care_of_address(home_addr, &coa) == 0) 
++              init_home_registration(home_addr, &coa);
++}
++
++/**
++ * mipv6_mninfo_del - Delete home info for MN 
++ * @home_addr : Home address or prefix 
++ * @del_dyn_only : Delete only dynamically created home entries 
++ *
++ * Deletes every mn_info entry that matches the first plen bits of
++ * @home_addr.  Returns number of deleted entries on success and a
++ * negative value otherwise.  Caller MUST NOT hold @mn_info_lock.
++ **/
++int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only)
++{
++      struct list_head *lh, *next;
++      struct mn_info *minfo;
++      int ret = -1;
++      if (!home_addr)
++              return -1;
++
++      write_lock(&mn_info_lock);
++
++      list_for_each_safe(lh, next, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              if (ipv6_addr_cmp(&minfo->home_addr, home_addr) == 0
++                  && ((!minfo->man_conf && del_dyn_only) || !del_dyn_only)){
++                      list_del(&minfo->list);
++                      kfree(minfo);
++                      ret++;
++              }
++      }
++      write_unlock(&mn_info_lock);
++      return ret;
++}
++
++void mipv6_mn_set_home(int ifindex, struct in6_addr *homeaddr, int plen,
++                     struct in6_addr *homeagent, int ha_plen)
++{
++      mipv6_mninfo_add(ifindex, homeaddr, plen, 0, 0, 
++                       homeagent, ha_plen, 0, 1);
++}
++
++static int skip_dad(struct in6_addr *addr)
++{
++      struct mn_info *minfo;
++      int ret = 0;
++
++      if (addr == NULL) {
++              DEBUG(DBG_CRITICAL, "Null argument");
++              return 0;
++      }
++      read_lock_bh(&mn_info_lock);
++      if ((minfo = mipv6_mninfo_get_by_home(addr)) != NULL) {
++              if ((minfo->is_at_home != MN_NOT_AT_HOME) && (minfo->has_home_reg))
++                      ret = 1;
++              DEBUG(DBG_INFO, "minfo->is_at_home = %d, minfo->has_home_reg = %d",
++                    minfo->is_at_home, minfo->has_home_reg);
++      }
++      read_unlock_bh(&mn_info_lock);
++      
++      return ret;
++}
++/**
++ * mipv6_mn_is_home_addr - Determines if addr is node's home address
++ * @addr: IPv6 address
++ *
++ * Returns 1 if addr is node's home address.  Otherwise returns zero.
++ **/
++int mipv6_mn_is_home_addr(struct in6_addr *addr)
++{
++      int ret = 0;
++
++      if (addr == NULL) {
++              DEBUG(DBG_CRITICAL, "Null argument");
++              return -1;
++      }
++      read_lock_bh(&mn_info_lock);
++      if (mipv6_mninfo_get_by_home(addr))
++              ret = 1;
++      read_unlock_bh(&mn_info_lock);
++
++      return (ret);
++}
++
++/** 
++ * mipv6_mn_is_at_home - determine if node is home for a home address
++ * @home_addr : home address of MN
++ *
++ * Returns 1 if home address in question is in the home network, 0
++ * otherwise.  Caller MUST NOT not hold @mn_info_lock.
++ **/ 
++int mipv6_mn_is_at_home(struct in6_addr *home_addr)
++{
++      struct mn_info *minfo;
++      int ret = 0;
++      read_lock_bh(&mn_info_lock);
++      if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) {
++              spin_lock(&minfo->lock);
++              ret = (minfo->is_at_home == MN_AT_HOME);
++              spin_unlock(&minfo->lock);
++      }
++      read_unlock_bh(&mn_info_lock);
++      return ret;
++}     
++void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg)
++{
++      struct mn_info *minfo;
++      read_lock_bh(&mn_info_lock);
++
++      if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) {
++              spin_lock(&minfo->lock);
++              minfo->has_home_reg = has_home_reg;
++              spin_unlock(&minfo->lock);
++      }
++      read_unlock_bh(&mn_info_lock);
++}     
++
++static int mn_inet6addr_event(
++      struct notifier_block *nb, unsigned long event, void *ptr)
++{
++      struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)ptr;
++
++      switch (event) {
++      case NETDEV_UP:
++              /* Is address a valid coa ?*/
++              if (!(ifp->flags & IFA_F_TENTATIVE))
++                      mipv6_mdet_finalize_ho(&ifp->addr, 
++                                             ifp->idev->dev->ifindex);
++              else if(skip_dad(&ifp->addr))
++                      ifp->flags &= ~IFA_F_TENTATIVE;
++              break;
++      case NETDEV_DOWN:       
++#if 0
++              /* This is useless with manually configured home 
++                 addresses, which will not expire
++              */
++              mipv6_mninfo_del(&ifp->addr, 0);
++#endif
++        break;
++
++      }
++
++      return NOTIFY_DONE;
++}
++
++struct notifier_block mipv6_mn_inet6addr_notifier = {
++      mn_inet6addr_event,
++      NULL,
++      0 /* check if using zero is ok */
++};
++
++static void mipv6_get_saddr_hook(struct in6_addr *homeaddr)
++{
++      int found = 0, reiter = 0;
++      struct list_head *lh;
++      struct mn_info *minfo = NULL;
++      struct in6_addr coa;
++
++      read_lock_bh(&mn_info_lock);
++restart:
++      list_for_each(lh, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              if ((ipv6_addr_scope(homeaddr) != ipv6_addr_scope(&minfo->home_addr)) 
++                  || ipv6_chk_addr(&minfo->home_addr, NULL) == 0)
++                      continue; 
++
++              spin_lock(&minfo->lock);
++              if (minfo->is_at_home == MN_AT_HOME || minfo->has_home_reg) {
++                      if ((mipv6_use_topol_corr_hoaddr && 
++                           minfo->is_at_home == MN_AT_HOME) || 
++                          (mipv6_use_preconfigured_hoaddr && 
++                           minfo->man_conf) ||
++                          (!(mipv6_use_preconfigured_hoaddr || 
++                             mipv6_use_topol_corr_hoaddr) || reiter)) {
++                              spin_unlock(&minfo->lock);
++                              ipv6_addr_copy(homeaddr, &minfo->home_addr);
++                              found = 1;
++                              break;
++                      }
++              }
++              spin_unlock(&minfo->lock);
++      }
++      if (!found && !reiter) {
++              reiter = 1;
++              goto restart;
++      }
++
++      if (!found && minfo && 
++          !mipv6_get_care_of_address(&minfo->home_addr, &coa)) {
++              ipv6_addr_copy(homeaddr, &coa); 
++      }
++      read_unlock_bh(&mn_info_lock);
++
++      DEBUG(DBG_DATADUMP, "Source address selection:  %x:%x:%x:%x:%x:%x:%x:%x", 
++            NIPV6ADDR(homeaddr));
++      return;
++}
++
++static void mv_home_addr(void *arg)
++{
++      mm_segment_t oldfs;
++      int err = 0, new_if = 0;
++      struct list_head *lh, *next;
++      struct ifr_holder *ifrh;
++      LIST_HEAD(list);
++      
++      DEBUG(DBG_INFO, "mipv6 move home address task");
++
++      spin_lock_bh(&ifrh_lock);
++      list_splice_init(&ifrh_list, &list);
++      spin_unlock_bh(&ifrh_lock);
++      
++      oldfs = get_fs(); set_fs(KERNEL_DS);
++      list_for_each_safe(lh, next, &list) {
++              ifrh = list_entry(lh, struct ifr_holder, list);
++              if (ifrh->old_ifi) {
++                      new_if = ifrh->ifr.ifr6_ifindex;
++                      ifrh->ifr.ifr6_ifindex = ifrh->old_ifi;
++                      err = addrconf_del_ifaddr(&ifrh->ifr); 
++                      ifrh->ifr.ifr6_ifindex = new_if;
++                      if (err < 0)
++                              DEBUG(DBG_WARNING, "removal of home address %x:%x:%x:%x:%x:%x:%x:%x from" 
++                                    " old interface %d failed with status %d", 
++                                    NIPV6ADDR(&ifrh->ifr.ifr6_addr), ifrh->old_ifi, err);             
++              }
++              if(!err) {
++                      err = addrconf_add_ifaddr(&ifrh->ifr);
++              }
++              if (ifrh->ho) {
++                      DEBUG(DBG_INFO, "Calling mobile_node moved after moving home address to new if");
++                      mipv6_mobile_node_moved(ifrh->ho);
++              }
++              list_del(&ifrh->list);
++              kfree(ifrh);
++      }
++      set_fs(oldfs);
++
++      if (err < 0)
++              DEBUG(DBG_WARNING, "adding of home address to a new interface %d failed %d", new_if, err);
++      else {
++              DEBUG(DBG_WARNING, "adding of home address to a new interface OK");
++      }
++}
++
++struct dhaad_halist {
++      struct list_head list;
++      struct in6_addr addr;
++      int retry;
++};
++
++/* clear all has from candidate list.  do this when a new dhaad reply
++ * is received. */
++int mipv6_mn_flush_ha_candidate(struct list_head *ha)
++{
++      struct list_head *p, *tmp;
++      struct dhaad_halist *e;
++
++      list_for_each_safe(p, tmp, ha) {
++              e = list_entry(p, struct dhaad_halist, list);
++              list_del(p);
++              kfree(e);
++              e = NULL;
++      }
++      return 0;
++}
++
++/* add new ha to candidates. only done when dhaad reply is received. */
++int mipv6_mn_add_ha_candidate(struct list_head *ha, struct in6_addr *addr)
++{
++      struct dhaad_halist *e;
++
++      e = kmalloc(sizeof(*e), GFP_ATOMIC);
++      memset(e, 0, sizeof(*e));
++      ipv6_addr_copy(&e->addr, addr);
++
++      list_add_tail(&e->list, ha);
++      return 0;
++}
++
++#define MAX_RETRIES_PER_HA 3
++
++/* get next ha candidate.  this is done when dhaad reply has been
++ * received and we want to register with the best available ha. */
++int mipv6_mn_get_ha_candidate(struct list_head *ha, struct in6_addr *addr)
++{
++      struct list_head *p;
++
++      list_for_each(p, ha) {
++              struct dhaad_halist *e;
++              e = list_entry(p, typeof(*e), list);
++              if (e->retry >= 0 && e->retry < MAX_RETRIES_PER_HA) {
++                      ipv6_addr_copy(addr, &e->addr);
++                      return 0;
++              }
++      }
++      return -1;
++}
++
++/* change candidate status.  if registration with ha fails, we
++ * increase retry for ha candidate.  if retry is >= 3 we set it to -1
++ * (failed), do get_ha_candidate() again */
++int mipv6_mn_try_ha_candidate(struct list_head *ha, struct in6_addr *addr)
++{
++      struct list_head *p;
++
++      list_for_each(p, ha) {
++              struct dhaad_halist *e;
++              e = list_entry(p, typeof(*e), list);
++              if (ipv6_addr_cmp(addr, &e->addr) == 0) {
++                      if (e->retry >= MAX_RETRIES_PER_HA) e->retry = -1;
++                      else if (e->retry >= 0) e->retry++;
++                      return 0;
++              }
++      }
++      return -1;
++}
++
++/**
++ * mipv6_mn_get_bulifetime - Get lifetime for a binding update
++ * @home_addr: home address for BU 
++ * @coa: care-of address for BU
++ * @flags: flags used for BU 
++ *
++ * Returns maximum lifetime for BUs determined by the lifetime of
++ * care-of address and the lifetime of home address.
++ **/
++__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, struct in6_addr *coa,
++                            __u8 flags)
++{
++      struct inet6_ifaddr *ifp_hoa, *ifp_coa;
++      __u32 lifetime = (flags & MIPV6_BU_F_HOME ? 
++                        HA_BU_DEF_LIFETIME : CN_BU_DEF_LIFETIME); 
++
++      ifp_hoa = ipv6_get_ifaddr(home_addr, NULL);
++      if(!ifp_hoa) {
++              DEBUG(DBG_INFO, "home address missing");
++              return 0;
++      }
++      if (!(ifp_hoa->flags & IFA_F_PERMANENT)){
++              if (ifp_hoa->valid_lft)
++                      lifetime = min_t(__u32, lifetime, ifp_hoa->valid_lft);
++              else
++                      DEBUG(DBG_ERROR, "Zero lifetime for home address");
++      }
++      in6_ifa_put(ifp_hoa);
++
++      ifp_coa = ipv6_get_ifaddr(coa, NULL);
++      if (!ifp_coa) { 
++              DEBUG(DBG_INFO, "care-of address missing");
++              return 0;
++      }
++      if (!(ifp_coa->flags & IFA_F_PERMANENT)) {
++              if(ifp_coa->valid_lft)
++                      lifetime = min_t(__u32, lifetime, ifp_coa->valid_lft);
++              else
++                      DEBUG(DBG_ERROR, 
++                            "Zero lifetime for care-of address");
++      }
++      in6_ifa_put(ifp_coa);
++
++      DEBUG(DBG_INFO, "Lifetime for binding is %ld", lifetime);
++      return lifetime;
++}
++
++static int 
++mipv6_mn_tnl_rcv_send_bu_hook(struct ip6_tnl *t, struct sk_buff *skb)
++{
++      struct ipv6hdr *inner;
++      struct ipv6hdr *outer = skb->nh.ipv6h; 
++      struct mn_info *minfo = NULL;
++      __u32 lifetime;
++      __u8 user_flags = 0;
++
++      DEBUG_FUNC();
++
++      if (!is_mip6_tnl(t))
++              return IP6_TNL_ACCEPT;
++
++      if (!mip6node_cnf.accept_ret_rout) {
++              DEBUG(DBG_INFO, "Return routability administratively disabled" 
++                    " not doing route optimization");
++              return IP6_TNL_ACCEPT;
++      }
++      if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*inner)))
++              return IP6_TNL_DROP;
++
++      inner = (struct ipv6hdr *)skb->h.raw;
++      
++      read_lock(&mn_info_lock);
++      minfo = mipv6_mninfo_get_by_home(&inner->daddr);
++
++      if (!minfo) {
++              DEBUG(DBG_WARNING, "MN info missing");
++              read_unlock(&mn_info_lock);
++              return IP6_TNL_ACCEPT;
++      }
++      DEBUG(DBG_DATADUMP, "MIPV6 MN: Received a tunneled IPv6 packet"
++            " to %x:%x:%x:%x:%x:%x:%x:%x,"
++            " from %x:%x:%x:%x:%x:%x:%x:%x with\n tunnel header"
++            "daddr: %x:%x:%x:%x:%x:%x:%x:%x,"
++            "saddr: %x:%x:%x:%x:%x:%x:%x:%x", 
++             NIPV6ADDR(&inner->daddr), NIPV6ADDR(&inner->saddr),
++             NIPV6ADDR(&outer->daddr), NIPV6ADDR(&outer->saddr));
++      
++      spin_lock(&minfo->lock);
++
++      /* We don't send bus in response to all tunneled packets */
++
++        if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) {
++              spin_unlock(&minfo->lock);
++              read_unlock(&mn_info_lock);
++                DEBUG(DBG_ERROR, "HA BUG: Received a tunneled packet "
++                    "originally sent by home agent, not sending BU");
++              return IP6_TNL_ACCEPT;
++        }
++      spin_unlock(&minfo->lock);
++      read_unlock(&mn_info_lock);
++
++      DEBUG(DBG_DATADUMP, "Sending BU to correspondent node");
++
++      user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0;
++
++      if (inner->nexthdr != IPPROTO_DSTOPTS && 
++          inner->nexthdr != IPPROTO_MOBILITY) {
++              struct in6_addr coa;
++              /* Don't start RR when receiving ICMP error messages */
++              if (inner->nexthdr == IPPROTO_ICMPV6) {
++                      int ptr = (u8*)(inner+1) - skb->data;
++                      u8 type;
++
++                      if (skb_copy_bits(skb,
++                                        ptr+offsetof(struct icmp6hdr,
++                                                     icmp6_type),
++                                        &type, 1)
++                          || !(type & ICMPV6_INFOMSG_MASK)) {
++                              return IP6_TNL_ACCEPT;
++                      }
++              }
++              lifetime = mipv6_mn_get_bulifetime(&inner->daddr,
++                                                 &outer->daddr, 0); 
++              if (lifetime && 
++                  !mipv6_get_care_of_address(&inner->daddr, &coa)) {
++                      write_lock(&bul_lock);
++                      mipv6_send_bu(&inner->daddr, &inner->saddr, &coa,
++                                    INITIAL_BINDACK_TIMEOUT,
++                                    MAX_BINDACK_TIMEOUT, 1, 
++                                    user_flags,
++                                    lifetime, NULL);
++                      write_unlock(&bul_lock);
++              }
++      }
++      DEBUG(DBG_DATADUMP, "setting rcv_tunnel flag in skb");
++      skb->security |= MIPV6_RCV_TUNNEL;
++      return IP6_TNL_ACCEPT;
++}
++
++static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_send_bu_ops = {
++      {NULL, NULL}, 
++      IP6_TNL_PRE_DECAP,
++      IP6_TNL_PRI_FIRST,
++      mipv6_mn_tnl_rcv_send_bu_hook
++};
++
++static int
++mipv6_mn_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb)
++{
++      DEBUG_FUNC();
++      if (is_mip6_tnl(t))
++              MIPV6_INC_STATS(n_encapsulations);
++      return IP6_TNL_ACCEPT;
++}
++
++static struct ip6_tnl_hook_ops mipv6_mn_tnl_xmit_stats_ops = {
++      {NULL, NULL},
++      IP6_TNL_PRE_ENCAP,
++      IP6_TNL_PRI_LAST,
++      mipv6_mn_tnl_xmit_stats_hook
++};
++
++static int
++mipv6_mn_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb)
++{
++      DEBUG_FUNC();   
++      if (is_mip6_tnl(t))
++              MIPV6_INC_STATS(n_decapsulations);
++      return IP6_TNL_ACCEPT;
++}
++
++static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_stats_ops = {
++      {NULL, NULL},
++      IP6_TNL_PRE_DECAP,
++      IP6_TNL_PRI_LAST,
++      mipv6_mn_tnl_rcv_stats_hook
++};
++
++static void mn_check_tunneled_packet(struct sk_buff *skb)
++{
++      DEBUG_FUNC();
++      /* If tunnel flag was set */
++      if (skb->security & MIPV6_RCV_TUNNEL) {
++              struct in6_addr coa; 
++              __u32 lifetime;
++              __u8 user_flags = 0;
++              int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data;
++              int len = skb->len - ptr;
++              __u8 nexthdr = skb->nh.ipv6h->nexthdr;
++
++              if (len < 0)
++                      return;
++
++              ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len);
++              if (ptr < 0)
++                      return;
++
++              if (!mip6node_cnf.accept_ret_rout) {
++                      DEBUG(DBG_INFO, "Return routability administratively disabled");
++                      return;
++              }
++              if (nexthdr == IPPROTO_MOBILITY)
++                      return;
++            
++              /* Don't start RR when receiving ICMP error messages */
++              if (nexthdr == IPPROTO_ICMPV6) {
++                      u8 type;
++
++                      if (skb_copy_bits(skb,
++                                        ptr+offsetof(struct icmp6hdr,
++                                                     icmp6_type),
++                                        &type, 1)
++                          || !(type & ICMPV6_INFOMSG_MASK)) {
++                              return;
++                      }
++              }
++              user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0;
++              mipv6_get_care_of_address(&skb->nh.ipv6h->daddr, &coa);
++              lifetime = mipv6_mn_get_bulifetime(&skb->nh.ipv6h->daddr,
++                                                       &coa, 0); 
++
++              DEBUG(DBG_WARNING, "packet to address %x:%x:%x:%x:%x:%x:%x:%x"
++                    "was tunneled. Sending BU to CN" 
++                    "%x:%x:%x:%x:%x:%x:%x:%x", 
++                    NIPV6ADDR(&skb->nh.ipv6h->daddr),
++                    NIPV6ADDR(&skb->nh.ipv6h->saddr)); 
++              /* This should work also with home address option */
++              
++              write_lock(&bul_lock);
++              mipv6_send_bu(&skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr, 
++                            &coa, INITIAL_BINDACK_TIMEOUT,
++                            MAX_BINDACK_TIMEOUT, 1, user_flags,
++                            lifetime, NULL);
++              write_unlock(&bul_lock);
++      }
++}
++
++static int sched_mv_home_addr_task(struct in6_addr *haddr, int plen_new, 
++                                 int newif, int oldif, struct handoff *ho)
++{
++      int alloc_size;
++      struct ifr_holder *ifrh;
++
++      alloc_size = sizeof(*ifrh) + (ho ? sizeof(*ho): 0);
++      if ((ifrh = kmalloc(alloc_size, GFP_ATOMIC)) == NULL) {
++              DEBUG(DBG_ERROR, "Out of memory");
++              return -1;
++      } 
++      if (ho) {
++              ifrh->ho = (struct handoff *)((struct ifr_holder *)(ifrh + 1));
++              memcpy(ifrh->ho, ho, sizeof(*ho));
++      } else 
++              ifrh->ho = NULL;
++
++      /* must queue task to avoid deadlock with rtnl */
++      ifrh->ifr.ifr6_ifindex = newif;
++      ifrh->ifr.ifr6_prefixlen = plen_new;
++      ipv6_addr_copy(&ifrh->ifr.ifr6_addr, haddr);
++      ifrh->old_ifi = oldif;
++      
++      spin_lock_bh(&ifrh_lock);
++      list_add_tail(&ifrh->list, &ifrh_list);
++      spin_unlock_bh(&ifrh_lock);
++
++      schedule_task(&mv_home_addr_task);
++
++      return 0;
++}
++
++static void send_ret_home_ns(struct in6_addr *ha_addr, 
++                           struct in6_addr *home_addr,
++                           int ifindex)
++{
++      struct in6_addr nil;
++      struct in6_addr mcaddr;
++      struct net_device *dev = dev_get_by_index(ifindex);
++      if (!dev)
++              return;
++      memset(&nil, 0, sizeof(nil));
++      addrconf_addr_solict_mult(home_addr, &mcaddr);
++      ndisc_send_ns(dev, NULL, home_addr, &mcaddr, &nil); 
++      dev_put(dev);
++}
++
++static inline int ha_is_reachable(int ifindex, struct in6_addr *ha)
++{
++      struct net_device *dev;
++      int reachable = 0;
++
++      dev = dev_get_by_index(ifindex);
++      if (dev) {
++              struct neighbour *neigh;
++              if ((neigh = ndisc_get_neigh(dev, ha)) != NULL) {
++                      read_lock_bh(&neigh->lock);
++                      if (neigh->nud_state&NUD_VALID)
++                              reachable = 1;
++                      read_unlock_bh(&neigh->lock);
++                      neigh_release(neigh);
++              }
++              dev_put(dev);
++      }
++      return reachable;
++}
++
++static int mn_ha_handoff(struct handoff *ho)
++{
++      struct list_head *lh;
++      struct mn_info *minfo;
++      struct in6_addr *coa= ho->coa;
++      int wait_mv_home = 0; 
++
++      read_lock_bh(&mn_info_lock);
++      list_for_each(lh, &mn_info_list) {
++              __u8 has_home_reg;
++              int ifindex;
++              struct in6_addr ha;
++              __u8 athome;
++              __u32 lifetime;
++              struct mipv6_bul_entry *entry = NULL;
++              
++              minfo = list_entry(lh, struct mn_info, list);
++              spin_lock(&minfo->lock);
++              has_home_reg = minfo->has_home_reg;
++              ifindex = minfo->ifindex;
++              ipv6_addr_copy(&ha, &minfo->ha);
++              
++              if (mipv6_prefix_compare(&ho->rtr_addr, &minfo->home_addr,
++                                       ho->plen)) {
++                      if (minfo->has_home_reg)
++                              athome = minfo->is_at_home = MN_RETURNING_HOME;
++                      else
++                              athome = minfo->is_at_home = MN_AT_HOME;
++                      coa = &minfo->home_addr;
++
++                      spin_unlock(&minfo->lock);
++#if 0                 
++                      /* Cancel prefix solicitation, rtr is our HA */
++                      mipv6_pfx_cancel_send(&ho->rtr_addr, ifindex);
++#endif                        
++                      minfo->ifindex = ho->ifindex;
++
++                      if (minfo->has_home_reg && 
++                          !ha_is_reachable(ho->ifindex, &minfo->ha)) {
++                              send_ret_home_ns(&minfo->ha,
++                                               &minfo->home_addr, 
++                                               ho->ifindex);
++                              mipv6_mdet_set_curr_rtr_reachable(0);
++                              wait_mv_home++;
++                      }
++                      if (ifindex != ho->ifindex){
++                              wait_mv_home++;
++                              DEBUG(DBG_INFO, 
++                                    "Moving home address back to "
++                                    "the home interface");
++                              sched_mv_home_addr_task(&minfo->home_addr, 
++                                                      128,
++                                                      ho->ifindex, 
++                                                      ifindex, ho);
++                      }
++                      if (!has_home_reg || wait_mv_home)
++                              continue;
++                      
++                      lifetime = 0;
++
++              } else {
++                      athome = minfo->is_at_home = MN_NOT_AT_HOME;
++                      if (minfo->ifindex_user != minfo->ifindex) {
++                              DEBUG(DBG_INFO, "Scheduling home address move to virtual interface");
++                              sched_mv_home_addr_task(&minfo->home_addr, 
++                                                      128,
++                                                      minfo->ifindex_user, 
++                                                      minfo->ifindex, ho); /* Is minfo->ifindex correct */
++                              
++                              wait_mv_home++;
++                      }
++                      minfo->ifindex = minfo->ifindex_user;
++                      spin_unlock(&minfo->lock);
++                      if (wait_mv_home)
++                              continue;
++                      if (!has_home_reg &&
++                          init_home_registration(&minfo->home_addr, 
++                                                 ho->coa)) {
++                              continue;
++                      }
++                      lifetime = mipv6_mn_get_bulifetime(&minfo->home_addr, 
++                                                         ho->coa,
++                                                         MIPV6_BU_F_HOME);
++                      
++              }
++              write_lock(&bul_lock);
++              if (!(entry = mipv6_bul_get(&ha, &minfo->home_addr)) ||
++                  !(entry->flags & MIPV6_BU_F_HOME)) {
++                      DEBUG(DBG_ERROR, 
++                            "Unable to find home registration for "
++                            "home address: %x:%x:%x:%x:%x:%x:%x:%x!\n",
++                            NIPV6ADDR(&minfo->home_addr));
++                      write_unlock(&bul_lock);
++                      continue;
++              }
++              DEBUG(DBG_INFO, "Sending home de ? %d registration for "
++                    "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" 
++                    "to home agent %x:%x:%x:%x:%x:%x:%x:%x, "
++                    "with lifetime %ld", 
++                    (athome != MN_NOT_AT_HOME),  
++                    NIPV6ADDR(&entry->home_addr), 
++                    NIPV6ADDR(&entry->cn_addr), lifetime);
++              mipv6_send_bu(&entry->home_addr, &entry->cn_addr, 
++                            coa, INITIAL_BINDACK_TIMEOUT, 
++                            MAX_BINDACK_TIMEOUT, 1, entry->flags, 
++                            lifetime, NULL);
++              write_unlock(&bul_lock);
++
++      }
++      read_unlock_bh(&mn_info_lock);
++      return wait_mv_home;
++}
++/**
++ * mn_cn_handoff - called for every bul entry to send BU to CN
++ * @rawentry: bul entry
++ * @args: handoff event
++ * @sortkey:
++ *
++ * Since MN can have many home addresses and home networks, every BUL
++ * entry needs to be checked
++ **/
++int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey)
++{
++      struct mipv6_bul_entry *entry = (struct mipv6_bul_entry *)rawentry;
++      struct in6_addr *coa = (struct in6_addr *)args;
++
++      DEBUG_FUNC();
++
++      /* Home registrations already handled by mn_ha_handoff */
++      if (entry->flags & MIPV6_BU_F_HOME)
++              return ITERATOR_CONT;
++
++      /* BUL is locked by mipv6_mobile_node_moved which calls us 
++         through mipv6_bul_iterate */
++
++      if (mipv6_prefix_compare(coa, 
++                               &entry->home_addr,
++                               64)) {
++              mipv6_send_bu(&entry->home_addr, &entry->cn_addr, 
++                            &entry->home_addr, INITIAL_BINDACK_TIMEOUT, 
++                            MAX_BINDACK_TIMEOUT, 1, entry->flags, 0, 
++                            NULL);
++      } else {
++              u32 lifetime = mipv6_mn_get_bulifetime(&entry->home_addr, 
++                                                     coa,
++                                                     entry->flags);
++              mipv6_send_bu(&entry->home_addr, &entry->cn_addr,
++                              coa, INITIAL_BINDACK_TIMEOUT,
++                            MAX_BINDACK_TIMEOUT, 1, entry->flags,
++                            lifetime, NULL);
++      }
++      return ITERATOR_CONT;
++}
++
++
++int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey)
++{
++      struct mipv6_bul_entry *bul = (struct mipv6_bul_entry *)rawentry;
++      struct bul_inval_args *arg = (struct bul_inval_args *)args;
++
++      DEBUG_FUNC();
++
++      if (!ipv6_addr_cmp(arg->cn, &bul->cn_addr) &&
++          (!ipv6_addr_cmp(arg->mn, &bul->home_addr) ||
++           !ipv6_addr_cmp(arg->mn, &bul->coa))) {
++              if (arg->all_rr_states || !bul->rr ||
++                  (bul->rr->rr_state != RR_INIT && 
++                   bul->rr->rr_state != RR_DONE)) {
++                      bul->state = ACK_ERROR;
++                      bul->callback = bul_entry_expired;
++                      bul->callback_time = jiffies +
++                              DUMB_CN_BU_LIFETIME * HZ;
++                      bul->expire = bul->callback_time;
++                      DEBUG(DBG_INFO, "BUL entry set to ACK_ERROR");
++                      mipv6_bul_reschedule(bul);
++              }
++      }
++      return ITERATOR_CONT;
++}
++/**
++ * init_home_registration - start Home Registration process
++ * @home_addr: home address
++ * @coa: care-of address
++ *
++ * Checks whether we have a Home Agent address for this home address.
++ * If not starts Dynamic Home Agent Address Discovery.  Otherwise
++ * tries to register with home agent if not already registered.
++ * Returns 1, if home registration process is started and 0 otherwise
++ **/
++int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa)
++{
++      struct mn_info *hinfo;
++      struct in6_addr ha;
++      __u8 man_conf;
++      int ifindex;
++      __u32 lifetime;
++      __u8 user_flags = 0, flags;
++
++      DEBUG_FUNC();
++
++      read_lock_bh(&mn_info_lock);
++        if ((hinfo = mipv6_mninfo_get_by_home(home_addr)) == NULL) {
++                DEBUG(DBG_ERROR, "No mn_info found for address: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x",
++                    NIPV6ADDR(home_addr));
++              read_unlock_bh(&mn_info_lock);
++                return -ENOENT;
++        }
++      spin_lock(&hinfo->lock);
++      if (mipv6_prefix_compare(&hinfo->home_addr, coa, hinfo->home_plen)) { 
++              spin_unlock(&hinfo->lock);
++              read_unlock_bh(&mn_info_lock);
++              DEBUG(DBG_INFO, "Adding home address, MN at home");
++              return 1;
++      }
++        if (ipv6_addr_any(&hinfo->ha)) {
++                int dhaad_id = mipv6_get_dhaad_id();
++                hinfo->dhaad_id = dhaad_id;
++              spin_unlock(&hinfo->lock);
++                mipv6_icmpv6_send_dhaad_req(home_addr, hinfo->home_plen, dhaad_id);
++              read_unlock_bh(&mn_info_lock);
++                DEBUG(DBG_INFO,
++                    "Home Agent address not set, initiating DHAAD");
++                return 1;
++        }
++        ipv6_addr_copy(&ha, &hinfo->ha);
++        man_conf = hinfo->man_conf;
++        ifindex = hinfo->ifindex;
++      spin_unlock(&hinfo->lock);
++      read_unlock_bh(&mn_info_lock);
++#if 0 
++      if (man_conf)
++              mipv6_pfx_add_ha(&ha, coa, ifindex);
++#endif        
++      if (mipv6_bul_exists(&ha, home_addr)) {
++              DEBUG(DBG_INFO, "BU already sent to HA");
++              return 0;
++      }
++      /* user flags received through sysctl */
++      user_flags |= mip6node_cnf.bu_lladdr ? MIPV6_BU_F_LLADDR : 0;
++      user_flags |= mip6node_cnf.bu_keymgm ? MIPV6_BU_F_KEYMGM : 0;
++
++      flags = MIPV6_BU_F_HOME | MIPV6_BU_F_ACK | user_flags;
++
++      lifetime = mipv6_mn_get_bulifetime(home_addr, coa, flags);
++
++      DEBUG(DBG_INFO, "Sending initial home registration for "
++            "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" 
++            "to home agent %x:%x:%x:%x:%x:%x:%x:%x, "
++            "with lifetime %ld, prefixlength %d",   
++            NIPV6ADDR(home_addr), NIPV6ADDR(&ha), lifetime, 0);
++
++      write_lock_bh(&bul_lock);
++      mipv6_send_bu(home_addr, &ha, coa, INITIAL_BINDACK_DAD_TIMEOUT,
++                    MAX_BINDACK_TIMEOUT, 1, flags, lifetime, NULL);
++      write_unlock_bh(&bul_lock);
++
++      return 1;
++}
++
++/**
++ * mipv6_mobile_node_moved - Send BUs to all HAs and CNs
++ * @ho: handoff structure contains the new and previous routers
++ *
++ * Event for handoff.  Sends BUs everyone on Binding Update List.
++ **/
++int mipv6_mobile_node_moved(struct handoff *ho)
++{
++#if 0
++      int bu_to_prev_router = 1;
++#endif
++      int dummy;
++
++      DEBUG_FUNC();
++
++      ma_ctl_upd_iface(ho->ifindex, 
++                       MA_IFACE_CURRENT | MA_IFACE_HAS_ROUTER, &dummy);
++
++      /* First send BU to HA, then to all other nodes that are on BU list */
++      if (mn_ha_handoff(ho) != 0)
++              return 0; /* Wait for move home address task */
++#if 0  
++      /* Add current care-of address to mn_info list, if current router acts 
++         as a HA.*/ 
++
++      if (ho->home_address && bu_to_prev_router) 
++              mipv6_mninfo_add(ho->coa, ho->plen, 
++                               MN_AT_HOME, 0, &ho->rtr_addr, 
++                               ho->plen, ROUTER_BU_DEF_LIFETIME,
++                               0);
++                                
++#endif
++      return 0;               
++}
++
++/**
++ * mipv6_mn_send_home_na - send NA when returning home
++ * @haddr: home address to advertise
++ *
++ * After returning home, MN must advertise all its valid addresses in
++ * home link to all nodes.
++ **/
++void mipv6_mn_send_home_na(struct in6_addr *haddr)
++{
++      struct net_device *dev = NULL;
++      struct in6_addr mc_allnodes;
++      struct mn_info *hinfo = NULL;
++ 
++      read_lock(&mn_info_lock);
++      hinfo = mipv6_mninfo_get_by_home(haddr);
++      if (!hinfo) {
++              read_unlock(&mn_info_lock);
++              return;
++      }
++      spin_lock(&hinfo->lock);
++      hinfo->is_at_home = MN_AT_HOME;
++      dev = dev_get_by_index(hinfo->ifindex);
++      spin_unlock(&hinfo->lock);
++      read_unlock(&mn_info_lock);
++      if (dev == NULL) {
++              DEBUG(DBG_ERROR, "Send home_na: device not found.");
++              return;
++      }
++      
++      ipv6_addr_all_nodes(&mc_allnodes);
++      ndisc_send_na(dev, NULL, &mc_allnodes, haddr, 0, 0, 1, 1);
++      dev_put(dev);
++}
++
++static int mn_use_hao(struct in6_addr *daddr, struct in6_addr *saddr)
++{
++      struct mipv6_bul_entry *entry;
++      struct mn_info *minfo = NULL;
++      int add_ha = 0;
++
++      read_lock_bh(&mn_info_lock);
++      minfo = mipv6_mninfo_get_by_home(saddr);
++      if (minfo && minfo->is_at_home != MN_AT_HOME) {
++              read_lock_bh(&bul_lock);
++              if ((entry = mipv6_bul_get(daddr, saddr)) == NULL) {
++                      read_unlock_bh(&bul_lock);
++                      read_unlock_bh(&mn_info_lock);
++                      return add_ha;
++              }
++              add_ha = (entry->state != ACK_ERROR &&
++                        (!entry->rr || entry->rr->rr_state == RR_DONE || 
++                         entry->flags & MIPV6_BU_F_HOME));
++              read_unlock_bh(&bul_lock);
++      }
++      read_unlock_bh(&mn_info_lock);
++      return add_ha;
++}
++
++static int 
++mn_dev_event(struct notifier_block *nb, unsigned long event, void *ptr)
++{
++      struct net_device *dev = ptr;
++      struct list_head *lh;
++      struct mn_info *minfo;
++      int newif = 0;
++
++      /* here are probably the events we need to worry about */
++      switch (event) {
++      case NETDEV_UP:
++              DEBUG(DBG_DATADUMP, "New netdevice %s registered.", dev->name);
++              if (dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) 
++                      ma_ctl_add_iface(dev->ifindex);
++                      
++              break;
++      case NETDEV_GOING_DOWN:
++              DEBUG(DBG_DATADUMP, "Netdevice %s disappeared.", dev->name);
++              /* 
++               * Go through mn_info list and move all home addresses on the
++               * netdev going down to a new device. This will make it 
++                 * practically impossible for the home address to return home,
++               * but allow MN to retain its connections using the address.
++               */
++
++              read_lock_bh(&mn_info_lock);
++              list_for_each(lh, &mn_info_list) {
++                      minfo = list_entry(lh, struct mn_info, list);
++                      spin_lock(&minfo->lock);
++                      if (minfo->ifindex == dev->ifindex) {
++                              if (sched_mv_home_addr_task(&minfo->home_addr, 128, 
++                                                          minfo->ifindex_user, 
++                                                          0, NULL) < 0) {
++                                      minfo->ifindex = 0;
++                                      spin_unlock(&minfo->lock);
++                                      read_unlock_bh(&mn_info_lock);
++                                      return NOTIFY_DONE;
++                              } else { 
++                                      minfo->ifindex = minfo->ifindex_user;
++                                      if (minfo->is_at_home) {
++                                              minfo->is_at_home = 0;
++
++                                      }
++                                      newif = minfo->ifindex_user;
++                              }
++                      }
++                      spin_unlock(&minfo->lock);                              
++              }
++              
++              read_unlock_bh(&mn_info_lock);
++      }
++      ma_ctl_upd_iface(dev->ifindex, MA_IFACE_NOT_PRESENT, &newif);
++      mipv6_mdet_del_if(dev->ifindex);
++
++      return NOTIFY_DONE;
++}
++
++struct notifier_block mipv6_mn_dev_notifier = {
++      mn_dev_event,
++      NULL,
++      0 /* check if using zero is ok */
++};
++
++static void deprecate_addr(struct mn_info *minfo)
++{
++      /*
++       * Lookup address from IPv6 address list and set deprecated flag
++       */
++      
++}
++
++/*
++ * Required because we can only modify addresses after the packet is
++ * constructed.  We otherwise mess with higher level protocol
++ * pseudoheaders. With strict protocol layering life would be SO much
++ * easier!  
++ */
++static unsigned int modify_xmit_addrs(unsigned int hooknum,
++                                    struct sk_buff **pskb,
++                                    const struct net_device *in,
++                                    const struct net_device *out,
++                                    int (*okfn) (struct sk_buff *))
++{
++      struct sk_buff *skb = *pskb;
++
++      DEBUG_FUNC();
++      
++      if (skb) {
++              struct ipv6hdr *hdr = skb->nh.ipv6h;
++              struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
++              struct mipv6_bul_entry *bule;
++              struct in6_addr *daddr;
++
++              if (!ipv6_addr_any(&opt->hoa))
++                      daddr = &opt->hoa;
++              else
++                      daddr = &hdr->daddr;
++
++              /* We don't consult bul when sending a BU to avoid deadlock, since
++               * BUL is already locked. 
++               */
++              
++
++              if (opt->mipv6_flags & MIPV6_SND_HAO && 
++                  !(opt->mipv6_flags & MIPV6_SND_BU)) {
++                      write_lock(&bul_lock);
++                      bule = mipv6_bul_get(daddr, &hdr->saddr);
++                      if (!bule) {
++                              write_unlock(&bul_lock);
++                              return NF_ACCEPT;
++                      }
++                      if (!bule->rr || bule->rr->rr_state == RR_DONE || 
++                          bule->flags & MIPV6_BU_F_HOME) {
++                              DEBUG(DBG_DATADUMP, 
++                                    "Replace source address with CoA and reroute");
++                              ipv6_addr_copy(&hdr->saddr, &bule->coa);
++                              skb->nfcache |= NFC_ALTERED;
++                      }
++                      write_unlock(&bul_lock);
++              } else if (opt->mipv6_flags & MIPV6_SND_HAO) {
++                      mipv6_get_care_of_address(&hdr->saddr, &hdr->saddr);
++                      skb->nfcache |= NFC_ALTERED;
++              }
++      }
++      return NF_ACCEPT;
++}
++
++/* We set a netfilter hook so that we can modify outgoing packet's
++ * source addresses 
++ */
++struct nf_hook_ops addr_modify_hook_ops = {
++      {NULL, NULL},           /* List head, no predecessor, no successor */
++      modify_xmit_addrs,
++      PF_INET6,
++      NF_IP6_LOCAL_OUT,
++      NF_IP6_PRI_FIRST        /* Should be of EXTREMELY high priority since we
++                               * do not want to mess with IPSec (possibly
++                               * implemented as packet filter)
++                               */
++};
++
++#define MN_INFO_LEN 77
++
++static int mn_proc_info(char *buffer, char **start, off_t offset,
++                      int length)
++{
++      struct list_head *p;
++      struct mn_info *minfo;
++      int len = 0, skip = 0;
++
++      DEBUG_FUNC();
++
++      read_lock_bh(&mn_info_lock);
++      list_for_each(p, &mn_info_list) {
++              if (len < offset / MN_INFO_LEN) {
++                      skip++;
++                      continue;
++              }
++              if (len >= length)
++                      break;
++              minfo = list_entry(p, struct mn_info, list);
++              spin_lock(&minfo->lock);
++              len += sprintf(buffer + len, "%02d %08x%08x%08x%08x %02x "
++                             "%08x%08x%08x%08x %d %d\n",
++                             minfo->ifindex,
++                             ntohl(minfo->home_addr.s6_addr32[0]),
++                             ntohl(minfo->home_addr.s6_addr32[1]),
++                             ntohl(minfo->home_addr.s6_addr32[2]),
++                             ntohl(minfo->home_addr.s6_addr32[3]),
++                             minfo->home_plen,
++                             ntohl(minfo->ha.s6_addr32[0]),
++                             ntohl(minfo->ha.s6_addr32[1]),
++                             ntohl(minfo->ha.s6_addr32[2]),
++                             ntohl(minfo->ha.s6_addr32[3]),
++                             minfo->is_at_home, minfo->has_home_reg);
++              spin_unlock(&minfo->lock);
++      }
++      read_unlock_bh(&mn_info_lock);
++
++      *start = buffer;
++      if (offset)
++              *start += offset % MN_INFO_LEN;
++
++      len -= offset % MN_INFO_LEN;
++
++      if (len > length)
++              len = length;
++      if (len < 0)
++              len = 0;
++      
++      return len;
++}
++
++int mipv6_mn_ha_nd_update(struct net_device *dev,
++                        struct in6_addr *ha, u8 *lladdr)
++{
++      int valid = 0;
++      struct neighbour *neigh;
++      if ((neigh = ndisc_get_neigh(dev, ha))) {
++              read_lock(&neigh->lock);
++              valid = neigh->nud_state & NUD_VALID;
++              read_unlock(&neigh->lock);
++              if (!valid && lladdr)
++                      neigh_update(neigh, lladdr, NUD_REACHABLE, 0, 1);
++              neigh_release(neigh);
++      }
++      return valid;
++}
++
++int mipv6_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr)
++{
++      struct mn_info *minfo;
++
++      if (!(minfo = mipv6_mninfo_get_by_home(&ifp->addr)) ||
++          ipv6_addr_any(&minfo->ha))
++              return 0;
++
++      if (mipv6_mn_ha_nd_update(ifp->idev->dev, &minfo->ha, lladdr))
++              mipv6_mdet_retrigger_ho();
++      return 1;
++}
++
++int __init mipv6_mn_init(void)
++{
++      struct net_device *dev;
++
++      DEBUG_FUNC();
++
++      if (mipv6_add_tnl_to_ha())
++              return -ENODEV;
++
++      mipv6_bul_init(MIPV6_BUL_SIZE);
++      mip6_fn.mn_use_hao = mn_use_hao;
++      mip6_fn.mn_check_tunneled_packet = mn_check_tunneled_packet;
++      INIT_TQUEUE(&mv_home_addr_task, mv_home_addr, NULL);
++
++      ma_ctl_init();
++      for (dev = dev_base; dev; dev = dev->next) {
++              if (dev->flags & IFF_UP && 
++                  dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) {
++                      ma_ctl_add_iface(dev->ifindex);
++              }
++      } 
++      DEBUG(DBG_INFO, "Multiaccess support initialized");
++
++      register_netdevice_notifier(&mipv6_mn_dev_notifier);
++      register_inet6addr_notifier(&mipv6_mn_inet6addr_notifier);
++
++      ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_send_bu_ops);
++      ip6ip6_tnl_register_hook(&mipv6_mn_tnl_xmit_stats_ops);
++      ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_stats_ops);
++
++      MIPV6_SETCALL(mipv6_set_home, mipv6_mn_set_home);
++
++      mipv6_initialize_mdetect();
++
++      /* COA to home transformation hook */
++      MIPV6_SETCALL(mipv6_get_home_address, mipv6_get_saddr_hook);
++      MIPV6_SETCALL(mipv6_mn_ha_probe, mipv6_mn_ha_probe);
++      MIPV6_SETCALL(mipv6_is_home_addr, mipv6_mn_is_home_addr);
++      proc_net_create("mip6_mninfo", 0, mn_proc_info);
++      /* Set packet modification hook (source addresses) */
++      nf_register_hook(&addr_modify_hook_ops);
++
++      return 0;
++}
++
++void __exit mipv6_mn_exit(void)
++{
++      struct list_head *lh, *tmp;
++      struct mn_info *minfo;
++      DEBUG_FUNC();
++      
++      mip6_fn.mn_use_hao = NULL;
++      mip6_fn.mn_check_tunneled_packet = NULL;
++      
++      MIPV6_RESETCALL(mipv6_set_home);
++      MIPV6_RESETCALL(mipv6_get_home_address);
++      MIPV6_RESETCALL(mipv6_mn_ha_probe);
++      MIPV6_RESETCALL(mipv6_is_home_addr);
++      nf_unregister_hook(&addr_modify_hook_ops);
++      proc_net_remove("mip6_mninfo");
++      mipv6_shutdown_mdetect();
++      ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_stats_ops);
++      ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_xmit_stats_ops);
++      ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_send_bu_ops);
++      ma_ctl_clean();
++
++      unregister_inet6addr_notifier(&mipv6_mn_inet6addr_notifier);
++      unregister_netdevice_notifier(&mipv6_mn_dev_notifier);
++      write_lock_bh(&mn_info_lock);
++
++      list_for_each_safe(lh, tmp, &mn_info_list) {
++              minfo = list_entry(lh, struct mn_info, list);
++              if (minfo->is_at_home == MN_NOT_AT_HOME) 
++                      deprecate_addr(minfo);
++              list_del(&minfo->list);
++              kfree(minfo);
++      }
++      write_unlock_bh(&mn_info_lock);
++      mipv6_bul_exit();
++      flush_scheduled_tasks();
++      mipv6_del_tnl_to_ha();
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mn.h
+@@ -0,0 +1,96 @@
++/*
++ *      MIPL Mobile IPv6 Mobile Node header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _MN_H
++#define _MN_H
++
++#include <linux/in6.h>
++
++/* constants for sending of BUs*/
++#define HA_BU_DEF_LIFETIME 10000
++#define CN_BU_DEF_LIFETIME 420 /* Max lifetime for RR bindings from RFC 3775 */
++#define DUMB_CN_BU_LIFETIME 600 /* BUL entry lifetime in case of dumb CN */
++#define ROUTER_BU_DEF_LIFETIME 30 /* For packet forwarding from previous coa */
++#define ERROR_DEF_LIFETIME DUMB_CN_BU_LIFETIME
++
++extern rwlock_t mn_info_lock;
++
++#define MN_NOT_AT_HOME 0
++#define MN_RETURNING_HOME 1
++#define MN_AT_HOME 2
++
++/*
++ * Mobile Node information record
++ */
++struct mn_info {
++      struct in6_addr home_addr;
++      struct in6_addr ha;
++      __u8 home_plen;
++      __u8 is_at_home;
++      __u8 has_home_reg;
++      __u8 man_conf;
++      int ifindex;
++      int ifindex_user; 
++      unsigned long home_addr_expires;
++      unsigned short dhaad_id;
++      struct list_head list;
++      spinlock_t lock;
++};
++
++/* prototypes for interface functions */
++int mipv6_mn_init(void);
++void mipv6_mn_exit(void);
++
++struct handoff;
++
++/* Interface to movement detection */
++int mipv6_mobile_node_moved(struct handoff *ho);
++
++void mipv6_mn_send_home_na(struct in6_addr *haddr);
++/* Init home reg. with coa */
++int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa);
++
++/* mn_info functions that require locking by caller */
++struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr);
++
++struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent);
++
++struct mn_info *mipv6_mninfo_get_by_id(unsigned short id);
++
++/* "safe" mn_info functions */
++void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, 
++                    int isathome, unsigned long lifetime, struct in6_addr *ha, 
++                    int ha_plen, unsigned long ha_lifetime, int man_conf);
++
++int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only);
++
++void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg);
++
++int mipv6_mn_is_at_home(struct in6_addr *addr);
++
++int mipv6_mn_is_home_addr(struct in6_addr *addr);
++
++__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, 
++                            struct in6_addr *coa, __u8 flags);
++int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey);
++
++int mipv6_mn_ha_nd_update(struct net_device *dev,
++                        struct in6_addr *ha, u8 *lladdr);
++
++struct bul_inval_args {
++      int all_rr_states;
++      struct in6_addr *cn;
++      struct in6_addr *mn;
++};
++
++int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey);
++
++#endif /* _MN_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr.h
+@@ -0,0 +1,101 @@
++/*
++ *      MIPL Mobile IPv6 Mobility Header send and receive
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _MOBHDR_H
++#define _MOBHDR_H
++
++#include <net/mipv6.h>
++
++/* RR states for mipv6_send_bu() */
++#define RR_INIT                       0x00
++#define RR_WAITH              0x01
++#define RR_WAITC              0x02
++#define RR_WAITHC             0x13
++#define RR_DONE                       0x10
++
++#define MH_UNKNOWN_CN 1
++#define MH_AUTH_FAILED 2
++#define MH_SEQUENCE_MISMATCH 3
++
++struct mipv6_bul_entry;
++struct sk_buff;
++
++int mipv6_mh_common_init(void);
++void mipv6_mh_common_exit(void);
++int mipv6_mh_mn_init(void);
++void mipv6_mh_mn_exit(void);
++
++struct mipv6_mh_opt {
++      struct mipv6_mo_alt_coa         *alt_coa;
++      struct mipv6_mo_nonce_indices   *nonce_indices;
++      struct mipv6_mo_bauth_data      *auth_data;
++      struct mipv6_mo_br_advice       *br_advice;
++      int freelen;
++      int totlen;
++      u8 *next_free;
++      u8 data[0];
++};
++
++struct mobopt {
++      struct mipv6_mo_alt_coa         *alt_coa;
++      struct mipv6_mo_nonce_indices   *nonce_indices;
++      struct mipv6_mo_bauth_data      *auth_data;
++      struct mipv6_mo_br_advice       *br_advice;
++};
++
++struct mipv6_mh_opt *alloc_mh_opts(int totlen);
++int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data);
++int parse_mo_tlv(void *mos, int len, struct mobopt *opts);
++int mipv6_add_pad(u8 *data, int n);
++
++struct mipv6_auth_parm {
++      struct in6_addr *coa;
++      struct in6_addr *cn_addr;
++      __u8 *k_bu;
++};
++
++int send_mh(struct in6_addr *daddr, struct in6_addr *saddr, 
++          u8 msg_type, u8 msg_len, u8 *msg,
++          struct in6_addr *hao_addr, struct in6_addr *rth_addr,
++          struct mipv6_mh_opt *ops, struct mipv6_auth_parm *parm);
++
++int mipv6_mh_register(int type, int (*func)(struct sk_buff *,
++      struct in6_addr *, struct in6_addr *, 
++      struct in6_addr *, struct in6_addr *, struct mipv6_mh *));
++
++void mipv6_mh_unregister(int type);
++
++int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr,
++                 struct mipv6_mh_opt *ops);
++
++int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, 
++                struct in6_addr *coa, __u32 initdelay, 
++                __u32 maxackdelay, __u8 exp, __u8 flags,
++                __u32 lifetime, struct mipv6_mh_opt *ops);
++
++int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, 
++                struct in6_addr *home, __u8 status);
++
++int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, 
++                struct in6_addr *auth_coa, struct in6_addr *rep_coa,
++                u8 status, u16 sequence, u32 lifetime, u8 *k_bu);
++
++/* Binding Authentication Data Option routines */
++#define MAX_HASH_LENGTH 20
++#define MIPV6_RR_MAC_LENGTH 12
++
++int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, 
++                   __u8 *opt, __u8 *aud_data, __u8 *k_bu);
++
++int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, 
++                   __u8 *opt, __u8 optlen, struct mipv6_mo_bauth_data *aud, 
++                   __u8 *k_bu);
++#endif /* _MOBHDR_H */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_common.c
+@@ -0,0 +1,1210 @@
++/*
++ *    Mobile IPv6 Mobility Header Common Functions
++ *
++ *    Authors:
++ *    Antti Tuominen <ajtuomin@tml.hut.fi>
++ *
++ *      $Id: s.mh_recv.c 1.159 02/10/16 15:01:29+03:00 antti@traci.mipl.mediapoli.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 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/autoconf.h>
++#include <linux/types.h>
++#include <linux/in6.h>
++#include <linux/skbuff.h>
++#include <linux/ipsec.h>
++#include <linux/init.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/addrconf.h>
++#include <net/mipv6.h>
++#include <net/checksum.h>
++#include <net/protocol.h>
++
++#include "stats.h"
++#include "debug.h"
++#include "mobhdr.h"
++#include "bcache.h"
++
++#include "rr_crypto.h"
++#include "exthdrs.h"
++#include "config.h"
++
++#define MIPV6_MH_MAX MIPV6_MH_BE
++struct mh_proto {
++      int     (*func) (struct sk_buff *,
++                       struct in6_addr *, struct in6_addr *, 
++                       struct in6_addr *, struct in6_addr *, 
++                       struct mipv6_mh *);
++};
++
++static struct mh_proto mh_rcv[MIPV6_MH_MAX];
++
++int mipv6_mh_register(int type, int (*func)(struct sk_buff *,
++      struct in6_addr *, struct in6_addr *, 
++      struct in6_addr *, struct in6_addr *, struct mipv6_mh *))
++{
++      if (mh_rcv[type].func != NULL)
++              return -1;
++
++      mh_rcv[type].func = func;
++
++      return 0;
++}
++
++void mipv6_mh_unregister(int type)
++{
++      if (type < 0 || type > MIPV6_MH_MAX)
++              return;
++
++      mh_rcv[type].func = NULL;
++}
++
++struct socket *mipv6_mh_socket = NULL;
++
++/* TODO: Fix fragmentation */
++static int dstopts_getfrag(
++      const void *data, struct in6_addr *addr,
++      char *buff, unsigned int offset, unsigned int len)
++{
++      memcpy(buff, data + offset, len);
++      return 0;
++}
++
++struct mipv6_mh_opt *alloc_mh_opts(int totlen)
++{
++      struct mipv6_mh_opt *ops;
++
++      ops = kmalloc(sizeof(*ops) + totlen, GFP_ATOMIC);
++      if (ops == NULL)
++              return NULL;
++
++      memset(ops, 0, sizeof(*ops));
++      ops->next_free = ops->data;
++      ops->freelen = totlen;
++
++      return ops;
++}
++
++int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data)
++{
++      struct mipv6_mo *mo;
++
++      if (ops->next_free == NULL) {
++              DEBUG(DBG_ERROR, "No free room for option");
++              return -ENOMEM;
++      }
++      if (ops->freelen < len + 2) {
++              DEBUG(DBG_ERROR, "No free room for option");
++              return -ENOMEM;
++      }
++      else {
++              ops->freelen -= (len + 2);
++              ops->totlen += (len + 2);
++      }
++
++      mo = (struct mipv6_mo *)ops->next_free;
++      mo->type = type;
++      mo->length = len;
++
++      switch (type) {
++      case MIPV6_OPT_ALTERNATE_COA:
++              ops->alt_coa = (struct mipv6_mo_alt_coa *)mo;
++              ipv6_addr_copy(&ops->alt_coa->addr, (struct in6_addr *)data);
++              break;
++      case MIPV6_OPT_NONCE_INDICES:
++              DEBUG(DBG_INFO, "Added nonce indices pointer");
++              ops->nonce_indices = (struct mipv6_mo_nonce_indices *)mo;
++              ops->nonce_indices->home_nonce_i = *(__u16 *)data;
++              ops->nonce_indices->careof_nonce_i = *((__u16 *)data + 1);
++              break;
++      case MIPV6_OPT_AUTH_DATA:
++              DEBUG(DBG_INFO, "Added opt auth_data pointer");
++              ops->auth_data = (struct mipv6_mo_bauth_data *)mo;
++              break;
++      case MIPV6_OPT_BIND_REFRESH_ADVICE:
++              ops->br_advice = (struct mipv6_mo_br_advice *)mo;
++              ops->br_advice->refresh_interval = htons(*(u16 *)data);
++              break;
++      default:
++              DEBUG(DBG_ERROR, "Unknow option type");
++              break;
++      }
++
++      if (ops->freelen == 0)
++              ops->next_free = NULL;
++      else
++              ops->next_free += (len + 2);
++
++      return 0;
++}
++
++/*
++ * Calculates required padding with xn + y requirement with offset
++ */
++static inline int optpad(int xn, int y, int offset)
++{
++      return ((y - offset) & (xn - 1));
++}
++
++static int option_pad(int type, int offset)
++{
++      if (type == MIPV6_OPT_ALTERNATE_COA)
++              return optpad(8, 6, offset); /* 8n + 6 */
++      if (type == MIPV6_OPT_BIND_REFRESH_ADVICE ||
++          type == MIPV6_OPT_NONCE_INDICES)
++              return optpad(2, 0, offset); /* 2n */
++      return 0;
++}
++
++/*
++ * Add Pad1 or PadN option to data
++ */
++int mipv6_add_pad(u8 *data, int n)
++{
++      struct mipv6_mo_padn *padn;
++
++      if (n <= 0) return 0;
++      if (n == 1) {
++              *data = MIPV6_OPT_PAD1;
++              return 1;
++      }
++      padn = (struct mipv6_mo_padn *)data;
++      padn->type = MIPV6_OPT_PADN;
++      padn->length = n - 2;
++      memset(padn->data, 0, n - 2);
++      return n;
++}
++
++/*
++ * Write options to mobility header buffer
++ */
++static int prepare_mh_opts(u8 *optdata, int off, struct mipv6_mh_opt *ops)
++{
++      u8 *nextopt = optdata;
++      int offset = off, pad = 0;
++
++      if (ops == NULL) {
++              nextopt = NULL;
++              return -1;
++      }
++
++      if (ops->alt_coa) {
++              pad = option_pad(MIPV6_OPT_ALTERNATE_COA, offset);
++              nextopt += mipv6_add_pad(nextopt, pad);
++              memcpy(nextopt, ops->alt_coa, sizeof(struct mipv6_mo_alt_coa));
++              nextopt += sizeof(struct mipv6_mo_alt_coa);
++              offset += pad + sizeof(struct mipv6_mo_alt_coa);
++      }
++
++      if (ops->br_advice) {
++              pad = option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset);
++              nextopt += mipv6_add_pad(nextopt, pad);
++              memcpy(nextopt, ops->br_advice, sizeof(struct mipv6_mo_br_advice));
++              nextopt += sizeof(struct mipv6_mo_br_advice);
++              offset += pad + sizeof(struct mipv6_mo_br_advice);
++      }
++
++      if (ops->nonce_indices) {
++              pad = option_pad(MIPV6_OPT_NONCE_INDICES, offset);
++              nextopt += mipv6_add_pad(nextopt, pad);
++              memcpy(nextopt, ops->nonce_indices, sizeof(struct mipv6_mo_nonce_indices));
++              nextopt += sizeof(struct mipv6_mo_nonce_indices);
++              offset += pad + sizeof(struct mipv6_mo_nonce_indices);
++      }
++
++      if (ops->auth_data) {
++              /* This option should always be the last.  Header
++               * length must be a multiple of 8 octects, so we pad
++               * if necessary. */
++              pad = optpad(8, 0, offset + ops->auth_data->length + 2);
++              nextopt += mipv6_add_pad(nextopt, pad);
++              memcpy(nextopt, ops->auth_data, ops->auth_data->length + 2);
++              nextopt += ops->auth_data->length + 2;
++      }
++      nextopt = NULL;
++
++      return 0;
++}
++
++static int calculate_mh_opts(struct mipv6_mh_opt *ops, int mh_len)
++{
++      int offset = mh_len;
++
++      if (ops == NULL)
++              return 0;
++
++      if (ops->alt_coa)
++              offset += sizeof(struct mipv6_mo_alt_coa)
++                      + option_pad(MIPV6_OPT_ALTERNATE_COA, offset);
++
++      if (ops->br_advice)
++              offset += sizeof(struct mipv6_mo_br_advice)
++                      + option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset);
++
++      if (ops->nonce_indices)
++              offset += sizeof(struct mipv6_mo_nonce_indices)
++                      + option_pad(MIPV6_OPT_NONCE_INDICES, offset);
++
++      if (ops->auth_data) /* no alignment */
++              offset += ops->auth_data->length + 2;
++
++      return offset - mh_len;
++}
++
++/*
++ *
++ * Mobility Header Message send functions
++ *
++ */
++
++/**
++ * send_mh - builds and sends a MH msg
++ *
++ * @daddr: destination address for packet
++ * @saddr: source address for packet
++ * @msg_type: type of MH
++ * @msg_len: message length
++ * @msg: MH type specific data
++ * @hao_addr: home address for home address option
++ * @rth_addr: routing header address
++ * @ops: mobility options
++ * @parm: auth data
++ *
++ * Builds MH, appends the type specific msg data to the header and
++ * sends the packet with a home address option, if a home address was
++ * given. Returns 0, if everything succeeded and a negative error code
++ * otherwise.
++ **/
++int send_mh(struct in6_addr *daddr, 
++          struct in6_addr *saddr, 
++          u8 msg_type, u8 msg_len, u8 *msg,
++          struct in6_addr *hao_addr,
++          struct in6_addr *rth_addr,
++          struct mipv6_mh_opt *ops,
++          struct mipv6_auth_parm *parm)
++{
++      struct flowi fl;
++      struct mipv6_mh *mh; 
++      struct sock *sk = mipv6_mh_socket->sk;
++      struct ipv6_txoptions *txopt = NULL;
++      int tot_len = sizeof(struct mipv6_mh) + msg_len;
++      int padded_len = 0, txopt_len = 0;
++
++      DEBUG_FUNC();
++      /* Add length of options */
++      tot_len += calculate_mh_opts(ops, tot_len);
++      /* Needs to be a multiple of 8 octets */
++      padded_len = tot_len + optpad(8, 0, tot_len);
++
++      mh = sock_kmalloc(sk, padded_len, GFP_ATOMIC);
++      if (!mh) {
++              DEBUG(DBG_ERROR, "memory allocation failed");
++              return -ENOMEM;
++      }
++
++      memset(&fl, 0, sizeof(fl)); 
++      fl.proto = IPPROTO_MOBILITY;
++      fl.fl6_dst = daddr;
++      fl.fl6_src = saddr;
++      fl.fl6_flowlabel = 0;
++      fl.oif = sk->bound_dev_if;
++
++      if (hao_addr || rth_addr) {
++              __u8 *opt_ptr;
++
++              if (hao_addr)
++                      txopt_len += sizeof(struct mipv6_dstopt_homeaddr) + 6;
++              if (rth_addr)
++                      txopt_len += sizeof(struct rt2_hdr);
++
++              txopt_len += sizeof(*txopt);
++              txopt = sock_kmalloc(sk, txopt_len, GFP_ATOMIC);
++              if (txopt == NULL) {
++                      DEBUG(DBG_ERROR, "No socket space left");
++                      sock_kfree_s(sk, mh, padded_len);
++                      return -ENOMEM;
++              }
++              memset(txopt, 0, txopt_len);
++              txopt->tot_len = txopt_len;
++              opt_ptr = (__u8 *) (txopt + 1);
++              if (hao_addr) {
++                      int holen = sizeof(struct mipv6_dstopt_homeaddr) + 6;
++                      txopt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr;
++                      txopt->opt_flen += holen;
++                      opt_ptr += holen;
++                      mipv6_append_dst1opts(txopt->dst1opt, saddr, 
++                                            NULL, holen);
++                      txopt->mipv6_flags = MIPV6_SND_HAO | MIPV6_SND_BU;
++              }
++              if (rth_addr) {
++                      int rtlen = sizeof(struct rt2_hdr);
++                      txopt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr;
++                      txopt->opt_nflen += rtlen;
++                      opt_ptr += rtlen;
++                      mipv6_append_rt2hdr(txopt->srcrt2, rth_addr);
++              }
++      }
++
++      /* Fill in the fields of MH */
++      mh->payload = NEXTHDR_NONE;
++      mh->length = (padded_len >> 3) - 1;     /* Units of 8 octets - 1 */
++      mh->type = msg_type;
++      mh->reserved = 0;
++      mh->checksum = 0;
++
++      memcpy(mh->data, msg, msg_len);
++      prepare_mh_opts(mh->data + msg_len, msg_len + sizeof(*mh), ops);
++      /* If BAD is present, this is already done. */
++      mipv6_add_pad((u8 *)mh + tot_len, padded_len - tot_len);
++      
++      if (parm && parm->k_bu && ops && ops->auth_data) {
++              /* Calculate the position of the authorization data before adding checksum*/
++              mipv6_auth_build(parm->cn_addr, parm->coa, (__u8 *)mh, 
++                               (__u8 *)mh + padded_len - MIPV6_RR_MAC_LENGTH, parm->k_bu);
++      }
++      /* Calculate the MH checksum */
++      mh->checksum = csum_ipv6_magic(fl.fl6_src, fl.fl6_dst, 
++                                     padded_len, IPPROTO_MOBILITY,
++                                     csum_partial((char *)mh, padded_len, 0));
++      ip6_build_xmit(sk, dstopts_getfrag, mh, &fl, padded_len, txopt, 255,
++                     MSG_DONTWAIT);
++      /* dst cache must be cleared so RR messages can be routed through 
++         different interfaces */
++      sk_dst_reset(sk);
++
++      if (txopt_len)
++              sock_kfree_s(sk, txopt, txopt_len);
++      sock_kfree_s(sk, mh, padded_len);
++      return 0;
++}
++
++/**
++ * mipv6_send_brr - send a Binding Refresh Request 
++ * @saddr: source address for BRR
++ * @daddr: destination address for BRR
++ * @ops: mobility options
++ *
++ * Sends a binding request.  On a mobile node, use the mobile node's
++ * home address for @saddr.  Returns 0 on success, negative on
++ * failure.
++ **/
++int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr,
++                 struct mipv6_mh_opt *ops)
++{
++      struct mipv6_mh_brr br;
++
++      memset(&br, 0, sizeof(br));
++      /* We don't need to explicitly add a RH to brr, since it will be 
++       * included automatically, if a BCE exists 
++       */
++      MIPV6_INC_STATS(n_brr_sent);
++      return send_mh(daddr, saddr, MIPV6_MH_BRR, sizeof(br), (u8 *)&br,
++                     NULL, NULL, ops, NULL);
++}
++
++/**
++ * mipv6_send_ba - send a Binding Acknowledgement 
++ * @saddr: source address for BA
++ * @daddr: destination address for BA 
++ * @reply_coa: destination care-of address of MN
++ * @auth_coa: care-of address of MN used for authentication
++ * @status: status field value
++ * @sequence: sequence number from BU
++ * @lifetime: granted lifetime for binding in seconds
++ * @ops: mobility options
++ *
++ * Send a binding acknowledgement.  On a mobile node, use the mobile
++ * node's home address for saddr.  Returns 0 on success, non-zero on
++ * failure.
++ **/
++int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, 
++                struct in6_addr *auth_coa, struct in6_addr *rep_coa,
++                u8 status, u16 sequence, u32 lifetime, u8 *k_bu)
++{
++      struct mipv6_mh_ba ba;
++      struct mipv6_auth_parm parm;
++      struct mipv6_mh_opt *ops = NULL; 
++      int ops_len = 0, ret = 0;
++      struct mipv6_bce bc_entry;
++      int coming_home = 0;
++      int bypass_tnl = 0;
++
++      memset(&ba, 0, sizeof(ba));
++      
++      ba.status = status;
++      ba.sequence = htons(sequence);
++      ba.lifetime = htons(lifetime >> 2);
++      
++      DEBUG(DBG_INFO, "sending a status %d BA %s authenticator to MN \n"
++            "%x:%x:%x:%x:%x:%x:%x:%x  at care of address \n" 
++            "%x:%x:%x:%x:%x:%x:%x:%x : with lifetime %d and \n" 
++            " sequence number %d",
++            status, k_bu ? "with" : "without", 
++            NIPV6ADDR(daddr), NIPV6ADDR(auth_coa), lifetime, sequence);
++
++      memset(&parm, 0, sizeof(parm));
++      parm.coa = auth_coa;
++      parm.cn_addr = saddr;
++
++      if (k_bu) {
++              ops_len += sizeof(struct mipv6_mo_bauth_data) + 
++                      MIPV6_RR_MAC_LENGTH;
++              parm.k_bu = k_bu;
++      }
++
++      if (mip6node_cnf.binding_refresh_advice) {
++              ops_len += sizeof(struct mipv6_mo_br_advice);
++      }
++      if (ops_len) {
++              ops = alloc_mh_opts(ops_len);
++              if (ops == NULL) {
++                      DEBUG(DBG_WARNING, "Out of memory");
++                      return -ENOMEM;
++              }
++              if (mip6node_cnf.binding_refresh_advice > 0) {
++                      if (append_mh_opt(ops, MIPV6_OPT_BIND_REFRESH_ADVICE, 2,
++                                        &mip6node_cnf.binding_refresh_advice) < 0) {
++                              DEBUG(DBG_WARNING, "Adding BRA failed");
++                              if (ops)
++                                      kfree(ops);
++                              return -ENOMEM;
++                      }
++              }
++              if (k_bu) {
++                      if (append_mh_opt(ops, MIPV6_OPT_AUTH_DATA,
++                                        MIPV6_RR_MAC_LENGTH, NULL) < 0) {
++                              DEBUG(DBG_WARNING, "Adding BAD failed");
++                              if (ops)
++                                      kfree(ops);
++                              return -ENOMEM;
++                      }
++              }
++      }
++      coming_home = !ipv6_addr_cmp(rep_coa, daddr);
++
++      bypass_tnl = (coming_home &&
++                    !mipv6_bcache_get(daddr, saddr, &bc_entry) &&
++                    bc_entry.flags&MIPV6_BU_F_HOME && 
++                    status >= 128);
++
++      if (bypass_tnl && mip6_fn.bce_tnl_rt_del)
++              mip6_fn.bce_tnl_rt_del(&bc_entry.coa,
++                                     &bc_entry.our_addr,
++                                     &bc_entry.home_addr);
++
++      if (coming_home)
++              ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba,
++                            NULL, NULL, ops, &parm);
++      else
++              ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba,
++                            NULL, rep_coa, ops, &parm);
++
++      if (bypass_tnl && mip6_fn.bce_tnl_rt_add)
++              mip6_fn.bce_tnl_rt_add(&bc_entry.coa,
++                                     &bc_entry.our_addr,
++                                     &bc_entry.home_addr);
++      
++      if (ret == 0) {
++              if (status < 128) {
++                      MIPV6_INC_STATS(n_ba_sent);
++              } else {
++                      MIPV6_INC_STATS(n_ban_sent);
++              }
++      }
++
++      if (ops)
++              kfree(ops);
++
++      return 0;
++}
++
++/**
++ * mipv6_send_be - send a Binding Error message
++ * @saddr: source address for BE
++ * @daddr: destination address for BE
++ * @home: Home Address in offending packet (if any)
++ *
++ * Sends a binding error.  On a mobile node, use the mobile node's
++ * home address for @saddr.  Returns 0 on success, negative on
++ * failure.
++ **/
++int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, 
++                struct in6_addr *home, __u8 status)
++{
++      struct mipv6_mh_be be;
++      int ret = 0;
++      struct mipv6_bce bc_entry;
++      int bypass_tnl = 0;
++
++      if (ipv6_addr_is_multicast(daddr))
++              return -EINVAL;
++
++      memset(&be, 0, sizeof(be));
++      be.status = status;
++      if (home)
++              ipv6_addr_copy(&be.home_addr, home);
++
++      if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0 &&
++          bc_entry.flags&MIPV6_BU_F_HOME)
++              bypass_tnl = 1;
++
++      if (bypass_tnl && mip6_fn.bce_tnl_rt_del)
++              mip6_fn.bce_tnl_rt_del(&bc_entry.coa,
++                                     &bc_entry.our_addr,
++                                     &bc_entry.home_addr);
++
++      ret = send_mh(daddr, saddr, MIPV6_MH_BE, sizeof(be), (u8 *)&be,
++                    NULL, NULL, NULL, NULL);
++      
++      if (bypass_tnl && mip6_fn.bce_tnl_rt_add)
++              mip6_fn.bce_tnl_rt_add(&bc_entry.coa,
++                                     &bc_entry.our_addr,
++                                     &bc_entry.home_addr);
++
++      if (ret == 0)
++              MIPV6_INC_STATS(n_be_sent);
++
++      return ret;
++}
++
++/**
++ * mipv6_send_addr_test - send a HoT or CoT message
++ * @saddr: source address
++ * @daddr: destination address
++ * @msg_type: HoT or CoT message
++ * @init: HoTI or CoTI message
++ *
++ * Send a reply to HoTI or CoTI message. 
++ **/
++static int mipv6_send_addr_test(struct in6_addr *saddr,
++                              struct in6_addr *daddr,
++                              int msg_type,
++                              struct mipv6_mh_addr_ti *init)
++{
++      u_int8_t                        *kgen_token = NULL;
++      struct mipv6_mh_addr_test       addr_test;      
++      struct mipv6_rr_nonce           *nonce;
++      struct mipv6_mh_opt *ops = NULL;
++      int ret = 0;
++
++      DEBUG_FUNC();
++
++      if ((nonce = mipv6_rr_get_new_nonce())== NULL) {
++              DEBUG(DBG_WARNING, "Nonce creation failed");
++              return 0;
++      } 
++      if (mipv6_rr_cookie_create(daddr, &kgen_token, nonce->index)) {
++              DEBUG(DBG_WARNING, "No cookie");
++              return 0;
++      }
++
++      addr_test.nonce_index = nonce->index;
++      memcpy(addr_test.init_cookie, init->init_cookie,
++                      MIPV6_RR_COOKIE_LENGTH);
++      memcpy(addr_test.kgen_token, kgen_token,
++                      MIPV6_RR_COOKIE_LENGTH);
++
++      /* No options defined */
++      ret = send_mh(daddr, saddr, msg_type, sizeof(addr_test),
++                    (u8 *)&addr_test, NULL, NULL, ops, NULL);
++
++      if (ret == 0) {
++              if (msg_type == MIPV6_MH_HOT) {
++                      MIPV6_INC_STATS(n_hot_sent);
++              } else {
++                      MIPV6_INC_STATS(n_cot_sent);
++              }
++      }
++
++      return 0;
++}
++
++static void bc_cache_add(int ifindex, struct in6_addr *daddr,
++                       struct in6_addr *haddr, struct in6_addr *coa,
++                       struct in6_addr *rep_coa, __u32 lifetime,
++                       __u16 sequence, __u8 flags, __u8 *k_bu)
++{
++      __u8 ba_status = SUCCESS;
++
++      if (lifetime >  MAX_RR_BINDING_LIFE)
++              lifetime = MAX_RR_BINDING_LIFE;
++
++      if (mipv6_bcache_add(ifindex, daddr, haddr, coa, lifetime,
++                           sequence, flags, CACHE_ENTRY) != 0) {
++              DEBUG(DBG_ERROR, "binding failed.");
++              ba_status = INSUFFICIENT_RESOURCES;
++      } 
++
++      if (flags & MIPV6_BU_F_ACK) {
++              DEBUG(DBG_INFO, "sending ack (code=%d)", ba_status);
++              mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence,
++                            lifetime, k_bu);
++      }
++}
++
++static void bc_cn_home_add(int ifindex, struct in6_addr *daddr, 
++                         struct in6_addr *haddr, struct in6_addr *coa,
++                         struct in6_addr *rep_coa, __u32 lifetime,
++                         __u16 sequence, __u8 flags, __u8 *k_bu)
++{
++      mipv6_send_ba(daddr, haddr, coa, rep_coa,
++                    HOME_REGISTRATION_NOT_SUPPORTED,
++                    sequence, lifetime, k_bu);
++}
++
++static void bc_cache_delete(struct in6_addr *daddr, struct in6_addr *haddr, 
++                          struct in6_addr *coa, struct in6_addr *rep_coa,
++                          __u16 sequence, __u8 flags,
++                          __u8 *k_bu)
++{
++      __u8 status = SUCCESS;
++
++      /* Cached Care-of Address Deregistration */
++      if (mipv6_bcache_exists(haddr, daddr) == CACHE_ENTRY) {
++              mipv6_bcache_delete(haddr, daddr, CACHE_ENTRY);
++      } else {
++              DEBUG(DBG_INFO, "entry is not in cache");
++              status = REASON_UNSPECIFIED;
++      }
++      if (flags & MIPV6_BU_F_ACK) {
++              mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, 
++                            0, k_bu);
++      }
++}
++
++static void bc_cn_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, 
++                            struct in6_addr *coa, struct in6_addr *rep_coa,
++                            __u16 sequence, __u8 flags,
++                            __u8 *k_bu)
++{
++}
++
++/**
++ * parse_mo_tlv - Parse TLV-encoded Mobility Options
++ * @mos: pointer to Mobility Options
++ * @len: total length of options
++ * @opts: structure to store option pointers
++ *
++ * Parses Mobility Options passed in @mos.  Stores pointers in @opts
++ * to all valid mobility options found in @mos.  Unknown options and
++ * padding (%MIPV6_OPT_PAD1 and %MIPV6_OPT_PADN) is ignored and
++ * skipped.
++ **/
++int parse_mo_tlv(void *mos, int len, struct mobopt *opts)
++{
++      struct mipv6_mo *curr = (struct mipv6_mo *)mos;
++      int left = len;
++
++      while (left > 0) {
++              int optlen = 0;
++              if (curr->type == MIPV6_OPT_PAD1)
++                      optlen = 1;
++              else
++                      optlen = 2 + curr->length;
++
++              if (optlen > left)
++                      goto bad;
++
++              switch (curr->type) {
++              case MIPV6_OPT_PAD1:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_PAD1 at %x", curr);
++                      break;
++              case MIPV6_OPT_PADN:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_PADN at %x", curr);
++                      break;
++              case MIPV6_OPT_ALTERNATE_COA:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_ACOA at %x", curr);
++                      opts->alt_coa = (struct mipv6_mo_alt_coa *)curr;
++                      break;
++              case MIPV6_OPT_NONCE_INDICES:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_NONCE_INDICES at %x", curr);
++                      opts->nonce_indices = 
++                              (struct mipv6_mo_nonce_indices *)curr;
++                      break;
++              case MIPV6_OPT_AUTH_DATA:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_AUTH_DATA at %x", curr);
++                      opts->auth_data = (struct mipv6_mo_bauth_data *)curr;
++                      break;
++              case MIPV6_OPT_BIND_REFRESH_ADVICE:
++                      DEBUG(DBG_DATADUMP, "MIPV6_OPT_BIND_REFRESH_ADVICE at %x", curr);
++                      opts->br_advice = (struct mipv6_mo_br_advice *)curr;
++                      break;
++              default:
++                      DEBUG(DBG_INFO, "MO Unknown option type %d at %x, ignoring.",
++                             curr->type, curr);
++                      /* unknown mobility option, ignore and skip */
++              }
++
++              (u8 *)curr += optlen;
++              left -= optlen;
++      }
++
++      if (left == 0)
++              return 0;
++ bad:
++      return -1;
++}
++
++/*
++ *
++ * Mobility Header Message handlers
++ *
++ */
++
++static int mipv6_handle_mh_testinit(struct sk_buff *skb,
++                                  struct in6_addr *cn,
++                                  struct in6_addr *lcoa,
++                                  struct in6_addr *saddr,
++                                  struct in6_addr *fcoa,
++                                  struct mipv6_mh *mh)
++{
++      struct mipv6_mh_addr_ti *ti = (struct mipv6_mh_addr_ti *)mh->data;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++      DEBUG_FUNC();
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*ti);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_INFO, "Mobility Header length less than H/C TestInit");
++              return -1;
++      }
++      if (!mip6node_cnf.accept_ret_rout) {
++              DEBUG(DBG_INFO, "Return routability administratively disabled");
++              return -1;
++      }
++      if (lcoa || fcoa) {
++              DEBUG(DBG_INFO, "H/C TestInit has HAO or RTH2, dropped.");
++              return -1;
++      }
++
++      if (mh->type == MIPV6_MH_HOTI) {
++              MIPV6_INC_STATS(n_hoti_rcvd);
++              return mipv6_send_addr_test(cn, saddr, MIPV6_MH_HOT, ti);
++      } else if (mh->type == MIPV6_MH_COTI) {
++              MIPV6_INC_STATS(n_coti_rcvd);
++              return mipv6_send_addr_test(cn, saddr, MIPV6_MH_COT, ti);
++      } else 
++              return -1; /* Impossible to get here */
++}
++
++/**
++ * mipv6_handle_mh_bu - Binding Update handler
++ * @src: care-of address of sender
++ * @dst: our address
++ * @haddr: home address of sender
++ * @mh: pointer to the beginning of the Mobility Header
++ *
++ * Handles Binding Update. Packet and offset to option are passed.
++ * Returns 0 on success, otherwise negative.
++ **/
++static int mipv6_handle_mh_bu(struct sk_buff *skb,
++                            struct in6_addr *dst,
++                            struct in6_addr *unused,
++                            struct in6_addr *haddr, 
++                            struct in6_addr *coaddr,
++                            struct mipv6_mh *mh)
++{
++      struct mipv6_mh_bu *bu = (struct mipv6_mh_bu *)mh->data;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++      int auth = 0;
++      int dereg; /* Is this deregistration? */ 
++      int addr_type;
++
++      struct mipv6_bce bc_entry;
++      struct in6_addr *coa, *reply_coa;
++      __u8 *key_bu = NULL; /* RR BU authentication key */
++      __u8 flags = bu->flags;
++      __u16 sequence;
++      __u32 lifetime;
++      __u16 nonce_ind = (__u16) -1; 
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*bu);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_INFO, "Mobility Header length less than BU");
++              MIPV6_INC_STATS(n_bu_drop.invalid);
++              return -1;
++      }
++
++      addr_type = ipv6_addr_type(haddr);
++      if (addr_type&IPV6_ADDR_LINKLOCAL || !(addr_type&IPV6_ADDR_UNICAST))
++              return -EINVAL;
++
++      /* If HAO not present, CoA == HAddr */
++      if (coaddr == NULL) 
++              coa = haddr;
++      else {
++              coa = coaddr;
++              addr_type = ipv6_addr_type(coa);
++              if (addr_type&IPV6_ADDR_LINKLOCAL ||
++                  !(addr_type&IPV6_ADDR_UNICAST))
++                      return -EINVAL;
++      }
++      reply_coa = coa;
++
++      sequence = ntohs(bu->sequence);
++      if (bu->lifetime == 0xffff)
++              lifetime = 0xffffffff;
++      else
++              lifetime = ntohs(bu->lifetime) << 2;
++
++      dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0);
++
++      if (opt_len > 0) {
++              struct mobopt opts;
++              memset(&opts, 0, sizeof(opts));
++              if (parse_mo_tlv(bu + 1, opt_len, &opts) < 0) {
++                      MIPV6_INC_STATS(n_bu_drop.invalid);
++                      return -1;
++              }
++              /*
++               * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_NONCE_INDICES, 
++               * MIPV6_OPT_ALT_COA
++               */
++              if (opts.alt_coa) {
++                      coa = &opts.alt_coa->addr;
++                      dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0);
++              }
++              addr_type = ipv6_addr_type(coa);
++              if (addr_type&IPV6_ADDR_LINKLOCAL || 
++                  !(addr_type&IPV6_ADDR_UNICAST))
++                      return -EINVAL;
++
++              if (flags & MIPV6_BU_F_HOME) {
++                      if (opts.nonce_indices)
++                              return -1;
++              } else {
++                      u8 ba_status = 0;
++                      u8 *h_ckie  = NULL, *c_ckie = NULL; /* Home and care-of cookies */
++
++                      /* BUs to CN MUST include authorization data and nonce indices options */
++                      if (!opts.auth_data || !opts.nonce_indices) {
++                              DEBUG(DBG_WARNING,
++                                    "Route optimization BU without authorization material, aborting processing");
++                              return MH_AUTH_FAILED;
++                      }
++                      if (mipv6_rr_cookie_create(
++                                  haddr, &h_ckie, opts.nonce_indices->home_nonce_i) < 0) {
++                              DEBUG(DBG_WARNING,
++                                    "mipv6_rr_cookie_create failed for home cookie");
++                              ba_status = EXPIRED_HOME_NONCE_INDEX;
++                      }
++                      nonce_ind = opts.nonce_indices->home_nonce_i;
++                      /* Don't create the care-of cookie, if MN deregisters */
++                      if (!dereg && mipv6_rr_cookie_create(
++                                  coa, &c_ckie,
++                                  opts.nonce_indices->careof_nonce_i) < 0) {
++                              DEBUG(DBG_WARNING,
++                                    "mipv6_rr_cookie_create failed for coa cookie");
++                              if (ba_status == 0)
++                                      ba_status = EXPIRED_CAREOF_NONCE_INDEX;
++                              else
++                                      ba_status = EXPIRED_NONCES;
++                      }
++                      if (ba_status == 0) {
++                              if (dereg)
++                                      key_bu = mipv6_rr_key_calc(h_ckie, NULL);
++                              else
++                                      key_bu = mipv6_rr_key_calc(h_ckie, c_ckie);            
++                              mh->checksum = 0;/* TODO: Don't mangle the packet */
++                              if (key_bu && mipv6_auth_check(
++                                      dst, coa, (__u8 *)mh,  msg_len + sizeof(*mh), opts.auth_data, key_bu) == 0) {
++                                      DEBUG(DBG_INFO, "mipv6_auth_check OK for BU");
++                                      auth = 1;
++                              } else {
++                                      DEBUG(DBG_WARNING, 
++                                            "BU Authentication failed");
++                              }
++                      }
++                      if (h_ckie)
++                              kfree(h_ckie);
++                      if (c_ckie)
++                              kfree(c_ckie);
++                      if (ba_status != 0) {
++                              MIPV6_INC_STATS(n_bu_drop.auth);
++                              mipv6_send_ba(dst, haddr, coa,
++                                            reply_coa, ba_status,
++                                            sequence, 0, NULL);
++                              goto out;
++                      }
++              }
++
++      }
++      /* Require authorization option for RO, home reg is protected by IPsec */
++      if (!(flags & MIPV6_BU_F_HOME) && !auth) {
++              MIPV6_INC_STATS(n_bu_drop.auth);
++              if (key_bu)
++                      kfree(key_bu);
++              return MH_AUTH_FAILED;
++      }
++
++      if (mipv6_bcache_get(haddr, dst, &bc_entry) == 0) {
++              if ((bc_entry.flags&MIPV6_BU_F_HOME) != 
++                  (flags&MIPV6_BU_F_HOME)) {
++                      DEBUG(DBG_INFO,
++                            "Registration type change. Sending BA REG_TYPE_CHANGE_FORBIDDEN");
++                      mipv6_send_ba(dst, haddr, coa, reply_coa,
++                                    REG_TYPE_CHANGE_FORBIDDEN,
++                                    sequence, lifetime, key_bu);
++                      goto out;
++              }
++              if (!MIPV6_SEQ_GT(sequence, bc_entry.seq)) {
++                      DEBUG(DBG_INFO,
++                            "Sequence number mismatch. Sending BA SEQUENCE_NUMBER_OUT_OF_WINDOW");
++                      mipv6_send_ba(dst, haddr, coa, reply_coa,
++                                    SEQUENCE_NUMBER_OUT_OF_WINDOW,
++                                    bc_entry.seq, lifetime, key_bu);
++                      goto out;
++              }
++      }
++
++      if (!dereg) {
++              int ifindex;
++              struct rt6_info *rt;
++
++              /* Avoid looping binding cache entries */
++              if (mipv6_bcache_get(coa, dst, &bc_entry) == 0) {
++                      DEBUG(DBG_WARNING, "Looped BU, dropping the packet");
++                      goto out;
++              }
++              DEBUG(DBG_INFO, "calling bu_add.");
++              if ((rt = rt6_lookup(haddr, dst, 0, 0)) != NULL) {
++                      ifindex = rt->rt6i_dev->ifindex;
++                      dst_release(&rt->u.dst);
++              } else {
++                      /*
++                       * Can't process the BU since the right interface is 
++                       * not found.
++                       */
++                      DEBUG(DBG_WARNING, "No route entry found for handling "
++                            "a BU request, (using 0 as index)");
++                      ifindex = 0;
++              }
++              if (flags & MIPV6_BU_F_HOME)
++                      mip6_fn.bce_home_add(ifindex, dst, haddr, coa, 
++                                           reply_coa, lifetime, sequence,
++                                           flags, key_bu);
++              else
++                      mip6_fn.bce_cache_add(ifindex, dst, haddr, coa, 
++                                            reply_coa, lifetime, sequence,
++                                            flags, key_bu);
++      } else {
++              DEBUG(DBG_INFO, "calling BCE delete.");
++
++              if (flags & MIPV6_BU_F_HOME)
++                      mip6_fn.bce_home_del(dst, haddr, coa, reply_coa,
++                                           sequence, flags, key_bu);
++              else {
++                      mipv6_rr_invalidate_nonce(nonce_ind);
++                      mip6_fn.bce_cache_del(dst, haddr, coa, reply_coa, 
++                                            sequence, flags, key_bu);
++              }
++      }
++ out:
++      MIPV6_INC_STATS(n_bu_rcvd);
++      if (key_bu)
++              kfree(key_bu);
++      return 0;
++}
++
++static int mipv6_mh_rcv(struct sk_buff *skb)
++{
++      struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
++      struct mipv6_mh *mh;
++      struct in6_addr *lhome, *fhome, *lcoa = NULL, *fcoa = NULL;
++      int ret = 0;
++
++      fhome = &skb->nh.ipv6h->saddr;
++      lhome = &skb->nh.ipv6h->daddr;
++
++      if (opt->hao != 0) {
++              struct mipv6_dstopt_homeaddr *hao;
++              hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao);
++              fcoa = &hao->addr;
++      }
++
++      if (opt->srcrt2 != 0) {
++              struct rt2_hdr *rt2;
++              rt2 = (struct rt2_hdr *)((u8 *)skb->nh.raw + opt->srcrt2);
++              lcoa = &rt2->addr;
++      }
++
++      /* Verify checksum is correct */
++      if (skb->ip_summed == CHECKSUM_HW) {
++              skb->ip_summed = CHECKSUM_UNNECESSARY;
++              if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY,
++                                  skb->csum)) {
++                      if (net_ratelimit())
++                              printk(KERN_WARNING "MIPv6 MH hw checksum failed\n");
++                      skb->ip_summed = CHECKSUM_NONE;
++              }
++      }
++      if (skb->ip_summed == CHECKSUM_NONE) {
++              if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY,
++                                  skb_checksum(skb, 0, skb->len, 0))) {
++                      if (net_ratelimit())
++                              printk(KERN_WARNING "MIPv6 MH checksum failed\n");
++                      goto bad;
++              }
++      }
++
++      if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*mh)) ||
++          !pskb_may_pull(skb, 
++                         skb->h.raw-skb->data+((skb->h.raw[1]+1)<<3))) {
++              DEBUG(DBG_INFO, "MIPv6 MH invalid length");
++              kfree_skb(skb);
++              return 0;
++      }
++
++      mh = (struct mipv6_mh *) skb->h.raw;
++
++      /* Verify there are no more headers after the MH */
++      if (mh->payload != NEXTHDR_NONE) {
++              __u32 pos = (__u32)&mh->payload - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_INFO, "MIPv6 MH error");
++              goto bad;
++      }
++
++      if (mh->type > MIPV6_MH_MAX) {
++              /* send binding error */
++              printk("Invalid mobility header type (%d)\n", mh->type);
++              mipv6_send_be(lhome, fcoa ? fcoa : fhome,
++                            fcoa ? fhome : NULL, 
++                            MIPV6_BE_UNKNOWN_MH_TYPE);
++              goto bad;
++      }
++      if (mh_rcv[mh->type].func != NULL) {
++              ret = mh_rcv[mh->type].func(skb, lhome, lcoa, fhome, fcoa, mh);
++      } else {
++              DEBUG(DBG_INFO, "No handler for MH Type %d", mh->type);
++              goto bad;
++      }
++
++      kfree_skb(skb);
++      return 0;
++
++bad:
++      MIPV6_INC_STATS(n_mh_in_error);
++      kfree_skb(skb);
++      return 0;
++
++}
++
++#if LINUX_VERSION_CODE >= 0x2052a
++struct inet6_protocol mipv6_mh_protocol =
++{
++      mipv6_mh_rcv,           /* handler              */
++      NULL                    /* error control        */
++};
++#else
++struct inet6_protocol mipv6_mh_protocol = 
++{
++      mipv6_mh_rcv,           /* handler              */
++      NULL,                   /* error control        */
++      NULL,                   /* next                 */
++      IPPROTO_MOBILITY,       /* protocol ID          */
++      0,                      /* copy                 */
++      NULL,                   /* data                 */
++      "MIPv6 MH"              /* name                 */
++};
++#endif
++
++/*
++ *
++ * Code module init/exit functions
++ *
++ */
++
++int __init mipv6_mh_common_init(void)
++{
++      struct sock *sk;
++      int err;
++
++      mip6_fn.bce_home_add = bc_cn_home_add;
++      mip6_fn.bce_cache_add = bc_cache_add;
++      mip6_fn.bce_home_del = bc_cn_home_delete;
++      mip6_fn.bce_cache_del = bc_cache_delete;
++
++      mipv6_mh_socket = sock_alloc();
++      if (mipv6_mh_socket == NULL) {
++              printk(KERN_ERR
++                     "Failed to create the MIP6 MH control socket.\n");
++              return -1;
++      }
++      mipv6_mh_socket->type = SOCK_RAW;
++
++      if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_MOBILITY, 
++                             &mipv6_mh_socket)) < 0) {
++              printk(KERN_ERR
++                     "Failed to initialize the MIP6 MH control socket (err %d).\n",
++                     err);
++              sock_release(mipv6_mh_socket);
++              mipv6_mh_socket = NULL; /* for safety */
++              return err;
++      }
++
++      sk = mipv6_mh_socket->sk;
++      sk->allocation = GFP_ATOMIC;
++      sk->sndbuf = 64 * 1024 + sizeof(struct sk_buff);
++      sk->prot->unhash(sk);
++
++      memset(&mh_rcv, 0, sizeof(mh_rcv));
++      mh_rcv[MIPV6_MH_HOTI].func = mipv6_handle_mh_testinit;
++      mh_rcv[MIPV6_MH_COTI].func = mipv6_handle_mh_testinit;
++      mh_rcv[MIPV6_MH_BU].func =  mipv6_handle_mh_bu;
++
++#if LINUX_VERSION_CODE >= 0x2052a
++      if (inet6_add_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY) < 0) {
++              printk(KERN_ERR "Failed to register MOBILITY protocol\n");
++              sock_release(mipv6_mh_socket);
++              mipv6_mh_socket = NULL;
++              return -EAGAIN;
++      }
++#else
++      inet6_add_protocol(&mipv6_mh_protocol);
++#endif
++      /* To disable the use of dst_cache, 
++       *  which slows down the sending of BUs ??
++       */
++      sk->dst_cache=NULL; 
++
++      return 0;
++}
++
++void __exit mipv6_mh_common_exit(void)
++{
++      if (mipv6_mh_socket) sock_release(mipv6_mh_socket);
++      mipv6_mh_socket = NULL; /* For safety. */
++
++#if LINUX_VERSION_CODE >= 0x2052a
++      inet6_del_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY);
++#else
++      inet6_del_protocol(&mipv6_mh_protocol);
++#endif
++      memset(&mh_rcv, 0, sizeof(mh_rcv));
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_mn.c
+@@ -0,0 +1,1155 @@
++/*
++ *    Mobile IPv6 Mobility Header Functions for Mobile Node
++ *
++ *    Authors:
++ *    Antti Tuominen  <ajtuomin@tml.hut.fi>
++ *    Niklas Kämpe    <nhkampe@cc.hut.fi>
++ *    Henrik Petander <henrik.petander@hut.fi>
++ *
++ *    $Id:$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License as
++ *    published by the Free Software Foundation; either version 2 of
++ *    the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/mipv6.h>
++
++#include "mobhdr.h"
++#include "mn.h"
++#include "bul.h"
++#include "rr_crypto.h"
++#include "debug.h"
++#include "util.h"
++#include "stats.h"
++
++int rr_configured = 1;
++
++/* Return value of mipv6_rr_state() */
++#define NO_RR                 0
++#define DO_RR                 1
++#define RR_FOR_COA            2
++#define INPROGRESS_RR         3
++
++/** 
++ * send_bu_msg - sends a Binding Update 
++ * @bulentry : BUL entry with the information for building a BU
++ *
++ * Function builds a BU msg based on the contents of a bul entry.
++ * Does not change the bul entry.
++ **/
++static int send_bu_msg(struct mipv6_bul_entry *binding)
++{ 
++      int auth = 0; /* Use auth */
++      int ret = 0;
++      struct mipv6_auth_parm parm;
++      struct mipv6_mh_bu bu;
++
++      if (!binding) {
++              DEBUG(DBG_ERROR, "called with a null bul entry");
++              return -1;
++      }
++      
++      memset(&parm, 0, sizeof(parm));
++      if (mipv6_prefix_compare(&binding->coa, &binding->home_addr, 64))
++              parm.coa = &binding->home_addr;
++      else
++              parm.coa = &binding->coa;
++      parm.cn_addr = &binding->cn_addr;
++
++      if (binding->rr && binding->rr->kbu) {
++              DEBUG(DBG_INFO, "Binding with key");
++              auth = 1;
++              parm.k_bu = binding->rr->kbu;
++      }
++      memset(&bu, 0, sizeof(bu));
++      bu.flags = binding->flags;
++      bu.sequence = htons(binding->seq);
++      bu.lifetime = htons(binding->lifetime >> 2);
++      bu.reserved = 0;
++
++      ret = send_mh(&binding->cn_addr, &binding->home_addr,
++                    MIPV6_MH_BU, sizeof(bu), (u8 *)&bu, 
++                    &binding->home_addr, NULL, 
++                    binding->ops, &parm);
++
++      if (ret == 0)
++              MIPV6_INC_STATS(n_bu_sent);
++
++      return ret;
++}
++
++/**
++ * mipv6_send_addr_test_init - send a HoTI or CoTI message
++ * @saddr: source address for H/CoTI
++ * @daddr: destination address for H/CoTI
++ * @msg_type: Identifies whether HoTI or CoTI
++ * @init_cookie: the HoTi or CoTi init cookie
++ *
++ * The message will be retransmitted till we get a HoT or CoT message, since 
++ * our caller (mipv6_RR_start) has entered this message in the BUL with
++ * exponential backoff retramission set.
++ */
++static int mipv6_send_addr_test_init(struct in6_addr *saddr,
++                                   struct in6_addr *daddr,
++                                   u8 msg_type,
++                                   u8 *init_cookie)
++{
++      struct mipv6_mh_addr_ti ti;
++      struct mipv6_mh_opt *ops = NULL;
++      int ret = 0;
++
++      /* Set reserved and copy the cookie from address test init msg */
++      ti.reserved = 0;
++      mipv6_rr_mn_cookie_create(init_cookie);
++      memcpy(ti.init_cookie, init_cookie, MIPV6_RR_COOKIE_LENGTH);
++
++      ret = send_mh(daddr, saddr, msg_type, sizeof(ti), (u8 *)&ti,
++                    NULL, NULL, ops, NULL);
++      if (ret == 0) {
++              if (msg_type == MIPV6_MH_HOTI) {
++                      MIPV6_INC_STATS(n_hoti_sent);
++              } else {
++                      MIPV6_INC_STATS(n_coti_sent);
++              }
++      }
++
++      return ret;
++}
++
++/*
++ *
++ * Callback handlers for binding update list
++ *
++ */
++
++/* Return value 0 means keep entry, non-zero means discard entry. */
++
++/* Callback for BUs not requiring acknowledgement
++ */
++int bul_entry_expired(struct mipv6_bul_entry *bulentry)
++{
++      /* Lifetime expired, delete entry. */
++      DEBUG(DBG_INFO, "bul entry 0x%p lifetime expired, deleting entry", 
++            bulentry);
++      return 1;
++}
++
++/* Callback for BUs requiring acknowledgement with exponential resending
++ * scheme */
++static int bul_resend_exp(struct mipv6_bul_entry *bulentry)
++{
++      unsigned long now = jiffies;
++      
++      DEBUG(DBG_INFO, "(0x%x) resending bu", (int) bulentry);
++
++      
++      /* If sending a de-registration, do not care about the
++       * lifetime value, as de-registrations are normally sent with
++       * a zero lifetime value. If the entry is a home entry get the 
++       * current lifetime. 
++       */
++
++      if (bulentry->lifetime != 0) {
++              bulentry->lifetime = mipv6_mn_get_bulifetime(
++                      &bulentry->home_addr, &bulentry->coa, bulentry->flags);
++
++              bulentry->expire = now + bulentry->lifetime * HZ;
++      } else {
++              bulentry->expire = now + HOME_RESEND_EXPIRE * HZ; 
++      }
++      if (bulentry->rr) {
++              /* Redo RR, if cookies have expired */
++              if (time_after(jiffies, bulentry->rr->home_time + MAX_TOKEN_LIFE * HZ)) 
++                      bulentry->rr->rr_state |= RR_WAITH;
++              if (time_after(jiffies, bulentry->rr->careof_time + MAX_NONCE_LIFE * HZ)) 
++                      bulentry->rr->rr_state |= RR_WAITC;
++
++              if (bulentry->rr->rr_state & RR_WAITH) {
++                      /* Resend HoTI directly */
++                      mipv6_send_addr_test_init(&bulentry->home_addr, 
++                                                &bulentry->cn_addr, MIPV6_MH_HOTI,
++                                                bulentry->rr->hot_cookie);
++              }
++              if (bulentry->rr->rr_state & RR_WAITC) {
++                              /* Resend CoTI directly */
++                              mipv6_send_addr_test_init(&bulentry->coa, 
++                                                        &bulentry->cn_addr, MIPV6_MH_COTI,
++                                                        bulentry->rr->cot_cookie);
++                      }
++              goto out;
++      }
++      
++      bulentry->seq++;
++
++      if (send_bu_msg(bulentry) < 0)
++              DEBUG(DBG_ERROR, "Resending of BU failed");
++
++out:
++      /* Schedule next retransmission */
++      if (bulentry->delay < bulentry->maxdelay) {
++              bulentry->delay = 2 * bulentry->delay;
++              if (bulentry->delay > bulentry->maxdelay) {
++                      /* can happen if maxdelay is not power(mindelay, 2) */
++                      bulentry->delay = bulentry->maxdelay;
++              }
++      } else if (bulentry->flags & MIPV6_BU_F_HOME) {
++              /* Home registration - continue sending BU at maxdelay rate */
++              DEBUG(DBG_INFO, "Sending BU to HA after max ack wait time "
++                    "reached(0x%x)", (int) bulentry);
++              bulentry->delay = bulentry->maxdelay;
++      } else if (!(bulentry->flags & MIPV6_BU_F_HOME)) {
++              /* Failed to get BA from a CN */
++              bulentry->callback_time = now;
++              return -1;
++      }
++      
++      bulentry->callback_time = now + bulentry->delay * HZ;
++      return 0;
++}
++
++
++
++/* Callback for sending a registration refresh BU
++ */
++static int bul_refresh(struct mipv6_bul_entry *bulentry)
++{
++      unsigned long now = jiffies;
++      
++      /* Refresh interval passed, send new BU */
++      DEBUG(DBG_INFO, "bul entry 0x%x refresh interval passed, sending new BU", (int) bulentry);
++      if (bulentry->lifetime == 0)
++              return 0;
++
++      /* Set new maximum lifetime and expiration time */
++      bulentry->lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, 
++                                                   &bulentry->coa, 
++                                                   bulentry->flags);
++      bulentry->expire = now + bulentry->lifetime * HZ;
++      bulentry->seq++;
++      /* Send update */
++      if (send_bu_msg(bulentry) < 0)
++              DEBUG(DBG_ERROR, "Resending of BU failed");
++      
++      if (time_after_eq(now, bulentry->expire)) {
++              /* Sanity check */
++              DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", ERROR_DEF_LIFETIME);
++              bulentry->lifetime = ERROR_DEF_LIFETIME;
++              bulentry->expire = now + ERROR_DEF_LIFETIME*HZ;
++      }
++
++      /* Set up retransmission */
++      bulentry->state = RESEND_EXP;
++      bulentry->callback = bul_resend_exp;
++      bulentry->callback_time = now + INITIAL_BINDACK_TIMEOUT*HZ;
++      bulentry->delay = INITIAL_BINDACK_TIMEOUT;
++      bulentry->maxdelay = MAX_BINDACK_TIMEOUT;
++
++      return 0;
++}
++
++static int mipv6_send_RR_bu(struct mipv6_bul_entry *bulentry)
++{
++      int ret;
++      int ops_len = 0;
++      u16 nonces[2];
++
++      DEBUG(DBG_INFO, "Sending BU to CN  %x:%x:%x:%x:%x:%x:%x:%x "
++            "for home address %x:%x:%x:%x:%x:%x:%x:%x", 
++            NIPV6ADDR(&bulentry->cn_addr), NIPV6ADDR(&bulentry->home_addr));
++      nonces[0] = bulentry->rr->home_nonce_index;
++      nonces[1] = bulentry->rr->careof_nonce_index;
++      ops_len = sizeof(struct mipv6_mo_bauth_data) + MIPV6_RR_MAC_LENGTH + 
++                      sizeof(struct mipv6_mo_nonce_indices);
++      if (bulentry->ops) {
++              DEBUG(DBG_WARNING, "Bul entry had existing mobility options, freeing them");
++              kfree(bulentry->ops);
++      }
++      bulentry->ops = alloc_mh_opts(ops_len);
++
++      if (!bulentry->ops)
++              return -ENOMEM;
++      if (append_mh_opt(bulentry->ops, MIPV6_OPT_NONCE_INDICES, 
++                        sizeof(struct mipv6_mo_nonce_indices) - 2, nonces) < 0)
++              return -ENOMEM;
++
++      if (append_mh_opt(bulentry->ops, MIPV6_OPT_AUTH_DATA,
++                        MIPV6_RR_MAC_LENGTH, NULL) < 0)
++              return -ENOMEM;
++      /* RR procedure is over, send a BU */
++      if (!(bulentry->flags & MIPV6_BU_F_ACK)) {
++              DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired");
++              bulentry->state = ACK_OK;
++              bulentry->callback = bul_entry_expired;
++              bulentry->callback_time = jiffies + HZ * bulentry->lifetime;
++              bulentry->expire = jiffies + HZ *  bulentry->lifetime;
++      }
++      else {
++              bulentry->callback_time = jiffies + HZ;
++              bulentry->expire = jiffies + HZ *  bulentry->lifetime;
++      }
++
++      ret  = send_bu_msg(bulentry);
++      mipv6_bul_reschedule(bulentry);
++      return ret;
++}
++
++static int mipv6_rr_state(struct mipv6_bul_entry *bul, struct in6_addr *saddr,
++                        struct in6_addr *coa, __u8 flags)
++{
++      if (!rr_configured)
++              return NO_RR;
++              if (flags & MIPV6_BU_F_HOME) {
++              /* We don't need RR, this is a Home Registration */
++              return NO_RR;
++      }
++      if (!bul || !bul->rr) {
++              /* First time BU to CN, need RR */
++              return DO_RR;
++      }
++
++      switch (bul->rr->rr_state) {
++      case RR_INIT:
++              /* Need RR if first BU to CN */
++              return DO_RR;
++      case RR_DONE:
++              /* If MN moves to a new coa, do RR for it */
++              if (!ipv6_addr_cmp(&bul->coa, coa))  
++                      return NO_RR; 
++              else
++                      return DO_RR;
++      default:
++              /*
++               * We are in the middle of RR, the HoTI and CoTI have been
++               * sent. But we haven't got HoT and CoT from the CN, so
++               * don't do anything more at this time.
++               */
++              return INPROGRESS_RR;
++      }
++}
++
++/**
++ * mipv6_RR_start - Start Return Routability procedure
++ * @home_addr: home address
++ * @cn_addr: correspondent address
++ * @coa: care-of address
++ * @entry: binding update list entry (if any)
++ * @initdelay: initial ack timeout
++ * @maxackdelay: maximum ack timeout
++ * @flags: flags
++ * @lifetime: lifetime of binding
++ * @ops: mobility options
++ *
++ * Caller must hold @bul_lock (write).
++ **/
++static int mipv6_RR_start(struct in6_addr *home_addr, struct in6_addr *cn_addr,
++                        struct in6_addr *coa, struct mipv6_bul_entry *entry,
++                        __u32 initdelay, __u32 maxackdelay, __u8 flags, 
++                        __u32 lifetime, struct mipv6_mh_opt *ops)
++{
++      int ret = -1;
++      struct mipv6_bul_entry *bulentry = entry;
++      struct mipv6_rr_info *rr = NULL;
++      int seq = 0;
++      DEBUG_FUNC();
++      
++      /* Do RR procedure only for care-of address after handoff, 
++         if home cookie is still valid */
++      if (bulentry && bulentry->rr) {
++              if (time_before(jiffies, bulentry->rr->home_time + MAX_NONCE_LIFE * HZ) &&
++                  lifetime && !(ipv6_addr_cmp(home_addr, coa) == 0)) { 
++                      mipv6_rr_mn_cookie_create(bulentry->rr->cot_cookie); 
++                      DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for CoA");
++                      ipv6_addr_copy(&bulentry->coa, coa);
++                      bulentry->rr->rr_state |= RR_WAITC;
++              } else if (!lifetime) { /* Send only HoTi when returning home */
++                      mipv6_rr_mn_cookie_create(bulentry->rr->hot_cookie); 
++                      DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for HoA");
++                      ipv6_addr_copy(&bulentry->coa, coa); /* Home address as CoA */
++                      bulentry->rr->rr_state |= RR_WAITH;
++              }
++      } else {
++              DEBUG(DBG_INFO, "Doing RR for both HoA and CoA");
++              rr = kmalloc(sizeof(*rr), GFP_ATOMIC);
++              memset(rr, 0, sizeof(*rr));
++              rr->rr_state = RR_WAITHC;
++      } 
++      if (bulentry) {
++              if (bulentry->state == ACK_ERROR)
++                      goto out;
++              seq = bulentry->seq + 1;
++      } else
++              seq = 0;
++      /* Save the info in the BUL to retransmit the BU after RR is done */
++      /* Caller must hold bul_lock (write) since we don't */
++       
++      if ((bulentry = mipv6_bul_add(cn_addr, home_addr, coa, 
++                                    min_t(__u32, lifetime, MAX_RR_BINDING_LIFE),
++                                    seq, flags, bul_resend_exp, initdelay, 
++                                    RESEND_EXP, initdelay, 
++                                    maxackdelay, ops, 
++                                    rr)) == NULL) {
++              DEBUG(DBG_INFO, "couldn't update BUL for HoTi");
++              goto out;
++      }
++
++      rr = bulentry->rr; 
++      if (rr->rr_state&RR_WAITH)
++              mipv6_send_addr_test_init(home_addr, cn_addr, MIPV6_MH_HOTI, 
++                                        rr->hot_cookie);
++      if (ipv6_addr_cmp(home_addr, coa) && lifetime) 
++              mipv6_send_addr_test_init(coa, cn_addr, MIPV6_MH_COTI, rr->cot_cookie);
++      else {
++              bulentry->rr->rr_state &= ~RR_WAITC;
++      }
++      ret = 0;
++out:
++      return ret;
++}
++
++/*
++ * Status codes for mipv6_ba_rcvd()
++ */
++#define STATUS_UPDATE 0
++#define STATUS_REMOVE 1
++
++/**
++ * mipv6_ba_rcvd - Update BUL for this Binding Acknowledgement
++ * @ifindex: interface BA came from
++ * @cnaddr: sender IPv6 address
++ * @home_addr: home address
++ * @sequence: sequence number
++ * @lifetime: lifetime granted by Home Agent in seconds
++ * @refresh: recommended resend interval
++ * @status: %STATUS_UPDATE (ack) or %STATUS_REMOVE (nack)
++ *
++ * This function must be called to notify the module of the receipt of
++ * a binding acknowledgement so that it can cease retransmitting the
++ * option. The caller must have validated the acknowledgement before calling
++ * this function. 'status' can be either STATUS_UPDATE in which case the
++ * binding acknowledgement is assumed to be valid and the corresponding
++ * binding update list entry is updated, or STATUS_REMOVE in which case
++ * the corresponding binding update list entry is removed (this can be
++ * used upon receiving a negative acknowledgement).
++ * Returns 0 if a matching binding update has been sent or non-zero if
++ * not.
++ */
++static int mipv6_ba_rcvd(int ifindex, struct in6_addr *cnaddr, 
++                       struct in6_addr *home_addr, 
++                       u16 sequence, u32 lifetime, 
++                       u32 refresh, int status)
++{
++      struct mipv6_bul_entry *bulentry;
++      unsigned long now = jiffies;
++      struct in6_addr coa;
++
++      DEBUG(DBG_INFO, "BA received with sequence number 0x%x, status: %d",
++            (int) sequence, status);
++
++      /* Find corresponding entry in binding update list. */
++      write_lock(&bul_lock);
++      if ((bulentry = mipv6_bul_get(cnaddr, home_addr)) == NULL) {
++              DEBUG(DBG_INFO, "- discarded, no entry in bul matches BA source address");
++              write_unlock(&bul_lock);
++              return -1;
++      }
++      
++      ipv6_addr_copy(&coa, &bulentry->coa); 
++      if (status == SEQUENCE_NUMBER_OUT_OF_WINDOW) {
++              __u32 lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, 
++                                                       &bulentry->coa, 
++                                                       bulentry->flags);
++              bulentry->seq = sequence;
++
++              mipv6_send_bu(&bulentry->home_addr, &bulentry->cn_addr, 
++                            &bulentry->coa, INITIAL_BINDACK_TIMEOUT,
++                            MAX_BINDACK_TIMEOUT, 1, bulentry->flags,
++                            lifetime, NULL);
++              write_unlock(&bul_lock);
++              return 0;
++      } else if (status >= REASON_UNSPECIFIED) {
++              int err;
++              int at_home = MN_NOT_AT_HOME;
++              DEBUG(DBG_WARNING, "- NACK - BA status:  %d, deleting bul entry", status);
++              if (bulentry->flags & MIPV6_BU_F_HOME) {
++                      struct mn_info *minfo;
++                      read_lock(&mn_info_lock);
++                      minfo = mipv6_mninfo_get_by_home(home_addr);
++                      if (minfo) {
++                              spin_lock(&minfo->lock);
++                              if (minfo->is_at_home != MN_NOT_AT_HOME)
++                                      minfo->is_at_home = MN_AT_HOME;
++                              at_home = minfo->is_at_home;
++                              minfo->has_home_reg = 0;
++                              spin_unlock(&minfo->lock);
++                      }
++                      read_unlock(&mn_info_lock);
++                      DEBUG(DBG_ERROR, "Home registration failed: BA status:  %d, deleting bul entry", status);
++              }
++              write_unlock(&bul_lock);
++              err = mipv6_bul_delete(cnaddr, home_addr);
++              if (at_home == MN_AT_HOME) {
++                      mipv6_mn_send_home_na(home_addr);
++                      write_lock_bh(&bul_lock);
++                      mipv6_bul_iterate(mn_cn_handoff, &coa);
++                      write_unlock_bh(&bul_lock);
++              }
++              return err;
++      }
++      bulentry->state = ACK_OK;
++
++      if (bulentry->flags & MIPV6_BU_F_HOME && lifetime > 0) {
++              /* For home registrations: schedule a refresh binding update.
++               * Use the refresh interval given by home agent or 80%
++               * of lifetime, whichever is less.
++               *
++               * Adjust binding lifetime if 'granted' lifetime
++               * (lifetime value in received binding acknowledgement)
++               * is shorter than 'requested' lifetime (lifetime
++               * value sent in corresponding binding update).
++               * max((L_remain - (L_update - L_ack)), 0)
++               */
++              if (lifetime * HZ < (bulentry->expire - bulentry->lastsend)) {
++                      bulentry->expire = 
++                              max_t(__u32, bulentry->expire - 
++                                    ((bulentry->expire - bulentry->lastsend) - 
++                                     lifetime * HZ), jiffies + 
++                                    ERROR_DEF_LIFETIME * HZ);
++              }
++              if (refresh > lifetime || refresh == 0)
++                      refresh = 4 * lifetime / 5;
++                      DEBUG(DBG_INFO, "setting callback for expiration of"
++                            " a Home Registration: lifetime:%d, refresh:%d",
++                            lifetime, refresh);
++              bulentry->callback = bul_refresh;
++              bulentry->callback_time = now + refresh * HZ;
++              bulentry->expire = now + lifetime * HZ;
++              bulentry->lifetime = lifetime;
++              if (time_after_eq(jiffies, bulentry->expire)) {
++                      /* Sanity check */
++                      DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs",
++                            ERROR_DEF_LIFETIME);
++                      bulentry->expire = jiffies + ERROR_DEF_LIFETIME * HZ;
++              }
++              mipv6_mn_set_home_reg(home_addr, 1);
++              mipv6_bul_iterate(mn_cn_handoff, &coa);
++      } else if ((bulentry->flags & MIPV6_BU_F_HOME) && bulentry->lifetime == 0) {
++              write_unlock(&bul_lock);
++              DEBUG(DBG_INFO, "Got BA for deregistration BU");
++              mipv6_mn_set_home_reg(home_addr, 0);
++              mipv6_bul_delete(cnaddr, home_addr);
++              mipv6_mn_send_home_na(home_addr);
++
++              write_lock_bh(&bul_lock);
++              mipv6_bul_iterate(mn_cn_handoff, &coa);
++              write_unlock_bh(&bul_lock);
++              return 0;
++      }
++
++      mipv6_bul_reschedule(bulentry);
++      write_unlock(&bul_lock);
++
++      return 0;
++}
++
++static int mipv6_handle_mh_HC_test(struct sk_buff *skb,
++                                 struct in6_addr *saddr,
++                                 struct in6_addr *fcoa,
++                                 struct in6_addr *cn,
++                                 struct in6_addr *lcoa,
++                                 struct mipv6_mh *mh)
++{
++      int ret = 0;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++
++      struct mipv6_mh_addr_test *tm = (struct mipv6_mh_addr_test *)mh->data;
++      struct mipv6_bul_entry *bulentry;
++
++      DEBUG_FUNC();
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*tm);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_INFO, "Mobility Header length less than H/C Test");
++              return -1;
++      }
++      if (fcoa || lcoa) {
++              DEBUG(DBG_INFO, "H/C Test has HAO or RTH2, dropped.");
++              return -1;
++      }
++      write_lock(&bul_lock);
++
++      /* We need to get the home address, since CoT only has the CoA*/
++      if (mh->type == MIPV6_MH_COT) {
++              if ((bulentry = mipv6_bul_get_by_ccookie(cn, tm->init_cookie)) == NULL) {
++                      DEBUG(DBG_ERROR, "has no BUL or RR state for "
++                            "source:%x:%x:%x:%x:%x:%x:%x:%x",
++                            NIPV6ADDR(cn));
++                      write_unlock(&bul_lock);
++                      return -1;
++              }
++      } else { /* HoT has the home address */
++              if (((bulentry = mipv6_bul_get(cn, saddr)) == NULL) || !bulentry->rr) {
++                      DEBUG(DBG_ERROR, "has no BUL or RR state for "
++                            "source:%x:%x:%x:%x:%x:%x:%x:%x "
++                            "dest:%x:%x:%x:%x:%x:%x:%x:%x",
++                            NIPV6ADDR(cn), NIPV6ADDR(saddr));
++                      write_unlock(&bul_lock);
++                      return -1;
++              }
++      }
++
++      switch (mh->type) {
++      case MIPV6_MH_HOT:
++              if ((bulentry->rr->rr_state & RR_WAITH) == 0) {
++                      DEBUG(DBG_ERROR, "Not waiting for a Home Test message");
++                      goto out;
++              }
++              /*
++               * Make sure no home cookies have been received yet.
++               * TODO: Check not being put in at this time since subsequent
++               * BU's after this time will have home cookie stored.
++               */
++      
++              /* Check if the cookie received is the right one */
++              if (!mipv6_equal_cookies(tm->init_cookie,
++                                       bulentry->rr->hot_cookie)) {
++                      /* Invalid cookie, might be an old cookie */
++                      DEBUG(DBG_WARNING, "Received HoT cookie does not match stored cookie");
++                      goto out;
++              }
++              DEBUG(DBG_INFO, "Got Care-of Test message");
++              bulentry->rr->rr_state &= ~RR_WAITH;
++              memcpy(bulentry->rr->home_cookie, tm->kgen_token, MIPV6_COOKIE_LEN);
++              bulentry->rr->home_nonce_index = tm->nonce_index;
++              bulentry->rr->home_time = jiffies;
++              ret = 1;
++              break;
++
++      case MIPV6_MH_COT:
++              if ((bulentry->rr->rr_state & RR_WAITC) == 0) {
++                      DEBUG(DBG_ERROR, "Not waiting for a Home Test message");
++                      goto out;
++              }
++              /*
++               * Make sure no home cookies have been received yet.
++               * TODO: Check not being put in at this time since subsequent
++               * BU's at this time will have careof cookie stored.
++               */
++      
++              /* Check if the cookie received is the right one */
++              if (!mipv6_equal_cookies(tm->init_cookie,
++                                       bulentry->rr->cot_cookie)) {
++                      DEBUG(DBG_INFO, "Received CoT cookie does not match stored cookie");
++                      goto out;
++              }
++              bulentry->rr->rr_state &= ~RR_WAITC;
++              memcpy(bulentry->rr->careof_cookie, tm->kgen_token, MIPV6_COOKIE_LEN);
++              bulentry->rr->careof_nonce_index = tm->nonce_index;
++              bulentry->rr->careof_time = jiffies;
++              ret = 1;
++              break;
++      default:
++              /* Impossible to get here */
++              break;
++      }
++out:
++      if (bulentry->rr->rr_state == RR_DONE) {
++              if (bulentry->rr->kbu) /* First free any old keys */
++                      kfree(bulentry->rr->kbu);
++              /* Store the session key to be used in BU's */
++              if (ipv6_addr_cmp(&bulentry->coa, &bulentry->home_addr) && bulentry->lifetime)
++                      bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie,
++                                                            bulentry->rr->careof_cookie);
++              else 
++                      bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie,
++                                                            NULL);
++              /* RR procedure is over, send a BU */
++              mipv6_send_RR_bu(bulentry);
++      }
++      write_unlock(&bul_lock);
++      return ret;
++}
++
++/**
++ * mipv6_handle_mh_brr - Binding Refresh Request handler
++ * @home: home address
++ * @coa: care-of address
++ * @cn: source of this packet
++ * @mh: pointer to the beginning of the Mobility Header
++ *
++ * Handles Binding Refresh Request.  Packet and offset to option are
++ * passed.  Returns 0 on success, otherwise negative.
++ **/
++static int mipv6_handle_mh_brr(struct sk_buff *skb,
++                             struct in6_addr *home,
++                             struct in6_addr *unused1,
++                             struct in6_addr *cn,
++                             struct in6_addr *unused2,
++                             struct mipv6_mh *mh)
++{
++      struct mipv6_mh_brr *brr = (struct mipv6_mh_brr *)mh->data;
++      struct mipv6_bul_entry *binding;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*brr);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_WARNING, "Mobility Header length less than BRR");
++              MIPV6_INC_STATS(n_brr_drop.invalid);
++              return -1;
++      }
++
++      /* check we know src, else drop */
++      write_lock(&bul_lock);
++      if ((binding = mipv6_bul_get(cn, home)) == NULL) {
++              MIPV6_INC_STATS(n_brr_drop.misc);
++              write_unlock(&bul_lock);
++              return MH_UNKNOWN_CN;
++      }
++
++      MIPV6_INC_STATS(n_brr_rcvd);
++
++      if (opt_len > 0) {
++              struct mobopt opts;
++              memset(&opts, 0, sizeof(opts));
++              if (parse_mo_tlv(brr + 1, opt_len, &opts) < 0) {
++                      write_unlock(&bul_lock);
++                      return -1;
++              }
++              /*
++               * MIPV6_OPT_AUTH_DATA
++               */
++      }
++
++      /* must hold bul_lock (write) */
++      mipv6_RR_start(home, cn, &binding->coa, binding, binding->delay, 
++                     binding->maxdelay, binding->flags,
++                     binding->lifetime, binding->ops);
++
++      write_unlock(&bul_lock);
++      /* MAY also decide to delete binding and send zero lifetime BU
++           with alt-coa set to home address */
++
++      return 0;
++}
++
++/**
++ * mipv6_handle_mh_ba - Binding Acknowledgement handler
++ * @src: source of this packet
++ * @coa: care-of address
++ * @home: home address
++ * @mh: pointer to the beginning of the Mobility Header
++ *
++ **/
++static int mipv6_handle_mh_ba(struct sk_buff *skb,
++                            struct in6_addr *home,
++                            struct in6_addr *coa,
++                            struct in6_addr *src,
++                            struct in6_addr *unused,
++                            struct mipv6_mh *mh)
++{
++      struct mipv6_mh_ba *ba = (struct mipv6_mh_ba *)mh->data;
++      struct mipv6_bul_entry *binding = NULL;
++      struct mobopt opts;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++
++      int auth = 1, req_auth = 1, refresh = -1, ifindex = 0;
++      u32 lifetime, sequence;
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*ba);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_WARNING, "Mobility Header length less than BA");
++              MIPV6_INC_STATS(n_ba_drop.invalid);
++              return -1;
++      }
++
++      lifetime = ntohs(ba->lifetime) << 2;
++      sequence = ntohs(ba->sequence);
++
++      if (opt_len > 0) {
++              memset(&opts, 0, sizeof(opts));
++              if (parse_mo_tlv(ba + 1, opt_len, &opts) < 0)
++                      return -1;
++              /*
++               * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_BR_ADVICE
++               */
++              if (opts.br_advice)
++                      refresh = ntohs(opts.br_advice->refresh_interval);
++      }
++
++      if (ba->status >= EXPIRED_HOME_NONCE_INDEX && 
++          ba->status <= EXPIRED_NONCES) 
++              req_auth = 0;
++      
++      write_lock(&bul_lock);
++      binding = mipv6_bul_get(src, home);
++      if (!binding) {
++              DEBUG(DBG_INFO, "No binding, BA dropped.");
++              write_unlock(&bul_lock);
++              return -1;
++      }
++
++      if (opts.auth_data && binding->rr && 
++          (mipv6_auth_check(src, coa, (__u8 *)mh, msg_len, 
++                            opts.auth_data, binding->rr->kbu) == 0))
++              auth = 1;
++
++      if (req_auth && binding->rr && !auth) {
++              DEBUG(DBG_INFO, "BA Authentication failed.");
++              MIPV6_INC_STATS(n_ba_drop.auth);
++              write_unlock(&bul_lock);
++              return MH_AUTH_FAILED;
++      }
++
++      if (ba->status == SEQUENCE_NUMBER_OUT_OF_WINDOW) {
++              DEBUG(DBG_INFO,
++                    "Sequence number out of window, setting seq to %d",
++                    sequence);
++      } else if (binding->seq != sequence) {
++              DEBUG(DBG_INFO, "BU/BA Sequence Number mismatch %d != %d",
++                    binding->seq, sequence);
++              MIPV6_INC_STATS(n_ba_drop.invalid);
++              write_unlock(&bul_lock);
++              return MH_SEQUENCE_MISMATCH;
++      }
++      if (ba->status == EXPIRED_HOME_NONCE_INDEX || ba->status == EXPIRED_NONCES) {
++              if (binding->rr) {
++                      /* Need to resend home test init to CN */
++                      binding->rr->rr_state |= RR_WAITH;
++                      mipv6_send_addr_test_init(&binding->home_addr, 
++                                                &binding->cn_addr, 
++                                                MIPV6_MH_HOTI,
++                                                binding->rr->hot_cookie);
++                      MIPV6_INC_STATS(n_ban_rcvd);
++              } else {
++                      DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_NONCE_INDEX"
++                            "for non-RR BU");
++                      MIPV6_INC_STATS(n_ba_drop.invalid);
++              }
++              write_unlock(&bul_lock);
++              return 0;
++      } 
++      if (ba->status == EXPIRED_CAREOF_NONCE_INDEX || ba->status == EXPIRED_NONCES) {
++              if (binding->rr) { 
++                      /* Need to resend care-of test init to CN */
++                      binding->rr->rr_state |= RR_WAITC;
++                      mipv6_send_addr_test_init(&binding->coa, 
++                                                &binding->cn_addr, 
++                                                MIPV6_MH_COTI,
++                                                binding->rr->cot_cookie);
++                      MIPV6_INC_STATS(n_ban_rcvd);
++              } else  {
++                      DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_CAREOF_INDEX"
++                            "for non-RR BU");
++                      MIPV6_INC_STATS(n_ba_drop.invalid);
++              }
++              write_unlock(&bul_lock);
++              return 0;
++      }
++      write_unlock(&bul_lock);
++      
++      if (ba->status >= REASON_UNSPECIFIED) {
++              DEBUG(DBG_INFO, "Binding Ack status : %d indicates error", ba->status);
++              mipv6_ba_rcvd(ifindex, src, home, sequence, lifetime,
++                            refresh, ba->status);
++              MIPV6_INC_STATS(n_ban_rcvd);
++              return 0;
++      }
++      MIPV6_INC_STATS(n_ba_rcvd);
++      if (mipv6_ba_rcvd(ifindex, src, home, ntohs(ba->sequence), lifetime,
++                        refresh, ba->status)) {
++              DEBUG(DBG_WARNING, "mipv6_ba_rcvd failed");
++      }
++      
++      return 0;
++}
++
++/**
++ * mipv6_handle_mh_be - Binding Error handler
++ * @cn: source of this packet
++ * @coa: care-of address
++ * @home: home address
++ * @mh: pointer to the beginning of the Mobility Header
++ *
++ **/
++
++static int mipv6_handle_mh_be(struct sk_buff *skb,
++                            struct in6_addr *home,
++                            struct in6_addr *coa,
++                            struct in6_addr *cn,
++                            struct in6_addr *unused,
++                            struct mipv6_mh *mh)
++{
++      struct mipv6_mh_be *be = (struct mipv6_mh_be *)mh->data;
++      int msg_len = (mh->length+1) << 3;
++      int opt_len;
++      struct in6_addr *hoa;
++      struct bul_inval_args args;
++
++      DEBUG_FUNC();
++
++      if (msg_len > skb->len)
++              return -1;
++
++      opt_len = msg_len - sizeof(*mh) - sizeof(*be);
++
++      if (opt_len < 0) {
++              __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw;
++              icmpv6_send(skb, ICMPV6_PARAMPROB,
++                          ICMPV6_HDR_FIELD, pos, skb->dev);
++
++              DEBUG(DBG_WARNING, "Mobility Header length less than BE");
++              MIPV6_INC_STATS(n_be_drop.invalid);
++              return -1;
++      }
++
++      
++      if (!ipv6_addr_any(&be->home_addr))
++              hoa = &be->home_addr;
++      else
++              hoa = home;
++
++      MIPV6_INC_STATS(n_be_rcvd);
++
++      args.all_rr_states = 0;
++      args.cn = cn;
++      args.mn = hoa;
++
++      switch (be->status) {
++      case 1: /* Home Address Option used without a binding */
++              /* Get ULP information about CN-MN communication.  If
++                   nothing in progress, MUST delete.  Otherwise MAY
++                   ignore. */
++              args.all_rr_states = 1;
++      case 2: /* Received unknown MH type */
++              /* If not expecting ack, SHOULD ignore.  If MH
++                   extension in use, stop it.  If not, stop RO for
++                   this CN. */
++              write_lock(&bul_lock);
++              mipv6_bul_iterate(mn_bul_invalidate, &args);
++              write_unlock(&bul_lock);
++              break;
++      }
++
++      return 0;
++}
++
++/*
++ * mipv6_bu_rate_limit() : Takes a bulentry, a COA and 'flags' to check
++ * whether BU being sent is for Home Registration or not.
++ *
++ * If the number of BU's sent is fewer than MAX_FAST_UPDATES, this BU
++ * is allowed to be sent at the MAX_UPDATE_RATE.
++ * If the number of BU's sent is greater than or equal to MAX_FAST_UPDATES,
++ * this BU is allowed to be sent at the SLOW_UPDATE_RATE.
++ *
++ * Assumption : This function is not re-entrant. and the caller holds the
++ * bulentry lock (by calling mipv6_bul_get()) to stop races with other
++ * CPU's executing this same function.
++ *
++ * Side-Effects. Either of the following could  on success :
++ *    1. Sets consecutive_sends to 1 if the entry is a Home agent
++ *       registration or the COA has changed.
++ *    2. Increments consecutive_sends if the number of BU's sent so
++ *       far is less than MAX_FAST_UPDATES, and this BU is being sent
++ *       atleast MAX_UPDATE_RATE after previous one.
++ * 
++ * Return Value : 0 on Success, -1 on Failure
++ */
++static int mipv6_bu_rate_limit(struct mipv6_bul_entry *bulentry, 
++                             struct in6_addr *coa, __u8 flags)
++{
++      if ((flags & MIPV6_BU_F_HOME) || ipv6_addr_cmp(&bulentry->coa, coa)) {
++              /* Home Agent Registration or different COA - restart from 1 */
++              bulentry->consecutive_sends = 1;
++              return 0;
++      }
++
++      if (bulentry->consecutive_sends < MAX_FAST_UPDATES) {
++              /* First MAX_FAST_UPDATES can be sent at MAX_UPDATE_RATE */
++              if (jiffies - bulentry->lastsend < MAX_UPDATE_RATE * HZ) {
++                      return -1;
++              }
++              bulentry->consecutive_sends ++;
++      } else {
++              /* Remaining updates SHOULD be sent at SLOW_UPDATE_RATE */
++              if (jiffies - bulentry->lastsend < SLOW_UPDATE_RATE * HZ) {
++                      return -1;
++              }
++              /* Don't inc 'consecutive_sends' to avoid overflow to zero */
++      }
++      /* OK to send a BU */
++      return 0;
++}
++
++/**
++ * mipv6_send_bu - send a Binding Update 
++ * @saddr: source address for BU
++ * @daddr: destination address for BU
++ * @coa: care-of address for MN
++ * @initdelay: initial BA wait timeout
++ * @maxackdelay: maximum BA wait timeout
++ * @exp: exponention back off
++ * @flags: flags for BU
++ * @lifetime: granted lifetime for binding
++ * @ops: mobility options
++ *
++ * Send a binding update.  'flags' may contain any of %MIPV6_BU_F_ACK,
++ * %MIPV6_BU_F_HOME, %MIPV6_BU_F_ROUTER bitwise ORed.  If
++ * %MIPV6_BU_F_ACK is included retransmission will be attempted until
++ * the update has been acknowledged.  Retransmission is done if no
++ * acknowledgement is received within @initdelay seconds.  @exp
++ * specifies whether to use exponential backoff (@exp != 0) or linear
++ * backoff (@exp == 0).  For exponential backoff the time to wait for
++ * an acknowledgement is doubled on each retransmission until a delay
++ * of @maxackdelay, after which retransmission is no longer attempted.
++ * For linear backoff the delay is kept constant and @maxackdelay
++ * specifies the maximum number of retransmissions instead.  If
++ * sub-options are present ops must contain all sub-options to be
++ * added.  On a mobile node, use the mobile node's home address for
++ * @saddr.  Returns 0 on success, non-zero on failure.
++ *
++ * Caller may not hold @bul_lock.
++ **/
++int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr,
++                struct in6_addr *coa, u32 initdelay, 
++                u32 maxackdelay, u8 exp, u8 flags, u32 lifetime,
++                struct mipv6_mh_opt *ops)
++{
++      int ret;
++      __u8 state;
++       __u16 seq = 0;
++      int (*callback)(struct mipv6_bul_entry *);
++      __u32 callback_time;
++      struct mipv6_bul_entry *bulentry;
++      
++      /* First a sanity check: don't send BU to local addresses */
++      if(ipv6_chk_addr(daddr, NULL)) {
++              DEBUG(DBG_ERROR, "BUG: Trying to send BU to local address");
++              return -1;
++      }
++      DEBUG(DBG_INFO, "Sending BU to CN  %x:%x:%x:%x:%x:%x:%x:%x "
++            "for home address %x:%x:%x:%x:%x:%x:%x:%x", 
++            NIPV6ADDR(daddr), NIPV6ADDR(saddr));
++
++      if ((bulentry = mipv6_bul_get(daddr, saddr)) != NULL) {
++              if (bulentry->state == ACK_ERROR) {
++                      /*
++                       * Don't send any more BU's to nodes which don't
++                       * understanding one. 
++                       */
++                      DEBUG(DBG_INFO, "Not sending BU to node which doesn't"
++                            " understand one");
++                      return -1;
++              }
++              if (mipv6_bu_rate_limit(bulentry, coa, flags) < 0) {
++                      DEBUG(DBG_DATADUMP, "Limiting BU sent.");
++                      return 0;
++              }
++      }
++
++      switch (mipv6_rr_state(bulentry, saddr, coa, flags)) {
++      case INPROGRESS_RR:
++              /* We are already doing RR, don't do BU at this time, it is
++               * done automatically later */
++              DEBUG(DBG_INFO, "RR in progress not sending BU");
++              return 0;
++
++      case DO_RR:
++              /* Just do RR and return, BU is done automatically later */
++              DEBUG(DBG_INFO, "starting RR" );
++              mipv6_RR_start(saddr, daddr, coa, bulentry, initdelay,
++                             maxackdelay, flags, lifetime, ops);
++              return 0;
++              
++      case NO_RR:
++              DEBUG(DBG_DATADUMP, "No RR necessary" );
++      default:
++              break;
++      }
++
++      if (bulentry)
++              seq = bulentry->seq + 1;
++      
++      /* Add to binding update list */
++      
++      if (flags & MIPV6_BU_F_ACK) {
++              DEBUG(DBG_INFO, "Setting bul callback to bul_resend_exp");
++              /* Send using exponential backoff */
++              state = RESEND_EXP;
++              callback = bul_resend_exp;
++              callback_time = initdelay;
++      } else {
++              DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired");
++              /* No acknowledgement/resending required */
++              state = ACK_OK; /* pretend we got an ack */
++              callback = bul_entry_expired;
++              callback_time = lifetime;
++      }
++
++      /* BU only for the home address */
++      /* We must hold bul_lock (write) while calling add */
++      if ((bulentry = mipv6_bul_add(daddr, saddr, coa, lifetime, seq,
++                                    flags, callback, callback_time, 
++                                    state, initdelay, maxackdelay, ops, 
++                                    NULL)) == NULL) {
++              DEBUG(DBG_INFO, "couldn't update BUL");
++              return 0;
++      }
++      ret = send_bu_msg(bulentry);
++
++      return ret;
++}
++
++int __init mipv6_mh_mn_init(void)
++{
++      mipv6_mh_register(MIPV6_MH_HOT, mipv6_handle_mh_HC_test);
++      mipv6_mh_register(MIPV6_MH_COT, mipv6_handle_mh_HC_test);
++      mipv6_mh_register(MIPV6_MH_BA, mipv6_handle_mh_ba);
++      mipv6_mh_register(MIPV6_MH_BRR, mipv6_handle_mh_brr);
++      mipv6_mh_register(MIPV6_MH_BE, mipv6_handle_mh_be);
++
++      return 0;
++}
++
++void __exit mipv6_mh_mn_exit(void)
++{
++      mipv6_mh_unregister(MIPV6_MH_HOT);
++      mipv6_mh_unregister(MIPV6_MH_COT);
++      mipv6_mh_unregister(MIPV6_MH_BA);
++      mipv6_mh_unregister(MIPV6_MH_BRR);
++      mipv6_mh_unregister(MIPV6_MH_BE);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/module_cn.c
+@@ -0,0 +1,167 @@
++/*
++ *    Mobile IPv6 Common Module
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Antti Tuominen          <ajtuomin@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License
++ *    as published by the Free Software Foundation; either version
++ *    2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include <net/mipglue.h>
++
++#include "bcache.h"
++#include "mipv6_icmp.h"
++#include "stats.h"
++#include "mobhdr.h"
++#include "exthdrs.h"
++
++int mipv6_debug = 1;
++
++#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
++MODULE_AUTHOR("MIPL Team");
++MODULE_DESCRIPTION("Mobile IPv6");
++MODULE_LICENSE("GPL");
++MODULE_PARM(mipv6_debug, "i");
++#endif
++
++#include "config.h"
++
++struct mip6_func mip6_fn;
++struct mip6_conf mip6node_cnf = {
++      capabilities:           CAP_CN,
++      accept_ret_rout:        1,
++      max_rtr_reachable_time: 0,
++      eager_cell_switching:   0,
++      max_num_tunnels:        0,
++      min_num_tunnels:        0,
++      binding_refresh_advice: 0,
++      bu_lladdr:              0,
++      bu_keymgm:              0,
++      bu_cn_ack:              0
++};
++
++#define MIPV6_BCACHE_SIZE 128
++
++/**********************************************************************
++ *
++ * MIPv6 CN Module Init / Cleanup
++ *
++ **********************************************************************/
++
++#ifdef CONFIG_SYSCTL
++/* Sysctl table */
++ctl_table mipv6_mobility_table[] = {
++      {NET_IPV6_MOBILITY_DEBUG, "debuglevel",
++       &mipv6_debug, sizeof(int), 0644, NULL,
++       &proc_dointvec},
++      {NET_IPV6_MOBILITY_RETROUT, "accept_return_routability",
++       &mip6node_cnf.accept_ret_rout, sizeof(int), 0644, NULL,
++       &proc_dointvec},
++      {0}
++};
++ctl_table mipv6_table[] = {
++      {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table},
++      {0}
++};
++
++static struct ctl_table_header *mipv6_sysctl_header;
++static struct ctl_table mipv6_net_table[];
++static struct ctl_table mipv6_root_table[];
++
++ctl_table mipv6_net_table[] = {
++      {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table},
++      {0}
++};
++
++ctl_table mipv6_root_table[] = {
++      {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table},
++      {0}
++};
++#endif /* CONFIG_SYSCTL */
++
++extern void mipv6_rr_init(void);
++
++/*  Initialize the module  */
++static int __init mip6_init(void)
++{
++      int err = 0;
++
++      printk(KERN_INFO "MIPL Mobile IPv6 for Linux Correspondent Node %s (%s)\n",
++             MIPLVERSION, MIPV6VERSION);
++
++#ifdef CONFIG_IPV6_MOBILITY_DEBUG
++      printk(KERN_INFO "Debug-level: %d\n", mipv6_debug);
++#endif
++
++      if ((err = mipv6_bcache_init(MIPV6_BCACHE_SIZE)) < 0)
++              goto bcache_fail;
++
++      if ((err = mipv6_icmpv6_init()) < 0)
++              goto icmp_fail;
++
++      if ((err = mipv6_stats_init()) < 0)
++              goto stats_fail;
++      mipv6_rr_init();
++
++#ifdef CONFIG_SYSCTL
++      mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0);
++#endif
++
++      if ((err = mipv6_mh_common_init()) < 0)
++              goto mh_fail;
++
++      MIPV6_SETCALL(mipv6_modify_txoptions, mipv6_modify_txoptions);
++              
++      MIPV6_SETCALL(mipv6_handle_homeaddr, mipv6_handle_homeaddr);
++      MIPV6_SETCALL(mipv6_icmp_swap_addrs, mipv6_icmp_swap_addrs);
++
++      return 0;
++
++mh_fail:
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++      mipv6_stats_exit();
++stats_fail:
++      mipv6_icmpv6_exit();
++icmp_fail:
++      mipv6_bcache_exit();
++bcache_fail:
++      return err;
++}
++module_init(mip6_init);
++
++#ifdef MODULE
++/*  Cleanup module  */
++static void __exit mip6_exit(void)
++{
++      printk(KERN_INFO "mip6_base.o exiting.\n");
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++
++      /* Invalidate all custom kernel hooks.  No need to do this
++           separately for all hooks. */
++      mipv6_invalidate_calls();
++
++      mipv6_mh_common_exit();
++      mipv6_stats_exit();
++      mipv6_icmpv6_exit();
++      mipv6_bcache_exit();
++}
++module_exit(mip6_exit);
++#endif /* MODULE */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/module_ha.c
+@@ -0,0 +1,264 @@
++/*
++ *    Mobile IPv6 Home Agent Module
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Antti Tuominen          <ajtuomin@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License
++ *    as published by the Free Software Foundation; either version
++ *    2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include <net/mipglue.h>
++#include <net/addrconf.h>
++
++#include "mobhdr.h"
++#include "tunnel_ha.h"
++#include "ha.h"
++#include "halist.h"
++#include "mipv6_icmp.h"
++//#include "prefix.h"
++#include "bcache.h"
++#include "debug.h"
++
++int mipv6_use_auth = 0;
++
++#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
++MODULE_AUTHOR("MIPL Team");
++MODULE_DESCRIPTION("Mobile IPv6 Home Agent");
++MODULE_LICENSE("GPL");
++#endif
++
++#include "config.h"
++
++#define MIPV6_HALIST_SIZE 128
++struct ha_info_opt {
++      u8 type;
++      u8 len;
++      u16 res;
++      u16 pref;
++      u16 ltime;
++};
++/*
++ * Called from ndisc.c's router_discovery.
++ */
++static int mipv6_ha_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts)
++{
++      unsigned int ha_info_pref = 0, ha_info_lifetime;
++      int ifi = ((struct inet6_skb_parm *)skb->cb)->iif;
++      struct ra_msg *ra = (struct ra_msg *) skb->h.raw;
++      struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
++      struct in6_addr ll_addr;
++      struct hal {
++              struct in6_addr prefix;
++              int plen;
++              struct hal *next;
++      };
++      
++      DEBUG_FUNC();
++
++      ha_info_lifetime = ntohs(ra->icmph.icmp6_rt_lifetime);
++      ipv6_addr_copy(&ll_addr, saddr);
++      
++      if (ndopts->nd_opts_hai) {
++              struct ha_info_opt *hai = (struct ha_info_opt *)ndopts->nd_opts_hai;
++              ha_info_pref = ntohs(hai->pref);
++              ha_info_lifetime = ntohs(hai->ltime);
++              DEBUG(DBG_DATADUMP,
++                    "received home agent info with preference : %d and lifetime : %d",
++                    ha_info_pref, ha_info_lifetime);
++      }
++      if (ndopts->nd_opts_pi) {
++              struct nd_opt_hdr *p;
++              for (p = ndopts->nd_opts_pi;
++                   p;
++                   p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) {
++                      struct prefix_info *pinfo;
++                      
++                      pinfo = (struct prefix_info *) p;
++                      
++                      if (pinfo->router_address) {
++                              DEBUG(DBG_DATADUMP, "Adding router address to "
++                                    "ha queue \n");
++                              /* If RA has H bit set and Prefix Info
++                               * Option R bit set, queue this
++                               * address to be added to Home Agents
++                               * List.  
++                               */
++                              if (ipv6_addr_type(&pinfo->prefix) &
++                                  IPV6_ADDR_LINKLOCAL)
++                                      continue;
++                              if (!ra->icmph.icmp6_home_agent || !ha_info_lifetime) {
++                                      mipv6_halist_delete(&pinfo->prefix); 
++                                      continue;
++                              } else {
++                                      
++                                      mipv6_halist_add(ifi, &pinfo->prefix, 
++                                                       pinfo->prefix_len, &ll_addr, 
++                                                       ha_info_pref, ha_info_lifetime);
++                              } 
++                              
++                      }
++                      
++              }
++      }
++      return MIPV6_ADD_RTR;
++}
++
++/**********************************************************************
++ *
++ * MIPv6 Module Init / Cleanup
++ *
++ **********************************************************************/
++
++#ifdef CONFIG_SYSCTL
++/* Sysctl table */
++extern int 
++mipv6_max_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *);
++
++extern int 
++mipv6_min_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *);
++
++int max_adv = ~(u16)0;
++int min_zero = 0;
++ctl_table mipv6_mobility_table[] = {
++      {NET_IPV6_MOBILITY_BINDING_REFRESH, "binding_refresh_advice",
++       &mip6node_cnf.binding_refresh_advice, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_adv},
++
++      {NET_IPV6_MOBILITY_MAX_TNLS, "max_tnls", &mipv6_max_tnls, sizeof(int),
++       0644, NULL, &mipv6_max_tnls_sysctl},
++      {NET_IPV6_MOBILITY_MIN_TNLS, "min_tnls", &mipv6_min_tnls, sizeof(int),
++       0644, NULL, &mipv6_min_tnls_sysctl},
++      {0}
++};
++ctl_table mipv6_table[] = {
++      {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table},
++      {0}
++};
++
++static struct ctl_table_header *mipv6_sysctl_header;
++static struct ctl_table mipv6_net_table[];
++static struct ctl_table mipv6_root_table[];
++
++ctl_table mipv6_net_table[] = {
++      {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table},
++      {0}
++};
++
++ctl_table mipv6_root_table[] = {
++      {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table},
++      {0}
++};
++#endif /* CONFIG_SYSCTL */
++
++extern void mipv6_check_dad(struct in6_addr *haddr);
++extern void mipv6_dad_init(void);
++extern void mipv6_dad_exit(void);
++extern int mipv6_forward(struct sk_buff *);
++
++/*  Initialize the module  */
++static int __init mip6_ha_init(void)
++{
++      int err = 0;
++
++      printk(KERN_INFO "MIPL Mobile IPv6 for Linux Home Agent %s (%s)\n",
++             MIPLVERSION, MIPV6VERSION);
++      mip6node_cnf.capabilities = CAP_CN | CAP_HA;
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_rcv_dhaad_req;
++      mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_no_rcv;
++
++#ifdef CONFIG_IPV6_MOBILITY_DEBUG
++      printk(KERN_INFO "Debug-level: %d\n", mipv6_debug);
++#endif
++
++#ifdef CONFIG_SYSCTL
++      mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0);
++#endif
++      mipv6_initialize_tunnel();
++
++      if ((err = mipv6_ha_init()) < 0)
++              goto ha_fail;
++
++      MIPV6_SETCALL(mipv6_ra_rcv, mipv6_ha_ra_rcv);
++      MIPV6_SETCALL(mipv6_forward, mipv6_forward);
++      mipv6_dad_init();
++      MIPV6_SETCALL(mipv6_check_dad, mipv6_check_dad);
++
++      if ((err = mipv6_halist_init(MIPV6_HALIST_SIZE)) < 0)
++              goto halist_fail;
++
++//    mipv6_initialize_pfx_icmpv6();
++
++      return 0;
++
++halist_fail:
++      mipv6_dad_exit();
++      mipv6_ha_exit();
++ha_fail:
++      mipv6_shutdown_tunnel();
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = NULL;
++      mip6_fn.icmpv6_dhaad_req_rcv = NULL;
++      mip6_fn.icmpv6_pfxadv_rcv = NULL;
++      mip6_fn.icmpv6_pfxsol_rcv = NULL;
++      mip6_fn.icmpv6_paramprob_rcv = NULL;
++
++      MIPV6_RESETCALL(mipv6_ra_rcv);
++      MIPV6_RESETCALL(mipv6_forward);
++      MIPV6_RESETCALL(mipv6_check_dad);
++
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++      return err;
++}
++module_init(mip6_ha_init);
++
++#ifdef MODULE
++/*  Cleanup module  */
++static void __exit mip6_ha_exit(void)
++{
++      printk(KERN_INFO "mip6_ha.o exiting.\n");
++      mip6node_cnf.capabilities &= ~(int)CAP_HA;
++
++      mipv6_bcache_cleanup(HOME_REGISTRATION);
++
++      MIPV6_RESETCALL(mipv6_ra_rcv);
++      MIPV6_RESETCALL(mipv6_forward);
++      MIPV6_RESETCALL(mipv6_check_dad);
++
++      mipv6_halist_exit();
++//    mipv6_shutdown_pfx_icmpv6();
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = NULL;
++      mip6_fn.icmpv6_dhaad_req_rcv = NULL;
++      mip6_fn.icmpv6_pfxadv_rcv = NULL;
++      mip6_fn.icmpv6_pfxsol_rcv = NULL;
++      mip6_fn.icmpv6_paramprob_rcv = NULL;
++
++      mipv6_dad_exit();
++      mipv6_ha_exit();
++      mipv6_shutdown_tunnel();
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++}
++module_exit(mip6_ha_exit);
++#endif /* MODULE */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/module_mn.c
+@@ -0,0 +1,188 @@
++/*
++ *    Mobile IPv6 Mobile Node Module
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Antti Tuominen          <ajtuomin@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *    modify it under the terms of the GNU General Public License
++ *    as published by the Free Software Foundation; either version
++ *    2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include <net/mipglue.h>
++
++extern int mipv6_debug;
++int mipv6_use_auth = 0;
++
++#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
++MODULE_AUTHOR("MIPL Team");
++MODULE_DESCRIPTION("Mobile IPv6 Mobile Node");
++MODULE_LICENSE("GPL");
++MODULE_PARM(mipv6_debug, "i");
++#endif
++
++#include "config.h"
++
++#include "mobhdr.h"
++#include "mn.h"
++#include "mipv6_icmp.h"
++//#include "prefix.h"
++
++/* TODO: These will go as soon as we get rid of the last two ioctls */
++extern int mipv6_ioctl_mn_init(void);
++extern void mipv6_ioctl_mn_exit(void);
++
++/**********************************************************************
++ *
++ * MIPv6 Module Init / Cleanup
++ *
++ **********************************************************************/
++
++#ifdef CONFIG_SYSCTL
++/* Sysctl table */
++
++extern int max_rtr_reach_time;
++extern int eager_cell_switching;
++
++static int max_reach = 1000;
++static int min_reach = 1;
++static int max_one = 1;
++static int min_zero = 0;
++
++extern int 
++mipv6_mdetect_mech_sysctl(ctl_table *, int, struct file *, void *, size_t *);
++
++extern int 
++mipv6_router_reach_sysctl(ctl_table *, int, struct file *, void *, size_t *);
++
++ctl_table mipv6_mobility_table[] = {
++      {NET_IPV6_MOBILITY_BU_F_LLADDR, "bu_flag_lladdr",
++       &mip6node_cnf.bu_lladdr, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one},
++      {NET_IPV6_MOBILITY_BU_F_KEYMGM, "bu_flag_keymgm",
++       &mip6node_cnf.bu_keymgm, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one},
++      {NET_IPV6_MOBILITY_BU_F_CN_ACK, "bu_flag_cn_ack",
++       &mip6node_cnf.bu_cn_ack, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one},
++
++      {NET_IPV6_MOBILITY_ROUTER_REACH, "max_router_reachable_time",
++       &max_rtr_reach_time, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_reach, &max_reach},
++
++      {NET_IPV6_MOBILITY_MDETECT_MECHANISM, "eager_cell_switching",
++       &eager_cell_switching, sizeof(int), 0644, NULL,
++       &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one},
++
++      {0}
++};
++ctl_table mipv6_table[] = {
++      {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table},
++      {0}
++};
++
++static struct ctl_table_header *mipv6_sysctl_header;
++static struct ctl_table mipv6_net_table[];
++static struct ctl_table mipv6_root_table[];
++
++ctl_table mipv6_net_table[] = {
++      {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table},
++      {0}
++};
++
++ctl_table mipv6_root_table[] = {
++      {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table},
++      {0}
++};
++#endif /* CONFIG_SYSCTL */
++
++/*  Initialize the module  */
++static int __init mip6_mn_init(void)
++{
++      int err = 0;
++
++      printk(KERN_INFO "MIPL Mobile IPv6 for Linux Mobile Node %s (%s)\n",
++             MIPLVERSION, MIPV6VERSION);
++      mip6node_cnf.capabilities = CAP_CN | CAP_MN;
++
++#ifdef CONFIG_IPV6_MOBILITY_DEBUG
++      printk(KERN_INFO "Debug-level: %d\n", mipv6_debug);
++#endif
++
++#ifdef CONFIG_SYSCTL
++      mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0);
++#endif
++      if ((err = mipv6_mn_init()) < 0)
++              goto mn_fail;
++
++      mipv6_mh_mn_init();
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_rcv_dhaad_rep;
++      mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv;
++      mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_rcv_paramprob;
++
++//    mipv6_initialize_pfx_icmpv6();
++
++      if ((err = mipv6_ioctl_mn_init()) < 0)
++              goto ioctl_fail;
++
++      return 0;
++
++ioctl_fail:
++//    mipv6_shutdown_pfx_icmpv6();
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = NULL;
++      mip6_fn.icmpv6_dhaad_req_rcv = NULL;
++      mip6_fn.icmpv6_pfxadv_rcv = NULL;
++      mip6_fn.icmpv6_pfxsol_rcv = NULL;
++      mip6_fn.icmpv6_paramprob_rcv = NULL;
++
++      mipv6_mh_mn_exit();
++      mipv6_mn_exit();
++mn_fail:
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++      return err;
++}
++module_init(mip6_mn_init);
++
++#ifdef MODULE
++/*  Cleanup module  */
++static void __exit mip6_mn_exit(void)
++{
++      printk(KERN_INFO "mip6_mn.o exiting.\n");
++      mip6node_cnf.capabilities &= ~(int)CAP_MN;
++
++      mipv6_ioctl_mn_exit();
++//    mipv6_shutdown_pfx_icmpv6();
++
++      mip6_fn.icmpv6_dhaad_rep_rcv = NULL;
++      mip6_fn.icmpv6_dhaad_req_rcv = NULL;
++      mip6_fn.icmpv6_pfxadv_rcv = NULL;
++      mip6_fn.icmpv6_pfxsol_rcv = NULL;
++      mip6_fn.icmpv6_paramprob_rcv = NULL;
++
++      mipv6_mn_exit();
++
++/* common cleanup */
++#ifdef CONFIG_SYSCTL
++      unregister_sysctl_table(mipv6_sysctl_header);
++#endif
++}
++module_exit(mip6_mn_exit);
++#endif /* MODULE */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.c
+@@ -0,0 +1,287 @@
++/*  
++ * 2001 (c) Oy L M Ericsson Ab
++ *
++ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com>
++ *
++ * $Id$
++ *
++ */
++
++/*
++ * Vertical hand-off information manager
++ */
++
++#include <linux/netdevice.h>
++#include <linux/in6.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/list.h>
++#include "multiaccess_ctl.h"
++#include "debug.h"
++
++/*
++ * Local variables
++ */
++static LIST_HEAD(if_list);
++
++/* Internal interface information list */
++struct ma_if_info {
++      struct list_head list;
++      int        interface_id;
++      int        preference;
++      __u8       status;
++};
++
++/**
++ * ma_ctl_get_preference - get preference value for interface
++ * @ifi: interface index
++ * 
++ * Returns integer value preference for given interface.
++ **/
++int ma_ctl_get_preference(int ifi)
++{
++      struct list_head *lh;
++      struct ma_if_info *info;
++      int pref = 0;
++
++      list_for_each(lh, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (info->interface_id == ifi) {
++                      pref = info->preference;
++                      return pref;
++              }
++      }
++      return -1;
++}
++/**
++ * ma_ctl_get_preference - get preference value for interface
++ * @ifi: interface index
++ * 
++ * Returns integer value interface index for interface with highest preference.
++ **/
++int ma_ctl_get_preferred_if(void)
++{
++      struct list_head *lh;
++      struct ma_if_info *info, *pref_if = NULL;
++      
++      list_for_each(lh, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (!pref_if || (info->preference > pref_if->preference)) {
++                      pref_if = info;
++              }
++      }
++      if (pref_if) return pref_if->interface_id;
++      return 0;
++}
++/**
++ * ma_ctl_set_preference - set preference for interface
++ * @arg: ioctl args
++ *
++ * Sets preference of an existing interface (called by ioctl).
++ **/
++void ma_ctl_set_preference(unsigned long arg)
++{
++      struct list_head *lh;
++      struct ma_if_info *info;
++      struct ma_if_uinfo uinfo;
++      
++      memset(&uinfo, 0, sizeof(struct ma_if_uinfo));
++      if (copy_from_user(&uinfo, (struct ma_if_uinfo *)arg, 
++                         sizeof(struct ma_if_uinfo)) < 0) {
++              DEBUG(DBG_WARNING, "copy_from_user failed");
++              return;
++      }
++
++      /* check if the interface exists */
++      list_for_each(lh, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (info->interface_id == uinfo.interface_id) {
++                      info->preference = uinfo.preference;
++                      return;
++              }
++      }
++}
++
++/**
++ * ma_ctl_add_iface - add new interface to list
++ * @if_index: interface index
++ *
++ * Adds new interface entry to preference list.  Preference is set to
++ * the same value as @if_index.  Entry @status is set to
++ * %MA_IFACE_NOT_USED.
++ **/
++void ma_ctl_add_iface(int if_index)
++{
++      struct list_head *lh;
++      struct ma_if_info *info;
++
++      DEBUG_FUNC();
++      
++      /* check if the interface already exists */
++      list_for_each(lh, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (info->interface_id == if_index) {
++                      info->status = MA_IFACE_NOT_USED;
++                      info->preference = if_index;
++                      return;
++              }
++      }
++
++      info = kmalloc(sizeof(struct ma_if_info), GFP_ATOMIC);
++      if (info == NULL) {
++              DEBUG(DBG_ERROR, "Out of memory");
++              return;
++      }
++      memset(info, 0, sizeof(struct ma_if_info));
++      info->interface_id = if_index;
++      info->preference = if_index;
++      info->status = MA_IFACE_NOT_USED;
++      list_add(&info->list, &if_list);
++}
++
++/**
++ * ma_ctl_del_iface - remove entry from the list
++ * @if_index: interface index
++ *
++ * Removes entry for interface @if_index from preference list.
++ **/
++int ma_ctl_del_iface(int if_index)
++{
++      struct list_head *lh, *next;
++      struct ma_if_info *info;
++
++      DEBUG_FUNC();
++
++      /* if the iface exists, change availability to 0 */
++      list_for_each_safe(lh, next, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (info->interface_id == if_index) {
++                      list_del(&info->list);
++                      kfree(info);
++                      return 0;
++              }
++      }
++
++      return -1;
++}
++
++/**
++ * ma_ctl_upd_iface - update entry (and list)
++ * @if_index: interface to update
++ * @status: new status for interface
++ * @change_if_index: new interface
++ *
++ * Updates @if_index entry on preference list.  Entry status is set to
++ * @status.  If new @status is %MA_IFACE_CURRENT, updates list to have
++ * only one current device.  If @status is %MA_IFACE_NOT_PRESENT,
++ * entry is deleted and further if entry had %MA_IFACE_CURRENT set,
++ * new current device is looked up and returned in @change_if_index.
++ * New preferred interface is also returned if current device changes
++ * to %MA_IFACE_NOT_USED.  Returns 0 on success, otherwise negative.
++ **/
++int ma_ctl_upd_iface(int if_index, int status, int *change_if_index)
++{
++      struct list_head *lh, *tmp;
++      struct ma_if_info *info, *pref = NULL;
++      int found = 0;
++
++      DEBUG_FUNC();
++
++      *change_if_index = 0;
++
++      /* check if the interface exists */
++      list_for_each_safe(lh, tmp, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              if (status == MA_IFACE_NOT_PRESENT) {
++                      if (info->interface_id == if_index) {
++                              list_del_init(&info->list);
++                              kfree(info);
++                              found = 1;
++                              break;
++                      }
++              } else if (status == MA_IFACE_CURRENT) {
++                      if (info->interface_id == if_index) {
++                              info->status |= MA_IFACE_CURRENT;
++                              found = 1;
++                      } else {
++                              info->status |= MA_IFACE_NOT_USED;
++                      }
++              } else if (status == MA_IFACE_NOT_USED) {
++                      if (info->interface_id == if_index) {
++                              if (info->status | MA_IFACE_CURRENT) {
++                                      found = 1;
++                              }
++                              info->status &= !MA_IFACE_CURRENT;
++                              info->status |= MA_IFACE_NOT_USED;
++                              info->status &= !MA_IFACE_HAS_ROUTER;
++                      }
++                      break;
++              } else if (status == MA_IFACE_HAS_ROUTER) {
++                      if (info->interface_id == if_index) {
++                              info->status |= MA_IFACE_HAS_ROUTER;
++                      }
++                      return 0;
++              }
++      }
++
++      if (status & (MA_IFACE_NOT_USED|MA_IFACE_NOT_PRESENT) && found) {
++              /* select new interface */
++              list_for_each(lh, &if_list) {
++                      info = list_entry(lh, struct ma_if_info, list);
++                      if (pref == NULL || ((info->preference > pref->preference) && 
++                                           info->status & MA_IFACE_HAS_ROUTER))
++                              pref = info;
++              }
++              if (pref) {
++                      *change_if_index = pref->interface_id;
++                      pref->status |= MA_IFACE_CURRENT;
++              } else {
++                      *change_if_index = -1;
++              }
++              return 0;
++      }
++
++      if (found) return 0;
++
++      return -1;
++}
++
++static int if_proc_info(char *buffer, char **start, off_t offset,
++                      int length)
++{
++      struct list_head *lh;
++      struct ma_if_info *info;
++      int len = 0;
++
++      list_for_each(lh, &if_list) {
++              info = list_entry(lh, struct ma_if_info, list);
++              len += sprintf(buffer + len, "%02d %010d %1d %1d\n",
++                             info->interface_id, info->preference,
++                             !!(info->status & MA_IFACE_HAS_ROUTER),
++                             !!(info->status & MA_IFACE_CURRENT));
++      }
++
++      *start = buffer + offset;
++
++      len -= offset;
++
++      if (len > length) len = length;
++
++      return len;
++
++}
++
++void ma_ctl_init(void)
++{
++      proc_net_create("mip6_iface", 0, if_proc_info);
++}
++
++void ma_ctl_clean(void)
++{
++      proc_net_remove("mip6_iface");
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.h
+@@ -0,0 +1,77 @@
++/*  
++ * 2001 (c) Oy L M Ericsson Ab
++ *
++ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com>
++ *
++ * $Id$
++ *
++ */
++
++#ifndef _MULTIACCESS_CTL_H
++#define _MULTIACCESS_CTL_H
++
++/* status */
++#define MA_IFACE_NOT_PRESENT 0x01
++#define MA_IFACE_NOT_USED    0x02
++#define MA_IFACE_HAS_ROUTER  0x04
++#define MA_IFACE_CURRENT     0x10
++
++struct ma_if_uinfo {
++      int        interface_id;
++      int        preference;
++      __u8       status;
++};
++/*
++ *  @ma_ctl_get_preferred_id: returns most preferred interface id
++ */
++int ma_ctl_get_preferred_if(void);
++
++/* @ma_ctl_get_preference: returns preference for an interface
++ * @name: name of the interface (dev->name)
++ */
++int ma_ctl_get_preference(int ifi);
++
++/*
++ * Public function: ma_ctl_set_preference
++ * Description: Set preference of an existing interface (called by ioctl)
++ * Returns:
++ */
++void ma_ctl_set_preference(unsigned long);
++
++/*
++ * Public function: ma_ctl_add_iface
++ * Description: Inform control module to insert a new interface
++ * Returns: 0 if success, any other number means an error
++ */
++void ma_ctl_add_iface(int);
++
++/*
++ * Public function: ma_ctl_del_iface
++ * Description: Inform control module to remove an obsolete interface
++ * Returns: 0 if success, any other number means an error
++ */
++int ma_ctl_del_iface(int);
++
++/*
++ * Public function: ma_ctl_upd_iface
++ * Description: Inform control module of status change.
++ * Returns: 0 if success, any other number means an error
++ */
++int ma_ctl_upd_iface(int, int, int *);
++
++/*
++ * Public function: ma_ctl_init
++ * Description: XXX
++ * Returns: XXX
++ */
++void ma_ctl_init(void);
++
++/*
++ * Public function: ma_ctl_clean
++ * Description: XXX
++ * Returns: -
++ */
++void ma_ctl_clean(void);
++
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/ndisc_ha.c
+@@ -0,0 +1,596 @@
++/*
++ *    Mobile IPv6 Duplicate Address Detection Functions
++ *
++ *    Authors:
++ *    Krishna Kumar <krkumar@us.ibm.com>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/autoconf.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/skbuff.h>
++#include <linux/in6.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/mipv6.h>
++
++#include "debug.h"
++#include "bcache.h"
++#include "ha.h" /* mipv6_generate_ll_addr */
++
++/*
++ * Binding Updates from MN are cached in this structure till DAD is performed.
++ * This structure is used to retrieve a pending Binding Update for the HA to
++ * reply to after performing DAD. The first cell is different from the rest as
++ * follows :
++ *    1. The first cell is used to chain the remaining cells. 
++ *    2. The timeout of the first cell is used to delete expired entries
++ *       in the list of cells, while the timeout of the other cells are
++ *       used for timing out a NS request so as to reply to a BU.
++ *    3. The only elements of the first cell that are used are :
++ *       next, prev, and callback_timer.
++ *
++ * TODO : Don't we need to do pneigh_lookup on the Link Local address ?
++ */
++struct mipv6_dad_cell {
++      /* Information needed for DAD management */
++      struct mipv6_dad_cell   *next;  /* Next element on the DAD list */
++      struct mipv6_dad_cell   *prev;  /* Prev element on the DAD list */
++      __u16                   probes; /* Number of times to probe for addr */
++      __u16                   flags;  /* Entry flags - see below */
++      struct timer_list       callback_timer; /* timeout for entry */
++
++      /* Information needed for performing DAD */
++      struct inet6_ifaddr     *ifp;
++      int                     ifindex;
++      struct in6_addr         daddr;
++      struct in6_addr         haddr;          /* home address */
++      struct in6_addr         ll_haddr;       /* Link Local value of haddr */
++      struct in6_addr         coa;
++      struct in6_addr         rep_coa;
++      __u32                   ba_lifetime;
++      __u16                   sequence;
++      __u8                    bu_flags;
++};
++
++/* Values for the 'flags' field in the mipv6_dad_cell */
++#define       DAD_INIT_ENTRY          0
++#define       DAD_DUPLICATE_ADDRESS   1
++#define       DAD_UNIQUE_ADDRESS      2
++
++/* Head of the pending DAD list */
++static struct mipv6_dad_cell dad_cell_head;
++
++/* Lock to access the pending DAD list */
++static rwlock_t dad_lock = RW_LOCK_UNLOCKED;
++
++/* Timer routine which deletes 'expired' entries in the DAD list */
++static void mipv6_dad_delete_old_entries(unsigned long unused)
++{
++      struct mipv6_dad_cell *curr, *next;
++      unsigned long next_time = 0;
++
++      write_lock(&dad_lock);
++      curr = dad_cell_head.next;
++      while (curr != &dad_cell_head) {
++              next = curr->next;
++              if (curr->flags != DAD_INIT_ENTRY) {
++                      if (curr->callback_timer.expires <= jiffies) {
++                              /* Entry has expired, free it up. */
++                              curr->next->prev = curr->prev;
++                              curr->prev->next = curr->next;
++                              in6_ifa_put(curr->ifp);
++                              kfree(curr);
++                      } else if (next_time <
++                                 curr->callback_timer.expires) {
++                              next_time = curr->callback_timer.expires;
++                      }
++              }
++              curr = next;
++      }
++      write_unlock(&dad_lock);
++      if (next_time) {
++              /*
++               * Start another timer if more cells need to be removed at
++               * a later stage.
++               */
++              dad_cell_head.callback_timer.expires = next_time;
++              add_timer(&dad_cell_head.callback_timer);
++      }
++}
++
++/* 
++ * Queue a timeout routine to clean up 'expired' DAD entries.
++ */
++static void mipv6_start_dad_head_timer(struct mipv6_dad_cell *cell)
++{
++      unsigned long expire = jiffies +
++          cell->ifp->idev->nd_parms->retrans_time * 10;
++
++      if (!timer_pending(&dad_cell_head.callback_timer) ||
++          expire < dad_cell_head.callback_timer.expires) {
++              /*
++               * Add timer if none pending, or mod the timer if new 
++               * cell needs to be expired before existing timer runs.
++               *
++               * We let the cell remain as long as possible, so that
++               * new BU's as part of retransmissions don't have to go
++               * through DAD before replying.
++               */
++              dad_cell_head.callback_timer.expires = expire;
++
++              /*
++               * Keep the cell around for atleast some time to handle
++               * retransmissions or BU's due to fast MN movement. This
++               * is needed otherwise a previous timeout can delete all
++               * expired entries including this new one.
++               */
++              cell->callback_timer.expires = jiffies +
++                  cell->ifp->idev->nd_parms->retrans_time * 5;
++              if (!timer_pending(&dad_cell_head.callback_timer)) {
++                      add_timer(&dad_cell_head.callback_timer);
++              } else {
++                      mod_timer(&dad_cell_head.callback_timer, expire);
++              }
++      }
++}
++
++
++/* Join solicited node MC address */
++static inline void mipv6_join_sol_mc_addr(struct in6_addr *addr,
++                                        struct net_device *dev)
++{
++      struct in6_addr maddr;
++
++      /* Join solicited node MC address */
++      addrconf_addr_solict_mult(addr, &maddr);
++      ipv6_dev_mc_inc(dev, &maddr);
++}
++
++/* Leave solicited node MC address */
++static inline void mipv6_leave_sol_mc_addr(struct in6_addr *addr,
++                                         struct net_device *dev)
++{
++      struct in6_addr maddr;
++
++      addrconf_addr_solict_mult(addr, &maddr);
++      ipv6_dev_mc_dec(dev, &maddr);
++}
++
++/* Send a NS */
++static inline void mipv6_dad_send_ns(struct inet6_ifaddr *ifp,
++                                   struct in6_addr *haddr)
++{
++      struct in6_addr unspec;
++      struct in6_addr mcaddr;
++
++      ipv6_addr_set(&unspec, 0, 0, 0, 0);
++      addrconf_addr_solict_mult(haddr, &mcaddr);
++
++      /* addr is 'unspec' since we treat this address as transient */
++      ndisc_send_ns(ifp->idev->dev, NULL, haddr, &mcaddr, &unspec);
++}
++
++/*
++ * Search for a home address in the list of pending DAD's. Called from
++ * Neighbor Advertisement
++ * Return values :
++ *    -1 : No DAD entry found for this advertisement, or entry already
++ *         finished processing.
++ *    0  : Entry found waiting for DAD to finish.
++ */
++static int dad_search_haddr(struct in6_addr *ll_haddr,
++                          struct in6_addr *daddr, struct in6_addr *haddr,
++                          struct in6_addr *coa, struct in6_addr *rep_coa,
++                          __u16 * seq, struct inet6_ifaddr **ifp)
++{
++      struct mipv6_dad_cell *cell;
++
++      read_lock(&dad_lock);
++      cell = dad_cell_head.next;
++      while (cell != &dad_cell_head &&
++             ipv6_addr_cmp(&cell->ll_haddr, ll_haddr) && 
++             ipv6_addr_cmp(&cell->haddr, ll_haddr)) {
++              cell = cell->next;
++      }
++      if (cell == &dad_cell_head || cell->flags != DAD_INIT_ENTRY) {
++              /* Not found element, or element already finished processing */
++              if (cell != &dad_cell_head) {
++                      /*
++                       * Set the state to DUPLICATE, even if it was UNIQUE
++                       * earlier. It is not needed to setup timer via 
++                       * mipv6_start_dad_head_timer since this must have
++                       * already been done.
++                       */
++                      cell->flags = DAD_DUPLICATE_ADDRESS;
++              }
++              read_unlock(&dad_lock);
++              return -1;
++      }
++
++      /*
++       * The NA found an unprocessed entry in the DAD list. Expire this
++       * entry since another node advertised this address. Caller should
++       * reject BU (DAD failed).
++       */
++      ipv6_addr_copy(daddr, &cell->daddr);
++      ipv6_addr_copy(haddr, &cell->haddr);
++      ipv6_addr_copy(coa, &cell->coa);
++      ipv6_addr_copy(rep_coa, &cell->rep_coa);
++      *seq = cell->sequence;
++      *ifp = cell->ifp;
++
++      if (del_timer(&cell->callback_timer) == 0) {
++              /* Timer already deleted, race with Timeout Handler */
++              /* No action needed */
++      }
++
++      cell->flags = DAD_DUPLICATE_ADDRESS;
++
++      /* Now leave this address to avoid future processing of NA's */
++      mipv6_leave_sol_mc_addr(&cell->ll_haddr, cell->ifp->idev->dev);
++      /* Leave also global address, if link local address was in use */
++      if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr))
++              mipv6_leave_sol_mc_addr(&cell->haddr, cell->ifp->idev->dev);
++      /* Start dad_head timer to remove this entry */
++      mipv6_start_dad_head_timer(cell);
++
++      read_unlock(&dad_lock);
++
++      return 0;
++}
++
++/* ENTRY routine called via Neighbor Advertisement */
++void mipv6_check_dad(struct in6_addr *ll_haddr)
++{
++      struct in6_addr daddr, haddr, coa, rep_coa;
++      struct inet6_ifaddr *ifp;
++      __u16 seq;
++
++      if (dad_search_haddr(ll_haddr, &daddr, &haddr, &coa, &rep_coa, &seq,
++                           &ifp) < 0) {
++              /* 
++               * Didn't find entry, or no action needed (the action has
++               * already been performed).
++               */
++              return;
++      }
++
++      /*
++       * A DAD cell was present, meaning that there is a pending BU
++       * request for 'haddr' - reject the BU.
++       */
++      mipv6_bu_finish(ifp, 0, DUPLICATE_ADDR_DETECT_FAIL,
++                      &daddr, &haddr, &coa, &rep_coa, 0, seq, 0, NULL);
++      return;
++}
++
++/*
++ * Check if the passed 'cell' is in the list of pending DAD's. Called from
++ * the Timeout Handler.
++ *
++ * Assumes that the caller is holding the dad_lock in reader mode.
++ */
++static int dad_search_cell(struct mipv6_dad_cell *cell)
++{
++      struct mipv6_dad_cell *tmp;
++
++      tmp = dad_cell_head.next;
++      while (tmp != &dad_cell_head && tmp != cell) {
++              tmp = tmp->next;
++      }
++      if (tmp == cell) {
++              if (cell->flags == DAD_INIT_ENTRY) {
++                      /* Found valid entry */
++                      if (--cell->probes == 0) {
++                              /*
++                               * Retransmission's are over - return success.
++                               */
++                              cell->flags = DAD_UNIQUE_ADDRESS;
++
++                              /* 
++                               * Leave this address to avoid future 
++                               * processing of NA's.
++                               */
++                              mipv6_leave_sol_mc_addr(&cell->ll_haddr,
++                                                      cell->ifp->idev->
++                                                      dev);
++                              if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr))
++                                      mipv6_leave_sol_mc_addr(&cell->haddr, 
++                                                              cell->ifp->idev->dev);
++                              /* start timeout to delete this cell. */
++                              mipv6_start_dad_head_timer(cell);
++                              return 0;
++                      }
++                      /*
++                       * Retransmission not finished, send another NS and
++                       * return failure.
++                       */
++                      mipv6_dad_send_ns(cell->ifp, &cell->ll_haddr);
++                      if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr))
++                              mipv6_leave_sol_mc_addr(&cell->haddr, 
++                                                      cell->ifp->idev->dev);
++                      cell->callback_timer.expires = jiffies +
++                          cell->ifp->idev->nd_parms->retrans_time;
++                      add_timer(&cell->callback_timer);
++              } else {
++                      /*
++                       * This means that an NA was received before the
++                       * timeout and when the state changed from
++                       * DAD_INIT_ENTRY, the BU got failed as a result.
++                       * There is nothing to be done.
++                       */
++              }
++      }
++      return -1;
++}
++
++/* ENTRY routine called via Timeout */
++static void mipv6_dad_timeout(unsigned long arg)
++{
++      __u8 ba_status = SUCCESS;
++      struct in6_addr daddr;
++      struct in6_addr haddr;
++      struct in6_addr coa;
++      struct in6_addr rep_coa;
++      struct inet6_ifaddr *ifp;
++      int ifindex;
++      __u32 ba_lifetime;
++      __u16 sequence;
++      __u8 flags;
++      struct mipv6_dad_cell *cell = (struct mipv6_dad_cell *) arg;
++
++      /*
++       * If entry is not in the list, we have already sent BU Failure
++       * after getting a NA.
++       */
++      read_lock(&dad_lock);
++      if (dad_search_cell(cell) < 0) {
++              /*
++               * 'cell' is no longer valid (may not be in the list or
++               * is already processed, due to NA processing), or NS
++               * retransmissions are not yet over.
++               */
++              read_unlock(&dad_lock);
++              return;
++      }
++
++      /* This is the final Timeout. Send Bind Ack Success */
++
++      ifp = cell->ifp;
++      ifindex = cell->ifindex;
++      ba_lifetime = cell->ba_lifetime;
++      sequence = cell->sequence;
++      flags = cell->bu_flags;
++
++      ipv6_addr_copy(&daddr, &cell->daddr);
++      ipv6_addr_copy(&haddr, &cell->haddr);
++      ipv6_addr_copy(&coa, &cell->coa);
++      ipv6_addr_copy(&rep_coa, &cell->rep_coa);
++      read_unlock(&dad_lock);
++
++      /* Send BU Acknowledgement Success */
++      mipv6_bu_finish(ifp, ifindex, ba_status, 
++                      &daddr, &haddr, &coa, &rep_coa,
++                      ba_lifetime, sequence, flags, NULL);
++      return;
++}
++
++/*
++ * Check if original home address exists in our DAD pending list, if so return
++ * the cell.
++ *
++ * Assumes that the caller is holding the dad_lock in writer mode.
++ */
++static struct mipv6_dad_cell *mipv6_dad_get_cell(struct in6_addr *haddr)
++{
++      struct mipv6_dad_cell *cell;
++
++      cell = dad_cell_head.next;
++      while (cell != &dad_cell_head
++             && ipv6_addr_cmp(&cell->haddr, haddr)) {
++              cell = cell->next;
++      }
++      if (cell == &dad_cell_head) {
++              /* Not found element */
++              return NULL;
++      }
++      return cell;
++}
++
++/*
++ * Save all parameters needed for doing a Bind Ack in the mipv6_dad_cell 
++ * structure.
++ */
++static void mipv6_dad_save_cell(struct mipv6_dad_cell *cell,
++                              struct inet6_ifaddr *ifp, int ifindex,
++                              struct in6_addr *daddr,
++                              struct in6_addr *haddr,
++                              struct in6_addr *coa, 
++                              struct in6_addr *rep_coa,
++                              __u32 ba_lifetime,
++                              __u16 sequence, __u8 flags)
++{
++      in6_ifa_hold(ifp);
++      cell->ifp = ifp;
++      cell->ifindex = ifindex;
++
++      ipv6_addr_copy(&cell->daddr, daddr);
++      ipv6_addr_copy(&cell->haddr, haddr);
++      ipv6_addr_copy(&cell->coa, coa);
++      ipv6_addr_copy(&cell->rep_coa, rep_coa);
++
++      /* Convert cell->ll_haddr to Link Local address */
++      if (flags & MIPV6_BU_F_LLADDR) 
++              mipv6_generate_ll_addr(&cell->ll_haddr, haddr);
++      else 
++              ipv6_addr_copy(&cell->ll_haddr, haddr);
++
++      cell->ba_lifetime = ba_lifetime;
++      cell->sequence = sequence;
++      cell->bu_flags = flags;
++}
++
++/*
++ * Top level DAD routine for performing DAD.
++ *
++ * Return values
++ *    0     : Don't need to do DAD.
++ *    1     : Need to do DAD.
++ *    -n    : Error, where 'n' is the reason for the error.
++ *
++ * Assumption : DAD process has been optimized by using cached values upto
++ * some time. However sometimes this can cause problems. Eg. when the first
++ * BU was received, DAD might have failed. Before the second BU arrived,
++ * the node using MN's home address might have stopped using it, but still
++ * we will return DAD_DUPLICATE_ADDRESS based on the first DAD's result. Or 
++ * this can go the other way around. However, it is a very small possibility
++ * and thus optimization is turned on by default. It is possible to change
++ * this feature (needs a little code-rewriting in this routine), but 
++ * currently DAD result is being cached for performance reasons.
++ */
++int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex,
++                  struct in6_addr *daddr, struct in6_addr *haddr,
++                  struct in6_addr *coa, struct in6_addr *rep_coa,
++                  __u32 ba_lifetime, __u16 sequence, __u8 flags)
++{
++      int found;
++      struct mipv6_dad_cell *cell;
++      struct mipv6_bce bc_entry;
++
++      if (ifp->idev->cnf.dad_transmits == 0) {
++              /* DAD is not configured on the HA, return SUCCESS */
++              return 0;
++      }
++
++      if (mipv6_bcache_get(haddr, daddr, &bc_entry) == 0) {
++              /*
++               * We already have an entry in our cache - don't need to 
++               * do DAD as we are already defending this home address.
++               */
++              return 0;
++      }
++
++      write_lock(&dad_lock);
++      if ((cell = mipv6_dad_get_cell(haddr)) != NULL) {
++              /*
++               * An existing entry for BU was found in our cache due
++               * to retransmission of the BU or a new COA registration.
++               */
++              switch (cell->flags) {
++              case DAD_INIT_ENTRY:
++                      /* Old entry is waiting for DAD to complete */
++                      break;
++              case DAD_UNIQUE_ADDRESS:
++                      /* DAD is finished successfully - return success. */
++                      write_unlock(&dad_lock);
++                      return 0;
++              case DAD_DUPLICATE_ADDRESS:
++                      /*
++                       * DAD is finished and we got a NA while doing BU -
++                       * return failure.
++                       */
++                      write_unlock(&dad_lock);
++                      return -DUPLICATE_ADDR_DETECT_FAIL;
++              default:
++                      /* Unknown state - should never happen */
++                      DEBUG(DBG_WARNING,
++                            "cell entry in unknown state : %d",
++                            cell->flags);
++                      write_unlock(&dad_lock);
++                      return -REASON_UNSPECIFIED;
++              }
++              found = 1;
++      } else {
++              if ((cell = (struct mipv6_dad_cell *)
++                   kmalloc(sizeof(struct mipv6_dad_cell), GFP_ATOMIC))
++                  == NULL) {
++                      return -INSUFFICIENT_RESOURCES;
++              }
++              found = 0;
++      }
++
++      mipv6_dad_save_cell(cell, ifp, ifindex, daddr, haddr, coa, rep_coa,
++                          ba_lifetime, sequence, flags);
++
++      if (!found) {
++              cell->flags = DAD_INIT_ENTRY;
++              cell->probes = ifp->idev->cnf.dad_transmits;
++
++              /* Insert element on dad_cell_head list */
++              dad_cell_head.prev->next = cell;
++              cell->next = &dad_cell_head;
++              cell->prev = dad_cell_head.prev;
++              dad_cell_head.prev = cell;
++              write_unlock(&dad_lock);
++              if (flags & MIPV6_BU_F_LLADDR) {
++                      /* join the solicited node MC of the global homeaddr.*/
++                      mipv6_join_sol_mc_addr(&cell->haddr, ifp->idev->dev);
++                      /* Send a NS */
++                      mipv6_dad_send_ns(ifp, &cell->haddr);
++              }
++              /* join the solicited node MC of the homeaddr. */
++              mipv6_join_sol_mc_addr(&cell->ll_haddr, ifp->idev->dev);
++              
++              /* Send a NS */
++              mipv6_dad_send_ns(ifp, &cell->ll_haddr);
++              
++              /* Initialize timer for this cell to timeout the NS. */
++              init_timer(&cell->callback_timer);
++              cell->callback_timer.data = (unsigned long) cell;
++              cell->callback_timer.function = mipv6_dad_timeout;
++              cell->callback_timer.expires = jiffies +
++                  ifp->idev->nd_parms->retrans_time;
++              add_timer(&cell->callback_timer);
++      } else {
++              write_unlock(&dad_lock);
++      }
++      return 1;
++}
++
++void __init mipv6_dad_init(void)
++{
++      dad_cell_head.next = dad_cell_head.prev = &dad_cell_head;
++      init_timer(&dad_cell_head.callback_timer);
++      dad_cell_head.callback_timer.data = 0;
++      dad_cell_head.callback_timer.function =
++              mipv6_dad_delete_old_entries;
++}
++
++void __exit mipv6_dad_exit(void)
++{
++      struct mipv6_dad_cell *curr, *next;
++
++      write_lock_bh(&dad_lock);
++      del_timer(&dad_cell_head.callback_timer);
++
++      curr = dad_cell_head.next;
++      while (curr != &dad_cell_head) {
++              next = curr->next;
++              del_timer(&curr->callback_timer);
++              if (curr->flags == DAD_INIT_ENTRY) {
++                      /*
++                       * We were in DAD_INIT state and listening to the
++                       * solicited node MC address - need to stop that.
++                       */
++                      mipv6_leave_sol_mc_addr(&curr->ll_haddr,
++                                              curr->ifp->idev->dev);
++                      if (ipv6_addr_cmp(&curr->ll_haddr, &curr->haddr))
++                              mipv6_leave_sol_mc_addr(&curr->haddr, 
++                                                      curr->ifp->idev->dev);
++              }
++              in6_ifa_put(curr->ifp);
++              kfree(curr);
++              curr = next;
++      }
++      dad_cell_head.next = dad_cell_head.prev = &dad_cell_head;
++      write_unlock_bh(&dad_lock);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.c
+@@ -0,0 +1,217 @@
++/**
++ * Prefix solicitation and advertisement
++ *
++ * Authors:
++ * Jaakko Laine <medved@iki.fi>
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/icmpv6.h>
++#include <linux/net.h>
++#include <linux/spinlock.h>
++#include <linux/timer.h>
++#include <linux/netdevice.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/ip6_route.h>
++#include <net/mipv6.h>
++
++#include "mipv6_icmp.h"
++#include "debug.h"
++#include "sortedlist.h"
++#include "prefix.h"
++#include "config.h"
++
++#define INFINITY 0xffffffff
++
++struct timer_list pfx_timer;
++
++struct list_head pfx_list;
++rwlock_t pfx_list_lock = RW_LOCK_UNLOCKED;
++
++int compare_pfx_list_entry(const void *data1, const void *data2,
++                         int datalen)
++{
++      struct pfx_list_entry *e1 = (struct pfx_list_entry *) data1;
++      struct pfx_list_entry *e2 = (struct pfx_list_entry *) data2;
++
++      return ((ipv6_addr_cmp(&e1->daddr, &e2->daddr) == 0)
++              && (e2->ifindex == -1 || e1->ifindex == e2->ifindex));
++}
++
++/**
++ * mipv6_pfx_cancel_send - cancel pending items to daddr from saddr
++ * @daddr: Destination address
++ * @ifindex: pending items on this interface will be canceled
++ *
++ * if ifindex == -1, all items to daddr will be removed
++ */
++void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex)
++{
++      unsigned long tmp;
++      struct pfx_list_entry entry;
++
++      DEBUG_FUNC();
++
++      /* We'll just be comparing these parts... */
++      memcpy(&entry.daddr, daddr, sizeof(struct in6_addr));
++      entry.ifindex = ifindex;
++
++      write_lock_bh(&pfx_list_lock);
++
++      while (mipv6_slist_del_item(&pfx_list, &entry,
++                                  compare_pfx_list_entry) == 0)
++              ;
++
++      if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
++              mod_timer(&pfx_timer, tmp);
++
++      write_unlock_bh(&pfx_list_lock);
++}
++
++/**
++ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to
++ * @daddr: address of HA
++ * @saddr: our address to use as source address
++ * @ifindex: interface index
++ */
++void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr,
++                    int ifindex)
++{
++      unsigned long tmp;
++      struct pfx_list_entry entry;
++
++      DEBUG_FUNC();
++
++      memcpy(&entry.daddr, daddr, sizeof(struct in6_addr));
++      memcpy(&entry.saddr, saddr, sizeof(struct in6_addr));
++      entry.retries = 0;
++      entry.ifindex = ifindex;
++
++      write_lock_bh(&pfx_list_lock);
++      if (mipv6_slist_modify(&pfx_list, &entry, sizeof(struct pfx_list_entry),
++                             jiffies + INITIAL_SOLICIT_TIMER * HZ,
++                             compare_pfx_list_entry))
++              DEBUG(DBG_WARNING, "Cannot add new HA to pfx list");
++
++      if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
++              mod_timer(&pfx_timer, tmp);
++      write_unlock_bh(&pfx_list_lock);
++}
++
++int mipv6_pfx_add_home(int ifindex, struct in6_addr *saddr, 
++                     struct in6_addr *daddr, unsigned long min_expire)
++{
++      unsigned long tmp;
++
++      write_lock(&pfx_list_lock);
++
++      if (min_expire != INFINITY) {
++              unsigned long expire;
++              struct pfx_list_entry entry;
++              
++              memcpy(&entry.daddr, saddr, sizeof(struct in6_addr));
++              memcpy(&entry.saddr, daddr, sizeof(struct in6_addr));
++              entry.retries = 0;
++              entry.ifindex = ifindex;
++
++              /* This is against the RFC 3775, but we need to set
++               * a minimum interval for a prefix solicitation.
++               * Otherwise a prefix solicitation storm will
++               * result if valid lifetime of the prefix is
++               * smaller than MAX_PFX_ADV_DELAY
++               */
++              min_expire -= MAX_PFX_ADV_DELAY;
++              min_expire = min_expire < MIN_PFX_SOL_DELAY ? MIN_PFX_SOL_DELAY : min_expire;
++
++              expire = jiffies + min_expire * HZ;
++
++              if (mipv6_slist_modify(&pfx_list, &entry,
++                                     sizeof(struct pfx_list_entry),
++                                     expire,
++                                     compare_pfx_list_entry) != 0)
++                      DEBUG(DBG_WARNING, "Cannot add new entry to pfx_list");
++      }
++
++      if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
++              mod_timer(&pfx_timer, tmp);
++
++      write_unlock(&pfx_list_lock);
++
++      return 0;
++}
++
++/**
++ * set_ha_pfx_list - manipulate pfx_list for HA when timer goes off
++ * @entry: pfx_list_entry that is due
++ */
++static void set_ha_pfx_list(struct pfx_list_entry *entry)
++{
++}
++
++/**
++ * set_mn_pfx_list - manipulate pfx_list for MN when timer goes off
++ * @entry: pfx_list_entry that is due
++ */
++static void set_mn_pfx_list(struct pfx_list_entry *entry)
++{
++}
++
++/**
++ * pfx_timer_handler - general timer handler
++ * @dummy: dummy
++ *
++ * calls set_ha_pfx_list and set_mn_pfx_list to do the thing when
++ * a timer goes off
++ */
++static void pfx_timer_handler(unsigned long dummy)
++{
++      unsigned long tmp;
++      struct pfx_list_entry *entry;
++
++      DEBUG_FUNC();
++
++      write_lock(&pfx_list_lock);
++      if (!(entry = mipv6_slist_get_first(&pfx_list)))
++              goto out;
++
++      if (mip6node_cnf.capabilities & CAP_HA)
++              set_ha_pfx_list(entry);
++      if (mip6node_cnf.capabilities & CAP_MN)
++              set_mn_pfx_list(entry);
++      if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
++              mod_timer(&pfx_timer, tmp);
++
++ out:
++      write_unlock(&pfx_list_lock);
++}
++
++int mipv6_initialize_pfx_icmpv6(void)
++{
++      INIT_LIST_HEAD(&pfx_list);
++
++      init_timer(&pfx_timer);
++      pfx_timer.function = pfx_timer_handler;
++
++      return 0;
++}
++
++void mipv6_shutdown_pfx_icmpv6(void)
++{
++      struct prefix_info *tmp;
++
++      if (timer_pending(&pfx_timer))
++              del_timer(&pfx_timer);
++
++      write_lock_bh(&pfx_list_lock);
++      while ((tmp = mipv6_slist_del_first(&pfx_list)))
++              kfree(tmp);
++      write_unlock_bh(&pfx_list_lock);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.h
+@@ -0,0 +1,57 @@
++/*
++ *      MIPL Mobile IPv6 Prefix solicitation and advertisement
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _PREFIX_H
++#define _PREFIX_H
++
++#include <net/addrconf.h>
++
++struct pfx_list_entry {
++      struct in6_addr daddr;
++      struct in6_addr saddr;
++      int retries;
++      int ifindex;
++};
++
++extern struct list_head pfx_list;
++extern rwlock_t pfx_list_lock;
++extern struct timer_list pfx_timer;
++
++int compare_pfx_list_entry(const void *data1, const void *data2,
++                         int datalen);
++
++/**
++ * mipv6_pfx_cancel_send - cancel pending pfx_advs/sols to daddr
++ * @daddr: destination address
++ * @ifindex: pending items on this interface will be canceled
++ *
++ * if ifindex == -1, all items to daddr will be removed
++ */
++void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex);
++
++/**
++ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to
++ * @daddr: address of HA
++ * @saddr: our address to use as source address
++ * @ifindex: interface index
++ */
++void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr,
++                    int ifindex);
++
++void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex);
++
++int mipv6_pfx_add_home(int ifindex, struct in6_addr *daddr,
++                     struct in6_addr *saddr, unsigned long min_expire);
++
++int mipv6_initialize_pfx_icmpv6(void);
++void mipv6_shutdown_pfx_icmpv6(void);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix_ha.c
+@@ -0,0 +1,122 @@
++/**
++ * Prefix advertisement for Home Agent
++ *
++ * Authors:
++ * Jaakko Laine <medved@iki.fi>
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/config.h>
++#include <linux/icmpv6.h>
++#include <linux/net.h>
++#include <linux/spinlock.h>
++#include <linux/timer.h>
++#include <linux/netdevice.h>
++#include <net/ipv6.h>
++#include <net/addrconf.h>
++#include <net/ip6_route.h>
++#include <net/mipv6.h>
++
++#include "mipv6_icmp.h"
++#include "debug.h"
++#include "sortedlist.h"
++#include "util.h"
++#include "bcache.h"
++#include "config.h"
++#include "prefix.h"
++
++/**
++ * pfx_adv_iterator - modify pfx_list entries according to new prefix info
++ * @data: MN's home registration bcache_entry
++ * @args: new prefix info
++ * @sortkey: ignored
++ */
++static int pfx_adv_iterator(void *data, void *args, unsigned long sortkey)
++{
++      struct mipv6_bce *bc_entry = (struct mipv6_bce *) data;
++      struct prefix_info *pinfo = (struct prefix_info *) args;
++
++      if (mipv6_prefix_compare(&bc_entry->coa, &pinfo->prefix,
++                               pinfo->prefix_len) == 0) {
++              struct pfx_list_entry pfx_entry;
++
++              memcpy(&pfx_entry.daddr, &bc_entry->coa,
++                     sizeof(struct in6_addr));
++              memcpy(&pfx_entry.daddr, &bc_entry->our_addr,
++                     sizeof(struct in6_addr));
++              pfx_entry.retries = 0;
++              pfx_entry.ifindex = bc_entry->ifindex;
++
++              mipv6_slist_modify(&pfx_list, &pfx_entry,
++                                 sizeof(struct pfx_list_entry),
++                                 jiffies +
++                                 net_random() % (MAX_PFX_ADV_DELAY * HZ),
++                                 compare_pfx_list_entry);
++      }
++
++      return 0;
++}
++
++struct homereg_iterator_args {
++      struct list_head *head;
++      int count;
++};
++
++static int homereg_iterator(void *data, void *args, unsigned long *sortkey)
++{
++      struct mipv6_bce *entry = (struct mipv6_bce *) data;
++      struct homereg_iterator_args *state =
++              (struct homereg_iterator_args *) args;
++
++      if (entry->type == HOME_REGISTRATION) {
++              mipv6_slist_add(state->head, entry,
++                              sizeof(struct mipv6_bce),
++                              state->count);
++              state->count++;
++      }
++      return 0;
++}
++
++static int mipv6_bcache_get_homeregs(struct list_head *head)
++{
++      struct homereg_iterator_args args;
++
++      DEBUG_FUNC();
++
++      args.count = 0;
++      args.head = head;
++
++      mipv6_bcache_iterate(homereg_iterator, &args);
++      return args.count;
++}
++
++/**
++ * mipv6_prefix_added - prefix was added to interface, act accordingly
++ * @pinfo: prefix_info that was added
++ * @ifindex: interface index
++ */
++void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex)
++{
++      int count;
++      unsigned long tmp;
++      struct list_head home_regs;
++
++      DEBUG_FUNC();
++
++      INIT_LIST_HEAD(&home_regs);
++
++      if (!(count = mipv6_bcache_get_homeregs(&home_regs)))
++              return;
++
++      write_lock_bh(&pfx_list_lock);
++      mipv6_slist_for_each(&home_regs, pinfo, pfx_adv_iterator);
++      if ((tmp = mipv6_slist_get_first_key(&pfx_list)))
++              mod_timer(&pfx_timer, tmp);
++      write_unlock_bh(&pfx_list_lock);
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.c
+@@ -0,0 +1,255 @@
++/*
++ *      rr_cookie.c - Mobile IPv6 return routability crypto  
++ *      Author :  Henrik Petander <henrik.petander@hut.fi>
++ * 
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ *
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/timer.h>
++#include <linux/in6.h>
++#include <linux/init.h>
++#include <linux/random.h>
++
++#include <net/ipv6.h>
++
++#include "debug.h"
++#include "hmac.h"
++#include "rr_crypto.h"
++
++#define DBG_RR 5
++
++u8 k_CN[HMAC_SHA1_KEY_SIZE]; // secret key of CN 
++
++u16 curr_index = 0;
++
++struct nonce_timestamp nonce_table[MAX_NONCES];
++spinlock_t nonce_lock = SPIN_LOCK_UNLOCKED;
++void update_nonces(void);
++
++/** nonce_is_fresh - whether the nonce was generated recently
++ *  
++ * @non_ts : table entry containing the nonce and a timestamp
++ * @interval : if nonce was generated within interval seconds it is fresh
++ *
++ * Returns 1 if the nonce is fresh, 0 otherwise.
++ */
++static int nonce_is_fresh(struct nonce_timestamp *non_ts, unsigned long interval)
++{
++      if (time_before(jiffies, non_ts->timestamp + interval * HZ) && !non_ts->invalid)
++              return 1;
++      return 0;
++}
++void mipv6_rr_invalidate_nonce(u16 nonce_ind)
++{
++      spin_lock_bh(&nonce_lock);
++      if (nonce_ind > MAX_NONCES) {
++              spin_unlock_bh(&nonce_lock);
++              return;
++      }
++      nonce_table[nonce_ind].invalid = 1;
++      spin_unlock_bh(&nonce_lock);
++}
++/* Returns a pointer to a new nonce  */
++struct mipv6_rr_nonce * mipv6_rr_get_new_nonce(void)
++{
++      struct mipv6_rr_nonce *nce = kmalloc(sizeof(*nce), GFP_ATOMIC);
++
++      if (!nce)
++              return NULL;
++      // Lock nonces here
++      spin_lock_bh(&nonce_lock);
++      // If nonce is not fresh create new one 
++      if (!nonce_is_fresh(&nonce_table[curr_index], MIPV6_RR_NONCE_LIFETIME)) {
++              // increment the last nonce pointer and create new nonce
++              curr_index++;
++              // Wrap around
++              if (curr_index == MAX_NONCES)
++                      curr_index = 0;
++              // Get random data to fill the nonce data
++              get_random_bytes(nonce_table[curr_index].nonce.data, MIPV6_RR_NONCE_DATA_LENGTH);
++              // Fill the index field
++              nonce_table[curr_index].nonce.index = curr_index;
++              nonce_table[curr_index].invalid = 0;
++              nonce_table[curr_index].timestamp = jiffies;
++      }
++      spin_unlock_bh(&nonce_lock);
++      memcpy(nce, &nonce_table[curr_index].nonce, sizeof(*nce));
++      // Unlock nonces
++      return nce;
++}
++/** mipv6_rr_nonce_get_by_index - returns a nonce for index 
++ * @nonce_ind : index of the nonce
++ *
++ * Returns a nonce or NULL if the nonce index was invalid or the nonce 
++ * for the index was not fresh.
++ */
++struct mipv6_rr_nonce * mipv6_rr_nonce_get_by_index(u16 nonce_ind)
++{
++      struct mipv6_rr_nonce *nce = NULL;
++      
++      spin_lock_bh(&nonce_lock);
++      if (nonce_ind >= MAX_NONCES) {
++              DEBUG(DBG_WARNING, "Nonce index field from BU invalid");
++
++              /* Here a double of the nonce_lifetime is used for freshness 
++               * verification, since the nonces 
++               * are not created in response to every initiator packet
++               */
++      } else if (nonce_is_fresh(&nonce_table[nonce_ind], 2 * MIPV6_RR_NONCE_LIFETIME)) {
++              nce = kmalloc(sizeof(*nce), GFP_ATOMIC);
++              memcpy(nce, &nonce_table[nonce_ind].nonce, sizeof(*nce));
++      }
++      spin_unlock_bh(&nonce_lock);
++
++      return nce;
++}
++
++/* Fills rr test init cookies with random bytes */  
++void mipv6_rr_mn_cookie_create(u8 *cookie)
++{
++      get_random_bytes(cookie, MIPV6_RR_COOKIE_LENGTH);
++}
++
++/** mipv6_rr_cookie_create - builds a home or care-of cookie
++ * 
++ * @addr : the home or care-of address from HoTI or CoTI
++ * @ckie : memory where the cookie is copied to
++ * @nce : pointer to a nonce used for the calculation, nce is freed during the function
++ *
++ */
++int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie,
++              u16 nonce_index)
++{
++      struct ah_processing ah_proc;
++      u8 digest[HMAC_SHA1_HASH_LEN];
++      struct mipv6_rr_nonce *nce;
++
++      if ((nce = mipv6_rr_nonce_get_by_index(nonce_index))== NULL)
++              return -1;
++
++      if (*ckie == NULL && (*ckie = kmalloc(MIPV6_RR_COOKIE_LENGTH,
++                                      GFP_ATOMIC)) == NULL) {
++              kfree(nce);
++              return -1;
++      }
++      /* Calculate the full hmac-sha1 digest from address and nonce using the secret key of cn */
++      
++      if (ah_hmac_sha1_init(&ah_proc, k_CN, HMAC_SHA1_KEY_SIZE) < 0) {
++              DEBUG(DBG_ERROR, "Hmac sha1 initialization failed");
++              kfree(nce);
++              return -1;
++      }
++
++      ah_hmac_sha1_loop(&ah_proc, addr, sizeof(*addr));
++      ah_hmac_sha1_loop(&ah_proc, nce->data,  MIPV6_RR_NONCE_DATA_LENGTH);
++      ah_hmac_sha1_result(&ah_proc, digest);
++
++      
++      /* clean up nonce */
++      kfree(nce);
++
++      /* Copy first 64 bits of hash target to the cookie */ 
++      memcpy(*ckie, digest, MIPV6_RR_COOKIE_LENGTH);
++      return 0;
++}
++
++/** mipv6_rr_key_calc - creates BU authentication key
++ * 
++ * @hoc : Home Cookie 
++ * @coc : Care-of Cookie 
++ * 
++ * Returns BU authentication key of length HMAC_SHA1_KEY_SIZE  or NULL in error cases, 
++ * caller needs to free the key.
++ */
++u8 *mipv6_rr_key_calc(u8 *hoc, u8 *coc)
++{
++      
++      u8 *key_bu = kmalloc(HMAC_SHA1_KEY_SIZE, GFP_ATOMIC);
++      SHA1_CTX c;
++
++      if (!key_bu) {
++              DEBUG(DBG_CRITICAL, "Memory allocation failed, could nort create BU authentication key");
++              return NULL;
++      }
++
++      /* Calculate the key from home and care-of cookies 
++       * Kbu = sha1(home_cookie | care-of cookie) 
++       * or KBu = sha1(home_cookie), if MN deregisters
++       */
++      sha1_init(&c);
++      sha1_compute(&c, hoc, MIPV6_RR_COOKIE_LENGTH);
++      if (coc)
++              sha1_compute(&c, coc, MIPV6_RR_COOKIE_LENGTH);
++      sha1_final(&c, key_bu);
++      DEBUG(DBG_RR, "Home and Care-of cookies used for calculating key ");
++      debug_print_buffer(DBG_RR, hoc,  MIPV6_RR_COOKIE_LENGTH);
++      if (coc)        
++              debug_print_buffer(DBG_RR, coc,  MIPV6_RR_COOKIE_LENGTH);
++
++      return key_bu;
++}
++
++void mipv6_rr_init(void)
++{
++      get_random_bytes(k_CN, HMAC_SHA1_KEY_SIZE);
++      memset(nonce_table, 0, MAX_NONCES * sizeof(struct nonce_timestamp));
++}
++
++#ifdef TEST_MIPV6_RR_CRYPTO
++void mipv6_test_rr(void)
++{
++      struct mipv6_rr_nonce *nonce;
++      struct in6_addr a1, a2;
++      int ind1, ind2;
++      u8 *ckie1 = NULL, *ckie2 = NULL;
++      u8 *key_mn = NULL, *key_cn = NULL;
++      mipv6_init_rr();
++
++      nonce = mipv6_rr_get_new_nonce();
++      if (!nonce) {
++              printk("mipv6_rr_get_new_nonce() failed, at 1! \n");
++              return;
++      }
++      mipv6_rr_cookie_create(&a1, &ckie1, nonce->index);
++      ind1 = nonce->index;
++      kfree(nonce);
++
++      nonce = mipv6_rr_get_new_nonce();
++      if (!nonce) {
++              printk("mipv6_rr_get_new_nonce() failed, at 2! \n");
++              return;
++      }
++
++      mipv6_rr_cookie_create(&a2, &ckie2, nonce->index); 
++      ind2 = nonce->index;
++      key_mn =  mipv6_rr_key_calc(ckie1, ckie2);
++
++      /* Create home and coa cookies based on indices */
++      mipv6_rr_cookie_create(&a1, &ckie1, ind1);
++      mipv6_rr_cookie_create(&a2, &ckie2, ind2);
++      key_cn =  mipv6_rr_key_calc(ckie1, ckie2);             
++      if (!key_cn || !key_mn) {
++              printk("creation of secret key failed!\n");
++              return;
++      }
++      if(memcmp(key_cn, key_mn, HMAC_SHA1_KEY_SIZE))
++              printk("mipv6_rr_key_calc produced different keys for MN and CN \n");
++      else
++              printk("mipv6_rr_crypto test OK\n");
++      kfree(nonce);
++      kfree(key_cn);
++      kfree(key_mn);
++}
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.h
+@@ -0,0 +1,72 @@
++/*
++ *      MIPL Mobile IPv6 Return routability crypto prototypes
++ *
++ *      $Id:$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _RR_CRYPTO
++#define _RR_CRYPTO
++
++#include <linux/in6.h>
++
++/* Macros and data structures */
++
++#define MIPV6_RR_NONCE_LIFETIME 60 
++#define MIPV6_RR_NONCE_DATA_LENGTH 8
++#define MIPV6_RR_COOKIE_LENGTH 8
++#define COOKIE_SIZE 8
++#define MAX_NONCES 4
++#define HMAC_SHA1_KEY_SIZE 20
++ 
++struct mipv6_rr_nonce {
++      u_int16_t index;
++      u_int8_t data[MIPV6_RR_NONCE_DATA_LENGTH];
++};
++
++struct nonce_timestamp {
++      struct  mipv6_rr_nonce nonce;
++      unsigned long timestamp;
++      u_int8_t invalid; 
++};
++
++/* Function definitions */
++
++/* Return 1 if equal, 0 if not */
++static __inline__ int mipv6_equal_cookies(u8 *c1, u8 *c2)
++{
++      return (memcmp(c1, c2, MIPV6_RR_COOKIE_LENGTH) == 0);
++}
++
++/* Function declarations */
++
++/* Create cookie for HoTi and CoTi */
++extern void mipv6_rr_mn_cookie_create(u8 *cookie);
++
++/* Create cookie for HoT and CoT */
++extern int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, u16 nonce_index);
++
++/* Calculate return routability key from home and care-of cookies, key length is 
++ *  HMAC_SHA1_KEY_SIZE  
++ */
++extern u_int8_t *mipv6_rr_key_calc(u8 *hoc, u8 *coc);
++
++extern struct mipv6_rr_nonce *mipv6_rr_get_new_nonce(void);
++
++/* For avoiding replay attacks when MN deregisters */
++extern void mipv6_rr_invalidate_nonce(u16 nonce_index);
++/*
++ * initializes the return routability crypto
++ */
++
++void mipv6_rr_init(void);
++
++#ifdef TEST_MIPV6_RR_CRYPTO
++void mipv6_test_rr(void);
++#endif /* TEST_MIPV6_RR_CRYPTO */
++
++#endif /* RR_CRYPTO */
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.c
+@@ -0,0 +1,349 @@
++/**
++ * Sorted list - linked list with sortkey.
++ *
++ * Authors:
++ * Jaakko Laine <medved@iki.fi>
++ *
++ * $Id$
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/string.h>
++
++struct mipv6_sorted_list_entry {
++      struct list_head list;
++      void *data;
++      int datalen;
++      unsigned long sortkey;
++};
++
++/**
++ * compare - compares two arbitrary data items
++ * @data1: first data item
++ * @data2: second data item
++ * @datalen: length of data items in bits
++ *
++ * datalen is in bits!
++ */
++int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen)
++{
++      int n = datalen;
++      __u8 * ptr1 = (__u8 *)data1;
++      __u8 * ptr2 = (__u8 *)data2;
++      
++      for (; n>=0; n-=8, ptr1++, ptr2++) {
++              if (n >= 8) {
++                      if (*ptr1 != *ptr2)
++                              return 0;
++              } else {
++                      if ((*ptr1 ^ *ptr2) & ((~0) << (8 - n)))
++                              return 0;
++              }
++      }
++
++      return 1;
++}
++
++/**
++ * mipv6_slist_add - add an entry to sorted list
++ * @head: list_head of the sorted list
++ * @data: item to store
++ * @datalen: length of data (in bytes)
++ * @key: sortkey of item
++ *
++ * Allocates memory for entry and data
++ */
++int mipv6_slist_add(struct list_head *head, void *data, int datalen,
++                  unsigned long sortkey)
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry, *tmp, *next;
++
++      entry = kmalloc(sizeof(struct mipv6_sorted_list_entry), GFP_ATOMIC);
++
++      if (!entry)
++              return -1;
++
++      entry->data = kmalloc(datalen, GFP_ATOMIC);
++
++      if (!entry->data) {
++              kfree(entry);
++              return -1;
++      }
++
++      memcpy(entry->data, data, datalen);
++      entry->datalen = datalen;
++      entry->sortkey = sortkey;
++
++      if ((pos = head->next) == head) {
++              list_add(&entry->list, head);
++              return 0;
++      }
++
++      tmp = list_entry(pos, struct mipv6_sorted_list_entry, list);
++      if (entry->sortkey < tmp->sortkey) {
++              list_add(&entry->list, head);
++              return 0;
++      }
++
++      for (; pos != head; pos = pos->next) {
++              tmp = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (pos->next == head) {
++                      list_add(&entry->list, &tmp->list);
++                      return 0;
++              }
++              next = list_entry(pos->next, struct mipv6_sorted_list_entry, list);
++              if (entry->sortkey >= tmp->sortkey && entry->sortkey < next->sortkey) {
++                      list_add(&entry->list, &tmp->list);
++                      return 0;
++              }
++      }
++
++      /* never reached */
++      return -1;
++}
++
++/**
++ * mipv6_slist_get_first - get the first data item in the list
++ * @head: list_head of the sorted list
++ *
++ * Returns the actual data item, not copy, so don't kfree it
++ */
++void *mipv6_slist_get_first(struct list_head *head)
++{
++      struct mipv6_sorted_list_entry *entry;
++
++      if (list_empty(head))
++              return NULL;
++
++      entry = list_entry(head->next, struct mipv6_sorted_list_entry, list);
++      return entry->data;
++}
++
++/**
++ * mipv6_slist_del_first - delete (and get) the first item in list
++ * @head: list_head of the sorted list
++ *
++ * Remember to kfree the item
++ */
++void *mipv6_slist_del_first(struct list_head *head)
++{
++      void *tmp;
++      struct mipv6_sorted_list_entry *entry;
++
++      if (list_empty(head))
++              return NULL;
++
++      entry = list_entry(head->next, struct mipv6_sorted_list_entry, list);
++      tmp = entry->data;
++
++      list_del(head->next);
++      kfree(entry);
++
++      return tmp;
++}
++
++/**
++ * mipv6_slist_del_item - delete entry
++ * @head: list_head of the sorted list
++ * @data: item to delete
++ * @compare: function used for comparing the data items
++ *
++ * compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen)
++ */
++int mipv6_slist_del_item(struct list_head *head, void *data,
++                       int (*compare)(const void *data1, const void *data2,
++                                      int datalen))
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++
++      for(pos = head->next; pos != head; pos = pos->next) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (compare(data, entry->data, entry->datalen)) {
++                      list_del(pos);
++                      kfree(entry->data);
++                      kfree(entry);
++                      return 0;
++              }
++      }
++
++      return -1;
++}
++
++/**
++ * mipv6_slist_get_first_key - get sortkey of the first item
++ * @head: list_head of the sorted list
++ */
++unsigned long mipv6_slist_get_first_key(struct list_head *head)
++{
++      struct mipv6_sorted_list_entry *entry;
++
++      if (list_empty(head))
++              return 0;
++
++      entry = list_entry(head->next, struct mipv6_sorted_list_entry, list);
++      return entry->sortkey;
++}
++
++/**
++ * mipv6_slist_get_key - get sortkey of the data item
++ * @head: list_head of the sorted list
++ * @data: the item to search for
++ * @compare: function used for comparing the data items
++ *
++ * compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen)
++ */
++unsigned long mipv6_slist_get_key(struct list_head *head, void *data,
++                                int (*compare)(const void *data1,
++                                               const void *data2,
++                                               int datalen))
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++      
++      for(pos = head->next; pos != head; pos = pos->next) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (compare(data, entry->data, entry->datalen))
++                      return entry->sortkey;
++      }
++      
++      return 0;
++}
++
++/**
++ * mipv6_slist_get_data - get the data item identified by sortkey
++ * @head: list_head of the sorted list
++ * @key: sortkey of the item
++ *
++ * Returns the actual data item, not copy, so don't kfree it
++ */
++void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey)
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++
++      list_for_each(pos, head) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (entry->sortkey == sortkey) 
++                      return entry->data;
++      }
++
++      return NULL;
++}
++
++/**
++ * reorder_entry - move an entry to a new position according to sortkey
++ * @head: list_head of the sorted list
++ * @entry_pos: current place of the entry
++ * @key: new sortkey
++ */
++static void reorder_entry(struct list_head *head, struct list_head *entry_pos,
++                        unsigned long sortkey)
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++
++      list_del(entry_pos);
++
++      for (pos = head->next; pos != head; pos = pos->next) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (sortkey >= entry->sortkey) {
++                      list_add(entry_pos, &entry->list);
++                      return;
++              }
++      }
++
++      list_add(entry_pos, head);
++}
++
++/**
++ * mipv6_slist_modify - modify data item
++ * @head: list_head of the sorted list
++ * @data: item, whose sortkey is to be modified
++ * @datalen: datalen in bytes
++ * @new_key: new sortkey
++ * @compare: function used for comparing the data items
++ *
++ * Compies the new data on top of the old one, if compare function returns
++ * true. If there's no matching entry, new one will be created.
++ * Compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen)
++ */
++int mipv6_slist_modify(struct list_head *head, void *data, int datalen,
++                     unsigned long new_key,
++                     int (*compare)(const void *data1, const void *data2,
++                                    int datalen))
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++
++      for (pos = head->next; pos != head; pos = pos->next) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (compare(data, entry->data, datalen)) {
++                      memcpy(entry->data, data, datalen);
++                      entry->sortkey = new_key;
++                      reorder_entry(head, &entry->list, new_key);
++                      return 0;
++              }
++      }
++
++      return mipv6_slist_add(head, data, datalen, new_key);
++}
++
++/**
++ * mipv6_slist_push_first - move the first entry to place indicated by new_key
++ * @head: list_head of the sorted list
++ * @new_key: new sortkey
++ */
++int mipv6_slist_push_first(struct list_head *head, unsigned long new_key)
++{
++      struct mipv6_sorted_list_entry *entry;
++
++      if (list_empty(head))
++              return -1;
++
++      entry = list_entry(head->next, struct mipv6_sorted_list_entry, list);
++      entry->sortkey = new_key;
++
++      reorder_entry(head, head->next, new_key);
++      return 0;
++}
++
++/**
++ * mipv6_slist_for_each - apply func to every item in list
++ * @head: list_head of the sorted list
++ * @args: args to pass to func
++ * @func: function to use
++ *
++ * function must be of type
++ * int (*func)(void *data, void *args, unsigned long sortkey)
++ * List iteration will stop once func has been applied to every item
++ * or when func returns true
++ */
++int mipv6_slist_for_each(struct list_head *head, void *args,
++                       int (*func)(void *data, void *args,
++                                   unsigned long sortkey))
++{
++      struct list_head *pos;
++      struct mipv6_sorted_list_entry *entry;
++
++      list_for_each(pos, head) {
++              entry = list_entry(pos, struct mipv6_sorted_list_entry, list);
++              if (func(entry->data, args, entry->sortkey))
++                      break;
++      }
++
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.h
+@@ -0,0 +1,133 @@
++/*
++ *      Sorted list - linked list with sortkey
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++/**
++ * compare - compares two arbitrary data items
++ * @data1: first data item
++ * @data2: second data item
++ * @datalen: length of data items in bits
++ *
++ * datalen is in bits!
++ */
++int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen);
++
++/**
++ * mipv6_slist_add - add an entry to sorted list
++ * @head: list_head of the sorted list
++ * @data: item to store
++ * @datalen: length of data (in bytes)
++ * @key: sortkey of item
++ *
++ * Allocates memory for entry and data
++ */
++int mipv6_slist_add(struct list_head *head, void *data, int datalen,
++                  unsigned long sortkey);
++
++/**
++ * mipv6_slist_get_first - get the first data item in the list
++ * @head: list_head of the sorted list
++ *
++ * Returns the actual data item, not copy, so don't kfree it
++ */
++void *mipv6_slist_get_first(struct list_head *head);
++
++/**
++ * mipv6_slist_del_first - delete (and get) the first item in list
++ * @head: list_head of the sorted list
++ *
++ * Remember to kfree the item
++ */
++void *mipv6_slist_del_first(struct list_head *head);
++
++/**
++ * mipv6_slist_del_item - delete entry
++ * @head: list_head of the sorted list
++ * @data: item to delete
++ * @compare: function used for comparing the data items
++ *
++ * compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen) where
++ * datalen is in bits
++ */
++int mipv6_slist_del_item(struct list_head *head, void *data,
++                       int (*compare)(const void *data1, const void *data2,
++                                      int datalen));
++
++/**
++ * mipv6_slist_get_first_key - get sortkey of the first item
++ * @head: list_head of the sorted list
++ */
++unsigned long mipv6_slist_get_first_key(struct list_head *head);
++
++/**
++ * mipv6_slist_get_key - get sortkey of the data item
++ * @head: list_head of the sorted list
++ * @data: the item to search for
++ * @compare: function used for comparing the data items
++ *
++ * compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen) where
++ * datalen is in bits
++ */
++unsigned long mipv6_slist_get_key(struct list_head *head, void *data,
++                                int (*compare)(const void *data1,
++                                               const void *data2,
++                                               int datalen));
++
++/**
++ * mipv6_slist_get_data - get the data item identified by sortkey
++ * @head: list_head of the sorted list
++ * @key: sortkey of the item
++ *
++ * Returns the actual data item, not copy, so don't kfree it
++ */
++void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey);
++
++/**
++ * mipv6_slist_modify - modify data item
++ * @head: list_head of the sorted list
++ * @data: item, whose sortkey is to be modified
++ * @datalen: datalen in bytes
++ * @new_key: new sortkey
++ * @compare: function used for comparing the data items
++ *
++ * Compies the new data on top of the old one, if compare function returns
++ * non-negative. If there's no matching entry, new one will be created.
++ * Compare function needs to have prototype
++ * int (*compare)(const void *data1, const void *data2, int datalen) where
++ * datalen is in bits.
++ */
++int mipv6_slist_modify(struct list_head *head, void *data, int datalen,
++                     unsigned long new_key,
++                     int (*compare)(const void *data1, const void *data2,
++                                    int datalen));
++
++/**
++ * mipv6_slist_push_first - move the first entry to place indicated by new_key
++ * @head: list_head of the sorted list
++ * @new_key: new sortkey
++ */
++int mipv6_slist_push_first(struct list_head *head, unsigned long new_key);
++
++/**
++ * mipv6_slist_for_each - apply func to every item in list
++ * @head: list_head of the sorted list
++ * @args: args to pass to func
++ * @func: function to use
++ *
++ * function must be of type
++ * int (*func)(void *data, void *args, unsigned long sortkey)
++ * List iteration will stop once func has been applied to every item
++ * or when func returns true
++ */
++int mipv6_slist_for_each(struct list_head *head, void *args,
++                       int (*func)(void *data, void *args,
++                                   unsigned long sortkey));
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/stats.c
+@@ -0,0 +1,90 @@
++/*
++ *      Statistics module
++ *
++ *      Authors:
++ *      Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *     
++ *      Changes:
++ *      Krishna Kumar, 
++ *      Venkata Jagana   :  SMP locking fix  
++ */
++
++#include <linux/config.h>
++#include <linux/proc_fs.h>
++#include "stats.h"
++
++struct mipv6_statistics mipv6_stats;
++
++static int proc_info_dump(
++      char *buffer, char **start,
++      off_t offset, int length)
++{
++      struct inf {
++              char *name;
++              int *value;
++      } int_stats[] = {
++              {"NEncapsulations", &mipv6_stats.n_encapsulations},
++              {"NDecapsulations", &mipv6_stats.n_decapsulations},
++              {"NBindRefreshRqsRcvd", &mipv6_stats.n_brr_rcvd},
++              {"NHomeTestInitsRcvd", &mipv6_stats.n_hoti_rcvd},
++              {"NCareofTestInitsRcvd", &mipv6_stats.n_coti_rcvd},
++              {"NHomeTestRcvd", &mipv6_stats.n_hot_rcvd},
++              {"NCareofTestRcvd", &mipv6_stats.n_cot_rcvd},
++              {"NBindUpdatesRcvd", &mipv6_stats.n_bu_rcvd},
++              {"NBindAcksRcvd", &mipv6_stats.n_ba_rcvd},
++              {"NBindNAcksRcvd", &mipv6_stats.n_ban_rcvd},
++              {"NBindErrorsRcvd", &mipv6_stats.n_be_rcvd},
++              {"NBindRefreshRqsSent", &mipv6_stats.n_brr_sent},
++              {"NHomeTestInitsSent", &mipv6_stats.n_hoti_sent},
++              {"NCareofTestInitsSent", &mipv6_stats.n_coti_sent},
++              {"NHomeTestSent", &mipv6_stats.n_hot_sent},
++              {"NCareofTestSent", &mipv6_stats.n_cot_sent},
++              {"NBindUpdatesSent", &mipv6_stats.n_bu_sent},
++              {"NBindAcksSent", &mipv6_stats.n_ba_sent},
++              {"NBindNAcksSent", &mipv6_stats.n_ban_sent},
++              {"NBindErrorsSent", &mipv6_stats.n_be_sent},
++              {"NBindUpdatesDropAuth", &mipv6_stats.n_bu_drop.auth},
++              {"NBindUpdatesDropInvalid", &mipv6_stats.n_bu_drop.invalid},
++              {"NBindUpdatesDropMisc", &mipv6_stats.n_bu_drop.misc},
++              {"NBindAcksDropAuth", &mipv6_stats.n_bu_drop.auth},
++              {"NBindAcksDropInvalid", &mipv6_stats.n_bu_drop.invalid},
++              {"NBindAcksDropMisc", &mipv6_stats.n_bu_drop.misc},
++              {"NBindRqsDropAuth", &mipv6_stats.n_bu_drop.auth},
++              {"NBindRqsDropInvalid", &mipv6_stats.n_bu_drop.invalid},
++              {"NBindRqsDropMisc", &mipv6_stats.n_bu_drop.misc}
++      };
++
++      int i;
++      int len = 0;
++      for(i=0; i<sizeof(int_stats) / sizeof(struct inf); i++) {
++              len += sprintf(buffer + len, "%s = %d\n",
++                             int_stats[i].name, *int_stats[i].value);
++      }
++
++      *start = buffer + offset;
++
++      len -= offset;
++
++      if(len > length) len = length;
++
++      return len;
++}
++
++int mipv6_stats_init(void)
++{
++      memset(&mipv6_stats, 0, sizeof(struct mipv6_statistics));
++      proc_net_create("mip6_stat", 0, proc_info_dump);
++      return 0;
++}
++
++void mipv6_stats_exit(void)
++{
++      proc_net_remove("mip6_stat");
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/stats.h
+@@ -0,0 +1,71 @@
++/*
++ *      MIPL Mobile IPv6 Statistics header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _STATS_H
++#define _STATS_H
++
++struct mipv6_drop {
++      __u32 auth;
++      __u32 invalid;
++      __u32 misc;
++};
++
++struct mipv6_statistics {
++      int n_encapsulations;
++      int n_decapsulations;
++      int n_mh_in_msg;
++      int n_mh_in_error;
++      int n_mh_out_msg;
++      int n_mh_out_error;
++
++      int n_brr_rcvd;
++      int n_hoti_rcvd;
++      int n_coti_rcvd;
++      int n_hot_rcvd;
++      int n_cot_rcvd;
++      int n_bu_rcvd;
++      int n_ba_rcvd;
++      int n_ban_rcvd;
++      int n_be_rcvd;
++
++      int n_brr_sent;
++      int n_hoti_sent;
++      int n_coti_sent;
++      int n_hot_sent;
++      int n_cot_sent;
++      int n_bu_sent;
++      int n_ba_sent;
++      int n_ban_sent;
++      int n_be_sent;
++
++      int n_ha_rcvd;
++      int n_ha_sent;
++
++      struct mipv6_drop n_bu_drop;
++      struct mipv6_drop n_ba_drop;
++      struct mipv6_drop n_brr_drop;
++      struct mipv6_drop n_be_drop;
++      struct mipv6_drop n_ha_drop;
++};
++
++extern struct mipv6_statistics mipv6_stats;
++
++#ifdef CONFIG_SMP
++/* atomic_t is max 24 bits long */
++#define MIPV6_INC_STATS(X) atomic_inc((atomic_t *)&mipv6_stats.X);
++#else
++#define MIPV6_INC_STATS(X) mipv6_stats.X++;
++#endif
++
++int mipv6_stats_init(void);
++void mipv6_stats_exit(void);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel.h
+@@ -0,0 +1,35 @@
++/*
++ *      MIPL Mobile IPv6 IP6-IP6 tunneling header file
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _TUNNEL_H
++#define _TUNNEL_H
++
++#include <linux/in6.h>
++#include <linux/if_arp.h>
++#include <net/ipv6_tunnel.h>
++
++static __inline__ int is_mip6_tnl(struct ip6_tnl *t)
++{
++      return (t != NULL && 
++              t->parms.flags & IP6_TNL_F_KERNEL_DEV &&
++              t->parms.flags & IP6_TNL_F_MIP6_DEV);
++                      
++}
++
++static __inline__ int dev_is_mip6_tnl(struct net_device *dev)
++{
++      struct ip6_tnl *t = (struct ip6_tnl *)dev->priv;
++      return (dev->type == ARPHRD_TUNNEL6 && is_mip6_tnl(t));
++}
++
++
++#endif
++
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.c
+@@ -0,0 +1,264 @@
++/*
++ *    IPv6-IPv6 tunneling module
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Ville Nuorvala          <vnuorval@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/net.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/init.h>
++#include <linux/route.h>
++#include <linux/ipv6_route.h>
++
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include <net/protocol.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/dst.h>
++#include <net/addrconf.h>
++
++#include "tunnel.h"
++#include "debug.h"
++#include "stats.h"
++#include "config.h"
++
++#define MIPV6_TNL_MAX IP6_TNL_MAX
++#define MIPV6_TNL_MIN 1
++
++int mipv6_max_tnls = 3;
++int mipv6_min_tnls = 1;
++
++DECLARE_MUTEX(tnl_sem);
++
++int mipv6_max_tnls_sysctl(ctl_table *ctl, int write, struct file *filp,
++                        void *buffer, size_t *lenp)
++{
++      int err;
++      
++      DEBUG_FUNC();
++
++      down(&tnl_sem);
++      if (write) {
++              int diff;
++              int old_max_tnls = mipv6_max_tnls;
++              err = proc_dointvec(ctl, write, filp, buffer, lenp);
++              if (err < 0) 
++                      goto out;
++              if (mipv6_max_tnls < mipv6_min_tnls || 
++                  mipv6_max_tnls > MIPV6_TNL_MAX) {
++                      mipv6_max_tnls = old_max_tnls;
++                      goto out;
++              }
++              if (mipv6_max_tnls < old_max_tnls) {
++                      diff = old_max_tnls - mipv6_max_tnls;
++                      ip6ip6_tnl_dec_max_kdev_count(diff);
++              } else if (mipv6_max_tnls > old_max_tnls) {
++                      diff = mipv6_max_tnls - old_max_tnls;
++                      ip6ip6_tnl_inc_max_kdev_count(diff);
++              }
++      } else {
++              err = proc_dointvec(ctl, write, filp, buffer, lenp);
++      }
++out:
++      up(&tnl_sem);
++      return err;
++}
++
++int mipv6_min_tnls_sysctl(ctl_table *ctl, int write, struct file *filp,
++                        void *buffer, size_t *lenp)
++{
++      int err;
++
++      DEBUG_FUNC();
++
++      down(&tnl_sem);
++      if (write) {
++              int diff;
++              int old_min_tnls = mipv6_min_tnls;
++              err = proc_dointvec(ctl, write, filp, buffer, lenp);
++              if (err < 0) 
++                      goto out;
++              if (mipv6_min_tnls > mipv6_max_tnls || 
++                  mipv6_min_tnls < MIPV6_TNL_MIN) {
++                      mipv6_min_tnls = old_min_tnls;
++                      goto out;
++              }
++              if (mipv6_min_tnls < old_min_tnls) {
++                      diff = old_min_tnls - mipv6_min_tnls;
++                      ip6ip6_tnl_dec_min_kdev_count(diff);
++              } else if (mipv6_min_tnls > old_min_tnls) {
++                      diff = mipv6_min_tnls - old_min_tnls;
++                      ip6ip6_tnl_inc_min_kdev_count(diff);
++              }
++      } else {
++              err = proc_dointvec(ctl, write, filp, buffer, lenp);
++      }
++out:
++      up(&tnl_sem);
++      return err;
++}
++
++static __inline__ int mipv6_tnl_add(struct in6_addr *remote, 
++                                  struct in6_addr *local) 
++{
++      struct ip6_tnl_parm p;
++      int ret;
++
++      DEBUG_FUNC();
++
++      memset(&p, 0, sizeof(p));
++      p.proto = IPPROTO_IPV6;
++      ipv6_addr_copy(&p.laddr, local);
++      ipv6_addr_copy(&p.raddr, remote);
++      p.hop_limit = 255;
++      p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV |
++                 IP6_TNL_F_IGN_ENCAP_LIMIT);
++
++      ret = ip6ip6_kernel_tnl_add(&p);
++      if (ret > 0) {
++              DEBUG(DBG_INFO, "added tunnel from: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", 
++                    NIPV6ADDR(local), NIPV6ADDR(remote));
++      } else {
++              DEBUG(DBG_WARNING, "unable to add tunnel from: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", 
++                    NIPV6ADDR(local), NIPV6ADDR(remote));             
++      }
++      return ret;
++}
++
++static __inline__ int mipv6_tnl_del(struct in6_addr *remote, 
++                                  struct in6_addr *local) 
++{
++      struct ip6_tnl *t = ip6ip6_tnl_lookup(remote, local);
++      
++      DEBUG_FUNC();
++      
++      if (t != NULL && (t->parms.flags & IP6_TNL_F_MIP6_DEV)) {
++              DEBUG(DBG_INFO, "deleting tunnel from: "
++                    "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", 
++                    NIPV6ADDR(local), NIPV6ADDR(remote));
++
++              return ip6ip6_kernel_tnl_del(t);
++      }
++      return 0;
++}
++
++static int add_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, 
++                         struct in6_addr *home_addr) 
++{
++      struct in6_rtmsg rtmsg;
++      int err;
++      struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr);
++      
++      if (!is_mip6_tnl(t)) {
++              DEBUG(DBG_CRITICAL,"Tunnel missing");
++              return -ENODEV;
++      }
++      
++      DEBUG(DBG_INFO, "adding route to: %x:%x:%x:%x:%x:%x:%x:%x via "
++            "tunnel device", NIPV6ADDR(home_addr));
++
++      memset(&rtmsg, 0, sizeof(rtmsg));
++      ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr);
++      rtmsg.rtmsg_dst_len = 128;
++      rtmsg.rtmsg_type = RTMSG_NEWROUTE;
++      rtmsg.rtmsg_flags = RTF_UP | RTF_NONEXTHOP | RTF_HOST | RTF_MOBILENODE;
++      rtmsg.rtmsg_ifindex = t->dev->ifindex;
++      rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6;
++      if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) {
++              err = 0;
++      }
++      return err;
++}
++
++static void del_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, 
++                          struct in6_addr *home_addr) 
++{
++      struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr);
++
++      DEBUG_FUNC();
++
++      if (is_mip6_tnl(t)) {
++              struct in6_rtmsg rtmsg;
++
++              DEBUG(DBG_INFO, "deleting route to: %x:%x:%x:%x:%x:%x:%x:%x "
++                    " via tunnel device", NIPV6ADDR(home_addr));
++
++              memset(&rtmsg, 0, sizeof(rtmsg));
++              ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr);
++              rtmsg.rtmsg_dst_len = 128;
++              rtmsg.rtmsg_ifindex = t->dev->ifindex;
++              rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6;
++              ip6_route_del(&rtmsg, NULL);
++      }
++}
++
++
++int mipv6_add_tnl_to_mn(struct in6_addr *coa, 
++                      struct in6_addr *ha_addr,
++                      struct in6_addr *home_addr)
++{
++      int ret;
++
++      DEBUG_FUNC();
++
++      ret = mipv6_tnl_add(coa, ha_addr);
++
++      if (ret > 0) {
++              int err = add_route_to_mn(coa, ha_addr, home_addr);
++              if (err) {
++                      if (err != -ENODEV) {
++                              mipv6_tnl_del(coa, ha_addr);
++                      }
++                      return err;
++              }
++      }
++      return ret;
++} 
++
++int mipv6_del_tnl_to_mn(struct in6_addr *coa, 
++                      struct in6_addr *ha_addr,
++                      struct in6_addr *home_addr)
++{
++      DEBUG_FUNC();
++      del_route_to_mn(coa, ha_addr, home_addr);
++      return mipv6_tnl_del(coa, ha_addr);
++} 
++
++__init void mipv6_initialize_tunnel(void)
++{
++      down(&tnl_sem);
++      ip6ip6_tnl_inc_max_kdev_count(mipv6_max_tnls);
++      ip6ip6_tnl_inc_min_kdev_count(mipv6_min_tnls);
++      up(&tnl_sem);
++      mip6_fn.bce_tnl_rt_add = add_route_to_mn;
++      mip6_fn.bce_tnl_rt_del = del_route_to_mn;
++}
++
++__exit void mipv6_shutdown_tunnel(void)
++{
++      mip6_fn.bce_tnl_rt_del = NULL;
++      mip6_fn.bce_tnl_rt_add = NULL;
++      down(&tnl_sem);
++      ip6ip6_tnl_dec_min_kdev_count(mipv6_min_tnls);
++      ip6ip6_tnl_dec_max_kdev_count(mipv6_max_tnls);
++      up(&tnl_sem);
++}
++
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.h
+@@ -0,0 +1,20 @@
++#ifndef _TUNNEL_HA_H
++#define _TUNNEL_HA_H
++
++#include "tunnel.h"
++
++extern int mipv6_max_tnls;
++extern int mipv6_min_tnls;
++
++extern void mipv6_initialize_tunnel(void);
++extern void mipv6_shutdown_tunnel(void);
++
++extern int mipv6_add_tnl_to_mn(struct in6_addr *coa, 
++                             struct in6_addr *ha_addr,
++                             struct in6_addr *home_addr);
++
++extern int mipv6_del_tnl_to_mn(struct in6_addr *coa, 
++                             struct in6_addr *ha_addr,
++                             struct in6_addr *home_addr);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.c
+@@ -0,0 +1,160 @@
++/*
++ *    IPv6-IPv6 tunneling module
++ *
++ *    Authors:
++ *    Sami Kivisaari          <skivisaa@cc.hut.fi>
++ *    Ville Nuorvala          <vnuorval@tml.hut.fi>
++ *
++ *    $Id$
++ *
++ *    This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ *
++ */
++
++#include <linux/net.h>
++#include <linux/skbuff.h>
++#include <linux/ipv6.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/init.h>
++#include <linux/route.h>
++#include <linux/ipv6_route.h>
++
++#ifdef CONFIG_SYSCTL
++#include <linux/sysctl.h>
++#endif /* CONFIG_SYSCTL */
++
++#include <net/protocol.h>
++#include <net/ipv6.h>
++#include <net/ip6_route.h>
++#include <net/dst.h>
++#include <net/addrconf.h>
++
++#include "tunnel.h"
++#include "debug.h"
++#include "stats.h"
++
++static struct net_device *mn_ha_tdev;
++
++static spinlock_t mn_ha_lock = SPIN_LOCK_UNLOCKED;
++
++static __inline__ int add_reverse_route(struct in6_addr *ha_addr,
++                                      struct in6_addr *home_addr, 
++                                      struct net_device *tdev) 
++{
++      struct in6_rtmsg rtmsg;
++      int err;
++
++      DEBUG_FUNC();
++
++      memset(&rtmsg, 0, sizeof(rtmsg));
++      rtmsg.rtmsg_type = RTMSG_NEWROUTE;
++      ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr);
++      rtmsg.rtmsg_src_len = 128;
++      rtmsg.rtmsg_flags = RTF_UP | RTF_DEFAULT;
++      rtmsg.rtmsg_ifindex = tdev->ifindex;
++      rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6;
++      if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) {
++              return 0;
++      }
++      return err;     
++}
++
++static __inline__ void del_reverse_route(struct in6_addr *ha_addr, 
++                                       struct in6_addr *home_addr,
++                                       struct net_device *tdev) 
++{
++      struct in6_rtmsg rtmsg;
++
++      DEBUG(DBG_INFO, "removing reverse route via tunnel device");
++
++      memset(&rtmsg, 0, sizeof(rtmsg));
++      ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr);
++      rtmsg.rtmsg_src_len = 128;
++      rtmsg.rtmsg_ifindex = tdev->ifindex;
++      rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6;
++      ip6_route_del(&rtmsg, NULL);
++}
++
++int mipv6_add_tnl_to_ha(void)
++{
++      struct ip6_tnl_parm p;
++      struct ip6_tnl *t;
++      int err;
++
++      DEBUG_FUNC();
++
++      memset(&p, 0, sizeof(p));
++      p.proto = IPPROTO_IPV6;
++      p.hop_limit = 255;
++      p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV |
++                 IP6_TNL_F_IGN_ENCAP_LIMIT);
++      strcpy(p.name, "mip6mnha1");
++
++      rtnl_lock();
++      if ((err = ip6ip6_tnl_create(&p, &t))) {
++              rtnl_unlock();
++              return err;
++      }
++      spin_lock_bh(&mn_ha_lock);
++
++      if (!mn_ha_tdev) {
++              mn_ha_tdev = t->dev;
++              dev_hold(mn_ha_tdev);
++      }
++      spin_unlock_bh(&mn_ha_lock);
++      dev_open(t->dev);
++      rtnl_unlock();
++      return 0;
++} 
++
++int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr,
++                     struct in6_addr *coa,
++                     struct in6_addr *home_addr)
++{
++      int err = -ENODEV;
++
++      DEBUG_FUNC();
++
++      spin_lock_bh(&mn_ha_lock);
++      if (mn_ha_tdev) {
++              struct ip6_tnl_parm p;
++              memset(&p, 0, sizeof(p));
++              p.proto = IPPROTO_IPV6;
++              ipv6_addr_copy(&p.laddr, coa);
++              ipv6_addr_copy(&p.raddr, ha_addr);
++              p.hop_limit = 255;
++              p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV |
++                         IP6_TNL_F_IGN_ENCAP_LIMIT);
++
++              ip6ip6_tnl_change((struct ip6_tnl *) mn_ha_tdev->priv, &p);
++              if (ipv6_addr_cmp(coa, home_addr)) {
++                      err = add_reverse_route(ha_addr, home_addr, 
++                                              mn_ha_tdev);
++              } else {
++                      del_reverse_route(ha_addr, home_addr, mn_ha_tdev);
++                      err = 0;
++              }
++      }
++      spin_unlock_bh(&mn_ha_lock);
++      return err;
++}
++
++void mipv6_del_tnl_to_ha(void)
++{
++      struct net_device *dev;
++
++      DEBUG_FUNC();
++
++      rtnl_lock();
++      spin_lock_bh(&mn_ha_lock);
++      dev = mn_ha_tdev;
++      mn_ha_tdev = NULL;
++      spin_unlock_bh(&mn_ha_lock);
++      dev_put(dev);
++      unregister_netdevice(dev);
++      rtnl_unlock();
++}
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.h
+@@ -0,0 +1,14 @@
++#ifndef _TUNNEL_MN_H
++#define _TUNNEL_MN_H
++
++#include "tunnel.h"
++
++extern int mipv6_add_tnl_to_ha(void);
++
++extern int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, 
++                            struct in6_addr *coa,
++                            struct in6_addr *home_addr);
++
++extern int mipv6_del_tnl_to_ha(void);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/net/ipv6/mobile_ip6/util.h
+@@ -0,0 +1,91 @@
++/*
++ *      MIPL Mobile IPv6 Utility functions
++ *
++ *      $Id$
++ *
++ *      This program is free software; you can redistribute it and/or
++ *      modify it under the terms of the GNU General Public License
++ *      as published by the Free Software Foundation; either version
++ *      2 of the License, or (at your option) any later version.
++ */
++
++#ifndef _UTIL_H
++#define _UTIL_H
++
++#include <linux/in6.h>
++#include <asm/byteorder.h>
++
++/**
++ * mipv6_prefix_compare - Compare two IPv6 prefixes
++ * @addr: IPv6 address
++ * @prefix: IPv6 address
++ * @nprefix: number of bits to compare
++ *
++ * Perform prefix comparison bitwise for the @nprefix first bits
++ * Returns 1, if the prefixes are the same, 0 otherwise 
++ **/
++static inline int mipv6_prefix_compare(const struct in6_addr *addr,
++                                     const struct in6_addr *prefix, 
++                                     const unsigned int pfix_len)
++{
++      int i;
++      unsigned int nprefix = pfix_len;
++
++      if (nprefix > 128)
++              return 0;
++
++      for (i = 0; nprefix > 0; nprefix -= 32, i++) {
++              if (nprefix >= 32) {
++                      if (addr->s6_addr32[i] != prefix->s6_addr32[i])
++                              return 0;
++              } else {
++                      if (((addr->s6_addr32[i] ^ prefix->s6_addr32[i]) &
++                           ((~0) << (32 - nprefix))) != 0)
++                              return 0;
++                      return 1;
++              }
++      }
++
++      return 1;
++}
++
++/**
++ * homeagent_anycast - Compute Home Agent anycast address
++ * @ac_addr: append home agent anycast suffix to passed prefix
++ * @prefix: prefix ha anycast address is generated from
++ * @plen: length of prefix in bits
++ *
++ * Calculate corresponding Home Agent Anycast Address (RFC2526) in a
++ * given subnet.
++ */
++static inline int 
++mipv6_ha_anycast(struct in6_addr *ac_addr, struct in6_addr *prefix, int plen)
++{
++      if (plen <= 0 || plen > 120)  {
++              /* error, interface id should be minimum 8 bits */
++              return -1;
++      }
++      ipv6_addr_copy(ac_addr, prefix);
++
++      if (plen < 32)
++              ac_addr->s6_addr32[0] |= htonl((u32)(~0) >> plen);
++      if (plen < 64)
++              ac_addr->s6_addr32[1] |= htonl((u32)(~0) >> (plen > 32 ? plen % 32 : 0));
++      if (plen < 92)
++              ac_addr->s6_addr32[2] |= htonl((u32)(~0) >> (plen > 64 ? plen % 32 : 0));
++      if (plen <= 120)
++              ac_addr->s6_addr32[3] |= htonl((u32)(~0) >> (plen > 92 ? plen % 32 : 0));
++
++      /* RFC2526: for interface identifiers in EUI-64
++       * format, the universal/local bit in the interface
++       * identifier MUST be set to 0. */
++      if (plen == 64) {
++              ac_addr->s6_addr32[2] &= (int)htonl(0xfdffffff);
++      }
++      /* Mobile IPv6 Home-Agents anycast id (0x7e) */
++      ac_addr->s6_addr32[3] &= (int)htonl(0xfffffffe);
++
++      return 0;
++}
++
++#endif /* _UTIL_H */
+--- linux-2.4.27/net/ipv6/ndisc.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/ndisc.c
+@@ -23,6 +23,7 @@
+  *                                            and moved to net/core.
+  *    Pekka Savola                    :       RFC2461 validation
+  *    YOSHIFUJI Hideaki @USAGI        :       Verify ND options properly
++ *    Ville Nuorvala                  :       RFC2461 fixes to proxy ND
+  */
+ /* Set to 3 to get tracing... */
+@@ -70,6 +71,7 @@
+ #include <net/ip6_route.h>
+ #include <net/addrconf.h>
+ #include <net/icmp.h>
++#include <net/mipglue.h>
+ #include <net/checksum.h>
+ #include <linux/proc_fs.h>
+@@ -187,6 +189,8 @@
+               case ND_OPT_TARGET_LL_ADDR:
+               case ND_OPT_MTU:
+               case ND_OPT_REDIRECT_HDR:
++              case ND_OPT_RTR_ADV_INTERVAL:
++              case ND_OPT_HOME_AGENT_INFO:
+                       if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
+                               ND_PRINTK2((KERN_WARNING
+                                           "ndisc_parse_options(): duplicated ND6 option found: type=%d\n",
+@@ -372,8 +376,8 @@
+  */
+ void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
+-                 struct in6_addr *daddr, struct in6_addr *solicited_addr,
+-                 int router, int solicited, int override, int inc_opt) 
++              struct in6_addr *daddr, struct in6_addr *solicited_addr, 
++              int router, int solicited, int override, int inc_opt) 
+ {
+       static struct in6_addr tmpaddr;
+       struct inet6_ifaddr *ifp;
+@@ -766,7 +770,8 @@
+               int addr_type = ipv6_addr_type(saddr);
+               if (in6_dev && in6_dev->cnf.forwarding &&
+-                  (addr_type & IPV6_ADDR_UNICAST) &&
++                  (addr_type & IPV6_ADDR_UNICAST || 
++                   addr_type == IPV6_ADDR_ANY) &&
+                   pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
+                       int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+@@ -778,13 +783,21 @@
+                                       nd_tbl.stats.rcv_probes_mcast++;
+                               else
+                                       nd_tbl.stats.rcv_probes_ucast++;
+-                                      
+-                              neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
+-                              if (neigh) {
+-                                      ndisc_send_na(dev, neigh, saddr, &msg->target,
+-                                                    0, 1, 0, 1);
+-                                      neigh_release(neigh);
++                              if (addr_type & IPV6_ADDR_UNICAST) {            
++                                      neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
++
++                                      if (neigh) {
++                                              ndisc_send_na(dev, neigh, saddr, &msg->target,
++                                                            0, 1, 0, 1);
++                                              neigh_release(neigh);
++                                      }
++                              } else {
++                                      /* the proxy should also protect against DAD */
++                                      struct in6_addr maddr;
++                                      ipv6_addr_all_nodes(&maddr);
++                                      ndisc_send_na(dev, NULL, &maddr, &msg->target,
++                                                    0, 0, 0, 1);
+                               }
+                       } else {
+                               struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
+@@ -849,6 +862,9 @@
+               if (ifp->flags & IFA_F_TENTATIVE) {
+                       addrconf_dad_failure(ifp);
+                       return;
++              } else if (ndisc_mip_mn_ha_probe(ifp, lladdr)) {
++                      in6_ifa_put(ifp);
++                      return;
+               }
+               /* What should we make now? The advertisement
+                  is invalid, but ndisc specs say nothing
+@@ -887,6 +903,7 @@
+                            msg->icmph.icmp6_override, 1);
+               neigh_release(neigh);
+       }
++      ndisc_check_mipv6_dad(&msg->target);
+ }
+ static void ndisc_router_discovery(struct sk_buff *skb)
+@@ -894,6 +911,7 @@
+         struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
+       struct neighbour *neigh;
+       struct inet6_dev *in6_dev;
++      int change_rtr;
+       struct rt6_info *rt;
+       int lifetime;
+       struct ndisc_options ndopts;
+@@ -923,10 +941,6 @@
+               ND_PRINTK1("RA: can't find in6 device\n");
+               return;
+       }
+-      if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
+-              in6_dev_put(in6_dev);
+-              return;
+-      }
+       if (!ndisc_parse_options(opt, optlen, &ndopts)) {
+               in6_dev_put(in6_dev);
+@@ -935,7 +949,12 @@
+                                  "ICMP6 RA: invalid ND option, ignored.\n");
+               return;
+       }
++      change_rtr = ndisc_mipv6_ra_rcv(skb, &ndopts);
++      if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
++              in6_dev_put(in6_dev);
++              return;
++      }
+       if (in6_dev->if_flags & IF_RS_SENT) {
+               /*
+                *      flag that an RA was received after an RS was sent
+@@ -963,8 +982,7 @@
+               ip6_del_rt(rt, NULL);
+               rt = NULL;
+       }
+-
+-      if (rt == NULL && lifetime) {
++      if (rt == NULL && lifetime && change_rtr) {
+               ND_PRINTK2("ndisc_rdisc: adding default router\n");
+               rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
+@@ -1087,6 +1105,8 @@
+       if (rt)
+               dst_release(&rt->u.dst);
+       in6_dev_put(in6_dev);
++
++      ndisc_mipv6_change_router(change_rtr);
+ }
+ static void ndisc_redirect_rcv(struct sk_buff *skb)
+--- linux-2.4.27/net/ipv6/raw.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/raw.c
+@@ -43,6 +43,7 @@
+ #include <net/transp_v6.h>
+ #include <net/udp.h>
+ #include <net/inet_common.h>
++#include <net/mipglue.h>
+ #include <net/rawv6.h>
+@@ -641,6 +642,7 @@
+                       hdr.daddr = daddr;
+               else
+                       hdr.daddr = NULL;
++              hdr.daddr = mipv6_get_fake_hdr_daddr(hdr.daddr, daddr);
+               err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
+                                    opt, hlimit, msg->msg_flags);
+--- linux-2.4.27/net/ipv6/route.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/route.c
+@@ -49,6 +49,7 @@
+ #include <net/addrconf.h>
+ #include <net/tcp.h>
+ #include <linux/rtnetlink.h>
++#include <net/mipglue.h>
+ #include <asm/uaccess.h>
+@@ -363,12 +364,8 @@
+               rt->u.dst.flags |= DST_HOST;
+ #ifdef CONFIG_IPV6_SUBTREES
+-              if (rt->rt6i_src.plen && saddr) {
+-                      ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
+-                      rt->rt6i_src.plen = 128;
+-              }
++              rt->rt6i_src.plen = ort->rt6i_src.plen;
+ #endif
+-
+               rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+               dst_hold(&rt->u.dst);
+@@ -511,14 +508,19 @@
+       struct rt6_info *rt;
+       int strict;
+       int attempts = 3;
++      struct in6_addr *saddr;
++      if (ipv6_chk_addr(fl->nl_u.ip6_u.daddr, NULL))
++              saddr = NULL;
++      else
++              saddr = fl->nl_u.ip6_u.saddr;
++              
+       strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
+ relookup:
+       read_lock_bh(&rt6_lock);
+-      fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
+-                       fl->nl_u.ip6_u.saddr);
++      fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, saddr);
+ restart:
+       rt = fn->leaf;
+@@ -663,25 +665,6 @@
+       return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);
+ }
+-/* Clean host part of a prefix. Not necessary in radix tree,
+-   but results in cleaner routing tables.
+-
+-   Remove it only when all the things will work!
+- */
+-
+-static void ipv6_addr_prefix(struct in6_addr *pfx,
+-                           const struct in6_addr *addr, int plen)
+-{
+-      int b = plen&0x7;
+-      int o = plen>>3;
+-
+-      memcpy(pfx->s6_addr, addr, o);
+-      if (o < 16)
+-              memset(pfx->s6_addr + o, 0, 16 - o);
+-      if (b != 0)
+-              pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b);
+-}
+-
+ static int ipv6_get_mtu(struct net_device *dev)
+ {
+       int mtu = IPV6_MIN_MTU;
+@@ -810,7 +793,7 @@
+                       if (!(gwa_type&IPV6_ADDR_UNICAST))
+                               goto out;
+-                      grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1);
++                      grt = rt6_lookup(gw_addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_ifindex, 1);
+                       err = -EHOSTUNREACH;
+                       if (grt == NULL)
+@@ -848,7 +831,15 @@
+                       goto out;
+               }
+       }
+-
++#ifdef USE_IPV6_MOBILITY
++        /* If destination is mobile node, add special skb->dst->input
++         * function for proxy ND.
++         */
++        if (rtmsg->rtmsg_flags & RTF_MOBILENODE) {
++                rt->u.dst.input = ip6_mipv6_forward;
++        }
++#endif /* CONFIG_IPV6_MOBILITY */
++        
+       if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
+               rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS;
+       else
+@@ -936,7 +927,7 @@
+       struct rt6_info *rt, *nrt;
+       /* Locate old route to this destination. */
+-      rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);
++      rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1);
+       if (rt == NULL)
+               return;
+@@ -1003,6 +994,9 @@
+       nrt = ip6_rt_copy(rt);
+       if (nrt == NULL)
+               goto out;
++#ifdef CONFIG_IPV6_SUBTREES
++      nrt->rt6i_src.plen = rt->rt6i_src.plen;
++#endif
+       nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+       if (on_link)
+@@ -1104,6 +1098,9 @@
+               nrt = ip6_rt_copy(rt);
+               if (nrt == NULL)
+                       goto out;
++#ifdef CONFIG_IPV6_SUBTREES
++              nrt->rt6i_src.plen = rt->rt6i_src.plen;
++#endif
+               ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr);
+               nrt->rt6i_dst.plen = 128;
+               nrt->u.dst.flags |= DST_HOST;
+--- linux-2.4.27/net/ipv6/tcp_ipv6.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/tcp_ipv6.c
+@@ -50,6 +50,7 @@
+ #include <net/addrconf.h>
+ #include <net/ip6_route.h>
+ #include <net/inet_ecn.h>
++#include <net/mipglue.h>
+ #include <asm/uaccess.h>
+@@ -557,6 +558,7 @@
+       struct flowi fl;
+       struct dst_entry *dst;
+       int addr_type;
++      int reroute = 0;
+       int err;
+       if (addr_len < SIN6_LEN_RFC2133) 
+@@ -660,7 +662,7 @@
+       fl.proto = IPPROTO_TCP;
+       fl.fl6_dst = &np->daddr;
+-      fl.fl6_src = saddr;
++      fl.fl6_src = saddr; 
+       fl.oif = sk->bound_dev_if;
+       fl.uli_u.ports.dport = usin->sin6_port;
+       fl.uli_u.ports.sport = sk->sport;
+@@ -669,31 +671,46 @@
+               struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+               fl.nl_u.ip6_u.daddr = rt0->addr;
+       }
+-
+       dst = ip6_route_output(sk, &fl);
+-
++#ifdef CONFIG_IPV6_SUBTREES
++      reroute = (saddr == NULL);
++#endif
+       if ((err = dst->error) != 0) {
+               dst_release(dst);
+               goto failure;
+       }
+-
+-      ip6_dst_store(sk, dst, NULL);
+-      sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
+-
++      if (!reroute) {
++              ip6_dst_store(sk, dst, NULL, NULL);
++              sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
++      }
+       if (saddr == NULL) {
+               err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
++
++              if (reroute)
++                      dst_release(dst);
+               if (err)
+                       goto failure;
+               saddr = &saddr_buf;
++              ipv6_addr_copy(&np->rcv_saddr, saddr);
++#ifdef CONFIG_IPV6_SUBTREES
++              fl.fl6_src = saddr; 
++              dst = ip6_route_output(sk, &fl);
++
++              if ((err = dst->error) != 0) {
++                      dst_release(dst);
++                      goto failure;
++              }
++              ip6_dst_store(sk, dst, NULL, NULL);
++              sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
++#endif
+       }
+       /* set the source address */
+-      ipv6_addr_copy(&np->rcv_saddr, saddr);
+       ipv6_addr_copy(&np->saddr, saddr);
+       sk->rcv_saddr= LOOPBACK4_IPV6;
+-      tp->ext_header_len = 0;
++      tp->ext_header_len = tcp_v6_get_mipv6_header_len();
+       if (np->opt)
+               tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen;
+       tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
+@@ -1338,7 +1355,7 @@
+ #endif
+       MOD_INC_USE_COUNT;
+-      ip6_dst_store(newsk, dst, NULL);
++      ip6_dst_store(newsk, dst, NULL, NULL);
+       sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
+       newtp = &(newsk->tp_pinfo.af_tcp);
+@@ -1383,7 +1400,7 @@
+                       sock_kfree_s(sk, opt, opt->tot_len);
+       }
+-      newtp->ext_header_len = 0;
++      newtp->ext_header_len = tcp_v6_get_mipv6_header_len();
+       if (np->opt)
+               newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen;
+@@ -1710,7 +1727,7 @@
+                       return err;
+               }
+-              ip6_dst_store(sk, dst, NULL);
++              ip6_dst_store(sk, dst, NULL, NULL);
+               sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM;
+       }
+@@ -1749,7 +1766,7 @@
+                       return -sk->err_soft;
+               }
+-              ip6_dst_store(sk, dst, NULL);
++              ip6_dst_store(sk, dst, NULL, NULL);
+       }
+       skb->dst = dst_clone(dst);
+--- linux-2.4.27/net/ipv6/udp.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/ipv6/udp.c
+@@ -48,6 +48,7 @@
+ #include <net/ip.h>
+ #include <net/udp.h>
+ #include <net/inet_common.h>
++#include <net/mipglue.h>
+ #include <net/checksum.h>
+@@ -232,6 +233,7 @@
+       struct ip6_flowlabel    *flowlabel = NULL;
+       int                     addr_type;
+       int                     err;
++      int                     reroute = 0;
+       if (usin->sin6_family == AF_INET) {
+               if (__ipv6_only_sock(sk))
+@@ -331,7 +333,7 @@
+       fl.proto = IPPROTO_UDP;
+       fl.fl6_dst = &np->daddr;
+-      fl.fl6_src = &saddr;
++      fl.fl6_src = NULL;
+       fl.oif = sk->bound_dev_if;
+       fl.uli_u.ports.dport = sk->dport;
+       fl.uli_u.ports.sport = sk->sport;
+@@ -348,29 +350,44 @@
+               struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
+               fl.fl6_dst = rt0->addr;
+       }
+-
+       dst = ip6_route_output(sk, &fl);
+-
+       if ((err = dst->error) != 0) {
+               dst_release(dst);
+               fl6_sock_release(flowlabel);
+-              return err;
+-      }
+-
+-      ip6_dst_store(sk, dst, fl.fl6_dst);
+-
++              return err; 
++      } 
++#ifdef CONFIG_IPV6_SUBTREES
++      reroute = (fl.fl6_src == NULL);
++#endif
+       /* get the source adddress used in the apropriate device */
+       err = ipv6_get_saddr(dst, daddr, &saddr);
++      if (reroute)
++              dst_release(dst);
++
+       if (err == 0) {
+-              if(ipv6_addr_any(&np->saddr))
++#ifdef CONFIG_IPV6_SUBTREES
++              if (reroute) {
++                      fl.fl6_src = &saddr;
++                      dst = ip6_route_output(sk, &fl);
++                      if ((err = dst->error) != 0) {
++                              dst_release(dst);
++                              fl6_sock_release(flowlabel);
++                              return err; 
++                      } 
++              }
++#endif
++              if(ipv6_addr_any(&np->saddr)) {
+                       ipv6_addr_copy(&np->saddr, &saddr);
+-
++                      fl.fl6_src = &np->saddr;
++              }
+               if(ipv6_addr_any(&np->rcv_saddr)) {
+                       ipv6_addr_copy(&np->rcv_saddr, &saddr);
+                       sk->rcv_saddr = LOOPBACK4_IPV6;
+               }
++              ip6_dst_store(sk, dst, fl.fl6_dst, 
++                            fl.fl6_src == &np->saddr ? fl.fl6_src : NULL);
+               sk->state = TCP_ESTABLISHED;
+       }
+       fl6_sock_release(flowlabel);
+@@ -889,6 +906,7 @@
+               opt = fl6_merge_options(&opt_space, flowlabel, opt);
+       if (opt && opt->srcrt)
+               udh.daddr = daddr;
++      udh.daddr = mipv6_get_fake_hdr_daddr(udh.daddr, daddr);
+       udh.uh.source = sk->sport;
+       udh.uh.len = len < 0x10000 ? htons(len) : 0;
+--- linux-2.4.27/net/netsyms.c~mipv6-1.1-v2.4.26
++++ linux-2.4.27/net/netsyms.c
+@@ -190,6 +190,7 @@
+ #endif
+ EXPORT_SYMBOL(pneigh_lookup);
+ EXPORT_SYMBOL(pneigh_enqueue);
++EXPORT_SYMBOL(pneigh_delete);
+ EXPORT_SYMBOL(neigh_destroy);
+ EXPORT_SYMBOL(neigh_parms_alloc);
+ EXPORT_SYMBOL(neigh_parms_release);
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/disable-pcmcia-probe.patch b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/disable-pcmcia-probe.patch
deleted file mode 100644 (file)
index 79ba036..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-
-#
-# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
-#
-
---- linux/drivers/pcmcia/Config.in~disable-pcmcia-probe        2003-05-13 11:18:23.000000000 +0200
-+++ linux/drivers/pcmcia/Config.in     2004-05-27 13:59:50.000000000 +0200
-@@ -15,9 +15,6 @@
- tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
- if [ "$CONFIG_PCMCIA" != "n" ]; then
-    # yes, I really mean the following...
--   if [ "$CONFIG_ISA" = "y" -o "$CONFIG_ARCH_SA1100" = "y" ]; then
--      define_bool CONFIG_PCMCIA_PROBE y
--   fi
-    if [ "$CONFIG_PCI" != "n" ]; then
-       bool '  CardBus support' CONFIG_CARDBUS
-    fi
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/mkdep.patch b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/mkdep.patch
deleted file mode 100644 (file)
index 57218a7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#
-# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
-#
-
---- linux-2.4.25/Makefile~mkdep        2004-03-31 17:15:12.000000000 +0200
-+++ linux-2.4.25/Makefile      2004-03-31 17:18:50.000000000 +0200
-@@ -502,7 +502,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
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-backlight-if.diff b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-backlight-if.diff
deleted file mode 100644 (file)
index 50a4ff7..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
---- /mnt/bdisk/openembedded/oetmp/base/opensimpad-2.4.25-vrs2-pxa1-jpm1-r5/linux-2.4.25/drivers/video/mq200fb.c        2004-07-01 21:10:30.000000000 +0200
-+++ drivers/video/mq200fb.c    2004-07-03 20:58:59.000000000 +0200
-@@ -82,6 +82,20 @@
-       write:  proc_write_reg
- };
-+#ifdef CONFIG_SA1100_SIMPAD
-+
-+static ssize_t proc_read_light(struct file * file, char * buf,
-+              size_t nbytes, loff_t *ppos);
-+static ssize_t proc_write_light(struct file * file, const char * buffer,
-+              size_t count, loff_t *ppos);
-+
-+static struct file_operations proc_light_operations = {
-+      read:   proc_read_light,
-+      write:  proc_write_light
-+};
-+#endif
-+
-+
- typedef struct sa1110_reg_entry {
-       u32 phyaddr;
-       char* name;
-@@ -622,6 +636,20 @@
-        }
-     }
-+#ifdef CONFIG_SA1100_SIMPAD
-+      entry = create_proc_entry("backlight",
-+                               S_IRWXU | S_IRWXG | S_IRWXO,
-+                               mq200dir);
-+      if(entry) {
-+        entry->proc_fops = &proc_light_operations;
-+      } 
-+    else {
-+        printk( KERN_ERR
-+                "mq200fb: can't create /proc/" MQ200_DIRNAME
-+                "/backlight\n");
-+        return(-ENOMEM);
-+    }
-+ #endif
- #ifdef MQ_SA1110
-@@ -1879,7 +1907,7 @@
- static void writeBrightness(void *pMQMMIO, int brightness)
- {
-     unsigned long dutyCycle, pwmcontrol;
--    int MAX_BRIGHT_REG = 0x000000fc; /* int 254 */
-+    int MAX_BRIGHT_REG = 0x000000fe; /* int 254 */
-     
-     if(brightness > MAX_BRIGHT_REG)
-       return;
-@@ -1961,3 +1989,43 @@
-       return (count+endp-buffer);
- }
-+#ifdef CONFIG_SA1100_SIMPAD
-+
-+#define SIMPAD_BACKLIGHT_MASK 0x00a10044
-+
-+static int proc_read_light(struct file * file, char * buf,
-+              size_t nbytes, loff_t *ppos)
-+{
-+      char outputbuf[15];
-+      int count;
-+      u32 pwmctl;
-+      if (*ppos>0) /* Assume reading completed in previous read*/
-+              return 0;
-+
-+      pwmctl = *((volatile *) mq200_p2v(0x4be0e03c));
-+      pwmctl &= ~SIMPAD_BACKLIGHT_MASK;
-+      pwmctl = pwmctl >> 8;   
-+      pwmctl = 254 - pwmctl;
-+              
-+      count = sprintf(outputbuf, "%d\n",pwmctl);
-+      *ppos+=count;
-+      if (count>nbytes)  /* Assume output can be read at one time */
-+              return -EINVAL;
-+      if (copy_to_user(buf, outputbuf, count))
-+              return -EFAULT;
-+      return count;
-+}
-+
-+static ssize_t proc_write_light(struct file * file, const char * buffer,
-+              size_t count, loff_t *ppos)
-+{
-+      void * pMQMMIO = (void *) mqMmioAddr;
-+      char *endp;
-+      unsigned long newvalue = simple_strtoul(buffer,&endp,0);
-+      if (newvalue > 254)
-+              newvalue = 254;
-+      writeBrightness(pMQMMIO,newvalue);
-+      mq200_backlight(pMQMMIO,(int)newvalue);
-+      return (count+endp-buffer);
-+}
-+#endif
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input.diff b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input.diff
deleted file mode 100644 (file)
index 6bf27bf..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
---- /mnt/bdisk/openembedded/oetmp/base/opensimpad-2.4.25-vrs2-pxa1-jpm1-r5/linux-2.4.25/drivers/misc/switches.h        2004-07-01 21:10:30.000000000 +0200
-+++ drivers/misc/switches.h    2004-07-03 23:45:46.000000000 +0200
-@@ -25,4 +25,14 @@
- extern int  switches_ucb1x00_init(void);
- extern void switches_ucb1x00_exit(void);
-+#ifdef CONFIG_SA1100_SIMPAD   
-+#define SIMPAD_KEY_SUSPEND    0x0002
-+#define SIMPAD_KEY_WWW                0x0008
-+#define SIMPAD_KEY_ENTER      0x0010
-+#define SIMPAD_KEY_UP         0x0020
-+#define SIMPAD_KEY_DOWN               0x0040
-+#define SIMPAD_KEY_LEFT               0x0080
-+#define SIMPAD_KEY_RIGHT      0x0100
-+#endif
-+
- #endif  /* !defined(_SWITCHES_H) */
---- /mnt/bdisk/openembedded/oetmp/base/opensimpad-2.4.25-vrs2-pxa1-jpm1-r5/linux-2.4.25/drivers/misc/switches-core.c   2004-07-01 21:10:30.000000000 +0200
-+++ drivers/misc/switches-core.c       2004-07-04 17:57:37.000000000 +0200
-@@ -16,6 +16,9 @@
-  *  11 September 2001 - UCB1200 driver framework support added.
-  *
-  *  19 December 2001 - separated out SA-1100 and UCB1x00 code.
-+ *
-+ *  3 July 2004 - Added generating of keyboard events. 
-+ *                Florian Boor <florian@handhelds.org>
-  */
- #include <linux/config.h>
-@@ -30,7 +33,11 @@
- #include <linux/slab.h>
- #include <linux/wait.h>
-+#include <linux/input.h>
-+
- #include <asm/uaccess.h>
-+#include <asm/hardware.h>
-+#include <asm/keyboard.h>
- #include "switches.h"
-@@ -53,6 +60,19 @@
- DECLARE_WAIT_QUEUE_HEAD(switches_wait);
- LIST_HEAD(switches_event_queue);
-+#ifdef CONFIG_INPUT
-+static struct input_dev idev;
-+      
-+int 
-+dummy_k_translate(unsigned char scancode, unsigned char *keycode, char raw_mode)
-+{
-+      *keycode = scancode;
-+      return 1;
-+}
-+
-+extern int (*k_translate)(unsigned char, unsigned char *, char);
-+
-+#endif
- static ssize_t switches_read(struct file *file, char *buffer,
-                            size_t count, loff_t *pos)
-@@ -148,6 +168,31 @@
- {
-       struct switches_action *action;
-+#ifdef CONFIG_INPUT
-+      /* create input events, the events to send depends on the platform */
-+#ifdef CONFIG_SA1100_SIMPAD   
-+      if (machine_is_simpad()) {
-+              if (SWITCHES_COUNT(mask) > 0)
-+              {
-+                      if (mask->events[0] & SIMPAD_KEY_SUSPEND)
-+                              input_report_key(&idev, KEY_POWER, (mask->states[0] & SIMPAD_KEY_SUSPEND) ? 0 : 1);
-+                      if (mask->events[0] & SIMPAD_KEY_ENTER)
-+                              input_report_key(&idev, KEY_ENTER, (mask->states[0] & SIMPAD_KEY_ENTER) ? 1 : 0);
-+                      if (mask->events[0] & SIMPAD_KEY_UP)
-+                              input_report_key(&idev, KEY_UP, (mask->states[0] & SIMPAD_KEY_UP) ? 1 : 0);
-+                      if (mask->events[0] & SIMPAD_KEY_DOWN)
-+                              input_report_key(&idev, KEY_DOWN, (mask->states[0] & SIMPAD_KEY_DOWN) ? 1 : 0);
-+                      if (mask->events[0] & SIMPAD_KEY_LEFT)
-+                              input_report_key(&idev, KEY_LEFT, (mask->states[0] & SIMPAD_KEY_LEFT) ? 1 : 0);
-+                      if (mask->events[0] & SIMPAD_KEY_RIGHT)
-+                              input_report_key(&idev, KEY_RIGHT, (mask->states[0] & SIMPAD_KEY_RIGHT) ? 1 : 0);
-+                      if (mask->events[0] & SIMPAD_KEY_WWW)
-+                              input_report_key(&idev, KEY_F10, (mask->states[0] & SIMPAD_KEY_WWW) ? 1 : 0);
-+              }
-+      }
-+#endif
-+#endif
-+      /* take care of switches device */
-       if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) {
-               if ((action = (struct switches_action *)
-@@ -197,6 +242,21 @@
-               return -EIO;
-       }
-+#ifdef CONFIG_INPUT
-+      /* init input driver stuff */
-+      k_translate = dummy_k_translate;
-+      idev.evbit[0] = BIT(EV_KEY); /* handle key events */
-+
-+      idev.keybit[LONG(KEY_POWER)] |= BIT(KEY_POWER);
-+      idev.keybit[LONG(KEY_UP)] |= BIT(KEY_UP);
-+      idev.keybit[LONG(KEY_DOWN)] |= BIT(KEY_DOWN);
-+      idev.keybit[LONG(KEY_LEFT)] |= BIT(KEY_LEFT);
-+      idev.keybit[LONG(KEY_RIGHT)] |= BIT(KEY_RIGHT);
-+      idev.keybit[LONG(KEY_ENTER)] |= BIT(KEY_ENTER);
-+      idev.keybit[LONG(KEY_F10)] |= BIT(KEY_F10);
-+
-+      input_register_device(&idev);
-+#endif        
-       printk("Console switches initialized\n");
-       return 0;
-@@ -214,6 +274,10 @@
-       switches_ucb1x00_exit();
- #endif
-+#ifdef CONFIG_INPUT
-+      input_unregister_device(&idev);
-+#endif
-+      
-       if (misc_deregister(&switches_misc) < 0)
-               printk(KERN_ERR "%s: unable to deregister misc device\n",
-                      SWITCHES_NAME);
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input2.diff b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-switches-input2.diff
deleted file mode 100644 (file)
index 4f9d0bf..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-
-#
-# Patch managed by http://www.holgerschurig.de/patcher.html
-#
-
---- drivers/misc/switches-core.c~Fooo
-+++ drivers/misc/switches-core.c
-@@ -66,7 +66,32 @@
- int 
- dummy_k_translate(unsigned char scancode, unsigned char *keycode, char raw_mode)
- {
--      *keycode = scancode;
-+      if (scancode == KEY_UP) 
-+              *keycode = 144;
-+      else if (scancode == KEY_LEFT) 
-+              *keycode = 146;
-+      else if (scancode == KEY_RIGHT) 
-+              *keycode = 151;
-+      else if (scancode == KEY_DOWN) 
-+              *keycode = 161;
-+      else if (scancode == 144) 
-+              *keycode = KEY_UP;
-+      else if (scancode == 146) 
-+              *keycode = KEY_LEFT;
-+      else if (scancode == 151) 
-+              *keycode = KEY_RIGHT;
-+      else if (scancode == 161) 
-+              *keycode = KEY_DOWN;
-+      else if (scancode == KEY_KP8) 
-+              *keycode = KEY_UP;
-+      else if (scancode == KEY_KP4) 
-+              *keycode = KEY_LEFT;
-+      else if (scancode == KEY_KP6) 
-+              *keycode = KEY_RIGHT;
-+      else if (scancode == KEY_KP2) 
-+              *keycode = KEY_DOWN;
-+      else
-+              *keycode = scancode;
-       return 1;
- }
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-ts-noninput.diff b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/simpad-ts-noninput.diff
deleted file mode 100644 (file)
index 08fffe5..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- /mnt/bdisk/openembedded/oetmp/base/opensimpad-2.4.25-vrs2-pxa1-jpm1-r5/linux-2.4.25/drivers/misc/ucb1x00-ts.c      2004-07-01 21:10:30.000000000 +0200
-+++ drivers/misc/ucb1x00-ts.c  2004-07-04 02:00:56.000000000 +0200
-@@ -35,7 +35,7 @@
- /*
-  * Define this if you want the UCB1x00 stuff to talk to the input layer
-  */
--#ifdef CONFIG_INPUT
-+#if defined(CONFIG_INPUT) && !defined(CONFIG_SA1100_SIMPAD)
- #define USE_INPUT
- #else
- #undef USE_INPUT
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/sound-volume-reversed.patch b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/sound-volume-reversed.patch
deleted file mode 100644 (file)
index 11fa5c7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-#
-# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher
-#
-
---- linux-2.4.25/drivers/misc/ucb1x00-audio.c~sound-volume-reversed.patch      2004-03-31 17:15:12.000000000 +0200
-+++ linux-2.4.25/drivers/misc/ucb1x00-audio.c  2004-03-31 17:15:13.000000000 +0200
-@@ -97,7 +97,7 @@
-                               ucba->output_level = gain | gain << 8;
-                               ucba->mod_cnt++;
-                               ucba->ctrl_b = (ucba->ctrl_b & 0xff00) |
--                                             ((gain * 31) / 100);
-+                                             (((100-gain) * 31) / 100);
-                               ucb1x00_reg_write(ucba->ucb, UCB_AC_B,
-                                                 ucba->ctrl_b);
-                               ret = 0;
diff --git a/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/support-128mb-flash.patch b/packages/linux/opensimpad-2.4.25-vrs2-pxa1-jpm1/support-128mb-flash.patch
deleted file mode 100644 (file)
index d5647d6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-
-#
-# Patch managed by http://www.holgerschurig.de/patcher.html
-#
-
---- linux-2.4.25/arch/arm/mach-sa1100/simpad.c~support-128mb-flash
-+++ linux-2.4.25/arch/arm/mach-sa1100/simpad.c
-@@ -83,11 +83,16 @@
- {
- #ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
-       SET_BANK( 0, 0xc0000000, 32*1024*1024 );
-+      mi->nr_banks = 1;
- #else
-       SET_BANK( 0, 0xc0000000, 64*1024*1024 );
--#endif
-       mi->nr_banks = 1;
-+#endif
-+#ifdef CONFIG_SA1100_SIMPAD_128M
-+      SET_BANK( 1, 0xc8000000, 64*1024*1024 );
-+      mi->nr_banks = 2;
-+#endif
-       setup_ramdisk( 1, 0, 0, 8192 );
-       setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 );
- }
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/.mtn2git_empty b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/.mtn2git_empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-mh1.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-mh1.patch
new file mode 100644 (file)
index 0000000..f5379f0
--- /dev/null
@@ -0,0 +1,1134 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/arch/sparc64/kernel/ioctl32.c~patch-2.4.27-mh1
++++ linux-2.4.27/arch/sparc64/kernel/ioctl32.c
+@@ -4322,6 +4322,11 @@
+ #define CMTPGETCONNLIST       _IOR('C', 210, int)
+ #define CMTPGETCONNINFO       _IOR('C', 211, int)
++#define HIDPCONNADD   _IOW('H', 200, int)
++#define HIDPCONNDEL   _IOW('H', 201, int)
++#define HIDPGETCONNLIST       _IOR('H', 210, int)
++#define HIDPGETCONNINFO       _IOR('H', 211, int)
++
+ struct ioctl_trans {
+       unsigned int cmd;
+       unsigned int handler;
+@@ -5050,6 +5055,10 @@
+ COMPATIBLE_IOCTL(CMTPCONNDEL)
+ COMPATIBLE_IOCTL(CMTPGETCONNLIST)
+ COMPATIBLE_IOCTL(CMTPGETCONNINFO)
++COMPATIBLE_IOCTL(HIDPCONNADD)
++COMPATIBLE_IOCTL(HIDPCONNDEL)
++COMPATIBLE_IOCTL(HIDPGETCONNLIST)
++COMPATIBLE_IOCTL(HIDPGETCONNINFO)
+ /* Scanner */
+ COMPATIBLE_IOCTL(SCANNER_IOCTL_VENDOR)
+ COMPATIBLE_IOCTL(SCANNER_IOCTL_PRODUCT)
+--- linux-2.4.27/CREDITS~patch-2.4.27-mh1
++++ linux-2.4.27/CREDITS
+@@ -1348,6 +1348,7 @@
+ 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
+--- linux-2.4.27/Documentation/Configure.help~patch-2.4.27-mh1
++++ linux-2.4.27/Documentation/Configure.help
+@@ -23313,6 +23313,7 @@
+                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 compile Bluetooth support into the kernel or say M to
+   compile it as module (bluez.o).
+@@ -23378,6 +23379,15 @@
+   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.
+--- linux-2.4.27/MAINTAINERS~patch-2.4.27-mh1
++++ linux-2.4.27/MAINTAINERS
+@@ -359,6 +359,11 @@
+ 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
+--- linux-2.4.27/net/bluetooth/Config.in~patch-2.4.27-mh1
++++ linux-2.4.27/net/bluetooth/Config.in
+@@ -14,6 +14,7 @@
+       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
+--- /dev/null
++++ linux-2.4.27/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.27/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.27/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.27/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.27/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.27/net/bluetooth/Makefile~patch-2.4.27-mh1
++++ linux-2.4.27/net/bluetooth/Makefile
+@@ -16,6 +16,7 @@
+ 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
+@@ -25,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)
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1-jpm1.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1-jpm1.patch
new file mode 100644 (file)
index 0000000..bf46339
--- /dev/null
@@ -0,0 +1,8760 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/Makefile~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/Makefile
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 4
+ SUBLEVEL = 27
+-EXTRAVERSION =-vrs1-pxa1
++EXTRAVERSION =-vrs1-pxa1-jpm1
+ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+--- linux-2.4.27/arch/arm/mach-sa1100/Makefile~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/mach-sa1100/Makefile
+@@ -18,7 +18,8 @@
+ export-objs :=        assabet.o consus.o badge4.o dma-sa1100.o dma-sa1111.o \
+               flexanet.o freebird.o frodo.o generic.o h3600.o \
+               huw_webpanel.o irq.o sa1111.o sa1111-pcibuf.o \
+-              system3.o yopy.o usb_ctl.o usb_recv.o usb_send.o simputer.o ssp.o
++              system3.o yopy.o usb_ctl.o usb_recv.o usb_send.o simputer.o ssp.o \
++              simpad.o
+ # These aren't present yet, and prevents a plain -ac kernel building.
+ # hwtimer.o
+@@ -30,7 +31,6 @@
+ ifeq ($(CONFIG_CPU_FREQ),y)
+ obj-$(CONFIG_SA1100_ASSABET) += cpu-sa1110.o
+ obj-$(CONFIG_SA1100_CEP) += cpu-sa1110.o
+-obj-$(CONFIG_SA1100_CONSUS) += cpu-sa1110.o
+ obj-$(CONFIG_SA1100_CERF) += cpu-sa1110.o
+ obj-$(CONFIG_SA1100_HACKKIT) += cpu-sa1110.o
+ obj-$(CONFIG_SA1100_PT_SYSTEM3) += cpu-sa1110.o
+@@ -52,7 +52,6 @@
+ obj-$(CONFIG_SA1100_BRUTUS) += brutus.o
+ obj-$(CONFIG_SA1100_CEP) += cep.o
+ obj-$(CONFIG_SA1100_CERF) += cerf.o
+-obj-$(CONFIG_SA1100_CONSUS) += consus.o
+ obj-$(CONFIG_SA1100_EMPEG) += empeg.o
+ obj-$(CONFIG_SA1100_FLEXANET) += flexanet.o
+ obj-$(CONFIG_SA1100_FREEBIRD) += freebird.o
+@@ -87,7 +86,6 @@
+ leds-$(CONFIG_SA1100_ASSABET) += leds-assabet.o
+ leds-$(CONFIG_SA1100_BRUTUS) += leds-brutus.o
+ leds-$(CONFIG_SA1100_CERF) += leds-cerf.o
+-leds-$(CONFIG_SA1100_CONSUS) += leds-consus.o
+ leds-$(CONFIG_SA1100_FLEXANET) += leds-flexanet.o
+ leds-$(CONFIG_SA1100_FRODO) += leds-frodo.o
+ leds-$(CONFIG_SA1100_GRAPHICSCLIENT) += leds-graphicsclient.o
+@@ -108,7 +106,12 @@
+ # Miscelaneous functions
+ obj-$(CONFIG_PM) += pm.o sleep.o
++obj-$(CONFIG_APM) += apm.o
++# SIMpad specific
++export-objs +=  simpad_pm.o
++obj-$(CONFIG_SIMPAD_PM) += simpad_pm.o
++                                                                                
+ obj-$(CONFIG_SA1100_SSP)              += ssp.o
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.27/drivers/video/fbmem.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/video/fbmem.c
+@@ -109,6 +109,7 @@
+ extern int chips_init(void);
+ extern int g364fb_init(void);
+ extern int sa1100fb_init(void);
++extern int mq200fb_init(void);
+ extern int pxafb_init(void);
+ extern int fm2fb_init(void);
+ extern int fm2fb_setup(char*);
+@@ -306,6 +307,9 @@
+ #ifdef CONFIG_FB_SA1100
+       { "sa1100", sa1100fb_init, NULL },
+ #endif
++#ifdef CONFIG_FB_MQ200
++      { "mq200fb", mq200fb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_PXA
+       { "pxa", pxafb_init, NULL },
+ #endif
+--- linux-2.4.27/arch/arm/config.in~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/config.in
+@@ -128,6 +128,9 @@
+ dep_bool '  Shannon' CONFIG_SA1100_SHANNON $CONFIG_ARCH_SA1100
+ dep_bool '  Sherman' CONFIG_SA1100_SHERMAN $CONFIG_ARCH_SA1100
+ dep_bool '  Simpad' CONFIG_SA1100_SIMPAD $CONFIG_ARCH_SA1100
++if [ "$CONFIG_SA1100_SIMPAD" = "y" ]; then
++   bool '    T-Sinus PAD' CONFIG_SA1100_SIMPAD_SINUSPAD
++fi
+ dep_bool '  Simputer' CONFIG_SA1100_SIMPUTER $CONFIG_ARCH_SA1100
+ dep_bool '  Tulsa' CONFIG_SA1100_PFS168 $CONFIG_ARCH_SA1100
+ dep_bool '  Victor' CONFIG_SA1100_VICTOR $CONFIG_ARCH_SA1100
+@@ -587,6 +590,10 @@
+ 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_tristate 'Advanced power management emulation support' CONFIG_APM $CONFIG_PM
++if [ "$CONFIG_APM" != "n" ]; then
++      bool ' SIMpad power management' CONFIG_SIMPAD_PM
++fi
+ dep_tristate 'RISC OS personality' CONFIG_ARTHUR $CONFIG_CPU_32
+ string 'Default kernel command string' CONFIG_CMDLINE ""
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/simpad
+@@ -0,0 +1,967 @@
++#
++# 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_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=y
++# CONFIG_ARCH_SHARK is not set
++# CONFIG_ARCH_AT91RM9200DK 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=y
++# CONFIG_SA1100_SIMPAD_SINUSPAD 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=m
++CONFIG_SA1100_USB_NETLINK=m
++CONFIG_SA1100_USB_CHAR=m
++# CONFIG_SA1100_SSP is not set
++
++#
++# 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_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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 is not set
++# CONFIG_CPU_ARM1026 is not set
++# CONFIG_CPU_SA110 is not set
++CONFIG_CPU_SA1100=y
++# CONFIG_CPU_32v3 is not set
++CONFIG_CPU_32v4=y
++CONFIG_DISCONTIGMEM=y
++
++#
++# General setup
++#
++# CONFIG_PCI is not set
++CONFIG_ISA=y
++# CONFIG_ISA_DMA is not set
++# CONFIG_ZBOOT_ROM is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++CONFIG_CPU_FREQ=y
++CONFIG_HOTPLUG=y
++
++#
++# PCMCIA/CardBus support
++#
++CONFIG_PCMCIA=y
++CONFIG_PCMCIA_PROBE=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=y
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++# CONFIG_BSD_PROCESS_ACCT is not set
++CONFIG_SYSCTL=y
++CONFIG_FPE_NWFPE=y
++# 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=m
++CONFIG_PM=y
++CONFIG_APM=y
++CONFIG_SIMPAD_PM=y
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="mtdparts=sa1100:512k(boot),1m(kernel),-(root) console=ttySA root=1f02 noinitrd mem=64M"
++CONFIG_LEDS=y
++CONFIG_LEDS_TIMER=y
++CONFIG_LEDS_CPU=y
++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=y
++CONFIG_MTD_CMDLINE_PARTS=y
++# 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
++
++#
++# RAM/ROM/Flash chip drivers
++#
++CONFIG_MTD_CFI=y
++CONFIG_MTD_JEDECPROBE=y
++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_CFI_B1 is not set
++CONFIG_MTD_CFI_B2=y
++# CONFIG_MTD_CFI_B4 is not set
++# CONFIG_MTD_CFI_B8 is not set
++CONFIG_MTD_CFI_I1=y
++# CONFIG_MTD_CFI_I2 is not set
++# 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_RAM is not set
++CONFIG_MTD_ROM=y
++# 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_PHYSMAP is not set
++# CONFIG_MTD_NORA is not set
++# CONFIG_MTD_ARM_INTEGRATOR is not set
++# CONFIG_MTD_CDB89712 is not set
++CONFIG_MTD_SA1100=y
++# CONFIG_MTD_DC21285 is not set
++# CONFIG_MTD_IQ80310 is not set
++# CONFIG_MTD_FORTUNET is not set
++# CONFIG_MTD_EPXA is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_PCI is not set
++
++#
++# Self-contained MTD device drivers
++#
++# CONFIG_MTD_PMC551 is not set
++# CONFIG_MTD_SLRAM is not set
++CONFIG_MTD_MTDRAM=y
++CONFIG_MTDRAM_TOTAL_SIZE=32768
++CONFIG_MTDRAM_ERASE_SIZE=1
++CONFIG_MTDRAM_ABS_POS=C2000000
++# CONFIG_MTD_BLKMTD is not set
++# CONFIG_MTD_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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=y
++# CONFIG_BLK_DEV_NBD is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_BLK_DEV_INITRD is not set
++
++#
++# 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=y
++# CONFIG_NETLINK_DEV is not set
++# 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=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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=y
++# 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 is not set
++# CONFIG_NET_VENDOR_RACAL is not set
++# CONFIG_AT1700 is not set
++# CONFIG_DEPCA is not set
++# CONFIG_HP100 is not set
++# CONFIG_NET_ISA is not set
++CONFIG_NET_PCI=y
++# CONFIG_PCNET32 is not set
++# CONFIG_ADAPTEC_STARFIRE is not set
++# CONFIG_AC3200 is not set
++# CONFIG_APRICOT is not set
++# CONFIG_CS89x0 is not set
++# CONFIG_TULIP is not set
++# CONFIG_TC35815 is not set
++# CONFIG_DM9102 is not set
++# CONFIG_EEPRO100 is not set
++# CONFIG_LNE390 is not set
++# CONFIG_FEALNX is not set
++# CONFIG_NATSEMI is not set
++# CONFIG_NE2K_PCI is not set
++# CONFIG_NE3210 is not set
++# CONFIG_ES3210 is not set
++# CONFIG_8139CP is not set
++# CONFIG_8139TOO is not set
++# CONFIG_8139TOO_PIO is not set
++# CONFIG_8139TOO_TUNE_TWISTER is not set
++# CONFIG_8139TOO_8129 is not set
++# CONFIG_8139_NEW_RX_RESET is not set
++# CONFIG_SIS900 is not set
++# CONFIG_EPIC100 is not set
++# CONFIG_SUNDANCE is not set
++# CONFIG_VIA_RHINE is not set
++# CONFIG_VIA_RHINE_MMIO is not set
++# CONFIG_WINBOND_840 is not set
++# CONFIG_NET_POCKET is not set
++
++#
++# Ethernet (1000 Mbit)
++#
++# CONFIG_ACENIC is not set
++# CONFIG_DL2K 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_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 is not set
++# 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_AIRO=m
++# CONFIG_HERMES is not set
++# CONFIG_PCMCIA_HERMES is not set
++CONFIG_AIRO_CS=m
++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=m
++CONFIG_PCMCIA_3C574=m
++# CONFIG_PCMCIA_FMVJ18X is not set
++CONFIG_PCMCIA_PCNET=m
++# CONFIG_PCMCIA_AXNET is not set
++# CONFIG_PCMCIA_NMCLAN is not set
++CONFIG_PCMCIA_SMC91C92=m
++CONFIG_PCMCIA_XIRC2PS=m
++# CONFIG_ARCNET_COM20020_CS is not set
++# CONFIG_PCMCIA_IBMTR is not set
++CONFIG_NET_PCMCIA_RADIO=y
++# CONFIG_PCMCIA_RAYCS is not set
++# CONFIG_PCMCIA_NETWAVE is not set
++CONFIG_PCMCIA_WAVELAN=m
++# CONFIG_AIRONET4500_CS is not set
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++CONFIG_IRDA=m
++CONFIG_IRLAN=m
++# CONFIG_IRNET is not set
++CONFIG_IRCOMM=m
++# CONFIG_IRDA_ULTRA is not set
++# CONFIG_IRDA_CACHE_LAST_LSAP is not set
++# CONFIG_IRDA_FAST_RR is not set
++# CONFIG_IRDA_DEBUG is not set
++
++#
++# 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_FIR is not set
++# CONFIG_SMC_IRCC_FIR is not set
++# CONFIG_ALI_FIR is not set
++# CONFIG_VLSI_FIR is not set
++CONFIG_SA1100_FIR=m
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=m
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=m
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=m
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
++# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
++# CONFIG_BLK_DEV_IDEDISK_IBM is not set
++# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
++# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
++# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
++# CONFIG_BLK_DEV_IDEDISK_WD is not set
++# CONFIG_BLK_DEV_COMMERIAL is not set
++# CONFIG_BLK_DEV_TIVO is not set
++CONFIG_BLK_DEV_IDECS=m
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_IDE_MODES is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++CONFIG_VT_CONSOLE=y
++CONFIG_SERIAL=m
++# 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=y
++CONFIG_SERIAL_SA1100_CONSOLE=y
++CONFIG_SA1100_DEFAULT_BAUDRATE=115200
++# CONFIG_SERIAL_OMAHA is not set
++# CONFIG_SERIAL_OMAHA_CONSOLE is not set
++# CONFIG_SERIAL_AT91US3 is not set
++# CONFIG_SERIAL_AT91US3_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_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=32
++
++#
++# I2C support
++#
++# CONFIG_I2C is not set
++
++#
++# 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
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++CONFIG_SA1100_RTC=y
++# 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_TDA8007=m
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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=m
++# CONFIG_REISERFS_CHECK is not set
++CONFIG_REISERFS_PROC_INFO=y
++# 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_BFS_FS is not set
++CONFIG_EXT3_FS=m
++CONFIG_JBD=m
++# CONFIG_JBD_DEBUG is not set
++CONFIG_FAT_FS=m
++CONFIG_MSDOS_FS=m
++CONFIG_UMSDOS_FS=m
++CONFIG_VFAT_FS=m
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS_FS=m
++CONFIG_JFFS_FS_VERBOSE=0
++CONFIG_JFFS_PROC_FS=y
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_CRAMFS=m
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++# CONFIG_ISO9660_FS is not set
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS 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=y
++# 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=y
++# CONFIG_ROOT_NFS is not set
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=y
++CONFIG_SMB_FS=m
++# CONFIG_SMB_NLS_DEFAULT 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
++CONFIG_ZLIB_FS_INFLATE=m
++
++#
++# Partition Types
++#
++# CONFIG_PARTITION_ADVANCED is not set
++CONFIG_MSDOS_PARTITION=y
++CONFIG_SMB_NLS=y
++CONFIG_NLS=y
++
++#
++# Native Language Support
++#
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=y
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++CONFIG_NLS_ISO8859_15=y
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# 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_CYBER2000 is not set
++CONFIG_FB_MQ200=y
++# 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=y
++CONFIG_FBCON_CFB8=y
++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=y
++CONFIG_FONT_8x8=y
++CONFIG_FONT_8x16=y
++# CONFIG_FONT_SUN8x16 is not set
++# CONFIG_FONT_SUN12x22 is not set
++# CONFIG_FONT_6x11 is not set
++# CONFIG_FONT_PEARL_8x8 is not set
++# CONFIG_FONT_ACORN_8x8 is not set
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++# 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_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_SA1100=y
++# CONFIG_SOUND_UDA1341 is not set
++# CONFIG_SOUND_ASSABET_UDA1341 is not set
++# CONFIG_SOUND_H3600_UDA1341 is not set
++# CONFIG_SOUND_PANGOLIN_UDA1341 is not set
++# CONFIG_SOUND_SA1111_UDA1341 is not set
++# CONFIG_SOUND_SA1111_AC97 is not set
++# CONFIG_SOUND_SA1100SSP is not set
++# CONFIG_SOUND_OSS is not set
++# CONFIG_SOUND_VIDC is not set
++# CONFIG_SOUND_WAVEARTIST is not set
++# CONFIG_SOUND_TVMIXER is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++CONFIG_MCP=y
++CONFIG_MCP_SA1100=y
++CONFIG_MCP_UCB1200=y
++CONFIG_MCP_UCB1200_AUDIO=y
++CONFIG_MCP_UCB1200_TS=y
++
++#
++# Console Switches
++#
++CONFIG_SWITCHES=y
++CONFIG_SWITCHES_SA1100=y
++CONFIG_SWITCHES_UCB1X00=y
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++# 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
+--- linux-2.4.27/arch/arm/kernel/head-armv.S~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/kernel/head-armv.S
+@@ -93,6 +93,8 @@
+               .section ".text.init",#alloc,#execinstr
+               .type   stext, #function
+ ENTRY(stext)
++              mov     r1, #87
++                mov     r0, #0
+               mov     r12, r0
+ /*
+  * NOTE!  Any code which is placed here should be done for one of
+--- linux-2.4.27/arch/arm/kernel/irq.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/kernel/irq.c
+@@ -82,9 +82,9 @@
+       spin_lock_irqsave(&irq_controller_lock, flags);
+       if (!desc->disable_depth++) {
+-#ifndef CONFIG_CPU_SA1100
++// #ifndef CONFIG_CPU_SA1100
+               desc->mask(irq);
+-#endif
++// #endif
+       }
+       spin_unlock_irqrestore(&irq_controller_lock, flags);
+ }
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/apm.c
+@@ -0,0 +1,520 @@
++/*
++ * 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>
++#if FIXME
++#include <asm/arch-sa1100/pm.h>
++#endif
++
++#ifdef CONFIG_IPAQ_HANDHELD
++#include <asm/arch-sa1100/h3600_hal.h>
++#endif
++
++#ifdef CONFIG_SA1100_SIMPAD
++#include <asm/arch-sa1100/simpad_pm.h>
++#endif
++
++#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
++extern int (*console_blank_hook)(int);
++#endif
++
++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-]debug                  log some debugging messages
++ *        [no-]power[-_]off           power off on shutdown
++ */
++
++/*
++ * Need to poll the APM BIOS every second
++ */
++#define APM_CHECK_TIMEOUT     (HZ)
++
++/*
++ * Ignore suspend events for this amount of time after a resume
++ */
++#define DEFAULT_BOUNCE_INTERVAL               (3 * HZ)
++
++/*
++ * Maximum number of events stored
++ */
++#define APM_MAX_EVENTS                20
++
++/*
++ * 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
++ */
++//static int                  suspends_pending;
++//static int                  standbys_pending;
++//static int                  ignore_normal_resume;
++
++#ifdef CONFIG_APM_RTC_IS_GMT
++#     define  clock_cmos_diff 0
++#     define  got_clock_diff  1
++#else
++//static long                 clock_cmos_diff;
++//static int                  got_clock_diff;
++#endif
++static int                    debug;
++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)
++{
++#ifdef CONFIG_IPAQ_HANDHELD
++        h3600_apm_get_power_status(ac_line_status, battery_status, battery_flag, battery_percentage, battery_life);
++#endif
++#ifdef CONFIG_SA1100_SIMPAD
++        simpad_apm_get_power_status(ac_line_status, battery_status, battery_flag, battery_percentage, battery_life);
++#endif
++      return APM_SUCCESS;
++}
++
++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:
++#if FIXME
++              pm_suggest_suspend();
++#endif
++              break;
++      default:
++              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) &&
++          !(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;
++
++      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, "debug", 5) == 0)
++                      debug = !invert;
++              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;
++}
++
++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_init(apm_init);
++module_exit(apm_exit);
++
++MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell");
++MODULE_DESCRIPTION("A minimal emulation of APM");
++MODULE_PARM(debug, "i");
++MODULE_PARM_DESC(debug, "Enable debug mode");
++MODULE_PARM(power_off, "i");
++MODULE_PARM_DESC(power_off, "Enable power off");
++
++EXPORT_NO_SYMBOLS;
+--- linux-2.4.27/arch/arm/mach-sa1100/leds.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/mach-sa1100/leds.c
+@@ -45,6 +45,8 @@
+               leds_event = pfs168_leds_event;
+       if (machine_is_pt_system3())
+               leds_event = system3_leds_event;
++      if (machine_is_simpad())
++                leds_event = simpad_leds_event;
+       leds_event(led_start);
+       return 0;
+--- linux-2.4.27/arch/arm/mach-sa1100/leds.h~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/mach-sa1100/leds.h
+@@ -12,4 +12,6 @@
+ extern void hackkit_leds_event(led_event_t evt);
+ extern void lart_leds_event(led_event_t evt);
+ extern void pfs168_leds_event(led_event_t evt);
++extern void simpad_leds_event(led_event_t evt);
+ extern void system3_leds_event(led_event_t evt);
++
+--- linux-2.4.27/arch/arm/mach-sa1100/pm.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/mach-sa1100/pm.c
+@@ -63,6 +63,7 @@
+       SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
+       SLEEP_SAVE_ICMR,
++      SLEEP_SAVE_MECR,
+       SLEEP_SAVE_Ser1SDCR0,
+       SLEEP_SAVE_SIZE
+@@ -109,6 +110,8 @@
+       SAVE(ICMR);
++      SAVE(MECR);
++
+       /* ... maybe a global variable initialized by arch code to set this? */
+       GRER = PWER;
+       GFER = 0;
+@@ -163,6 +166,8 @@
+       ICCR = 1;
+       RESTORE(ICMR);
++      RESTORE(MECR);
++
+       /* restore current time */
+       xtime.tv_sec = RCNR + delta;
+--- linux-2.4.27/arch/arm/mach-sa1100/simpad.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/arch/arm/mach-sa1100/simpad.c
+@@ -10,6 +10,7 @@
+ #include <linux/tty.h>
+ #include <linux/proc_fs.h>
+ #include <linux/string.h> 
++#include <linux/pm.h> 
+ #include <asm/hardware.h>
+ #include <asm/setup.h>
+@@ -28,6 +29,11 @@
+       return cs3_shadow;
+ }
++void set_cs3(long value)
++{
++        *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow = value;
++}
++
+ void set_cs3_bit(int value)
+ {
+       cs3_shadow |= value;
+@@ -40,31 +46,62 @@
+       *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
+ }
++EXPORT_SYMBOL(set_cs3_bit);
++EXPORT_SYMBOL(clear_cs3_bit);
++
++static void simpad_power_off(void)
++{
++    cli();
++    set_cs3(0x800);        /* only SD_MEDIAQ */
++
++    /* disable internal oscillator, float CS lines */
++    PCFR = (PCFR_OPDE | PCFR_FP | PCFR_FS);
++    /* enable wake-up on GPIO0 (Assabet...) */
++    PWER = GFER = GRER = 1;
++    /*
++     * set scratchpad to zero, just in case it is used as a
++     * restart address by the bootloader.
++     */
++    PSPR = 0;
++    PGSR = 0;
++    /* enter sleep mode */
++    PMCR = PMCR_SF;
++    while(1);
++}
++           
++static int __init simpad_init(void)
++{
++    pm_power_off = simpad_power_off;
++    return 0;
++}
++
++__initcall(simpad_init);
++
+ static void __init
+ fixup_simpad(struct machine_desc *desc, struct param_struct *params,
+                  char **cmdline, struct meminfo *mi)
+ {
+-#ifdef CONFIG_SA1100_SIMPAD_DRAM_64MB /* DRAM */
+-      SET_BANK( 0, 0xc0000000, 64*1024*1024 );
+-#else
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
+       SET_BANK( 0, 0xc0000000, 32*1024*1024 );
++#else
++      SET_BANK( 0, 0xc0000000, 64*1024*1024 );
+ #endif
+       mi->nr_banks = 1;
+-      ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
++
+       setup_ramdisk( 1, 0, 0, 8192 );
+       setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 );
+ }
+-
+ static struct map_desc simpad_io_desc[] __initdata = {
+-  /* virtual  physical    length      domain     r  w  c  b */
+-  { 0xe8000000, 0x00000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, 
+-  { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */  
+-  { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Paules CS3, write only */
++  /* virtual    physical    length      domain     r  w  c  b */
++  { 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 },
++  { 0xe9000000, 0x08000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 },
++  { 0xf1000000, 0x18000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CS3, write only */
++  { 0xf2000000, 0x40000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CS4, tda8007 */
++  { 0xf2800000, 0x4b800000, 0x00800000, DOMAIN_IO, 0, 1, 0, 0 }, /* MQ200 */
+   LAST_DESC
+ };
+-
+ static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+ {
+       if (port->mapbase == (u_int)&Ser1UTCR0) {
+@@ -81,20 +118,32 @@
+ static void __init simpad_map_io(void)
+ {
+-      sa1100_map_io();
+-      iotable_init(simpad_io_desc);
++        sa1100_map_io();
++        iotable_init(simpad_io_desc);
+-      PSPR = 0xc0008000;
+-      GPDR &= ~GPIO_GPIO0;
+-      cs3_shadow = (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON | 
+-                    ENABLE_5V | RESET_SIMCARD);
+-      *(CS3BUSTYPE *)(CS3_BASE) = cs3_shadow;
++        set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
++                      ENABLE_5V | nRESET_SIMCARD);
+-      //It is only possible to register 3 UART in serial_sa1100.c
+-      sa1100_register_uart(0, 3);
+-      sa1100_register_uart(1, 1);
++        //It is only possible to register 3 UART in serial_sa1100.c
++        sa1100_register_uart(0, 3);
++        sa1100_register_uart(1, 1);
+-      set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ, GPIO_RISING_EDGE);
++      GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
++      GPDR |= GPIO_UART_TXD;
++      GPDR &= ~GPIO_UART_RXD;
++      PPAR |= PPAR_UPR;
++
++        set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ, GPIO_RISING_EDGE);
++        set_GPIO_IRQ_edge(GPIO_POWER_BUTTON, GPIO_FALLING_EDGE);
++
++        /*
++         * Set up registers for sleep mode.
++         */
++
++        PWER = PWER_GPIO0;
++        PGSR = 0x818;
++        PCFR = 0;
++        PSDR = 0;
+ }
+ #ifdef CONFIG_PROC_FS
+@@ -140,7 +189,17 @@
+  
+       return len;
+ }
+- 
++
++static int proc_cs3_write(struct file * file, const char * buffer,
++                size_t count, loff_t *ppos)
++{
++        unsigned long newRegValue;
++        char *endp;
++
++        newRegValue = simple_strtoul(buffer,&endp,0);
++        set_cs3( newRegValue );
++        return (count+endp-buffer);
++}
+  
+ static struct proc_dir_entry *proc_cs3;
+  
+@@ -148,7 +207,10 @@
+ {
+       proc_cs3 = create_proc_entry("cs3", 0, 0);
+       if (proc_cs3)
++      {
+               proc_cs3->read_proc = proc_cs3_read;
++              proc_cs3->write_proc = (void*)proc_cs3_write;
++      }
+       return 0;
+ }
+  
+@@ -165,6 +227,7 @@
+ MACHINE_START(SIMPAD, "Simpad")
+       MAINTAINER("Juergen Messerer")
+       BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
++      BOOT_PARAMS(0xc0000100)
+       FIXUP(fixup_simpad)
+       MAPIO(simpad_map_io)
+       INITIRQ(sa1100_init_irq)
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/simpad_pm.c
+@@ -0,0 +1,147 @@
++/*
++* Powermanagement layer for SIMPad.
++*
++* Copyright 2003 Peter Pregler
++* Copyright 2000,2001 Compaq Computer Corporation.
++*
++* Use consistent with the GNU GPL is permitted,
++* provided that this copyright notice is
++* preserved in its entirety in all copies and derived works.
++*
++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++* FITNESS FOR ANY PARTICULAR PURPOSE.
++*
++* Author: Peter Pregler (based on work for ipaq by Andrew Christian)
++* May, 2003
++*/
++
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <asm/uaccess.h>        /* get_user,copy_to_user */
++#include <linux/string.h>
++#include <linux/interrupt.h>
++#include <linux/sysctl.h>
++#include <linux/console.h>
++#include <linux/devfs_fs_kernel.h>
++
++#include <linux/tqueue.h>
++#include <linux/sched.h>
++#include <linux/pm.h>
++#include <linux/proc_fs.h>
++#include <linux/apm_bios.h>
++#include <linux/kmod.h>
++
++#include <asm/hardware.h>
++#include <asm/arch-sa1100/simpad_pm.h>
++
++MODULE_AUTHOR("Peter Pregler");
++MODULE_DESCRIPTION("Power manamgement abstraction layer for the SIMpad");
++
++/****************************************************************************/
++/*  Functions exported for use by the kernel and kernel modules             */
++/****************************************************************************/
++
++int simpad_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)
++{
++      struct simpad_battery bstat;
++      unsigned char ac    = APM_AC_UNKNOWN;
++      unsigned char level = APM_BATTERY_STATUS_UNKNOWN;
++      int status, result;
++
++      result = simpad_get_battery(&bstat);
++      if (result) {
++              printk("%s: unable to access battery information: result=%d\n", __FUNCTION__, result);
++              return 0;
++      }
++
++      switch (bstat.ac_status) {
++      case SIMPAD_AC_STATUS_AC_OFFLINE:
++              ac = APM_AC_OFFLINE;
++              break;
++      case SIMPAD_AC_STATUS_AC_ONLINE:
++              ac = APM_AC_ONLINE;
++              break;
++      case SIMPAD_AC_STATUS_AC_BACKUP:
++              ac = APM_AC_BACKUP;
++              break;
++      }
++
++      if (ac_line_status != NULL)
++              *ac_line_status = ac;
++
++      status = bstat.status;
++      if (status & (SIMPAD_BATT_STATUS_CHARGING | SIMPAD_BATT_STATUS_CHARGE_MAIN))
++              level = APM_BATTERY_STATUS_CHARGING;
++      else if (status & (SIMPAD_BATT_STATUS_HIGH | SIMPAD_BATT_STATUS_FULL))
++              level = APM_BATTERY_STATUS_HIGH;
++      else if (status & SIMPAD_BATT_STATUS_LOW)
++              level = APM_BATTERY_STATUS_LOW;
++      else if (status & SIMPAD_BATT_STATUS_CRITICAL)
++              level = APM_BATTERY_STATUS_CRITICAL;
++
++      if (battery_status != NULL)
++              *battery_status = level;
++
++      if (battery_percentage != NULL)
++              *battery_percentage = bstat.percentage;
++
++      /* we have a dumb battery - so we know nothing */
++      if (battery_life != NULL) {
++              *battery_life = APM_BATTERY_LIFE_UNKNOWN;
++      }
++                        
++#if 0
++      printk("apm_get_power: ac: %02x / bs: %02x / bf: %02x / perc: %02x / life: %d\n",
++             *ac_line_status, *battery_status, *battery_flag,
++             *battery_percentage, *battery_life );
++#endif
++      return 1;
++}
++
++EXPORT_SYMBOL(simpad_apm_get_power_status);
++
++
++/***********************************************************************************/
++/*       Initialization                                                            */
++/***********************************************************************************/
++
++#ifdef CONFIG_FB_MQ200
++extern void (*mq200_blank_helper)(int blank);
++#endif
++
++int __init simpad_hal_init_module(void)
++{
++      int i;
++        printk(KERN_INFO "SIMpad Registering HAL abstraction layer\n");
++
++      /* Request the appropriate underlying module to provide services */
++
++#ifdef CONFIG_FB_SA1100
++      sa1100fb_blank_helper = simpad_hal_backlight_helper;
++#endif
++
++      return 0;
++}
++
++void simpad_hal_cleanup_module(void)
++{
++      int i;
++      printk(KERN_INFO "SIMpad shutting down HAL abstraction layer\n");
++
++#ifdef CONFIG_FB_SA1100
++      sa1100fb_blank_helper = NULL;
++#endif
++}
++
++module_init(simpad_hal_init_module);
++module_exit(simpad_hal_cleanup_module);
+--- linux-2.4.27/drivers/char/Config.in~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/char/Config.in
+@@ -424,4 +424,7 @@
+    tristate ' MT6N TTL I/O suport' CONFIG_TRIZEPS2_TTLIO
+ fi
++if [ "$CONFIG_SA1100_SIMPAD" = "y" ]; then
++ tristate 'Smartcardreader(TDA8007) support' CONFIG_TDA8007
++fi
+ endmenu
+--- linux-2.4.27/drivers/char/Makefile~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/char/Makefile
+@@ -376,6 +376,8 @@
+   obj-y += ipmi/ipmi.o
+ endif
++obj-$(CONFIG_TDA8007)   += tda8007.o
++
+ include $(TOPDIR)/Rules.make
+ fastdep:
+--- /dev/null
++++ linux-2.4.27/drivers/char/tda8007.c
+@@ -0,0 +1,514 @@
++/*
++ *  linux/drivers/char/tda8007.c
++ *
++ *  Copyright (C) 2001 juergen.messerer@freesurf.ch, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  The TDA8007B driver provides basic services for handling IO,
++ *  interrupts, and accessing registers.  
++ */
++
++#include <linux/delay.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/proc_fs.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/arch/simpad.h>
++#include <asm/uaccess.h>
++
++#include "tda8007b.h"
++
++#define TDA8007_DIRNAME "driver/tda8007"
++#define REG_DIRNAME "registers"
++
++extern void clear_cs3_bit(int value);
++
++static struct proc_dir_entry *regdir;
++static struct proc_dir_entry *tda8007dir;
++
++static ssize_t proc_tda8007_read(struct file * file, char * buf,
++                size_t nbytes, loff_t *ppos);
++static ssize_t proc_tda8007_write(struct file * file, const char * buffer,
++                size_t count, loff_t *ppos);
++
++static struct file_operations proc_reg_operations = {
++        read:   proc_tda8007_read,
++        write:  proc_tda8007_write
++};
++
++static int __init tda8007_init();
++
++/* ------------------------------------------------------------------------- */
++void tda8007_reg_write(int reg, int val)
++{
++    printk("Address:%x \n", CS4_BASE+reg);
++    printk("Value:%x \n", val);
++    TDA_REG_WRITE(reg,val);
++}
++/* ------------------------------------------------------------------------- */
++int tda8007_reg_read(int reg)
++{
++    printk("Address:%x \n", CS4_BASE+reg);
++    return(TDA_REG_READ(reg)&0xff);
++}
++/* ------------------------------------------------------------------------- */
++int tdaregs[16];
++/* ------------------------------------------------------------------------- */
++static void tda8007_irq(int irqnr, void *devid, struct pt_regs *regs)
++{
++    printk("\n****tda8007_irq****\n");
++}
++/* ------------------------------------------------------------------------- */
++static int tda_card_present( uint cardport )
++{
++    int val=0;
++
++    switch( cardport )
++    {
++      case CARD_PORT1:
++          if( tda8007_reg_read(TDA_MSR) & TDA_MSR_PR1 )
++              val = 1;
++          break;
++      case CARD_PORT2:
++          if( tda8007_reg_read(TDA_MSR) & TDA_MSR_PR2 )
++              val = 1;
++          break;
++      default:
++          val =0;
++          break;
++    }
++    
++    return val;
++}
++/* ------------------------------------------------------------------------- */
++void tda_inituart(void)
++{
++    int hsr_reg, fcr_reg;
++
++    printk("Init TDA8007 Uart\n");
++    hsr_reg = tda8007_reg_read(TDA_HSR);       
++    tda8007_reg_write(TDA_PCR, 0x00); 
++   
++    tda8007_reg_write(TDA_CSR, 0x00); 
++    tda8007_reg_write(TDA_CSR, TDA_CSR_SC1);          /* select Card 1 */
++    
++    tda8007_reg_write(TDA_CSR, TDA_CSR_nRIU|TDA_CSR_SC1);     
++    tda8007_reg_write(TDA_PCR, 0x00); 
++
++    tda8007_reg_write(TDA_PDR, TDA_PDR_VAL);  /* Rat v. jandu 8.9.2000 */
++    tda8007_reg_write(TDA_UCR2, TDA_UCR2_DIV);
++    
++    tda8007_reg_write(TDA_CCR, 0x40|TDA_CCR_AC1);  /*1=XTAL/2 2=XTAL/4 3=XTAL/8  */
++    tda8007_reg_write(TDA_GTR, TDA_GTR_GT1);  
++
++    fcr_reg = tda8007_reg_read(TDA_FCR);
++    tda8007_reg_write(TDA_FCR, (fcr_reg & 0xf0) | TDA_FCR_FL1);       
++    
++    tda8007_reg_write(TDA_FCR,  TDA_FCR_FL2|TDA_FCR_FL1|TDA_FCR_FL0); 
++    tda8007_reg_write(TDA_UCR1, TDA_UCR_SS|TDA_UCR_CONV);
++    tda8007_reg_write(TDA_PCR,  0x00);                
++    
++    while( tda8007_reg_read(TDA_USR) & TDA_USR_TBE_RBF )
++    {
++        hsr_reg = tda8007_reg_read(TDA_URR);
++      udelay(5);
++    }
++}
++/* ------------------------------------------------------------------------- */
++void start_tda8007_sync(int volt)
++{
++    int i=0,j=0;      
++    if( tda_card_present( CARD_PORT1 ) )
++    {
++        printk("Card Present ");
++      tda8007_reg_write(TDA_TOR1, TDA_TOR1_TOL2|TDA_TOR1_TOL3);
++      tda8007_reg_write(TDA_TOR2, TDA_TOR2_TOL16|TDA_TOR2_TOL15|
++                                  TDA_TOR2_TOL13|TDA_TOR2_TOL12|
++                                  TDA_TOR2_TOL11);
++      tda8007_reg_write(TDA_TOR3, 0x00);
++      tda8007_reg_write(TDA_TOC,  TDA_TOC_MODE2);
++      tda_inituart();         
++      tda8007_reg_write(TDA_UCR2, TDA_UCR_DISAUX|TDA_UCR2_DIV); // DIS_AUX ASYNC MODE
++      
++      if( volt == 3 ) 
++          volt = TDA_PCR_3V_5V;
++      else            
++          volt = 0x00;
++              
++      tda8007_reg_write(TDA_PCR, 0x00|volt); // Set /Reset,3V
++      udelay(1000);           
++      tda8007_reg_write(TDA_PCR, TDA_PCR_START|volt); // /Reset,3V,Start
++      udelay(2000);           
++      tda8007_reg_write(TDA_PCR, TDA_PCR_RSTIN|TDA_PCR_START|volt); // Set Reset High
++      i=0;            
++      while( 1 )// !serstat()
++      {
++          if( ((msr[i]=tda8007_reg_read(TDA_MSR)) &  TDA_MSR_FE) == 0 )
++          {
++              hsr[i]=tda8007_reg_read(TDA_HSR);
++              usr[i]=tda8007_reg_read(TDA_USR);
++              csr[i]=tda8007_reg_read(TDA_CSR);
++              urr[i]=tda8007_reg_read(TDA_URR);                               
++              i++;                            
++          }
++          if( i == 1 )
++          {   
++              /* Reset SS */
++              tda8007_reg_write(TDA_UCR1, 
++                                tda8007_reg_read(TDA_UCR1) & ~TDA_UCR_SS); 
++              /* Set Autoconv high */
++              tda8007_reg_write(TDA_UCR2, 
++                                tda8007_reg_read(TDA_UCR2) | TDA_UCR_nAUTOCONV); 
++          }
++          
++          if( i >= BUFFSIZE )
++          {
++              printk("Buffer Overflow");                              
++              break;                  
++          }                   
++          // tda8007_reg_write(TDA_FCR, TDA_FCR_PEC0|TDA_FCR_FL0);    
++      }
++      hsr[i]=tda8007_reg_read(TDA_HSR);
++      msr[i]=tda8007_reg_read(TDA_MSR);
++      csr[i]=tda8007_reg_read(TDA_CSR);
++      urr[i]=tda8007_reg_read(TDA_URR);                               
++      i++;                    
++      //serin();
++      if( i==1 )
++          printk("No Characters received\n");
++      else
++        for(j=0;j<i-1; j++)
++          printk("Buffer[%3d]=USR(0x%02x) HSR(0x%02x) MSR(0x%02x) CSR(0x%02x) URR(0x%02x=%c)\n",
++                 j,usr[j],hsr[j],msr[j],csr[j],urr[j],pascii(urr[j]));
++      
++      printk("Now  USR(0x%02x) HSR(0x%02x) MSR(0x%02x) CSR(0x%02x) URR(0x%02x=%c)\n",
++          usr[j],hsr[j],msr[j],csr[j],urr[j],pascii(urr[j]));
++      
++      tda8007_reg_write(TDA_PCR, TDA_PCR_RSTIN|volt); // remove start
++      udelay(2000);           
++      tda8007_reg_write(TDA_PCR, 0x00|volt); // remove Reset
++              
++    } 
++    else 
++    {         
++        tda8007_reg_write(TDA_PCR, TDA_PCR_3V_5V);
++    }
++      
++}
++/* -------------------------------------------------------------------------*/ 
++int test_tda8007(void)
++{
++    int c, i,j, reg,end=0;
++
++    printk("\nTDA8007 TEST:");
++
++    for( i=0; i < 0x10; i++ )
++        printk("\nTDA8007 Reg %2d = 0x%02x ", i, tda8007_reg_read(i)&0xff);
++
++    for( i=0 ;i < 0x10; i++ )
++    {
++        tdaregs[i]=tda8007_reg_read(i) & 0xff;
++    }
++    do
++    {
++        printk("\nTDA8007 IRQ=%s  Command:",
++             (GPLR&(1<<10) ? "HIGH":"LOW"));
++
++      //c=serin();
++      //serout(c);
++      printk("\n");
++      
++      switch (c )
++      {
++          
++          case 'c':
++              printk("\nReg?:");  
++              //reg=gethex(serin,serout);
++              printk("\nVal?:");  
++              //i=gethex(serin,serout);                       
++              tda8007_reg_write(reg,i);
++              j=tda8007_reg_read(reg);
++              printk("Reg 0x%02x (0x%02x) now 0x%02x\n", reg, i, j);  
++              break;
++              
++          case 'i':
++              printk("\nInit\n");
++              tda8007_init();                 
++              
++          case 'p':
++              for( i=0; i < 0x10; i++ )
++                  printk("\nTDA8007 Reg %2d = 0x%02x ", i, 
++                         tda8007_reg_read(i)&0xff);
++              break;
++          case 'e':
++              end=1;
++              break;
++              
++          case 'r':
++              while(  1 ) // serstat() == 0
++              {
++                  for( i=0 ;i < 0x10; i++ )
++                  {
++                      tdaregs[i]=tda8007_reg_read(i) & 0xff;
++                  }
++              }
++              //serin();
++              break;
++                      
++          case 'S':
++              start_tda8007_sync(5);                  
++              break;
++          case 's':
++              start_tda8007_sync(3);                  
++              break;
++          case 'w':
++              while( 1 )//serstat() == 0
++              {
++                  for( i=0 ;i < 0x10; i++ )
++                  {
++                      tda8007_reg_write(i,0x10-i);            
++                  }
++              }
++              //serin();
++              break;
++          
++          default :
++              //serout(0x07);
++              break;          
++      }
++      
++    }while( end == 0 );
++    return(0);        
++}
++/*-------------------------------------------------------------------------*/
++static int tda8007_ioctl(struct inode *ino, struct file *filp, 
++                             uint cmd, ulong arg)
++{
++    unsigned int val, gain;
++    int ret = 0;
++
++    if (_IOC_TYPE(cmd) != 'M')
++        return -EINVAL;
++
++    switch (_IOC_NR(cmd)) 
++    {
++      case TDA_INFO:
++        break;
++
++      case TDA_INIT:
++        break;
++
++      case TDA_SET:
++        break;
++
++      case TDA_CARD_PRESENT:
++        break;
++
++      case TDA_CARD_VOLT:
++        break;
++
++      default:
++        val = 0;
++        ret = -EINVAL;
++        break;
++    }
++    return ret;
++}
++/* ------------------------------------------------------------------------- */
++static ssize_t proc_tda8007_read( struct file *file, char *buf, size_t nbytes, 
++                            loff_t *ppos)
++{
++    char outputbuf[80];
++    int count = 0;
++    int i     = 0;
++    int i_ino = (file->f_dentry->d_inode)->i_ino;
++    tda8007_reg_entry_t* current_reg = NULL;
++
++    if ((*ppos) > 0) /* Assume reading completed in previous read*/
++      return 0;
++
++    for (i=0; i<NUM_OF_TDA8007_REG_ENTRY; i++) 
++    {
++      if (tda8007_regs[i].low_ino==i_ino) 
++      {
++          if( tda8007_regs[i].mode == 2 ) /* write only */
++          {
++              printk("%s\n", tda8007_regs[i].description);
++              printk("Read operation is on this register not possible!\n");
++              return -EINVAL;
++          }
++          current_reg = &tda8007_regs[i];
++          
++          break;
++      }
++    }
++    
++    if (current_reg==NULL)
++      return -EINVAL;
++
++    printk("%s\n", current_reg->description);
++    count += sprintf(outputbuf, "%s: 0x%x\n",  current_reg->name,
++    tda8007_reg_read( current_reg->addr ));
++    /*  count = sprintf(outputbuf, "value: 0x%x\n",  
++      tda8007_reg_read( current_reg->addr ));*/
++    
++    *ppos+=count;
++
++    if (count>nbytes)  /* Assume output can be read at one time */
++      return -EINVAL;
++  
++    if (copy_to_user(buf, outputbuf, count))
++      return -EFAULT;
++
++    return count;
++}
++/* ------------------------------------------------------------------------- */
++static ssize_t proc_tda8007_write(struct file * file, const char * buffer,
++                size_t count, loff_t *ppos)
++{
++    int i;
++    unsigned long newRegValue;
++    char *endp;
++    int i_ino = (file->f_dentry->d_inode)->i_ino;
++    tda8007_reg_entry_t* current_reg=NULL;
++
++    for (i=0; i<NUM_OF_TDA8007_REG_ENTRY; i++) 
++    {
++      if (tda8007_regs[i].low_ino==i_ino) 
++      {
++          if( tda8007_regs[i].mode == 1 ) /* read only */
++          {
++              printk("%s\n", tda8007_regs[i].description);
++              printk("Write operation is on this register not possible!\n");
++              return -EINVAL;
++          }
++          current_reg = &tda8007_regs[i];
++          break;
++      }
++    }
++    if (current_reg==NULL)
++      return -EINVAL;
++
++    newRegValue = simple_strtoul(buffer,&endp,0);
++    tda8007_reg_write( current_reg->addr, newRegValue);
++    return (count+endp-buffer);
++}
++/* ------------------------------------------------------------------------- */
++static int __init tda8007_init()
++{
++      int i, hsr_reg, res;
++      int ret = -ENODEV;
++      struct proc_dir_entry *entry;
++      int tda8007_major = 60;
++      
++      res = register_chrdev( tda8007_major, "tda8007", NULL );
++      
++      if(res < 0){
++        printk(KERN_WARNING "tda8007: can't get major%d\n", tda8007_major);
++        return res;
++      }
++      
++      if( tda8007_major == 0 )
++        tda8007_major = res;
++      
++      set_GPIO_IRQ_edge(GPIO_SMART_CARD, GPIO_RISING_EDGE);
++
++      ret = request_irq( IRQ_GPIO_SMART_CARD, tda8007_irq, 
++                             SA_INTERRUPT, "SMARTCARD_CD", NULL );
++      if (ret) {
++        printk(KERN_ERR "tda8007: unable to grab irq%d: %d\n",
++               IRQ_GPIO_SMART_CARD, ret);
++        return ret;
++      }
++
++      printk("\nInit TDA8007 IRQ=%s\n", 
++           (GPLR&(1<<10) ? "HIGH":"LOW"));
++
++//      clear_cs3_bit(RESET_SIMCARD);
++
++
++#ifdef CONFIG_PROC_FS
++      /* Create two dir entries for the TDA8007 */
++      tda8007dir = proc_mkdir("tda8007"/*TDA8007_DIRNAME*/, NULL);
++      if (tda8007dir == NULL) {
++        printk(KERN_ERR "tda80007: can't create /proc/" TDA8007_DIRNAME "\n");
++        return(-ENOMEM);
++      }
++      
++      regdir = proc_mkdir(REG_DIRNAME, tda8007dir);
++      if (regdir == NULL) {
++        printk(KERN_ERR "tda8007: can't create /proc/" TDA8007_DIRNAME "/" REG_DIRNAME "\n");
++        return(-ENOMEM);
++      }
++
++      for(i=0;i<NUM_OF_TDA8007_REG_ENTRY;i++) {
++        entry = create_proc_entry(tda8007_regs[i].name,
++                                  S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH,
++                                  regdir);
++        if(entry) {
++            tda8007_regs[i].low_ino = entry->low_ino;
++            entry->proc_fops = &proc_reg_operations;
++        }
++        else {
++            printk( KERN_ERR
++                    "tda8007: can't create /proc/" REG_DIRNAME
++                    "/%s\n", tda8007_regs[i].name);
++            return(-ENOMEM);
++        }
++      }
++
++#endif // CONFIG_PROC_FS
++
++
++      tda8007_reg_write(TDA_CSR, 0);
++      tda8007_reg_write(TDA_CSR, TDA_CSR_nRIU);
++      for( i=0; i < 16; i++ )
++        tda8007_reg_write(i,0);
++
++      tda8007_reg_write(TDA_CSR, TDA_CSR_nRIU|TDA_CSR_SC2);
++      tda8007_reg_write(TDA_PCR, 0);    /* START=0 */
++      tda8007_reg_write(TDA_CSR, TDA_CSR_nRIU|TDA_CSR_SC1);
++      tda8007_reg_write(TDA_PCR, 0);    /* START=0 */
++      tda8007_reg_write(TDA_TOC, 0);
++      tda8007_reg_write(TDA_FCR, TDA_FCR_FL2|TDA_FCR_FL1|TDA_FCR_FL0);
++      
++      tda8007_reg_write(TDA_UCR2, TDA_UCR_DISAUX|TDA_UCR2_DIV); // DIS_AUX DIS_CLK
++      tda8007_reg_write(TDA_UCR2, TDA_UCR_DISAUX|TDA_UCR2_DIV); // DIS_AUX CLK SYNC-MODE
++      hsr_reg = tda8007_reg_read(TDA_HSR);
++      
++      tda8007_reg_write(TDA_CCR, TDA_CCR_AC1|TDA_CCR_AC0); /* XTAL/8 */
++
++      return 0;
++}
++/* ------------------------------------------------------------------------- */
++static void __exit tda8007_exit(void)
++{
++  int i;
++
++  free_irq(IRQ_GPIO_SMART_CARD, NULL);
++     /*   kfree(my_ucb);*/
++
++  if (regdir)
++  {
++       for(i=0;i<NUM_OF_TDA8007_REG_ENTRY;i++) {
++        remove_proc_entry( tda8007_regs[i].name, regdir);
++       }
++  }
++}
++/* ------------------------------------------------------------------------- */
++module_init(tda8007_init);
++module_exit(tda8007_exit);
++
++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>");
++MODULE_DESCRIPTION("TDA8007 driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/char/tda8007b.h
+@@ -0,0 +1,312 @@
++/*
++ *  Double multiprotocol IC car interface (Philips SmartCard reader)
++ *
++ *  linux/drivers/char/tda8007b.h
++ *
++ *  Copyright (C) 2002 juergen.messerer@freesurf.ch, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ */
++#ifndef TDA8007B_H
++#define TDA8007B_H
++
++#define CS4BUSTYPE unsigned volatile long
++#define CS4_BASE  0xf2000000
++
++#define CARD_PORT1 1
++#define CARD_PORT2 2
++#define CARD_PORT3 3
++
++#define TDA_REG_READ(reg)       *(CS4BUSTYPE *)(CS4_BASE+reg)
++#define TDA_REG_WRITE(reg,val)  *(CS4BUSTYPE *)(CS4_BASE+reg)=val
++
++#define TDA_MULTIPLEXED_MODE 0
++
++#define TDA_UCR2_DIV              0
++#define TDA_PDR_VAL               12
++
++#define pascii(i) ((i>=' ' && i < 0x7f) ? (i):'.')
++
++#define BUFFSIZE 128
++
++#define TDA_READ  1
++#define TDA_WRITE 2
++
++#define TDA_INFO         1
++#define TDA_INIT         2
++#define TDA_SET          3
++#define TDA_CARD_PRESENT 4
++#define TDA_CARD_VOLT    5
++
++int hsr[BUFFSIZE];
++int msr[BUFFSIZE];
++int csr[BUFFSIZE];
++int urr[BUFFSIZE];
++int usr[BUFFSIZE];
++
++/*************************** Control Register ********************************/
++
++/* 
++ * Card select register (read/write) 
++ * all significant bits are cleared execept SC1 which is set (xxxx'0001)
++ */
++#define TDA_CSR        0x00
++#define TDA_CSR_SC1    (1 << 0)
++#define TDA_CSR_SC2    (1 << 1)
++#define TDA_CSR_SC3    (1 << 2)
++#define TDA_CSR_nRIU   (1 << 3)
++
++/* 
++ * Clock configuration register (read/write) 
++ * all bits are cleared (0000'0000) 
++ */
++#define TDA_CCR            0x01
++#define TDA_CCR_AC0        (1 << 0)
++#define TDA_CCR_AC1        (1 << 1)
++#define TDA_CCR_AC2        (1 << 2)
++#define TDA_CCR_SC         (1 << 3)
++#define TDA_CCR_CST        (1 << 4)
++#define TDA_CCR_SHL        (1 << 5)
++
++/* 
++ * Programmable divider register (read/write) 
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_PDR        0x02
++#define TDA_PDR_PD0    (1 << 0)
++#define TDA_PDR_PD1    (1 << 1)
++#define TDA_PDR_PD2    (1 << 2)
++#define TDA_PDR_PD3    (1 << 3)
++#define TDA_PDR_PD4    (1 << 4)
++#define TDA_PDR_PD5    (1 << 5)
++#define TDA_PDR_PD6    (1 << 6)
++#define TDA_PDR_PD7    (1 << 7)
++
++/* 
++ * UART configuration register 2(read/write) 
++ * all relevant bits are cleared after reset (x000'0000)
++ */
++#define TDA_UCR2             0x03
++#define TDA_UCR_PSC          (1 << 0)
++#define TDA_UCR_CKU          (1 << 1)
++#define TDA_UCR_nAUTOCONV    (1 << 2)
++#define TDA_UCR_SAN          (1 << 3)
++#define TDA_UCR_PDWN         (1 << 4)
++#define TDA_UCR_DISAUX       (1 << 5)
++#define TDA_UCR_DISTBE_RBF   (1 << 6)
++
++/* 
++ * Guard time register (read/write) 
++ * all bits are cleared (0000'0000) 
++ */
++#define TDA_GTR        0x05
++#define TDA_GTR_GT0    (1 << 0)
++#define TDA_GTR_GT1    (1 << 1)
++#define TDA_GTR_GT2    (1 << 2)
++#define TDA_GTR_GT3    (1 << 3)
++#define TDA_GTR_GT4    (1 << 4)
++#define TDA_GTR_GT5    (1 << 5)
++#define TDA_GTR_GT6    (1 << 6)
++#define TDA_GTR_GT7    (1 << 7)
++
++/* 
++ * UART configuration register 1(read/write) 
++ * all relevant bits are cleared after reset (x000'0000)
++ */
++#define TDA_UCR1             0x06
++#define TDA_UCR_CONV         (1 << 0)
++#define TDA_UCR_SS           (1 << 1)
++#define TDA_UCR_LCT          (1 << 2)
++#define TDA_UCR_T_R          (1 << 3)
++#define TDA_UCR_PROT         (1 << 4)
++#define TDA_UCR_FC           (1 << 5)
++#define TDA_UCR_FIP          (1 << 6)
++
++/* 
++ * Power control register (read/write) 
++ * all relevant bits are cleared after reset (xx11'0000)
++ */
++#define TDA_PCR              0x07
++#define TDA_PCR_START        (1 << 0)
++#define TDA_PCR_3V_5V        (1 << 1)
++#define TDA_PCR_RSTIN        (1 << 2)
++#define TDA_PCR_1V8          (1 << 3)
++#define TDA_PCR_C4           (1 << 4)
++#define TDA_PCR_C8           (1 << 5)
++
++/* 
++ * Time-out configuration register (read/write) 
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_TOC            0x08
++#define TDA_TOC_STOP_ALL   0x00
++#define TDA_TOC_MODE1      0x61
++#define TDA_TOC_MODE2      0x65
++#define TDA_TOC_MODE3      0x68
++#define TDA_TOC_MODE4      0x7c
++#define TDA_TOC_MODE5      0xe5
++
++/* 
++ * Time-out register 1(write only) 
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_TOR1         0x09
++#define TDA_TOR1_TOL0    (1 << 0)
++#define TDA_TOR1_TOL1    (1 << 1)
++#define TDA_TOR1_TOL2    (1 << 2)
++#define TDA_TOR1_TOL3    (1 << 3)
++#define TDA_TOR1_TOL4    (1 << 4)
++#define TDA_TOR1_TOL5    (1 << 5)
++#define TDA_TOR1_TOL6    (1 << 6)
++#define TDA_TOR1_TOL7    (1 << 7)
++
++/* 
++ * Time-out register 2(write only) 
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_TOR2          0x0a
++#define TDA_TOR2_TOL10    (1 << 0)
++#define TDA_TOR2_TOL11    (1 << 1)
++#define TDA_TOR2_TOL12    (1 << 2)
++#define TDA_TOR2_TOL13    (1 << 3)
++#define TDA_TOR2_TOL14    (1 << 4)
++#define TDA_TOR2_TOL15    (1 << 5)
++#define TDA_TOR2_TOL16    (1 << 6)
++#define TDA_TOR2_TOL17    (1 << 7)
++
++/* 
++ * Time-out register 3(write only) 
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_TOR3          0x0b
++#define TDA_TOR3_TOL16    (1 << 0)
++#define TDA_TOR3_TOL17    (1 << 1)
++#define TDA_TOR3_TOL18    (1 << 2)
++#define TDA_TOR3_TOL19    (1 << 3)
++#define TDA_TOR3_TOL20    (1 << 4)
++#define TDA_TOR3_TOL21    (1 << 5)
++#define TDA_TOR3_TOL22    (1 << 6)
++#define TDA_TOR3_TOL23    (1 << 7)
++
++/* 
++ * Mixed status register (read only)
++ * bits TBE, RBF and BGT are cleared, bit FE is set after reset (x10x'xxx0)
++ */
++#define TDA_MSR            0x0c
++#define TDA_MSR_TBE_RBF    (1 << 0)
++#define TDA_MSR_INTAUX     (1 << 1)
++#define TDA_MSR_PR1        (1 << 2)
++#define TDA_MSR_PR2        (1 << 3)
++#define TDA_MSR_BGT        (1 << 5)
++#define TDA_MSR_FE         (1 << 6)
++
++/* 
++ * FIFO control register (write only)
++ * all relevant bits are cleared after reset (x000'x000)
++ */
++#define TDA_FCR        0x0c
++#define TDA_FCR_FL0    (1 << 0)
++#define TDA_FCR_FL1    (1 << 1)
++#define TDA_FCR_FL2    (1 << 2)
++#define TDA_FCR_PEC0   (1 << 4)
++#define TDA_FCR_PEC1   (1 << 5)
++#define TDA_FCR_PEC2   (1 << 6)
++
++/* 
++ * UART transmit register (write only)
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_UTR        0x0d
++#define TDA_UTR_UT0    (1 << 0)
++#define TDA_UTR_UT1    (1 << 1)
++#define TDA_UTR_UT2    (1 << 2)
++#define TDA_UTR_UT3    (1 << 3)
++#define TDA_UTR_UT4    (1 << 4)
++#define TDA_UTR_UT5    (1 << 5)
++#define TDA_UTR_UT6    (1 << 6)
++#define TDA_UTR_UT7    (1 << 7)
++
++/* 
++ * UART receive register (read only)
++ * all bits are cleared (0000'0000)
++ */
++#define TDA_URR        0x0d
++#define TDA_URR_UR0    (1 << 0)
++#define TDA_URR_UR1    (1 << 1)
++#define TDA_URR_UR2    (1 << 2)
++#define TDA_URR_UR3    (1 << 3)
++#define TDA_URR_UR4    (1 << 4)
++#define TDA_URR_UR5    (1 << 5)
++#define TDA_URR_UR6    (1 << 6)
++#define TDA_URR_UR7    (1 << 7)
++
++/* 
++ * UART status register (read only)
++ * all bits are cleared (0x00'0000)  
++ */
++#define TDA_USR            0x0e
++#define TDA_USR_TBE_RBF    (1 << 0)
++#define TDA_USR_FER        (1 << 1)
++#define TDA_USR_OVR        (1 << 2)
++#define TDA_USR_PE         (1 << 3)
++#define TDA_USR_EA         (1 << 4)
++#define TDA_USR_TO1        (1 << 5)
++#define TDA_USR_TO3        (1 << 7)
++
++/* 
++ * Hardware status register (read only) 
++ * all significant bits are cleared, except SUPL (x001'0000)
++ */
++#define TDA_HSR            0x0f
++#define TDA_HSR_PTL        (1 << 0)
++#define TDA_HSR_INTAUXL    (1 << 1)
++#define TDA_HSR_PRL1       (1 << 2)
++#define TDA_HSR_PRL2       (1 << 3)
++#define TDA_HSR_SUPL       (1 << 4)
++#define TDA_HSR_PRTL1      (1 << 5)
++#define TDA_HSR_PRTL2      (1 << 6)
++
++typedef struct tda8007_reg_entry {
++        u32 addr;
++        char* name;
++        char* description;
++        u8  mode;
++        unsigned short low_ino;
++} tda8007_reg_entry_t;
++
++
++/*
++ * Read       : 1
++ * Write      : 2
++ * Read/Write : 3
++ */
++
++static tda8007_reg_entry_t tda8007_regs[] =
++{
++  {TDA_CSR,  "TDA_CSR",  "Card select register (read/write)",            3},
++  {TDA_CCR,  "TDA_CCR",  "Clock configuration register (read/write)",    3},
++  {TDA_PDR,  "TDA_PDR",  "Programmable divider register (read/write)",   3},
++  {TDA_UCR2, "TDA_UCR2", "UART configuration register 2(read/write)",    3},
++  {TDA_GTR,  "TDA_GTR",  "Guard time register (read/write)",             3},
++  {TDA_UCR1, "TDA_UCR1", "UART configuration register 1(read/write)",    3},
++  {TDA_PCR,  "TDA_PCR",  "Power control register (read/write)",          3},
++  {TDA_TOC,  "TDA_TOC",  "Time-out configuration register (read/write)", 3},
++  {TDA_MSR,  "TDA_MSR",  "Mixed status register (read only)",       1},
++  {TDA_URR,  "TDA_URR",  "UART receive register (read only)",       1},
++  {TDA_USR,  "TDA_USR",  "UART status register (read only)",        1},
++  {TDA_HSR,  "TDA_HSR",  "Hardware status register (read only)",    1},
++  {TDA_TOR1, "TDA_TOR1", "Time-out register 1(write only)",     2},
++  {TDA_TOR2, "TDA_TOR2", "Time-out register 2(write only)",     2},
++  {TDA_TOR3, "TDA_TOR3", "Time-out register 3(write only)",     2},
++  {TDA_FCR,  "TDA_FCR",  "FIFO control register (write only)",  2},
++  {TDA_UTR,  "TDA_UTR",  "UART transmit register (write only)", 2}
++};
++#define NUM_OF_TDA8007_REG_ENTRY  (sizeof(tda8007_regs)/sizeof(tda8007_reg_entry_t))
++/*
++struct tda8007 {
++
++};
++*/
++#endif /* TDA8007B_H */
+--- linux-2.4.27/drivers/misc/Config.in~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/misc/Config.in
+@@ -16,3 +16,15 @@
+ dep_tristate '  UCB1400 Touchscreen support' CONFIG_MCP_UCB1400_TS $CONFIG_ARCH_PXA $CONFIG_SOUND
+ endmenu
++mainmenu_option next_comment
++comment 'Console Switches'
++
++tristate 'Console Switch Support' CONFIG_SWITCHES
++if [ "$CONFIG_SWITCHES" != "n" ]; then
++   dep_bool '  SA-1100 switches' CONFIG_SWITCHES_SA1100 $CONFIG_ARCH_SA1100
++   if [ "$CONFIG_MCP_UCB1200" != "n" ]; then
++      bool '  UCB1x00 switches' CONFIG_SWITCHES_UCB1X00
++   fi
++fi
++
++endmenu
+--- linux-2.4.27/drivers/misc/Makefile~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/misc/Makefile
+@@ -21,6 +21,24 @@
+ obj-$(CONFIG_MCP_UCB1400_TS)  += mcp-pxa.o ucb1x00-core.o ucb1x00-ts.o
+ obj-$(CONFIG_PXA_CERF_PDA)    += cerf_ucb1400gpio.o
++ifeq ($(CONFIG_SA1100_ASSABET),y)
++obj-$(CONFIG_MCP_UCB1200)       += ucb1x00-assabet.o
++endif
++
++ifeq ($(CONFIG_SA1100_SIMPAD),y)
++export-objs                   += ucb1x00-simpad.o
++obj-$(CONFIG_MCP_UCB1200)       += ucb1x00-simpad.o
++endif
++
++obj-$(CONFIG_SWITCHES)          += switches.o
++
++switches-objs-y                                 += switches-core.o
++switches-objs-$(CONFIG_SWITCHES_SA1100)         += switches-sa1100.o
++switches-objs-$(CONFIG_SWITCHES_UCB1X00)        += switches-ucb1x00.o
++
+ include $(TOPDIR)/Rules.make
++switches.o: $(switches-objs-y)
++      $(LD) $(LD_RFLAG) -r -o $@ $(switches-objs-y)
++
+ fastdep:
+--- /dev/null
++++ linux-2.4.27/drivers/misc/switches-core.c
+@@ -0,0 +1,226 @@
++/*
++ *  linux/drivers/misc/switches-core.c
++ *
++ *  Copyright (C) 2000-2001 John Dorsey
++ *
++ *  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.
++ *
++ *  5 October 2000 - created.
++ *
++ *  25 October 2000 - userland file interface added.
++ *
++ *  13 January 2001 - added support for Spot.
++ *
++ *  11 September 2001 - UCB1200 driver framework support added.
++ *
++ *  19 December 2001 - separated out SA-1100 and UCB1x00 code.
++ */
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/miscdevice.h>
++#include <linux/module.h>
++#include <linux/mm.h>
++#include <linux/poll.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/wait.h>
++
++#include <asm/uaccess.h>
++
++#include "switches.h"
++
++
++MODULE_AUTHOR("John Dorsey");
++MODULE_DESCRIPTION("Console switch support");
++MODULE_LICENSE("GPL");
++
++
++struct switches_action {
++      struct list_head list;
++      switches_mask_t  mask;
++};
++
++
++static int switches_users = 0;
++
++static spinlock_t switches_lock = SPIN_LOCK_UNLOCKED;
++
++DECLARE_WAIT_QUEUE_HEAD(switches_wait);
++LIST_HEAD(switches_event_queue);
++
++
++static ssize_t switches_read(struct file *file, char *buffer,
++                           size_t count, loff_t *pos)
++{
++      unsigned long flags;
++      struct list_head *event;
++      struct switches_action *action;
++
++      if (count < sizeof(struct switches_mask_t))
++              return -EINVAL;
++
++      while (list_empty(&switches_event_queue)) {
++
++              if (file->f_flags & O_NDELAY)
++                      return -EAGAIN;
++
++              interruptible_sleep_on(&switches_wait);
++
++              if (signal_pending(current))
++                      return -ERESTARTSYS;
++
++      }
++
++      if (verify_area(VERIFY_WRITE, buffer, sizeof(struct switches_mask_t)))
++              return -EFAULT;
++
++      spin_lock_irqsave(&switches_lock, flags);
++
++      event = switches_event_queue.next;
++      action = list_entry(event, struct switches_action, list);
++      copy_to_user(buffer, &(action->mask), sizeof(struct switches_mask_t));
++      list_del(event);
++      kfree(action);
++
++      spin_unlock_irqrestore(&switches_lock, flags);
++
++      return sizeof(struct switches_mask_t);
++
++}
++
++static ssize_t switches_write(struct file *file, const char *buffer,
++                            size_t count, loff_t *ppos)
++{
++      return -EINVAL;
++}
++
++static unsigned int switches_poll(struct file *file, poll_table *wait)
++{
++
++      poll_wait(file, &switches_wait, wait);
++
++      if (!list_empty(&switches_event_queue))
++              return POLLIN | POLLRDNORM;
++
++      return 0;
++
++}
++
++static int switches_open(struct inode *inode, struct file *file)
++{
++
++      if (switches_users > 0)
++              return -EBUSY;
++
++      MOD_INC_USE_COUNT;
++      ++switches_users;
++      return 0;
++
++}
++
++static int switches_release(struct inode *inode, struct file *file)
++{
++
++      --switches_users;
++      MOD_DEC_USE_COUNT;
++      return 0;
++
++}
++
++static struct file_operations switches_ops = {
++      read:    switches_read,
++      write:   switches_write,
++      poll:    switches_poll,
++      open:    switches_open,
++      release: switches_release
++};
++
++static struct miscdevice switches_misc = {
++      MISC_DYNAMIC_MINOR, SWITCHES_NAME, &switches_ops
++};
++
++int switches_event(switches_mask_t *mask)
++{
++      struct switches_action *action;
++
++      if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) {
++
++              if ((action = (struct switches_action *)
++                   kmalloc(sizeof(struct switches_action),
++                           GFP_ATOMIC)) == NULL) {
++                      printk(KERN_ERR "%s: unable to allocate action "
++                             "descriptor\n", SWITCHES_NAME);
++                      return -1;
++              }
++
++              action->mask = *mask;
++
++              spin_lock(&switches_lock);
++              list_add_tail(&action->list, &switches_event_queue);
++              spin_unlock(&switches_lock);
++
++              wake_up_interruptible(&switches_wait);
++
++      }
++
++      return 0;
++
++}
++
++static int __init switches_init(void)
++{
++
++#ifdef CONFIG_SWITCHES_SA1100
++      if (switches_sa1100_init() < 0) {
++              printk(KERN_ERR "%s: unable to initialize SA-1100 switches\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++#endif
++
++#ifdef CONFIG_SWITCHES_UCB1X00
++      if (switches_ucb1x00_init() < 0) {
++              printk(KERN_ERR "%s: unable to initialize UCB1x00 switches\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++#endif
++
++      if (misc_register(&switches_misc) < 0) {
++              printk(KERN_ERR "%s: unable to register misc device\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++
++      printk("Console switches initialized\n");
++
++      return 0;
++
++}
++
++static void __exit switches_exit(void)
++{
++
++#ifdef CONFIG_SWITCHES_SA1100
++      switches_sa1100_exit();
++#endif
++
++#ifdef CONFIG_SWITCHES_UCB1X00
++      switches_ucb1x00_exit();
++#endif
++
++      if (misc_deregister(&switches_misc) < 0)
++              printk(KERN_ERR "%s: unable to deregister misc device\n",
++                     SWITCHES_NAME);
++
++}
++
++module_init(switches_init);
++module_exit(switches_exit);
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/misc/switches-sa1100.c
+@@ -0,0 +1,311 @@
++/*
++ *  linux/drivers/misc/switches-sa1100.c
++ *
++ *  Copyright (C) 2001 John Dorsey
++ *
++ *  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.
++ *
++ *  19 December 2001 - created from sa1100_switches.c.
++ */
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++
++#ifdef CONFIG_SA1100_ASSABET
++#include <asm/arch/assabet.h>
++#endif
++
++#ifdef CONFIG_SA1100_SIMPAD
++#include <asm/arch/simpad.h>
++#endif
++
++#include "switches.h"
++
++
++static void switches_sa1100_handler(int irq, void *dev_id,
++                                  struct pt_regs *regs);
++
++
++#ifdef CONFIG_SA1100_ASSABET
++
++/* Assabet
++ * ^^^^^^^
++ * We have two general-purpose switches, S1 and S2, available via GPIO
++ * on Assabet. This code sets bits in the range [1, 2] in the mask that
++ * we return to userland.
++ */
++
++static int assabet_switches_sa1100_init(void)
++{
++
++      if (machine_has_neponset())
++              NCR_0 |= NCR_GP01_OFF;
++
++      set_irq_type(IRQ_GPIO0, IRQT_BOTHEDGE);
++      set_irq_type(IRQ_GPIO1, IRQT_BOTHEDGE);
++
++      if (request_irq(IRQ_GPIO0, switches_sa1100_handler, SA_INTERRUPT,
++                      SWITCHES_NAME, NULL) < 0) {
++              printk(KERN_ERR "%s: unable to register IRQ for GPIO 0\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++
++      if (request_irq(IRQ_GPIO1, switches_sa1100_handler, SA_INTERRUPT,
++                      SWITCHES_NAME, NULL) < 0) {
++              printk(KERN_ERR "%s: unable to register IRQ for GPIO 1\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++
++      return 0;
++
++}
++
++static void assabet_switches_sa1100_shutdown(void)
++{
++
++      free_irq(IRQ_GPIO1, NULL);
++      free_irq(IRQ_GPIO0, NULL);
++
++}
++
++static void assabet_switches_sa1100_handler(int irq, switches_mask_t *mask)
++{
++      unsigned int s, last, this;
++      static unsigned int states = 0;
++
++      switch (irq) {
++
++      case IRQ_GPIO0: s = 0; break;
++
++      case IRQ_GPIO1: s = 1; break;
++
++      default:        return;
++
++      }
++
++      last = ((states & (1 << s)) != 0);
++      this = ((GPLR & GPIO_GPIO(s)) != 0);
++
++      if (last == this) /* debounce */
++              return;
++
++      SWITCHES_SET(mask, s + 1, this);
++
++      states = this ? (states | (1 << s)) : (states & ~(1 << s));
++
++}
++#endif  /* CONFIG_SA1100_ASSABET */
++
++
++#ifdef CONFIG_SA1100_SPOT
++
++/* Spot
++ * ^^^^
++ * Spot (R2, R3) has a single general-purpose switch (S1), which is
++ * also the power-on switch. We set bit [1] in the mask we return to
++ * userland.
++ */
++
++static int spot_switches_sa1100_init(void)
++{
++
++      set_GPIO_IRQ_edge(GPIO_SW1, GPIO_BOTH_EDGES);
++
++      if (request_irq(IRQ_GPIO_SW1, switches_sa1100_handler, SA_INTERRUPT,
++                      SWITCHES_NAME, NULL) < 0) {
++              printk(KERN_ERR "%s: unable to register IRQ for SW1\n",
++                     SWITCHES_NAME);
++              return -EIO;
++      }
++
++      return 0;
++
++}
++
++static void spot_switches_sa1100_shutdown(void)
++{
++
++      free_irq(IRQ_GPIO_SW1, NULL);
++
++}
++
++static void spot_switches_sa1100_handler(int irq, switches_mask_t *mask)
++{
++      unsigned int s, last, this;
++      static unsigned int states = 0;
++
++      switch (irq) {
++
++      case IRQ_GPIO_SW1:      s = 0; break;
++
++      default:                return;
++
++      }
++
++      last = ((states & (1 << s)) != 0);
++      this = ((GPLR & GPIO_GPIO(s)) != 0);
++
++      if (last == this) /* debounce */
++              return;
++
++      SWITCHES_SET(mask, s + 1, this);
++
++      states = this ? (states | (1 << s)) : (states & ~(1 << s));
++
++}
++#endif  /* CONFIG_SA1100_SPOT */
++
++#ifdef CONFIG_SA1100_SIMPAD
++
++/* SIMpad
++ * ^^^^
++ * SIMpad has a single general-purpose switch (S0), which is
++ * also the power-on switch. We set bit [1] in the mask we return to
++ * userland.
++ */
++
++static int simpad_switches_sa1100_init(void)
++{
++
++      set_GPIO_IRQ_edge(GPIO_GPIO0, GPIO_BOTH_EDGES);
++      
++      if (request_irq(IRQ_GPIO0, switches_sa1100_handler, SA_INTERRUPT,
++                      SWITCHES_NAME, NULL) < 0) {
++              printk(KERN_ERR "%s: unable to register IRQ for SW0\n",
++              SWITCHES_NAME);
++              return -EIO;
++      }
++
++      return 0;
++
++}
++
++static void simpad_switches_sa1100_shutdown(void)
++{
++
++      free_irq(IRQ_GPIO0, NULL);
++
++}
++
++static void simpad_switches_sa1100_handler(int irq, switches_mask_t *mask)
++{
++      unsigned int s, last, this;
++      static unsigned int states = 0;
++
++      switch (irq) {
++
++      case IRQ_GPIO0: s = 0; break;
++
++      default:                return;
++
++      }
++
++      last = ((states & (1 << s)) != 0);
++      this = ((GPLR & GPIO_GPIO(s)) != 0);
++
++      if (last == this) /* debounce */
++              return;
++
++      SWITCHES_SET(mask, s + 1, this);
++
++      states = this ? (states | (1 << s)) : (states & ~(1 << s));
++
++}
++#endif  /* CONFIG_SA1100_SIMPAD */
++
++
++
++/* switches_sa1100_handler()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^
++ * This routine is a generalized handler for SA-1100 switches
++ * which manages action descriptors and calls a board-specific
++ * service routine. This routine is appropriate for GPIO switches
++ * or other primary interrupt sources, and can be registered as a
++ * first-class IRQ handler using request_irq().
++ */
++static void switches_sa1100_handler(int irq, void *dev_id,
++                                  struct pt_regs *regs)
++{
++      switches_mask_t mask;
++
++      SWITCHES_ZERO(&mask);
++
++      /* Porting note: call a board-specific switch interrupt handler
++       * here. The handler can assume that sufficient storage for
++       * `mask' has been allocated, and that the corresponding
++       * switches_mask_t structure has been zeroed.
++       */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              assabet_switches_sa1100_handler(irq, &mask);
++#endif
++      } else if (machine_is_spot()) {
++#ifdef CONFIG_SA1100_SPOT
++              spot_switches_sa1100_handler(irq, &mask);
++#endif
++      } else if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              simpad_switches_sa1100_handler(irq, &mask);
++#endif
++      }
++
++      switches_event(&mask);
++
++}
++
++int __init switches_sa1100_init(void)
++{
++
++      /* Porting note: call a board-specific init routine here. */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              if (assabet_switches_sa1100_init() < 0)
++                      return -EIO;
++#endif
++      } else if (machine_is_spot()) {
++#ifdef CONFIG_SA1100_SPOT
++              if (spot_switches_sa1100_init() < 0)
++                      return -EIO;
++#endif
++      } else if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              if (simpad_switches_sa1100_init() < 0)
++                      return -EIO;
++#endif
++      }
++
++      return 0;
++
++}
++
++void __exit switches_sa1100_exit(void)
++{
++
++      /* Porting note: call a board-specific shutdown routine here. */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              assabet_switches_sa1100_shutdown();
++#endif
++      } else if (machine_is_spot()) {
++#ifdef CONFIG_SA1100_SPOT
++              spot_switches_sa1100_shutdown();
++#endif
++      } else if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              simpad_switches_sa1100_shutdown();
++#endif
++      }
++
++}
+--- /dev/null
++++ linux-2.4.27/drivers/misc/switches-ucb1x00.c
+@@ -0,0 +1,331 @@
++/*
++ *  linux/drivers/misc/switches-ucb1x00.c
++ *
++ *  Copyright (C) 2001 John Dorsey
++ *
++ *  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.
++ *
++ *  19 December 2001 - created from sa1100_switches.c.
++ */
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++
++#ifdef CONFIG_SA1100_ASSABET
++#include <asm/arch/assabet.h>
++#endif
++
++#ifdef CONFIG_SA1100_SIMPAD
++#include <asm/arch/simpad.h>
++#endif
++
++#include "switches.h"
++#include "ucb1x00.h"
++
++
++static struct ucb1x00 *ucb1x00;
++
++static void switches_ucb1x00_handler(int irq, void *devid);
++
++
++#ifdef CONFIG_SA1100_ASSABET
++
++/* Assabet
++ * ^^^^^^^
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ * This code sets bits in the range [3, 8] in the mask that we
++ * return to userland. Note that we transpose signals SW7 and SW8;
++ * see assabet_switches_ucb1x00_handler().
++ */
++
++static int assabet_switches_ucb1x00_init(void)
++{
++      int i;
++
++      /* Note that ucb1x00_init() must complete before this point: */
++
++      if ((ucb1x00 = ucb1x00_get()) == NULL) {
++              printk(KERN_ERR "%s: UCB1300 driver not ready; switches "
++                     "3 -- 8 will not be available\n",
++                     SWITCHES_NAME);
++              return 0;
++      }
++
++      ucb1x00_enable(ucb1x00);
++
++      ucb1x00_io_set_dir(ucb1x00,
++                         UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++                         UCB_IO_3 | UCB_IO_4 | UCB_IO_5, 0);
++
++      for (i = 0; i < 6; ++i) {
++
++              ucb1x00_enable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING);
++
++              if (ucb1x00_hook_irq(ucb1x00, i,
++                                   switches_ucb1x00_handler, NULL) < 0) {
++                      printk(KERN_ERR "%s: unable to hook IRQ for "
++                             "UCB1300 IO_%d\n", SWITCHES_NAME, i);
++                      return -EBUSY;
++              }
++
++      }
++
++      return 0;
++
++}
++
++static void assabet_switches_ucb1x00_shutdown(void)
++{
++      int i;
++
++      for (i = 5; i >= 0; --i) {
++
++              ucb1x00_disable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING);
++
++              /* Only error conditions are ENOENT and EINVAL; silently
++               * ignore:
++               */
++              ucb1x00_free_irq(ucb1x00, i, NULL);
++
++      }
++
++}
++
++static void assabet_switches_ucb1x00_handler(int irq, switches_mask_t *mask)
++{
++      unsigned int last, this;
++      static unsigned int states = 0;
++
++      last = ((states & (1 << irq)) != 0);
++      this = ((ucb1x00_io_read(ucb1x00) & (1 << irq)) != 0);
++
++      if (last == this) /* debounce */
++              return;
++
++      /* Intel StrongARM SA-1110 Development Board
++       * Schematics Figure 5, Sheet 5 of 12
++       *
++       * See switches S8 and S7. Notice their
++       * relationship to signals SW7 and SW8. Hmmm.
++       */
++
++      switch (irq) {
++
++      case 4:
++
++              SWITCHES_SET(mask, 8, this);
++              break;
++
++      case 5:
++
++              SWITCHES_SET(mask, 7, this);
++              break;
++
++      default:
++
++              SWITCHES_SET(mask, irq + 3, this);
++
++      }
++
++      states = this ? (states | (1 << irq)) : (states & ~(1 << irq));
++
++}
++#endif  /* CONFIG_SA1100_ASSABET */
++
++#ifdef CONFIG_SA1100_SIMPAD
++
++/* SIMpad
++ * ^^^^^^
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ * This code sets bits in the range [3, 8] in the mask that we
++ * return to userland. 
++ */
++
++static int simpad_switches_ucb1x00_init(void)
++{
++      int i;
++
++      /* Note that ucb1x00_init() must complete before this point: */
++
++      if ((ucb1x00 = ucb1x00_get()) == NULL) {
++              printk(KERN_ERR "%s: UCB1300 driver not ready; switches "
++                     "3 -- 8 will not be available\n",
++                     SWITCHES_NAME);
++              return 0;
++      }
++
++      ucb1x00_enable(ucb1x00);
++
++      ucb1x00_io_set_dir(ucb1x00,
++                         UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++                         UCB_IO_3 | UCB_IO_4 | UCB_IO_5, 
++                         UCB_IO_8 | UCB_IO_9);
++
++      ucb1x00_disable(ucb1x00);
++
++      for (i = 0; i < 6; ++i) {
++
++              if (ucb1x00_hook_irq(ucb1x00, i,
++                                   switches_ucb1x00_handler, NULL) < 0) {
++                      printk(KERN_ERR "%s: unable to hook IRQ for "
++                             "UCB1300 IO_%d\n", SWITCHES_NAME, i);
++                      return -EBUSY;
++              }
++              
++              ucb1x00_enable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING);
++      }
++
++      return 0;
++
++}
++
++int simpad_switches_ucb1x00_reinit(void)
++{
++      int i;
++      ucb1x00_enable(ucb1x00);
++
++      ucb1x00_io_set_dir(ucb1x00,
++                      UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++                      UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
++                      UCB_IO_8 | UCB_IO_9);
++
++      ucb1x00_disable(ucb1x00);
++
++      for (i = 0; i < 6; ++i)
++              ucb1x00_enable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING);
++
++      return 0;
++}
++
++static void simpad_switches_ucb1x00_shutdown(void)
++{
++      int i;
++
++      for (i = 5; i >= 0; --i) {
++
++              ucb1x00_disable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING);
++
++              /* Only error conditions are ENOENT and EINVAL; silently
++               * ignore:
++               */
++              ucb1x00_free_irq(ucb1x00, i, NULL);
++
++      }
++
++}
++
++static void simpad_switches_ucb1x00_handler(int irq, switches_mask_t *mask)
++{
++      unsigned int last, this;
++      static unsigned int states = 0;
++
++      last = ((states & (1 << irq)) != 0);
++      this = ((~ucb1x00_io_read(ucb1x00) & (1 << irq)) != 0);
++
++      if (last == this) /* debounce */
++              return;
++
++      switch (irq) {
++
++      case 4:
++
++              
++
++      case 5:
++
++              
++
++      default:
++
++              SWITCHES_SET(mask, irq + 3, this);
++
++      }
++
++      states = this ? (states | (1 << irq)) : (states & ~(1 << irq));
++
++}
++#endif  /* CONFIG_SA1100_SIMPAD */
++
++
++/* switches_ucb1x00_handler()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * This routine is a generalized handler for UCB1x00 GPIO switches
++ * which calls a board-specific service routine and passes an event
++ * mask to the core event handler. This routine is appropriate for
++ * systems which use the ucb1x00 framework, and can be registered
++ * using ucb1x00_hook_irq().
++ */
++static void switches_ucb1x00_handler(int irq, void *devid)
++{
++      switches_mask_t mask;
++
++      SWITCHES_ZERO(&mask);
++
++      /* Porting note: call a board-specific UCB1x00 switch handler here.
++       * The handler can assume that sufficient storage for `mask' has
++       * been allocated, and that the corresponding switches_mask_t
++       * structure has been zeroed.
++       */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              assabet_switches_ucb1x00_handler(irq, &mask);
++#endif
++      }
++      if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              simpad_switches_ucb1x00_handler(irq, &mask);
++#endif
++      }
++
++      switches_event(&mask);
++
++}
++
++int __init switches_ucb1x00_init(void)
++{
++
++      /* Porting note: call a board-specific init routine here. */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              if (assabet_switches_ucb1x00_init() < 0)
++                      return -EIO;
++#endif
++      }
++      if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              if (simpad_switches_ucb1x00_init() < 0)
++                      return -EIO;
++#endif
++      }
++
++      return 0;
++
++}
++
++void __exit switches_ucb1x00_exit(void)
++{
++
++      /* Porting note: call a board-specific shutdown routine here. */
++
++      if (machine_is_assabet()) {
++#ifdef CONFIG_SA1100_ASSABET
++              assabet_switches_ucb1x00_shutdown();
++#endif
++      }
++      if (machine_is_simpad()) {
++#ifdef CONFIG_SA1100_SIMPAD
++              simpad_switches_ucb1x00_shutdown();
++#endif
++      }
++
++}
+--- /dev/null
++++ linux-2.4.27/drivers/misc/switches.h
+@@ -0,0 +1,28 @@
++/*
++ *  linux/drivers/misc/switches.h
++ *
++ *  Copyright (C) 2001 John Dorsey
++ *
++ *  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.
++ *
++ *  19 December 2001 - created.
++ */
++
++#if !defined(_SWITCHES_H)
++# define _SWITCHES_H
++
++#include <linux/switches.h>
++
++#define SWITCHES_NAME "switches"
++
++extern int  switches_event(switches_mask_t *mask);
++
++extern int  switches_sa1100_init(void);
++extern void switches_sa1100_exit(void);
++
++extern int  switches_ucb1x00_init(void);
++extern void switches_ucb1x00_exit(void);
++
++#endif  /* !defined(_SWITCHES_H) */
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00-assabet.c
+@@ -0,0 +1,114 @@
++/*
++ *  linux/drivers/misc/ucb1x00-assabet.c
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  We handle the machine-specific bits of the UCB1x00 driver here.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++
++#include <asm/dma.h>
++
++#include "ucb1x00.h"
++
++static struct proc_dir_entry *dir;
++static struct ucb1x00 *ucb;
++
++static int ucb1x00_assabet_read_vbatt(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD1, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return val;
++}
++
++static int ucb1x00_assabet_read_vcharger(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD0, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return val;
++}
++
++static int ucb1x00_assabet_read_batt_temp(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD2, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return val;
++}
++
++static int ucb_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      char *p = page;
++      int (*fn)(struct ucb1x00 *) = data;
++      int v, len;
++
++      v = fn(ucb);
++
++      p += sprintf(p, "%d\n", v);
++
++      len = (p - page) - off;
++      if (len < 0)
++              len = 0;
++
++      *eof = (len <= count) ? 1 : 0;
++      *start = page + off;
++
++      return len;
++}
++
++static int __init ucb1x00_assabet_init(void)
++{
++      struct proc_dir_entry *res;
++
++      ucb = ucb1x00_get();
++
++      if (!ucb)
++              return -ENODEV;
++
++      dir = proc_mkdir("ucb1x00", NULL);
++      if (!dir)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("vbatt", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_vbatt);
++      if (!res)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("vcharger", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_vcharger);
++      if (!res)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("batt_temp", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_batt_temp);
++      if (!res)
++              return -ENOMEM;
++
++      return 0;
++}
++
++static void __exit ucb1x00_assabet_exit(void)
++{
++      remove_proc_entry("vbatt", dir);
++      remove_proc_entry("vcharger", dir);
++      remove_proc_entry("batt_temp", dir);
++}
++
++module_init(ucb1x00_assabet_init);
++module_exit(ucb1x00_assabet_exit);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver");
++MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/misc/ucb1x00-audio.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/misc/ucb1x00-audio.c
+@@ -283,7 +283,7 @@
+ {
+       struct ucb1x00_audio *ucba;
+-      ucba = kmalloc(sizeof(*ucba), GFP_KERNEL);
++      ucba = kmalloc(sizeof(*ucba), GFP_ATOMIC);
+       if (ucba) {
+               memset(ucba, 0, sizeof(*ucba));
+--- linux-2.4.27/drivers/misc/ucb1x00-core.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/misc/ucb1x00-core.c
+@@ -215,6 +215,9 @@
+               ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
+               ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
+               ucb1x00_disable(ucb);
++#ifdef CONFIG_SA1100_SIMPAD
++              simpad_switches_ucb1x00_reinit();
++#endif
+       }
+       return 0;
+@@ -561,8 +564,10 @@
+               default_irq = IRQ_GPIO_UCB1300_IRQ;
+ #endif
+ #ifdef CONFIG_SA1100_SIMPAD
+-      if (machine_is_simpad())
++      if (machine_is_simpad()) {
+               default_irq = IRQ_GPIO_UCB1300_IRQ;
++              irq_gpio_pin = GPIO_UCB1300_IRQ;
++      }
+ #endif
+ #ifdef CONFIG_SA1100_SIMPUTER
+       if (machine_is_simputer()) {
+@@ -660,7 +665,7 @@
+       if (id == UCB_ID_1400 && mcp_reg_read(mcp, 0x00) == 0x002a)
+               id = UCB_ID_1400_BUGGY;
+-      my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL);
++      my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_ATOMIC);
+       ret = -ENOMEM;
+       if (!my_ucb)
+               goto out;
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00-simpad.c
+@@ -0,0 +1,241 @@
++/*
++ *  linux/drivers/misc/ucb1x00-simpad.c
++ *
++ *  Modified by Juergen Messerer for SIMpad
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  We handle the machine-specific bits of the UCB1x00 driver here.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/proc_fs.h>
++
++#include <asm/dma.h>
++
++#include <asm/arch-sa1100/simpad_pm.h>
++
++#include "ucb1x00.h"
++
++/*
++ * Conversion from AD -> mV
++ * 7.5V = 1023   7.3313mV/Digit
++ *
++ * 400 Units == 9.7V
++ * a     = ADC value
++ * 21    = ADC error
++ * 12600 = Divident to get 2*7.3242
++ * 860   = Divider to get 2*7.3242
++ * 170   = Voltagedrop over
++ */
++#define CALIBRATE_BATTERY(a)   ((((a + 21)*12600)/860) + 170)
++
++/*
++ * We have two types of batteries a small and a large one
++ * To get the right value we to distinguish between those two
++ * 450 Units == 15 V
++ */
++#ifdef SMALL_BATTERY 
++#define CALIBRATE_SUPPLY(a)   (((a) * 1500) / 51)
++#define MIN_SUPPLY            8500 /* Less then 8.5V means no powersupply */
++#else
++#define CALIBRATE_SUPPLY(a)   (((a) * 1500) / 45)
++//#define MIN_SUPPLY            14000 /* Less then 14V means no powersupply */
++#define MIN_SUPPLY            12000 /* Less then 12V means no powersupply */
++#endif
++
++/*
++ * Charging Current 
++ * if value is >= 50 then charging is on
++ */
++#define CALIBRATE_CHARGING(a)    (((a)* 1000)/(152/4)))
++//#define CHARGING_LED_LEVEL     50
++
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
++
++#define CHARGING_LED_LEVEL     12
++#define CHARGING_MAX_LEVEL     120
++#define BATT_FULL     8100
++#define BATT_LOW      7300
++#define BATT_CRITICAL 6700
++#define BATT_EMPTY    6400
++
++
++#else // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++#define CHARGING_LED_LEVEL     28
++#define CHARGING_MAX_LEVEL     265
++#define BATT_FULL     8300
++#define BATT_LOW      7400
++#define BATT_CRITICAL 6800
++#define BATT_EMPTY    6500
++
++#endif // CONFIG_SA1100_SIMPAD_SINUSPAD
++
++
++static struct proc_dir_entry *dir;
++static struct ucb1x00 *ucb;
++
++static int ucb1x00_simpad_read_vbatt(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD1, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return CALIBRATE_BATTERY(val);
++}
++
++static int ucb1x00_simpad_read_vcharger(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD2, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return CALIBRATE_SUPPLY(val);
++}
++
++static int ucb1x00_simpad_read_icharger(struct ucb1x00 *ucb)
++{
++      int val;
++      ucb1x00_adc_enable(ucb);
++      val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD3, UCB_NOSYNC);
++      ucb1x00_adc_disable(ucb);
++
++      return val;
++}
++
++static int ucb_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      char *p = page;
++      int (*fn)(struct ucb1x00 *) = data;
++      int v, len;
++
++      v = fn(ucb);
++
++      p += sprintf(p, "%d\n", v);
++
++      len = (p - page) - off;
++      if (len < 0)
++              len = 0;
++
++      *eof = (len <= count) ? 1 : 0;
++      *start = page + off;
++
++      return len;
++}
++
++/****************************************************************************/
++/*  Functions exported for use by the kernel and kernel modules             */
++/****************************************************************************/
++
++int simpad_get_battery(struct simpad_battery *bstat)
++{
++  int icharger, vcharger, vbatt;
++
++  if ( ucb ) {
++      icharger = ucb1x00_simpad_read_icharger( ucb );
++      vcharger = ucb1x00_simpad_read_vcharger( ucb );
++      vbatt    = ucb1x00_simpad_read_vbatt( ucb );
++  } else {
++      bstat->ac_status = SIMPAD_AC_STATUS_AC_UNKNOWN;
++      bstat->status = SIMPAD_BATT_STATUS_UNKNOWN;
++      bstat->percentage = 0x64; /* lets say 100% */
++      bstat->life = 360;      /* lets say a long time */
++      return 0;
++  }
++
++  /* AC status */
++  bstat->ac_status = SIMPAD_AC_STATUS_AC_OFFLINE;
++  if ( vcharger>MIN_SUPPLY ) {
++      bstat->ac_status = SIMPAD_AC_STATUS_AC_ONLINE;
++  }
++
++  /* charging */
++  bstat->status = 0x0;
++  if ( icharger > CHARGING_LED_LEVEL ) {
++      bstat->status = SIMPAD_BATT_STATUS_CHARGING;
++  }
++
++  if ( vbatt > BATT_LOW )
++      bstat->status |= SIMPAD_BATT_STATUS_HIGH;
++  else if ( vbatt < BATT_CRITICAL )
++      bstat->status |= SIMPAD_BATT_STATUS_CRITICAL;
++  else
++      bstat->status |= SIMPAD_BATT_STATUS_LOW;
++
++  if (bstat->status & SIMPAD_BATT_STATUS_CHARGING) {
++      if (icharger > CHARGING_MAX_LEVEL)  icharger = CHARGING_MAX_LEVEL;
++      if (icharger < CHARGING_LED_LEVEL)  icharger = CHARGING_LED_LEVEL;
++      bstat->percentage = 100 - 100 * (icharger - CHARGING_LED_LEVEL) /
++      (CHARGING_MAX_LEVEL - CHARGING_LED_LEVEL);
++  } else {
++      if (vbatt > BATT_FULL)  vbatt = BATT_FULL;
++      if (vbatt < BATT_EMPTY) vbatt = BATT_EMPTY;
++      bstat->percentage = 100 * (vbatt - BATT_EMPTY) / (BATT_FULL - BATT_EMPTY);
++  }
++
++  /* let's assume: full load is 7h */
++  /* bstat->life = 420*bstat->percentage/100; */
++  bstat->life = 0;
++
++#if 0
++  printk("get_battery: ac: %02x / ch: %02x /  perc: %02x / life: %d\n",
++       bstat->ac_status, bstat->status,
++       bstat->percentage, bstat->life );
++#endif
++
++  return 0;
++}
++
++EXPORT_SYMBOL(simpad_get_battery);
++
++/****************************************************************************/
++/*  sample proc interface                                                   */
++/****************************************************************************/
++static int __init ucb1x00_simpad_init(void)
++{
++      struct proc_dir_entry *res;
++
++      ucb = ucb1x00_get();
++
++      if (!ucb)
++              return -ENODEV;
++
++      dir = proc_mkdir("ucb1x00", NULL);
++      if (!dir)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("vbatt", S_IRUGO, dir, ucb_read, ucb1x00_simpad_read_vbatt);
++      if (!res)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("vcharger", S_IRUGO, dir, ucb_read, ucb1x00_simpad_read_vcharger);
++      if (!res)
++              return -ENOMEM;
++
++      res = create_proc_read_entry("icharger", S_IRUGO, dir, ucb_read, ucb1x00_simpad_read_icharger);
++      if (!res)
++              return -ENOMEM;
++
++      return 0;
++}
++
++static void __exit ucb1x00_simpad_exit(void)
++{
++      remove_proc_entry("vbatt", dir);
++      remove_proc_entry("vcharger", dir);
++      remove_proc_entry("icharger", dir);
++}
++
++module_init(ucb1x00_simpad_init);
++module_exit(ucb1x00_simpad_exit);
++
++MODULE_AUTHOR("Juergen Messerer <juergen.messerer@freesurf.ch>");
++MODULE_DESCRIPTION("SIMpad noddy testing only example ADC driver");
++MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/misc/ucb1x00-ts.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/misc/ucb1x00-ts.c
+@@ -356,7 +356,7 @@
+                       UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
+                       UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+-      udelay(55);
++      udelay(250); /*former 55*/
+       return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
+ }
+@@ -379,7 +379,7 @@
+                       UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
+                       UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
+-      udelay(55);
++      udelay(250); /*former 55*/
+       return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
+ }
+--- linux-2.4.27/drivers/mtd/Config.in~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/mtd/Config.in
+@@ -11,6 +11,9 @@
+    if [ "$CONFIG_MTD_DEBUG" = "y" ]; then
+       int '  Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
+    fi
++   if [ "$CONFIG_CRAMFS" = "y" ]; then
++      bool '  Cramfs root partition' CONFIG_ROOT_CRAMFS
++   fi
+    dep_tristate '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
+    dep_tristate '  MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD
+    dep_tristate '  RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS
+--- linux-2.4.27/drivers/mtd/maps/sa1100-flash.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/mtd/maps/sa1100-flash.c
+@@ -767,40 +767,38 @@
+ #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,
++
++#ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
++#define SIMPAD_FLASH_SIZE               0x01000000
+ #else
+-              name:           "SIMpad initrd",
+-              size:           0x00300000,
+-              offset:         0x00180000,
+-      }, {
+-              name:           "SIMpad root cramfs",
+-              size:           0x00300000,
+-              offset:         0x00480000,
+-      }, {
+-              name:           "SIMpad usr cramfs",
+-              size:           0x005c0000,
+-              offset:         0x00780000,
+-      }, {
+-              name:           "SIMpad usr local",
+-              size:           MTDPART_SIZ_FULL,
+-              offset:         0x00d40000,
++#define SIMPAD_FLASH_SIZE               0x02000000
+ #endif
+-      }
++
++static struct mtd_partition simpad_partitions[] = {
++        {
++                name: "SIMpad boot firmware",
++                offset: 0,
++                size: 0x00080000,
++                mask_flags: MTD_WRITEABLE  /* force read-only */
++        },{
++                name: "SIMpad kernel",
++                offset: MTDPART_OFS_APPEND,
++                size: 0x00100000
++        },{
++#ifdef CONFIG_ROOT_CRAMFS
++                name: "SIMpad root cramfs",
++                offset: MTDPART_OFS_APPEND,
++                size: 0x00D80000
++        },{
++                name: "SIMpad local jffs",
++                offset: MTDPART_OFS_APPEND,
++                size: MTDPART_SIZ_FULL
++#else
++                name: "SIMpad root jffs2",
++                offset:         MTDPART_OFS_APPEND,
++                size:           MTDPART_SIZ_FULL
++#endif /* CONFIG_CRAM_FS */
++        }
+ };
+ #endif /* CONFIG_SA1100_SIMPAD */
+--- linux-2.4.27/drivers/pcmcia/sa1100_simpad.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/pcmcia/sa1100_simpad.c
+@@ -9,20 +9,18 @@
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
++#include <asm/arch/simpad.h>
++
+ #include "sa1100_generic.h"
+- 
++
+ extern long get_cs3_shadow(void);
+-extern void set_cs3_bit(int value); 
++extern void set_cs3_bit(int value);
+ extern void clear_cs3_bit(int value);
+ static int simpad_pcmcia_init(struct pcmcia_init *init){
+   int irq, res;
+-  set_cs3_bit(PCMCIA_RESET);
+-  clear_cs3_bit(PCMCIA_BUFF_DIS);
+-  clear_cs3_bit(PCMCIA_RESET);
+-
+   clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+   /* Set transition detect */
+@@ -63,6 +61,9 @@
+   if(state_array->size<2) return -1;
++  memset(state_array->state, 0, 
++       (state_array->size)*sizeof(struct pcmcia_state));
++
+   levels=GPLR;
+   state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0;
+@@ -100,13 +101,15 @@
+ static int simpad_pcmcia_configure_socket(const struct pcmcia_configure
+                                          *configure)
+ {
+-  unsigned long value, flags;
++  static int irq_disabled = 0;
+-  if(configure->sock>1) return -1;
++  if(configure->sock>1) 
++      return -1;
+-  if(configure->sock==0) return 0;
++  if(configure->sock==0) 
++      return 0;
+-  save_flags_cli(flags);
++  //local_irq_save(flags);
+   /* Murphy: see table of MIC2562a-1 */
+@@ -116,8 +119,8 @@
+     break;
+   case 33:  
+-    clear_cs3_bit(VCC_3V_EN|EN0);
+-    set_cs3_bit(VCC_5V_EN|EN1);
++    clear_cs3_bit(VCC_3V_EN|EN1);
++    set_cs3_bit(VCC_5V_EN|EN0);
+     break;
+   case 50:
+@@ -129,26 +132,50 @@
+     printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
+          configure->vcc);
+     clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+-    restore_flags(flags);
++    //restore_flags(flags);
+     return -1;
+   }
+-  /* Silently ignore Vpp, output enable, speaker enable. */
++  if(configure->reset)
++      set_cs3_bit(PCMCIA_RESET);
++  else
++      clear_cs3_bit(PCMCIA_RESET);
++  
++  if(configure->output)
++      clear_cs3_bit(PCMCIA_BUFF_DIS);
++  else
++      set_cs3_bit(PCMCIA_BUFF_DIS);
+-  restore_flags(flags);
++  if(configure->irq) {
++    enable_irq(IRQ_GPIO_CF_IRQ);
++    irq_disabled = 0;
++  }
++  else {
++    if (!irq_disabled) {
++      disable_irq(IRQ_GPIO_CF_IRQ);
++      irq_disabled = 1;
++    }
++  }
++
++  //local_irq_restore(flags);
+   return 0;
+ }
+ static int simpad_pcmcia_socket_init(int sock)
+ {
+-  set_GPIO_IRQ_edge(GPIO_CF_CD, GPIO_BOTH_EDGES);
++  if(sock == 1)
++      set_GPIO_IRQ_edge(GPIO_CF_CD, GPIO_BOTH_EDGES);
+   return 0;
+ }
+ static int simpad_pcmcia_socket_suspend(int sock)
+ {
+-  set_GPIO_IRQ_edge(GPIO_CF_CD, GPIO_NO_EDGES);
++  if(sock == 1)
++  {
++    set_GPIO_IRQ_edge(GPIO_CF_CD, GPIO_NO_EDGES);
++    set_cs3_bit(PCMCIA_RESET);  
++  }
+   return 0;
+ }
+--- linux-2.4.27/drivers/video/Config.in~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/video/Config.in
+@@ -61,6 +61,10 @@
+      fi
+    fi
+    dep_tristate '  CyberPro 2000/2010/5000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI
++   if [ "$CONFIG_SA1100_SIMPAD" = "y"  -o \
++        "$CONFIGG_SA1100_GDS2200" = "y" ]; then
++      bool '  MQ200 VGA support' CONFIG_FB_MQ200
++   fi
+    if [ "$CONFIG_APOLLO" = "y" ]; then
+       define_bool CONFIG_FB_APOLLO y
+    fi
+--- linux-2.4.27/drivers/video/Makefile~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/drivers/video/Makefile
+@@ -141,6 +141,7 @@
+ obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
+ obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
+ obj-$(CONFIG_FB_ANAKIN)           += anakinfb.o
++obj-$(CONFIG_FB_MQ200)            += mq200fb.o
+ # Generic Low Level Drivers
+--- /dev/null
++++ linux-2.4.27/drivers/video/mq200fb.c
+@@ -0,0 +1,1963 @@
++/*  MQ200 console frame buffer driver---mq200fb.c
++ *  
++ *
++ *  This file is subject to the terms and conditions of the GNU General Public
++ *  License. See the file COPYING in the main directory of this archive for
++ *  more details.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/proc_fs.h>              /* all the /proc functions */
++#include <linux/ioport.h>
++#include <asm/uaccess.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <asm/io.h>
++
++#include <linux/console.h>
++
++#include <video/fbcon.h>
++#ifdef CONFIG_FBCON_MFB
++#include <video/fbcon-mfb.h>
++#endif
++#ifdef CONFIG_FBCON_CFB2
++#include <video/fbcon-cfb2.h>
++#endif
++#ifdef CONFIG_FBCON_CFB4
++#include <video/fbcon-cfb4.h>
++#endif
++#ifdef CONFIG_FBCON_CFB8
++#include <video/fbcon-cfb8.h>
++#endif
++#ifdef CONFIG_FBCON_CFB16
++#include <video/fbcon-cfb16.h>
++#endif
++#ifdef CONFIG_FBCON_CFB24
++#include <video/fbcon-cfb24.h>
++#endif
++#ifdef CONFIG_FBCON_CFB32
++#include <video/fbcon-cfb32.h>
++#endif
++
++#include <video/MQ200/mq2hw.h>
++#include <video/MQ200/mqdata.h>
++#include <video/MQ200/mqplat.h>
++
++/* GPIO handling */
++#include <asm/irq.h>
++#include <asm/hardware.h>
++
++#ifdef TEST
++#undef PDEBUG /* Safety */
++
++#define PDEBUG(fmt, args...) printk(KERN_EMERG fmt, ##args)
++#else
++#define PDEBUG(fmt, args...)
++#endif
++
++#define MQ200_DIRNAME "driver/mq200"
++#define REG_DIRNAME "registers"
++
++void enable_cursor(void *pMQMMIO);
++
++static ssize_t proc_read_reg(struct file * file, char * buf,
++              size_t nbytes, loff_t *ppos);
++static ssize_t proc_write_reg(struct file * file, const char * buffer,
++              size_t count, loff_t *ppos);
++
++static struct file_operations proc_reg_operations = {
++      read:   proc_read_reg,
++      write:  proc_write_reg
++};
++
++typedef struct sa1110_reg_entry {
++      u32 phyaddr;
++      char* name;
++      char* description;
++      unsigned short low_ino;
++} sa1110_reg_entry_t;
++
++#define CMAPSIZE 32
++#define arraysize(x)  (sizeof(x)/sizeof(*(x)))
++
++#define mq200_p2v( x )             \
++   (((x) - 0x4b800000) + 0xf2800000)
++
++/* The following is copied from mq2hw.c for initialization of MQ200 */
++/* PLL1 data */
++#define PLL1_83MHZ                      0x0EF2082A
++#define DEF_MIU2_83MHZ                  0x4143E086
++#define PLL1_50MHZ                      0x0B200A2A
++#define DEF_MIU2_50MHZ                  0x40C30086
++
++/* Miscellaneous default data */
++#define DEF_D1                          0x05000271
++#define DEF_D2                          0x00000271
++#define DEF_MIU3                        0x6D6AABFF
++#define DEF_MIU4                        0x00000001
++#define DEF_MIU5                        0x0000010D
++         
++#ifdef CONFIG_SA1100_GDS2200
++#define DEF_GPO_CONTROL                 0x00020054
++#else
++#define DEF_GPO_CONTROL                 0x00000000
++#endif
++         
++#define DEF_GPIO_CONTROL                0x00000000
++#define DEF_PWM_CONTROL                 0x00A16c44
++#define PWMOFFMASK                      0xFF0FFF0F
++
++struct fb_info_mq200 {
++        struct fb_info fb_info;
++        struct fb_fix_screeninfo fix;
++        struct fb_var_screeninfo var;
++        struct display disp;
++        struct {
++            __u8 red, green, blue;
++        } palette[256];
++        struct fb_info_mq200 *next;
++        unsigned int mqMmioAddrVirt;
++        unsigned int mqFbAddrVirt;
++        unsigned int mqMmioAddrPhys;
++        unsigned int mqFbAddrPhys;
++#ifdef CONFIG_PM
++        struct pm_dev         *pm;
++#endif
++};
++
++u32 mqflag;
++u32 mqMmioAddr, mqFbAddr;
++
++/* Interface need to console.c. The following variable are defined in 
++   drivers/char/console.c */
++
++extern unsigned char color_table[];
++extern int default_red[];
++extern int default_grn[];
++extern int default_blu[];
++
++#ifdef CONFIG_SA1100_SIMPAD
++    extern void set_cs3_bit(int value);
++    extern void clear_cs3_bit(int value);
++    DISPLAY_CONFIG dc = {800, 600, 16, 60, 1600, 0x00130004};
++#else
++    DISPLAY_CONFIG dc = {800, 600, 32, 60, 3200, 0x0023000f};
++#endif
++
++static int currcon = 0;
++static char mq200fb_name[16] = "MQ200FB";
++
++static struct fb_var_screeninfo mq200fb_default = {
++    /* 800x600, 32 bpp */
++    800, 600, 800, 600, 0, 0, 32, 0,
++    {0, 8, 0}, {8, 8, 0}, {16, 8, 0}, {24, 8, 0},
++    0, 0, -1, -1, 0, MQ200_FB_SIZE, 64, 64, 32, 32, 64, 2,
++    0, FB_VMODE_NONINTERLACED
++};
++static union {
++    u16 cfb16[CMAPSIZE];
++    u32 cfb24[CMAPSIZE];
++    u32 cfb32[CMAPSIZE];
++} fbcon_cmap;
++
++static struct proc_dir_entry *regdir;
++static struct proc_dir_entry *mq200dir;
++static struct proc_dir_entry *driverdir;
++
++/* Functions used to initialize MQ200 chip */
++void setmqmode(PDISPLAY_CONFIG, void *);
++void setup_cursor(void *);
++void onoffdisplay(int, void *);
++unsigned long getbppbits(int);
++PDISPLAY_TIMING getgcparam(int, int, int);
++void setpal(int, unsigned long, void *);
++void setupfp(int, void *);
++void setuphfbuffer(int, unsigned long, void *);
++void setupgc(int, int, int, int, int, void *);
++void setupgcmem(PDISPLAY_CONFIG, unsigned long, void *);
++long idmqchip(void *pMQMMIO);
++void turnoffMQ200(void * pMQMMIO);
++
++/*  Interface used by the world */
++int mq200fb_setup(char*);
++static int mq200fb_open(struct fb_info *info, int user);
++static int mq200fb_release (struct fb_info *info, int user);
++static int mq200fb_get_fix(struct fb_fix_screeninfo *fix, int con, \
++                     struct fb_info *info);
++static int mq200fb_get_var(struct fb_var_screeninfo *var, int con, \
++                     struct fb_info *info);
++static int mq200fb_set_var(struct fb_var_screeninfo *var, int con, \
++                     struct fb_info *info);
++static int mq200fb_pan_display(struct fb_var_screeninfo *var, int con, \
++                         struct fb_info *info);
++static int mq200fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, \
++                      struct fb_info *info);
++static int mq200fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, \
++                      struct fb_info *info);
++static int mq200fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
++                     u_long arg, int con, struct fb_info *info);
++
++/*  Interface to the low level console driver */
++int mq200fb_init(void);
++static int mq200fbcon_switch(int con, struct fb_info *info);
++static int mq200fbcon_updatevar(int con, struct fb_info *info);
++static void mq200fbcon_blank(int blank, struct fb_info *info);
++
++/* int  sa1100fb_map_video_memory(void * pmem int memsize); */
++
++/*  
++ *Internal routines 
++ */
++
++static u_long get_line_length(int xres_virtual, int bpp);
++static void mq200fb_encode_fix(struct fb_fix_screeninfo *fix,
++                         struct fb_var_screeninfo *var);
++static void set_color_bitfields(struct fb_var_screeninfo *var);
++static int mq200fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
++                         u_int *transp, struct fb_info *info);
++static int mq200fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                         u_int transp, struct fb_info *info);
++static void do_install_cmap(int con, struct fb_info *info);
++
++#ifdef CONFIG_PM
++static int mq200fb_pm_callback(struct pm_dev *, pm_request_t, void *);
++#endif
++
++#if defined(CONFIG_SA1100_GDS2200) || defined(CONFIG_SA1100_SIMPAD)
++static void mq200_backlight(void *, int);
++#endif
++
++#ifdef CONFIG_SA1100_SIMPAD
++static void writeBrightness(void *, int);
++#endif
++
++static struct fb_ops mq200fb_ops = { 
++        owner:          THIS_MODULE,
++        fb_get_fix:     mq200fb_get_fix,
++        fb_get_var:     mq200fb_get_var,
++        fb_set_var:     mq200fb_set_var,
++        fb_get_cmap:    mq200fb_get_cmap,
++        fb_set_cmap:    mq200fb_set_cmap,
++        fb_pan_display: mq200fb_pan_display,
++        fb_ioctl:       mq200fb_ioctl,
++};
++
++typedef struct mq200_reg_entry {
++      u32 phyaddr;
++      char* name;
++      char* description;
++      unsigned short low_ino;
++} mq200_reg_entry_t;
++
++static mq200_reg_entry_t mq200_regs[] =
++{
++      { 0x4be00000, "PM_MISC", "MQ200 PM_MISC" },
++      { 0x4be00004, "D1_STATE", "MQ200 D1_STATE" },
++      { 0x4be00008, "D2_STATE", "MQ200 D2_STATE" },
++      { 0x4be00018, "PLL2_CONTROL", "MQ200 PLL2_CONTROL" },
++      { 0x4be0001c, "PLL3_CONTROL", "MQ200 PLL3_CONTROL" },
++      { 0x4be02000, "CPU_CONTROL", "MQ200 CPU_CONTROL" },
++      { 0x4be02004, "DRAW_STATUS", "MQ200 DRAW_STATUS" },
++      { 0x4be04000, "MIU_CONTROL1", "MQ200 MIU_CONTROL1" },
++      { 0x4be04004, "MIU_CONTROL2", "MQ200 MIU_CONTROL2" },
++      { 0x4be04008, "MIU_CONTROL3", "MQ200 MIU_CONTROL3" },
++      { 0x4be0400c, "MIU_CONTROL4", "MQ200 MIU_CONTROL4" },
++      { 0x4be04010, "MIU_CONTROL5", "MQ200 MIU_CONTROL5" },
++      { 0x4be0a000, "GC1_CONTROL", "MQ200 GC1_CONTROL" },
++      { 0x4be0a004, "GC1_CRT_CONTROL", "MQ200 GC1_CRT_CONTROL" },
++      { 0x4be0a008, "HD1_CONTROL", "MQ200 HD1_CONTROL" },
++      { 0x4be0a00c, "VD1_CONTROL", "MQ200 VD1_CONTROL" },
++      { 0x4be0a010, "HS1_CONTROL", "MQ200 HS1_CONTROL" },
++      { 0x4be0a014, "VS1_CONTROL", "MQ200 VS1_CONTROL" },
++      { 0x4be0a020, "HW1_CONTROL", "MQ200 HW1_CONTROL" },
++      { 0x4be0a024, "VW1_CONTROL", "MQ200 VW1_CONTROL" },
++      { 0x4be0a040, "HW_CURSOR1_POS", "MQ200 HW_CURSOR1_POS" },
++      { 0x4be0a044, "HW_CURSOR1_ADDR", "MQ200 HW_CURSOR1_ADDR" },
++      { 0x4be0e000, "FP_CONTROL", "MQ200 FP_CONTROL" },
++      { 0x4be0e004, "FP_PIN_CONTROL", "MQ200 FP_PIN_CONTROL" },
++      { 0x4be0e008, "FP_GPO_CONTROL", "MQ200 FP_GPO_CONTROL" },
++      { 0x4be0e00c, "FP_GPIO_CONTROL", "MQ200 FP_GPIO_CONTROL" },
++      { 0x4be0e010, "STN_CONTROL", "MQ200 STN_CONTROL" },
++      { 0x4be0e014, "DSTN_FB_CONTROL", "MQ200 DSTN_FB_CONTROL" },
++      { 0x4be0e03c, "PWM_CONTROL", "MQ200 PWM_CONTROL" },
++      { 0x4be14000, "DC_0", "MQ200 DC_1" },
++      { 0x4be14004, "DC_1", "MQ200 DC_2" },
++      { 0x4be14008, "DC_SW_0", "MQ200 DC_SW_0" },
++      { 0x4be1400c, "DC_SW_1", "MQ200 DC_SW_1" },
++      { 0x4be16040, "PMR", "MQ200 PMR" },
++      { 0x4be16044, "PMCSR", "MQ200 PMCSR" }
++};
++
++#define NUM_OF_MQ200_REG_ENTRY        (sizeof(mq200_regs)/sizeof(mq200_reg_entry_t))
++
++static int mq200fb_open(struct fb_info *info, int user)
++{
++    /*
++     *  Nothing, only a usage count for the moment
++     */
++    MOD_INC_USE_COUNT;
++    return(0);
++
++}
++
++/* Release console */
++static int mq200fb_release (struct fb_info *info, int user)
++{
++    struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;
++    turnoffMQ200((void *) p->mqMmioAddrVirt);
++    MOD_DEC_USE_COUNT;
++    return 0;
++}
++/*  Get the Fixed Part of the Display */
++static int mq200fb_get_fix(struct fb_fix_screeninfo *fix, int con,
++                     struct fb_info *info)
++{
++     struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;
++
++     PDEBUG("mq200fb: %i---in mq200fb_get_fix.\n", __LINE__);
++
++     *fix = p->fix;
++     return 0;
++
++}
++
++
++    /*
++     *  Get the User Defined Part of the Display
++     */
++
++static int mq200fb_get_var(struct fb_var_screeninfo *var, int con,
++                     struct fb_info *info)
++{
++    struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;
++
++    PDEBUG("mq200fb: %i---in mq200fb_get_var.\n", __LINE__);
++
++    *var = p->var;
++    return 0;
++}
++
++
++    /*
++     *  Set the User Defined Part of the Display
++     */
++
++static int mq200fb_set_var(struct fb_var_screeninfo *var, int con,
++                     struct fb_info *info)
++{
++    struct fb_info_mq200 * p = (struct fb_info_mq200 *) info;
++    int err, activate = var->activate;
++    int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
++    u_long line_length;
++   
++    struct display *display;
++
++    PDEBUG("mq200fb: %i---in mq200fb_set_var.\n", __LINE__);
++
++    if (con >= 0)
++      display = &fb_display[con];
++    else
++      display = &(p->disp);   /* used during initialization */
++
++    /*
++     *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
++     *  as FB_VMODE_SMOOTH_XPAN is only used internally
++     */
++
++    if (var->vmode & FB_VMODE_CONUPDATE) {
++      var->vmode |= FB_VMODE_YWRAP;
++      var->xoffset = display->var.xoffset;
++      var->yoffset = display->var.yoffset;
++    }
++
++    /*
++     *  Memory limit
++     */
++    line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
++    if (line_length*var->yres_virtual > MQ200_FB_SIZE)
++      return -ENOMEM;
++
++    set_color_bitfields(var);
++
++    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
++      oldxres = display->var.xres;
++      oldyres = display->var.yres;
++      oldvxres = display->var.xres_virtual;
++      oldvyres = display->var.yres_virtual;
++      oldbpp = display->var.bits_per_pixel;
++      display->var = *var;
++      if (oldxres != var->xres || oldyres != var->yres ||
++          oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
++          oldbpp != var->bits_per_pixel) {
++
++          display->screen_base = (char *) p->mqFbAddrVirt;
++          display->visual = p->fix.visual;
++          display->type = p->fix.type;
++          display->type_aux = p->fix.type_aux;
++          display->ypanstep = p->fix.ypanstep;
++          display->ywrapstep = p->fix.ywrapstep;
++          display->line_length = p->fix.line_length;
++          display->can_soft_blank = 1;
++          display->inverse = 0;
++
++          switch (var->bits_per_pixel) {
++#ifdef CONFIG_FBCON_MFB
++              case 1:
++                  display->dispsw = &fbcon_mfb;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB2
++              case 2:
++                  display->dispsw = &fbcon_cfb2;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB4
++              case 4:
++                  display->dispsw = &fbcon_cfb4;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB8
++              case 8:
++                  display->dispsw = &fbcon_cfb8;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB16
++              case 16:
++                  display->dispsw = &fbcon_cfb16;
++                  display->dispsw_data = fbcon_cmap.cfb16;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB24
++              case 24:
++                  display->dispsw = &fbcon_cfb24;
++                  display->dispsw_data = fbcon_cmap.cfb24;
++                  break;
++#endif
++#ifdef CONFIG_FBCON_CFB32
++              case 32:
++                  display->dispsw = &fbcon_cfb32;
++                  display->dispsw_data = fbcon_cmap.cfb32;
++                  break;
++#endif
++              default:
++                  display->dispsw = &fbcon_dummy;
++                  break;
++          }
++
++
++          if (p->fb_info.changevar)
++              (*p->fb_info.changevar)(con);
++      }
++
++      if (oldbpp != var->bits_per_pixel) {
++          if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
++              return err;
++          do_install_cmap(con, info);
++      }
++
++    }
++
++    return 0;
++}
++
++
++    /*
++     *  Pan or Wrap the Display
++     *
++     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
++     */
++
++static int mq200fb_pan_display(struct fb_var_screeninfo *var, int con,
++                         struct fb_info *info)
++{
++    PDEBUG("mq200fb: %i---mq200fb_pan_display.\n", __LINE__);
++
++    if (var->vmode & FB_VMODE_YWRAP) {
++      if (var->yoffset < 0 ||
++          var->yoffset >= fb_display[con].var.yres_virtual ||
++          var->xoffset)
++          return -EINVAL;
++    } else {
++      if (var->xoffset+fb_display[con].var.xres >
++          fb_display[con].var.xres_virtual ||
++          var->yoffset+fb_display[con].var.yres >
++          fb_display[con].var.yres_virtual)
++          return -EINVAL;
++    }
++    fb_display[con].var.xoffset = var->xoffset;
++    fb_display[con].var.yoffset = var->yoffset;
++    if (var->vmode & FB_VMODE_YWRAP)
++      fb_display[con].var.vmode |= FB_VMODE_YWRAP;
++    else
++      fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
++    return 0;
++}
++
++    /*
++     *  Get the Colormap
++     */
++
++static int mq200fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++                      struct fb_info *info)
++{
++    PDEBUG("mq200fb: %i---mq200fb_get_cmap.\n", __LINE__);
++
++    if (con == currcon) /* current console? */
++      return fb_get_cmap(cmap, kspc, mq200fb_getcolreg, info);
++    else if (fb_display[con].cmap.len) /* non default colormap? */
++      fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
++    else
++      {
++       int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
++       fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
++      }
++    return 0;
++}
++
++    /*
++     *  Set the Colormap
++     */
++
++static int mq200fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++                      struct fb_info *info)
++{
++    int err;
++
++    PDEBUG("mq200fb: %i---mq200fb_set_cmap.\n", __LINE__);
++
++    if (!fb_display[con].cmap.len) {  /* no colormap allocated? */
++      int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;        
++        if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
++          return err;
++    }
++    if (con == currcon)                       /* current console? */
++      return fb_set_cmap(cmap, kspc, mq200fb_setcolreg, info);
++    else
++      fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
++    return 0;
++}
++
++
++static int mq200fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
++                     u_long arg, int con, struct fb_info *info)
++{
++      return -EINVAL;
++}
++
++
++int __init mq200fb_setup(char *options)
++{
++      PDEBUG("mq200fb.c: %i---mq200fb_setup\n", __LINE__);
++
++/*
++    char *this_opt;
++
++    fb_info.fontname[0] = '\0';
++
++    if (!options || !*options)
++      return 0;
++
++    for (this_opt = strtok(options, ","); this_opt;
++       this_opt = strtok(NULL, ",")) {
++      if (!strncmp(this_opt, "font:", 5))
++          strcpy(fb_info.fontname, this_opt+5);
++    }
++*/
++    return 0;
++}
++
++
++    /*
++     *  Initialisation
++     */
++
++int __init mq200fb_init(void)
++{
++    struct fb_info_mq200 *p = NULL;
++    int i; /* used as loop counter */
++    struct proc_dir_entry *entry;
++
++    p = (struct fb_info_mq200 *) kmalloc(sizeof(*p), GFP_ATOMIC);
++    if(p==NULL)
++       return 0;
++    memset(p, 0, sizeof(*p));
++
++    mq200dir = proc_mkdir(MQ200_DIRNAME, NULL);
++    if (mq200dir == NULL) {
++       printk(KERN_ERR "mq200fb: can't create /proc/" MQ200_DIRNAME "\n");
++       return(-ENOMEM);
++    }
++
++    regdir = proc_mkdir(REG_DIRNAME, mq200dir);
++    if (regdir == NULL) {
++       printk(KERN_ERR "mq200fb: can't create /proc/" MQ200_DIRNAME "/" REG_DIRNAME "\n");
++       return(-ENOMEM);
++    }
++
++    for(i=0;i<NUM_OF_MQ200_REG_ENTRY;i++) {
++       entry = create_proc_entry(mq200_regs[i].name,
++                               S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH,
++                               regdir);
++       if(entry) {
++        mq200_regs[i].low_ino = entry->low_ino;
++        entry->proc_fops = &proc_reg_operations;
++       } 
++       else {
++        printk( KERN_ERR
++                "mq200fb: can't create /proc/" REG_DIRNAME
++                "/%s\n", mq200_regs[i].name);
++        return(-ENOMEM);
++       }
++    }
++
++
++#ifdef MQ_SA1110
++
++#ifdef CONFIG_SA1100_ASSABET
++    pCPUReg=(u32 *)ioremap( MSC2, 4);
++    *pCPUReg = 0x42194449;
++    iounmap((void *) pCPUReg);
++
++    GPDR |=  0x08000000;
++    GAFR |=  0x08000000;
++    TUCR |= 0x20000000;
++
++    ASSABET_BCR_set(ASSABET_BCR_GFX_RST); /* ASSABET_BCR */
++
++    MQ_DELAY(8);
++    ASSABET_BCR_clear(ASSABET_BCR_GFX_RST);
++    MQ_DELAY(30);
++    ASSABET_BCR_set(ASSABET_BCR_GFX_RST); /* ASSABET_BCR */
++#endif
++
++#ifdef CONFIG_SA1100_SIMPAD
++    GPDR |=   (1<<3);
++    GAFR |=  ~(1<<3);
++    GPSR |=   (1<<3);
++#endif
++
++    p->mqMmioAddrPhys=REGISTER_BASE;
++    p->mqFbAddrPhys=FB_BASE;
++
++    p->mqMmioAddrVirt = mqMmioAddr = 0xf2e00000;
++    p->mqFbAddrVirt = mqFbAddr = 0xf2800000;
++
++#endif /* MQ_SA1110 */
++
++    mqflag = dc.flag;
++    
++    PDEBUG("mq200fb.c: line %i, mqMmioAddr = 0X%08X,  mqFbAddr = 0X%08X\n",\
++           __LINE__, mqMmioAddr, mqFbAddr);
++
++    /* Setmode for MQ200 chip */
++    setmqmode(&dc, (void *) mqMmioAddr);
++
++    /* Set fb_info_mq200.fix info */
++    strcpy(p->fix.id, mq200fb_name);
++    p->fix.smem_start = p->mqFbAddrPhys;
++    p->fix.smem_len = MQ200_FB_SIZE;
++    p->fix.mmio_start = p->mqMmioAddrPhys;
++    p->fix.mmio_len = MQ200_MMIO_SIZE;
++    p->fix.type = FB_TYPE_PACKED_PIXELS;
++
++    if(dc.bpp <= 8) 
++      p->fix.visual = FB_VISUAL_PSEUDOCOLOR;
++    else if (dc.bpp >= 16)
++       p->fix.visual = FB_VISUAL_DIRECTCOLOR;
++    
++    p->fix.line_length = dc.stride;
++
++    /* Set fb_info_mq200.var info */
++    p->var.xres = dc.x;
++    p->var.yres = dc.y;
++    p->var.xres_virtual = dc.x;
++    p->var.yres_virtual = dc.y;
++    p->var.bits_per_pixel = dc.bpp;
++
++    if(dc.bpp == 8) {
++       p->var.red.offset = 0;
++       p->var.green.offset = 0;
++       p->var.blue.offset = 0;
++       p->var.red.length = p->var.green.length = \
++       p->var.blue.length = dc.bpp;
++       p->var.transp.length = 0;
++       p->var.transp.offset = 0;
++    } 
++    else if(dc.bpp == 16) {
++#ifdef CONF
++       IG_PREP
++       p->var.red.offset = 2;
++       p->var.green.offset = -3;
++       p->var.blue.offset = 8;
++#else
++       p->var.red.offset = 11;
++       p->var.green.offset = 5;
++       p->var.blue.offset = 0;
++#endif
++
++       p->var.red.length = 5;
++       p->var.green.length = 6;
++       p->var.blue.length = 5;
++    } 
++    else if (dc.bpp == 24) {
++#ifdef CONFIG_PREP
++       p->var.red.offset = 8;
++       p->var.green.offset = 16;
++       p->var.blue.offset = 24;
++#else
++       p->var.red.offset = 16;
++       p->var.green.offset = 8;
++       p->var.blue.offset = 0;
++#endif
++       p->var.red.length = 8;
++       p->var.green.length = 8;
++       p->var.blue.length = 8;
++    } 
++    else if(dc.bpp == 32) {
++#ifdef CONFIG_PREP
++       p->var.red.offset = 8;
++       p->var.green.offset = 16;
++       p->var.blue.offset = 24;
++#else
++       p->var.red.offset = 0;
++       p->var.green.offset = 8;
++       p->var.blue.offset = 16;
++#endif /* CONFIG_PREP */
++       p->var.red.length = 8;
++       p->var.green.length = 8;
++       p->var.blue.length = 8;
++    }
++     
++    p->var.transp.length = 0;
++    p->var.transp.offset = 0;
++    p->var.height = p->var.width = -1;
++    p->var.vmode = FB_VMODE_NONINTERLACED;
++    p->var.pixclock = 10000;
++    p->var.left_margin = p->var.right_margin = 16;
++    p->var.upper_margin = p->var.lower_margin = 16;
++    p->var.hsync_len = p->var.vsync_len = 8;
++
++    /* Set fb_info_mq200.disp info */
++    p->disp.var = p->var;
++    p->disp.cmap.red = NULL;
++    p->disp.cmap.green = NULL;
++    p->disp.cmap.blue = NULL;
++    p->disp.cmap.transp = NULL;
++    p->disp.screen_base = (char *) p->mqFbAddrVirt;
++    p->disp.visual = p->fix.visual;
++    p->disp.type = p->fix.type;
++    p->disp.type_aux = p->fix.type_aux;
++    p->disp.line_length = p->fix.line_length;
++    p->disp.can_soft_blank = 1;
++
++    switch(dc.bpp) {
++#ifdef CONFIG_FBCON_CFB8
++      case 8:
++          p->disp.dispsw = &fbcon_cfb8;
++          break;
++#endif
++#ifdef CONFIG_FBCON_CFB16
++      case 16:
++          p->disp.dispsw = &fbcon_cfb16;
++          p->disp.dispsw_data = fbcon_cmap.cfb16;
++          break;
++#endif
++#ifdef CONFIG_FBCON_CFB24
++      case 24:
++          p->disp.dispsw = &fbcon_cfb24;
++          p->disp.dispsw_data = fbcon_cmap.cfb24;
++          break;
++#endif
++#ifdef CONFIG_FBCON_CFB32
++      case 32:
++          p->disp.dispsw = &fbcon_cfb32;
++          p->disp.dispsw_data = fbcon_cmap.cfb32;
++          break;
++#endif
++      default:
++          PDEBUG("mq200fb.c: %i---Wrong configuration options", __LINE__);
++    }
++
++    p->disp.scrollmode = SCROLL_YREDRAW;
++
++    strcpy(p->fb_info.modename, p->fix.id);
++    p->fb_info.changevar = NULL;
++    p->fb_info.node = NODEV;
++   
++    p->fb_info.fbops = &mq200fb_ops;
++    p->fb_info.disp = &(p->disp);
++    p->fb_info.switch_con = &mq200fbcon_switch;
++    p->fb_info.updatevar = &mq200fbcon_updatevar;
++    p->fb_info.blank = &mq200fbcon_blank;
++    p->fb_info.flags = FBINFO_FLAG_DEFAULT;
++
++    for (i = 0; i < 16; i++) {
++       int j = color_table[i];
++       p->palette[i].red = default_red[j];
++       p->palette[i].green = default_grn[j];
++       p->palette[i].blue = default_blu[j];
++    }
++
++    if (register_framebuffer(&p->fb_info) < 0) {
++        PDEBUG("Oops...register_framebuffer failed!\n");
++        iounmap(p);
++        iounmap((void *)mqMmioAddr);
++        iounmap((void *)mqFbAddr);
++        return -EINVAL;
++    }
++    
++#ifdef CONFIG_PM
++    /*
++     * Note that the console registers this as well, but we want to
++     * power down the display prior to sleeping.
++     */
++     p->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, mq200fb_pm_callback);
++     if (p->pm)
++        p->pm->data = p;
++#endif
++     PDEBUG("fb%d: Virtual frame buffer device, using %ldK of video memory\n", \
++          GET_FB_IDX(p->fb_info.node), MQ200_FB_SIZE >> 10);
++     return 0;
++}
++
++static int mq200fbcon_switch(int con, struct fb_info *info)
++{
++    /* Do we have to save the colormap? */
++
++     PDEBUG("mq200fb: mq200fbcon_switch.\n");
++
++    if (fb_display[currcon].cmap.len)
++      fb_get_cmap(&fb_display[currcon].cmap, 1, mq200fb_getcolreg, info);
++
++    currcon = con;
++    /* Install new colormap */
++    do_install_cmap(con, info);
++    return 0;
++}
++
++/*
++ *  Update the `var' structure (called by fbcon.c)
++ */
++
++static int mq200fbcon_updatevar(int con, struct fb_info *info)
++{
++      /* Nothing */
++
++      PDEBUG("mq200fb: mq200fbcon_updatevar.\n");
++
++      return 0;
++}
++
++/*
++ *  Blank the display.
++ */
++
++static void mq200fbcon_blank(int blank, struct fb_info *info)
++{
++  /*struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;*/
++      
++      /* Nothing */
++      /*
++      if(blank)
++        onoffdisplay(DISABLE_LCD_GC1, (void *) p->mqMmioAddrVirt);
++      else
++      onoffdisplay(ENABLE_LCD_GC1, (void *) p->mqMmioAddrVirt);*/
++}
++
++static u_long get_line_length(int xres_virtual, int bpp)
++{
++    u_long length;
++
++    PDEBUG("mq200fb: get_line_length.\n");
++
++    length = (xres_virtual+bpp-1)/bpp;
++    length = (length+31)&-32;
++    length >>= 3;
++    return(length);
++}
++
++static void mq200fb_encode_fix(struct fb_fix_screeninfo *fix,
++                         struct fb_var_screeninfo *var)
++{
++
++    PDEBUG("mq200fb: mq200fb_encode_fix.\n");
++
++    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
++    strcpy(fix->id, mq200fb_name);
++    fix->smem_start = mqFbAddr;
++    fix->smem_len = MQ200_FB_SIZE;
++    fix->type = FB_TYPE_PACKED_PIXELS;
++    fix->type_aux = 0;
++    switch (var->bits_per_pixel) {
++      case 1:
++          fix->visual = FB_VISUAL_MONO01;
++          break;
++      case 2:
++      case 4:
++      case 8:
++          fix->visual = FB_VISUAL_PSEUDOCOLOR;
++          break;
++      case 16:
++      case 24:
++      case 32:
++          fix->visual = FB_VISUAL_DIRECTCOLOR;
++          break;
++    }
++    fix->ywrapstep = 1;
++    fix->xpanstep = 1;
++    fix->ypanstep = 1;
++    fix->line_length = get_line_length(var->xres_virtual, var->bits_per_pixel);
++}
++
++static void set_color_bitfields(struct fb_var_screeninfo *var)
++{
++
++    PDEBUG("mq200fb: set_color_bitfields.\n");
++
++    switch (var->bits_per_pixel) {
++      case 1:
++      case 8:
++          var->red.offset = 0;
++          var->red.length = 8;
++          var->green.offset = 0;
++          var->green.length = 8;
++          var->blue.offset = 0;
++          var->blue.length = 8;
++          var->transp.offset = 0;
++          var->transp.length = 0;
++          break;
++      case 16:        /* RGB 565 */
++#ifdef CONFIG_PREP
++          var->red.offset = 2;
++          var->green.offset = -3;
++            var->blue.offset = 8;
++#else
++            var->red.offset = 11;
++            var->green.offset = 5;
++            var->blue.offset = 0;
++#endif
++            var->red.length = 5;
++          var->green.length = 6;
++            var->blue.length = 5;
++            var->transp.length = 0;
++            var->transp.offset = 0;
++          break;
++      case 24:        /* RGB 888 */
++#ifdef CONFIG_PREP
++            var->red.offset = 8;
++            var->green.offset = 16;
++            var->blue.offset = 24;
++#else
++            var->red.offset = 16;
++            var->green.offset = 8;
++            var->blue.offset = 0;
++#endif
++            var->red.length = 8;
++            var->green.length = 8;
++            var->blue.length = 8;
++          break;
++      case 32:        /* RGBA 8888 */
++          var->red.offset = 0;
++          var->red.length = 8;
++          var->green.offset = 8;
++          var->green.length = 8;
++          var->blue.offset = 16;
++          var->blue.length = 8;
++          var->transp.offset = 24;
++          var->transp.length = 8;
++          break;
++    }
++    var->red.msb_right = 0;
++    var->green.msb_right = 0;
++    var->blue.msb_right = 0;
++    var->transp.msb_right = 0;
++}
++
++
++    /*
++     *  Read a single color register and split it into
++     *  colors/transparent. Return != 0 for invalid regno.
++     */
++
++static int mq200fb_getcolreg(u_int regno, u_int *red, u_int *green, 
++                           u_int *blue, u_int *transp, struct fb_info *info)
++{
++    struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;
++
++    /*PDEBUG("mq200fb: mq200fb_getcolreg.\n");*/
++
++    if (regno > 255)
++      return 1;
++    *red = (p->palette[regno].red<<8) | p->palette[regno].red;
++    *green = (p->palette[regno].green<<8) | p->palette[regno].green;
++    *blue = (p->palette[regno].blue<<8) | p->palette[regno].blue;
++    *transp = 0;
++    return 0;
++}
++
++
++    /*
++     *  Set a single color register. The values supplied are already
++     *  rounded down to the hardware's capabilities (according to the
++     *  entries in the var structure). Return != 0 for invalid regno.
++     */
++
++static int mq200fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                         u_int transp, struct fb_info *info)
++{
++    struct fb_info_mq200 *p = (struct fb_info_mq200 *) info;
++    unsigned long color;
++
++    if (regno > 255)
++      return 1;
++
++    /*PDEBUG("In mq200fb_setcolreg, regno = %d, 0x%0x\n", regno, red);*/
++
++    /*PDEBUG("mq200fb: mq200fb_setcolreg.\n");*/
++
++  switch (p->var.bits_per_pixel) {
++#ifdef CONFIG_FBCON_CFB16
++    case 16:
++       if(regno < CMAPSIZE)
++#ifdef CONFIG_PREP
++          fbcon_cmap.cfb16[regno] =
++                    ((red & 0xf800) >> 9) |
++                    ((green & 0xf800) >> 14) |
++                    ((green & 0xf800) << 2) | ((blue & 0xf800) >> 3);
++#else
++           fbcon_cmap.cfb16[regno] =
++                    ((red & 0xf800) >> 0) |
++                    ((green & 0xf800) >> 5) | ((blue & 0xf800) >> 11);
++
++#endif /* CONFIG_PREP */
++       break;
++#endif
++#ifdef CONFIG_FBCON_CFB24
++    case 24:
++       if (regno < CMAPSIZE)
++#ifdef CONFIG_PREP
++           fbcon_cmap.cfb24[regno] =
++                    ((red & 0xff00)) |
++                    ((green & 0xff00) << 8) | ((blue & 0xff00) << 16);
++#else
++           fbcon_cmap.cfb24[regno] =
++                    ((red & 0xff00) << 8) |
++                    ((green & 0xff00)) | ((blue & 0xff00) >> 8);
++#endif
++       break;
++#endif
++#ifdef CONFIG_FBCON_CFB32
++    case 32:
++        if(regno < CMAPSIZE)
++#ifdef CONFIG_PREP
++           fbcon_cmap.cfb32[regno] =
++                    ((red & 0xff00)) |
++                    ((green & 0xff00) << 8) | ((blue & 0xff00) << 16);
++#else
++           fbcon_cmap.cfb32[regno] =
++                    ((red & 0xff00) >> 8) |
++                    ((green & 0xff00)) | ((blue & 0xff00) << 8);
++#endif
++
++       break;
++#endif
++    default: 
++       break;
++  }
++
++  red &= 0xFF;
++  green &= 0xFF;
++  blue &= 0xFF;
++
++  p->palette[regno].red = red;
++  p->palette[regno].green = green;
++  p->palette[regno].blue = blue;
++
++  color = red | (green << 8) | (blue << 16);
++  setpal(regno, color, (void *)p->mqMmioAddrVirt);
++
++    return 0;
++}
++
++
++static void do_install_cmap(int con, struct fb_info *info)
++{
++    if (con != currcon)
++      return;
++
++     PDEBUG("mq200fb: do_install_cmap.\n");
++
++    if (fb_display[con].cmap.len)
++      fb_set_cmap(&fb_display[con].cmap, 1, mq200fb_setcolreg, info);
++    else {
++        int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
++      fb_set_cmap(fb_default_cmap(size), 1, mq200fb_setcolreg, info);
++    }
++}
++
++
++#ifdef MODULE
++int init_module(void)
++{
++      return mq200fb_init();
++}
++
++void cleanup_module(void)
++{
++      unregister_framebuffer(&(fb_info_mq200.fb_info));
++      iounmap((void *)(fb_info_mq200.mqMmioAddrVirt));
++      iounmap((void *)(fb_info_mq200.mqFbAddrVirt));
++}
++
++#endif /* MODULE */
++
++/* The following is copied from mq2hw.c for initialization of MQ200 */
++
++long idmqchip(void *pMQMMIO)
++{
++      unsigned long id;
++      
++      id = READ32(PCI_VENDOR_DEVICE);
++      return (id);
++}
++
++/* Setmode for MediaQ chip
++ *
++ */
++void setmqmode(PDISPLAY_CONFIG pDC, void *pMQMMIO)
++{
++      volatile unsigned long regdata, pmmisc;
++      int x=0, y=0, freq=0, paneltype; /* i is used as loop counter */
++      unsigned long screensize, gc_startaddr;
++
++      printk("mq200fb: setmqmode - before reset\n");
++      regdata = SW_CHIP_RESET;
++      REG32(DC_0, regdata);
++      MQ_DELAY(10);
++      /* use 50MHz for LCD only and 83MHz if CRT is on */
++      if (pDC->flag & CRT_ON)
++              regdata = PLL1_83MHZ;
++      else
++              regdata = PLL1_50MHZ;
++      REG32(DC_0, regdata);
++        MQ_DELAY(30);
++
++      /* Enter D0 state from reset D3 state */
++
++      REG32(PCI_PM_CNTL_STATUS, ENTER_D0); 
++      MQ_DELAY(30);
++
++        while(1)
++        {
++          if((READ32(PCI_PM_CNTL_STATUS) & POWER_STATE_MASK) == 0x0) 
++              break;
++        }
++
++      /* In stable D3 state here ... */
++      /* 
++       * Set up PMU misc registers 
++       * - also PMCLK_2048CYCLE and FP_PMCLK_128K if SA1110
++       */
++      if ((READ32(DC_1) & BUS_MODE_MASK) == BUS_MODE_SA1110)
++              regdata = GE_ENABLE|GE_BY_PLL1|PMCLK_2048CYCLE|FP_PMCLK_512;
++      else
++              /* the rest of CPUs */
++              regdata = GE_ENABLE|GE_BY_PLL1;
++      REG32(PM_MISC, regdata);
++
++      REG32(D1_STATE, DEF_D1);
++      REG32(D2_STATE, DEF_D2);
++
++      /* To initialize MIU block ... */
++      REG32(MIU_CONTROL1, DRAM_RESET_DISABLE);
++      MQ_DELAY(5);
++
++      REG32(MIU_CONTROL1, 0x00);
++      MQ_DELAY(5);
++
++      if (pDC->flag & CRT_ON)
++              regdata = DEF_MIU2_83MHZ;
++      else
++              regdata = DEF_MIU2_50MHZ;
++      REG32(MIU_CONTROL2, regdata);
++      REG32(MIU_CONTROL3, DEF_MIU3);
++      /* MIU REG 5 MUST BE PROGRAMMED BEFORE MIU REG 4 */
++      REG32(MIU_CONTROL5, DEF_MIU5);
++      REG32(MIU_CONTROL4, DEF_MIU4);
++      MQ_DELAY(5);
++
++      REG32(MIU_CONTROL1, MIU_ENABLE | MIU_RESET_DISABLE);
++      MQ_DELAY(5);
++      
++      /* Here, MIU is supposed to ready to serve ... */
++
++      gc_startaddr = 0;
++      /* Last 1KB is reserved for hardware cursor */
++      if (mqflag & ENA_HW_CURSOR) 
++      {
++              printk("mq200fb: enabling hardware cursor\n");
++              setup_cursor(pMQMMIO);
++              enable_cursor(pMQMMIO);
++              
++      }
++      /* Set up GE Base Address */
++      REG32(BASE_ADDRESS, gc_startaddr);
++      /* Set up flat panel parameters
++       *
++       */
++      paneltype = pDC->flag & PANEL_TYPE_MASK;
++      if (paneltype)
++      {
++              /* Panel is used as a display in the system */
++              setupfp(paneltype, pMQMMIO);
++
++              /* Set up DSTN half frame buffer */
++              screensize = pDC->x * pDC->y * pDC->bpp / 8 + gc_startaddr;
++              setuphfbuffer(paneltype, screensize, pMQMMIO);
++              
++              /* Get flat panel frequency */
++              freq = fpControlData[paneltype].freq;
++      }
++      
++      /* Based on display configuration, proper GC is set up .. */
++      if (pDC->flag & LARGE_DESKTOP)
++      {
++              switch (pDC->flag & LCDCRT_POS_MASK)
++              {
++                      case HORI_CRT_LCD:
++                      case HORI_LCD_CRT:
++                              x = pDC->x / 2;
++                              y = pDC->y;
++                              break;
++                              
++                      case VERT_CRT_LCD:
++                      case VERT_LCD_CRT:
++                              x = pDC->x;
++                              y = pDC->y / 2;
++                              break;
++              }
++      }
++      else
++      {
++              /* SAME_IMAGE and simultaneous LCD and/or CRT */
++              x = pDC->x;
++              y = pDC->y;
++      }
++
++      /* Set up GC memory configuration */
++      setupgcmem(pDC, gc_startaddr, pMQMMIO);
++      
++      pmmisc = READ32(PM_MISC);
++      
++      /* Set up 2 GCs */
++      if (pDC->flag & USE_2GCs)
++      {
++              /* Set up GC1 for CRT */
++              setupgc(IS_GC1, x, y, pDC->bpp, pDC->refresh, pMQMMIO);
++
++              /* Set up GC2 for flat panel */
++              setupgc(IS_GC2, x, y, pDC->bpp, freq, pMQMMIO);
++
++              /* PLL2 and PLL3 are both used ... */
++              /* to save a little bit power, can shut down PLL3 if both LCD
++                      and CRT are the same frequency...
++              */
++              pmmisc |= (PLL2_ENABLE | PLL3_ENABLE);
++              REG32(PM_MISC, pmmisc);
++              
++              /* Enable panel and CRT accordingly */
++              if (pDC->flag & LCD_ON)
++                      onoffdisplay(ENABLE_LCD_GC2, pMQMMIO);
++                      
++              if (pDC->flag & CRT_ON)
++                      onoffdisplay(ENABLE_CRT_GC1, pMQMMIO);
++      }
++      else
++      {
++              /* Simultaneous mode - set up GC1 only */
++              if (paneltype)
++                      setupgc(IS_GC1, x, y, pDC->bpp, freq, pMQMMIO);
++              else
++                      setupgc(IS_GC1, x, y, pDC->bpp, pDC->refresh, pMQMMIO);
++
++              /* Use PLL2 */
++              pmmisc |= PLL2_ENABLE;
++              REG32(PM_MISC, pmmisc);
++
++              /* Enable panel and CRT accordingly */
++              if (pDC->flag & LCD_ON)
++                      onoffdisplay(ENABLE_LCD_GC1, pMQMMIO);
++                      
++              if (pDC->flag & CRT_ON)
++                      onoffdisplay(ENABLE_CRT_GC1, pMQMMIO);
++      }
++}
++
++/* Set up flat panel register depending on panel type
++ *
++ */
++void setupfp(int panel, void *pMQMMIO)
++{
++      PFPDATA_CONTROL pFP;
++      int frcaddr, frcidx, i;
++
++      /* Locate panel data pointer */
++      pFP = &fpControlData[panel];
++      printk("FP_PIN_CONTROL set to %x\n", (u_int)pFP->fpPinControl);
++      REG32(FP_CONTROL, pFP->fpControl);
++      REG32(FP_PIN_CONTROL, pFP->fpPinControl);
++      REG32(STN_CONTROL, pFP->stnControl);
++      REG32(FP_GPO_CONTROL, DEF_GPO_CONTROL);
++      REG32(FP_GPIO_CONTROL, DEF_GPIO_CONTROL);
++      REG32(PWM_CONTROL, DEF_PWM_CONTROL);
++
++      /* Program FRC registers for STN panel (DSTN and SSTN) */
++      frcidx = 0; /* DSTN */
++      if ( (pFP->fpControl & FP_TYPE_MASK) != FP_TYPE_TFT )
++      {
++              if ((pFP->fpControl & FP_TYPE_MASK) == FP_TYPE_SSTN)
++                      frcidx++; /* SSTN index */
++                      
++              for ( i = frcaddr = 0; i < FRC_PATTERN_CNT; i++,frcaddr+=4 )
++                      REG32((FRC_PATTERN + frcaddr),
++                                              FRCControlData[frcidx].frcPattern[i]);
++                                              
++              for ( i = frcaddr = 0; i < FRC_WEIGHT_CNT; i++,frcaddr+=4 )
++                      REG32((FRC_WEIGHT + frcaddr), FRCControlData[frcidx].frcWeight[i]);
++      }
++      
++      /* Set up flat panel GPO and GPIO register from default */
++      REG32(FP_GPO_CONTROL, DEF_GPO_CONTROL);
++      REG32(FP_GPIO_CONTROL, DEF_GPIO_CONTROL);
++
++      return;
++}
++
++/* Set up DSTN half frame buffer register depending on panel type
++ *
++ * panel : panel type
++ * sizeused : used (occupied) area of frame buffer
++ *
++ */
++void setuphfbuffer(int panel, unsigned long sizeused, void *pMQMMIO)
++{
++      PFPDATA_CONTROL pFP;
++      unsigned long dstnfbsize, dstnstart, dstnend;
++
++      /* Locate panel data pointer */
++      pFP = &fpControlData[panel];
++      
++      /* Figure out half frame buffer for DSTN panel */
++      if ( (pFP->fpControl & FP_TYPE_MASK) == FP_TYPE_DSTN )
++      {
++              /* Color D-STN memory requirement - no need to *3 for mono dstn panel */
++              if (pFP->fpControl & FP_MONO) 
++                      dstnfbsize = pFP->x;
++              else
++                      dstnfbsize = pFP->x * 3;
++              dstnfbsize = (((dstnfbsize + 127) >> 7) * pFP->y) << 3;
++
++              /* make it suitable for register bits definition */
++              dstnstart = (sizeused + 127) >> 7;
++              dstnend = (sizeused + dstnfbsize + 15) >> 4;
++              REG32(DSTN_FB_CONTROL, (dstnstart | ((dstnend - 1) << 16)));
++      }
++      return;
++}
++
++/* Set up graphics controller (GC1 or GC2) timing registers and PLLx
++ *
++ * gc: GC1 or GC2
++ * x : horizontal viewport size
++ * y : vertical viewport size
++ * refresh : refresh rate (mainly VESA-supported mode)
++ *
++ */
++void setupgc(int gc, int x, int y, int bpp, int refresh, void *pMQMMIO)
++{
++      PDISPLAY_TIMING pDT;
++      unsigned long gccontrol;
++
++      /* Locate GC timing parameters first */
++      pDT = getgcparam(x, y, refresh);
++
++      /* error checking for pDT here */
++
++      gccontrol = getbppbits(bpp) |
++                              FDx_1 | (1L << 24) |
++                              IM_ENABLE;
++
++      if (gc == IS_GC1)
++      {
++              /* Set up GC window as display */
++              REG32(HW1_CONTROL, ((x - 1) << 16) | (1 << 28));
++              REG32(VW1_CONTROL, ((y - 1) << 16));
++              
++              REG32(HD1_CONTROL, pDT->hd);
++              REG32(VD1_CONTROL, pDT->vd);
++              REG32(HS1_CONTROL, pDT->hs);
++              REG32(VS1_CONTROL, pDT->vs);
++              REG32(VS1_CONTROL, pDT->vs);
++              REG32(GC1_CRT_CONTROL, pDT->crtc);
++
++              /* Program PLL2 for GC1 */
++              REG32(PLL2_CONTROL, pDT->pll);
++
++              /* GC1 control register */
++              gccontrol |= GxRCLK_PLL2;
++              REG32(GC1_CONTROL, gccontrol);
++      }
++      else
++      if (gc == IS_GC2)
++      {
++              /* Set up GC window as display */
++              REG32(HW2_CONTROL, ((x - 1) << 16));
++              REG32(VW2_CONTROL, ((y - 1) << 16));
++              
++              REG32(HD2_CONTROL, pDT->hd);
++              REG32(VD2_CONTROL, pDT->vd);
++              REG32(HS2_CONTROL, pDT->hs);
++              REG32(VS2_CONTROL, pDT->vs);
++              REG32(VS2_CONTROL, pDT->vs);
++              REG32(GC1_CRT_CONTROL, pDT->crtc);
++
++              /* Program PLL3 for GC2 */
++              REG32(PLL3_CONTROL, pDT->pll);
++
++              /* GC2 control register */
++              gccontrol |= GxRCLK_PLL3;
++              REG32(GC2_CONTROL, gccontrol);
++      }
++      return;
++}
++
++/* Set up graphics controller (GC1 or GC2) memory configuration (stride and
++ * starting address etc.)
++ *
++ * pDC : pointer to active DIPSLAY_CONFIG structure
++ * startaddr : starting address - 0 if starting from very beginning
++ *
++ * - use GC1 for Simultaneous mode (1 GC)
++ * - use GC1 for CRT and GC2 for LCD at QView mode
++ *
++ */
++void setupgcmem(PDISPLAY_CONFIG pDC, unsigned long startaddr, void *pMQMMIO)
++{
++      unsigned long stride=0, start1=0, start2=0;
++
++      if (pDC->flag & LARGE_DESKTOP)
++      {
++              /* 4 possible layouts */
++              switch (pDC->flag & LCDCRT_POS_MASK)
++              {
++                      case HORI_CRT_LCD:
++                              stride = (pDC->x / 2) * pDC->bpp / 8;
++                              start1 = startaddr;
++                              start2 = startaddr + stride;
++                              break;
++                              
++                      case HORI_LCD_CRT:
++                              stride = (pDC->x / 2) * pDC->bpp / 8;
++                              start1 = startaddr + stride;
++                              start2 = startaddr;
++                              break;
++                              
++                      case VERT_CRT_LCD:
++                              stride = pDC->x * pDC->bpp / 8;
++                              start1 = startaddr;
++                              start2 = startaddr + stride * pDC->y / 2;
++                              break;
++                              
++                      case VERT_LCD_CRT:
++                              stride = pDC->x * pDC->bpp / 8;
++                              start1 = startaddr + stride * pDC->y / 2;
++                              start2 = startaddr;
++                              break;
++              }
++
++              /* Program to the chip */
++              REG32(IW1_STRIDE, stride);
++              REG32(IW2_STRIDE, stride);
++              
++              REG32(IW1_START_ADDR, start1);
++              REG32(IW2_START_ADDR, start2);
++      }
++      else
++      {
++              /* QView Same Image and Simultaneous LCD and/or CRT
++               *
++               * - set up 2 GCs in any cases ...
++               * - 2 addidtional registers write vs. code size
++               *
++               */
++
++              /* Calculate stride */
++              stride = pDC->x * pDC->bpp / 8;
++
++              REG32(IW1_STRIDE, stride);
++              REG32(IW2_STRIDE, stride);
++              
++              REG32(IW1_START_ADDR, startaddr);
++              REG32(IW2_START_ADDR, startaddr);
++      }
++      return;
++}
++
++/* Program palette entry
++ *
++ */
++void setpal(int index, unsigned long color, void *pMQMMIO)
++{
++    /*PDEBUG("mq200fb: setpal. %d %d\n", index, color);*/
++
++    REG32_PAL(index, color);
++}
++
++/* Vertical blank time is in progress ..
++ *
++ */
++void invblank(void *pMQMMIO)
++{
++      unsigned long *intstat = (unsigned long *)(pMQMMIO+INT_STATUS_REG);
++
++      /* Make sure int occurs first */
++      while ( !(*intstat & ST_GC1_VDE_F) );
++
++      /* Reset GC1 VDE F status bit - write 1 to clear the status */
++      REG32(INT_STATUS_REG,ST_GC1_VDE_F);
++
++      /* Wait for next VDE falling edge for DAC access */
++      while ( !(*intstat & ST_GC1_VDE_F) );
++
++      /* Here MQ200 should be in V blank period ... */
++      return;
++}
++
++/* Retrive graphics controller parameters from supported table
++ *
++ */
++PDISPLAY_TIMING getgcparam(int x, int y, int refresh)
++{
++      int i;
++      
++      for (i=0; i < MAX_MQMODE; i++)
++      {
++              if ( TimingParam[i].x == x
++                      && TimingParam[i].y == y
++                      && TimingParam[i].refresh == refresh )
++                      return ( (PDISPLAY_TIMING)&TimingParam[i] );
++      }
++      return (NULL);          /* not existed */
++}
++
++/* Return color depth setting for GC
++ *
++ */
++unsigned long getbppbits(int bpp)
++{
++      unsigned long bppbits = 0;
++
++      switch(bpp)
++      {
++              case  8UL: bppbits = GC_8BPP;   break;
++              case 16UL: bppbits = GC_16BPP_BP;       break;
++              case 24UL: bppbits = GC_24BPP_BP;       break;
++              case 32UL: bppbits = GC_32BPP_ARGB_BP;  break;
++              case  4UL: bppbits = GC_4BPP;   break;
++              case  2UL: bppbits = GC_2BPP;   break;
++              case  1UL: bppbits = GC_1BPP;   break;
++      }
++      return (bppbits);
++}
++
++/* Turn on/off LCD or CRT driven by either GC1 or GC2
++ *
++ */
++void onoffdisplay(int display_flag, void *pMQMMIO)
++{
++      unsigned long fpcontrol, gccontrol, crtcontrol;
++
++      switch (display_flag)
++      {
++              case ENABLE_LCD_GC1:
++                      /* Obtain current setting */
++                      fpcontrol = READ32(FP_CONTROL) & FPI_BY_GCxMASK;
++                      gccontrol = READ32(GC1_CONTROL);
++
++                      /* Turn on GC1 first if remain disabled */
++                      if (!(gccontrol & GC_ENABLE))
++                              REG32(GC1_CONTROL, gccontrol | GC_ENABLE);
++
++                      /* Flat panel controlled by GC1 */
++                      REG32(FP_CONTROL, fpcontrol | FPI_BY_GC1);
++
++#if defined(CONFIG_SA1100_GDS2200) || defined(CONFIG_SA1100_SIMPAD)
++                      mq200_backlight(pMQMMIO, 1);
++#endif
++
++                      break;
++                      
++              case ENABLE_LCD_GC2:
++                      /* Obtain current setting */
++                      fpcontrol = READ32(FP_CONTROL) & FPI_BY_GCxMASK;
++                      gccontrol = READ32(GC2_CONTROL);
++
++                      /* Turn on GC1 first if remain disabled */
++                      if (!(gccontrol & GC_ENABLE))
++                              REG32(GC2_CONTROL, gccontrol | GC_ENABLE);
++
++                      /* Flat panel controlled by GC1 */
++                      REG32(FP_CONTROL, fpcontrol | FPI_BY_GC2);
++                      break;
++                      
++              case DISABLE_LCD_GC1:
++                      /* Disable flat panel first */
++                      fpcontrol = READ32(FP_CONTROL) & FPI_BY_GCxMASK;
++                      REG32(FP_CONTROL, fpcontrol);
++
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & (~CRT_BY_GCxMASK);
++#if defined(CONFIG_SA1100_GDS2200) || defined(CONFIG_SA1100_SIMPAD)
++                      mq200_backlight(pMQMMIO, 0);
++#endif
++
++                      /* Disable GC1 if not used for CRT */
++                      if (!(crtcontrol == CRT_BY_GC1))
++                      {
++                              gccontrol = READ32(GC1_CONTROL);
++                              REG32(GC1_CONTROL, gccontrol & GC_DISABLE);
++                      }
++                      break;
++
++              case DISABLE_LCD_GC2:
++                      /* Disable flat panel first */
++                      fpcontrol = READ32(FP_CONTROL) & FPI_BY_GCxMASK;
++                      REG32(FP_CONTROL, fpcontrol);
++
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & (~CRT_BY_GCxMASK);
++
++                      /* Disable GC2 if not used for CRT */
++                      if (!(crtcontrol == CRT_BY_GC2))
++                      {
++                              gccontrol = READ32(GC2_CONTROL);
++                              REG32(GC2_CONTROL, gccontrol & GC_DISABLE);
++                      }
++                      break;
++
++              case ENABLE_CRT_GC1:
++                      /* Enable GC1 if not yet enabled */
++                      gccontrol = READ32(GC1_CONTROL);
++                      if (!(gccontrol & GC_ENABLE))
++                              REG32(GC1_CONTROL, gccontrol | GC_ENABLE);
++
++                      /* Enable CRT by GC1 */
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & CRT_BY_GCxMASK;
++                      REG32(GC1_CRT_CONTROL, crtcontrol | CRT_BY_GC1);
++                      break;
++
++              case ENABLE_CRT_GC2:
++                      /* Enable GC2 if not yet enabled */
++                      gccontrol = READ32(GC2_CONTROL);
++                      if (!(gccontrol & GC_ENABLE))
++                              REG32(GC2_CONTROL, gccontrol | GC_ENABLE);
++
++                      /* Enable CRT by GC2 */
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & CRT_BY_GCxMASK;
++                      REG32(GC1_CRT_CONTROL, crtcontrol | CRT_BY_GC2);
++                      break;
++
++              case DISABLE_CRT_GC1:
++                      /* Disable CRT first */
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & CRT_BY_GCxMASK;
++                      REG32(GC1_CRT_CONTROL, crtcontrol);
++                      
++                      fpcontrol = READ32(FP_CONTROL) & (~FPI_BY_GCxMASK);
++
++                      /* Disable GC1 if not used for CRT */
++                      if (!(crtcontrol == CRT_BY_GC1))
++                      {
++                              gccontrol = READ32(GC1_CONTROL);
++                              REG32(GC1_CONTROL, gccontrol & GC_DISABLE);
++                      }
++                      break;
++
++              case DISABLE_CRT_GC2:
++                      /* Disable CRT first */
++                      crtcontrol = READ32(GC1_CRT_CONTROL) & CRT_BY_GCxMASK;
++                      REG32(GC1_CRT_CONTROL, crtcontrol);
++                      
++                      fpcontrol = READ32(FP_CONTROL) & (~FPI_BY_GCxMASK);
++
++                      /* Disable GC2 if not used for CRT */
++                      if (!(crtcontrol == CRT_BY_GC2))
++                      {
++                              gccontrol = READ32(GC2_CONTROL);
++                              REG32(GC2_CONTROL, gccontrol & GC_DISABLE);
++                      }
++                      break;
++      }
++      return;
++}
++
++/* Setup hardware cursor data area start address in the frame buffer
++ *
++ */
++void setup_cursor(void *pMQMMIO)
++{
++      REG32(HW_CURSOR1_FGCLR, CURSOR_FGCLR);
++      REG32(HW_CURSOR2_FGCLR, CURSOR_FGCLR);
++      REG32(HW_CURSOR1_BGCLR, CURSOR_BGCLR);
++      REG32(HW_CURSOR2_BGCLR, CURSOR_BGCLR);
++      REG32(HW_CURSOR1_ADDR, 0x000007ff);
++      REG32(HW_CURSOR2_ADDR, 0x000007ff);
++}
++
++/* Move cursor position and adjust hot spot offset
++ *
++ */
++void move_cursor(unsigned long pos, unsigned long addr, void *pMQMMIO)
++{
++      REG32(HW_CURSOR1_POS, pos);
++      REG32(HW_CURSOR2_POS, pos);
++}
++
++/* Enable hardware cursor
++ *
++ */
++void enable_cursor(void *pMQMMIO)
++{
++      u32 temp;
++      
++      temp = READ32(GC1_CONTROL) | HC_ENABLE;
++      REG32(GC1_CONTROL, temp);
++      if (mqflag & USE_2GCs)
++      {
++              temp = READ32(GC2_CONTROL) | HC_ENABLE;
++              REG32(GC2_CONTROL, temp);
++      }
++}
++
++/* Disable hardware cursor
++ *
++ */
++void disable_cursor(void *pMQMMIO)
++{
++      u32 temp;
++      
++      temp = READ32(GC1_CONTROL) & HC_DISABLE;
++      REG32(GC1_CONTROL, temp);
++      if (mqflag & USE_2GCs)
++      {
++              temp = READ32(GC2_CONTROL) & HC_DISABLE;
++              REG32(GC2_CONTROL, temp);
++      }
++}
++/* The above is copied from mq2hw.c for initialization of MQ200 */
++
++void turnoffMQ200(void * pMQMMIO)
++{
++    volatile u32 temp;
++    
++    temp = READ32(FP_CONTROL);
++    temp &=0xfffffffc;
++    REG32(FP_CONTROL, temp);
++    udelay(5000);
++    temp =READ32(FP_CONTROL) & 0x3;
++    if(temp != 0)
++        PDEBUG("FP_CONTROL is not cleared properly");
++    else
++        PDEBUG("FP_CONTROL is cleared properly");
++
++    temp = READ32(FP_PIN_CONTROL);
++    temp |= 0x1;
++    REG32(FP_PIN_CONTROL, temp);
++    udelay(5000);
++
++    temp = READ32(GC1_CONTROL);
++    temp &=0xfffffffe;
++    REG32(GC1_CONTROL, temp);
++    udelay(5000);
++    temp = READ32(GC1_CONTROL) & 0x1;
++    if(temp != 0)
++        PDEBUG("GC1_CONTROL is not cleared properly");
++    else
++        PDEBUG("GC1_CONTROL is cleared properly");
++
++    temp = READ32(GC1_CRT_CONTROL);
++    temp &=0xfffffffe;
++    REG32(GC1_CRT_CONTROL, temp);
++    udelay(5000);
++
++    temp = READ32(GC2_CONTROL);
++    temp &=0xfffffffe;
++    REG32(GC2_CONTROL, temp);
++    udelay(5000);
++    temp = READ32(GC2_CONTROL) & 0x1;
++    if(temp != 0)
++        PDEBUG("GC2_CONTROL is not cleared properly");
++    else
++        PDEBUG("GC2_CONTROL is cleared properly");
++
++    return;
++}
++
++#ifdef CONFIG_PM
++/*
++ * Power management hook.  Note that we won't be called from IRQ context,
++ * unlike the blank functions above, so we may sleep.
++ */
++static int
++mq200fb_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
++{
++      struct fb_info_mq200 *p = (struct fb_info_mq200 *) pm_dev->data;
++      void * pMQMMIO = (void *) p->mqMmioAddrVirt;
++      
++      if (req == PM_SUSPEND){
++        
++          onoffdisplay(DISABLE_LCD_GC1, pMQMMIO);
++          REG32(PCI_PM_CNTL_STATUS, ENTER_D3); 
++          MQ_DELAY(30);
++      }
++      if( req == PM_RESUME){
++          setmqmode(&dc, pMQMMIO);
++          onoffdisplay(ENABLE_LCD_GC1, pMQMMIO);
++          REG32(PCI_PM_CNTL_STATUS, ENTER_D0); 
++          MQ_DELAY(30);
++      }
++      
++      return 0;
++}
++#endif
++
++#if defined(CONFIG_SA1100_GDS2200) || defined(CONFIG_SA1100_SIMPAD)
++
++static void mq200_backlight(void *pMQMMIO, int flag)
++{
++
++#ifdef CONFIG_SA1100_GDS2200
++    unsigned long gpiocontrol, data;
++    int i;
++    
++    gpiocontrol = READ32(FP_GPIO_CONTROL) & 0x3f;
++    data = (flag ? GPIO2_OUT_HIGH : 0);
++    for(i = 0 ; i < 128 ; i++) {
++      REG32(FP_GPIO_CONTROL, gpiocontrol | data);
++      MQ_DELAY(1);
++      REG32(FP_GPIO_CONTROL, gpiocontrol | GPIO1_OUT_HIGH | data);
++      MQ_DELAY(1);
++      REG32(FP_GPIO_CONTROL, gpiocontrol);
++      MQ_DELAY(1);
++    }
++    
++#endif /* CONFIG_SA1100_GDS2200 */
++
++#ifdef CONFIG_SA1100_SIMPAD
++
++    if(flag)
++      set_cs3_bit(DISPLAY_ON);
++    else
++      clear_cs3_bit(DISPLAY_ON);
++    
++#endif /* CONFIG_SA1100_SIMPAD */
++
++}
++#endif /* CONFIG_SA1100_GDS2200 || CONFIG_SA1100_SIMPAD */
++
++#ifdef CONFIG_SA1100_SIMPAD
++
++static void writeBrightness(void *pMQMMIO, int brightness)
++{
++    unsigned long dutyCycle, pwmcontrol;
++    int MAX_BRIGHT_REG = 0x000000fc; /* int 254 */
++    
++    if(brightness > MAX_BRIGHT_REG)
++      return;
++    else
++    {
++      /*
++       *Determine dutyCycle.
++       *Note: the lower the value, the brighter the display!
++       */
++      
++       dutyCycle = MAX_BRIGHT_REG - brightness;
++    
++      /*
++       *Configure PWM0 (source clock = oscillator clock, pwm always enabled,
++       *zero, clock pre-divider = 4) pwm frequency = 12.0kHz
++       */
++       pwmcontrol = READ32(PWM_CONTROL);
++       REG32(PWM_CONTROL, 0x00000044 | (pwmcontrol & 0xffffff00));
++   
++
++      /* Write to pwm duty cycle register.  */
++   
++       REG32(PWM_CONTROL, ((dutyCycle << 8) & 0x0000ff00) | 
++           (pwmcontrol & 0xffff00ff));
++    }
++}
++
++#endif /* CONFIG_SA1100_SIMPAD */
++
++static int proc_read_reg(struct file * file, char * buf,
++              size_t nbytes, loff_t *ppos)
++{
++      int i_ino = (file->f_dentry->d_inode)->i_ino;
++      char outputbuf[15];
++      int count;
++      int i;
++      mq200_reg_entry_t* current_reg=NULL;
++      if (*ppos>0) /* Assume reading completed in previous read*/
++              return 0;
++      for (i=0;i<NUM_OF_MQ200_REG_ENTRY;i++) {
++              if (mq200_regs[i].low_ino==i_ino) {
++                      current_reg = &mq200_regs[i];
++                      break;
++              }
++      }
++      if (current_reg==NULL)
++              return -EINVAL;
++
++      count = sprintf(outputbuf, "0x%08X\n",
++                      *((volatile *) mq200_p2v(current_reg->phyaddr)));
++      *ppos+=count;
++      if (count>nbytes)  /* Assume output can be read at one time */
++              return -EINVAL;
++      if (copy_to_user(buf, outputbuf, count))
++              return -EFAULT;
++      return count;
++}
++
++static ssize_t proc_write_reg(struct file * file, const char * buffer,
++              size_t count, loff_t *ppos)
++{
++      int i_ino = (file->f_dentry->d_inode)->i_ino;
++      mq200_reg_entry_t* current_reg=NULL;
++      int i;
++      unsigned long newRegValue;
++      char *endp;
++
++      for (i=0;i<NUM_OF_MQ200_REG_ENTRY;i++) {
++              if (mq200_regs[i].low_ino==i_ino) {
++                      current_reg = &mq200_regs[i];
++                      break;
++              }
++      }
++      if (current_reg==NULL)
++              return -EINVAL;
++
++      newRegValue = simple_strtoul(buffer,&endp,0);
++      *((volatile *) mq200_p2v(current_reg->phyaddr))=newRegValue;
++      return (count+endp-buffer);
++}
++
+--- linux-2.4.27/include/asm-arm/arch-sa1100/simpad.h~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/include/asm-arm/arch-sa1100/simpad.h
+@@ -28,6 +28,7 @@
+ #define GPIO_UART3_DCD        GPIO_GPIO18
+ #define GPIO_UART3_DSR        GPIO_GPIO17
++#define GPIO_POWER_BUTTON       GPIO_GPIO0
+ #define GPIO_UCB1300_IRQ      GPIO_GPIO (22)  /* UCB GPIO and touchscreen */
+ #define IRQ_UART1_CTS IRQ_GPIO15
+@@ -37,7 +38,12 @@
+ #define IRQ_UART3_DCD GPIO_GPIO18
+ #define IRQ_UART3_DSR GPIO_GPIO17
+-#define IRQ_GPIO_UCB1300_IRQ IRQ_GPIO22 
++#define IRQ_GPIO_UCB1300_IRQ IRQ_GPIO22
++#define IRQ_GPIO_POWER_BUTTON    IRQ_GPIO0
++
++/*---  SmartCard  ---*/
++#define GPIO_SMART_CARD         GPIO_GPIO10
++#define IRQ_GPIO_SMART_CARD     IRQ_GPIO10
+ /*---  PCMCIA  ---*/
+ #define GPIO_CF_CD              GPIO_GPIO24
+@@ -65,7 +71,7 @@
+ #define LED2_ON         0x1000 
+ #define IRDA_MODE       0x2000 // Fast/Slow IrDA mode
+ #define ENABLE_5V       0x4000 // Enable 5V circuit
+-#define RESET_SIMCARD   0x8000
++#define nRESET_SIMCARD  0x8000
+ #define RS232_ENABLE    0x0440
+ #define PCMCIAMASK      0x402f   
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-sa1100/simpad_pm.h
+@@ -0,0 +1,236 @@
++/*
++* Abstraction interface for microcontroller connection to rest of system
++*
++* Copyright 2003 Peter Pregler
++* Copyright 2000,1 Compaq Computer Corporation.
++*
++* Use consistent with the GNU GPL is permitted,
++* provided that this copyright notice is
++* preserved in its entirety in all copies and derived works.
++*
++* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++* FITNESS FOR ANY PARTICULAR PURPOSE.
++*
++* Author: Peter Pregler (based on work for ipaq by Andrew Christian)
++*
++*/
++
++#ifndef __SIMPAD_HAL_H
++#define __SIMPAD_HAL_H
++
++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status, 
++                                    unsigned char *battery_flag, unsigned char *battery_percentage, 
++                                    unsigned short *battery_life);
++
++
++struct simpad_battery {
++  unsigned char ac_status;    /* line connected yes/no */
++  unsigned char status;               /* battery loading yes/no */
++  unsigned char percentage;   /* percentage loaded */
++  unsigned short life;                /* life till empty */
++};
++
++extern int simpad_get_battery(struct simpad_battery *bstat);
++
++/* These should match the apm_bios.h definitions */
++#define SIMPAD_AC_STATUS_AC_OFFLINE      0x00
++#define SIMPAD_AC_STATUS_AC_ONLINE       0x01
++#define SIMPAD_AC_STATUS_AC_BACKUP       0x02   /* What does this mean? */
++#define SIMPAD_AC_STATUS_AC_UNKNOWN      0xff
++                                                                                                                                           
++/* These bitfields are rarely "or'd" together */
++#define SIMPAD_BATT_STATUS_HIGH          0x01
++#define SIMPAD_BATT_STATUS_LOW           0x02
++#define SIMPAD_BATT_STATUS_CRITICAL      0x04
++#define SIMPAD_BATT_STATUS_CHARGING      0x08
++#define SIMPAD_BATT_STATUS_CHARGE_MAIN   0x10
++#define SIMPAD_BATT_STATUS_DEAD          0x20   /* Battery will not charge */
++#define SIMPAD_BATT_NOT_INSTALLED        0x20   /* For expansion pack batteries */
++#define SIMPAD_BATT_STATUS_FULL          0x40   /* Battery fully charged (and connected to AC) */
++#define SIMPAD_BATT_STATUS_NOBATT        0x80
++#define SIMPAD_BATT_STATUS_UNKNOWN       0xff
++                                                                                                                                           
++#if FIXME
++#include <linux/simpad_ts.h>
++
++enum simpad_asset_type {
++      ASSET_TCHAR = 0,
++      ASSET_SHORT,
++      ASSET_LONG
++};
++
++#define TTYPE(_type)           (((unsigned int)_type) << 8)
++#define TCHAR(_len)            (TTYPE(ASSET_TCHAR) | (_len))
++#define TSHORT                 TTYPE(ASSET_SHORT)
++#define TLONG                  TTYPE(ASSET_LONG)
++#define ASSET(_type,_num)      ((((unsigned int)_type)<<16) | (_num))
++
++#define ASSET_HM_VERSION        ASSET( TCHAR(10), 0 )   /* 1.1, 1.2 */
++#define ASSET_SERIAL_NUMBER     ASSET( TCHAR(40), 1 )   /* Unique iPAQ serial number */
++#define ASSET_MODULE_ID         ASSET( TCHAR(20), 2 )   /* E.g., "iPAQ 3700" */    
++#define ASSET_PRODUCT_REVISION  ASSET( TCHAR(10), 3 )   /* 1.0, 2.0 */
++#define ASSET_PRODUCT_ID        ASSET( TSHORT,    4 )   /* 2 = Palm-sized computer */
++#define ASSET_FRAME_RATE        ASSET( TSHORT,    5 )
++#define ASSET_PAGE_MODE         ASSET( TSHORT,    6 )   /* 0 = Flash memory */
++#define ASSET_COUNTRY_ID        ASSET( TSHORT,    7 )   /* 0 = USA */
++#define ASSET_IS_COLOR_DISPLAY  ASSET( TSHORT,    8 )   /* Boolean, 1 = yes */
++#define ASSET_ROM_SIZE          ASSET( TSHORT,    9 )   /* 16, 32 */
++#define ASSET_RAM_SIZE          ASSET( TSHORT,   10 )   /* 32768 */
++#define ASSET_HORIZONTAL_PIXELS ASSET( TSHORT,   11 )   /* 240 */
++#define ASSET_VERTICAL_PIXELS   ASSET( TSHORT,   12 )   /* 320 */
++
++#define ASSET_TYPE(_asset)       (((_asset)&0xff000000)>>24)
++#define ASSET_TCHAR_LEN(_asset)  (((_asset)&0x00ff0000)>>16)
++#define ASSET_NUMBER(_asset)     ((_asset)&0x0000ffff)
++
++#define MAX_TCHAR_LEN 40
++
++struct simpad_asset {
++      unsigned int type;
++      union {
++              unsigned char  tchar[ MAX_TCHAR_LEN ];
++              unsigned short vshort;
++              unsigned long  vlong;
++      } a;
++};
++
++/********************************************************************
++ * Interface to the hardware-type specific functions
++ *
++ * get_version           Read the version number of the microcontroller on the option pack SPI bus
++ * spi_read              Reads from the serial EEPROM memory on the option pack SPI bus
++ * spi_write             Write to the serial EEPROM memory on the option pack SPI bus
++ * get_option_detect     Returns whether or not an option pack is present
++ *
++ * get_thermal_sensor    Return measured temperature of the unit, in units of 0.125 deg C
++ * set_notify_led        Turns on, off, or blinks the Green LED
++ * read_light_sensor     Returns the value of the front light sensor
++ * get_battery           Returns the current voltage and charging state of all batteries
++ * audio_clock           Sets the audio CODEC to run at a particular rate
++ * audio_power           Turns on/off audio CODEC (internally calls audio_clock)
++ * audio_mute            Mutes the audio CODEC
++ * asset_read            Extracts PocketPC-style asset information from persistent memory
++ * backlight_control     Adjusts the backlight level  (only on/off for 3100)
++ *
++ *
++ * iPAQ 3100 only
++ * ==============
++ * codec_control         Reset/mute/control level of 3100 audio codec
++ * contrast_control      Adjusts the contrast level   (only for 3100)
++ *
++ * iPAQ 3600, 3700 only
++ * ====================
++ * eeprom_read           Reads from the asset information on the eeprom of a 3600 (deprecated)
++ * eeprom_write          Writes to the asset information on the eeprom (deprecated)
++ *
++ * The interfaces to the EEPROM functions are maintained only because the simpad_ts driver has
++ * a deprecated ioctl call for them.  They are used internally by the "asset_read" function.
++ *
++ * iPAQ 3800, 3900 only
++ * ====================
++ * set_ebat              Tells enhanced PCMCIA sleeves that this iPAQ can handle 
++ *                       a wider voltage range (applies to 3800, 3900)
++ *
++ *********************************************************************/
++
++struct simpad_hal_ops {
++      /* Functions provided by the underlying hardware */
++      int (*get_version)( struct simpad_ts_version * );
++      int (*eeprom_read)( unsigned short address, unsigned char *data, unsigned short len );
++      int (*eeprom_write)( unsigned short address, unsigned char *data, unsigned short len );
++      int (*get_thermal_sensor)( unsigned short * );
++      int (*set_notify_led)( unsigned char mode, unsigned char duration, 
++                             unsigned char ontime, unsigned char offtime );
++      int (*read_light_sensor)( unsigned char *result );
++      int (*get_battery)( struct simpad_battery * );
++      int (*spi_read)( unsigned short address, unsigned char *data, unsigned short len );
++      int (*spi_write)( unsigned short address, unsigned char *data, unsigned short len );
++      int (*codec_control)( unsigned char, unsigned char );
++      int (*get_option_detect)( int *result );
++      int (*audio_clock)( long samplerate );
++      int (*audio_power)( long samplerate );
++      int (*audio_mute)( int mute );
++      int (*asset_read)( struct simpad_asset *asset );
++      int (*set_ebat)( void );
++
++      /* Functions indirectly provided by the underlying hardware */
++      int (*backlight_control)( enum flite_pwr power, unsigned char level );
++      int (*contrast_control)( unsigned char level );
++
++        /* for module use counting */ 
++        struct module *owner;
++};
++
++/* Used by the device-specific hardware module to register itself */
++extern int  simpad_hal_register_interface( struct simpad_hal_ops *ops );
++extern void simpad_hal_unregister_interface( struct simpad_hal_ops *ops );
++
++/* 
++ * Calls into HAL from the device-specific hardware module
++ * These run at interrupt time 
++ */
++extern void simpad_hal_keypress( unsigned char key );
++extern void simpad_hal_touchpanel( unsigned short x, unsigned short y, int down );
++extern void simpad_hal_option_detect( int present );
++
++/* Callbacks registered by device drivers */
++struct simpad_driver_ops {
++      void (*keypress)( unsigned char key );
++      void (*touchpanel)( unsigned short x, unsigned short y, int down );
++      void (*option_detect)( int present );
++};
++
++extern int  simpad_hal_register_driver( struct simpad_driver_ops * );
++extern void simpad_hal_unregister_driver( struct simpad_driver_ops * );
++
++
++/* Calls into HAL from device drivers and other kernel modules */
++extern void simpad_get_flite( struct simpad_ts_backlight *bl );
++extern void simpad_get_contrast( unsigned char *contrast );
++extern int  simpad_set_flite( enum flite_pwr pwr, unsigned char brightness );
++extern int  simpad_set_contrast( unsigned char contrast );
++extern int  simpad_toggle_frontlight( void );
++
++extern int simpad_apm_get_power_status(unsigned char *ac_line_status, unsigned char *battery_status, 
++                                    unsigned char *battery_flag, unsigned char *battery_percentage, 
++                                    unsigned short *battery_life);
++
++extern struct simpad_hal_ops *simpad_hal_ops;
++
++/* Do not use this macro in driver files - instead, use the inline functions defined below */
++#define CALL_HAL( f, args... ) \
++        { int __result = -EIO;                             \
++          if ( simpad_hal_ops && simpad_hal_ops->f ) {       \
++                __MOD_INC_USE_COUNT(simpad_hal_ops->owner); \
++                __result = simpad_hal_ops->f(args);         \
++                __MOD_DEC_USE_COUNT(simpad_hal_ops->owner); \
++          }                                                \
++          return __result; }
++
++#define HFUNC  static __inline__ int
++
++/* The eeprom_read/write address + len has a maximum value of 512.  Both must be even numbers */
++HFUNC simpad_eeprom_read( u16 addr, u8 *data, u16 len )  CALL_HAL(eeprom_read,addr,data,len)
++HFUNC simpad_eeprom_write( u16 addr, u8 *data, u16 len)  CALL_HAL(eeprom_write,addr,data,len)
++HFUNC simpad_spi_read( u8 addr, u8 *data, u16 len)    CALL_HAL(spi_read,addr,data,len)
++HFUNC simpad_spi_write( u8 addr, u8 *data, u16 len)   CALL_HAL(spi_write,addr,data,len)
++HFUNC simpad_get_version( struct simpad_ts_version *v )   CALL_HAL(get_version,v)
++HFUNC simpad_get_thermal_sensor( u16 *thermal )               CALL_HAL(get_thermal_sensor,thermal)
++HFUNC simpad_set_led( u8 mode, u8 dur, u8 ont, u8 offt ) CALL_HAL(set_notify_led, mode, dur, ont, offt)
++HFUNC simpad_get_light_sensor( u8 *result )           CALL_HAL(read_light_sensor,result)
++HFUNC simpad_get_battery( struct simpad_battery *bat )        CALL_HAL(get_battery,bat)
++HFUNC simpad_get_option_detect( int *result)             CALL_HAL(get_option_detect,result)
++HFUNC simpad_audio_clock( long samplerate )              CALL_HAL(audio_clock,samplerate)
++HFUNC simpad_audio_power( long samplerate )              CALL_HAL(audio_power,samplerate)
++HFUNC simpad_audio_mute( int mute )                      CALL_HAL(audio_mute,mute)
++HFUNC simpad_asset_read( struct simpad_asset *asset )     CALL_HAL(asset_read,asset)
++HFUNC simpad_set_ebat( void )                            CALL_HAL(set_ebat)
++
++/* Don't use these functions directly - rather, call {get,set}_{flite,contrast} */
++      /* Functions indirectly provided by the underlying hardware */
++HFUNC simpad_backlight_control( enum flite_pwr p, u8 v ) CALL_HAL(backlight_control,p,v)
++HFUNC simpad_contrast_control( u8 level )                CALL_HAL(contrast_control,level)
++
++#endif
++#endif
+--- linux-2.4.27/include/linux/apm_bios.h~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/include/linux/apm_bios.h
+@@ -216,4 +216,19 @@
+ #define APM_IOC_STANDBY               _IO('A', 1)
+ #define APM_IOC_SUSPEND               _IO('A', 2)
++#define APM_AC_OFFLINE 0
++#define APM_AC_ONLINE 1
++#define APM_AC_BACKUP 2
++#define APM_AC_UNKNOWN 0xFF
++                                                                                
++#define APM_BATTERY_STATUS_HIGH 0
++#define APM_BATTERY_STATUS_LOW  1
++#define APM_BATTERY_STATUS_CRITICAL 2
++#define APM_BATTERY_STATUS_CHARGING 3
++#define APM_BATTERY_STATUS_UNKNOWN 0xFF
++                                                                                
++#define APM_BATTERY_LIFE_UNKNOWN 0xFFFF
++#define APM_BATTERY_LIFE_MINUTES 0x8000
++#define APM_BATTERY_LIFE_VALUE_MASK 0x7FFF
++                                                                                
+ #endif        /* LINUX_APM_H */
+--- /dev/null
++++ linux-2.4.27/include/linux/switches.h
+@@ -0,0 +1,74 @@
++/*
++ *  linux/include/linux/switches.h
++ *
++ *  Copyright (C) 2000 John Dorsey
++ *
++ *  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.
++ *
++ *  23 October 2000 - created.
++ */
++
++#if !defined(_LINUX_SWITCHES_H)
++#define _LINUX_SWITCHES_H
++
++#define SWITCHES_MASK_SIZE  (128)
++
++typedef unsigned long switches_bitfield;
++
++#define SWITCHES_BITS            (sizeof(switches_bitfield) * 8)
++#define SWITCHES_NUM_FIELDS      (SWITCHES_MASK_SIZE /  SWITCHES_BITS)
++#define SWITCHES_FIELD_SELECT(i) ((i) / SWITCHES_BITS)
++#define SWITCHES_FIELD_MASK(i)   ((switches_bitfield)(1 << (i) % \
++                                  SWITCHES_BITS))
++
++typedef struct switches_mask_t {
++      unsigned int count;
++      switches_bitfield events[SWITCHES_NUM_FIELDS];
++      switches_bitfield states[SWITCHES_NUM_FIELDS];
++} switches_mask_t;
++
++#define SWITCHES_ZERO(m) \
++do { \
++      unsigned int sz_i; \
++      (m)->count = 0; \
++      for(sz_i = 0; sz_i < SWITCHES_NUM_FIELDS; ++sz_i) \
++              (m)->events[sz_i] = (m)->states[sz_i] = 0; \
++} while (0)
++
++/* `s' is the state of the switch, either 0 or non-zero: */
++#define SWITCHES_SET(m, i, s) \
++do { \
++      ((m)->events[SWITCHES_FIELD_SELECT((i))] |= \
++         SWITCHES_FIELD_MASK((i))); \
++      if(s) \
++              ((m)->states[SWITCHES_FIELD_SELECT((i))] |= \
++                 SWITCHES_FIELD_MASK((i))); \
++      else \
++              ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \
++                 ~SWITCHES_FIELD_MASK((i))); \
++      ++((m)->count); \
++} while (0)
++
++/* Should only use to clear an event set by SWITCHES_SET(): */
++#define SWITCHES_CLEAR(m, i) \
++do { \
++      ((m)->events[SWITCHES_FIELD_SELECT((i))] &= \
++         ~SWITCHES_FIELD_MASK((i))); \
++      ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \
++         ~SWITCHES_FIELD_MASK((i))); \
++      --((m)->count); \
++}
++
++#define SWITCHES_COUNT(m) ((m)->count)
++
++/* Returns 0 or non-zero: */
++#define SWITCHES_EVENT(m, i) \
++((m)->events[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i)))
++
++/* Returns 0 or non-zero: */
++#define SWITCHES_STATE(m, i) \
++((m)->states[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i)))
++
++#endif  /* !defined(_LINUX_SWITCHES_H) */
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mq2ge.h
+@@ -0,0 +1,81 @@
++#ifndef _VIDEO_MQ200_MQ2GE_H
++#define _VIDEO_MQ200_MQ2GE_H
++
++
++/* Misc. GE Function Macro */
++#undef CHECK_SRCFIFO 
++#define RGB_RASTER_CHECK
++#define INCLUDE_GENERIC_CODE
++
++#ifdef CHECK_SRCFIFO
++#define geWAITSRCFIFO(cnt)                    geWaitSrcFIFO(cnt) 
++#define       geWAITCMDFIFO(cnt)                      geWaitCmdFIFO(cnt)
++#define geWAITNOTBUSY()                               geWaitNotBusy()
++#else
++#define geWAITSRCFIFO(cnt) 
++#define geWAITCMDFIFO(cnt) 
++#define geWAITNOTBUSY()
++#endif
++
++/* Additional UGL Raster Ops */
++#define UGL_RASTER_OP_NOP                     0x00000000L
++
++#define UGL_RASTER_OP_BLACKNESS                       0x00000001L
++#define UGL_RASTER_OP_BSRC_BLACK              0x00000002L
++#define UGL_RASTER_OP_BSRC_OCOPY              0x00000003L
++#define UGL_RASTER_OP_BSRC_XCOPY              0x00000004L
++
++#define UGL_RASTER_OP_WHITENESS                       0x00010001L
++#define UGL_RASTER_OP_WSRC_COPY                       0x00010002L
++#define UGL_RASTER_OP_WSRC_WHITE              0x00010003L
++#define UGL_RASTER_OP_WSRC_INVERT             0x00010004L
++
++#define UGL_RASTER_OP_SRC_COPY                        0x00020001L
++#define UGL_RASTER_OP_SRCDEST_AND             0x00020002L
++#define UGL_RASTER_OP_SRCDEST_OR              0x00020003L
++#define UGL_RASTER_OP_SRCDEST_XOR             0x00020004L
++
++#define UGL_RASTER_OP_DEST_COPY                       0x00030001L
++#define UGL_RASTER_OP_DESTSRC_AND             0x00030002L
++#define UGL_RASTER_OP_DESTSRC_OR              0x00030003L
++#define UGL_RASTER_OP_DESTSRC_XOR             0x00030004L
++
++/* MediaQ Raster Ops */
++#define MQ200_SOURCE_ROP                      0x01
++#define MQ200_PATTERN_ROP                     0x02
++#define MQ200_GE_NOP                          0x000000AAL
++#define MQ200_GE_BLACKNESS                    0x00000000L
++#define MQ200_GE_WHITENESS                    0x000000FFL
++#define MQ200_GE_SRC_INVERT                   0x00000033L
++#define MQ200_GE_SRC_COPY                     0x000000CCL
++#define MQ200_GE_SRCDEST_XOR          0x00000066L
++#define MQ200_GE_SRCDEST_AND          0x00000088L
++#define MQ200_GE_SRCDEST_OR                   0x000000EEL
++#define MQ200_GE_PATTERN_INVERT               0x0000000FL
++#define MQ200_GE_PATTERN_COPY         0x000000F0L
++#define MQ200_GE_PATDEST_XOR          0x0000005AL
++#define MQ200_GE_PATDEST_AND          0x000000A0L
++#define MQ200_GE_PATDEST_OR                   0x000000FAL
++/* MediaQ Raster Ops mapping table */
++#define UGL_NR_OPERAND                                        4
++#define UGL_NR_OPERATION                              5
++
++#define geREG_2( idx1, val1, idx2, val2 ) \
++        geREG( idx2, val2 ); \
++        geREG( idx1, val1 )
++#define geREG_3( idx1, val1, idx2, val2, idx3, val3 ) \
++        geREG_2( idx2, val2, idx3, val3 ); \
++        geREG( idx1, val1 )
++#define geREG_4( idx1, val1, idx2, val2, idx3, val3, idx4, val4 ) \
++        geREG_3( idx2, val2, idx3, val3, idx4, val4 ); \
++        geREG( idx1, val1 )
++#define geREG_5( idx1, val1, idx2, val2, idx3, val3, idx4, val4, idx5, val5 ) \
++        geREG_4( idx2, val2, idx3, val3, idx4, val4, idx5, val5 ); \
++        geREG( idx1, val1 )
++
++/* Declare MQ200 GE Utility Functions */
++void geWaitNotBusy(void);
++void geWaitCmdFIFO(u32 cnt);
++void geWaitSrcFIFO(u32 cnt);
++
++#endif /* _VIDEO_MQ200_MQ2GE_H */
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mq2hw.h
+@@ -0,0 +1,879 @@
++/***************************************************************************
++ MQ200HW.H
++
++ MQ200 chip definition file
++
++ Copyright (c) 2000 by MediaQ, Incorporated.
++ All Rights Reserved.
++ 
++***************************************************************************/
++#ifndef _VIDEO_MQ200_MQ2HW_H
++#define _VIDEO_MQ200_MQ2HW_H
++
++#define MQ200_VENDOR_ID               0x4D51
++#define MQ200_DEVICE_ID               0x0200
++#define MQ200_ID              0x02004D51
++#define PM_ID_CAP             0x06210001 /* Power management ID/capability */
++
++/* Revision ID */
++#define MQ200_REV_0X          0x00
++#define MQ200_REV_1A          0x01
++#define MQ200_REV_1B1C                0x11
++#define MQ200_REV_1D          0x10
++
++/* Some useful defines */
++#ifndef ULONG
++#define ULONG   unsigned long
++#endif
++#ifndef USHORT
++#define USHORT  unsigned short
++#endif
++#ifndef BYTE
++#define BYTE    unsigned char
++#endif
++
++/* To access MediaQ memory-mapped IO register (32bit in size) */
++#define REG32(addr,val)               (*(volatile ULONG *)((ULONG)pMQMMIO+addr)=(val))
++#define READ32(addr)          (*((volatile ULONG *)((ULONG)pMQMMIO+addr)))
++#define geREG(addr,val)               (*(volatile ULONG *)(mqMmioAddr+addr)=(val))
++#define geREAD(addr)          (*((volatile ULONG *)(mqMmioAddr+addr)))
++#define gcREG(addr,val)               geREG(addr,val)
++#define gcREAD(addr)          geREAD(addr)
++#define cpuREG(addr,val)      geREG(addr,val)
++#define cpuREAD(addr)         geREAD(addr)
++#define pmuREG(addr,val)      geREG(addr,val)
++#define pmuREAD(addr)         geREAD(addr)
++#define pciREG(addr,val)      geREG(addr,val)
++#define pciREAD(addr)         geREAD(addr)
++
++/* To access MediaQ DAC - index-based */
++#define REG32_PAL(idx,val)  (*(ULONG *)((ULONG)pMQMMIO+C1_BASE+idx*4)=(val))
++#define READ32_PAL(idx)           (*(ULONG *)((ULONG)pMQMMIO+C1_BASE+idx*4))
++
++/* MQ200 module offset */
++#define PM_BASE         (0)           /* Power Management + Clk Gen */
++#define CC_BASE               (0x2000)        /* CPU interface */
++#define MM_BASE               (0x4000)        /* Memory Controller (m1/m2) */
++#define VI_BASE               (0x6000)        /* Video-in controller */
++#define IN_BASE               (0x8000)        /* Interrupt controller */
++#define GC_BASE               (0xA000)        /* Graphics Controller 1/2 */
++#define GE_BASE               (0xC000)        /* Graphics engine */
++#define GE2_BASE      (0xC200)        /* Graphics engine (GE2) */
++#define FP_BASE               (0xE000)        /* Flat panel interface */
++#define C1_BASE               (0x10000)       /* Color palette 1 */
++#define C2_BASE               (0x12000)       /* Color palette 2 */
++#define DC_BASE               (0x14000)       /* Device Configuration Space */
++#define PC_BASE               (0x16000)       /* PCI Configuration Header */
++#define PSF_BASE      (0x18000)       /* Primary Source FIFO Space */
++#define SSF_BASE      (0x1A000)       /* Secondary Source FIFO Space */
++
++#define MQ200_MMIO_SIZE       (0x1C0000L)
++#define MQ200_FB_SIZE (0x200000L)     /* 2MB memory */
++#define GC_OFFSET     (0x80)
++
++/* Interrupt Controller */
++#define INT_CONTROL_REG               (IN_BASE + 0x00) /* Global interrupt ctrl reg */
++#define INT_MASK_REG          (IN_BASE + 0x04) /* Interrupt mask reg */
++#define INT_STATUS_REG          (IN_BASE + 0x08) /* Interrupt status reg */
++#define INT_RAW_STATUS_REG      (IN_BASE + 0x0C) /* Interrupt pin raw */
++                                                 /*   status reg*/
++
++/* INT_CONTROL_REG - Global Interrupt Control Register */
++#define INT_ENABLE            0x00000001      /* Interrupt to CPU enabled */
++#define INT_PORLARITY_HIGH    0x00000002      /* Interrupt is active high */
++#define INT_GPIO1_0To1                0x00000004      /* Interrupt as xition 0 to 1 */
++#define INT_GPIO2_0To1                0x00000008      /* Interrupt as xition 0 to 1 */
++#define INT_GPIO3_0To1                0x00000010      /* Interrupt as xtion 0 to 1 */
++
++/* INT_MASK_REG -- Interrupt Mask Register */
++#define UM_GC1_VSE_R          0x00000001      /* GC1 VSE - Rising edge */
++#define UM_GC1_VSE_F          0x00000002      /* GC1 VSE - Falling edge */
++#define UM_GC1_VDE_R          0x00000004      /* GC1 VDE - Rising edge */
++#define UM_GC1_VDE_F          0x00000008      /* GC1 VDE - Falling edge */
++#define UM_GC2_VSE_R          0x00000010      /* GC2 VSE - Rising edge */
++#define UM_GC2_VSE_F          0x00000020      /* GC2 VSE - Falling edge */
++#define UM_GC2_VDE_R          0x00000040      /* GC2 VDE - Rising edge */
++#define UM_GC2_VDE_F          0x00000080      /* GC2 VDE - Falling edge */
++#define UM_CFIFO_HALF_EMPTY   0x00000100      /* Command fifo half empty */
++#define UM_CFIFO_EMPTY                0x00000200      /* Command fifo empty */
++#define UM_SFIFO_HALF_EMPTY   0x00000400      /* Source fifo half empty */
++#define UM_SFIFO_EMPTY                0x00000800      /* Source fifo empty */
++#define UM_GE_IDLE            0x00001000      /* GE is idle */
++#define UM_GPIO_1             0x00002000      /* GPIO pin 1 */
++#define UM_GPIO_2             0x00004000      /* GPIO pin 2 */
++#define UM_GPIO_3             0x00008000      /* GPIO pin 3 */
++
++/* INT_STATUS_REG -- Interrupt Status Register */
++#define ST_GC1_VSE_R          0x00000001      /* GC1 VSE - Rising edge */
++#define ST_GC1_VSE_F          0x00000002      /* GC1 VSE - Falling edge */
++#define ST_GC1_VDE_R          0x00000004      /* GC1 VDE - Rising edge */
++#define ST_GC1_VDE_F          0x00000008      /* GC1 VDE - Falling edge */
++#define ST_GC2_VSE_R          0x00000010      /* GC2 VSE - Rising edge */
++#define ST_GC2_VSE_F          0x00000020      /* GC2 VSE - Falling edge */
++#define ST_GC2_VDE_R          0x00000040      /* GC2 VDE - Rising edge */
++#define ST_GC2_VDE_F          0x00000080      /* GC2 VDE - Falling edge */
++#define ST_CFIFO_HALF_EMPTY   0x00000100      /* Command fifo half empty */
++#define ST_CFIFO_EMPTY                0x00000200      /* Command fifo empty */
++#define ST_SFIFO_HALF_EMPTY   0x00000400      /* Source fifo half empty */
++#define ST_SFIFO_EMPTY                0x00000800      /* Source fifo empty */
++#define ST_GE_IDLE            0x00001000      /* GE is idle */
++#define ST_GPIO_1             0x00002000      /* GPIO pin 1 */
++#define ST_GPIO_2             0x00004000      /* GPIO pin 2 */
++#define ST_GPIO_3             0x00008000      /* GPIO pin 3 */
++
++/* INT_RAW_STATUA_REG -- Interrupt Pin Raw Status Register */
++#define GC1_VSE                       0x00000001      /* GC1 - VSE */
++#define GC1_VDE                       0x00000004      /* GC1 - VDE */
++#define GC2_VSE                       0x00000010      /* GC2 - VSE */
++#define GC2_VDE                       0x00000040      /* GC2 - VDE */
++#define INT_GE_BUSY           0x00000100      /* GE busy */
++#define SFIFO_EMPTY           0x00000200      /* Source fifo empty */
++#define SFIFO_HEMPTY          0x00000400      /* Source fifo half empty */
++#define CFIFO_EMPTY           0x00000800      /* Command fifo empty */
++#define CFIFO_HEMPTY          0x00001000      /* Command fifo half empty */
++#define GPIO_PIN_1            0x00002000      /* GPIO pin 1 */
++#define GPIO_PIN_2            0x00004000      /* GPIO pin 2 */
++#define GPIO_PIN_3            0x00008000      /* GPIO pin 3 */
++
++/* 2D Engine registers - GE1 (0x00 - 0x7F) */
++#define DRAW_CMD      (GE_BASE + 0x00)    /* Drawing command register */
++#define WIDTH_HEIGHT  (GE_BASE + 0x04)    /* Width/height register */
++#define       LINE_DRAW       WIDTH_HEIGHT        /* Bresenham Line Draw reg */
++#define DEST_XY               (GE_BASE + 0x08)    /* Destination X/Y register */
++#define LINE_MAJOR_X  DEST_XY             /* Bresenham Line Start X/Y reg */
++#define PAT_OFFSET    DEST_XY             /* Pattern Offset register */
++#define SRC_XY                (GE_BASE + 0x0C)    /* Source X/Y register */
++#define LINE_MINOR_Y  SRC_XY              /* Bresenham Line Delta register */
++#define COLOR_COMPARE (GE_BASE + 0x10)    /* Color compare register */
++#define CLIP_LeftT    (GE_BASE + 0x14)    /* Clip Left/Top register */
++#define CLIP_RightB   (GE_BASE + 0x18)    /* Clip Right/Bottom register */
++#define FG_COLOR      (GE_BASE + 0x1C)    /* Fg color for Mono src reg */
++#define BG_COLOR      (GE_BASE + 0x20)    /* Bg color for Mono src reg */
++#define SRC_STRIDE_OFFSET (GE_BASE + 0x24)  /* Source Stride & Offset Reg */
++#define DEST_STRIDE   (GE_BASE + 0x28)    /* Base address register */
++#define BASE_ADDRESS  (GE_BASE + 0x2C)    /* Base address register */
++#define TEST_RESULT_REG       (GE_BASE + 0x7C)    /* Test result register */
++#define COLOR_PATTERN (GE_BASE + 0x100)   /* Color pattern registers */
++#define MONO_PATTERN0 COLOR_PATTERN       /* Mono Pattern register 0 */
++#define MONO_PATTERN1 (GE_BASE + 0x104)   /* Mono Pattern register 1 */
++#define PAT_FG_COLOR  (GE_BASE + 0x108)   /* Mono Pattern Fg color reg */
++#define PAT_BG_COLOR  (GE_BASE + 0x10C)   /* Mono Pattern Bg color reg */
++#define       _FIRST_GE       DRAW_CMD
++#define       _LAST_GE        (COLOR_PATTERN + 0x80)
++#define SRC_IMAGE_DATA        (GE_BASE + 0xC000)  /* Source Data register */
++
++/* 2D Engine registers - GE2 (0x80 to 0xFF) */
++#define DRAW_CMD2     (GE2_BASE + 0x00)   /* Drawing command register */
++#define WIDTH_HEIGHT2 (GE2_BASE + 0x04)   /* Width/height register */
++#define       LINE_DRAW2      WIDTH_HEIGHT2       /* Bresenham Line Draw register */
++#define DEST_XY2      (GE2_BASE + 0x08)   /* Destination X/Y register */
++#define LINE_MAJOR_X2 DEST_XY2            /* Bresenham Line Start X/Y reg */
++#define PAT_OFFSET2   DEST_XY2            /* Pattern Offset register */
++#define SRC_XY2               (GE2_BASE + 0x0C)   /* Source X/Y register */
++#define LINE_MINOR_Y2 SRC_XY2             /* Bresenham Line Delta register */
++#define COLOR_COMPARE2        (GE2_BASE + 0x10)   /* Color compare register */
++#define CLIP_LeftT2   (GE2_BASE + 0x14)   /* Clip Left/Top register */
++#define CLIP_RightB2  (GE2_BASE + 0x18)   /* Clip Right/Bottom register */
++#define FG_COLOR2     (GE2_BASE + 0x1C)   /* Fg color for Mono src reg */
++#define BG_COLOR2     (GE2_BASE + 0x20)   /* Bg color for Mono src reg */
++#define SRC_STRIDE_OFFSET2 (GE2_BASE + 0x24) /* Source Stride & Offset Reg */
++#define DEST_STRIDE2  (GE2_BASE + 0x28)   /* Base address register */
++#define BASE_ADDRESS2 (GE2_BASE + 0x2C)   /* Base address register */
++#define TEST_RESULT_REG2 (GE2_BASE + 0x7C)  /* Test result register */
++#define COLOR_PATTERN2        (GE2_BASE + 0x100)  /* Color pattern registers */
++#define MONO_PATTERN02        COLOR_PATTERN2      /* Mono Pattern register 0 */
++#define MONO_PATTERN12        (GE2_BASE + 0x104)  /* Mono Pattern register 1 */
++#define PAT_FG_COLOR2 (GE2_BASE + 0x108)  /* Mono Pattern Fg color reg */
++#define PAT_BG_COLOR2 (GE2_BASE + 0x10C)  /* Mono Pattern Bg color reg */
++#define       _FIRST_GE2      DRAW_CMD2
++#define       _LAST_GE2       (COLOR_PATTERN2 + 0x80)
++#define SRC_IMAGE_DATA2       (GE2_BASE + 0xC000) /* Source Data register */
++
++
++/* DEST_STRIDE color depth */
++#define GE_8BPP               0x00000000      /* 8BPP mode */
++#define GE_16BPP      0x40000000      /* 16BPP mode */
++#define GE_24BPP      0x80000000      /* 24BPP mode */
++#define GE_32BPP      0xC0000000      /* 24BPP mode */
++
++/* BASE_ADDRESS */
++#define       GE_TEST_MODE_ENABLE     0x20000000      /* Test mode enabled */
++#define GE_TEST_MASK          0xc0000000      /* Test mode read path select */
++#define SEL_CLIP_LR           0x40000000      /* Select clipping left/right */
++#define SEL_CLIP_TB           0x80000000      /* Select clipping top/bottom */
++
++/* Draw command register bits */
++#define DO_BITBLT             0x00000200
++#define       DO_AAFONT               0x00000300
++#define       DO_LINEDRAW             0x00000400
++#define X_DIR                 0x00000800      /* Negative X direction */
++#define Y_DIR                 0x00001000      /* Negative Y direction */
++#define SRC_IS_MEMORY         0x00002000      /* Source is in system memory */
++#define MONO_SRC              0x00004000      /* Source is mono bitmap */
++#define MONO_PATTERN          0x00008000      /* Pattern is monochrome */
++#define TRANS_COLOR           0x00010000      /* Transparency is enabled */
++#define TRANS_NOT_EQUAL               0x00020000      /* Polarity for color */
++#define TRANS_MONO            0x00040000      /* Mono xparency is enabled */
++#define TRANS_MONO_FG         0x00080000      /* Polarity for mono */
++#define       PACKED_MODE             0x00100000      /* Memory xfer mode select */
++#define       ALPHA_BYTE_MASK         0x00600000      /* Alpha Byte mask for 32bpp */
++#define MONO_SOLID            0x00800000      /* Solid Mono Pattern */
++#define SRC_NE_DEST_STRIDE    0x01000000      /* Src Not Equal Dest Stride */
++#define       ROP2_ENABLE             0x02000000      /* Use Rop2 code */
++#define CLIP_ENABLE           0x04000000      /* Clipping is enabled */
++#define AUTO_EXEC             0x08000000      /* Auto execute at dest X/Y */
++#define VDE_GC2_ENABLE                0x10000000      /* Enable falling edge check */
++#define VDE_GC1_ENABLE                0x20000000      /* Enable falling edge check */
++#define       COLOR_DEPTH_MASK        0xC0000000      /* Color Depth mask */
++#define GE_8BPP                       0x00000000      /* 8BPP mode */
++#define GE_16BPP              0x40000000      /* 16BPP mode */
++#define GE_24BPP              0x80000000      /* 24BPP mode */
++
++/* Graphics Controller 1 Registers */
++#define GC1_CONTROL   (GC_BASE + 0x00)  /* Graphics Controll 1 Control Reg */
++#define GC1_CRT_CONTROL       (GC_BASE + 0x04)  /* CRT controll register */
++#define HD1_CONTROL   (GC_BASE + 0x08)  /* Horizontal Display 1 Control */
++#define VD1_CONTROL   (GC_BASE + 0x0C)  /* Vertical Display 1 Control */
++#define HS1_CONTROL   (GC_BASE + 0x10)  /* Horizontal Sync 1 Control */
++#define VS1_CONTROL   (GC_BASE + 0x14)  /* Vertical Sync 1 Control */
++#define HW1_CONTROL   (GC_BASE + 0x20)  /* Horizontal Window 1 Control */
++#define VW1_CONTROL   (GC_BASE + 0x24)  /* Vertical Window 1 Control */
++#define AHW1_CONTROL  (GC_BASE + 0x28)  /* Alt Horizontal Window 1 Control */
++#define AVW1_CONTROL  (GC_BASE + 0x2C)  /* Alt Vertical Window 1 Control */
++#define IW1_START_ADDR        (GC_BASE + 0x30)  /* Image Window 1 Start Address */
++#define AIW1_START_ADDR       (GC_BASE + 0x34)  /* Alt Image Window 1 Start Address */
++#define IW1_STRIDE    (GC_BASE + 0x38)  /* (Alt) Image Window 1 Stride */
++#define IW1_LINE_SIZE (GC_BASE + 0x3C)  /* (Alt) Image Window 1 Line Size */
++#define HW_CURSOR1_POS        (GC_BASE + 0x40)  /* Hardware cursor 1 position */
++#define HW_CURSOR1_ADDR       (GC_BASE + 0x44)  /* Start address and offset */
++#define HW_CURSOR1_FGCLR (GC_BASE + 0x48) /* Foreground color */
++#define HW_CURSOR1_BGCLR (GC_BASE + 0x4C)  /* Background color */
++
++/* Graphics Controller 2 Registers */
++#define GC2_CONTROL   (GC_BASE + 0x80)  /* Graphics Controll 2 Control Reg */
++#define GC2_CRC_CONTROL       (GC_BASE + 0x84)  /* CRC Control */
++#define HD2_CONTROL   (GC_BASE + 0x88)  /* Horizontal Display 2 Control */
++#define VD2_CONTROL   (GC_BASE + 0x8C)  /* Vertical Display 2 Control */
++#define HS2_CONTROL   (GC_BASE + 0x90)  /* Horizontal Sync 2 Control */
++#define VS2_CONTROL   (GC_BASE + 0x94)  /* Vertical Sync 2 Control */
++#define HW2_CONTROL   (GC_BASE + 0xA0)  /* Horizontal Window 2 Control */
++#define VW2_CONTROL   (GC_BASE + 0xA4)  /* Vertical Window 2 Control */
++#define AHW2_CONTROL  (GC_BASE + 0xA8)  /* Alt Horizontal Window 2 Control */
++#define AVW2_CONTROL  (GC_BASE + 0xAC)  /* Alt Vertical Window 2 Control */
++#define IW2_START_ADDR        (GC_BASE + 0xB0)  /* Image Window 2 Start Address */
++#define AIW2_START_ADDR (GC_BASE + 0xB4)  /* Alt Image Window 2 Start Address */
++#define IW2_STRIDE    (GC_BASE + 0xB8)  /* (Alt) Image Window 2 Stride */
++#define IW2_LINE_SIZE (GC_BASE + 0xBC)  /* (Alt) Image Window 2 Line Size */
++#define HW_CURSOR2_POS        (GC_BASE + 0xC0)  /* Hardware cursor 2 position */
++#define HW_CURSOR2_ADDR       (GC_BASE + 0xC4)  /* Start address and offset */
++#define HW_CURSOR2_FGCLR (GC_BASE + 0xC8) /* Foreground color */
++#define HW_CURSOR2_BGCLR (GC_BASE + 0xCC) /* Background color */
++
++/* GC1_CONTROL/GC2_CONTROL register */
++#define GC_ENABLE             0x00000001UL    /* Controll 1/2 enabled */
++#define GC_DISABLE                    0xfffffffeUL    /* Controll 1/2 disabled */
++#define HORZ_COUNT_RESET      0x00000002UL    /* Horiz counter 1/2 reset */
++#define VERT_COUNT_RESET      0x00000004UL    /* Vertical counter 1/2 reset */
++#define IM_ENABLE             0x00000008UL    /* Image Window 1/2 Enable */
++#define IM_DISABLE            0xfffffff7UL    /* Image Window 1/2 Disable */
++
++#define GC_1BPP                       0x00000000UL    /* GC1/2 color depth */
++#define GC_2BPP                       0x00000010UL
++#define GC_4BPP                       0x00000020UL
++#define GC_8BPP                       0x00000030UL
++#define GC_16BPP              0x00000040UL    /* with color palette enabled */
++#define GC_24BPP_NBP          0x00000050UL    /* with color palette enabled */
++#define GC_32BPP_ABGR         0x00000060UL    /* with color palette enabled */
++#define GC_32BPP_ARGB         0x00000070UL    /* with color palette enabled */
++#define GC_16BPP_BP           0x000000C0UL    /* with color pal bypassed */
++#define GC_24BPP_BP           0x000000D0UL    /* with color pal bypassed */
++#define GC_32BPP_ABGR_BP      0x000000E0UL    /* with color pal bypassed */
++#define GC_32BPP_ARGB_BP      0x000000F0UL    /* with color pal bypassed */
++#define       GC_32BPP                GC_32BPP_ARGB   /* Default 32bpp with ARGB */
++#define GC_24BPP              GC_24BPP_NBP
++
++#define HC_ENABLE             0x00000100UL    /* Hardware cursor enable */
++#define HC_DISABLE            0xfffffeffUL    /* And mask to disable HC */
++#define AIM_ENABLE            0x00000800UL    /* Alt Image Win 1/2 Enable */
++
++#define AGC_1BPP              0x00000000UL    /* Alt GC1/2 color depth */
++#define AGC_2BPP              0x00001000UL
++#define AGC_4BPP              0x00002000UL
++#define AGC_8BPP              0x00003000UL
++#define AGC_16BPP             0x00004000UL
++#define AGC_24BPP             0x00005000UL
++#define AGC_32BPP_ABGR                0x00006000UL
++#define AGC_32BPP_ARGB                0x00007000UL
++#define AGC_16BPP_BP          0x0000C000UL
++#define AGC_24BPP_BP          0x0000D000UL
++#define AGC_32BPP_ABGR_BP     0x0000E000UL
++#define AGC_32BPP_ARGB_BP     0x0000F000UL
++#define       AGC_32BPP               AGC_32BPP_ARGB_BP /* Default 32bpp w/ ARGB_BP */
++
++#define GxRCLK_BUSCLK         0x00000000UL    /* G1RCLK source is bus clock */
++#define GxRCLK_PLL1           0x00010000UL    /* G1RCLK source is PLL1 */
++#define GxRCLK_PLL2           0x00020000UL    /* G1RCLK source is PLL2 */
++#define GxRCLK_PLL3           0x00030000UL    /* G1RCLK source is PLL3 */
++#define GxRCLK_PLL_MASK       0x00030000UL    /* G1RCLK source mask */
++#define GC_TEST_MODE0         0x00040000UL    /* Test mode 0 enabled */
++#define GC_TEST_MODE1         0x00080000UL    /* Test mode 1 enabled */
++
++#define FDx_1                 0x00000000UL    /* FD1 = 1 */
++#define FDx_15                        0x00100000UL    /* FD1 = 1.5 */
++#define FDx_25                        0x00200000UL    /* FD1 = 2.5 */
++#define FDx_35                        0x00300000UL    /* FD1 = 3.5 */
++#define FDx_45                        0x00400000UL    /* FD1 = 4.5 */
++#define FDx_55                        0x00500000UL    /* FD1 = 5.5 */
++#define FDx_65                        0x00600000UL    /* FD1 = 6.5 */
++
++/* GC1_CRT_CONTROL register */
++#define CRT_ENABLE            0x00000001UL    /* CRT DAC enabled */
++#define CRT_DISABLE           0xfffffffeUL    /* CRT DAC disabled -and mask */
++#define CRT_BY_GC1            0x00000001UL    /* CRT DAC driven by GC1 */
++#define CRT_BY_GC2            0x00000003UL    /* CRT DAC driven by GC2 */
++#define CRT_BY_GCxMASK                0xfffffffcUL    /* Mask for CRT DAC */
++#define VSYNC_OUT_PMCLK               0x00000004UL    /* CRT VSYNC output PMCLK
++                                                   at PwrDn */
++#define HSYNC_OUT_PMCLK               0x00000008UL    /* CRT HSYNC output PMCLK
++                                                   at PwrDn */
++#define HSYNC_OUT_LOW         0x00000010UL    /* CRT HSYNC output pin low */
++#define HSYNC_OUT_HIGH                0x00000020UL    /* CRT HSYNC output pin high */
++#define VSYNC_OUT_LOW         0x00000040UL    /* CRT VSYNC output pin low */
++#define VSYNC_OUT_HIGH                0x00000080UL    /* CRT VSYNC output pin high */
++#define HSYNC_POLARITY_LOW    0x00000100UL    /* active low */
++#define VSYNC_POLARITY_LOW    0x00000200UL    /* active low */
++#define SYNC_PED_ENABLE               0x00000400UL    /* Sync pedestal enable */
++#define BLANK_PED_ENABLE      0x00000800UL    /* Blank pedestal enable */
++#define CSYNC_ENABLE          0x00001000UL    /* Composite Sync Enable */
++#define VREF_EXTERNAL         0x00002000UL    /* Select external VREF */
++#define MON_SENSE_ENABLE      0x00004000UL    /* CRT DAC monitor sense
++                                                   enable */
++#define CONST_OUT_ENABLE      0x00008000UL    /* Constant output enable */
++#define BLUE_NOT_LOADED               0x01000000UL    /* Blue DAC is not loaded */
++#define GREEN_NOT_LOADED      0x02000000UL    /* Green DAC is not loaded */
++#define RED_NOT_LOADED                0x04000000UL    /* Red DAC is not loaded */
++
++/* GC2_CRC_CONTROL */
++#define CRC_ENABLE            0x00000001UL    /* Enable CRC logic */
++#define CRC_2_VSYNC           0x00000002UL    /* Wait for 2 vsync */
++#define CRC_READ_BLUE         0x00000000UL    /* Read CRC result for blue */
++#define CRC_READ_GREEN                0x00000004UL    /* Read CRC result for green */
++#define CRC_READ_RED          0x00000008UL    /* Read CRC result for red */
++#define CRC_RESULT_MASK               0x3fffff00UL    /* CRC result mask */
++
++/* Flat Panel Interface Registers */
++#define FP_CONTROL    (FP_BASE + 0x00)    /* Flat panel control */
++#define FP_PIN_CONTROL        (FP_BASE + 0x04)    /* Flat panel pin control */
++#define FP_GPO_CONTROL        (FP_BASE + 0x08)    /* FP Gen. purpose output ctrl */
++#define FP_GPIO_CONTROL       (FP_BASE + 0x0C)    /* FP Gen. purpose I/O control */
++#define STN_CONTROL   (FP_BASE + 0x10)    /* STN panel control */
++#define DSTN_FB_CONTROL       (FP_BASE + 0x14)    /* D-STN frame buffer control */
++#define PWM_CONTROL   (FP_BASE + 0x3C)    /* PWM control */
++#define FRC_PATTERN   (FP_BASE + 0x40)    /* FRC pattern starting index */
++#define FRC_WEIGHT    (FP_BASE + 0xC0)    /* FRC weight starting index */
++
++/* FP_CONTROL */
++#define FPI_ENABLE          0x00000001UL    /* Trigger fp power up sequence */
++#define FPI_DISABLE       0xfffffffeUL    /* Trigger fp power down sequence */
++#define FPI_BY_GC1        0x00000001UL    /* FPI enabled & driven by GC1 */
++#define FPI_BY_GC2        0x00000003UL    /* FPI enabled & driven by GC2 */
++#define FPI_BY_GCxMASK            0xfffffffcUL    /* mask */
++#define FP_TYPE_TFT       0x00000000UL    /* Flat panel type TFT */
++#define FP_TYPE_SSTN      0x00000004UL    /* Flat panel type S-STN */
++#define FP_TYPE_DSTN      0x00000008UL    /* Flat panel type D-STN */
++#define FP_TYPE_MASK      0x0000000cUL    /* Flat panel type mask */
++#define FP_COLOR          0x00000000UL    /* Color flat panel */
++#define FP_MONO                   0x00000010UL    /* Mono flat panel */
++#define TFT_4BITS_MONO      0x00000000UL    /* Specify num of bits/pixel */
++#define TFT_12BITS_COLOR    0x00000000UL    /* Specify num of bits/pixel */
++#define SSTN_4BITS_MONOCLR  0x00000000UL    /* Specify num of bits/pixel */
++#define DSTN_8BITS_MONOCLR  0x00000000UL    /* Specify num of bits/pixel */
++#define TFT_6BITS_MONO      0x00000020UL    /* Specify num of bits/pixel */
++#define TFT_18BITS_COLOR    0x00000020UL    /* Specify num of bits/pixel */
++#define SSTN_8BITS_MONOCLR  0x00000020UL    /* Specify num of bits/pixel */
++#define DSTN_16BITS_MONOCLR 0x00000020UL    /* Specify num of bits/pixel */
++#define TFT_8BITS_MONO      0x00000040UL    /* Specify num of bits/pixel */
++#define TFT_24BITS_COLOR    0x00000040UL    /* Specify num of bits/pixel */
++#define SSTN_12BITS_COLOR   0x00000040UL    /* Specify num of bits/pixel */
++#define DSTN_24BITS_COLOR   0x00000040UL    /* Specify num of bits/pixel */
++#define SSTN_16BITS_MONOCLR 0x00000060UL    /* Specify num of bits/pixel */
++#define SSTN_24BITS_COLOR   0x00000080UL    /* Specify num of bits/pixel */
++#define DITHER_PATTERN_0    0x00000000UL    /* Dither pattern */
++#define DITHER_PATTERN_1    0x00000100UL    /* Dither pattern */
++#define DITHER_PATTERN_2    0x00000200UL    /* Dither pattern */
++#define DITHER_PATTERN_3    0x00000300UL    /* Dither pattern */
++#define DITHER_BASE_8BITS   0x00000000UL    /* No dithering */
++#define DITHER_BASE_2BITS   0x00002000UL    /* Num of bits to be dithered */
++#define DITHER_BASE_3BITS   0x00003000UL    /* Num of bits to be dithered */
++#define DITHER_BASE_4BITS   0x00004000UL    /* Num of bits to be dithered */
++#define DITHER_BASE_6BITS   0x00006000UL    /* Num of bits to be dithered */
++#define FRC_ALTWIN_DISABLE  0x00008000UL    /* Disable Dither/FRC if Alt enabled */
++#define FRC_2LEVEL        0x00000000UL    /* Disable FRC */
++#define FRC_4LEVEL        0x00010000UL    /* 4-level FRC */
++#define FRC_8LEVEL        0x00020000UL    /* 8-level FRC */
++#define FRC_16LEVEL       0x00030000UL    /* 16-level FRC */
++#define DITHER_PATTERN_ADJ1 0x00fc0000UL    /* Dither pattern adjust 1 */
++#define DITHER_PATTERN_ADJ2 0x07000000UL    /* Dither pattern adjust 2 */
++#define DITHER_PATTERN_ADJ3 0x08000000UL    /* Dither pattern adjust 3 */
++#define TEST_MODE0_ENABLE   0x10000000UL    /* Enable test mode 0 */
++#define TEST_MODE1_ENABLE   0x20000000UL    /* Enable test mode 1 */
++#define TEST_MODE2_ENABLE   0x40000000UL    /* Enable test mode 2 */
++#define TEST_MODE3_ENABLE   0x80000000UL    /* Enable test mode 3 */
++
++/* FP_PIN_CONTROL */
++#define FP_PIN_DISABLE            0x00000001UL    /* Disable flat panel pins */
++#define DATA_INV_ENABLE           0x00000002UL    /* TFT fp data inversion enabled */
++#define FP_DISP_ENABLE            0x00000004UL    /* FP Display enable control */
++#define FMOD_ENABLE       0x00000008UL    /* Flat panel AC mod enable */
++#define FD2_SCLK          0x00000010UL    /* STN output shift clk on FD2 pin*/
++#define FSCLK_OUTPUT_ENABLE 0x00000020UL    /* FSCLK output enable */
++#define TFT_SCLK_SELECT           0x00000040UL    /* TFT shift clock select */
++#define SCLK_MASK         0x00000080UL    /* Shift clock mask */
++#define STN_LP_DISABLE            0x00000100UL    /* STN LP control */
++#define SCLK_DISABLE      0x00000200UL    /* STN shift clock control */
++#define STN_ExtraLP_ENABLE  0x00000400UL    /* STN extra LP control */
++#define FP_FD2_MAX        0x00000000UL    /* FD2 drive strength - max (16mA)*/
++#define FP_FD2_MEDIUM     0x00001000UL    /* FD2 drive strength - medium */
++#define FP_FD2_MEDIUM2            0x00002000UL    /* FD2 drive strength - medium 2 */
++#define FP_FD2_MIN        0x00003000UL    /* FD2 drive strength - min */
++#define FP_DATA_MAX       0x00000000UL    /* Data drv strength - max (16mA) */
++#define FP_DATA_MEDIUM            0x00004000UL    /* Data drive strength - medium */
++#define FP_DATA_MEDIUM2           0x00008000UL    /* Data drive strength - medium 2 */
++#define FP_DATA_MIN       0x0000c000UL    /* Data drive strength - min */
++#define FD2_ACTIVE_L      0x00010000UL    /* Flat panel data bit 2 polarity */
++#define FD_ACTIVE_L       0x00020000UL    /* Flat panel data polarity */
++#define FDE_ACTIVE_L      0x00040000UL    /* Data enable polarity */
++#define FHSYNC_ACTIVE_L           0x00080000UL    /* Horz sync polarity */
++#define FVSYNC_ACTIVE_L           0x00100000UL    /* Vert sync polarity */
++#define FSCLK_ACTIVE_L            0x00200000UL    /* Shift clock polarity */
++#define FP_FSCLK_MAX      0x00000000UL    /* Sh clk drv strength -max (16mA)*/
++#define FP_FSCLK_MEDIUM           0x00400000UL    /* Sh clk drv strength -medium */
++#define FP_FSCLK_MEDIUM2    0x00800000UL    /* Sh clk drv strength -medium 2 */
++#define FP_FSCLK_MIN      0x00c00000UL    /* Sh clk drv strength -min */
++#define FSCLK_DELAY       0x07000000UL    /* Shift clock delay */
++
++/* FP_GPO_CONTROL */
++#define ENCTL_AS_GPO0     0x00000001UL    /* ENCTL used as GPO 0 */
++#define ENCTL_AS_OSC      0x00000002UL    /* ENCTL used as Oscillator clock */
++#define ENCTL_AS_PLL3     0x00000003UL    /* ENCTL used as PLL3 clock */
++#define ENVEE_AS_GPO1     0x00000004UL    /* ENVEE used as GPO 1 */
++#define PWM0_AS_GPO2      0x00000010UL    /* PWM0 pin used as GPO 2 */
++#define PWM1_AS_GPO3      0x00000040UL    /* PWM1 pin used as GPO 3 */
++#define ENVDD_AS_GPO4     0x00000100UL    /* ENVDD pin used as GPO 4 */
++#define       FP_PWM_MAX          0x00000000UL    /* PWM0/1 drv strength -max (16mA)*/
++#define       FP_PWM_MEDIUM       0x00000400UL    /* PWM0/1 drv strength -medium */
++#define       FP_PWM_MEDIUM2      0x00000800UL    /* PWM0/1 drv strength -medium 2 */
++#define       FP_PWM_MIN          0x00000c00UL    /* PWM0/1 drv strength -min */
++#define       FP_GPIO_MAX         0x00000000UL    /* GPIO0/1/2 drv strgth. -max 16mA*/
++#define       FP_GPIO_MEDIUM      0x00001000UL    /* GPIO0/1/2 drv strgth. -medium */
++#define       FP_GPIO_MEDIUM2     0x00002000UL    /* GPIO0/1/2 drv strgth. -medium 2*/
++#define       FP_GPIO_MIN         0x00003000UL    /* GPIO0/1/2 drv strgth. -min */
++#define       FP_EN_MAX           0x00000000UL    /* ENVDD/ENCTL/ENVEE -max (16mA) */
++#define       FP_EN_MEDIUM        0x00004000UL    /* ENVDD/ENCTL/ENVEE -medium */
++#define       FP_EN_MEDIUM2       0x00008000UL    /* ENVDD/ENCTL/ENVEE -medium 2 */
++#define       FP_EN_MIN           0x0000c000UL    /* ENVDD/ENCTL/ENVEE -min */
++#define GPO0_DATA_HIGH            0x00010000UL    /* ENCTL is driven high */
++#define GPO1_DATA_HIGH            0x00020000UL    /* ENVEE is driven high */
++#define GPO2_DATA_HIGH            0x00040000UL    /* PWM0 is driven high */
++#define GPO3_DATA_HIGH            0x00080000UL    /* PWM1 is driven high */
++#define GPO4_DATA_HIGH            0x00100000UL    /* ENVDD is driven high */
++
++/* FP_GPIO_CONTROL */
++#define GPIO0_IN          0x00000000UL    /* General-purpose input */
++#define GPIO0_OUT         0x00000001UL    /* General-purpose output */
++#define GPIO0_PLL1        0x00000002UL    /* GPIO0 used to output PLL 1 clk */
++#define GPIO0_CRC_B       0x00000003UL    /* GPIO0 used to output CRC Blue */
++#define GPIO1_IN          0x00000000UL    /* General-purpose input */
++#define GPIO1_OUT         0x00000004UL    /* General-purpose output */
++#define GPIO1_PLL2        0x00000008UL    /* GPIO1 used to output PLL 2 clk */
++#define GPIO1_CRC_G       0x0000000cUL    /* GPIO1 used to output CRC Green */
++#define GPIO2_IN          0x00000000UL    /* General-purpose input */
++#define GPIO2_OUT         0x00000010UL    /* General-purpose output */
++#define GPIO2_PLL3        0x00000020UL    /* GPIO2 used to output PLL 3 clk */
++#define GPIO2_CRC_R       0x00000030UL    /* GPIO2 used to output CRC Red */
++#define GPIO0_OUT_HIGH            0x00010000UL    /* GOIO0 output data */
++#define GPIO1_OUT_HIGH            0x00020000UL    /* GOIO1 output data */
++#define GPIO2_OUT_HIGH            0x00040000UL    /* GOIO2 output data */
++#define GPIO0_IN_HIGH     0x01000000UL    /* GOIO0 input data */
++#define GPIO1_IN_HIGH     0x02000000UL    /* GOIO1 input data */
++#define GPIO2_IN_HIGH     0x04000000UL    /* GOIO2 input data */
++
++/* STN_CONTROL */
++#define FMOD_FRAMECLK 0x00000000UL    /* FMOD generated using frame clock */
++#define FMOD_LINECLK  0x80000000UL    /* FMOD generated using line clock */
++
++/* PWM_CONTROL */
++#define PWM0_BY_PLL   0x00000000UL    /* PWM 0 signal by PLL */
++#define PWM0_BY_BUS   0x00000001UL    /* PWM 0 signal using bus clk */
++#define PWM0_BY_PMC   0x00000002UL    /* PWM 0 signal by power mgt clock */
++#define PWM0_ALWAYS_ON        0x00000004UL    /* PWM 0 signal always generated */
++#define PWM0_DC_MASK  0xffff00ffUL    /* PWM 0 duty cycle mask */
++#define PWM0_MASK     0xffff0000UL    /* PWM 0 mask */
++#define PWM1_BY_PLL   0x00000000UL    /* PWM 1 signal by PLL */
++#define PWM1_BY_BUS   0x00010000UL    /* PWM 1 signal using bus clk */
++#define PWM1_BY_PMC   0x00020000UL    /* PWM 1 signal by power mgt clock */
++#define PWM1_ALWAYS_ON        0x00040000UL    /* PWM 1 signal always generated */
++#define PWM1_DC_MASK  0x00ffffffUL    /* PWM 0 duty cycle mask */
++#define PWM1_MASK     0x0000ffffUL    /* PWM 1 mask */
++
++/* PCI Power Management Interface Registers */
++#ifndef PCI_VENDOR_DEVICE
++#define PCI_VENDOR_DEVICE     (PC_BASE + 0x00)
++#endif
++
++#ifndef PCI_CMD_STATUS
++#define PCI_CMD_STATUS                (PC_BASE + 0x04)
++#endif
++
++#ifndef PCI_REV_CLASS
++#define PCI_REV_CLASS         (PC_BASE + 0x08)
++#endif
++
++#ifndef PCI_HEADER_TYPE
++#define PCI_HEADER_TYPE               (PC_BASE + 0x0c)
++#endif
++
++#ifndef PCI_SUB_ID
++#define PCI_SUB_ID            (PC_BASE + 0x2c)
++#endif
++
++#ifndef PCI_ROM_BASE
++#define PCI_ROM_BASE          (PC_BASE + 0x30)
++#endif
++
++#ifndef PCI_CAP_PTR
++#define PCI_CAP_PTR           (PC_BASE + 0x34)
++#endif
++
++#ifndef PCI_INTERRUPT
++#define PCI_INTERRUPT         (PC_BASE + 0x3c)
++#endif
++
++#ifndef PCI_PM_REGISTER
++#define PCI_PM_REGISTER               (PC_BASE + 0x40)
++#endif
++
++#ifndef PCI_PM_CNTL_STATUS
++#define PCI_PM_CNTL_STATUS    (PC_BASE + 0x44)
++#endif
++
++/* POWER_STATE */
++#define POWER_STATE_MASK      0x00000003UL    /* Device power state mask */
++#define ENTER_D0              0x00000000UL    /* Enter D0 state */
++#define ENTER_D1              0x00000001UL    /* Enter D1 state */
++#define ENTER_D2              0x00000002UL    /* Enter D2 state */
++#define ENTER_D3              0x00000003UL    /* Enter D3 state */
++
++/* DC (Device Configuration Unit) Registers */
++#define DC_0  (DC_BASE + 0x00)        /* Device Configruation Register 0 */
++#define DC_1  (DC_BASE + 0x04)        /* Device Configruation Register 1 */
++#define       DC_SW_0 (DC_BASE + 0x08)        /* Software Register 0 */
++#define       DC_SW_1 (DC_BASE + 0x0C)        /* Software Register 1 */
++
++/* DC_0 */
++#define       OSC_BYPASSED    0x00000001UL    /* Oscillator bypassed, powered down */
++#define       OSC_ENABLE      0x00000002UL    /* Oscillator control can be enabled */
++#define PLL1_BYPASSED 0x00000004UL    /* PLL1 bypassed */
++#define PLL1_ENABLE   0x00000008UL    /* PLL1 can be enabled */
++#define       PLL1_DIVBY1     0x00000000UL    /* PLL1 P output divisor by 1 */
++#define       PLL1_DIVBY2     0x00000010UL    /* PLL1 P output divisor by 2 */
++#define       PLL1_DIVBY4     0x00000020UL    /* PLL1 P output divisor by 4 */
++#define       PLL1_DIVBY8     0x00000030UL    /* PLL1 P output divisor by 8 */
++#define       PLL1_DIVBY16    0x00000040UL    /* PLL1 P output divisor by 16 */
++#define       PLL1_DIV_MASK   0x00000070UL    /* PLL1 P output divisor mask */
++#define       CIF_DIVBY1      0x00000000UL    /* CPU Interface clk divisor by 1 */
++#define       CIF_DIVBY2      0x00000080UL    /* CPU Interface clk divisor by 2 */
++#define       STRONGARM_SYNC_F    0x00002000UL /* StrongARM bus intrf at fall edge */
++#define       SW_CHIP_RESET       0x00004000UL /* Software chip reset */
++#define       MEM_STANDBY_DISABLE 0x00008000UL /* Memory Power unit Standby disab. */
++#define       OSC_SHAPER_DISABLE  0x01000000UL /* Oscillator waveform shaper disab. */
++#define       FAST_POWER_DISABLE  0x02000000UL /* Fast Power Sequencing disable */
++#define       OSC_FREQ_SEL_0      0x00000000UL /* Osc frequency select range 0 */
++#define       OSC_FREQ_SEL_1      0x04000000UL /* Osc frequency select range 1 */
++#define       OSC_FREQ_SEL_2      0x08000000UL /* Osc frequency select range 2 */
++#define       OSC_FREQ_SEL_3      0x0c000000UL /* Osc frequency select range 3 */
++
++/* DC_1 */
++#define       BUS_MODE_MASK   0x0000003FUL    /* Bus interface mode mask */
++#define       BUS_MODE_SH7709 0x00000001UL    /* Bus interface mode - SH7709 */
++#define       BUS_MODE_SH7750 0x00000002UL    /* Bus interface mode - SH7750 */
++#define       BUS_MODE_VR41xx 0x00000004UL    /* Bus interface mode - VR4111/21 */
++#define       BUS_MODE_SA1110 0x00000008UL    /* Bus interface mode - SA1110 */
++#define       BUS_MODE_TX3922 0x00000010UL    /* Bus interface mode - TX3922 */
++#define       BUS_MODE_PCI    0x00000020UL    /* Bus interface mode - PCI */
++
++/* PMU (Power Management Unit) Registers */
++#define PM_MISC               (PM_BASE + 0x00)        /* Power management misc ctrl */
++#define D1_STATE      (PM_BASE + 0x04)        /* D1 state control */
++#define D2_STATE      (PM_BASE + 0x08)        /* D2 state control */
++#define PLL2_CONTROL  (PM_BASE + 0x18)        /* PLL2 programming */
++#define PLL3_CONTROL  (PM_BASE + 0x1C)        /* PLL3 programming */
++
++/* PM_MISC */
++#define PLL1_N_BIT5           0x00000001UL    /* Bit 5 of PLL1 N parameter */
++#define PLL2_ENABLE           0x00000004UL    /* PLL2 can be enabled */
++#define PLL3_ENABLE           0x00000008UL    /* PLL3 can be enabled */
++#define FORCE_POWER_STATE     0x00000020UL    /* For testing */
++#define GE_ENABLE             0x00000100UL    /* GE can be enabled */
++#define GE_CLOCK_ON           0x00000200UL    /* GE clock is always running */
++#define GE_PIPELINE_ON                0x00000400UL    /* GE pipeline always running */
++#define GE_BY_BUS             0x00000000UL    /* GE driven by bus intf clk */
++#define GE_BY_PLL1            0x00000800UL    /* GE driven by PLL1 */
++#define GE_BY_PLL2            0x00001000UL    /* GE driven by PLL2 */
++#define GE_BY_PLL3            0x00001800UL    /* GE driven by PLL3 */
++#define GE_BY_MASK            0x00001800UL    /* GE clock select mask */
++#define GE_CMDFIFO_RESET      0x00002000UL    /* GE command FIFO is reset */
++#define GE_SRCFIFO_RESET      0x00004000UL    /* GE CPU src FIFO is reset */
++#define       POWER_ON_IF_MIU_ON      0x00008000UL    /* Pwr seq on when MIU enab.*/
++#define D3_MEM_REFRESF                0x00010000UL    /* FrameBuf refreshed in D3 */
++#define D4_MEM_REFRESF                0x00020000UL    /* FrameBuf refreshed in D4 */
++#define PMCLK_4CYCLE          0x00000000UL    /* Power sequencing interval */
++#define PMCLK_8CYCLE          0x00040000UL    /* Power sequencing interval */
++#define PMCLK_16CYCLE         0x00080000UL    /* Power sequencing interval */
++#define PMCLK_2048CYCLE               0x000c0000UL    /* Power sequencing interval */
++#define FP_PMCLK_512          0x00000000UL    /* FP power seq interval */
++#define FP_PMCLK_1024         0x00100000UL    /* FP power seq interval */
++#define FP_PMCLK_2048         0x00200000UL    /* FP power seq interval */
++#define FP_PMCLK_128K         0x00300000UL    /* FP power seq interval */
++#define POWER_SEQ_ALL         0x00400000UL    /* General power seq interval */
++#define PMU_TEST_MODE         0x008000UL      /* PMU test mode */
++#define PM_POWER_MASK         0x03000000UL    /* Power state mask */
++#define PM_D0_STATE           0x00000000UL    /* Power state D0 */
++#define PM_D1_STATE           0x01000000UL    /* Power state D1 */
++#define PM_D2_STATE           0x02000000UL    /* Power state D2 */
++#define PM_D3_STATE           0x03000000UL    /* Power state D3 */
++#define POWER_IN_PROGRESS     0x04000000UL    /* Power seq. active status */
++
++/* D1_STATE and D2_STATE */
++#define DxOSC_ENABLE    0x00000001UL  /* Oscillator can be enabled in D1/2 */
++#define DxPLL1_ENABLE   0x00000002UL  /* PLL1 can be enabled in D1/2 */
++#define DxPLL2_ENABLE   0x00000004UL  /* PLL2 can be enabled in D1/2 */
++#define DxPLL3_ENABLE   0x00000008UL  /* PLL3 can be enabled in D1/2 */
++#define DxMIU_ENABLE    0x00000010UL  /* MIU can be enabled in D1/2 */
++#define DxMEM_REFRESH 0x00000020UL    /* Memory is refreshed in D1/2 */
++#define DxGE_ENABLE           0x00000040UL    /* GE can be enabled in D1/2 */
++#define DxCRT_ENABLE    0x00000100UL  /* CRT can be enabled in D1/2 */
++#define DxFP_ENABLE     0x00000200UL  /* Flat panel can be enabled in D1/2 */
++#define DxGC1_ENABLE    0x00010000UL  /* GC1 can be enabled in D1/2 */
++#define DxW1_ENABLE     0x00020000UL  /* Window 1 can be enabled in D1/2 */
++#define DxAW1_ENABLE    0x00040000UL  /* Alt window 1 enabled in D1/2 */
++#define DxHC1_ENABLE    0x00080000UL  /* Cursor 1 enabled in D1/2 */
++#define DxGC2_ENABLE    0x01000000UL  /* GC2 can be enabled in D1/2 */
++#define DxW2_ENABLE     0x02000000UL  /* Window 2 can be enabled in D1/2 */
++#define DxAW2_ENABLE    0x04000000UL  /* Alt window 2 enabled in D1/2 */
++#define DxHC2_ENABLE    0x08000000UL  /* Cursor 2 enabled in D1/2 */
++
++/* PLL2_CONTROL/PLL3_CONTROL */
++#define PLL_FROM_OSC    0x00000000UL  /* PLL2/3 ref clock from OSCCLK */
++#define PLL_FROM_PxCLK  0x00000001UL  /* PLL2/3 ref clock from P2CLK */
++#define PLL_BYPASSED    0x00000002UL  /* PLL2/3 is bypassed */
++#define PLL_DIVBY1    0x00000000UL    /* PLL2/3 P output divisor by 1 */
++#define PLL_DIVBY2    0x00000010UL    /* PLL2/3 P output divisor by 2 */
++#define PLL_DIVBY4    0x00000020UL    /* PLL2/3 P output divisor by 4 */
++#define PLL_DIVBY8    0x00000030UL    /* PLL2/3 P output divisor by 8 */
++#define PLL_DIVBY16   0x00000040UL    /* PLL2/3 P output divisor by 16 */
++#define PLL_DIV_MASK  0x00000070UL    /* PLL2/3 P output divisor mask */
++
++/* CPU Interface Registers */
++#define CPU_CONTROL   (CC_BASE + 0x00)        /* CPU control register */
++#define DRAW_STATUS   (CC_BASE + 0x04)        /* Drawing status register */
++
++/* CPU_CONTROL */
++#define SW_RESET      0x00000002UL    /* Reset all modules except CIF */
++#define MIU_READ_REQ  0x00000004UL    /* MIU read request */
++#define CLKRUN_ENABLE 0x00000008UL    /* CLKRUN enabled. On Pwr-on, disab. */
++
++/* DRAW_STATUS */
++#define CMD_FIFO_MASK 0x0000001fUL    /* Command FIFO entries mask */
++#define SRC_FIFO_MASK 0x00000f00UL    /* Source FIFO entry mask */
++#define GE_BUSY               0x00010000UL    /* Any command in Comm FIFO */
++#define CMD_FIFO_FULL 0x00000000UL    /* Cmd fifo full bit */
++#define CMD_FIFO_EMPTY        0x00000010UL    /* Cmd fifo empty, 16x32 bits free */
++#define SRC_FIFO_FULL 0x00000000UL    /* Src fifo full bit */
++#define SRC_FIFO_EMPTY        0x00000800UL    /* Src fifo empty, 8x128 bits free */
++
++#define CMD_FIFO_CNT          16      /* Command FIFO full entry */
++#define CMD_FIFO_MAX_INDEX    64
++#define SRC_FIFO_MAX_BYTES    128     /* max pixels in src fifo - 8bits */
++#define SRC_FIFO_MAX_WORDS    64      /* max pixels in src fifo - 16bits */
++#define SRC_FIFO_MAX_DWORDS   32      /* max dwords in src fifo - 32bits */
++
++/* MIU (Memory Interface Unit) Registers */
++#define MIU_CONTROL1  (MM_BASE + 0x00)        /* Memory interface control 1 */
++#define MIU_CONTROL2  (MM_BASE + 0x04)        /* Memory interface control 2 */
++#define MIU_CONTROL3  (MM_BASE + 0x08)        /* Memory interface control 3 */
++#define MIU_CONTROL4  (MM_BASE + 0x0C)        /* Memory interface control 4 */
++#define MIU_CONTROL5  (MM_BASE + 0x10)        /* Memory interface control 5 */
++
++/* MIU_CONTROL1 */
++#define MIU_ENABLE            0x00000001UL    /* Enable MIU */
++#define MIU_RESET_DISABLE     0x00000002UL    /* MIU reset is disabled */
++#define DRAM_RESET_DISABLE    0x00000004UL    /* DRAM reset is disabled */
++
++/* MIU_CONTROL2 */
++#define CLK_FROM_BUS     0x00000001UL /* Bus clk for mem clk src */
++#define CLK_FROM_PLL2    0x00000001UL /* PLL2 for mem clock source */
++#define MEM_REFRESH_ENABLE 0x00000002UL       /* Mem ref disab at pwr dw mod*/
++#define CPU_PB_ENABLE    0x00000004UL /* Page Break enab after CPU mem cyc */
++#define GC1_PB_ENABLE    0x00000008UL /* Page Break after GC1 mem cycles */
++#define GC2_PB_ENABLE    0x00000010UL /* Page Break after GC2 mem cycles */
++#define STN_R_PB_ENABLE          0x00000020UL /* Page Break after STN read mem cyc */
++#define STN_W_PB_ENABLE          0x00000040UL /* Page Break after STN wr. mem cyc */
++#define GE_PB_ENABLE     0x00000080UL /* Page Break after GE memory cycles */
++#define AUTO_REF_ENABLE          0x40000000UL /* Standby sig enab. when MIU active */
++#define       STANDBY_ENABLE     0x80000000UL /* Standby sig enab. when MIU active */
++
++/* MIU_CONTROL3 */
++#define DISPLAY_BURST2        0x00000000UL    /* Burst size for disp mem refresh */
++#define DISPLAY_BURST4        0x00000001UL
++#define DISPLAY_BURST6        0x00000002UL
++#define DISPLAY_BURST8        0x00000003UL
++#define STN_R_BURST2  0x00000000UL    /* Burst size for STN read mem cycle */
++#define STN_R_BURST4  0x00000004UL
++#define STN_R_BURST6  0x00000008UL
++#define STN_R_BURST8  0x0000000cUL
++#define STN_W_BURST2  0x00000000UL    /* Burst size for STN write mem cyc */
++#define STN_W_BURST4  0x00000010UL
++#define STN_W_BURST6  0x00000020UL
++#define STN_W_BURST8  0x00000030UL
++#define GE_RW_BURST2  0x00000000UL    /* Burst size for GE r/w mem cycle */
++#define GE_RW_BURST4  0x00000040UL
++#define GE_RW_BURST6  0x00000080UL
++#define GE_RW_BURST8  0x000000c0UL
++#define CPU_RW_BURST2 0x00000000UL    /* Burst size for CPU r/w mem cycle */
++#define CPU_RW_BURST4 0x00000100UL
++#define CPU_RW_BURST6 0x00000200UL
++#define CPU_RW_BURST8 0x00000300UL
++
++/* MIU_CONTROL4 */
++#define R_LATENCY_REQUEST  0x00000001UL       /* Read Latency Request */
++
++/* MIU_CONTROL5 */
++#define       LATENCY_1          0x00000001UL /* EDRAM Latency 1 */
++#define       LATENCY_2          0x00000005UL /* EDRAM Latency 2 */
++#define       LATENCY_3          0x00000007UL /* EDRAM Latency 3 */
++#define       DUMMY_IN_COMMANDS  0x00000008UL /* Dummy cycle insertion betw cmds */
++#define       DUMMY_IN_PRECHARGE 0x00000010UL /* Dummy cyc between precharge cyc  */
++#define DELAY_1ns        0x00000000UL /* Internal memory clock delay */
++#define       ACT_TO_CLOSE_3     0x00000100UL /* Bank activate to close - 3 mclk */
++#define       ACT_TO_CLOSE_4     0x00000200UL /* Bank activate to close - 4 mclk */
++#define       ACT_TO_CLOSE_5     0x00000300UL /* Bank activate to close - 5 mclk */
++#define       ACT_TO_COMMAND_2   0x00000000UL /* Bank activate to cmd r/w - 2 mclk */
++#define       ACT_TO_COMMAND_3   0x00000400UL /* Bank activate to cmd r/w - 3 mclk */
++#define       CLOSE_TO_ACT_2     0x00000000UL /* Bank close to activate - 2 mclk */
++#define       CLOSE_TO_ACT_3     0x00000800UL /* Bank close to activate - 3 mclk */
++#define       ROW_CYCLE_6        0x00000000UL /* Row Cycle time - 6 memory clock */
++#define       ROW_CYCLE_8        0x00001000UL /* Row Cycle time - 8 memory clock */
++#define       DELAY_R_CLOCK_0_0  0x00000000UL /* Delay for read clock - no delay */
++#define       DELAY_R_CLOCK_0_5  0x00010000UL /* Delay for read clock - 0.5ns */
++#define       DELAY_R_CLOCK_1_0  0x00020000UL /* Delay for read clock - 1.0ns */
++#define       DELAY_R_CLOCK_1_5  0x00030000UL /* Delay for read clock - 1.5ns */
++#define       DELAY_M_CLOCK_0_0  0x00000000UL /* Delay for memory clock - no delay */
++#define       DELAY_M_CLOCK_0_5  0x00020000UL /* Delay for memory clock - 0.5ns */
++#define       DELAY_M_CLOCK_1_0  0x00080000UL /* Delay for memory clock - 1.0ns */
++#define       DELAY_M_CLOCK_1_5  0x000c0000UL /* Delay for memory clock - 1.5ns */
++
++
++/*
++ * Data structure and defines for MQ chip and driver interface
++ */
++
++/* Display configuration structure - for interface */
++typedef struct DisplayConfig
++{
++    int       x;                      /* x resolution */
++    int       y;                      /* y resolution */
++    int       bpp;                    /* color depth */
++    int       refresh;                /* CRT refresh rate */
++    int stride;                       /* memory stride */
++    unsigned long flag;               /* display flag */
++} DISPLAY_CONFIG, *PDISPLAY_CONFIG;
++
++/* Flag definition */
++#define PANEL_TYPE_MASK               0x000000ff      /* Panel type mask */
++#define PROCESSOR_MASK                0x00003f00      /* Mask of processor type */
++#define IS_SH3                        0x00000000 
++#define IS_SH4                        0x00000100
++#define IS_NEC                        0x00000200
++#define IS_SARM                       0x00000300
++#define IS_TOSHIBA            0x00000400
++#define IS_PCI                        0x00000500
++#define LCD_ON                        0x00010000      /* LCD mode */
++#define CRT_ON                        0x00020000      /* CRT mode */
++#define LARGE_DESKTOP           0x00040000    /* Large desktop mode is on */
++#define INDEP_DISPLAY         0x00080000      /* Independent display */
++#define SAME_IMAGE            0x00100000      /* Use 2 GC but same image */
++#define USE_2GCs              0x001C0000      /* 2 GCs are used */
++#define USE_2GCs_MASK         0x001C0000      /* mask for 2 GCs */
++#define ENA_HW_CURSOR         0x00200000      /* Enable hw cursor */
++#define HORI_LCD_CRT          0x00000000      /* QView hori arrangement */
++#define HORI_CRT_LCD          0x10000000      /* QView hori arrangement */
++#define VERT_CRT_LCD          0x20000000      /* QView vert arrangement */
++#define VERT_LCD_CRT          0x30000000      /* QView vert arrangement */
++#define LCDCRT_POS_MASK               0x30000000      /* mask for QV orientation */
++
++/* Display timing structure */
++typedef struct DisplayTiming
++{
++    int       x;              /* x resolution */
++    int       y;              /* y resolution */
++    int       refresh;        /* refresh rate */
++    unsigned long hd;       /* hori display control */
++    unsigned long vd; /* vert display control */
++    unsigned long hs; /* hori sync control */
++    unsigned long vs; /* vert sync control */
++    unsigned long crtc;       /* crt control */
++    unsigned long pll;        /* PLL2 or PLL3 setting */
++} DISPLAY_TIMING, *PDISPLAY_TIMING;
++
++/* Flat panel register */
++typedef struct FPControl
++{
++    int       x;                      /* panel size x */
++    int       y;                      /* panel size y */
++    int       freq;                   /* panel freq */
++    unsigned long fpControl;        /* flat panel control */
++    unsigned long fpPinControl;     /* flat panel pin control */
++    unsigned long stnControl;       /* stn panel control */
++} FPDATA_CONTROL, *PFPDATA_CONTROL;
++
++/* Frame rate control */
++#define FRC_PATTERN_CNT               32
++#define FRC_WEIGHT_CNT                8
++typedef struct FRCControl
++{
++    ULONG frcPattern[FRC_PATTERN_CNT];        /* FRC pattern control */
++    ULONG frcWeight[FRC_WEIGHT_CNT];  /* FRC weight control */
++} FRC_CONTROL, *PFRC_CONTROL;
++
++/* Miscellaneous defines */
++#define IS_GC1                        1
++#define IS_GC2                        2
++
++/* Turn on/off display */
++#define ENABLE_LCD_GC1                0
++#define ENABLE_LCD_GC2                1
++#define DISABLE_LCD_GC1               2
++#define DISABLE_LCD_GC2               3
++#define ENABLE_CRT_GC1                4
++#define ENABLE_CRT_GC2                5
++#define DISABLE_CRT_GC1               6
++#define DISABLE_CRT_GC2               7
++
++/*
++ * Handy macro
++ *
++ */
++/*
++#define CHECK_IF_STATE_D(s)   {\
++      unsigned long ulState = (s);\
++      unsigned long ulPMReg;\
++      while(1)\
++      {\
++      ulPMReg = READ32(PCI_PM_CNTL_STATUS);\
++      if((ulPMReg &0x03) == ulState)\
++              break;\
++      } }
++*/
++#endif /* _VIDEO_MQ200_MQ2HW_H */
++
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mqdata.h
+@@ -0,0 +1,754 @@
++/***************************************************************************
++ MQDATA.C
++
++ This file contains timing and flat panel parameters for MediaQ graphics
++ chip.
++
++ Copyright (c) 2000 by MediaQ, Incorporated.
++ All Rights Reserved.
++ 
++***************************************************************************/
++#ifndef _VIDEO_MQ200_MQDATA_H
++#define _VIDEO_MQ200_MQDATA_H
++/* LCD/CRT timing parameters for each resolution - VESA modes 
++ *
++ * . the first entry is reserved to provide customized panel timing support
++ * . OEM can fill in proper timing for non-VESA LCD (or CRT)
++ *
++ */
++DISPLAY_TIMING TimingParam[] =
++{
++    {  /* customized refresh rate - reserved for non-VESA panel timing */
++      0,0,0,                          /* X/Y/Freq */
++      0,                              /* HD Total + HD End */
++      0,                              /* VD Total + VD End */
++      0,                              /* HS Start + HS End */
++      0,                              /* VS Start + VS End */
++      0x00000000,                     /* CRT control */
++      0x00000000,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 640 x 240 90Hz (25.175 MHz) */
++      640,240,90,                     /* X/Y/Freq */
++      (793-2) | (640L << 16),         /* HD Total + HD End */
++      (262-1) | ((240L-1) << 16),     /* VD Total + VD End */
++      647 | (704L << 16),             /* HS Start + HS End */
++      245 | (246L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00a30930,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 640 x 480 60Hz (25.175 MHz) */
++      640,480,60,                     /* X/Y/Freq */
++      (800-2) | (640L << 16),         /* HD Total + HD End */
++      (525-1) | ((480L-1) << 16),     /* VD Total + VD End */
++      656 | (752L << 16),             /* HS Start + HS End */
++      490 | (492L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00a30930,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 640 x 480 72Hz (31.5 MHz) */
++      640,480,72,                     /* X/Y/Freq */
++      (832-2) | (640L << 16),         /* HD Total + HD End */
++      (520-1) | ((480L-1) << 16),     /* VD Total + VD End */
++      688 | (728L << 16),             /* HS Start + HS End */
++      489 | (492L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00f50b30,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 640 x 480 75Hz (31.5 MHz) */
++      640,480,75,                     /* X/Y/Freq */
++      (840-2) | (640L << 16),         /* HD Total + HD End */
++      (500-1) | ((480L-1) << 16),     /* VD Total + VD End */
++      680 | (744L << 16),             /* HS Start + HS End */
++      481 | (484L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00f50b30,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 640 x 480 85Hz (36 MHz) */
++      640,480,85,                     /* X/Y/Freq */
++      (832-2) | (640L << 16),         /* HD Total + HD End */
++      (509-1) | ((480L-1) << 16),     /* VD Total + VD End */
++      696 | (752L << 16),             /* HS Start + HS End */
++      481 | (484L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00d20830,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 800 x 600 60Hz (40 MHz) */
++      800,600,60,                     /* X/Y/Freq */
++      (1056-2) | (800L << 16),        /* HD Total + HD End */
++      (628-1) | ((600L-1) << 16),     /* VD Total + VD End */
++      839 | (967L << 16),             /* HS Start + HS End */
++      601 | (605L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x00e90830,                     /* PLLx multiplier and control */
++    },
++   
++    {  /* 800 x 600 72Hz 50 MHz) */
++      800,600,72,                     /* X/Y/Freq */
++      (1040-2) | (800L << 16),        /* HD Total + HD End */
++      (666-1) | ((600L-1) << 16),     /* VD Total + VD End */
++      856 | (976L << 16),             /* HS Start + HS End */
++      637 | (643L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x00b20a20,                     /* PLLx multiplier and control */
++    },
++   
++    {  /* 800 x 600 75Hz (49.5 MHz) */
++      800,600,75,                     /* X/Y/Freq */
++      (1056-2) | (800L << 16),        /* HD Total + HD End */
++      (625-1) | ((600L-1) << 16),     /* VD Total + VD End */
++      816 | (896L << 16),             /* HS Start + HS End */
++      601 | (604L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x00900820,                     /* PLLx multiplier and control */
++    },
++   
++    {  /* 800 x 600 85Hz (56.25 MHz) */
++      800,600,85,                     /* X/Y/Freq */
++      (1047-2) | (800L << 16),        /* HD Total + HD End */
++      (631-1) | ((600L-1) << 16),     /* VD Total + VD End */
++      832 | (896L << 16),             /* HS Start + HS End */
++      601 | (604L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x00b60920,                     /* PLLx multiplier and control */
++    },
++   
++    {  /* 1024 x 768 60Hz (65 MHz) */
++      1024,768,60,                    /* X/Y/Freq */
++      (1344-2) | (1024L << 16),       /* HD Total + HD End */
++      (806-1) | ((768L-1) << 16),     /* VD Total + VD End */
++      1048 | (1184L << 16),           /* HS Start + HS End */
++      771 | (777L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00fd0b20,                     /* PLLx multiplier and control */
++    },                        
++
++    {  /* 1024 x 768 70Hz (75 MHz) */
++      1024,768,70,                    /* X/Y/Freq */
++      (1327-2) | (1024L << 16),       /* HD Total + HD End */
++      (806-1) | ((768L-1) << 16),     /* VD Total + VD End */
++      1047 | (1183L << 16),           /* HS Start + HS End */
++      771 | (777L << 16),             /* VS Start + VS End */
++      HSYNC_POLARITY_LOW|VSYNC_POLARITY_LOW|BLANK_PED_ENABLE,
++                                        /* CRT control */
++      0x00f30920,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 1024 x 768 75Hz (78.750 MHz) */
++      1024,768,75,                    /* X/Y/Freq */
++      (1312-2) | (1024L << 16),       /* HD Total + HD End */
++      (806-1) | ((768L-1) << 16),     /* VD Total + VD End */
++      1040 | (1136L << 16),           /* HS Start + HS End */
++      769 | (772L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x00cc0720,                     /* PLLx multiplier and control */
++    },
++
++    {  /* 1024 x 768 85Hz (94.5 MHz) */
++      1024,768,85,                    /* X/Y/Freq */
++      (1375-2) | (1024L << 16),       /* HD Total + HD End */
++      (808-1) | ((768L-1) << 16),     /* VD Total + VD End */
++      1072 | (1168L << 16),           /* HS Start + HS End */
++      769 | (772L << 16),             /* VS Start + VS End */
++      BLANK_PED_ENABLE,               /* CRT control */
++      0x007a0710,                     /* PLLx multiplier and control */
++    }
++};       
++#define MAX_MQMODE    (sizeof(TimingParam) / sizeof(TimingParam[0]))
++
++/* Flat panel control registers
++ */
++FPDATA_CONTROL fpControlData[] =
++{
++   /* Type 0 : OEM Specific panel
++    */
++    { /* Flat panel info */
++      0, 0, 0,
++
++      /* Flat panel Control */
++      0
++      ,
++
++      /* Flat panel pin control */
++      0
++      ,
++
++      /* STN panel control */
++      0x0
++    },
++   /* Type 1 : SSTN VGA 8Bit Color - 72Hz
++    * - Sanyo SSTN 640x480 8-bit color interface
++    */
++    { /* Flat panel info */
++      640, 480, 72,
++
++      /* Flat panel Control */
++      FP_TYPE_SSTN
++      | FP_COLOR
++      | SSTN_8BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x00400000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0000
++    },
++
++    /* Type 2 : DSTN 16 Bit VGA Color - 72Hz
++     * - Hitachi 8.2" SX21V001
++     * - Sanyo 10.4" LM-CJ53-22NTK
++     * - Sharp 10.4" LM64C35P
++     */
++    { /* Flat panel info */
++      640, 480, 72,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 3 : TFT 18 Bit VGA - 60Hz
++    * - NEC 10.4" NL6448AC33-24
++    */
++    { /* Flat panel info */
++      640, 480, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_TFT
++      | FP_COLOR
++      | TFT_18BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_6BITS 
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++    /* Type 4 : TFT 18 Bit SVGA - 60Hz
++     * - Hitachi 12.1" 800x600 TX31D24VC1CAA
++     */
++    { /* Flat panel info */
++      800, 600, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_TFT
++      | FP_COLOR
++      | TFT_18BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_6BITS 
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 5 : DSTN 16Bit SVGA Color Panel - 72Hz
++    * - Hitachi 10.0" SX25S001
++    * - Hitachi 12.1" SX25S003
++    */
++    { /* Flat panel info */
++      800, 600, 72,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 6 : DSTN 8 Bit VGA Color - 72Hz
++    */
++    { /* Flat panel info */
++      640, 480, 72,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_8BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 7 : SSTN VGA 16Bit Color - 72Hz
++    */        
++    { /* Flat panel info */
++      640, 480, 72,
++
++      /* Flat panel Control */
++      FP_TYPE_SSTN
++      | FP_COLOR
++      | SSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x00400000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0000
++    },
++
++    /* Type 8 : SSTN VGA 8Bit Color - 60Hz
++     * - Sanyo SSTN 640x480 8-bit color interface
++     */
++    { /* Flat panel info */
++      640, 480, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_SSTN
++      | FP_COLOR
++      | SSTN_8BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x00400000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0000
++    },
++
++   /* Type 9 : DSTN 16 Bit VGA Color - 60Hz
++    * - Hitachi 8.2" SX21V001
++    * - Sanyo 10.4" LM-CJ53-22NTK
++    * - Sharp 10.4" LM64C35P
++    */
++    { /* Flat panel info */
++      640, 480, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_1
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 10 : DSTN 16Bit SVGA Color Panel - 60Hz
++    * - Hitachi 10.0" SX25S001
++    * - Hitachi 12.1" SX25S003
++    * - Sanyo LM-FC53-22NTK
++    */
++    { /* Flat panel info */
++      800, 600, 60,
++
++      /* Flat panel Control */
++#if 1
++      0x0C1B4128
++#else
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0C840000            
++#endif
++        ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 11 : DSTN 24Bit XGA Color Panel - 60Hz
++    * - Hitachi 12.1" SX25S003
++    */
++    { /* Flat panel info */
++      1024, 768, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_24BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 12 : DSTN 16Bit XGA Color Panel - 60Hz
++    * - Hitachi 12.1" SX25S003
++    */
++    { /* Flat panel info */
++      1024, 768, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_DSTN
++      | FP_COLOR
++      | DSTN_16BITS_MONOCLR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x0c840000
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++    /* Type 13 : TFT 18Bit XGA - 60Hz
++    * - Hitachi 12.1" 800x600 TX31D24VC1CAA
++    */
++    { /* Flat panel info */
++      1024, 768, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_TFT
++      | FP_COLOR
++      | TFT_18BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_6BITS 
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | FHSYNC_ACTIVE_L
++      | FVSYNC_ACTIVE_L
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++    /* Type 14 : TFT 24Bit XGA - 60Hz
++    * - Hitachi 12.1" 800x600 TX31D24VC1CAA
++    */
++    { /* Flat panel info */
++      1024, 768, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_TFT
++      | FP_COLOR
++      | TFT_24BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_6BITS 
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | FHSYNC_ACTIVE_L
++      | FVSYNC_ACTIVE_L
++      ,
++
++      /* STN panel control */
++      0x00bd0001
++    },
++
++   /* Type 15 : TFT 18 Bit SVGA - 60Hz (Similar to type 4)
++    * - NEC 12.1" 800x600 TX31D24VC1CAA
++    */
++    { /* Flat panel control */
++      800, 600, 60,
++
++      /* Flat panel Control */
++      FP_TYPE_TFT
++      | FP_COLOR
++      | TFT_18BITS_COLOR
++      | DITHER_PATTERN_3
++      | DITHER_BASE_6BITS 
++      ,
++
++      /* Flat panel pin control */
++      FSCLK_OUTPUT_ENABLE
++      | FHSYNC_ACTIVE_L
++      | FVSYNC_ACTIVE_L
++      | FSCLK_ACTIVE_L
++      | FSCLK_DELAY
++      ,
++      /* STN panel control */
++      0x00bd0001
++    },
++      
++    /* Type 16 : SSTN VGA 8Bit Color - 90Hz
++    * - Sharp LM8M64 SSTN 640x240 8-bit color interface
++    */
++    { /* Flat panel control */
++      640, 240, 90,
++
++      FP_TYPE_SSTN
++      | FP_COLOR
++      | SSTN_8BITS_MONOCLR
++      | DITHER_PATTERN_1
++      | DITHER_BASE_4BITS
++      | FRC_16LEVEL
++      | 0x00400000
++      ,
++
++      FSCLK_OUTPUT_ENABLE
++      | SCLK_MASK
++      | FDE_ACTIVE_L  
++      ,
++
++      /* STN panel control */
++      0x00bd0000
++    }
++};
++
++/* Flat panel FRC weight/pattern registers - for SSTN and DSTN panel only
++ *
++ */
++FRC_CONTROL FRCControlData[] =
++{
++    {
++        {
++      0x97A4C5F8,
++      0x61E3DB02,
++      0xD3E081BC,
++      0x25A79F46,
++      0x5B680934,
++      0xAD2F17CE,
++      0x1F2C4D70,
++      0xE96B538A,
++      0x0E3D5C61,
++      0xF87A429B,
++      0x4A791825,
++      0xBC3E06DF,
++      0xC2F190AD,
++      0x34B68E57,
++      0x86B5D4E9,
++      0x70F2CA13,
++      0xF1C2A39E,
++      0x0785BD64,
++      0xB586E7DA,
++      0x43C1F920,
++      0x3D0E6F52,
++      0xCB4971A8,
++      0x794A2B16,
++      0x8F0D35EC,
++      0x685B3A07,
++      0x9E1C24FD,
++      0x2C1F7E43,
++      0xDA5860B9,
++      0xA497F6CB,
++      0x52D0E831,
++      0xE0D3B28F,
++      0x1694AC75,
++        },
++
++        {
++      /* FRC weight data */
++      0x80800000,
++      0x88888420,
++      0x94a49248,
++      0xaaaaaa54,
++      0x6b5b55ab,
++      0x77776db7,
++      0x7f7f7bdf,
++      0xffff7fff
++        }
++    },
++
++    /* FRC Pattern Data - 2FCA (for SSTN) */
++    {
++        {
++      0x97A4C5F8,
++      0x61E3DB02,
++      0xD3E081BC,
++      0x25A79F46,
++      0x4A791825,
++      0xBC3E06DF,
++      0x0E3D5C61,
++      0xF87A429B,
++      0xF1C2A39E,
++      0x0785BD64,
++      0xB586E7DA,
++      0x43C1F920,
++      0x2C1F7E43,
++      0xDA5860B9,
++      0x685B3A07,
++      0x9E1C24FD,
++      0xE0D3B28F,
++      0x1694AC75,
++      0xA497F6CB,
++      0x52D0E831,
++      0x3D0E6F52,
++      0xCB4971A8,
++      0x794A2B16,
++      0x8F0D35EC,
++      0x86B5D4E9,
++      0x70F2CA13,
++      0xC2F190AD,
++      0x34B68E57,
++      0x5B680934,
++      0xAD2F17CE,
++      0x1F2C4D70,
++      0xE96B538A,
++        },
++
++        {
++      /* FRC weight data */
++      0x80800000,
++      0x88888420,
++      0x94a49248,
++      0xaaaaaa54,
++      0x6b5b55ab,
++      0x77776db7,
++      0x7f7f7bdf,
++      0xffff7fff
++        }
++    },
++    
++    /* FRC Pattern Data - (for Panletype 4-> Simpad) */
++    {
++      {
++      0x97A4C5F8, 
++      0x61E3DB02,
++      0x3D0E6F52,
++      0xCB4971A8,
++      0x794A2B16,
++      0x8F0D35EC,
++      0xD3E081BC,
++      0x25A79F46,
++      0x1F2C4D70,
++      0xE96B538A,
++      0xB586E7DA,
++      0x43C1F920,
++      0xF1C2A39E,
++      0x0785BD64,
++      0x5B680934,
++      0xAD2F17CE,
++      0x794A2B16,
++      0x8F0D35EC,
++      0xD3E081BC,
++      0x25A79F46,
++      0x97A4C5F8,
++      0x61E3DB02,
++      0x3D0E6F52,
++      0xCB4971A8,
++      0xF1C2A39E,
++      0x0785BD64,
++      0x5B680934,
++      0xAD2F17CE,
++      0x1F2C4D70,
++      0xE96B538A,
++      0xB586E7DA,
++      0x43C1F920,
++      },
++      {
++      /* FRC weight data */
++      0x80800000,
++      0x88888420,
++      0x94a49248,
++      0xaaaaaa54,
++      0x6b5b55ab,
++      0x77776db7,
++      0x7f7f7bdf,
++      0xffff7fff
++      }
++    }
++};
++#endif /* _VIDEO_MQ200_MQDATA_H */
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mqmacros.h
+@@ -0,0 +1,86 @@
++#ifndef _VIDEO_MQ200_MQMACROS_H
++#define _VIDEO_MQ200_MQMACROS_H
++
++#ifdef CHECK_SRCFIFO
++
++#define PUMP_PACKED_SRCFIFO(pSrcData,srcStride,nDwords,height,extras) \
++      { \
++              u32 *pData; \
++              u32     i,j; \
++              while( height-- ) \
++              { \
++                      pData = (u32 *)((u32)(pSrcData + 3UL) & ~0x03UL); \
++                      j = nDwords; \
++                      while ( j >= SRC_FIFO_MAX_DWORDS ) \
++                      { \
++                              geWAITSRCFIFO( SRC_FIFO_MAX_DWORDS ); \
++                              for ( i = 0; i < SRC_FIFO_MAX_DWORDS; i++ ) \
++                                      geREG(SRC_IMAGE_DATA, *pData++); \
++                              j -= SRC_FIFO_MAX_DWORDS; \
++                      } \
++                      geWAITSRCFIFO( j ); \
++                      while( j-- ) \
++                              geREG(SRC_IMAGE_DATA, *pData++);  \
++                      pSrcData += srcStride; \
++              } \
++              geWAITSRCFIFO( extras ); \
++              while( extras-- ) \
++                      geREG(SRC_IMAGE_DATA, 0UL); \
++      } 
++
++
++#define PUMP_REAL_PACKED_SRCFIFO(pSrcData,nDwords,extras) \
++      { \
++              u32 *pData =(u32 *)pSrcData; \
++              u32 i; \
++              while(nDwords) \
++              { \
++                   if (nDwords >= SRC_FIFO_MAX_DWORDS) \
++                   { \
++                      geWAITSRCFIFO( SRC_FIFO_MAX_DWORDS ); \
++                      for (i = SRC_FIFO_MAX_DWORDS; i > 0; i--) \
++                              geREG(SRC_IMAGE_DATA, *pData++); \
++                        nDwords -= SRC_FIFO_MAX_DWORDS; \
++                   } \
++                   else \
++                   { \
++                      geWAITSRCFIFO( nDwords ); \
++                      for (i = nDwords; i > 0; i--) \
++                              geREG(SRC_IMAGE_DATA, *pData++); \
++                        nDwords -= nDwords; \
++                   } \
++              } \
++                geWAITSRCFIFO(extras); \
++              while( extras-- ) \
++                   geREG(SRC_IMAGE_DATA, 0UL); \
++      } 
++
++#else /* CHECK_SRCFIFO */
++
++#define PUMP_PACKED_SRCFIFO(pSrcData,srcStride,nDwords,height,extras) \
++      { \
++              u32 *pData; \
++              u32     i; \
++              while( height-- ) \
++              { \
++                      pData = (u32 *)((u32)(pSrcData + 3UL) & ~0x03UL); \
++                      for ( i = 0; i < nDwords; i++ ) \
++                              geREG(SRC_IMAGE_DATA, *pData++); \
++                      pSrcData += srcStride; \
++              } \
++              while( extras-- ) \
++                      geREG(SRC_IMAGE_DATA, 0UL); \
++      }
++
++#define PUMP_REAL_PACKED_SRCFIFO(pSrcData,nDwords,extras) \
++      { \
++              u32 *pData =(u32 *)pSrcData; \
++              while(nDwords--) \
++                   geREG(SRC_IMAGE_DATA, *pData++); \
++              while( extras-- ) \
++                   geREG(SRC_IMAGE_DATA, 0UL); \
++      }       
++
++#endif
++
++#endif /* _VIDEO_MQ200_MQMACROS_H */
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mqplat.h
+@@ -0,0 +1,106 @@
++/***************************************************************************
++ MQPLAT.H
++
++ MQ200 platform, system or OS specific header file
++
++ Copyright (c) 2000 by MediaQ, Incorporated.
++ All Rights Reserved.
++
++***************************************************************************/
++#ifndef _VIDEO_MQ200_MQPLAT_H
++#define _VIDEO_MQ200_MQPLAT_H
++
++/* OS variation - ONLY define one */
++#undef  MQ_VXWORKS
++#undef        MQ_EPOC32 
++#undef        MQ_WINCE
++#undef        MQ_OS9
++#define MQ_LINUX
++
++/* CPU variation - ONLY define one */
++#undef  MQ_X86
++#define       MQ_SA1110
++#undef        MQ_MIPS_NEC
++#undef        MQ_MIPS_TOSHIBA
++#undef        MQ_SH4
++
++/* PCI support - undef accordingly */
++#undef MQ_PCI
++#ifdef MQ_PCI
++ #warning "MQ200 driver compiled for PCI !"
++#endif
++
++/* Derived equates from CPU type */
++#ifdef        MQ_SHx
++ #warning "MQ200 driver compiled for SHx !"
++ #define FB_BASE      0x93800000L             /* MQ200 frame buffer adddr */
++#endif
++
++#ifdef        MQ_MIPS_NEC
++ #warning "MQ200 driver compiled for NEC MIPS !"
++ #define CHECK_FIFO_REQUIRED  /* GE fifo checking required */
++ #define FB_BASE      0xAA000000L
++ 
++ #ifdef  MQ_PCI
++ #define IOREGS_BASE  0xAF000000L             /* for VR4122 */
++ #define IOREGS_SIZE  0x00002000L             
++ #else
++ #define IOREGS_BASE  0xAB000000L             /* for VR 4111/21 */
++ #define IOREGS_SIZE  0x00001000L
++ #define CHECK_NOTBUSY                          /* Needed for NEC MIPS */
++ #define CHECK_CMDFIFO                          /* Needed for NEC MIPS */
++ #endif /* MQ_PCI */
++#endif
++
++#ifdef        MQ_MIPS_TOSHIBA
++ #warning "MQ200 driver compiled for TOSHIBA MIPS !"
++ #define FB_BASE      0x6D800000L
++#endif
++
++#ifdef        MQ_SA1110
++ #warning "MQ200 driver compiled for Intel SA1110 !"
++ #define FB_BASE_CS3  0x1b800000L             /* configured as CS3 */
++ #define FB_BASE_CS4  0x43800000L             /* configured as CS4 */
++#define FB_BASE_CS5   0x4b800000L             /* configured as CS5 */
++#define FB_BASE               FB_BASE_CS5             /* change accordingly! */
++#define REGISTER_BASE   0x4be00000L 
++#endif
++
++/* OS-derived misc defines */
++#ifdef MQ_VXWORKS
++ #warning "MQ200 driver compiled for VxWorks !"
++ #define MQ_DELAY(cnt) taskDelay(cnt*30);
++ #define MQ_COLOR_RGB                         /* Color (32bit):ARGB */
++#endif
++
++#ifdef MQ_WINCE
++ #warning "MQ200 driver compiled for Window CE !"
++ #define MQ_DELAY(cnt) Sleep(cnt)
++#endif
++
++#ifdef MQ_LINUX
++ #warning "MQ200 driver compiled for Linux !"
++/* #define MQ_DELAY(cnt) {volatile int delay; for (delay=0; delay<10000*cnt; delay++); } */
++
++#define MQ_DELAY(cnt) mdelay(cnt * 10)
++#endif
++
++/* Further derivation
++#ifdef MQ_COLOR_RGB
++ #define GETR(color)          (unsigned char)(color >> 16)
++ #define GETG(color)          (unsigned char)((unsigned short)(color) >> 8)
++ #define GETB(color)          (unsigned char)(color)
++ #define MAKERGB(r,g,b)               ((unsigned long)(((unsigned char)(r)|\
++                              ((unsigned short)((unsigned char)(g))<<8))|\
++                              (((unsigned long)(unsigned char)(b))<<16)))
++#endif
++*/
++
++/* Cursor color */
++#ifndef CURSOR_FGCLR
++ #define CURSOR_FGCLR         0x0000FFFF              /* Yellow */
++#endif
++#ifndef CURSOR_BGCLR
++ #define CURSOR_BGCLR         0x00000000              /* Black */
++#endif
++#endif /* _VIDEO_MQ200_MQPLAT_H */
+--- /dev/null
++++ linux-2.4.27/include/video/MQ200/mqproto.h
+@@ -0,0 +1,19 @@
++/***************************************************************************
++ MQPROTO.H
++
++ MQ200 common function prototypes
++
++ Copyright (c) 2000 by MediaQ, Incorporated.
++ All Rights Reserved.
++
++***************************************************************************/
++#ifndef MQPROTO_H
++#define MQPROTO_H
++
++/*
++ * mqdata.h
++ */
++extern FPDATA_CONTROL fpControlData[];
++extern DISPLAY_TIMING TimingParam[];
++
++#endif /* MQPROTO_H */
+--- linux-2.4.27/init/do_mounts.c~2.4.27-vrs1-pxa1-jpm1
++++ linux-2.4.27/init/do_mounts.c
+@@ -789,7 +789,13 @@
+                       return;
+               }
+               printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
++#ifdef CONFIG_SA1100_SIMPAD
++              /* no floppy -> cramfs */
++              printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying cramfs.\n");
++              ROOT_DEV = MKDEV(31, 2);
++#else
+               ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0);
++#endif
+       }
+ #endif
+       devfs_make_root(root_device_name);
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1-pxa1.patch
new file mode 100644 (file)
index 0000000..7c1252e
--- /dev/null
@@ -0,0 +1,40457 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/Documentation/Configure.help~2.4.27-vrs1-pxa1
++++ linux-2.4.27/Documentation/Configure.help
+@@ -4630,6 +4630,24 @@
+   building a kernel for install/rescue disks or your system is very
+   limited in memory.
++Kernel Execute-In-Place from ROM
++CONFIG_XIP_KERNEL
++  Execute-In-Place allows the kernel to run directly from 
++  non-volatile storage, such as flash. This saves RAM space since
++  the text section of the kernel is not loaded from flash to
++  RAM.  Read-write sections, such as the data section and stack,
++  are still copied to RAM.  The XIP kernel is not compressed since it
++  has to run directly from flash, so it will take more space to store 
++  it.  The flash address where the kernel is linked to run from and 
++  is stored is board dependent. Therefore, if you say Y, you must 
++  know the proper physical address where to store the kernel image.
++
++  Also note that the make target becomes "make xipImage" rather than
++  "make zImage" or "make Image".  The final kernel binary to put in
++  ROM memory will be arch/arm/boot/xipImage.
++
++  If unsure, say N.
++
+ # Choice: kcore
+ Kernel core (/proc/kcore) format
+ CONFIG_KCORE_ELF
+@@ -13609,6 +13627,30 @@
+   If you don't know what this all is, saying Y is a safe choice.
++Workaround for XScale cache errata
++CONFIG_XSCALE_CACHE_ERRATA
++  There are couple errata that say that the cache may get confused
++  whether some cache lines are dirty or not, resulting in some memory
++  corruptions. The workaround (using the cache only in write through
++  mode) is performance impairing, and the bug _might_ just not be
++  that visible or critical to you depending on many esoteric
++  hardware factors.
++  
++  Not using the workaround makes Linux unreliable. If you're used
++  to some other OSes which requires to be rebooted once in a while
++  then this won't look so bad to you. On the other hand you may
++  stress test the system for hours without seeing any effect of this
++  bug.
++  
++  So this is configurable. Let's hope a future core revision will tell
++  this was just a bad dream. But in the mean time the risk and
++  trade-off is yours to decide.
++  
++  This should apply to all PXA250 up to rev B2 (erratum #120) and
++  possibly other current XScale cores as well.
++  
++  If you don't know what to answer, say Y.
++
+ Support CD-ROM drives that are not SCSI or IDE/ATAPI
+ CONFIG_CD_NO_IDESCSI
+   If you have a CD-ROM drive that is neither SCSI nor IDE/ATAPI, say Y
+@@ -16812,6 +16854,40 @@
+   If unsure, say N.
++Use linear addressing for cramfs
++CONFIG_CRAMFS_LINEAR
++  This option tells the cramfs driver to load data directly from a linear
++  adressed memory range (usually non volatile memory like flash) instead
++  of going through the block device layer.  This saves some memory since
++  no intermediate buffering is necessary.
++
++  This is also a prerequisite for XIP of binaries stored on the filesystem.
++
++  The location of the cramfs image in memory is board dependent. Therefore,
++  if you say Y, you must know the proper physical address where to store
++  the cramfs image and specify it using the physaddr=0x******** mount
++  option (for example: "mount -t cramfs -o physaddr=0x100000 none /mnt").
++
++  If unsure, say N.
++
++Support XIP on linear cramfs
++CONFIG_CRAMFS_LINEAR_XIP
++  You must say Y to this option if you want to be able to run applications
++  directly from non-volatile memory.  XIP applications are marked by
++  setting the sticky bit (ie, "chmod +t <app name>").  A cramfs file system
++  then needs to be created using mkcramfs (with XIP cramfs support
++  in it). Applications marked for XIP execution will not be compressed
++  since they have to run directly from flash.
++
++Root file system on linear cramfs
++CONFIG_ROOT_CRAMFS_LINEAR
++  Say Y if you have enabled linear cramfs, and you want to be able to use
++  the linear cramfs image as a root file system.  To actually have the
++  kernel mount this cramfs image as a root file system, you must also pass
++  the command line parameter "root=/dev/null rootflags=physaddr=0x********"
++  to the kernel (replace 0x******** with the physical address location
++  of the linear cramfs image to boot with).
++
+ CMS file system support
+ CONFIG_CMS_FS
+   Read only support for CMS minidisk file systems found on IBM
+--- /dev/null
++++ linux-2.4.27/Documentation/arm/XScale/PXA/USB-client
+@@ -0,0 +1,38 @@
++Date: Wed, 05 Jun 2002 13:38:53 -0700
++From: Frank Becker <fbecker@intrinsyc.com>
++To: Nicolas Pitre <nico@cam.org>
++Subject: [PATCH] PXA-USB
++
++Hi Nicolas,
++
++one more patch...
++
++This patch adds minimal USB client (UDC) support.
++
++Some notes:
++It adds just enough to get usb-eth working. I.e.
++endpoints 0-2, no dma. Performance isn't stellar
++partially due to UDC bug workarounds...
++(~350K @ 100Mhz, ~550K @ 200Mhz).
++
++Endpoint 1&2 have changed direction compared to
++the SA, so the host side requires a change to
++usbnet.c to flip endpoints (in:2/out:1 -> in:1/out:2).
++
++usb-eth and usb-char for PXA are almost identical
++to the SA versions, so they could probably be merged at
++one point. I made some minor changes to the eth driver
++to grab the usb resources at open, rather than at init
++and allow eth&char to be loaded at the same time.
++
++Stuart Lynne was working on his own USB client driver
++(and he was getting higher throughput than my driver).
++Assuming you guys have something in the oven for USB
++as well, there should be good selection for best of
++breed :)
++
++Cheers,
++Frank.
++-- 
++Frank Becker - Intrinsyc Software, Inc. - http://www.intrinsyc.com/
++Need a break? http://criticalmass.sf.net/
+--- linux-2.4.27/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/Makefile
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 4
+ SUBLEVEL = 27
+-EXTRAVERSION = -vrs1
++EXTRAVERSION =-vrs1-pxa1
+ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+@@ -167,6 +167,7 @@
+ DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+ DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
+ DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o
++DRIVERS-$(CONFIG_MMC) += drivers/mmc/mmcdrivers.o
+ DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
+ DRIVERS-$(CONFIG_NET_PCMCIA) += drivers/net/pcmcia/pcmcia_net.o
+ DRIVERS-$(CONFIG_NET_WIRELESS) += drivers/net/wireless/wireless_net.o
+--- linux-2.4.27/arch/arm/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/Makefile
+@@ -13,10 +13,11 @@
+ CFLAGS                +=-Uarm -fno-common -pipe
+ ifeq ($(CONFIG_FRAME_POINTER),y)
+-CFLAGS                :=$(CFLAGS:-fomit-frame-pointer=-mapcs -mno-sched-prolog)
++CFLAGS                :=$(CFLAGS:-fomit-frame-pointer=)
++CFLAGS                +=-fno-omit-frame-pointer -mapcs -mno-sched-prolog
+ endif
+-CFLAGS                :=$(CFLAGS:-O2=-Os)
++#CFLAGS               :=$(CFLAGS:-O2=-Os)
+ ifeq ($(CONFIG_DEBUG_INFO),y)
+ CFLAGS                +=-g
+@@ -38,6 +39,8 @@
+ arch-$(CONFIG_CPU_32v3)               :=-D__LINUX_ARM_ARCH__=3 -march=armv3
+ arch-$(CONFIG_CPU_32v4)               :=-D__LINUX_ARM_ARCH__=4 -march=armv4
+ arch-$(CONFIG_CPU_32v5)               :=-D__LINUX_ARM_ARCH__=5 -march=armv5
++#arch-$(CONFIG_CPU_XSCALE)    :=-D__LINUX_ARM_ARCH__=5 -mcpu=xscale
++arch-$(CONFIG_CPU_XSCALE)     :=-D__LINUX_ARM_ARCH__=5 -march=armv4 -Wa,-mxscale
+ # This selects how we optimise for the processor.
+ tune-y                                :=
+@@ -49,6 +52,8 @@
+ tune-$(CONFIG_CPU_ARM926T)    :=-mtune=arm9tdmi
+ tune-$(CONFIG_CPU_SA110)      :=-mtune=strongarm110
+ tune-$(CONFIG_CPU_SA1100)     :=-mtune=strongarm1100
++#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
+@@ -127,6 +132,10 @@
+ MACHINE                = sa1100
+ endif
++ifeq ($(CONFIG_ARCH_PXA),y)
++MACHINE                = pxa
++endif
++
+ ifeq ($(CONFIG_ARCH_L7200),y)
+ MACHINE                = l7200
+ endif
+@@ -164,6 +173,17 @@
+ MACHINE                = omaha
+ endif
++ifeq ($(CONFIG_XIP_KERNEL),y) 
++  DATAADDR   := $(TEXTADDR)
++  # Replace phys addr with virt addr while keeping offset from base.
++  # Virt base addr also defined in include/asm-arm/arch-*/hardware.h
++  TEXTADDR    = $(shell echo 0x`echo $(CONFIG_XIP_PHYS_ADDR)|sed -e's/^0x//'` |\
++                      awk --non-decimal-data '/[:xdigit:]/ \
++                        {printf("0x%x\n",and($$0,0x001fffff)+0xe8000000)}' )
++  LDSCRIPT    = arch/arm/vmlinux-armv-xip.lds.in
++  export DATAADDR
++endif
++
+ export        MACHINE PROCESSOR TEXTADDR GZFLAGS CFLAGS_BOOT OBJCOPYFLAGS
+ # Only set INCDIR if its not already defined above
+@@ -269,7 +289,7 @@
+ arch/arm/kernel arch/arm/mm arch/arm/lib: dummy
+       $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
+-bzImage zImage zinstall Image bootpImage install: vmlinux
++bzImage zImage zinstall Image xipImage bootpImage install: vmlinux
+       @$(MAKEBOOT) $@
+ CLEAN_FILES   += \
+--- linux-2.4.27/arch/arm/boot/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/boot/Makefile
+@@ -113,6 +113,10 @@
+ endif
+ endif
++ifeq ($(CONFIG_ARCH_PXA),y)
++ZRELADDR       = 0xa0008000
++endif
++
+ ifeq ($(CONFIG_ARCH_ANAKIN),y)
+ ZRELADDR       = 0x20008000
+ endif
+@@ -140,6 +144,14 @@
+ zImage:       compressed/vmlinux
+       $(OBJCOPY) $(OBJCOPYFLAGS) $< $@
++ifeq ($(CONFIG_XIP_KERNEL),y)
++xipImage: $(CONFIGURE) $(SYSTEM)
++      $(OBJCOPY) -S -O binary -R .data $(SYSTEM) vmlinux-text.bin
++      $(OBJCOPY) -S -O binary -R .init -R .text -R __ex_table -R __ksymtab $(SYSTEM) vmlinux-data.bin
++      cat vmlinux-text.bin vmlinux-data.bin > $@
++      $(RM) -f vmlinux-text.bin vmlinux-data.bin
++endif
++
+ bootpImage: bootp/bootp
+       $(OBJCOPY) $(OBJCOPYFLAGS) $< $@
+@@ -160,7 +172,7 @@
+       sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+ clean:
+-      $(RM) Image zImage bootpImage
++      $(RM) Image xipImage zImage bootpImage
+       @$(MAKE) -C compressed clean
+       @$(MAKE) -C bootp clean
+--- linux-2.4.27/arch/arm/boot/compressed/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/boot/compressed/Makefile
+@@ -71,6 +71,10 @@
+ OBJS          += head-sa1100.o
+ endif
++ifeq ($(CONFIG_CPU_XSCALE),y)
++OBJS          += head-xscale.o
++endif
++
+ SEDFLAGS      = s/TEXT_START/$(ZTEXTADDR)/;s/LOAD_ADDR/$(ZRELADDR)/;s/BSS_START/$(ZBSSADDR)/
+ LIBGCC                := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
+--- /dev/null
++++ linux-2.4.27/arch/arm/boot/compressed/head-xscale.S
+@@ -0,0 +1,50 @@
++/* 
++ * linux/arch/arm/boot/compressed/head-xscale.S
++ * 
++ * XScale specific tweaks.  This is merged into head.S by the linker.
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/linkage.h>
++#include <asm/mach-types.h>
++
++              .section        ".start", #alloc, #execinstr
++
++__XScale_start:
++
++              @ Preserve r8/r7 i.e. kernel entry values
++
++              @ Data cache might be active.
++              @ Be sure to flush kernel binary out of the cache,
++              @ whatever state it is, before it is turned off.
++              @ This is done by fetching through currently executed
++              @ memory to be sure we hit the same cache.
++              bic     r2, pc, #0x1f
++              add     r3, r2, #0x10000        @ 64 kb is quite enough...
++1:            ldr     r0, [r2], #32
++              teq     r2, r3
++              bne     1b
++              mcr     p15, 0, r0, c7, c10, 4  @ drain WB
++              mcr     p15, 0, r0, c7, c7, 0   @ flush I & D caches
++
++              @ disabling MMU and caches
++              mrc     p15, 0, r0, c1, c0, 0   @ read control reg
++              bic     r0, r0, #0x05           @ clear DC, MMU
++              bic     r0, r0, #0x1000         @ clear Icache
++              mcr     p15, 0, r0, c1, c0, 0
++
++#ifdef CONFIG_ARCH_LUBBOCK
++              mov     r7, #MACH_TYPE_LUBBOCK
++#endif
++
++#ifdef CONFIG_ARCH_PXA_IDP
++              mov     r7, #MACH_TYPE_PXA_IDP
++#endif
++
++#ifdef CONFIG_ARCH_TRIZEPS2
++              mov     r7, #(MACH_TYPE_TRIZEPS2 & 0xFF00)
++              add     r7, r7, #(MACH_TYPE_TRIZEPS2 & 0xFF)
++#endif
++
++
+--- linux-2.4.27/arch/arm/boot/compressed/head.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/boot/compressed/head.S
+@@ -351,7 +351,11 @@
+               orr     r1, r1, #3 << 10
+               add     r2, r3, #16384
+ 1:            cmp     r1, r8                  @ if virt > start of RAM
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++              orrhs   r1, r1, #0x08           @ set cacheable, not bufferable
++#else
+               orrhs   r1, r1, #0x0c           @ set cacheable, bufferable
++#endif
+               cmp     r1, r9                  @ if virt > end of RAM
+               bichs   r1, r1, #0x0c           @ clear cacheable, bufferable
+               str     r1, [r0], #4            @ 1:1 mapping
+@@ -364,7 +368,11 @@
+  * so there is no map overlap problem for up to 1 MB compressed kernel.
+  * If the execution is in RAM then we would only be duplicating the above.
+  */
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++              mov     r1, #0x1a
++#else
+               mov     r1, #0x1e
++#endif
+               orr     r1, r1, #3 << 10
+               mov     r2, pc, lsr #20
+               orr     r1, r1, r2, lsl #20
+--- linux-2.4.27/arch/arm/config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/config.in
+@@ -38,6 +38,7 @@
+        Cirrus-CL-PS7500FE     CONFIG_ARCH_CLPS7500 \
+        CLPS711x/EP721x-based  CONFIG_ARCH_CLPS711X \
+        Co-EBSA285             CONFIG_ARCH_CO285 \
++       PXA250/210-based       CONFIG_ARCH_PXA \
+        EBSA-110               CONFIG_ARCH_EBSA110 \
+        Excalibur-ARM          CONFIG_ARCH_CAMELOT \
+        FootBridge             CONFIG_ARCH_FOOTBRIDGE \
+@@ -148,6 +149,47 @@
+ endmenu
+ mainmenu_option next_comment
++comment 'Intel PXA250/210 Implementations'
++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 '  Trizeps-II MT6N' CONFIG_ARCH_TRIZEPS2 $CONFIG_ARCH_PXA
++
++if [ "$CONFIG_ARCH_PXA_CERF" = "y" ]; then
++   define_bool CONFIG_PXA_CERF y
++
++   choice 'CerfBoard Style' \
++      "PDA   CONFIG_PXA_CERF_PDA \
++       BOARD CONFIG_PXA_CERF_BOARD" PDA
++
++   choice 'CerfBoard RAM Available' \
++      "128MB  CONFIG_PXA_CERF_RAM_128MB \
++        64MB  CONFIG_PXA_CERF_RAM_64MB \
++        32MB  CONFIG_PXA_CERF_RAM_32MB \
++        16MB  CONFIG_PXA_CERF_RAM_16MB" 64MB
++
++   choice 'CerfBoard Flash Available' \
++      "64MB  CONFIG_PXA_CERF_FLASH_64MB \
++       32MB  CONFIG_PXA_CERF_FLASH_32MB \
++       16MB  CONFIG_PXA_CERF_FLASH_16MB \
++        8MB  CONFIG_PXA_CERF_FLASH_8MB" 32MB
++fi
++
++if [ "$CONFIG_ARCH_LUBBOCK" = "y" ]; then
++   define_bool CONFIG_SA1111 y
++fi
++
++if [ "$CONFIG_ARCH_TRIZEPS2" = "y" ]; then
++   define_bool CONFIG_TRIZEPS2 y
++fi
++
++dep_tristate 'PXA USB function support' CONFIG_PXA_USB $CONFIG_ARCH_PXA
++dep_tristate '  Support for PXA USB network link function' CONFIG_PXA_USB_NETLINK $CONFIG_PXA_USB
++dep_tristate '  Support for PXA USB character device emulation' CONFIG_PXA_USB_CHAR $CONFIG_PXA_USB
++
++endmenu
++
++mainmenu_option next_comment
+ comment 'CLPS711X/EP721X Implementations'
+ dep_bool '  AUTCPU12' CONFIG_ARCH_AUTCPU12 $CONFIG_ARCH_CLPS711X
+ dep_bool '  CDB89712' CONFIG_ARCH_CDB89712 $CONFIG_ARCH_CLPS711X
+@@ -385,6 +427,12 @@
+    define_bool CONFIG_CPU_SA1100 n
+ fi
++if [ "$CONFIG_ARCH_PXA" = "y" ]; then
++   define_bool CONFIG_CPU_32v5 y
++   define_bool CONFIG_CPU_XSCALE y
++   bool 'Workaround for XScale cache errata (see help)' CONFIG_XSCALE_CACHE_ERRATA
++fi
++
+ # Figure out what processor architecture version we should be using.
+ # This defines the compiler instruction set which depends on the machine type.
+@@ -493,6 +541,7 @@
+ hex 'Compressed ROM boot loader BSS address' CONFIG_ZBOOT_ROM_BSS 0
+ if [ "$CONFIG_ARCH_SA1100" = "y" -o \
++     "$CONFIG_ARCH_PXA" = "y" -o \
+      "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+    dep_bool 'Support CPU clock change (EXPERIMENTAL)' CONFIG_CPU_FREQ $CONFIG_EXPERIMENTAL
+ fi
+@@ -501,8 +550,10 @@
+ bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
+ if [ "$CONFIG_HOTPLUG" = "y" ]; then
+    source drivers/pcmcia/Config.in
++   source drivers/mmc/Config.in
+ else
+    define_bool CONFIG_PCMCIA n
++   define_bool CONFIG_MMC n
+ fi
+ if [ "$CONFIG_SA1100_ACCELENT" = "y" ]; then
+    if [ "$CONFIG_PCMCIA" != "n" ]; then
+@@ -513,6 +564,14 @@
+ bool 'System V IPC' CONFIG_SYSVIPC
+ bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
+ bool 'Sysctl support' CONFIG_SYSCTL
++
++if [ "$CONFIG_ARCH_PXA" = "y" ]; then
++    dep_bool 'Kernel Execute-In-Place from ROM (EXPERIMENTAL)' CONFIG_XIP_KERNEL $CONFIG_EXPERIMENTAL
++    if [ "$CONFIG_XIP_KERNEL" = "y" ]; then
++        hex '  Kernel .text physical address' CONFIG_XIP_PHYS_ADDR 0
++    fi    
++fi   
++
+ comment 'At least one math emulation must be selected'
+ tristate 'NWFPE math emulation' CONFIG_FPE_NWFPE
+ if [ "$CONFIG_FPE_NWFPE" != "n" ]; then
+@@ -538,6 +597,9 @@
+      "$CONFIG_ARCH_SHARK" = "y" -o      \
+      "$CONFIG_ARCH_CO285" = "y" -o      \
+      "$CONFIG_ARCH_SA1100" = "y" -o     \
++     "$CONFIG_ARCH_LUBBOCK" = "y" -o    \
++     "$CONFIG_ARCH_PXA_IDP" = "y" -o \
++     "$CONFIG_ARCH_PXA_CERF" = "y" -o \
+      "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+      "$CONFIG_ARCH_CDB89712" = "y" -o   \
+      "$CONFIG_ARCH_P720T" = "y" -o    \
+@@ -550,9 +612,12 @@
+          "$CONFIG_ARCH_SHARK" = "y" -o      \
+          "$CONFIG_ARCH_CO285" = "y" -o      \
+          "$CONFIG_ARCH_SA1100" = "y" -o     \
++           "$CONFIG_ARCH_LUBBOCK" = "y" -o    \
+            "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+          "$CONFIG_ARCH_P720T" = "y" -o      \
+          "$CONFIG_ARCH_OMAHA" = "y" -o      \
++         "$CONFIG_ARCH_PXA_CERF" = "y" -o   \
++         "$CONFIG_ARCH_PXA_IDP" = "y" -o   \
+          "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
+          bool '  Timer LED' CONFIG_LEDS_TIMER
+          bool '  CPU usage LED' CONFIG_LEDS_CPU
+@@ -686,6 +751,7 @@
+    if [ "$CONFIG_FOOTBRIDGE_HOST" = "y" -o \
+         "$CONFIG_ARCH_SHARK" = "y" -o      \
+         "$CONFIG_ARCH_SA1100" = "y" -o     \
++        "$CONFIG_ARCH_PXA" = "y" -o        \
+         "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+         "$CONFIG_ARCH_TBOX" = "y" -o       \
+         "$CONFIG_ARCH_CLPS7500" = "y" -o   \
+@@ -707,6 +773,7 @@
+      "$CONFIG_ARCH_TBOX" = "y" -o \
+      "$CONFIG_ARCH_SHARK" = "y" -o \
+      "$CONFIG_ARCH_SA1100" = "y" -o \
++     "$CONFIG_ARCH_PXA" = "y" -o \
+      "$CONFIG_PCI" = "y" ]; then
+    mainmenu_option next_comment
+    comment 'Sound'
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/cerfboard_pxa
+@@ -0,0 +1,857 @@
++#
++# 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
++
++#
++# 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_ADSBITSY 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_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_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_H3600_SLEEVE is not set
++
++#
++# Intel PXA250/210 Implementations
++#
++# CONFIG_ARCH_LUBBOCK is not set
++# CONFIG_ARCH_PXA_IDP is not set
++CONFIG_ARCH_PXA_CERF=y
++CONFIG_PXA_CERF=y
++# CONFIG_PXA_CERF_PDA is not set
++CONFIG_PXA_CERF_BOARD=y
++# CONFIG_PXA_CERF_RAM_128MB is not set
++CONFIG_PXA_CERF_RAM_64MB=y
++# CONFIG_PXA_CERF_RAM_32MB is not set
++# CONFIG_PXA_CERF_RAM_16MB is not set
++# CONFIG_PXA_CERF_FLASH_64MB is not set
++CONFIG_PXA_CERF_FLASH_32MB=y
++# CONFIG_PXA_CERF_FLASH_16MB is not set
++# CONFIG_PXA_CERF_FLASH_8MB is not set
++CONFIG_PXA_USB=y
++CONFIG_PXA_USB_NETLINK=y
++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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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=y
++# 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++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
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_SYSCTL=y
++CONFIG_FPE_NWFPE=y
++# 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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=1f03 rw console=tty0 console=ttyS0,38400 init=/linuxrc"
++CONFIG_LEDS=y
++# CONFIG_LEDS_TIMER is not set
++CONFIG_LEDS_CPU=y
++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=y
++# 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
++
++#
++# 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_CFI_B1 is not set
++# CONFIG_MTD_CFI_B2 is not set
++CONFIG_MTD_CFI_B4=y
++# CONFIG_MTD_CFI_B8 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_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_PHYSMAP is not set
++# CONFIG_MTD_LUBBOCK is not set
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++CONFIG_MTD_PXA_CERF=y
++# CONFIG_MTD_EPXA10DB is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_PCI 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_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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 is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=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 is not set
++# CONFIG_NETFILTER is not set
++CONFIG_FILTER=y
++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=y
++CONFIG_IP_PNP_RARP=y
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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_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_MYRI_SBUS is not set
++# CONFIG_NS83820 is not set
++# CONFIG_HAMACHI is not set
++# CONFIG_YELLOWFIN 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 is not set
++CONFIG_PPP_DEFLATE=m
++CONFIG_PPP_BSDCOMP=m
++# CONFIG_PPPOE is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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=m
++CONFIG_PCMCIA_3C574=m
++CONFIG_PCMCIA_FMVJ18X=m
++CONFIG_PCMCIA_PCNET=m
++CONFIG_PCMCIA_AXNET=m
++CONFIG_PCMCIA_NMCLAN=m
++CONFIG_PCMCIA_SMC91C92=m
++CONFIG_PCMCIA_XIRC2PS=m
++# CONFIG_ARCNET_COM20020_CS is not set
++# CONFIG_PCMCIA_IBMTR is not set
++CONFIG_NET_PCMCIA_RADIO=y
++CONFIG_PCMCIA_RAYCS=m
++CONFIG_PCMCIA_NETWAVE=m
++CONFIG_PCMCIA_WAVELAN=m
++CONFIG_AIRONET4500_CS=m
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++# CONFIG_IRDA is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
++# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
++# CONFIG_BLK_DEV_IDEDISK_IBM is not set
++# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
++# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
++# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
++# CONFIG_BLK_DEV_IDEDISK_WD is not set
++# CONFIG_BLK_DEV_COMMERIAL is not set
++# CONFIG_BLK_DEV_TIVO is not set
++CONFIG_BLK_DEV_IDECS=m
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_IDE_MODES is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_KEYBDEV is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV 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_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=256
++
++#
++# 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 is not set
++# CONFIG_I2C_PROC is not set
++# CONFIG_I2C_DS1307 is not set
++
++#
++# 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_QIC02_TAPE is not set
++
++#
++# Watchdog Cards
++#
++CONFIG_WATCHDOG=y
++# CONFIG_WATCHDOG_NOWAYOUT is not set
++# CONFIG_ACQUIRE_WDT is not set
++# CONFIG_ADVANTECH_WDT is not set
++# CONFIG_ALIM7101_WDT is not set
++# CONFIG_SC520_WDT is not set
++# CONFIG_PCWATCHDOG is not set
++# CONFIG_21285_WATCHDOG is not set
++# CONFIG_977_WATCHDOG is not set
++# CONFIG_SA1100_WATCHDOG is not set
++CONFIG_PXA_WATCHDOG=m
++# CONFIG_OMAHA_WATCHDOG is not set
++# CONFIG_EUROTECH_WDT is not set
++# CONFIG_IB700_WDT is not set
++# CONFIG_WAFER_WDT is not set
++# CONFIG_I810_TCO is not set
++# CONFIG_MIXCOMWD is not set
++# CONFIG_60XX_WDT is not set
++# CONFIG_SC1200_WDT is not set
++# CONFIG_SOFT_WATCHDOG is not set
++# CONFIG_W83877F_WDT is not set
++# CONFIG_WDT is not set
++# CONFIG_WDTPCI is not set
++# CONFIG_MACHZ_WDT is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++# CONFIG_PXA_RTC is not set
++# 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=y
++CONFIG_PCMCIA_CHRDEV=y
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV is not set
++
++#
++# File systems
++#
++# CONFIG_QUOTA is not set
++CONFIG_AUTOFS_FS=y
++CONFIG_AUTOFS4_FS=y
++# 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_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=m
++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_CRAMFS 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_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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++CONFIG_ROMFS_FS=y
++CONFIG_EXT2_FS=y
++# 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=y
++CONFIG_ROOT_NFS=y
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=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
++# CONFIG_ZLIB_FS_INFLATE 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 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=m
++CONFIG_NLS_CODEPAGE_852=m
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++CONFIG_NLS_CODEPAGE_863=m
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=m
++CONFIG_NLS_ISO8859_2=m
++CONFIG_NLS_ISO8859_3=m
++CONFIG_NLS_ISO8859_4=m
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# Console drivers
++#
++CONFIG_PC_KEYMAP=y
++# CONFIG_VGA_CONSOLE is not set
++
++#
++# Frame-buffer support
++#
++# CONFIG_FB is not set
++
++#
++# Sound
++#
++# CONFIG_SOUND is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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 is not set
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++# CONFIG_DEBUG_INFO is not set
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++# CONFIG_DEBUG_SLAB is not set
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_DEBUG_SPINLOCK is not set
++# CONFIG_DEBUG_WAITQ is not set
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/cerfpda_pxa
+@@ -0,0 +1,962 @@
++#
++# 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_L7200 is not set
++# CONFIG_ARCH_MX1ADS is not set
++# CONFIG_ARCH_RPC is not set
++# CONFIG_ARCH_SA1100 is not set
++# CONFIG_ARCH_SHARK 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_ASSABET is not set
++# CONFIG_ASSABET_NEPONSET is not set
++# CONFIG_SA1100_ADSBITSY 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_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_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_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_H3600_SLEEVE is not set
++
++#
++# Intel PXA250/210 Implementations
++#
++# CONFIG_ARCH_LUBBOCK is not set
++# CONFIG_ARCH_PXA_IDP is not set
++CONFIG_ARCH_PXA_CERF=y
++CONFIG_PXA_CERF=y
++CONFIG_PXA_CERF_PDA=y
++# CONFIG_PXA_CERF_BOARD is not set
++# CONFIG_PXA_CERF_RAM_128MB is not set
++CONFIG_PXA_CERF_RAM_64MB=y
++# CONFIG_PXA_CERF_RAM_32MB is not set
++# CONFIG_PXA_CERF_RAM_16MB is not set
++# CONFIG_PXA_CERF_FLASH_64MB is not set
++CONFIG_PXA_CERF_FLASH_32MB=y
++# CONFIG_PXA_CERF_FLASH_16MB is not set
++# CONFIG_PXA_CERF_FLASH_8MB is not set
++CONFIG_PXA_USB=y
++CONFIG_PXA_USB_NETLINK=y
++CONFIG_PXA_USB_CHAR=y
++
++#
++# 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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_32v3 is not set
++# CONFIG_CPU_32v4 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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_ARM_THUMB 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++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
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++CONFIG_BSD_PROCESS_ACCT=y
++CONFIG_SYSCTL=y
++CONFIG_FPE_NWFPE=y
++# 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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=1f03 rw console=tty0 console=ttyS0,38400 init=/linuxrc"
++CONFIG_LEDS=y
++# CONFIG_LEDS_TIMER is not set
++CONFIG_LEDS_CPU=y
++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=y
++# 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
++
++#
++# 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_CFI_B1 is not set
++# CONFIG_MTD_CFI_B2 is not set
++CONFIG_MTD_CFI_B4=y
++# CONFIG_MTD_CFI_B8 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_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_PHYSMAP is not set
++# CONFIG_MTD_LUBBOCK is not set
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++CONFIG_MTD_PXA_CERF=y
++# CONFIG_MTD_EPXA10DB is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_PCI 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_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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_BLK_DEV_DAC960 is not set
++CONFIG_BLK_DEV_LOOP=m
++# CONFIG_BLK_DEV_NBD is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=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 is not set
++# CONFIG_NETFILTER is not set
++CONFIG_FILTER=y
++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=y
++CONFIG_IP_PNP_RARP=y
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# CONFIG_IPX is not set
++# CONFIG_ATALK is not set
++# CONFIG_DECNET is not set
++# CONFIG_BRIDGE is not set
++# 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 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_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 is not set
++# 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_MYRI_SBUS is not set
++# CONFIG_NS83820 is not set
++# CONFIG_HAMACHI is not set
++# CONFIG_YELLOWFIN is not set
++# CONFIG_SK98LIN 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 is not set
++CONFIG_PPP_DEFLATE=m
++CONFIG_PPP_BSDCOMP=m
++# CONFIG_PPPOE is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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=m
++CONFIG_PCMCIA_3C574=m
++CONFIG_PCMCIA_FMVJ18X=m
++CONFIG_PCMCIA_PCNET=m
++CONFIG_PCMCIA_AXNET=m
++CONFIG_PCMCIA_NMCLAN=m
++CONFIG_PCMCIA_SMC91C92=m
++CONFIG_PCMCIA_XIRC2PS=m
++# 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=y
++CONFIG_IRLAN=y
++# CONFIG_IRNET is not set
++CONFIG_IRCOMM=y
++CONFIG_IRDA_ULTRA=y
++# CONFIG_IRDA_CACHE_LAST_LSAP is not set
++# CONFIG_IRDA_FAST_RR is not set
++CONFIG_IRDA_DEBUG=y
++
++#
++# Infrared-port device drivers
++#
++CONFIG_IRTTY_SIR=y
++# CONFIG_IRPORT_SIR is not set
++# 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_FIR is not set
++# CONFIG_SMC_IRCC_FIR is not set
++# CONFIG_ALI_FIR is not set
++# CONFIG_VLSI_FIR is not set
++
++#
++# ATA/IDE/MFM/RLL support
++#
++CONFIG_IDE=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
++# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
++# CONFIG_BLK_DEV_IDEDISK_IBM is not set
++# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
++# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
++# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
++# CONFIG_BLK_DEV_IDEDISK_WD is not set
++# CONFIG_BLK_DEV_COMMERIAL is not set
++# CONFIG_BLK_DEV_TIVO is not set
++CONFIG_BLK_DEV_IDECS=m
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_IDE_MODES is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++CONFIG_INPUT_EVDEV=y
++
++#
++# 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_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=256
++
++#
++# I2C support
++#
++# CONFIG_I2C is not set
++
++#
++# 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
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_INTEL_RNG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++# 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=y
++CONFIG_PCMCIA_CHRDEV=y
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV is not set
++
++#
++# File systems
++#
++# CONFIG_QUOTA is not set
++CONFIG_AUTOFS_FS=y
++CONFIG_AUTOFS4_FS=y
++# 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_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=m
++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_CRAMFS is not set
++CONFIG_TMPFS=y
++CONFIG_RAMFS=m
++# CONFIG_ISO9660_FS is not set
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS 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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++CONFIG_ROMFS_FS=y
++CONFIG_EXT2_FS=y
++# 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=y
++CONFIG_ROOT_NFS=y
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=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
++# CONFIG_ZLIB_FS_INFLATE 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 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=m
++CONFIG_NLS_CODEPAGE_852=m
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++CONFIG_NLS_CODEPAGE_863=m
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=m
++CONFIG_NLS_ISO8859_2=m
++CONFIG_NLS_ISO8859_3=m
++CONFIG_NLS_ISO8859_4=m
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# 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_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 is not set
++CONFIG_FBCON_CFB2=y
++CONFIG_FBCON_CFB4=y
++CONFIG_FBCON_CFB8=y
++CONFIG_FBCON_CFB16=y
++# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
++CONFIG_FBCON_FONTS=y
++# CONFIG_FONT_8x8 is not set
++# CONFIG_FONT_8x16 is not set
++# CONFIG_FONT_SUN8x16 is not set
++# CONFIG_FONT_SUN12x22 is not set
++# CONFIG_FONT_6x11 is not set
++# CONFIG_FONT_PEARL_8x8 is not set
++CONFIG_FONT_ACORN_8x8=y
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++# 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_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_WAVEARTIST is not set
++CONFIG_SOUND_PXA_AC97=y
++# CONFIG_SOUND_TVMIXER is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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 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_AUDIO is not set
++# CONFIG_USB_BLUETOOTH is not set
++# CONFIG_USB_STORAGE is not set
++# CONFIG_USB_STORAGE_DEBUG is not set
++# CONFIG_USB_STORAGE_DATAFAB is not set
++# CONFIG_USB_STORAGE_FREECOM is not set
++# CONFIG_USB_STORAGE_ISD200 is not set
++# CONFIG_USB_STORAGE_DPCM is not set
++# CONFIG_USB_STORAGE_HP8200e is not set
++# CONFIG_USB_STORAGE_SDDR09 is not set
++# CONFIG_USB_STORAGE_JUMPSHOT is not set
++# CONFIG_USB_ACM is not set
++# CONFIG_USB_PRINTER is not set
++# CONFIG_USB_HID is not set
++# CONFIG_USB_HIDDEV is not set
++# CONFIG_USB_KBD is not set
++# CONFIG_USB_MOUSE is not set
++# CONFIG_USB_WACOM is not set
++# CONFIG_USB_DC2XX is not set
++# CONFIG_USB_MDC800 is not set
++# CONFIG_USB_SCANNER is not set
++# CONFIG_USB_MICROTEK is not set
++# CONFIG_USB_HPUSBSCSI is not set
++# CONFIG_USB_PEGASUS 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 is not set
++# CONFIG_USB_SERIAL_GENERIC is not set
++# CONFIG_USB_SERIAL_BELKIN is not set
++# CONFIG_USB_SERIAL_WHITEHEAT is not set
++# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
++# CONFIG_USB_SERIAL_EMPEG is not set
++# 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_KEYSPAN_PDA is not set
++# CONFIG_USB_SERIAL_KEYSPAN is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
++# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
++# CONFIG_USB_SERIAL_MCT_U232 is not set
++# CONFIG_USB_SERIAL_KLSI 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
++
++#
++# Bluetooth support
++#
++CONFIG_BLUEZ=y
++CONFIG_BLUEZ_L2CAP=y
++
++#
++# Bluetooth device drivers
++#
++# CONFIG_BLUEZ_HCIUSB is not set
++CONFIG_BLUEZ_HCIUART=y
++CONFIG_BLUEZ_HCIVHCI=y
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++# CONFIG_DEBUG_SLAB is not set
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_DEBUG_SPINLOCK is not set
++# CONFIG_DEBUG_WAITQ is not set
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/csb226
+@@ -0,0 +1,615 @@
++#
++# 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_AT91RM9200DK 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_ADSBITSY 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_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_H3600_SLEEVE is not set
++
++#
++# Intel PXA250/210 Board
++#
++# CONFIG_ARCH_PXA_IDP is not set
++# CONFIG_ARCH_INNOKOM is not set
++CONFIG_ARCH_CSB226=y
++# CONFIG_ARCH_LUBBOCK is not set
++# CONFIG_ARCH_PXA_CERF is not set
++# CONFIG_PXA_USB is not set
++# CONFIG_PXA_USB_NETLINK is not set
++# CONFIG_PXA_USB_CHAR is not set
++
++#
++# 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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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=y
++# 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++# CONFIG_CPU_FREQ is not set
++# CONFIG_HOTPLUG is not set
++# CONFIG_PCMCIA is not set
++# CONFIG_MMC is not set
++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_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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="console=ttyS0,19200"
++CONFIG_ALIGNMENT_TRAP=y
++CONFIG_ARM_HWTIMER=y
++
++#
++# Parallel port support
++#
++# CONFIG_PARPORT is not set
++
++#
++# Memory Technology Devices (MTD)
++#
++# CONFIG_MTD 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 is not set
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_RAM is not set
++# CONFIG_BLK_DEV_INITRD is not set
++
++#
++# 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 is not set
++# CONFIG_NETLINK_DEV is not set
++# 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 is not set
++CONFIG_IP_PNP_BOOTP=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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=y
++# 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 is not set
++# 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_MYRI_SBUS is not set
++# CONFIG_NS83820 is not set
++# CONFIG_HAMACHI is not set
++# CONFIG_YELLOWFIN 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 is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++# CONFIG_IRDA is not set
++
++#
++# 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 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 is not set
++# CONFIG_INPUT_KEYBDEV is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV is not set
++
++#
++# Character devices
++#
++# CONFIG_VT is not set
++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_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=256
++
++#
++# I2C support
++#
++# CONFIG_I2C is not set
++
++#
++# 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_QIC02_TAPE is not set
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++# CONFIG_PXA_RTC is not set
++# 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
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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_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 is not set
++# CONFIG_MSDOS_FS is not set
++# CONFIG_UMSDOS_FS is not set
++# CONFIG_VFAT_FS is not set
++# CONFIG_EFS_FS is not set
++# CONFIG_JFFS_FS is not set
++# CONFIG_JFFS2_FS is not set
++# CONFIG_CRAMFS is not set
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++# CONFIG_ISO9660_FS is not set
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS 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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++# CONFIG_EXT2_FS is not set
++# 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_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
++# CONFIG_ZLIB_FS_INFLATE is not set
++
++#
++# Partition Types
++#
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_ACORN_PARTITION is not set
++# CONFIG_OSF_PARTITION is not set
++# CONFIG_AMIGA_PARTITION is not set
++# CONFIG_ATARI_PARTITION is not set
++# CONFIG_MAC_PARTITION is not set
++# CONFIG_MSDOS_PARTITION is not set
++# CONFIG_LDM_PARTITION is not set
++# CONFIG_SGI_PARTITION is not set
++# CONFIG_ULTRIX_PARTITION is not set
++# CONFIG_SUN_PARTITION is not set
++# CONFIG_SMB_NLS is not set
++# CONFIG_NLS is not set
++
++#
++# Sound
++#
++# CONFIG_SOUND is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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 is not set
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++CONFIG_DEBUG_SLAB=y
++CONFIG_MAGIC_SYSRQ=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_WAITQ=y
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/innokom
+@@ -0,0 +1,699 @@
++#
++# 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_AT91RM9200DK 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_ADSBITSY 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_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_H3600_SLEEVE is not set
++
++#
++# Intel PXA250/210 Board
++#
++# CONFIG_ARCH_PXA_IDP is not set
++CONFIG_ARCH_INNOKOM=y
++# CONFIG_ARCH_CSB226 is not set
++# CONFIG_ARCH_LUBBOCK is not set
++# CONFIG_ARCH_PXA_CERF is not set
++# CONFIG_PXA_USB is not set
++# CONFIG_PXA_USB_NETLINK is not set
++# CONFIG_PXA_USB_CHAR is not set
++
++#
++# 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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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=y
++# 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++# CONFIG_CPU_FREQ is not set
++# CONFIG_HOTPLUG is not set
++# CONFIG_PCMCIA is not set
++# CONFIG_MMC is not set
++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_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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/nfs mem=32M ip=dhcp console=ttyS0,19200"
++CONFIG_ALIGNMENT_TRAP=y
++CONFIG_ARM_HWTIMER=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=y
++# 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
++
++#
++# 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 is not set
++CONFIG_MTD_CFI_INTELEXT=y
++CONFIG_MTD_CFI_AMDSTD=y
++# CONFIG_MTD_CFI_STAA is not set
++# 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_PHYSMAP is not set
++# CONFIG_MTD_NORA 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_EPXA10DB is not set
++# CONFIG_MTD_FORTUNET is not set
++CONFIG_MTD_INNOKOM=y
++CONFIG_MTD_INNOKOM_16MB=y
++# CONFIG_MTD_INNOKOM_64MB is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_CEIVA 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_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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 is not set
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_RAM is not set
++# CONFIG_BLK_DEV_INITRD is not set
++
++#
++# 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 is not set
++# CONFIG_NETLINK_DEV is not set
++# 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 is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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_MYRI_SBUS is not set
++# CONFIG_NS83820 is not set
++# CONFIG_HAMACHI is not set
++# CONFIG_YELLOWFIN 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 is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++# CONFIG_IRDA is not set
++
++#
++# 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 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 is not set
++# CONFIG_INPUT_KEYBDEV is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV is not set
++
++#
++# Character devices
++#
++# CONFIG_VT is not set
++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_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=256
++
++#
++# 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=y
++CONFIG_I2C_PROC=y
++# CONFIG_I2C_DS1307 is not set
++
++#
++# 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_QIC02_TAPE is not set
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++# CONFIG_PXA_RTC is not set
++# 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
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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_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 is not set
++# CONFIG_MSDOS_FS is not set
++# CONFIG_UMSDOS_FS is not set
++# CONFIG_VFAT_FS is not set
++# CONFIG_EFS_FS is not set
++# CONFIG_JFFS_FS is not set
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++# CONFIG_JFFS2_FS_NAND is not set
++# CONFIG_CRAMFS is not set
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++# CONFIG_ISO9660_FS is not set
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS 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=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++# CONFIG_EXT2_FS is not set
++# 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=y
++CONFIG_ROOT_NFS=y
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=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
++# CONFIG_ZLIB_FS_INFLATE is not set
++
++#
++# Partition Types
++#
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_ACORN_PARTITION is not set
++# CONFIG_OSF_PARTITION is not set
++# CONFIG_AMIGA_PARTITION is not set
++# CONFIG_ATARI_PARTITION is not set
++# CONFIG_MAC_PARTITION is not set
++# CONFIG_MSDOS_PARTITION is not set
++# CONFIG_LDM_PARTITION is not set
++# CONFIG_SGI_PARTITION is not set
++# CONFIG_ULTRIX_PARTITION is not set
++# CONFIG_SUN_PARTITION is not set
++# CONFIG_SMB_NLS is not set
++# CONFIG_NLS is not set
++
++#
++# Sound
++#
++# CONFIG_SOUND is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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 is not set
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++CONFIG_DEBUG_SLAB=y
++CONFIG_MAGIC_SYSRQ=y
++CONFIG_DEBUG_SPINLOCK=y
++CONFIG_DEBUG_WAITQ=y
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/lubbock
+@@ -0,0 +1,971 @@
++#
++# Automatically generated make config: 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 is not set
++
++#
++# 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
++#
++
++#
++# Archimedes/A5000 Implementations (select only ONE)
++#
++# 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=y
++# CONFIG_ARCH_PXA_IDP is not set
++# CONFIG_ARCH_PXA_CERF is not set
++# CONFIG_ARCH_TRIZEPS2 is not set
++CONFIG_SA1111=y
++# CONFIG_PXA_USB is not set
++# CONFIG_PXA_USB_NETLINK is not set
++# CONFIG_PXA_USB_CHAR is not set
++
++#
++# 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_FOOTBRIDGE is not set
++# CONFIG_FOOTBRIDGE_HOST is not set
++# CONFIG_FOOTBRIDGE_ADDIN is not set
++
++#
++# Processor Type
++#
++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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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
++
++#
++# Processor Features
++#
++# 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++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
++
++#
++# At least one math emulation must be selected
++#
++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_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/nfs ip=bootp console=ttyS0,115200 mem=32M"
++CONFIG_LEDS=y
++CONFIG_LEDS_TIMER=y
++CONFIG_LEDS_CPU=y
++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=y
++# CONFIG_MTD_CMDLINE_PARTS is not set
++# CONFIG_MTD_AFS_PARTS is not set
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++# CONFIG_NFTL 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_CFI_B1 is not set
++CONFIG_MTD_CFI_B2=y
++CONFIG_MTD_CFI_B4=y
++# CONFIG_MTD_CFI_B8 is not set
++CONFIG_MTD_CFI_I1=y
++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_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_PHYSMAP is not set
++CONFIG_MTD_LUBBOCK=y
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++# CONFIG_MTD_EPXA is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_CEIVA 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
++
++#
++# Disk-On-Chip Device Drivers
++#
++# CONFIG_MTD_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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 is not set
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_RAM is not set
++# CONFIG_BLK_DEV_INITRD is not set
++# CONFIG_BLK_STATS is not set
++
++#
++# 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 is not set
++# CONFIG_NETLINK_DEV is not set
++# 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 is not set
++CONFIG_IP_PNP_BOOTP=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++
++#
++#  
++#
++# 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 is not set
++# 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 is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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=y
++# 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 is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++
++#
++# Please see Documentation/ide.txt for help/info on IDE drives
++#
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++CONFIG_BLK_DEV_IDECS=y
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++
++#
++# IDE chipset support/bugfixes
++#
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++CONFIG_BLK_DEV_IDE_MODES=y
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++# CONFIG_BLK_DEV_ATARAID_SII is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_MX1TS is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++# CONFIG_VT_CONSOLE is not set
++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=256
++
++#
++# I2C support
++#
++# CONFIG_I2C is not set
++
++#
++# L3 serial bus support
++#
++# CONFIG_L3 is not set
++# CONFIG_L3_ALGOBIT is not set
++# CONFIG_L3_BIT_SA1100_GPIO is not set
++
++#
++# Other L3 adapters
++#
++# CONFIG_L3_SA1111 is not set
++# CONFIG_BIT_SA1100_GPIO is not set
++
++#
++# Mice
++#
++CONFIG_BUSMOUSE=y
++# CONFIG_ATIXL_BUSMOUSE is not set
++# CONFIG_LOGIBUSMOUSE is not set
++# CONFIG_MS_BUSMOUSE is not set
++CONFIG_MOUSE=y
++CONFIG_PSMOUSE=y
++# CONFIG_82C710_MOUSE is not set
++# CONFIG_PC110_PAD is not set
++# CONFIG_MK712_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
++
++#
++# Joysticks
++#
++# 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 is not set
++CONFIG_PXA_RTC=y
++# 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 is not set
++# CONFIG_SYNCLINK_CS is not set
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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=y
++CONFIG_MSDOS_FS=y
++# CONFIG_UMSDOS_FS is not set
++# CONFIG_VFAT_FS is not set
++# CONFIG_EFS_FS is not set
++# CONFIG_JFFS_FS is not set
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++# CONFIG_CRAMFS is not set
++# CONFIG_CRAMFS_LINEAR is not set
++# CONFIG_CRAMFS_LINEAR_XIP is not set
++# CONFIG_ROOT_CRAMFS_LINEAR is not set
++# CONFIG_TMPFS is not set
++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=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++CONFIG_EXT2_FS=y
++# 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 is not set
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# 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_PXA_QVGA is not set
++# CONFIG_FB_CYBER2000 is not set
++# CONFIG_FB_VIRTUAL is not set
++# CONFIG_FBCON_ADVANCED is not set
++CONFIG_FBCON_CFB2=y
++CONFIG_FBCON_CFB4=y
++CONFIG_FBCON_CFB8=y
++CONFIG_FBCON_CFB16=y
++CONFIG_FBCON_FONTWIDTH8_ONLY=y
++# 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 is not set
++# 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 is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++# CONFIG_DEBUG_SLAB is not set
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_DEBUG_SPINLOCK is not set
++# CONFIG_DEBUG_WAITQ is not set
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
++
++#
++# Library routines
++#
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/pxa_idp
+@@ -0,0 +1,933 @@
++#
++# 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
++
++#
++# 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_ADSBITSY 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_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_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_H3600_SLEEVE is not set
++
++#
++# Intel PXA250/210 Implementations
++#
++# CONFIG_ARCH_LUBBOCK is not set
++CONFIG_ARCH_PXA_IDP=y
++# CONFIG_ARCH_PXA_CERF 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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++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
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++# CONFIG_BSD_PROCESS_ACCT is not set
++CONFIG_SYSCTL=y
++CONFIG_FPE_NWFPE=y
++# 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_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/mtdblock2 init=/linuxrc console=ttyS0,115200"
++CONFIG_LEDS=y
++CONFIG_LEDS_TIMER=y
++CONFIG_LEDS_CPU=y
++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
++
++#
++# 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_CFI_B1 is not set
++# CONFIG_MTD_CFI_B2 is not set
++CONFIG_MTD_CFI_B4=y
++# CONFIG_MTD_CFI_B8 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_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_PHYSMAP is not set
++CONFIG_MTD_LUBBOCK=y
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++# CONFIG_MTD_PXA_CERF is not set
++# CONFIG_MTD_EPXA10DB is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++CONFIG_ASI_MTD0_SIZE=40000
++CONFIG_ASI_MTD1_SIZE=100000
++CONFIG_ASI_MTD2_SIZE=1e00000
++# CONFIG_MTD_PCI 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_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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=y
++# CONFIG_BLK_DEV_NBD is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=4096
++CONFIG_BLK_DEV_INITRD=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 is not set
++# 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 is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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_SMC91111=m
++# CONFIG_NET_VENDOR_RACAL is not set
++# CONFIG_NET_ISA is not set
++# CONFIG_NET_PCI is not set
++CONFIG_NET_POCKET=y
++# CONFIG_DE600 is not set
++# CONFIG_DE620 is not set
++
++#
++# Ethernet (1000 Mbit)
++#
++# CONFIG_ACENIC is not set
++# CONFIG_DL2K 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_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 is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++CONFIG_NET_RADIO=y
++# CONFIG_STRIP is not set
++CONFIG_WAVELAN=m
++# CONFIG_ARLAN is not set
++CONFIG_AIRONET4500=y
++# CONFIG_AIRONET4500_NONCS is not set
++# CONFIG_AIRONET4500_PROC is not set
++CONFIG_HERMES=m
++CONFIG_PCMCIA_HERMES=m
++CONFIG_AIRO_CS=m
++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=m
++# CONFIG_PCMCIA_AXNET is not set
++CONFIG_PCMCIA_NMCLAN=m
++CONFIG_PCMCIA_SMC91C92=m
++CONFIG_PCMCIA_XIRC2PS=m
++# CONFIG_ARCNET_COM20020_CS is not set
++# CONFIG_PCMCIA_IBMTR is not set
++CONFIG_NET_PCMCIA_RADIO=y
++CONFIG_PCMCIA_RAYCS=m
++CONFIG_PCMCIA_NETWAVE=m
++CONFIG_PCMCIA_WAVELAN=m
++CONFIG_AIRONET4500_CS=m
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++CONFIG_IRDA=m
++CONFIG_IRLAN=m
++# CONFIG_IRNET is not set
++CONFIG_IRCOMM=m
++CONFIG_IRDA_ULTRA=y
++# CONFIG_IRDA_CACHE_LAST_LSAP is not set
++# CONFIG_IRDA_FAST_RR is not set
++CONFIG_IRDA_DEBUG=y
++
++#
++# Infrared-port device drivers
++#
++CONFIG_IRTTY_SIR=m
++# CONFIG_IRPORT_SIR is not set
++# 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_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=y
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=y
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=y
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
++# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
++# CONFIG_BLK_DEV_IDEDISK_IBM is not set
++# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
++# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
++# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
++# CONFIG_BLK_DEV_IDEDISK_WD is not set
++# CONFIG_BLK_DEV_COMMERIAL is not set
++# CONFIG_BLK_DEV_TIVO is not set
++CONFIG_BLK_DEV_IDECS=m
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_IDE_MODES is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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=m
++CONFIG_INPUT_MOUSEDEV=m
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
++# CONFIG_INPUT_JOYDEV is not set
++CONFIG_INPUT_EVDEV=y
++
++#
++# Character devices
++#
++CONFIG_VT=y
++# CONFIG_VT_CONSOLE is not set
++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_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_IDP_KEYB is not set
++CONFIG_MATRIX_KEYBOARD=y
++# CONFIG_SA1111_PS2_KEYB is not set
++CONFIG_UNIX98_PTYS=y
++CONFIG_UNIX98_PTY_COUNT=256
++
++#
++# I2C support
++#
++# CONFIG_I2C is not set
++
++#
++# 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=y
++# CONFIG_ATIXL_BUSMOUSE is not set
++# CONFIG_LOGIBUSMOUSE is not set
++# CONFIG_MS_BUSMOUSE is not set
++CONFIG_MOUSE=y
++CONFIG_PSMOUSE=y
++# CONFIG_82C710_MOUSE is not set
++# CONFIG_PC110_PAD is not set
++# CONFIG_MK712_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
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++# CONFIG_PXA_RTC is not set
++# 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
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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_BFS_FS is not set
++CONFIG_EXT3_FS=m
++CONFIG_JBD=m
++# 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_CRAMFS=y
++# CONFIG_TMPFS is not set
++CONFIG_RAMFS=y
++# CONFIG_ISO9660_FS is not set
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS 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=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++CONFIG_EXT2_FS=y
++# 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=y
++# CONFIG_ROOT_NFS is not set
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=y
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=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
++CONFIG_ZLIB_FS_INFLATE=y
++
++#
++# Partition Types
++#
++CONFIG_PARTITION_ADVANCED=y
++# CONFIG_ACORN_PARTITION is not set
++# CONFIG_OSF_PARTITION is not set
++# CONFIG_AMIGA_PARTITION is not set
++# CONFIG_ATARI_PARTITION is not set
++# CONFIG_MAC_PARTITION is not set
++CONFIG_MSDOS_PARTITION=y
++# CONFIG_BSD_DISKLABEL is not set
++# CONFIG_MINIX_SUBPARTITION is not set
++# CONFIG_SOLARIS_X86_PARTITION is not set
++# CONFIG_UNIXWARE_DISKLABEL is not set
++# CONFIG_LDM_PARTITION is not set
++# CONFIG_SGI_PARTITION is not set
++# CONFIG_ULTRIX_PARTITION is not set
++# CONFIG_SUN_PARTITION is not set
++# 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 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# 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_PXA=y
++# CONFIG_FB_CYBER2000 is not set
++# CONFIG_FB_VIRTUAL is not set
++# CONFIG_FBCON_ADVANCED is not set
++CONFIG_FBCON_CFB2=y
++CONFIG_FBCON_CFB4=y
++CONFIG_FBCON_CFB8=y
++CONFIG_FBCON_CFB16=y
++# 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_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_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_WAVEARTIST is not set
++CONFIG_SOUND_PXA_AC97=y
++# CONFIG_SOUND_TVMIXER is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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=m
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++# CONFIG_DEBUG_INFO is not set
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++CONFIG_DEBUG_SLAB=y
++# CONFIG_MAGIC_SYSRQ is not set
++# CONFIG_DEBUG_SPINLOCK is not set
++# CONFIG_DEBUG_WAITQ is not set
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++# CONFIG_DEBUG_LL is not set
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/trizeps2
+@@ -0,0 +1,873 @@
++#
++# 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
++
++#
++# 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_ADSBITSY 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_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_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_H3600_SLEEVE 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_TRIZEPS2=y
++CONFIG_TRIZEPS2=y
++CONFIG_PXA_USB=y
++# CONFIG_PXA_USB_NETLINK is not set
++# CONFIG_PXA_USB_CHAR is not set
++
++#
++# 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_P720T is not set
++# CONFIG_ARCH_FORTUNET is not set
++# CONFIG_ARCH_EP7211 is not set
++# CONFIG_ARCH_EP7212 is not set
++# CONFIG_ARCH_ACORN 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_PLD is not set
++# CONFIG_CPU_ARM926T is not set
++# CONFIG_CPU_ARM1020 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++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
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++# CONFIG_BSD_PROCESS_ACCT is not set
++CONFIG_SYSCTL=y
++CONFIG_FPE_NWFPE=y
++# 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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="root=/dev/mtdblock3 rw console=ttyS0,38400 mem=32M noinitrd init=/linuxrc"
++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=y
++CONFIG_MTD_CMDLINE_PARTS=y
++# 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
++
++#
++# 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 is not set
++CONFIG_MTD_CFI_INTELEXT=y
++# CONFIG_MTD_CFI_AMDSTD is not set
++# 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_PHYSMAP is not set
++# CONFIG_MTD_LUBBOCK is not set
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++# CONFIG_MTD_PXA_CERF is not set
++# CONFIG_MTD_EPXA10DB is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++CONFIG_MTD_TRIZEPS2=y
++# CONFIG_MTD_PCI 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_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++# CONFIG_MTD_NAND 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=y
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_RAM is not set
++# CONFIG_BLK_DEV_INITRD is not set
++
++#
++# 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 is not set
++# CONFIG_NETLINK_DEV is not set
++# 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 is not set
++CONFIG_IP_PNP_BOOTP=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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 is not set
++# 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 is not set
++# 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=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_MYRI_SBUS is not set
++# CONFIG_NS83820 is not set
++# CONFIG_HAMACHI is not set
++# CONFIG_YELLOWFIN 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 is not set
++# 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 is not set
++# CONFIG_PCMCIA_HERMES is not set
++CONFIG_AIRO_CS=m
++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=m
++# 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=y
++# CONFIG_PCMCIA_RAYCS is not set
++# CONFIG_PCMCIA_NETWAVE is not set
++# CONFIG_PCMCIA_WAVELAN is not set
++# CONFIG_AIRONET4500_CS is not set
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++# CONFIG_IRDA is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++CONFIG_IDE=m
++
++#
++# IDE, ATA and ATAPI Block devices
++#
++CONFIG_BLK_DEV_IDE=m
++# CONFIG_BLK_DEV_HD_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++CONFIG_BLK_DEV_IDEDISK=m
++# CONFIG_IDEDISK_MULTI_MODE is not set
++# CONFIG_IDEDISK_STROKE is not set
++# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
++# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
++# CONFIG_BLK_DEV_IDEDISK_IBM is not set
++# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
++# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
++# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
++# CONFIG_BLK_DEV_IDEDISK_WD is not set
++# CONFIG_BLK_DEV_COMMERIAL is not set
++# CONFIG_BLK_DEV_TIVO is not set
++CONFIG_BLK_DEV_IDECS=m
++# CONFIG_BLK_DEV_IDECD is not set
++# CONFIG_BLK_DEV_IDETAPE is not set
++# CONFIG_BLK_DEV_IDEFLOPPY is not set
++# CONFIG_BLK_DEV_IDESCSI is not set
++# CONFIG_IDE_TASK_IOCTL is not set
++# CONFIG_BLK_DEV_CMD640 is not set
++# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
++# CONFIG_BLK_DEV_ISAPNP is not set
++# CONFIG_IDE_CHIPSETS is not set
++# CONFIG_IDEDMA_AUTO is not set
++# CONFIG_DMA_NONPCI is not set
++# CONFIG_BLK_DEV_IDE_MODES is not set
++# CONFIG_BLK_DEV_ATARAID is not set
++# CONFIG_BLK_DEV_ATARAID_PDC is not set
++# CONFIG_BLK_DEV_ATARAID_HPT is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_KEYBDEV is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++# CONFIG_VT_CONSOLE is not set
++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_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=256
++
++#
++# 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=y
++CONFIG_I2C_PROC=y
++# CONFIG_I2C_DS1307 is not set
++
++#
++# 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_QIC02_TAPE is not set
++
++#
++# Watchdog Cards
++#
++# CONFIG_WATCHDOG is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++CONFIG_PXA_RTC=y
++# 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 is not set
++CONFIG_TRIZEPS2_TTLIO=m
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV 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_BFS_FS is not set
++CONFIG_EXT3_FS=y
++CONFIG_JBD=y
++# 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_CRAMFS 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_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 is not set
++# CONFIG_DEVFS_MOUNT is not set
++# CONFIG_DEVFS_DEBUG is not set
++CONFIG_DEVPTS_FS=y
++# CONFIG_QNX4FS_FS is not set
++# CONFIG_QNX4FS_RW is not set
++# CONFIG_ROMFS_FS is not set
++CONFIG_EXT2_FS=y
++# 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=m
++# CONFIG_NFS_V3 is not set
++# CONFIG_ROOT_NFS is not set
++# CONFIG_NFSD is not set
++# CONFIG_NFSD_V3 is not set
++CONFIG_SUNRPC=m
++CONFIG_LOCKD=m
++# 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
++# CONFIG_ZLIB_FS_INFLATE 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 is not set
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++# CONFIG_NLS_CODEPAGE_850 is not set
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++# CONFIG_NLS_ISO8859_15 is not set
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++# CONFIG_NLS_UTF8 is not set
++
++#
++# 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_PXA=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=y
++CONFIG_FBCON_FONTS=y
++# CONFIG_FONT_8x8 is not set
++# CONFIG_FONT_8x16 is not set
++# CONFIG_FONT_SUN8x16 is not set
++# CONFIG_FONT_PEARL_8x8 is not set
++CONFIG_FONT_ACORN_8x8=y
++
++#
++# Sound
++#
++CONFIG_SOUND=y
++# 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_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_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
++CONFIG_MCP_UCB1X00_TS_COMPAT=y
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++CONFIG_DEBUG_INFO=y
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++# CONFIG_DEBUG_SLAB is not set
++CONFIG_MAGIC_SYSRQ=y
++# CONFIG_DEBUG_SPINLOCK is not set
++# CONFIG_DEBUG_WAITQ is not set
++CONFIG_DEBUG_BUGVERBOSE=y
++CONFIG_DEBUG_ERRORS=y
++CONFIG_DEBUG_LL=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
+--- linux-2.4.27/arch/arm/kernel/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/kernel/Makefile
+@@ -10,7 +10,7 @@
+ HEAD_OBJ  = head-$(PROCESSOR).o
+ ENTRY_OBJ = entry-$(PROCESSOR).o
+-AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR)
++AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -DDATAADDR=$(DATAADDR)
+ AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR)
+ # This is depreciated.
+@@ -45,7 +45,7 @@
+                  $(CONFIG_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) \
+                  $(CONFIG_ARCH_SA1100) $(CONFIG_ARCH_CAMELOT) \
+                  $(CONFIG_ARCH_MX1ADS) $(CONFIG_ARCH_OMAHA) \
+-                 $(CONFIG_ARCH_AT91RM9200)
++                 $(CONFIG_ARCH_AT91RM9200) $(CONFIG_ARCH_PXA)
+ ifneq ($(findstring y,$(no-irq-arch)),y)
+   obj-y               += irq-arch.o
+--- linux-2.4.27/arch/arm/kernel/debug-armv.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/kernel/debug-armv.S
+@@ -221,6 +221,31 @@
+               bne     1001b
+               .endm
++#elif defined(CONFIG_ARCH_PXA)
++
++              .macro  addruart,rx
++              mrc     p15, 0, \rx, c1, c0
++              tst     \rx, #1                 @ MMU enabled?
++              moveq   \rx, #0x40000000                @ physical
++              movne   \rx, #io_p2v(0x40000000)        @ virtual
++              orr     \rx, \rx, #0x00100000           @ FFUART
++              .endm
++
++              .macro  senduart,rd,rx
++              str     \rd, [\rx, #0]
++              .endm
++
++              .macro  busyuart,rd,rx
++1002:         ldr     \rd, [\rx, #0x14]
++              tst     \rd, #(1 << 6)
++              beq     1002b
++              .endm
++
++              .macro  waituart,rd,rx
++1001:         ldr     \rd, [\rx, #0x14]
++              tst     \rd, #(1 << 5)
++              beq     1001b
++              .endm
+ #elif defined(CONFIG_ARCH_CLPS7500)
+               .macro  addruart,rx
+               mov     \rx, #0xe0000000
+--- linux-2.4.27/arch/arm/kernel/entry-armv.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/kernel/entry-armv.S
+@@ -615,6 +615,27 @@
+               .text
+               .endm
++#elif CONFIG_ARCH_PXA
++
++              .macro  disable_fiq
++              .endm
++
++              .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
++              mov     \base, #io_p2v(0x40000000)      @ IIR Ctl = 0x40d00000
++              add     \base, \base, #0x00d00000
++              ldr     \irqstat, [\base, #0]           @ ICIP
++              ldr     \irqnr, [\base, #4]             @ ICMR
++              ands    \irqstat, \irqstat, \irqnr
++              beq     1001f
++              rsb     \irqnr, \irqstat, #0
++              and     \irqstat, \irqstat, \irqnr
++              clz     \irqnr, \irqstat
++              rsb     \irqnr, \irqnr, #(31 - PXA_IRQ_SKIP)
++1001:
++              .endm
++
++              .macro  irq_prio_table
++              .endm
+ #else
+ #error Unknown architecture
+ #endif
+@@ -891,9 +912,17 @@
+               stmfd   sp!, {r4 - sl, fp, lr}          @ Store most regs on stack
+               mrs     ip, cpsr
+               str     ip, [sp, #-4]!                  @ Save cpsr_SVC
++#ifdef CONFIG_CPU_XSCALE
++              mra     r4, r5, acc0
++              stmfd   sp!, {r4, r5}
++#endif
+               str     sp, [r0, #TSS_SAVE]             @ Save sp_SVC
+               ldr     sp, [r1, #TSS_SAVE]             @ Get saved sp_SVC
+               ldr     r2, [r1, #TSS_DOMAIN]
++#ifdef CONFIG_CPU_XSCALE
++              ldmfd   sp!, {r4, r5}
++              mar     acc0, r4, r5
++#endif
+               ldr     ip, [sp], #4
+               mcr     p15, 0, r2, c3, c0              @ Set domain register
+               msr     spsr, ip                        @ Save tasks CPSR into SPSR for this return
+--- linux-2.4.27/arch/arm/kernel/head-armv.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/kernel/head-armv.S
+@@ -30,6 +30,7 @@
+  *
+  * swapper_pg_dir, pgtbl and krnladr are all closely related.
+  */
++#ifndef CONFIG_XIP_KERNEL
+ #if (TEXTADDR & 0xffff) != 0x8000
+ #error TEXTADDR must start at 0xXXXX8000
+ #endif
+@@ -41,6 +42,26 @@
+               adr     \reg, stext
+               sub     \reg, \reg, #0x4000
+               .endm
++#else
++#if (DATAADDR & 0xffff) != 0x8000
++#error DATAADDR must start at 0xXXXX8000
++#endif
++
++#define       PAGE_OFFSET     0xc0000000
++#ifdef CONFIG_ARCH_LUBBOCK
++#define       PHYS_OFFSET     0xa0000000
++#elif CONFIG_ARCH_OMAP
++#define       PHYS_OFFSET     0x10000000
++#endif
++
++              .globl  SYMBOL_NAME(swapper_pg_dir)
++              .equ    SYMBOL_NAME(swapper_pg_dir), DATAADDR - 0x4000
++
++              .macro  pgtbl, reg, rambase
++              ldr     \reg, PGTBL
++              add     \reg, \reg, #PHYS_OFFSET - PAGE_OFFSET
++              .endm
++#endif
+ /*
+  * Since the page table is closely related to the kernel start address, we
+@@ -131,6 +152,32 @@
+               mov     r1, #MACH_TYPE_L7200
+ #endif
++#ifdef CONFIG_XIP_KERNEL
++
++#if defined(CONFIG_ARCH_LUBBOCK)
++              mov     r1, #MACH_TYPE_LUBBOCK
++#endif
++
++              @ Data cache might be active.
++              @ Be sure to flush kernel binary out of the cache,
++              @ whatever state it is, before it is turned off.
++              @ This is done by fetching through currently executed
++              @ memory to be sure we hit the same cache.
++              bic     r2, pc, #0x1f
++              add     r3, r2, #0x10000        @ 64 kb is quite enough...
++1:            ldr     r0, [r2], #32
++              teq     r2, r3
++              bne     1b
++              mcr     p15, 0, r0, c7, c10, 4  @ drain WB
++              mcr     p15, 0, r0, c7, c7, 0   @ flush I & D caches
++
++              @ disabling MMU and caches
++              mrc     p15, 0, r0, c1, c0, 0   @ read control reg
++              bic     r0, r0, #0x05           @ clear DC, MMU
++              bic     r0, r0, #0x1000         @ clear Icache
++              mcr     p15, 0, r0, c1, c0, 0
++#endif
++
+               mov     r0, #F_BIT | I_BIT | MODE_SVC   @ make sure svc mode
+               msr     cpsr_c, r0                      @ and all irqs disabled
+               bl      __lookup_processor_type
+@@ -179,6 +226,17 @@
+  */
+               .align  5
+ __mmap_switched:
++#ifdef CONFIG_XIP_KERNEL
++              ldr     r3, ETEXT                       @ data section copy
++              ldr     r4, SDATA
++              ldr     r5, EDATA
++1:
++              ldr     r6, [r3], #4
++              str     r6, [r4], #4
++              cmp     r4, r5
++              blo     1b
++#endif
++
+               adr     r3, __switch_data + 4
+               ldmia   r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
+                                                       @ sp = stack pointer
+@@ -233,6 +291,8 @@
+               teq     r0, r2
+               bne     1b
++#ifndef CONFIG_XIP_KERNEL
++
+               /*
+                * Create identity mapping for first MB of kernel to
+                * cater for the MMU enable.  This identity mapping
+@@ -271,6 +331,43 @@
+               add     r3, r8, r2                      @ flags + rambase
+               str     r3, [r0]
++#else /* CONFIG_XIP_KERNEL */
++
++              mov     r3, pc, lsr #20
++              mov     r3, r3, lsl #20         @ phys kernel start
++
++              add     r0, r4, r3, lsr #18
++              orr     r3, r3, r8
++              str     r3, [r0]
++
++              mov     r0, #TEXTADDR & 0xff000000
++              add     r0, r0, #TEXTADDR & 0x00f00000  @ virt kernel start
++              add     r0, r4, r0, lsr #18
++              add     r2, r3, #4 << 20                @ kernel + 4MB
++
++1:
++              str     r3, [r0], #4
++              add     r3, r3, #1 << 20
++              cmp     r3, r2
++              bne     1b
++
++              bic     r3, r4, #0x000ff000             @ ram start
++              add     r0, r4, r3, lsr #18
++              orr     r3, r3, r8
++              str     r3, [r0], #4
++
++              add     r0, r3, #PAGE_OFFSET - PHYS_OFFSET
++              add     r0, r4, r0, lsr #18
++              add     r2, r3, #4 << 20                @ ram + 4MB
++
++1:
++              str     r3, [r0], #4
++              add     r3, r3, #1 << 20
++              cmp     r3, r2
++              bne     1b
++
++#endif        /* CONFIG_XIP_KERNEL */
++
+               bic     r8, r8, #0x0c                   @ turn off cacheable
+                                                       @ and bufferable bits
+ #ifdef CONFIG_DEBUG_LL
+@@ -433,3 +530,13 @@
+               mov     pc, lr
+ 2:            ldmib   r4, {r5, r6, r7}                @ found, get results
+               mov     pc, lr
++
++#ifdef CONFIG_XIP_KERNEL
++
++PGTBL:                .long   SYMBOL_NAME(swapper_pg_dir)
++
++ETEXT:                .long   SYMBOL_NAME(_endtext)
++SDATA:                .long   SYMBOL_NAME(_sdata)
++EDATA:                .long   SYMBOL_NAME(__bss_start)
++
++#endif
+--- linux-2.4.27/arch/arm/kernel/setup.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/kernel/setup.c
+@@ -55,6 +55,10 @@
+ extern void reboot_setup(char *str);
+ extern int root_mountflags;
+ extern int _stext, _text, _etext, _edata, _end;
++#ifdef CONFIG_XIP_KERNEL
++extern int _endtext, _sdata;
++#endif
++
+ unsigned int processor_id;
+ unsigned int __machine_arch_type;
+@@ -105,6 +109,109 @@
+ #define lp1 io_res[1]
+ #define lp2 io_res[2]
++#ifdef CONFIG_CPU_32
++static const char *cache_types[16] = {
++      "write-through",
++      "write-back",
++      "write-back",
++      "undefined 3",
++      "undefined 4",
++      "undefined 5",
++      "write-back",
++      "write-back",
++      "undefined 8",
++      "undefined 9",
++      "undefined 10",
++      "undefined 11",
++      "undefined 12",
++      "undefined 13",
++      "undefined 14",
++      "undefined 15",
++};
++
++static const char *cache_clean[16] = {
++      "not required",
++      "read-block",
++      "cp15 c7 ops",
++      "undefined 3",
++      "undefined 4",
++      "undefined 5",
++      "cp15 c7 ops",
++      "cp15 c7 ops",
++      "undefined 8",
++      "undefined 9",
++      "undefined 10",
++      "undefined 11",
++      "undefined 12",
++      "undefined 13",
++      "undefined 14",
++      "undefined 15",
++};
++
++static const char *cache_lockdown[16] = {
++      "not supported",
++      "not supported",
++      "not supported",
++      "undefined 3",
++      "undefined 4",
++      "undefined 5",
++      "format A",
++      "format B",
++      "undefined 8",
++      "undefined 9",
++      "undefined 10",
++      "undefined 11",
++      "undefined 12",
++      "undefined 13",
++      "undefined 14",
++      "undefined 15",
++};
++
++#define CACHE_TYPE(x) (((x) >> 25) & 15)
++#define CACHE_S(x)    ((x) & (1 << 24))
++#define CACHE_DSIZE(x)        (((x) >> 12) & 4095)    /* only if S=1 */
++#define CACHE_ISIZE(x)        ((x) & 4095)
++
++#define CACHE_SIZE(y) (((y) >> 6) & 7)
++#define CACHE_ASSOC(y)        (((y) >> 3) & 7)
++#define CACHE_M(y)    ((y) & (1 << 2))
++#define CACHE_LINE(y) ((y) & 3)
++
++static inline void dump_cache(const char *prefix, unsigned int cache)
++{
++      unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
++
++      printk("%s size %dK associativity %d line length %d sets %d\n",
++              prefix,
++              mult << (8 + CACHE_SIZE(cache)),
++              (mult << CACHE_ASSOC(cache)) >> 1,
++              8 << CACHE_LINE(cache),
++              1 << (6 + CACHE_SIZE(cache) - CACHE_ASSOC(cache) -
++                      CACHE_LINE(cache)));
++}
++
++static inline void dump_cpu_cache_id(void)
++{
++      unsigned int cache_info;
++
++      asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (cache_info));
++
++      if (cache_info == processor_id)
++              return;
++
++      printk("CPU: D %s cache\n", cache_types[CACHE_TYPE(cache_info)]);
++      if (CACHE_S(cache_info)) {
++              dump_cache("CPU: I cache", CACHE_ISIZE(cache_info));
++              dump_cache("CPU: D cache", CACHE_DSIZE(cache_info));
++      } else {
++              dump_cache("CPU: cache", CACHE_ISIZE(cache_info));
++      }
++}
++
++#else
++#define dump_cpu_cache_id() do { } while (0)
++#endif
++
+ static void __init setup_processor(void)
+ {
+       extern struct proc_info_list __proc_info_begin, __proc_info_end;
+@@ -272,7 +379,11 @@
+       kernel_code.start  = __virt_to_phys(init_mm.start_code);
+       kernel_code.end    = __virt_to_phys(init_mm.end_code - 1);
++#ifndef CONFIG_XIP_KERNEL
+       kernel_data.start  = __virt_to_phys(init_mm.end_code);
++#else
++      kernel_data.start  = __virt_to_phys(init_mm.start_data);
++#endif
+       kernel_data.end    = __virt_to_phys(init_mm.brk - 1);
+       for (i = 0; i < mi->nr_banks; i++) {
+@@ -531,7 +642,12 @@
+       }
+       init_mm.start_code = (unsigned long) &_text;
++#ifndef CONFIG_XIP_KERNEL
+       init_mm.end_code   = (unsigned long) &_etext;
++#else
++      init_mm.end_code   = (unsigned long) &_endtext;
++      init_mm.start_data   = (unsigned long) &_sdata;
++#endif
+       init_mm.end_data   = (unsigned long) &_edata;
+       init_mm.brk        = (unsigned long) &_end;
+@@ -568,6 +684,41 @@
+       NULL
+ };
++static const char *proc_arch[16] = {
++      "undefined 0",
++      "4",
++      "4T",
++      "5",
++      "5T",
++      "5TE",
++      "undefined 6",
++      "undefined 7",
++      "undefined 8",
++      "undefined 9",
++      "undefined 10",
++      "undefined 11",
++      "undefined 12",
++      "undefined 13",
++      "undefined 14",
++      "undefined 15"
++};
++
++static void
++c_show_cache(struct seq_file *m, const char *type, unsigned int cache)
++{
++      unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
++
++      seq_printf(m, "%s size\t\t: %d\n"
++                    "%s assoc\t\t: %d\n"
++                    "%s line length\t: %d\n"
++                    "%s sets\t\t: %d\n",
++              type, mult << (8 + CACHE_SIZE(cache)),
++              type, (mult << CACHE_ASSOC(cache)) >> 1,
++              type, 8 << CACHE_LINE(cache),
++              type, 1 << (6 + CACHE_SIZE(cache) - CACHE_ASSOC(cache) -
++                          CACHE_LINE(cache)));
++}
++
+ static int c_show(struct seq_file *m, void *v)
+ {
+       int i;
+@@ -586,7 +737,60 @@
+               if (elf_hwcap & (1 << i))
+                       seq_printf(m, "%s ", hwcap_str[i]);
+-      seq_puts(m, "\n\n");
++      seq_puts(m, "\n");
++
++      if ((processor_id & 0x0000f000) == 0x00000000) {
++              /* pre-ARM7 */
++              seq_printf(m, "CPU part\t\t: %07x\n", processor_id >> 4);
++      } else if ((processor_id & 0x0000f000) == 0x00007000) {
++              /* ARM7 */
++              seq_printf(m, "CPU implementor\t: 0x%02x\n"
++                            "CPU architecture: %s\n"
++                            "CPU variant\t: 0x%02x\n"
++                            "CPU part\t: 0x%03x\n",
++                         processor_id >> 24,
++                         processor_id & (1 << 23) ? "4T" : "3",
++                         (processor_id >> 16) & 127,
++                         (processor_id >> 4) & 0xfff);
++      } else {
++              /* post-ARM7 */
++              seq_printf(m, "CPU implementor\t: 0x%02x\n"
++                            "CPU architecture: %s\n"
++                            "CPU variant\t: 0x%x\n"
++                            "CPU part\t: 0x%03x\n",
++                         processor_id >> 24,
++                         proc_arch[(processor_id >> 16) & 15],
++                         (processor_id >> 20) & 15,
++                         (processor_id >> 4) & 0xfff);
++      }
++      seq_printf(m, "CPU revision\t: %d\n", processor_id & 15);
++
++#ifdef CONFIG_CPU_32
++      {
++              unsigned int cache_info;
++
++              asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (cache_info));
++              if (cache_info != processor_id) {
++                      seq_printf(m, "Cache type\t: %s\n"
++                                    "Cache clean\t: %s\n"
++                                    "Cache lockdown\t: %s\n"
++                                    "Cache unified\t: %s\n",
++                                 cache_types[CACHE_TYPE(cache_info)],
++                                 cache_clean[CACHE_TYPE(cache_info)],
++                                 cache_lockdown[CACHE_TYPE(cache_info)],
++                                 CACHE_S(cache_info) ? "harvard" : "unified");
++
++                      if (CACHE_S(cache_info)) {
++                              c_show_cache(m, "I", CACHE_ISIZE(cache_info));
++                              c_show_cache(m, "D", CACHE_DSIZE(cache_info));
++                      } else {
++                              c_show_cache(m, "Cache", CACHE_ISIZE(cache_info));
++                      }
++              }
++      }
++#endif
++
++      seq_puts(m, "\n");
+       seq_printf(m, "Hardware\t: %s\n", machine_name);
+       seq_printf(m, "Revision\t: %04x\n", system_rev);
+--- linux-2.4.27/arch/arm/lib/copy_page.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/lib/copy_page.S
+@@ -13,6 +13,8 @@
+ #include <asm/assembler.h>
+ #include <asm/constants.h>
++#define COPY_COUNT (PAGE_SZ/64 PLD( -1 ))
++
+               .text
+               .align  5
+ /*
+@@ -23,9 +25,13 @@
+  */
+ ENTRY(copy_page)
+               stmfd   sp!, {r4, lr}                   @       2
+-              mov     r2, #PAGE_SZ/64                 @       1
++      PLD(    pld     [r1, #0]                )
++      PLD(    pld     [r1, #32]               )
++              mov     r2, #COPY_COUNT                 @       1
+               ldmia   r1!, {r3, r4, ip, lr}           @       4+1
+-1:            stmia   r0!, {r3, r4, ip, lr}           @       4
++1:    PLD(    pld     [r1, #64]               )
++      PLD(    pld     [r1, #96]               )
++2:            stmia   r0!, {r3, r4, ip, lr}           @       4
+               ldmia   r1!, {r3, r4, ip, lr}           @       4+1
+               stmia   r0!, {r3, r4, ip, lr}           @       4
+               ldmia   r1!, {r3, r4, ip, lr}           @       4+1
+@@ -33,6 +39,8 @@
+               ldmia   r1!, {r3, r4, ip, lr}           @       4
+               subs    r2, r2, #1                      @       1
+               stmia   r0!, {r3, r4, ip, lr}           @       4
+-              ldmneia r1!, {r3, r4, ip, lr}           @       4
+-              bne     1b                              @       1
++              ldmgtia r1!, {r3, r4, ip, lr}           @       4
++              bgt     1b                              @       1
++      PLD(    ldmeqia r1!, {r3, r4, ip, lr}   )
++      PLD(    beq     2b                      )
+               LOADREGS(fd, sp!, {r4, pc})             @       3
+--- linux-2.4.27/arch/arm/lib/findbit.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/lib/findbit.S
+@@ -43,7 +43,15 @@
+ /*
+  * One or more bits in the LSB of r3 are assumed to be set.
+  */
+-.found:               tst     r3, #0x0f
++.found:
++#if __LINUX_ARM_ARCH__ >= 5
++              rsb     r1, r3, #0
++              and     r3, r3, r1
++              clz     r3, r3
++              rsb     r3, r3, #31
++              add     r0, r2, r3
++#else
++              tst     r3, #0x0f
+               addeq   r2, r2, #4
+               movne   r3, r3, lsl #4
+               tst     r3, #0x30
+@@ -52,5 +60,6 @@
+               tst     r3, #0x40
+               addeq   r2, r2, #1
+               mov     r0, r2
++#endif
+               RETINSTR(mov,pc,lr)
+--- linux-2.4.27/arch/arm/lib/getuser.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/lib/getuser.S
+@@ -18,7 +18,7 @@
+  * Inputs:    r0 contains the address
+  * Outputs:   r0 is the error code
+  *            r1, r2 contains the zero-extended value
+- *            lr corrupted
++ *            ip, lr corrupted
+  *
+  * No other registers must be altered.  (see include/asm-arm/uaccess.h
+  * for specific ASM register usage).
+@@ -42,14 +42,14 @@
+       .global __get_user_2
+ __get_user_2:
+-      bic     r2, sp, #0x1f00
+-      bic     r2, r2, #0x00ff
+-      ldr     r2, [r2, #TSK_ADDR_LIMIT]
+-      sub     r2, r2, #2
+-      cmp     r0, r2
++      bic     ip, sp, #0x1f00
++      bic     ip, ip, #0x00ff
++      ldr     ip, [ip, #TSK_ADDR_LIMIT]
++      sub     ip, ip, #2
++      cmp     r0, ip
+ 2:    ldrlsbt r1, [r0], #1
+-3:    ldrlsbt r2, [r0]
+-      orrls   r1, r1, r2, lsl #8
++3:    ldrlsbt ip, [r0]
++      orrls   r1, r1, ip, lsl #8
+       movls   r0, #0
+       movls   pc, lr
+       b       __get_user_bad
+--- linux-2.4.27/arch/arm/lib/memcpy.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/lib/memcpy.S
+@@ -8,6 +8,9 @@
+  * published by the Free Software Foundation.
+  *
+  *  ASM optimised string functions
++ *
++ *  Big Endian, prefetching and code factorization provided by Nicolas Pitre:
++ *    Copyright (C) 2002-2003 MontaVista Software, Inc.
+  */
+ #include <linux/linkage.h>
+ #include <asm/assembler.h>
+@@ -27,15 +30,16 @@
+ /*
+  * Prototype: void memcpy(void *to,const void *from,unsigned long n);
+- * ARM3: cant use memcopy here!!!
+  */
+ ENTRY(memcpy)
+ ENTRY(memmove)
+               ENTER
+-              cmp     r1, r0
+-              bcc     19f
++              subs    ip, r0, r1
++              cmphi   r2, ip
++              bhi     18f
+               subs    r2, r2, #4
+               blt     6f
++      PLD(    pld     [r1, #0]                )
+               ands    ip, r0, #3
+               bne     7f
+               ands    ip, r1, #3
+@@ -43,29 +47,59 @@
+ 1:            subs    r2, r2, #8
+               blt     5f
+-              subs    r2, r2, #0x14
+-              blt     3f
+-2:            ldmia   r1!,{r3 - r9, ip}
+-              stmia   r0!,{r3 - r9, ip}
++              subs    r2, r2, #20
++              blt     4f
++
++      PLD(    subs    r2, r2, #65             )
++      PLD(    blt     3f                      )
++      PLD(    pld     [r1, #32]               )
++
++      PLD(    @ cache alignment               )
++      PLD(    ands    ip, r1, #31             )
++      PLD(    pld     [r1, #64]               )
++      PLD(    beq     2f                      )
++      PLD(    rsb     ip, ip, #32             )
++      PLD(    cmp     r2, ip                  )
++      PLD(    pld     [r1, #96]               )
++      PLD(    blt     2f                      )
++      PLD(    cmp     ip, #16                 )
++      PLD(    sub     r2, r2, ip              )
++      PLD(    ldmgeia r1!, {r3 - r6}          )
++      PLD(    stmgeia r0!, {r3 - r6}          )
++      PLD(    beq     2f                      )
++      PLD(    and     ip, ip, #15             )
++      PLD(    cmp     ip, #8                  )
++      PLD(    ldr     r3, [r1], #4            )
++      PLD(    ldrge   r4, [r1], #4            )
++      PLD(    ldrgt   r5, [r1], #4            )
++      PLD(    str     r3, [r0], #4            )
++      PLD(    strge   r4, [r0], #4            )
++      PLD(    strgt   r5, [r0], #4            )
++
++2:    PLD(    pld     [r1, #96]               )
++3:            ldmia   r1!, {r3 - r9, ip}
+               subs    r2, r2, #32
++              stmia   r0!, {r3 - r9, ip}
+               bge     2b
+-              cmn     r2, #16
++      PLD(    cmn     r2, #65                 )
++      PLD(    bge     3b                      )
++      PLD(    add     r2, r2, #65             )
++4:            cmn     r2, #16
+               ldmgeia r1!, {r3 - r6}
++              subge   r2, r2, #16
+               stmgeia r0!, {r3 - r6}
+-              subge   r2, r2, #0x10
+-3:            adds    r2, r2, #0x14
+-4:            ldmgeia r1!, {r3 - r5}
++              adds    r2, r2, #20
++              ldmgeia r1!, {r3 - r5}
++              subge   r2, r2, #12
+               stmgeia r0!, {r3 - r5}
+-              subges  r2, r2, #12
+-              bge     4b
+ 5:            adds    r2, r2, #8
+               blt     6f
+               subs    r2, r2, #4
+               ldrlt   r3, [r1], #4
+               ldmgeia r1!, {r4, r5}
++              subge   r2, r2, #4
+               strlt   r3, [r0], #4
+               stmgeia r0!, {r4, r5}
+-              subge   r2, r2, #4
+ 6:            adds    r2, r2, #4
+               EXITEQ
+@@ -92,122 +126,175 @@
+               beq     1b
+ 8:            bic     r1, r1, #3
+-              ldr     r7, [r1], #4
+               cmp     ip, #2
+-              bgt     15f
+-              beq     11f
++              ldr     lr, [r1], #4
++              bgt     17f
++              beq     16f
++
++
++              .macro  forward_copy_shift pull push
++
+               cmp     r2, #12
+-              blt     10f
+-              sub     r2, r2, #12
+-9:            mov     r3, r7, lsr #8
+-              ldmia   r1!, {r4 - r7}
+-              orr     r3, r3, r4, lsl #24
+-              mov     r4, r4, lsr #8
+-              orr     r4, r4, r5, lsl #24
+-              mov     r5, r5, lsr #8
+-              orr     r5, r5, r6, lsl #24
+-              mov     r6, r6, lsr #8
+-              orr     r6, r6, r7, lsl #24
++      PLD(    pld     [r1, #0]                )
++              blt     14f
++              subs    r2, r2, #28
++              blt     12f
++
++      PLD(    subs    r2, r2, #97             )
++      PLD(    blt     11f                     )
++      PLD(    pld     [r1, #32]               )
++
++      PLD(    @ cache alignment               )
++      PLD(    rsb     ip, r1, #36             )
++      PLD(    pld     [r1, #64]               )
++      PLD(    ands    ip, ip, #31             )
++      PLD(    pld     [r1, #96]               )
++      PLD(    beq     10f                     )
++      PLD(    cmp     r2, ip                  )
++      PLD(    pld     [r1, #128]              )
++      PLD(    blt     10f                     )
++      PLD(    sub     r2, r2, ip              )
++9:    PLD(    mov     r3, lr, pull #\pull     )
++      PLD(    ldr     lr, [r1], #4            )
++      PLD(    subs    ip, ip, #4              )
++      PLD(    orr     r3, r3, lr, push #\push )
++      PLD(    str     r3, [r0], #4            )
++      PLD(    bgt     9b                      )
++
++10:   PLD(    pld     [r1, #128]              )
++11:           mov     r3, lr, pull #\pull
++              ldmia   r1!, {r4 - r9, ip, lr}
++              subs    r2, r2, #32
++              orr     r3, r3, r4, push #\push
++              mov     r4, r4, pull #\pull
++              orr     r4, r4, r5, push #\push
++              mov     r5, r5, pull #\pull
++              orr     r5, r5, r6, push #\push
++              mov     r6, r6, pull #\pull
++              orr     r6, r6, r7, push #\push
++              mov     r7, r7, pull #\pull
++              orr     r7, r7, r8, push #\push
++              mov     r8, r8, pull #\pull
++              orr     r8, r8, r9, push #\push
++              mov     r9, r9, pull #\pull
++              orr     r9, r9, ip, push #\push
++              mov     ip, ip, pull #\pull
++              orr     ip, ip, lr, push #\push
++              stmia   r0!, {r3 - r9, ip}
++              bge     10b
++      PLD(    cmn     r2, #97                 )
++      PLD(    bge     11b                     )
++      PLD(    add     r2, r2, #97             )
++              cmn     r2, #16
++              blt     13f
++12:           mov     r3, lr, pull #\pull
++              ldmia   r1!, {r4 - r6, lr}
++              sub     r2, r2, #16
++              orr     r3, r3, r4, push #\push
++              mov     r4, r4, pull #\pull
++              orr     r4, r4, r5, push #\push
++              mov     r5, r5, pull #\pull
++              orr     r5, r5, r6, push #\push
++              mov     r6, r6, pull #\pull
++              orr     r6, r6, lr, push #\push
+               stmia   r0!, {r3 - r6}
+-              subs    r2, r2, #16
+-              bge     9b
+-              adds    r2, r2, #12
+-              blt     100f
+-10:           mov     r3, r7, lsr #8
+-              ldr     r7, [r1], #4
++13:           adds    r2, r2, #28
++              blt     15f
++14:           mov     r3, lr, pull #\pull
++              ldr     lr, [r1], #4
+               subs    r2, r2, #4
+-              orr     r3, r3, r7, lsl #24
++              orr     r3, r3, lr, push #\push
+               str     r3, [r0], #4
+-              bge     10b
+-100:          sub     r1, r1, #3
++              bge     14b
++15:
++              .endm
++
++
++              forward_copy_shift      pull=8  push=24
++              sub     r1, r1, #3
+               b       6b
+-11:           cmp     r2, #12
+-              blt     13f             /* */
+-              sub     r2, r2, #12
+-12:           mov     r3, r7, lsr #16
+-              ldmia   r1!, {r4 - r7}
+-              orr     r3, r3, r4, lsl #16
+-              mov     r4, r4, lsr #16
+-              orr     r4, r4, r5, lsl #16
+-              mov     r5, r5, lsr #16
+-              orr     r5, r5, r6, lsl #16
+-              mov     r6, r6, lsr #16
+-              orr     r6, r6, r7,LSL#16
+-              stmia   r0!, {r3 - r6}
+-              subs    r2, r2, #16
+-              bge     12b
+-              adds    r2, r2, #12
+-              blt     14f
+-13:           mov     r3, r7, lsr #16
+-              ldr     r7, [r1], #4
+-              subs    r2, r2, #4
+-              orr     r3, r3, r7, lsl #16
+-              str     r3, [r0], #4
+-              bge     13b
+-14:           sub     r1, r1, #2
++16:           forward_copy_shift      pull=16 push=16
++              sub     r1, r1, #2
+               b       6b
+-15:           cmp     r2, #12
+-              blt     17f
+-              sub     r2, r2, #12
+-16:           mov     r3, r7, lsr #24
+-              ldmia   r1!,{r4 - r7}
+-              orr     r3, r3, r4, lsl #8
+-              mov     r4, r4, lsr #24
+-              orr     r4, r4, r5, lsl #8
+-              mov     r5, r5, lsr #24
+-              orr     r5, r5, r6, lsl #8
+-              mov     r6, r6, lsr #24
+-              orr     r6, r6, r7, lsl #8
+-              stmia   r0!, {r3 - r6}
+-              subs    r2, r2, #16
+-              bge     16b
+-              adds    r2, r2, #12
+-              blt     18f
+-17:           mov     r3, r7, lsr #24
+-              ldr     r7, [r1], #4
+-              subs    r2, r2, #4
+-              orr     r3, r3, r7, lsl#8
+-              str     r3, [r0], #4
+-              bge     17b
+-18:           sub     r1, r1, #1
++17:           forward_copy_shift      pull=24 push=8
++              sub     r1, r1, #1
+               b       6b
+-19:           add     r1, r1, r2
++18:           add     r1, r1, r2
+               add     r0, r0, r2
+               subs    r2, r2, #4
+               blt     24f
++      PLD(    pld     [r1, #-4]               )
+               ands    ip, r0, #3
+               bne     25f
+               ands    ip, r1, #3
+               bne     26f
+-20:           subs    r2, r2, #8
++19:           subs    r2, r2, #8
+               blt     23f
+-              subs    r2, r2, #0x14
++              subs    r2, r2, #20
+               blt     22f
+-21:           ldmdb   r1!, {r3 - r9, ip}
+-              stmdb   r0!, {r3 - r9, ip}
++
++      PLD(    subs    r2, r2, #96             )
++      PLD(    pld     [r1, #-32]              )
++      PLD(    blt     21f                     )
++
++      PLD(    @ cache alignment               )
++      PLD(    ands    ip, r1, #31             )
++      PLD(    pld     [r1, #-64]              )
++      PLD(    beq     20f                     )
++      PLD(    cmp     r2, ip                  )
++      PLD(    pld     [r1, #-96]              )
++      PLD(    blt     20f                     )
++      PLD(    cmp     ip, #16                 )
++      PLD(    sub     r2, r2, ip              )
++      PLD(    ldmgedb r1!, {r3 - r6}          )
++      PLD(    stmgedb r0!, {r3 - r6}          )
++      PLD(    beq     20f                     )
++      PLD(    and     ip, ip, #15             )
++      PLD(    cmp     ip, #8                  )
++      PLD(    ldr     r3, [r1, #-4]!          )
++      PLD(    ldrge   r4, [r1, #-4]!          )
++      PLD(    ldrgt   r5, [r1, #-4]!          )
++      PLD(    str     r3, [r0, #-4]!          )
++      PLD(    strge   r4, [r0, #-4]!          )
++      PLD(    strgt   r5, [r0, #-4]!          )
++
++20:   PLD(    pld     [r1, #-96]              )
++      PLD(    pld     [r1, #-128]             )
++21:           ldmdb   r1!, {r3 - r6}
+               subs    r2, r2, #32
+-              bge     21b
+-22:           cmn     r2, #16
++              stmdb   r0!, {r3 - r6}
++              ldmdb   r1!, {r3 - r6}
++              stmgedb r0!, {r3 - r6}
+               ldmgedb r1!, {r3 - r6}
+               stmgedb r0!, {r3 - r6}
++              ldmgedb r1!, {r3 - r6}
++              subges  r2, r2, #32
++              stmdb   r0!, {r3 - r6}
++              bge     20b
++      PLD(    cmn     r2, #96                 )
++      PLD(    bge     21b                     )
++      PLD(    add     r2, r2, #96             )
++22:           cmn     r2, #16
++              ldmgedb r1!, {r3 - r6}
+               subge   r2, r2, #16
++              stmgedb r0!, {r3 - r6}
+               adds    r2, r2, #20
+               ldmgedb r1!, {r3 - r5}
+-              stmgedb r0!, {r3 - r5}
+               subge   r2, r2, #12
++              stmgedb r0!, {r3 - r5}
+ 23:           adds    r2, r2, #8
+               blt     24f
+               subs    r2, r2, #4
+               ldrlt   r3, [r1, #-4]!
+               ldmgedb r1!, {r4, r5}
++              subge   r2, r2, #4
+               strlt   r3, [r0, #-4]!
+               stmgedb r0!, {r4, r5}
+-              subge   r2, r2, #4
+ 24:           adds    r2, r2, #4
+               EXITEQ
+@@ -230,89 +317,101 @@
+               subs    r2, r2, ip
+               blt     24b
+               ands    ip, r1, #3
+-              beq     20b
++              beq     19b
+ 26:           bic     r1, r1, #3
+-              ldr     r3, [r1], #0
+               cmp     ip, #2
+-              blt     34f
+-              beq     30f
+-              cmp     r2, #12
+-              blt     28f
+-              sub     r2, r2, #12
+-27:           mov     r7, r3, lsl #8
+-              ldmdb   r1!, {r3, r4, r5, r6}
+-              orr     r7, r7, r6, lsr #24
+-              mov     r6, r6, lsl #8
+-              orr     r6, r6, r5, lsr #24
+-              mov     r5, r5, lsl #8
+-              orr     r5, r5, r4, lsr #24
+-              mov     r4, r4, lsl #8
+-              orr     r4, r4, r3, lsr #24
+-              stmdb   r0!, {r4, r5, r6, r7}
+-              subs    r2, r2, #16
+-              bge     27b
+-              adds    r2, r2, #12
+-              blt     29f
+-28:           mov     ip, r3, lsl #8
+-              ldr     r3, [r1, #-4]!
+-              subs    r2, r2, #4
+-              orr     ip, ip, r3, lsr #24
+-              str     ip, [r0, #-4]!
+-              bge     28b
+-29:           add     r1, r1, #3
+-              b       24b
++              ldr     r3, [r1], #0
++              blt     35f
++              beq     34f
+-30:           cmp     r2, #12
++
++              .macro  backward_copy_shift push pull
++
++              cmp     r2, #12
++      PLD(    pld     [r1, #-4]               )
+               blt     32f
+-              sub     r2, r2, #12
+-31:           mov     r7, r3, lsl #16
+-              ldmdb   r1!, {r3, r4, r5, r6}
+-              orr     r7, r7, r6, lsr #16
+-              mov     r6, r6, lsl #16
+-              orr     r6, r6, r5, lsr #16
+-              mov     r5, r5, lsl #16
+-              orr     r5, r5, r4, lsr #16
+-              mov     r4, r4, lsl #16
+-              orr     r4, r4, r3, lsr #16
+-              stmdb   r0!, {r4, r5, r6, r7}
+-              subs    r2, r2, #16
+-              bge     31b
+-              adds    r2, r2, #12
++              subs    r2, r2, #28
++              blt     30f
++
++      PLD(    subs    r2, r2, #96             )
++      PLD(    pld     [r1, #-32]              )
++      PLD(    blt     29f                     )
++      PLD(    pld     [r1, #-64]              )
++
++      PLD(    @ cache alignment               )
++      PLD(    ands    ip, r1, #31             )
++      PLD(    pld     [r1, #-96]              )
++      PLD(    beq     28f                     )
++      PLD(    cmp     r2, ip                  )
++      PLD(    pld     [r1, #-128]             )
++      PLD(    blt     28f                     )
++      PLD(    sub     r2, r2, ip              )
++27:   PLD(    mov     r4, r3, push #\push     )
++      PLD(    ldr     r3, [r1, #-4]!          )
++      PLD(    subs    ip, ip, #4              )
++      PLD(    orr     r4, r4, r3, pull #\pull )
++      PLD(    str     r4, [r0, #-4]!          )
++      PLD(    bgt     27b                     )
++
++28:   PLD(    pld     [r1, #-128]             )
++29:           mov     lr, r3, push #\push
++              ldmdb   r1!, {r3 - r9, ip}
++              subs    r2, r2, #32
++              orr     lr, lr, ip, pull #\pull
++              mov     ip, ip, push #\push
++              orr     ip, ip, r9, pull #\pull
++              mov     r9, r9, push #\push
++              orr     r9, r9, r8, pull #\pull
++              mov     r8, r8, push #\push
++              orr     r8, r8, r7, pull #\pull
++              mov     r7, r7, push #\push
++              orr     r7, r7, r6, pull #\pull
++              mov     r6, r6, push #\push
++              orr     r6, r6, r5, pull #\pull
++              mov     r5, r5, push #\push
++              orr     r5, r5, r4, pull #\pull
++              mov     r4, r4, push #\push
++              orr     r4, r4, r3, pull #\pull
++              stmdb   r0!, {r4 - r9, ip, lr}
++              bge     28b
++      PLD(    cmn     r2, #96                 )
++      PLD(    bge     29b                     )
++      PLD(    add     r2, r2, #96             )
++              cmn     r2, #16
++              blt     31f
++30:           mov     r7, r3, push #\push
++              ldmdb   r1!, {r3 - r6}
++              sub     r2, r2, #16
++              orr     r7, r7, r6, pull #\pull
++              mov     r6, r6, push #\push
++              orr     r6, r6, r5, pull #\pull
++              mov     r5, r5, push #\push
++              orr     r5, r5, r4, pull #\pull
++              mov     r4, r4, push #\push
++              orr     r4, r4, r3, pull #\pull
++              stmdb   r0!, {r4 - r7}
++31:           adds    r2, r2, #28
+               blt     33f
+-32:           mov     ip, r3, lsl #16
++32:           mov     r4, r3, push #\push
+               ldr     r3, [r1, #-4]!
+               subs    r2, r2, #4
+-              orr     ip, ip, r3, lsr #16
+-              str     ip, [r0, #-4]!
++              orr     r4, r4, r3, pull #\pull
++              str     r4, [r0, #-4]!
+               bge     32b
+-33:           add     r1, r1, #2
++33:
++              .endm
++
++
++              backward_copy_shift     push=8  pull=24
++              add     r1, r1, #3
+               b       24b
+-34:           cmp     r2, #12
+-              blt     36f
+-              sub     r2, r2, #12
+-35:           mov     r7, r3, lsl #24
+-              ldmdb   r1!, {r3, r4, r5, r6}
+-              orr     r7, r7, r6, lsr #8
+-              mov     r6, r6, lsl #24
+-              orr     r6, r6, r5, lsr #8
+-              mov     r5, r5, lsl #24
+-              orr     r5, r5, r4, lsr #8
+-              mov     r4, r4, lsl #24
+-              orr     r4, r4, r3, lsr #8
+-              stmdb   r0!, {r4, r5, r6, r7}
+-              subs    r2, r2, #16
+-              bge     35b
+-              adds    r2, r2, #12
+-              blt     37f
+-36:           mov     ip, r3, lsl #24
+-              ldr     r3, [r1, #-4]!
+-              subs    r2, r2, #4
+-              orr     ip, ip, r3, lsr #8
+-              str     ip, [r0, #-4]!
+-              bge     36b
+-37:           add     r1, r1, #1
++34:           backward_copy_shift     push=16 pull=16
++              add     r1, r1, #2
++              b       24b
++
++35:           backward_copy_shift     push=24 pull=8
++              add     r1, r1, #1
+               b       24b
+-              .align
+--- linux-2.4.27/arch/arm/lib/uaccess.S~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/lib/uaccess.S
+@@ -43,6 +43,8 @@
+               stmfd   sp!, {r2, r4 - r7, lr}
+               cmp     r2, #4
+               blt     .c2u_not_enough
++      PLD(    pld     [r1, #0]                )
++      PLD(    pld     [r0, #0]                )
+               ands    ip, r0, #3
+               bne     .c2u_dest_not_aligned
+ .c2u_dest_aligned:
+@@ -71,13 +73,26 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #32
+               blt     .c2u_0rem8lp
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
++      PLD(    subs    ip, ip, #64                     )
++      PLD(    blt     .c2u_0cpynopld          )
++      PLD(    pld     [r1, #60]               )
++      PLD(    pld     [r0, #60]               )
+-.c2u_0cpy8lp: ldmia   r1!, {r3 - r6}
++.c2u_0cpy8lp:
++      PLD(    pld     [r1, #92]               )
++      PLD(    pld     [r0, #92]               )
++.c2u_0cpynopld:       ldmia   r1!, {r3 - r6}
+               stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               ldmia   r1!, {r3 - r6}
+-              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               subs    ip, ip, #32
++              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               bpl     .c2u_0cpy8lp
++      PLD(    cmn     ip, #64                 )
++      PLD(    bge     .c2u_0cpynopld          )
++      PLD(    add     ip, ip, #64             )
++
+ .c2u_0rem8lp: cmn     ip, #16
+               ldmgeia r1!, {r3 - r6}
+               stmgeia r0!, {r3 - r6}                  @ Shouldnt fault
+@@ -115,9 +130,9 @@
+ .c2u_1fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .c2u_1nowords
+-              mov     r3, r7, lsr #8
++              mov     r3, r7, pull #8
+               ldr     r7, [r1], #4
+-              orr     r3, r3, r7, lsl #24
++              orr     r3, r3, r7, push #24
+ USER(         strt    r3, [r0], #4)                   @ May fault
+               mov     ip, r0, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -128,50 +143,63 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .c2u_1rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .c2u_1cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.c2u_1cpy8lp: mov     r3, r7, lsr #8
++.c2u_1cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.c2u_1cpynopld:       mov     r3, r7, pull #8
+               ldmia   r1!, {r4 - r7}
+-              orr     r3, r3, r4, lsl #24
+-              mov     r4, r4, lsr #8
+-              orr     r4, r4, r5, lsl #24
+-              mov     r5, r5, lsr #8
+-              orr     r5, r5, r6, lsl #24
+-              mov     r6, r6, lsr #8
+-              orr     r6, r6, r7, lsl #24
+-              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               subs    ip, ip, #16
++              orr     r3, r3, r4, push #24
++              mov     r4, r4, pull #8
++              orr     r4, r4, r5, push #24
++              mov     r5, r5, pull #8
++              orr     r5, r5, r6, push #24
++              mov     r6, r6, pull #8
++              orr     r6, r6, r7, push #24
++              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               bpl     .c2u_1cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .c2u_1cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .c2u_1rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #8
++              movne   r3, r7, pull #8
+               ldmneia r1!, {r4, r7}
+-              orrne   r3, r3, r4, lsl #24
+-              movne   r4, r4, lsr #8
+-              orrne   r4, r4, r7, lsl #24
++              orrne   r3, r3, r4, push #24
++              movne   r4, r4, pull #8
++              orrne   r4, r4, r7, push #24
+               stmneia r0!, {r3 - r4}                  @ Shouldnt fault
+               tst     ip, #4
+-              movne   r3, r7, lsr #8
++              movne   r3, r7, pull #8
+               ldrne   r7, [r1], #4
+-              orrne   r3, r3, r7, lsl #24
++              orrne   r3, r3, r7, push #24
+               strnet  r3, [r0], #4                    @ Shouldnt fault
+               ands    ip, ip, #3
+               beq     .c2u_1fupi
+-.c2u_1nowords:        mov     r3, r7, lsr #8
++.c2u_1nowords:        mov     r3, r7, lsr #byte(1)
+               teq     ip, #0
+               beq     .c2u_finished
+               cmp     ip, #2
+ USER(         strbt   r3, [r0], #1)                   @ May fault
+-              movge   r3, r3, lsr #8
++              movge   r3, r7, lsr #byte(2)
+ USER(         strgebt r3, [r0], #1)                   @ May fault
+-              movgt   r3, r3, lsr #8
++              movgt   r3, r7, lsr #byte(3)
+ USER(         strgtbt r3, [r0], #1)                   @ May fault
+               b       .c2u_finished
+ .c2u_2fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .c2u_2nowords
+-              mov     r3, r7, lsr #16
++              mov     r3, r7, pull #16
+               ldr     r7, [r1], #4
+-              orr     r3, r3, r7, lsl #16
++              orr     r3, r3, r7, push #16
+ USER(         strt    r3, [r0], #4)                   @ May fault
+               mov     ip, r0, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -182,39 +210,52 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .c2u_2rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .c2u_2cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.c2u_2cpy8lp: mov     r3, r7, lsr #16
++.c2u_2cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.c2u_2cpynopld:       mov     r3, r7, pull #16
+               ldmia   r1!, {r4 - r7}
+-              orr     r3, r3, r4, lsl #16
+-              mov     r4, r4, lsr #16
+-              orr     r4, r4, r5, lsl #16
+-              mov     r5, r5, lsr #16
+-              orr     r5, r5, r6, lsl #16
+-              mov     r6, r6, lsr #16
+-              orr     r6, r6, r7, lsl #16
+-              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               subs    ip, ip, #16
++              orr     r3, r3, r4, push #16
++              mov     r4, r4, pull #16
++              orr     r4, r4, r5, push #16
++              mov     r5, r5, pull #16
++              orr     r5, r5, r6, push #16
++              mov     r6, r6, pull #16
++              orr     r6, r6, r7, push #16
++              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               bpl     .c2u_2cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .c2u_2cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .c2u_2rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #16
++              movne   r3, r7, pull #16
+               ldmneia r1!, {r4, r7}
+-              orrne   r3, r3, r4, lsl #16
+-              movne   r4, r4, lsr #16
+-              orrne   r4, r4, r7, lsl #16
++              orrne   r3, r3, r4, push #16
++              movne   r4, r4, pull #16
++              orrne   r4, r4, r7, push #16
+               stmneia r0!, {r3 - r4}                  @ Shouldnt fault
+               tst     ip, #4
+-              movne   r3, r7, lsr #16
++              movne   r3, r7, pull #16
+               ldrne   r7, [r1], #4
+-              orrne   r3, r3, r7, lsl #16
++              orrne   r3, r3, r7, push #16
+               strnet  r3, [r0], #4                    @ Shouldnt fault
+               ands    ip, ip, #3
+               beq     .c2u_2fupi
+-.c2u_2nowords:        mov     r3, r7, lsr #16
++.c2u_2nowords:        mov     r3, r7, lsr #byte(2)
+               teq     ip, #0
+               beq     .c2u_finished
+               cmp     ip, #2
+ USER(         strbt   r3, [r0], #1)                   @ May fault
+-              movge   r3, r3, lsr #8
++              movge   r3, r7, lsr #byte(3)
+ USER(         strgebt r3, [r0], #1)                   @ May fault
+               ldrgtb  r3, [r1], #0
+ USER(         strgtbt r3, [r0], #1)                   @ May fault
+@@ -223,9 +264,9 @@
+ .c2u_3fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .c2u_3nowords
+-              mov     r3, r7, lsr #24
++              mov     r3, r7, pull #24
+               ldr     r7, [r1], #4
+-              orr     r3, r3, r7, lsl #8
++              orr     r3, r3, r7, push #8
+ USER(         strt    r3, [r0], #4)                   @ May fault
+               mov     ip, r0, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -236,41 +277,54 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .c2u_3rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .c2u_3cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.c2u_3cpy8lp: mov     r3, r7, lsr #24
++.c2u_3cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.c2u_3cpynopld:       mov     r3, r7, pull #24
+               ldmia   r1!, {r4 - r7}
+-              orr     r3, r3, r4, lsl #8
+-              mov     r4, r4, lsr #24
+-              orr     r4, r4, r5, lsl #8
+-              mov     r5, r5, lsr #24
+-              orr     r5, r5, r6, lsl #8
+-              mov     r6, r6, lsr #24
+-              orr     r6, r6, r7, lsl #8
+-              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               subs    ip, ip, #16
++              orr     r3, r3, r4, push #8
++              mov     r4, r4, pull #24
++              orr     r4, r4, r5, push #8
++              mov     r5, r5, pull #24
++              orr     r5, r5, r6, push #8
++              mov     r6, r6, pull #24
++              orr     r6, r6, r7, push #8
++              stmia   r0!, {r3 - r6}                  @ Shouldnt fault
+               bpl     .c2u_3cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .c2u_3cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .c2u_3rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #24
++              movne   r3, r7, pull #24
+               ldmneia r1!, {r4, r7}
+-              orrne   r3, r3, r4, lsl #8
+-              movne   r4, r4, lsr #24
+-              orrne   r4, r4, r7, lsl #8
++              orrne   r3, r3, r4, push #8
++              movne   r4, r4, pull #24
++              orrne   r4, r4, r7, push #8
+               stmneia r0!, {r3 - r4}                  @ Shouldnt fault
+               tst     ip, #4
+-              movne   r3, r7, lsr #24
++              movne   r3, r7, pull #24
+               ldrne   r7, [r1], #4
+-              orrne   r3, r3, r7, lsl #8
++              orrne   r3, r3, r7, push #8
+               strnet  r3, [r0], #4                    @ Shouldnt fault
+               ands    ip, ip, #3
+               beq     .c2u_3fupi
+-.c2u_3nowords:        mov     r3, r7, lsr #24
++.c2u_3nowords:        mov     r3, r7, lsr #byte(3)
+               teq     ip, #0
+               beq     .c2u_finished
+               cmp     ip, #2
+ USER(         strbt   r3, [r0], #1)                   @ May fault
+-              ldrge   r3, [r1], #0
++              ldrgeb  r3, [r1], #1
+ USER(         strgebt r3, [r0], #1)                   @ May fault
+-              movgt   r3, r3, lsr #8
++              ldrgtb  r3, [r1], #0
+ USER(         strgtbt r3, [r0], #1)                   @ May fault
+               b       .c2u_finished
+@@ -302,6 +356,8 @@
+               stmfd   sp!, {r0, r2, r4 - r7, lr}
+               cmp     r2, #4
+               blt     .cfu_not_enough
++      PLD(    pld     [r1, #0]                )
++      PLD(    pld     [r0, #0]                )
+               ands    ip, r0, #3
+               bne     .cfu_dest_not_aligned
+ .cfu_dest_aligned:
+@@ -329,13 +385,26 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #32
+               blt     .cfu_0rem8lp
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
++      PLD(    subs    ip, ip, #64                     )
++      PLD(    blt     .cfu_0cpynopld          )
++      PLD(    pld     [r1, #60]               )
++      PLD(    pld     [r0, #60]               )
+-.cfu_0cpy8lp: ldmia   r1!, {r3 - r6}                  @ Shouldnt fault
++.cfu_0cpy8lp:
++      PLD(    pld     [r1, #92]               )
++      PLD(    pld     [r0, #92]               )
++.cfu_0cpynopld:       ldmia   r1!, {r3 - r6}                  @ Shouldnt fault
+               stmia   r0!, {r3 - r6}
+               ldmia   r1!, {r3 - r6}                  @ Shouldnt fault
+-              stmia   r0!, {r3 - r6}
+               subs    ip, ip, #32
++              stmia   r0!, {r3 - r6}
+               bpl     .cfu_0cpy8lp
++      PLD(    cmn     ip, #64                 )
++      PLD(    bge     .cfu_0cpynopld          )
++      PLD(    add     ip, ip, #64             )
++
+ .cfu_0rem8lp: cmn     ip, #16
+               ldmgeia r1!, {r3 - r6}                  @ Shouldnt fault
+               stmgeia r0!, {r3 - r6}
+@@ -374,9 +443,9 @@
+ .cfu_1fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .cfu_1nowords
+-              mov     r3, r7, lsr #8
++              mov     r3, r7, pull #8
+ USER(         ldrt    r7, [r1], #4)                   @ May fault
+-              orr     r3, r3, r7, lsl #24
++              orr     r3, r3, r7, push #24
+               str     r3, [r0], #4
+               mov     ip, r1, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -387,50 +456,63 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .cfu_1rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .cfu_1cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.cfu_1cpy8lp: mov     r3, r7, lsr #8
++.cfu_1cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.cfu_1cpynopld:       mov     r3, r7, pull #8
+               ldmia   r1!, {r4 - r7}                  @ Shouldnt fault
+-              orr     r3, r3, r4, lsl #24
+-              mov     r4, r4, lsr #8
+-              orr     r4, r4, r5, lsl #24
+-              mov     r5, r5, lsr #8
+-              orr     r5, r5, r6, lsl #24
+-              mov     r6, r6, lsr #8
+-              orr     r6, r6, r7, lsl #24
+-              stmia   r0!, {r3 - r6}
+               subs    ip, ip, #16
++              orr     r3, r3, r4, push #24
++              mov     r4, r4, pull #8
++              orr     r4, r4, r5, push #24
++              mov     r5, r5, pull #8
++              orr     r5, r5, r6, push #24
++              mov     r6, r6, pull #8
++              orr     r6, r6, r7, push #24
++              stmia   r0!, {r3 - r6}
+               bpl     .cfu_1cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .cfu_1cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .cfu_1rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #8
++              movne   r3, r7, pull #8
+               ldmneia r1!, {r4, r7}                   @ Shouldnt fault
+-              orrne   r3, r3, r4, lsl #24
+-              movne   r4, r4, lsr #8
+-              orrne   r4, r4, r7, lsl #24
++              orrne   r3, r3, r4, push #24
++              movne   r4, r4, pull #8
++              orrne   r4, r4, r7, push #24
+               stmneia r0!, {r3 - r4}
+               tst     ip, #4
+-              movne   r3, r7, lsr #8
++              movne   r3, r7, pull #8
+ USER(         ldrnet  r7, [r1], #4)                   @ May fault
+-              orrne   r3, r3, r7, lsl #24
++              orrne   r3, r3, r7, push #24
+               strne   r3, [r0], #4
+               ands    ip, ip, #3
+               beq     .cfu_1fupi
+-.cfu_1nowords:        mov     r3, r7, lsr #8
++.cfu_1nowords:        mov     r3, r7, lsr #byte(1)
+               teq     ip, #0
+               beq     .cfu_finished
+               cmp     ip, #2
+               strb    r3, [r0], #1
+-              movge   r3, r3, lsr #8
++              movge   r3, r7, lsr #byte(2)
+               strgeb  r3, [r0], #1
+-              movgt   r3, r3, lsr #8
++              movgt   r3, r7, lsr #byte(3)
+               strgtb  r3, [r0], #1
+               b       .cfu_finished
+ .cfu_2fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .cfu_2nowords
+-              mov     r3, r7, lsr #16
++              mov     r3, r7, pull #16
+ USER(         ldrt    r7, [r1], #4)                   @ May fault
+-              orr     r3, r3, r7, lsl #16
++              orr     r3, r3, r7, push #16
+               str     r3, [r0], #4
+               mov     ip, r1, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -441,39 +523,52 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .cfu_2rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .cfu_2cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.cfu_2cpy8lp: mov     r3, r7, lsr #16
++.cfu_2cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.cfu_2cpynopld:       mov     r3, r7, pull #16
+               ldmia   r1!, {r4 - r7}                  @ Shouldnt fault
+-              orr     r3, r3, r4, lsl #16
+-              mov     r4, r4, lsr #16
+-              orr     r4, r4, r5, lsl #16
+-              mov     r5, r5, lsr #16
+-              orr     r5, r5, r6, lsl #16
+-              mov     r6, r6, lsr #16
+-              orr     r6, r6, r7, lsl #16
+-              stmia   r0!, {r3 - r6}
+               subs    ip, ip, #16
++              orr     r3, r3, r4, push #16
++              mov     r4, r4, pull #16
++              orr     r4, r4, r5, push #16
++              mov     r5, r5, pull #16
++              orr     r5, r5, r6, push #16
++              mov     r6, r6, pull #16
++              orr     r6, r6, r7, push #16
++              stmia   r0!, {r3 - r6}
+               bpl     .cfu_2cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .cfu_2cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .cfu_2rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #16
++              movne   r3, r7, pull #16
+               ldmneia r1!, {r4, r7}                   @ Shouldnt fault
+-              orrne   r3, r3, r4, lsl #16
+-              movne   r4, r4, lsr #16
+-              orrne   r4, r4, r7, lsl #16
++              orrne   r3, r3, r4, push #16
++              movne   r4, r4, pull #16
++              orrne   r4, r4, r7, push #16
+               stmneia r0!, {r3 - r4}
+               tst     ip, #4
+-              movne   r3, r7, lsr #16
++              movne   r3, r7, pull #16
+ USER(         ldrnet  r7, [r1], #4)                   @ May fault
+-              orrne   r3, r3, r7, lsl #16
++              orrne   r3, r3, r7, push #16
+               strne   r3, [r0], #4
+               ands    ip, ip, #3
+               beq     .cfu_2fupi
+-.cfu_2nowords:        mov     r3, r7, lsr #16
++.cfu_2nowords:        mov     r3, r7, lsr #byte(2)
+               teq     ip, #0
+               beq     .cfu_finished
+               cmp     ip, #2
+               strb    r3, [r0], #1
+-              movge   r3, r3, lsr #8
++              movge   r3, r7, lsr #byte(3)
+               strgeb  r3, [r0], #1
+ USER(         ldrgtbt r3, [r1], #0)                   @ May fault
+               strgtb  r3, [r0], #1
+@@ -482,9 +577,9 @@
+ .cfu_3fupi:   subs    r2, r2, #4
+               addmi   ip, r2, #4
+               bmi     .cfu_3nowords
+-              mov     r3, r7, lsr #24
++              mov     r3, r7, pull #24
+ USER(         ldrt    r7, [r1], #4)                   @ May fault
+-              orr     r3, r3, r7, lsl #8
++              orr     r3, r3, r7, push #8
+               str     r3, [r0], #4
+               mov     ip, r1, lsl #32 - PAGE_SHIFT
+               rsb     ip, ip, #0
+@@ -495,41 +590,54 @@
+               sub     r2, r2, ip
+               subs    ip, ip, #16
+               blt     .cfu_3rem8lp
++      PLD(    pld     [r1, #12]               )
++      PLD(    pld     [r0, #12]               )
++      PLD(    subs    ip, ip, #32             )
++      PLD(    blt     .cfu_3cpynopld          )
++      PLD(    pld     [r1, #28]               )
++      PLD(    pld     [r0, #28]               )
+-.cfu_3cpy8lp: mov     r3, r7, lsr #24
++.cfu_3cpy8lp:
++      PLD(    pld     [r1, #44]               )
++      PLD(    pld     [r0, #44]               )
++.cfu_3cpynopld:       mov     r3, r7, pull #24
+               ldmia   r1!, {r4 - r7}                  @ Shouldnt fault
+-              orr     r3, r3, r4, lsl #8
+-              mov     r4, r4, lsr #24
+-              orr     r4, r4, r5, lsl #8
+-              mov     r5, r5, lsr #24
+-              orr     r5, r5, r6, lsl #8
+-              mov     r6, r6, lsr #24
+-              orr     r6, r6, r7, lsl #8
++              orr     r3, r3, r4, push #8
++              mov     r4, r4, pull #24
++              orr     r4, r4, r5, push #8
++              mov     r5, r5, pull #24
++              orr     r5, r5, r6, push #8
++              mov     r6, r6, pull #24
++              orr     r6, r6, r7, push #8
+               stmia   r0!, {r3 - r6}
+               subs    ip, ip, #16
+               bpl     .cfu_3cpy8lp
++      PLD(    cmn     ip, #32                 )
++      PLD(    bge     .cfu_3cpynopld          )
++      PLD(    add     ip, ip, #32             )
++
+ .cfu_3rem8lp: tst     ip, #8
+-              movne   r3, r7, lsr #24
++              movne   r3, r7, pull #24
+               ldmneia r1!, {r4, r7}                   @ Shouldnt fault
+-              orrne   r3, r3, r4, lsl #8
+-              movne   r4, r4, lsr #24
+-              orrne   r4, r4, r7, lsl #8
++              orrne   r3, r3, r4, push #8
++              movne   r4, r4, pull #24
++              orrne   r4, r4, r7, push #8
+               stmneia r0!, {r3 - r4}
+               tst     ip, #4
+-              movne   r3, r7, lsr #24
++              movne   r3, r7, pull #24
+ USER(         ldrnet  r7, [r1], #4)                   @ May fault
+-              orrne   r3, r3, r7, lsl #8
++              orrne   r3, r3, r7, push #8
+               strne   r3, [r0], #4
+               ands    ip, ip, #3
+               beq     .cfu_3fupi
+-.cfu_3nowords:        mov     r3, r7, lsr #24
++.cfu_3nowords:        mov     r3, r7, lsr #byte(3)
+               teq     ip, #0
+               beq     .cfu_finished
+               cmp     ip, #2
+               strb    r3, [r0], #1
+-USER(         ldrget  r3, [r1], #0)                   @ May fault
++USER(         ldrgebt r3, [r1], #1)                   @ May fault
+               strgeb  r3, [r0], #1
+-              movgt   r3, r3, lsr #8
++USER(         ldrgtbt r3, [r1], #1)                   @ May fault
+               strgtb  r3, [r0], #1
+               b       .cfu_finished
+@@ -544,7 +652,7 @@
+               ldr     r1, [sp], #4                    @ unsigned long count
+               subs    r4, r1, r2                      @ bytes left to copy
+               movne   r1, r4
+-              blne    SYMBOL_NAME(__memzero)
++              blne    __memzero
+               mov     r0, r4
+               LOADREGS(fd,sp!, {r4 - r7, pc})
+               .previous
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/Makefile
+@@ -0,0 +1,56 @@
++#
++# Makefile for the linux kernel.
++#
++# 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).
++
++USE_STANDARD_AS_RULE := true
++
++O_TARGET              := pxa.o
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++export-objs := generic.o irq.o dma.o sa1111.o \
++               usb_ctl.o usb_recv.o usb_send.o
++
++# Common support (must be linked before board specific support)
++obj-y += generic.o irq.o dma.o
++obj-$(CONFIG_SA1111) += sa1111.o
++
++# Specific board support
++obj-$(CONFIG_ARCH_CSB226) += csb226.o
++obj-$(CONFIG_ARCH_INNOKOM) += innokom.o
++obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o
++obj-$(CONFIG_ARCH_PXA_CERF) += cerf.o
++obj-$(CONFIG_ARCH_PXA_IDP) += idp.o
++obj-$(CONFIG_ARCH_TRIZEPS2) += trizeps2.o
++
++# Support for blinky lights
++leds-y := leds.o
++leds-$(CONFIG_ARCH_CSB226) += leds-csb226.o
++leds-$(CONFIG_ARCH_INNOKOM) += leds-innokom.o
++leds-$(CONFIG_ARCH_LUBBOCK) += leds-lubbock.o
++leds-$(CONFIG_ARCH_PXA_IDP) += leds-idp.o
++leds-$(CONFIG_ARCH_PXA_CERF) += leds-cerf.o
++
++obj-$(CONFIG_LEDS) += $(leds-y)
++
++# PXA USB client support
++list-multi += pxausb_core.o
++pxausb_core-objs := usb_ctl.o usb_ep0.o usb_recv.o usb_send.o
++obj-$(CONFIG_PXA_USB) += pxausb_core.o
++obj-$(CONFIG_PXA_USB_NETLINK) += usb-eth.o
++obj-$(CONFIG_PXA_USB_CHAR) += usb-char.o
++
++# Misc features
++obj-$(CONFIG_PM) += pm.o sleep.o
++obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o
++
++include $(TOPDIR)/Rules.make
++
++pxausb_core.o: $(pxausb_core-objs)
++      $(LD) -r -o $@ $(pxausb_core-objs)
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/cerf.c
+@@ -0,0 +1,266 @@
++/*
++ *  linux/arch/arm/mach-pxa/cerf.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/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++
++#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/io.h>
++#include <asm/arch/irq.h>
++
++#include "generic.h"
++
++/*
++ * Set this to zero to remove all the debug statements via
++ * dead code elimination.
++ */
++#define DEBUGGING       1
++
++#if DEBUGGING
++static unsigned int cerf_debug = DEBUGGING;
++#else
++#define cerf_debug       0
++#endif
++
++static void __init cerf_init_irq(void)
++{
++      pxa_init_irq();
++
++      if( cerf_debug > 1)
++      {
++#if 0
++              GPDR0 = 0xc05b9130;
++              GPDR1 = 0xfcffab82;
++              GPDR2 = 0x0001ffff;
++#endif
++
++              printk(KERN_INFO "Pin directions:\n");
++              printk(KERN_INFO "GPDR0 0x%08x\n", GPDR0);
++              printk(KERN_INFO "GPDR1 0x%08x\n", GPDR1);
++              printk(KERN_INFO "GPDR2 0x%08x\n", GPDR2);
++
++              printk(KERN_INFO "Pin State:\n");
++              printk(KERN_INFO "GPLR0 0x%08x\n", GPLR0);
++              printk(KERN_INFO "GPLR1 0x%08x\n", GPLR1);
++              printk(KERN_INFO "GPLR2 0x%08x\n", GPLR2);
++
++              printk(KERN_INFO "Rising Edge:\n");
++              printk(KERN_INFO "GRER0 0x%08x\n", GRER0);
++              printk(KERN_INFO "GRER1 0x%08x\n", GRER1);
++              printk(KERN_INFO "GRER2 0x%08x\n", GRER2);
++
++              printk(KERN_INFO "Falling Edge:\n");
++              printk(KERN_INFO "GFER0 0x%08x\n", GFER0);
++              printk(KERN_INFO "GFER1 0x%08x\n", GFER1);
++              printk(KERN_INFO "GFER2 0x%08x\n", GFER2);
++      }
++
++      /* set_GPIO_IRQ_edge has to be called before an irq can be requested */
++      set_GPIO_IRQ_edge(  0, GPIO_FALLING_EDGE); /* CPLD               */
++#ifdef CONFIG_PXA_CERF_PDA
++      set_GPIO_IRQ_edge(  2, GPIO_RISING_EDGE);  /* UART B Interrupt   */
++      set_GPIO_IRQ_edge(  3, GPIO_RISING_EDGE);  /* UART A Interrupt   */
++      set_GPIO_IRQ_edge( 32, GPIO_RISING_EDGE);  /* UCB1400 Interrupt  */
++#endif
++      set_GPIO_IRQ_edge( 14, GPIO_FALLING_EDGE); /* PCMCIA Card Detect */
++      set_GPIO_IRQ_edge( 21, GPIO_RISING_EDGE);  /* Ethernet Interrupt */
++}
++
++static int __init cerf_init(void)
++{
++      /*
++       * All of the code that was here was SA1111 init code
++       * which we do not have.
++       */
++      return 0;
++}
++
++__initcall(cerf_init);
++
++static void __init
++fixup_cerf(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++      SET_BANK (0, CERF_RAM_BASE, CERF_RAM_SIZE);
++      mi->nr_banks      = 1;
++
++#if 0 // Enable this stuff if you plan on not using jffs2
++      setup_ramdisk (1, 0, 0, 8192);
++      setup_initrd (__phys_to_virt(0xa1000000), 4*1024*1024);
++      ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
++#endif
++}
++
++/*
++ * IO map for the devices.
++ */
++static struct map_desc cerf_io_desc[] __initdata = {
++ /* virtual           physical          length            domain     r  w  c  b */
++  { CERF_FLASH_BASE , CERF_FLASH_PHYS , CERF_FLASH_SIZE , DOMAIN_IO, 0, 1, 0, 0 },
++  { CERF_ETH_BASE   , CERF_ETH_PHYS   , CERF_ETH_SIZE   , DOMAIN_IO, 0, 1, 0, 0 },
++#ifdef CONFIG_PXA_CERF_PDA
++  { CERF_BT_BASE    , CERF_BT_PHYS    , CERF_BT_SIZE    , DOMAIN_IO, 0, 1, 0, 0 },
++  { CERF_SERIAL_BASE, CERF_SERIAL_PHYS, CERF_SERIAL_SIZE, DOMAIN_IO, 0, 1, 0, 0 },
++  { CERF_CPLD_BASE  , CERF_CPLD_PHYS  , CERF_CPLD_SIZE  , DOMAIN_IO, 0, 1, 0, 0 },
++#endif
++
++  LAST_DESC
++};
++
++static void __init cerf_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(cerf_io_desc);
++
++      if( cerf_debug > 1)
++      {
++              printk(KERN_INFO "origMCS0 = 0x%08x\n", MSC0);
++              printk(KERN_INFO "origMCS1 = 0x%08x\n", MSC1);
++              printk(KERN_INFO "origMCS2 = 0x%08x\n", MSC2);
++      }
++
++      /* setup memory timing for CS0/1 */
++      MSC0 = MSC_CS(0, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(3) |
++                       MSC_RDN(15) |
++                       MSC_RDF(13) |
++                       MSC_RBW(0) |
++                       MSC_RT(0)) |
++#ifdef CONFIG_PXA_CERF_PDA
++               MSC_CS(1, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(7) |
++                       MSC_RDN(15) |
++                       MSC_RDF(15) |
++                       MSC_RBW(1) |
++                       MSC_RT(0));
++#elif defined(CONFIG_PXA_CERF_BOARD)
++               MSC_CS(1, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(1) |
++                       MSC_RDN(2) |
++                       MSC_RDF(4) |
++                       MSC_RBW(0) |
++                       MSC_RT(4));
++#endif
++      printk(KERN_INFO "MCS0 = 0x%08x\n", MSC0);
++
++      /* setup memory timing for CS2/3 */
++      MSC1 = MSC_CS(2, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(5) |
++                       MSC_RDN(10) |
++                       MSC_RDF(10) |
++                       MSC_RBW(1) |
++                       MSC_RT(0)) |
++               MSC_CS(3, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(5) |
++                       MSC_RDN(10) |
++                       MSC_RDF(10) |
++                       MSC_RBW(1) |
++                       MSC_RT(0));
++      printk(KERN_INFO "MCS1 = 0x%08x\n", MSC1);
++
++      /* setup memory timing for CS4/5 */
++      MSC2 = MSC_CS(4, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(2) |
++                       MSC_RDN(4) |
++                       MSC_RDF(4) |
++                       MSC_RBW(1) |
++                       MSC_RT(0)) |
++               MSC_CS(5, MSC_RBUFF(MSC_RBUFF_SLOW) | 
++                       MSC_RRR(2) |
++                       MSC_RDN(4) |
++                       MSC_RDF(4) |
++                       MSC_RBW(1) |
++                       MSC_RT(0));
++      printk(KERN_INFO "MCS2 = 0x%08x\n", MSC2);
++
++#ifdef CONFIG_SOUND_PXA_AC97
++      printk(KERN_INFO "Enabling sound amp for pxa cerf pda.\n");
++      outw( CERF_PDA_SOUND_ENABLE, CERF_CPLD_BASE+CERF_PDA_CPLD_SOUND_ENA);
++#endif
++
++#ifdef CONFIG_FB_PXA
++      printk(KERN_INFO "Setting LCD to brightness to %d/15\n", CERF_PDA_DEFAULT_BRIGHTNESS);
++      outw( CERF_PDA_DEFAULT_BRIGHTNESS, CERF_CPLD_BASE+CERF_PDA_CPLD_BRIGHTNESS);
++#endif
++
++#ifdef CONFIG_IRDA
++      /* Enable IrDA UART (SIR)*/
++      CKEN |= CKEN5_STUART;
++
++      /* We want to get our goods from the STUART */
++      set_GPIO_mode(GPIO46_STRXD_MD);
++      set_GPIO_mode(GPIO47_STTXD_MD);
++
++      /* make sure FIR ICP is off */
++      ICCR0 = 0;
++
++      /* configure STUART to for SIR
++       * NOTE: RCVEIR and XMITIR must not be set at the same time!
++       * Start with receive in IR mode, and switch transmit to IR only
++       * when we need to send something in serial driver.
++       */
++      STISR = IrSR_IR_RECEIVE_ON;
++#endif
++
++#if 0
++      /* Connect FIR ICP to GPIO pins */
++      CKEN |= CKEN13_FICP;
++      set_GPIO_mode(GPIO46_ICPRXD_MD);
++      set_GPIO_mode(GPIO47_ICPTXD_MD);
++      ICCR0 = 0x1 | 0x18; //ICP unit enable
++#endif
++
++#if 0
++      /* Enable BT UART */
++      CKEN |= CKEN7_BTUART;
++      set_GPIO_mode(GPIO42_BTRXD_MD);
++      set_GPIO_mode(GPIO43_BTTXD_MD);
++      set_GPIO_mode(GPIO44_BTCTS_MD);
++      set_GPIO_mode(GPIO45_BTRTS_MD);
++#endif
++
++      if( cerf_debug > 1)
++      {
++              printk(KERN_INFO "GPDR0   0x%08x\n", GPDR0);
++              printk(KERN_INFO "GPDR1   0x%08x\n", GPDR1);
++              printk(KERN_INFO "GPDR2   0x%08x\n", GPDR2);
++              printk(KERN_INFO "GPLR0   0x%08x\n", GPLR0);
++              printk(KERN_INFO "GPLR1   0x%08x\n", GPLR1);
++              printk(KERN_INFO "GPLR2   0x%08x\n", GPLR2);
++              printk(KERN_INFO "GAFR0_L 0x%08x\n", GAFR0_L);
++              printk(KERN_INFO "GAFR0_U 0x%08x\n", GAFR0_U);
++              printk(KERN_INFO "GAFR1_L 0x%08x\n", GAFR1_L);
++              printk(KERN_INFO "GAFR1_U 0x%08x\n", GAFR1_U);
++              printk(KERN_INFO "GAFR2_L 0x%08x\n", GAFR2_L);
++              printk(KERN_INFO "GAFR2_U 0x%08x\n", GAFR2_U);
++              printk(KERN_INFO "CKEN  = 0x%08x\n", CKEN);
++              printk(KERN_INFO "ICCR0 = 0x%08x\n", ICCR0);
++              printk(KERN_INFO "STISR = 0x%08x\n", STISR);
++      }
++}
++
++MACHINE_START(PXA_CERF, "CerfBoard PXA Reference Board")
++      MAINTAINER("Intrinsyc Software Inc.")
++      BOOT_MEM(0xa0000000, 0x40000000, 0xfc000000)
++      BOOT_PARAMS(0xa0000100)
++      FIXUP(fixup_cerf)
++      MAPIO(cerf_map_io)
++      INITIRQ(cerf_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/cpu-pxa.c
+@@ -0,0 +1,240 @@
++/*
++ *  linux/arch/arm/mach-pxa/cpu-pxa.c
++ *
++ *  Copyright (C) 2002,2003 Intrinsyc Software
++ *
++ * 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.
++ *
++ * History:
++ *   31-Jul-2002 : Initial version [FB]
++ *   29-Jan-2003 : added PXA255 support [FB]
++ * 
++ * Note:
++ *
++ * Quote from erratum 134: 
++ * ""If the operation of these peripherals would be adversely affected,
++ *   then these peripherals would have to be disabled during a frequency
++ *   change. (MMC,FFUART,STUART,BTUART,IRDA,SSP,UDC,AC97)""
++ *
++ * This sounds like they are not sure what the bug is...
++ * If you run into problems with any of these peripherals, the effected
++ * driver should register with cpu freq notification and disable/enable 
++ * the peripheral on CPUFREQ_PRECHANGE and CPUFREQ_POSTCHANGE.
++ *  
++ * So far I've tested this code only under light load. It works for me.
++ *
++ * TODO: 
++ *   - determine min/max freq at runtime
++ *   - determine pxbus value at runtime
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++
++#include <asm/hardware.h>
++
++#define DEBUGGING     1
++
++#if DEBUGGING
++static unsigned int freq_debug = DEBUGGING;
++#else
++#define freq_debug    0
++#endif  
++
++typedef struct
++{
++      unsigned int khz;
++      unsigned int cccr;
++      unsigned int pxbus;
++} pxa_freqs_t;
++
++#define CCLKCFG_TURBO         0x1
++#define CCLKCFG_FCS           0x2
++
++#define PXA250_REV_A1         0x1
++#define PXA250_REV_B2         0x4
++#define PXA25x_MIN_FREQ               99000
++
++//#define PXA25x_ALLOW_OVERCLOCK
++
++#ifdef PXA25x_ALLOW_OVERCLOCK
++#warning *** Overclocking enabled - this may fry your hardware - you have been warned ***
++#define OC(x...)      x
++#define PXA25x_MAX_FREQ               471000
++#else
++#define OC(x...)
++#define PXA25x_MAX_FREQ               400000
++#endif
++
++/* If CONFIG_CPU_FREQ is turned on but we find (at runtime) 
++ * we can't support scaling, try to handle requests gracefully.
++ */
++static int supported;
++
++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 */
++      {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 */
++      {0,0}
++};
++
++static pxa_freqs_t *pxa_valid_freqs;
++
++/* This should be called with a valid freq point that was
++ * obtained via pxa_validate_speed
++ */
++static pxa_freqs_t * pxa_get_freq_info( unsigned int khz)
++{
++      int i=0;
++      while( pxa_valid_freqs[i].khz)
++      {
++              if( pxa_valid_freqs[i].khz == khz) 
++                      return &pxa_valid_freqs[i]; 
++              i++;
++      }
++
++      /* shouldn't get here */
++      return 0;
++}
++
++/* find a valid frequency point */
++static unsigned int pxa_validate_speed(unsigned int khz)
++{
++      int i=0;
++      unsigned int vfreq = 0;
++      while( pxa_valid_freqs[i].khz && (khz >= pxa_valid_freqs[i].khz))
++      {
++              vfreq = pxa_valid_freqs[i].khz;
++              i++;
++      }
++      return vfreq;
++}
++
++/* This should be called with a valid freq point that was
++ * obtained via pxa_validate_speed
++ */
++static void pxa_setspeed(unsigned int khz)
++{
++      unsigned long flags;
++      unsigned int unused;
++      void *ramstart = phys_to_virt(0xa0000000);
++      pxa_freqs_t *freq_info;
++
++      if( ! supported) return;
++
++      freq_info = pxa_get_freq_info( khz);
++
++      if( ! freq_info) return;
++
++      CCCR = freq_info->cccr;
++      if( freq_debug)
++              printk(KERN_INFO "Changing CPU frequency to %d Mhz (PXbus=%dMhz).\n", 
++                      khz/1000, freq_info->pxbus);
++
++      local_irq_save(flags);
++      __asm__ __volatile__("\
++              ldr     r4, [%1]                        @load MDREFR    \n\
++              b       2f                                              \n\
++              .align  5                                               \n\
++1:                                                                    \n\
++              mcr     p14, 0, %2, c6, c0, 0           @ set CCLKCFG[FCS] \n\
++                                                                      \n\
++              @ restart sdcke 0 / 1                                   \n\
++              bic     r5,  r4,  #(0x00001000 | 0x00008000)    @ MDREFR_E0PIN | MDREFR_E1PIN \n\
++              str     r5,  [%1]                       @clear          \n\
++              str     r4,  [%1]                       @restore        \n\
++                                                                      \n\
++              @ Generate refresh cycles for all banks                 \n\
++              ldr     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++              str     r4, [%3]                                        \n\
++                                                                      \n\
++              b       3f                                              \n\
++2:            b       1b                                              \n\
++3:            nop                                                     \n\
++              "
++              : "=&r" (unused)
++              : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart)
++              : "r4", "r5");
++      local_irq_restore(flags);
++}
++
++static int pxa_init_freqs( void)
++{
++      int cpu_ver; 
++      asm("mrc%? p15, 0, %0, c0, c0" : "=r" (cpu_ver));
++
++      if( (cpu_ver & 0xf) <= PXA250_REV_A1)
++      {
++              return 0;
++      }
++
++      if( (cpu_ver & 0xf) <= PXA250_REV_B2)
++      {
++              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");
++              pxa_valid_freqs = pxa255_valid_freqs;
++      }
++      
++      return 1;
++}
++
++static int __init pxa_clk_init(void)
++{
++      if( pxa_init_freqs())
++      {
++              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");
++              /* 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);
++      }
++
++      return 0;
++}
++
++module_init(pxa_clk_init);
++
++MODULE_AUTHOR ("Intrinsyc Software Inc.");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/csb226.c
+@@ -0,0 +1,180 @@
++/*
++ *  linux/arch/arm/mach-pxa/csb226.c
++ *
++ * (c) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
++ *
++ * 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/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++
++#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/irqs.h>
++#include <asm/hardware/sa1111.h>
++
++#include "generic.h"
++
++static unsigned long csb226_irq_en_mask;
++
++static void csb226_mask_and_ack_irq(unsigned int irq)
++{
++      int csb226_irq = (irq - CSB226_IRQ(0));
++      csb226_irq_en_mask &= ~(1 << csb226_irq);
++      CSB226_IRQ_MASK_EN &= ~(1 << csb226_irq);
++      CSB226_IRQ_SET_CLR &= ~(1 << csb226_irq);
++}
++
++static void csb226_mask_irq(unsigned int irq)
++{
++      int csb226_irq = (irq - CSB226_IRQ(0));
++      csb226_irq_en_mask &= ~(1 << csb226_irq);
++      CSB226_IRQ_MASK_EN &= ~(1 << csb226_irq);
++}
++
++static void csb226_unmask_irq(unsigned int irq)
++{
++      int csb226_irq = (irq - CSB226_IRQ(0));
++      csb226_irq_en_mask |= (1 << csb226_irq);
++      CSB226_IRQ_MASK_EN |= (1 << csb226_irq);
++}
++
++void csb226_irq_demux(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned long irq_status;
++      int i;
++
++      while ((irq_status = CSB226_IRQ_SET_CLR & csb226_irq_en_mask)) {
++              for (i = 0; i < 6; i++) {
++                      if(irq_status & (1<<i)) 
++                              do_IRQ(CSB226_IRQ(i), regs);
++              }
++      }
++}
++
++/* FIXME: this should not be necessary on csb226 */
++static struct irqaction csb226_irq = {
++      name:           "CSB226 FPGA",
++      handler:        csb226_irq_demux,
++      flags:          SA_INTERRUPT
++};
++
++static void __init csb226_init_irq(void)
++{
++      int irq;
++      
++      pxa_init_irq();
++
++      /* setup extra csb226 irqs */
++/* RS: ???
++      for(irq = CSB226_IRQ(0); irq <= CSB226_IRQ(5); irq++)
++      {
++              irq_desc[irq].valid     = 1;
++              irq_desc[irq].probe_ok  = 1;
++              irq_desc[irq].mask_ack  = csb226_mask_and_ack_irq;
++              irq_desc[irq].mask      = csb226_mask_irq;
++              irq_desc[irq].unmask    = csb226_unmask_irq;
++      }
++
++      set_GPIO_IRQ_edge(GPIO_CSB226_IRQ, GPIO_FALLING_EDGE);
++      setup_arm_irq(IRQ_GPIO_CSB226_IRQ, &csb226_irq);
++*/
++}
++
++/* FIXME: not necessary on CSB226? */
++static int __init csb226_init(void)
++{
++      int ret;
++
++      return 0;
++}
++
++__initcall(csb226_init);
++
++static void __init
++fixup_csb226(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++      SET_BANK (0, 0xa0000000, 64*1024*1024);
++      mi->nr_banks      = 1;
++#if 0
++      setup_ramdisk (1, 0, 0, 8192);
++      setup_initrd (__phys_to_virt(0xa1000000), 4*1024*1024);
++      ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
++#endif
++}
++
++/* FIXME: shouldn't this be moved to arch/arm/mach-pxa/mm.c? [RS] */
++static struct map_desc csb226_io_desc[] __initdata = {
++      /* virtual    physical    length      domain     r  w  c  b */
++//    { 0xf4000000, 0x04000000, 0x00ffffff, DOMAIN_IO, 1, 1, 0, 0 }, /* HT4562B PS/2 controller */
++      { 0xf8000000, 0x08000000, 1024*1024,  DOMAIN_IO, 0, 1, 0, 0 }, /* CS8900 LAN controller   */
++//    { 0xe0000000, 0x20000000, 0x0fffffff, DOMAIN_IO, 1, 1, 0, 0 }, /* CompactFlash            */
++#if 0
++      { 0xf0000000, 0x08000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD                    */
++      { 0xf1000000, 0x0c000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 IO             */
++      { 0xf1100000, 0x0e000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 Attr           */
++      { 0xf4000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA1111                  */
++#endif
++  LAST_DESC
++};
++
++static void __init csb226_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(csb226_io_desc);
++
++      /* This enables the BTUART */
++      CKEN |= CKEN7_BTUART;
++      set_GPIO_mode(GPIO42_BTRXD_MD);
++      set_GPIO_mode(GPIO43_BTTXD_MD);
++      set_GPIO_mode(GPIO44_BTCTS_MD);
++      set_GPIO_mode(GPIO45_BTRTS_MD);
++
++      /* This is for the CS8900 chip select */
++      set_GPIO_mode(GPIO78_nCS_2_MD);
++
++      /* setup sleep mode values */
++      PWER  = 0x00000002;
++      PFER  = 0x00000000;
++      PRER  = 0x00000002;
++      PGSR0 = 0x00008000;
++      PGSR1 = 0x003F0202;
++      PGSR2 = 0x0001C000;
++      PCFR |= PCFR_OPDE;
++}
++
++MACHINE_START(CSB226, "Cogent CSB226 Development Platform")
++      MAINTAINER("Robert Schwebel, Pengutronix")
++      BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
++      BOOT_PARAMS(0xa0000100)
++      FIXUP(fixup_csb226)
++      MAPIO(csb226_map_io)
++      INITIRQ(csb226_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/dma.c
+@@ -0,0 +1,131 @@
++/*
++ *  linux/arch/arm/mach-pxa/dma.c
++ *
++ *  PXA DMA registration and IRQ dispatching
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Nov 15, 2001
++ *  Copyright:        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/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++
++static struct dma_channel {
++      char *name;
++      void (*irq_handler)(int, void *, struct pt_regs *);
++      void *data;
++} dma_channels[16];
++
++
++int pxa_request_dma (char *name, pxa_dma_prio prio,
++                       void (*irq_handler)(int, void *, struct pt_regs *),
++                       void *data)
++{
++      unsigned long flags;
++      int i, found = 0;
++
++      /* basic sanity checks */
++      if (!name || !irq_handler)
++              return -EINVAL;
++
++      local_irq_save(flags);
++
++      /* try grabbing a DMA channel with the requested priority */
++      for (i = prio; i < prio + (prio == DMA_PRIO_LOW) ? 8 : 4; i++) {
++              if (!dma_channels[i].name) {
++                      found = 1;
++                      break;
++              }
++      }
++
++      if (!found) {
++              /* requested prio group is full, try hier priorities */
++              for (i = prio-1; i >= 0; i--) {
++                      if (!dma_channels[i].name) {
++                              found = 1;
++                              break;
++                      }
++              }
++      }
++              
++      if (found) {
++              DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
++              dma_channels[i].name = name;
++              dma_channels[i].irq_handler = irq_handler;
++              dma_channels[i].data = data;
++      } else {
++              printk (KERN_WARNING "No more available DMA channels for %s\n", name);
++              i = -ENODEV;
++      }
++
++      local_irq_restore(flags);
++      return i;
++}
++
++void pxa_free_dma (int dma_ch)
++{
++      unsigned long flags;
++
++      if (!dma_channels[dma_ch].name) {
++              printk (KERN_CRIT __FUNCTION__
++                      ": trying to free channel %d which is already freed\n",
++                      dma_ch);
++              return;
++      }
++
++      local_irq_save(flags);
++      DCSR(dma_ch) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
++      dma_channels[dma_ch].name = NULL;
++      local_irq_restore(flags);
++}
++
++static void dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
++{
++      int i, dint = DINT;
++
++      for (i = 0; i < 16; i++) {
++              if (dint & (1 << i)) {
++                      struct dma_channel *channel = &dma_channels[i];
++                      if (channel->name && channel->irq_handler) {
++                              channel->irq_handler(i, channel->data, regs);
++                      } else {
++                              /*
++                               * IRQ for an unregistered DMA channel:
++                               * let's clear the interrupts and disable it.
++                               */
++                              printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i);
++                              DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
++                      }
++              }
++      }
++}
++
++static int __init pxa_dma_init (void)
++{
++      int ret;
++
++      ret = request_irq (IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
++      if (ret)
++              printk (KERN_CRIT "Wow!  Can't register IRQ for DMA\n");
++      return ret;
++}
++
++__initcall(pxa_dma_init);
++
++EXPORT_SYMBOL(pxa_request_dma);
++EXPORT_SYMBOL(pxa_free_dma);
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/generic.c
+@@ -0,0 +1,142 @@
++/*
++ *  linux/arch/arm/mach-pxa/generic.c
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        MontaVista Software Inc.
++ * 
++ * Code common to all PXA machines.
++ *
++ * 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.
++ *
++ * Since this file should be linked before any other machine specific file,
++ * the __initcall() here will be executed first.  This serves as default
++ * initialization stuff for PXA machines which can be overriden later if
++ * need be.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++
++#include <asm/hardware.h>
++#include <asm/system.h>
++#include <asm/pgtable.h>
++#include <asm/mach/map.h>
++
++#include "generic.h"
++
++/*
++ * Various clock factors driven by the CCCR register.
++ */
++
++/* Crystal Frequency to Memory Frequency Multiplier (L) */
++static unsigned char L_clk_mult[32] = { 0, 27, 32, 36, 40, 45, 0, };
++
++/* Memory Frequency to Run Mode Frequency Multiplier (M) */
++static unsigned char M_clk_mult[4] = { 0, 1, 2, 4 };
++
++/* Run Mode Frequency to Turbo Mode Frequency Multiplier (N) */
++/* Note: we store the value N * 2 here. */
++static unsigned char N2_clk_mult[8] = { 0, 0, 2, 3, 4, 0, 6, 0 };
++
++/* Crystal clock */
++#define BASE_CLK      3686400
++
++/*
++ * Get the clock frequency as reflected by CCCR and the turbo flag.
++ * We assume these values have been applied via a fcs.
++ * If info is not 0 we also display the current settings.
++ */
++unsigned int get_clk_frequency_khz( int info)
++{
++      unsigned long cccr, turbo;
++      unsigned int l, L, m, M, n2, N;
++
++      cccr = CCCR;
++      asm( "mrc\tp14, 0, %0, c6, c0, 0" : "=r" (turbo) );
++
++      l  =  L_clk_mult[(cccr >> 0) & 0x1f];
++      m  =  M_clk_mult[(cccr >> 5) & 0x03];
++      n2 = N2_clk_mult[(cccr >> 7) & 0x07];
++
++      L = l * BASE_CLK;
++      M = m * L;
++      N = n2 * M / 2;
++
++      if( info)
++      {
++              L += 5000;
++              printk( KERN_INFO "Memory clock: %d.%02dMHz (*%d)\n",
++                      L / 1000000, (L % 1000000) / 10000, l );
++              M += 5000;
++              printk( KERN_INFO "Run Mode clock: %d.%02dMHz (*%d)\n",
++                      M / 1000000, (M % 1000000) / 10000, m );
++              N += 5000;
++              printk( KERN_INFO "Turbo Mode clock: %d.%02dMHz (*%d.%d, %sactive)\n",
++                      N / 1000000, (N % 1000000) / 10000, n2 / 2, (n2 % 2) * 5,
++                      (turbo & 1) ? "" : "in" );
++      }
++
++      return (turbo & 1) ? (N/1000) : (M/1000);
++}
++
++EXPORT_SYMBOL(get_clk_frequency_khz);
++
++/*
++ * Return the current lclk requency in units of 10kHz
++ */
++unsigned int get_lclk_frequency_10khz(void)
++{
++      return L_clk_mult[(CCCR >> 0) & 0x1f] * BASE_CLK / 10000;
++}
++
++EXPORT_SYMBOL(get_lclk_frequency_10khz);
++
++/*
++ * Handy function to set GPIO alternate functions
++ */
++
++void set_GPIO_mode(int gpio_mode)
++{
++      long flags;
++      int gpio = gpio_mode & GPIO_MD_MASK_NR;
++      int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8;
++      int gafr;
++
++      local_irq_save(flags);
++      if (gpio_mode & GPIO_MD_MASK_DIR)
++              GPDR(gpio) |= GPIO_bit(gpio);
++      else
++              GPDR(gpio) &= ~GPIO_bit(gpio);
++      gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2));
++      GAFR(gpio) = gafr |  (fn  << (((gpio) & 0xf)*2));
++      local_irq_restore(flags);
++}
++
++EXPORT_SYMBOL(set_GPIO_mode);
++
++/* 
++ * Note that 0xfffe0000-0xffffffff is reserved for the vector table and
++ * cache flush area.
++ */
++static struct map_desc standard_io_desc[] __initdata = {
++ /* virtual     physical    length      domain     r  w  c  b */
++  { 0xf6000000, 0x20000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA0 IO */
++  { 0xf7000000, 0x30000000, 0x01000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA1 IO */
++  { 0xf8000000, 0x40000000, 0x01800000, DOMAIN_IO, 0, 1, 0, 0 }, /* Devs */
++  { 0xfa000000, 0x44000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD */
++  { 0xfc000000, 0x48000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Mem Ctl */
++  { 0xff000000, 0x00000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* UNCACHED_PHYS_0 */
++  LAST_DESC
++};
++
++void __init pxa_map_io(void)
++{
++      iotable_init(standard_io_desc);
++      get_clk_frequency_khz( 1);
++}
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/generic.h
+@@ -0,0 +1,19 @@
++/*
++ *  linux/arch/arm/mach-pxa/generic.h
++ *
++ * Author:    Nicolas Pitre
++ * Copyright: 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.
++ */
++
++extern void __init pxa_map_io(void);
++extern void __init pxa_init_irq(void);
++
++#define SET_BANK(__nr,__start,__size) \
++      mi->bank[__nr].start = (__start), \
++      mi->bank[__nr].size = (__size), \
++      mi->bank[__nr].node = (((unsigned)(__start) - PHYS_OFFSET) >> 27)
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/idp.c
+@@ -0,0 +1,142 @@
++/*
++ *  linux/arch/arm/mach-pxa/idp.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) 2001 Cliff Brake, Accelent Systems Inc.
++ * 
++ *  2001-09-13: Cliff Brake <cbrake@accelent.com>
++ *              Initial code
++ */
++#include <linux/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++
++#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 "generic.h"
++
++#define PXA_IDP_REV02
++
++#ifndef PXA_IDP_REV02
++/* shadow registers for write only registers */
++unsigned int idp_cpld_led_control_shadow = 0x1;
++unsigned int idp_cpld_periph_pwr_shadow = 0xd;
++unsigned int ipd_cpld_cir_shadow = 0;
++unsigned int idp_cpld_kb_col_high_shadow = 0;
++unsigned int idp_cpld_kb_col_low_shadow = 0;
++unsigned int idp_cpld_pccard_en_shadow = 0xC3;
++unsigned int idp_cpld_gpioh_dir_shadow = 0;
++unsigned int idp_cpld_gpioh_value_shadow = 0;
++unsigned int idp_cpld_gpiol_dir_shadow = 0;
++unsigned int idp_cpld_gpiol_value_shadow = 0;
++
++/*
++ * enable all LCD signals -- they should still be on
++ * write protect flash
++ * enable all serial port transceivers
++ */
++
++unsigned int idp_control_port_shadow = ((0x7 << 21) |                 /* LCD power */
++                                      (0x1 << 19) |           /* disable flash write enable */
++                                      (0x7 << 9));            /* enable serial port transeivers */
++
++#endif
++
++static int __init idp_init(void)
++{
++      printk("idp_init()\n");
++      return 0;
++}
++
++__initcall(idp_init);
++
++static void __init idp_init_irq(void)
++{
++      pxa_init_irq();
++}
++
++static void __init
++fixup_idp(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++#ifdef PXA_IDP_REV02
++      SET_BANK (0, 0xa0000000, 64*1024*1024);
++#else
++      SET_BANK (0, 0xa0000000, 32*1024*1024);
++#endif
++      mi->nr_banks      = 1;
++#if 0
++      setup_ramdisk (1, 0, 0, 8192);
++      setup_initrd (__phys_to_virt(0xa1000000), 4*1024*1024);
++      ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
++#endif
++}
++
++static struct map_desc idp_io_desc[] __initdata = {
++ /* virtual     physical    length      domain     r  w  c  b */
++
++
++#ifndef PXA_IDP_REV02
++  { IDP_CTRL_PORT_BASE,
++    IDP_CTRL_PORT_PHYS,
++    IDP_CTRL_PORT_SIZE,
++    DOMAIN_IO,
++    0, 1, 0, 0 },
++#endif
++
++  { IDP_IDE_BASE,
++    IDP_IDE_PHYS,
++    IDP_IDE_SIZE,
++    DOMAIN_IO,
++    0, 1, 0, 0 },
++  { IDP_ETH_BASE,
++    IDP_ETH_PHYS,
++    IDP_ETH_SIZE,
++    DOMAIN_IO,
++    0, 1, 0, 0 },
++  { IDP_COREVOLT_BASE,
++    IDP_COREVOLT_PHYS,
++    IDP_COREVOLT_SIZE,
++    DOMAIN_IO,
++    0, 1, 0, 0 },
++  { IDP_CPLD_BASE,
++    IDP_CPLD_PHYS,
++    IDP_CPLD_SIZE,
++    DOMAIN_IO,
++    0, 1, 0, 0 },
++      
++  LAST_DESC
++};
++
++static void __init idp_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(idp_io_desc);
++
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO(TOUCH_PANEL_IRQ), TOUCH_PANEL_IRQ_EDGE);
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO(SMC_IRQ), GPIO_RISING_EDGE);
++}
++
++MACHINE_START(PXA_IDP, "Accelent Xscale IDP")
++      MAINTAINER("Accelent Systems Inc.")
++      BOOT_MEM(0xa0000000, 0x40000000, 0xfc000000)
++      FIXUP(fixup_idp)
++      MAPIO(idp_map_io)
++      INITIRQ(idp_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/innokom.c
+@@ -0,0 +1,129 @@
++/*
++ *  linux/arch/arm/mach-pxa/innokom.c
++ *
++ * (c) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
++ *
++ * 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/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++
++#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/irqs.h>
++#include <asm/hardware/sa1111.h>
++
++#include "generic.h"
++
++
++static void __init innokom_init_irq(void)
++{
++      pxa_init_irq();
++}
++
++
++void sw_update_handler( int irq, void* dev_id,struct pt_regs* regs)
++{
++}
++
++
++void reset_handler( int irq, void* dev_id,struct pt_regs* regs)
++{
++}
++
++
++static int __init innokom_init(void)
++{
++      int sw_irq = GPIO_2_80_TO_IRQ(11);      /* software update button */
++      int reset_irq = GPIO_2_80_TO_IRQ(3);    /* reset button           */
++
++      set_GPIO_IRQ_edge(11,GPIO_FALLING_EDGE);
++      if (request_irq(sw_irq,sw_update_handler,SA_INTERRUPT,"software update button",NULL))
++              printk(KERN_INFO "innokom: can't get assigned irq %i\n",sw_irq);
++
++      set_GPIO_IRQ_edge(3,GPIO_FALLING_EDGE);
++      if (request_irq(reset_irq,reset_handler,SA_INTERRUPT,"reset button",NULL))
++              printk(KERN_INFO "innokom: can't get assigned irq %i\n",reset_irq);
++
++      return 0;
++}
++
++
++__initcall(innokom_init);
++
++
++static void __init
++fixup_innokom(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++      /* we probably want to get this information from the bootloader later */
++      SET_BANK (0, 0xa0000000, 64*1024*1024); 
++      mi->nr_banks      = 1;
++}
++
++
++/* memory mapping */
++static struct map_desc innokom_io_desc[] __initdata = {
++/*  virtual           physical          length            domain     r  w  c  b                      */
++  { INNOKOM_ETH_BASE, INNOKOM_ETH_PHYS, INNOKOM_ETH_SIZE, DOMAIN_IO, 0, 1, 0, 0 }, /* ETH SMSC 91111 */
++  LAST_DESC
++};
++
++static void __init innokom_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(innokom_io_desc);
++
++      /* Enable the BTUART */
++      CKEN |= CKEN7_BTUART;
++      set_GPIO_mode(GPIO42_BTRXD_MD);
++      set_GPIO_mode(GPIO43_BTTXD_MD);
++      set_GPIO_mode(GPIO44_BTCTS_MD);
++      set_GPIO_mode(GPIO45_BTRTS_MD);
++
++      set_GPIO_mode(GPIO33_nCS_5_MD); /* SMSC network chip */
++
++      /* setup sleep mode values */
++      PWER  = 0x00000002;
++      PFER  = 0x00000000;
++      PRER  = 0x00000002;
++      PGSR0 = 0x00008000;
++      PGSR1 = 0x003F0202;
++      PGSR2 = 0x0001C000;
++      PCFR |= PCFR_OPDE;
++}
++
++MACHINE_START(INNOKOM, "Auerswald Innokom")
++      MAINTAINER("Robert Schwebel, Pengutronix")
++      BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
++      BOOT_PARAMS(0xa0000100)
++      FIXUP(fixup_innokom)
++      MAPIO(innokom_map_io)
++      INITIRQ(innokom_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/irq.c
+@@ -0,0 +1,282 @@
++/*
++ *  linux/arch/arm/mach-pxa/irq.c
++ *  
++ *  Generic PXA IRQ handling, GPIO IRQ demultiplexing, etc.
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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/init.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/ptrace.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/irq.h>
++
++#include "generic.h"
++
++
++/*
++ * PXA GPIO edge detection for IRQs:
++ * IRQs are generated on Falling-Edge, Rising-Edge, or both.
++ * This must be called *before* the appropriate IRQ is registered.
++ * Use this instead of directly setting GRER/GFER.
++ */
++
++static int GPIO_IRQ_rising_edge[3];
++static int GPIO_IRQ_falling_edge[3];
++
++void set_GPIO_IRQ_edge (int gpio_nr, int edge)
++{
++      long flags;
++      local_irq_save(flags);
++      set_GPIO_mode(gpio_nr | GPIO_IN);
++      if (edge & GPIO_FALLING_EDGE)
++              set_bit (gpio_nr, GPIO_IRQ_falling_edge);
++      else
++              clear_bit (gpio_nr, GPIO_IRQ_falling_edge);
++      if (edge & GPIO_RISING_EDGE)
++              set_bit (gpio_nr, GPIO_IRQ_rising_edge);
++      else
++              clear_bit (gpio_nr, GPIO_IRQ_rising_edge);
++      irq_desc[IRQ_GPIO(gpio_nr)].valid = 1;
++      local_irq_restore(flags);
++}
++
++EXPORT_SYMBOL(set_GPIO_IRQ_edge);
++
++
++/*
++ * We don't need to ACK IRQs on the PXA unless they're GPIOs
++ * this is for IRQs known as PXA_IRQ([10...31]).
++ */
++
++static void pxa_mask_irq(unsigned int irq)
++{
++      ICMR &= ~(1 << (irq + PXA_IRQ_SKIP));
++}
++
++static void pxa_unmask_irq(unsigned int irq)
++{
++      ICMR |= (1 << (irq + PXA_IRQ_SKIP));
++}
++
++/*
++ * GPIO IRQs must be acknoledged.  This is for GPIO 0 and 1.
++ */
++
++static void pxa_mask_and_ack_GPIO_0_1_irq(unsigned int irq)
++{
++      ICMR &= ~(1 << (irq + PXA_IRQ_SKIP));
++      GEDR0 = (1 << (irq - IRQ_GPIO0));
++}
++
++static void pxa_mask_GPIO_0_1_irq(unsigned int irq)
++{
++      ICMR &= ~(1 << (irq + PXA_IRQ_SKIP));
++}
++
++static void pxa_unmask_GPIO_0_1_irq(unsigned int irq)
++{
++      int gpio = irq - IRQ_GPIO0;
++      GRER0 = (GRER0 & ~(1 << gpio))|(GPIO_IRQ_rising_edge[0] & (1 << gpio));
++      GFER0 = (GFER0 & ~(1 << gpio))|(GPIO_IRQ_falling_edge[0] & (1 << gpio));
++      ICMR |= (1 << (irq + PXA_IRQ_SKIP));
++}
++
++/*
++ * Demux handler for GPIO 2-80 edge detect interrupts
++ */
++
++static int GPIO_2_80_enabled[3];      /* enabled i.e. unmasked GPIO IRQs */
++static int GPIO_2_80_spurious[3];     /* GPIOs that triggered when masked */
++
++static void pxa_GPIO_2_80_demux(int irq, void *dev_id,
++                                  struct pt_regs *regs)
++{
++      int i, gedr, spurious;
++
++      while ((gedr = (GEDR0 & ~3))) {
++              /*
++               * We don't want to clear GRER/GFER when the corresponding
++               * IRQ is masked because we could miss a level transition
++               * i.e. an IRQ which need servicing as soon as it is
++               * unmasked.  However, such situation should happen only
++               * during the loop below.  Thus all IRQs which aren't
++               * enabled at this point are considered spurious.  Those
++               * are cleared but only de-activated if they happen twice.
++               */
++              spurious = gedr & ~GPIO_2_80_enabled[0];
++              if (spurious) {
++                      GEDR0 = spurious;
++                      GRER0 &= ~(spurious & GPIO_2_80_spurious[0]);
++                      GFER0 &= ~(spurious & GPIO_2_80_spurious[0]);
++                      GPIO_2_80_spurious[0] |= spurious;
++                      gedr ^= spurious;
++                      if (!gedr) continue;
++              }
++
++              for (i = 2; i < 32; ++i) {
++                      if (gedr & (1<<i)) {
++                              do_IRQ (IRQ_GPIO(2) + i - 2, regs);
++                      }
++              }
++      }
++      while ((gedr = GEDR1)) {
++              spurious = gedr & ~GPIO_2_80_enabled[1];
++              if (spurious) {
++                      GEDR1 = spurious;
++                      GRER1 &= ~(spurious & GPIO_2_80_spurious[1]);
++                      GFER1 &= ~(spurious & GPIO_2_80_spurious[1]);
++                      GPIO_2_80_spurious[1] |= spurious;
++                      gedr ^= spurious;
++                      if (!gedr) continue;
++              }
++
++              for (i = 0; i < 32; ++i) {
++                      if (gedr & (1<<i)) {
++                              do_IRQ (IRQ_GPIO(32) + i, regs);
++                      }
++              }
++      }
++      while ((gedr = (GEDR2 & 0x0001ffff))) {
++              spurious = gedr & ~GPIO_2_80_enabled[2];
++              if (spurious) {
++                      GEDR2 = spurious;
++                      GRER2 &= ~(spurious & GPIO_2_80_spurious[2]);
++                      GFER2 &= ~(spurious & GPIO_2_80_spurious[2]);
++                      GPIO_2_80_spurious[2] |= spurious;
++                      gedr ^= spurious;
++                      if (!gedr) continue;
++              }
++
++              for (i = 0; i < 17; ++i) {
++                      if (gedr & (1<<i)) {
++                              do_IRQ (IRQ_GPIO(64) + i, regs);
++                      }
++              }
++      }
++}
++
++static struct irqaction GPIO_2_80_irqaction = {
++      name:           "GPIO 2-80",
++      handler:        pxa_GPIO_2_80_demux,
++      flags:          SA_INTERRUPT
++};
++
++#define GRER_x(i)     (*(&GRER0 + (i)))
++#define GFER_x(i)     (*(&GFER0 + (i)))
++#define GEDR_x(i)     (*(&GEDR0 + (i)))
++#define GPLR_x(i)     (*(&GPLR0 + (i)))
++
++static void pxa_mask_and_ack_GPIO_2_80_irq(unsigned int irq)
++{
++      int gpio_nr = IRQ_TO_GPIO_2_80(irq);
++      int mask = 1 << (gpio_nr & 0x1f);
++      int index = gpio_nr >> 5;
++      GPIO_2_80_spurious[index] &= ~mask;
++      GPIO_2_80_enabled[index] &= ~mask;
++      GEDR_x(index) = mask;
++}
++
++static void pxa_mask_GPIO_2_80_irq(unsigned int irq)
++{
++      int gpio_nr = IRQ_TO_GPIO_2_80(irq);
++      int mask = 1 << (gpio_nr & 0x1f);
++      int index = gpio_nr >> 5;
++      GPIO_2_80_spurious[index] &= ~mask;
++      GPIO_2_80_enabled[index] &= ~mask;
++}
++
++static void pxa_unmask_GPIO_2_80_irq(unsigned int irq)
++{
++      int gpio_nr = IRQ_TO_GPIO_2_80(irq);
++      int mask = 1 << (gpio_nr & 0x1f);
++      int index = gpio_nr >> 5;
++      if (GPIO_2_80_spurious[index] & mask) {
++              /*
++               * We don't want to miss an interrupt that would have occurred
++               * while it was masked.  Simulate it if it is the case.
++               */
++              int state = GPLR_x(index);
++              if (((state & GPIO_IRQ_rising_edge[index]) |
++                   (~state & GPIO_IRQ_falling_edge[index])) & mask)
++              {
++                      /* just in case it gets referenced: */
++                      struct pt_regs dummy;
++
++                      memzero(&dummy, sizeof(dummy));
++                      do_IRQ(irq, &dummy);
++
++                      /* we are being called recursively from do_IRQ() */
++                      return;
++              }
++      }
++      GPIO_2_80_enabled[index] |= mask;
++      GRER_x(index) =
++              (GRER_x(index) & ~mask) | (GPIO_IRQ_rising_edge[index] & mask);
++      GFER_x(index) =
++              (GFER_x(index) & ~mask) | (GPIO_IRQ_falling_edge[index] & mask);
++}
++
++
++void __init pxa_init_irq(void)
++{
++      int irq;
++
++      /* disable all IRQs */
++      ICMR = 0;
++
++      /* all IRQs are IRQ, not FIQ */
++      ICLR = 0;
++
++      /* clear all GPIO edge detects */
++      GFER0 = GFER1 = GFER2 = 0;
++      GRER0 = GRER1 = GRER2 = 0;
++      GEDR0 = GEDR0;
++      GEDR1 = GEDR1;
++      GEDR2 = GEDR2;
++
++      /* only unmasked interrupts kick us out of idle */
++      ICCR = 1;
++
++      for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq <= PXA_IRQ(31); irq++) {
++              irq_desc[irq].valid     = 1;
++              irq_desc[irq].probe_ok  = 0;
++              irq_desc[irq].mask_ack  = pxa_mask_irq;
++              irq_desc[irq].mask      = pxa_mask_irq;
++              irq_desc[irq].unmask    = pxa_unmask_irq;
++      }
++
++      /*
++       * Note: GPIO IRQs are initially invalid until set_GPIO_IRQ_edge()
++       * is called at least once.
++       */
++
++      for (irq = IRQ_GPIO0; irq <= IRQ_GPIO1; irq++) {
++              irq_desc[irq].valid     = 0;
++              irq_desc[irq].probe_ok  = 1;
++              irq_desc[irq].mask_ack  = pxa_mask_and_ack_GPIO_0_1_irq;
++              irq_desc[irq].mask      = pxa_mask_GPIO_0_1_irq;
++              irq_desc[irq].unmask    = pxa_unmask_GPIO_0_1_irq;
++      }
++
++      for (irq = IRQ_GPIO(2); irq <= IRQ_GPIO(80); irq++) {
++              irq_desc[irq].valid     = 0;
++              irq_desc[irq].probe_ok  = 1;
++              irq_desc[irq].mask_ack  = pxa_mask_and_ack_GPIO_2_80_irq;
++              irq_desc[irq].mask      = pxa_mask_GPIO_2_80_irq;
++              irq_desc[irq].unmask    = pxa_unmask_GPIO_2_80_irq;
++      }
++      setup_arm_irq( IRQ_GPIO_2_80, &GPIO_2_80_irqaction );
++}
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/leds-cerf.c
+@@ -0,0 +1,135 @@
++/*
++ *  linux/arch/arm/mach-pxa/leds-cerf.c
++ *
++ *  Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
++ *
++ *  Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com>
++ *
++ *  Original (leds-footbridge.c) by Russell King
++ *
++ *  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/init.h>
++
++#include <asm/hardware.h>
++#include <asm/leds.h>
++#include <asm/system.h>
++
++#include "leds.h"
++
++
++#define LED_STATE_ENABLED     1
++#define LED_STATE_CLAIMED     2
++
++static unsigned int led_state;
++static unsigned int hw_led_state;
++
++void pxa_cerf_leds_event(led_event_t evt)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      switch (evt) {
++      case led_start:
++              hw_led_state = CERF_HEARTBEAT_LED;
++              led_state = LED_STATE_ENABLED;
++              break;
++
++      case led_stop:
++              led_state &= ~LED_STATE_ENABLED;
++              break;
++
++      case led_claim:
++              led_state |= LED_STATE_CLAIMED;
++              hw_led_state = CERF_HEARTBEAT_LED;
++              break;
++
++      case led_release:
++              led_state &= ~LED_STATE_CLAIMED;
++              hw_led_state = CERF_HEARTBEAT_LED;
++              break;
++
++#ifdef CONFIG_LEDS_TIMER
++      case led_timer:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state ^= CERF_HEARTBEAT_LED;
++              break;
++#endif
++
++#ifdef CONFIG_LEDS_CPU
++      case led_idle_start:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state |= CERF_SYS_BUSY_LED;
++              break;
++
++      case led_idle_end:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state &= ~CERF_SYS_BUSY_LED;
++              break;
++#endif
++
++      case led_halted:
++              break;
++
++      case led_green_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~CERF_HEARTBEAT_LED;
++              break;
++
++      case led_green_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= CERF_HEARTBEAT_LED;
++              break;
++
++      case led_amber_on:
++              break;
++
++      case led_amber_off:
++              break;
++
++#ifndef CONFIG_PXA_CERF_PDA
++      case led_red_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~CERF_SYS_BUSY_LED;
++              break;
++
++      case led_red_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= CERF_SYS_BUSY_LED;
++              break;
++#endif
++      default:
++              break;
++      }
++
++      if  (led_state & LED_STATE_ENABLED)
++      {
++              switch (hw_led_state) {
++              case 0: // all on
++                      CERF_HEARTBEAT_LED_ON;
++                      CERF_SYS_BUSY_LED_ON;
++                      break;
++              case 1: // turn off heartbeat, status on:
++                      CERF_HEARTBEAT_LED_OFF;
++                      CERF_SYS_BUSY_LED_ON;
++                      break;
++              case 2: // status off, heartbeat on:
++                      CERF_HEARTBEAT_LED_ON;
++                      CERF_SYS_BUSY_LED_OFF;
++                      break;
++              case 3: // turn them both off...
++                      CERF_HEARTBEAT_LED_OFF;
++                      CERF_SYS_BUSY_LED_OFF;
++                      break;
++              default:
++                      break;
++              }
++      }
++      local_irq_restore(flags);
++}
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/leds-idp.c
+@@ -0,0 +1,112 @@
++/*
++ * linux/arch/arm/mach-pxa/leds-idp.c
++ *
++ * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
++ *
++ * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com>
++ *
++ * Original (leds-footbridge.c) by Russell King
++ * 
++ * Macros for actual LED manipulation should be in machine specific
++ * files in this 'mach' directory.
++ */
++
++
++#include <linux/config.h>
++#include <linux/init.h>
++
++#include <asm/hardware.h>
++#include <asm/leds.h>
++#include <asm/system.h>
++
++#include "leds.h"
++
++#define LED_STATE_ENABLED     1
++#define LED_STATE_CLAIMED     2
++
++static unsigned int led_state;
++static unsigned int hw_led_state;
++
++void idp_leds_event(led_event_t evt)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      switch (evt) {
++      case led_start:
++              hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
++              led_state = LED_STATE_ENABLED;
++              break;
++
++      case led_stop:
++              led_state &= ~LED_STATE_ENABLED;
++              break;
++
++      case led_claim:
++              led_state |= LED_STATE_CLAIMED;
++              hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
++              break;
++
++      case led_release:
++              led_state &= ~LED_STATE_CLAIMED;
++              hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
++              break;
++
++#ifdef CONFIG_LEDS_TIMER
++      case led_timer:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state ^= IDP_HB_LED;
++              break;
++#endif
++
++#ifdef CONFIG_LEDS_CPU
++      case led_idle_start:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state |= IDP_BUSY_LED;
++              break;
++
++      case led_idle_end:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state &= ~IDP_BUSY_LED;
++              break;
++#endif
++
++      case led_halted:
++              break;
++
++      case led_green_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~IDP_HB_LED;
++              break;
++
++      case led_green_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= IDP_HB_LED;
++              break;
++
++      case led_amber_on:
++              break;
++
++      case led_amber_off:
++              break;
++
++      case led_red_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~IDP_BUSY_LED;
++              break;
++
++      case led_red_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= IDP_BUSY_LED;
++              break;
++
++      default:
++              break;
++      }
++
++      if  (led_state & LED_STATE_ENABLED)
++              IDP_WRITE_LEDS(hw_led_state);
++
++      local_irq_restore(flags);
++}
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/leds-lubbock.c
+@@ -0,0 +1,134 @@
++/*
++ * linux/arch/arm/mach-pxa/leds-lubbock.c
++ *
++ * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
++ *
++ * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com>
++ *
++ * Original (leds-footbridge.c) by Russell King
++ *
++ * See leds.h for bit definitions.  The first version defines D28 on the
++ * Lubbock dev board as the heartbeat, and D27 as the Sys_busy led.
++ * There's plenty more if you're interested in adding them :)
++ */
++
++
++#include <linux/config.h>
++#include <linux/init.h>
++
++#include <asm/hardware.h>
++#include <asm/leds.h>
++#include <asm/system.h>
++
++#include "leds.h"
++
++
++#define LED_STATE_ENABLED     1
++#define LED_STATE_CLAIMED     2
++
++static unsigned int led_state;
++static unsigned int hw_led_state;
++
++void lubbock_leds_event(led_event_t evt)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++
++      switch (evt) {
++      case led_start:
++              hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED;
++              led_state = LED_STATE_ENABLED;
++              break;
++
++      case led_stop:
++              led_state &= ~LED_STATE_ENABLED;
++              break;
++
++      case led_claim:
++              led_state |= LED_STATE_CLAIMED;
++              hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED;
++              break;
++
++      case led_release:
++              led_state &= ~LED_STATE_CLAIMED;
++              hw_led_state = HEARTBEAT_LED | SYS_BUSY_LED;
++              break;
++
++#ifdef CONFIG_LEDS_TIMER
++      case led_timer:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state ^= HEARTBEAT_LED;
++              break;
++#endif
++
++#ifdef CONFIG_LEDS_CPU
++      case led_idle_start:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state |= SYS_BUSY_LED;
++              break;
++
++      case led_idle_end:
++              if (!(led_state & LED_STATE_CLAIMED))
++                      hw_led_state &= ~SYS_BUSY_LED;
++              break;
++#endif
++
++      case led_halted:
++              break;
++
++      case led_green_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~HEARTBEAT_LED;
++              break;
++
++      case led_green_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= HEARTBEAT_LED;
++              break;
++
++      case led_amber_on:
++              break;
++
++      case led_amber_off:
++              break;
++
++      case led_red_on:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state &= ~SYS_BUSY_LED;
++              break;
++
++      case led_red_off:
++              if (led_state & LED_STATE_CLAIMED)
++                      hw_led_state |= SYS_BUSY_LED;
++              break;
++
++      default:
++              break;
++      }
++
++      if  (led_state & LED_STATE_ENABLED)
++      {
++              switch (hw_led_state) {
++              case 0: // all on
++                      HEARTBEAT_LED_ON;
++                      SYS_BUSY_LED_ON;
++                      break;
++              case 1: // turn off heartbeat, status on:
++                      HEARTBEAT_LED_OFF;
++                      SYS_BUSY_LED_ON;
++                      break;
++              case 2: // status off, heartbeat on:
++                      HEARTBEAT_LED_ON;
++                      SYS_BUSY_LED_OFF;
++                      break;
++              case 3: // turn them both off...
++                      HEARTBEAT_LED_OFF;
++                      SYS_BUSY_LED_OFF;
++                      break;
++              default:
++                      break;
++              }
++      }
++      local_irq_restore(flags);
++}
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/leds.c
+@@ -0,0 +1,32 @@
++/*
++ * linux/arch/arm/mach-pxa/leds.c
++ *
++ * xscale LEDs dispatcher
++ * 
++ * Copyright (C) 2001 Nicolas Pitre
++ * 
++ * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc.
++ */
++#include <linux/config.h>
++#include <linux/init.h>
++
++#include <asm/leds.h>
++#include <asm/mach-types.h>
++
++#include "leds.h"
++
++static int __init
++pxa_leds_init(void)
++{
++      if (machine_is_lubbock())
++              leds_event = lubbock_leds_event;
++      if (machine_is_pxa_idp())
++              leds_event = idp_leds_event;
++      if (machine_is_pxa_cerf())
++              leds_event = pxa_cerf_leds_event;
++
++      leds_event(led_start);
++      return 0;
++}
++
++__initcall(pxa_leds_init);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/leds.h
+@@ -0,0 +1,12 @@
++/*
++ * include/asm-arm/arch-pxa/leds.h
++ *
++ * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc.
++ *
++ * blinky lights for various PXA-based systems:
++ *
++ */
++
++extern void lubbock_leds_event(led_event_t evt);
++extern void idp_leds_event(led_event_t evt);
++extern void pxa_cerf_leds_event(led_event_t evt);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/lubbock.c
+@@ -0,0 +1,157 @@
++/*
++ *  linux/arch/arm/mach-pxa/lubbock.c
++ *
++ *  Support for the Intel DBPXA250 Development Platform.
++ *  
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/bitops.h>
++
++#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/hardware/sa1111.h>
++
++#include "generic.h"
++
++#ifdef CONFIG_SA1111
++ #include "sa1111.h"
++#endif
++
++
++static unsigned long lubbock_irq_enabled;
++
++static void lubbock_mask_irq(unsigned int irq)
++{
++      int lubbock_irq = (irq - LUBBOCK_IRQ(0));
++      LUB_IRQ_MASK_EN = (lubbock_irq_enabled &= ~(1 << lubbock_irq));
++}
++
++static void lubbock_unmask_irq(unsigned int irq)
++{
++      int lubbock_irq = (irq - LUBBOCK_IRQ(0));
++      /* the irq can be acknowledged only if deasserted, so it's done here */
++      LUB_IRQ_SET_CLR &= ~(1 << lubbock_irq);
++      LUB_IRQ_MASK_EN = (lubbock_irq_enabled |= (1 << lubbock_irq));
++}
++
++void lubbock_irq_demux(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned long pending = LUB_IRQ_SET_CLR & lubbock_irq_enabled;
++      do {
++              GEDR(0) = GPIO_bit(0);  /* clear useless edge notification */
++              if (likely(pending))
++                      do_IRQ( LUBBOCK_IRQ(0) + __ffs(pending), regs );
++              pending = LUB_IRQ_SET_CLR & lubbock_irq_enabled;
++      } while (pending);
++}
++
++static struct irqaction lubbock_irq = {
++      name:           "Lubbock FPGA",
++      handler:        lubbock_irq_demux,
++      flags:          SA_INTERRUPT
++};
++
++static void __init lubbock_init_irq(void)
++{
++      int irq;
++      
++      pxa_init_irq();
++
++      /* setup extra lubbock irqs */
++      for(irq = LUBBOCK_IRQ(0); irq <= LUBBOCK_LAST_IRQ; irq++) {
++              irq_desc[irq].valid     = 1;
++              irq_desc[irq].probe_ok  = 1;
++              irq_desc[irq].mask_ack  = lubbock_mask_irq;
++              irq_desc[irq].mask      = lubbock_mask_irq;
++              irq_desc[irq].unmask    = lubbock_unmask_irq;
++      }
++
++      set_GPIO_IRQ_edge(GPIO_LUBBOCK_IRQ, GPIO_FALLING_EDGE);
++      setup_arm_irq(IRQ_GPIO_LUBBOCK_IRQ, &lubbock_irq);
++}
++
++static int __init lubbock_init(void)
++{
++      int ret;
++
++      ret = sa1111_probe(LUBBOCK_SA1111_BASE);
++      if (ret)
++              return ret;
++      sa1111_wake();
++      sa1111_init_irq(LUBBOCK_SA1111_IRQ);
++      return 0;
++}
++
++__initcall(lubbock_init);
++
++static void __init
++fixup_lubbock(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++      /* Some boards have 32MB some 64MB.  Let's use a safe default */
++      SET_BANK (0, 0xa0000000, 32*1024*1024);
++      mi->nr_banks      = 1;
++}
++
++static struct map_desc lubbock_io_desc[] __initdata = {
++ /* virtual     physical    length      domain     r  w  c  b */
++  { 0xf0000000, 0x08000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD */
++  { 0xf1000000, 0x0c000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 IO */
++  { 0xf1100000, 0x0e000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 Attr */
++  { 0xf4000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* SA1111 */
++  LAST_DESC
++};
++
++static void __init lubbock_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(lubbock_io_desc);
++
++      /* This enables the BTUART */
++      CKEN |= CKEN7_BTUART;
++      set_GPIO_mode(GPIO42_BTRXD_MD);
++      set_GPIO_mode(GPIO43_BTTXD_MD);
++      set_GPIO_mode(GPIO44_BTCTS_MD);
++      set_GPIO_mode(GPIO45_BTRTS_MD);
++
++      /* This is for the SMC chip select */
++      set_GPIO_mode(GPIO79_nCS_3_MD);
++
++      /* setup sleep mode values */
++      PWER  = 0x00000002;
++      PFER  = 0x00000000;
++      PRER  = 0x00000002;
++      PGSR0 = 0x00008000;
++      PGSR1 = 0x003F0202;
++      PGSR2 = 0x0001C000;
++      PCFR |= PCFR_OPDE;
++}
++
++MACHINE_START(LUBBOCK, "Intel DBPXA250 Development Platform")
++      MAINTAINER("MontaVista Software Inc.")
++      BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
++      FIXUP(fixup_lubbock)
++      MAPIO(lubbock_map_io)
++      INITIRQ(lubbock_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/pm.c
+@@ -0,0 +1,265 @@
++/*
++ * PXA250/210 Power Management Routines
++ *
++ * Original code for the SA11x0:
++ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
++ *
++ * Modified for the PXA250 by Nicolas Pitre:
++ * Copyright (c) 2002 Monta Vista Software, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++#include <linux/config.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 <asm/hardware.h>
++#include <asm/memory.h>
++#include <asm/system.h>
++#include <asm/leds.h>
++
++
++/*
++ * Debug macros
++ */
++#undef DEBUG
++
++extern void pxa_cpu_suspend(void);
++extern void pxa_cpu_resume(void);
++
++#define SAVE(x)               sleep_save[SLEEP_SAVE_##x] = x
++#define RESTORE(x)    x = sleep_save[SLEEP_SAVE_##x]
++
++/*
++ * List of global PXA peripheral registers to preserve.
++ * More ones like CP and general purpose register values are preserved
++ * with the stack pointer in sleep.S.
++ */
++enum {        SLEEP_SAVE_START = 0,
++
++      SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER,
++      SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3,
++
++      SLEEP_SAVE_GPDR0, SLEEP_SAVE_GPDR1, SLEEP_SAVE_GPDR2,
++      SLEEP_SAVE_GRER0, SLEEP_SAVE_GRER1, SLEEP_SAVE_GRER2,
++      SLEEP_SAVE_GFER0, SLEEP_SAVE_GFER1, SLEEP_SAVE_GFER2,
++      SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR2_L,
++      SLEEP_SAVE_GAFR0_U, SLEEP_SAVE_GAFR1_U, SLEEP_SAVE_GAFR2_U,
++
++      SLEEP_SAVE_FFIER, SLEEP_SAVE_FFLCR, SLEEP_SAVE_FFMCR,
++      SLEEP_SAVE_FFSPR, SLEEP_SAVE_FFISR,
++      SLEEP_SAVE_FFDLL, SLEEP_SAVE_FFDLH,
++
++      SLEEP_SAVE_ICMR,
++      SLEEP_SAVE_CKEN,
++
++      SLEEP_SAVE_CKSUM,
++
++      SLEEP_SAVE_SIZE
++};
++
++
++int pm_do_suspend(void)
++{
++      unsigned long sleep_save[SLEEP_SAVE_SIZE];
++      unsigned long checksum = 0;
++      int i;
++
++      cli();
++      clf();
++
++      leds_event(led_stop);
++
++      /* preserve current time */
++      RCNR = xtime.tv_sec;
++
++      /* 
++       * Temporary solution.  This won't be necessary once
++       * we move pxa support into the serial/* driver
++       * Save the FF UART 
++       */
++      SAVE(FFIER);
++      SAVE(FFLCR);
++      SAVE(FFMCR);
++      SAVE(FFSPR);
++      SAVE(FFISR);
++      FFLCR |= 0x80;
++      SAVE(FFDLL);
++      SAVE(FFDLH);
++      FFLCR &= 0xef;
++
++      /* save vital registers */
++      SAVE(OSCR);
++      SAVE(OSMR0);
++      SAVE(OSMR1);
++      SAVE(OSMR2);
++      SAVE(OSMR3);
++      SAVE(OIER);
++
++      SAVE(GPDR0); SAVE(GPDR1); SAVE(GPDR2);
++      SAVE(GRER0); SAVE(GRER1); SAVE(GRER2);
++      SAVE(GFER0); SAVE(GFER1); SAVE(GFER2);
++      SAVE(GAFR0_L); SAVE(GAFR0_U);
++      SAVE(GAFR1_L); SAVE(GAFR1_U);
++      SAVE(GAFR2_L); SAVE(GAFR2_U);
++
++      SAVE(ICMR);
++      ICMR = 0;
++
++      SAVE(CKEN);
++      CKEN = 0;
++
++      /* Note: wake up source are set up in each machine specific files */
++
++      /* clear GPIO transition detect  bits */
++      GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2;
++
++      /* Clear sleep reset status */
++      RCSR = RCSR_SMR;
++
++      /* set resume return address */
++      PSPR = virt_to_phys(pxa_cpu_resume);
++
++      /* before sleeping, calculate and save a checksum */
++      for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++)
++              checksum += sleep_save[i];
++      sleep_save[SLEEP_SAVE_CKSUM] = checksum;
++
++      /* *** go zzz *** */
++      pxa_cpu_suspend();
++
++      /* after sleeping, validate the checksum */
++      checksum = 0;
++      for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++)
++              checksum += sleep_save[i];
++
++      /* if invalid, display message and wait for a hardware reset */
++      if (checksum != sleep_save[SLEEP_SAVE_CKSUM]) {
++#ifdef CONFIG_ARCH_LUBBOCK
++              LUB_HEXLED = 0xbadbadc5;
++#endif
++              while (1);
++      }
++
++      /* ensure not to come back here if it wasn't intended */
++      PSPR = 0;
++
++      /* restore registers */
++      RESTORE(GPDR0); RESTORE(GPDR1); RESTORE(GPDR2);
++      RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2);
++      RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2);
++      RESTORE(GAFR0_L); RESTORE(GAFR0_U);
++      RESTORE(GAFR1_L); RESTORE(GAFR1_U);
++      RESTORE(GAFR2_L); RESTORE(GAFR2_U);
++
++      PSSR = PSSR_PH;
++
++      RESTORE(OSMR0);
++      RESTORE(OSMR1);
++      RESTORE(OSMR2);
++      RESTORE(OSMR3);
++      RESTORE(OSCR);
++      RESTORE(OIER);
++
++      RESTORE(CKEN);
++
++      ICLR = 0;
++      ICCR = 1;
++      RESTORE(ICMR);
++
++      /* 
++       * Temporary solution.  This won't be necessary once
++       * we move pxa support into the serial/* driver.
++       * Restore the FF UART.
++       */
++      RESTORE(FFMCR);
++      RESTORE(FFSPR);
++      RESTORE(FFLCR);
++      FFLCR |= 0x80;
++      RESTORE(FFDLH);
++      RESTORE(FFDLL);
++      RESTORE(FFLCR);
++      RESTORE(FFISR);
++      FFFCR = 0x07;
++      RESTORE(FFIER);
++
++      /* restore current time */
++      xtime.tv_sec = RCNR;
++
++#ifdef DEBUG
++      printk(KERN_DEBUG "*** made it back from resume\n");
++#endif
++
++      leds_event(led_start);
++
++      sti();
++
++      return 0;
++}
++
++unsigned long sleep_phys_sp(void *sp)
++{
++      return virt_to_phys(sp);
++}
++
++#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},
++      {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
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/pxa_usb.h
+@@ -0,0 +1,230 @@
++/*
++ * pxa_usb.h
++ *
++ * Public interface to the pxa USB core. For use by client modules
++ * like usb-eth and usb-char.
++ *
++ * 02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100_usb.h
++ *
++ */
++
++#ifndef _PXA_USB_H
++#define _PXA_USB_H
++#include <asm/byteorder.h>
++
++typedef void (*usb_callback_t)(int flag, int size);
++
++/* in usb_ctl.c (see also descriptor methods at bottom of file) */
++
++// Open the USB client for client and initialize data structures
++// to default values, but _do not_ start UDC.
++int pxa_usb_open( const char * client_name );
++
++// Start UDC running
++int pxa_usb_start( void );
++
++// Immediately stop udc, fire off completion routines w/-EINTR
++int pxa_usb_stop( void ) ;
++
++// Disconnect client from usb core
++int pxa_usb_close( void ) ;
++
++// set notify callback for when core reaches configured state
++// return previous pointer (if any)
++typedef void (*usb_notify_t)(void);
++usb_notify_t pxa_set_configured_callback( usb_notify_t callback );
++
++/* in usb_send.c */
++int pxa_usb_xmitter_avail( void );
++int pxa_usb_send(char *buf, int len, usb_callback_t callback);
++void sa110a_usb_send_reset(void);
++
++/* in usb_recev.c */
++int pxa_usb_recv(char *buf, int len, usb_callback_t callback);
++void pxa_usb_recv_reset(void);
++
++//////////////////////////////////////////////////////////////////////////////
++// Descriptor Management
++//////////////////////////////////////////////////////////////////////////////
++
++#define DescriptorHeader \
++      __u8 bLength;        \
++      __u8 bDescriptorType
++
++
++// --- Device Descriptor -------------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 bcdUSB;                  /* USB specification revision number in BCD */
++       __u8  bDeviceClass;    /* USB class for entire device */
++       __u8  bDeviceSubClass; /* USB subclass information for entire device */
++       __u8  bDeviceProtocol; /* USB protocol information for entire device */
++       __u8  bMaxPacketSize0; /* Max packet size for endpoint zero */
++       __u16 idVendor;        /* USB vendor ID */
++       __u16 idProduct;       /* USB product ID */
++       __u16 bcdDevice;       /* vendor assigned device release number */
++       __u8  iManufacturer;   /* index of manufacturer string */
++       __u8  iProduct;        /* index of string that describes product */
++       __u8  iSerialNumber;   /* index of string containing device serial number */
++       __u8  bNumConfigurations; /* number fo configurations */
++} __attribute__ ((packed)) device_desc_t;
++
++// --- Configuration Descriptor ------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 wTotalLength;        /* total # of bytes returned in the cfg buf 4 this cfg */
++       __u8  bNumInterfaces;      /* number of interfaces in this cfg */
++       __u8  bConfigurationValue; /* used to uniquely ID this cfg */
++       __u8  iConfiguration;      /* index of string describing configuration */
++       __u8  bmAttributes;        /* bitmap of attributes for ths cfg */
++       __u8  MaxPower;                    /* power draw in 2ma units */
++} __attribute__ ((packed)) config_desc_t;
++
++// bmAttributes:
++enum { 
++      USB_CONFIG_REMOTEWAKE=0x20, 
++      USB_CONFIG_SELFPOWERED=0x40,
++      USB_CONFIG_BUSPOWERED=0x80 
++};
++
++// MaxPower:
++#define USB_POWER( x)  ((x)>>1) /* convert mA to descriptor units of A for MaxPower */
++
++// --- Interface Descriptor ---------------
++
++typedef struct {
++       DescriptorHeader;
++       __u8  bInterfaceNumber;   /* Index uniquely identfying this interface */
++       __u8  bAlternateSetting;  /* ids an alternate setting for this interface */
++       __u8  bNumEndpoints;      /* number of endpoints in this interface */
++       __u8  bInterfaceClass;    /* USB class info applying to this interface */
++       __u8  bInterfaceSubClass; /* USB subclass info applying to this interface */
++       __u8  bInterfaceProtocol; /* USB protocol info applying to this interface */
++       __u8  iInterface;         /* index of string describing interface */
++} __attribute__ ((packed)) intf_desc_t;
++
++// --- Endpoint  Descriptor ---------------
++
++typedef struct {
++       DescriptorHeader;
++       __u8  bEndpointAddress;  /* 0..3 ep num, bit 7: 0 = 0ut 1= in */
++       __u8  bmAttributes;      /* 0..1 = 0: ctrl, 1: isoc, 2: bulk 3: intr */
++       __u16 wMaxPacketSize;    /* data payload size for this ep in this cfg */
++       __u8  bInterval;         /* polling interval for this ep in this cfg */
++} __attribute__ ((packed)) ep_desc_t;
++
++// bEndpointAddress:
++enum { 
++      USB_OUT =0, 
++      USB_IN  =1 
++};
++
++#define USB_EP_ADDRESS(a,d) (((a)&0xf) | ((d) << 7))
++// bmAttributes:
++enum { 
++      USB_EP_CNTRL    =0, 
++      USB_EP_BULK     =2, 
++      USB_EP_INT      =3, 
++      USB_EP_ISO      =4 
++};
++
++// --- String Descriptor -------------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 bString[1];                /* unicode string .. actaully 'n' __u16s */
++} __attribute__ ((packed)) string_desc_t;
++
++/*=======================================================
++ * Handy helpers when working with above
++ *
++ */
++// these are x86-style 16 bit "words" ...
++#define make_word_c( w ) __constant_cpu_to_le16(w)
++#define make_word( w )   __cpu_to_le16(w)
++
++// descriptor types
++enum { 
++    USB_DESC_DEVICE   = 1,
++    USB_DESC_CONFIG   = 2,
++    USB_DESC_STRING   = 3,
++    USB_DESC_INTERFACE        = 4,
++    USB_DESC_ENDPOINT = 5
++};
++
++
++/*=======================================================
++ * Default descriptor layout for SA-1100 and SA-1110 UDC
++ */
++
++enum {
++  UNUSED = 0,
++
++  BULK_IN1  =  1,
++  BULK_OUT1 =  2,
++  ISO_IN1   =  3,
++  ISO_OUT1  =  4,
++  INT_IN1   =  5,
++
++  BULK_IN2  =  6,
++  BULK_OUT2 =  7,
++  ISO_IN2   =  8,
++  ISO_OUT2  =  9,
++  INT_IN2   = 10,
++
++  BULK_IN3  = 11,
++  BULK_OUT3 = 12,
++  ISO_IN3   = 13,
++  ISO_OUT3  = 14,
++  INT_IN3   = 15
++} /*endpoint_type*/;
++
++/* "config descriptor buffer" - that is, one config,
++   ..one interface and 2 endpoints */
++struct cdb {
++       config_desc_t cfg;
++       intf_desc_t   intf;
++       ep_desc_t     ep1;
++       ep_desc_t     ep2;
++} __attribute__ ((packed));
++
++/* all SA device descriptors */
++typedef struct {
++       device_desc_t dev;   /* device descriptor */
++       struct cdb b;        /* bundle of descriptors for this cfg */
++} __attribute__ ((packed)) desc_t;
++
++
++/*=======================================================
++ * Descriptor API
++ */
++
++/* Get the address of the statically allocated desc_t structure
++   in the usb core driver. Clients can modify this between
++   the time they call pxa_usb_open() and pxa_usb_start()
++*/
++desc_t *
++pxa_usb_get_descriptor_ptr( void );
++
++
++/* Set a pointer to the string descriptor at "index". The driver
++ ..has room for 8 string indicies internally. Index zero holds
++ ..a LANGID code and is set to US English by default. Inidices
++ ..1-7 are available for use in the config descriptors as client's
++ ..see fit. This pointer is assumed to be good as long as the
++ ..SA usb core is open (so statically allocate them). Returnes -EINVAL
++ ..if index out of range */
++int pxa_usb_set_string_descriptor( int index, string_desc_t * p );
++
++/* reverse of above */
++string_desc_t *
++pxa_usb_get_string_descriptor( int index );
++
++/* kmalloc() a string descriptor and convert "p" to unicode in it */
++string_desc_t *
++pxa_usb_kmalloc_string_descriptor( const char * p );
++
++#endif /* _PXA_USB_H */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/sa1111.c
+@@ -0,0 +1,3 @@
++#include "../mach-sa1100/sa1111.c"
++
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/sa1111.h
+@@ -0,0 +1,2 @@
++#include "../mach-sa1100/sa1111.h"
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/sleep.S
+@@ -0,0 +1,150 @@
++/*
++ * Low-level PXA250/210 sleep/wakeUp support
++ *
++ * Initial SA1110 code:
++ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
++ *
++ * Adapted for PXA by Nicolas Pitre:
++ * Copyright (c) 2002 Monta Vista Software, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++#include <linux/config.h>
++#include <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/hardware.h>
++
++              .text
++
++/*
++ * pxa_cpu_suspend()
++ *
++ * Forces CPU into sleep state
++ */
++
++ENTRY(pxa_cpu_suspend)
++
++      mra     r2, r3, acc0
++      stmfd   sp!, {r2 - r12, lr}             @ save registers on stack
++
++      @ get coprocessor registers
++      mrc     p15, 0, r4, c15, c1, 0          @ CP access reg
++      mrc     p15, 0, r5, c13, c0, 0          @ PID
++      mrc     p15, 0, r6, c3, c0, 0           @ domain ID
++      mrc     p15, 0, r7, c2, c0, 0           @ translation table base addr
++      mrc     p15, 0, r8, c1, c1, 0           @ auxiliary control reg
++      mrc     p15, 0, r9, c1, c0, 0           @ control reg
++
++      @ store them plus current virtual stack ptr on stack
++      mov     r10, sp
++      stmfd   sp!, {r4 - r10}
++
++      @ preserve phys address of stack
++      mov     r0, sp
++      bl      sleep_phys_sp
++      ldr     r1, =sleep_save_sp
++      str     r0, [r1]
++
++      @ clean data cache 
++      bl      cpu_xscale_cache_clean_invalidate_all
++
++      @ Put the processor to sleep
++      @ (also workaround for sighting 28071)
++
++      @ prepare value for sleep mode
++      mov     r1, #3                          @ sleep mode
++
++      @ prepare to put SDRAM into self-refresh manually
++      ldr     r4, =MDREFR
++      ldr     r5, [r4]
++      orr     r5, r5, #MDREFR_SLFRSH
++
++      @ prepare pointer to physical address 0 (virtual mapping in generic.c)
++      mov     r2, #UNCACHED_PHYS_0
++
++      @ align execution to a cache line
++      b       1f
++
++      .ltorg
++      .align  5
++1:
++
++      @ All needed values are now in registers. 
++      @ These last instructions should be in cache
++
++      @ put SDRAM into self-refresh   
++      str     r5, [r4]
++
++      @ force address lines low by reading at physical address 0
++      ldr     r3, [r2]
++
++      @ enter sleep mode
++      mcr     p14, 0, r1, c7, c0, 0
++
++20:   nop
++      b       20b                             @ loop waiting for sleep
++
++/*
++ * cpu_pxa_resume()
++ *
++ * entry point from bootloader into kernel during resume
++ *
++ * Note: Yes, part of the following code is located into the .data section.
++ *       This is to allow sleep_save_sp to be accessed with a relative load
++ *       while we can't rely on any MMU translation.  We could have put
++ *       sleep_save_sp in the .text section as well, but some setups might
++ *       insist on it to be truely read-only.
++ */
++
++      .data
++      .align 5
++ENTRY(pxa_cpu_resume)
++      mov     r0, #I_BIT | F_BIT | MODE_SVC   @ set SVC, irqs off
++      msr     cpsr_c, r0
++
++      ldr     r0, sleep_save_sp               @ stack phys addr
++      ldr     r2, =resume_after_mmu           @ its absolute virtual address
++      ldmfd   r0, {r4 - r9, sp}               @ CP regs + virt stack ptr
++
++      mov     r1, #0
++      mcr     p15, 0, r1, c8, c7, 0           @ invalidate I & D TLBs
++      mcr     p15, 0, r1, c7, c7, 0           @ invalidate I & D caches, BTB
++
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++      bic     r9, r9, #0x0004                 @ see cpu_xscale_proc_init
++#endif
++
++      mcr     p15, 0, r4, c15, c1, 0          @ CP access reg
++      mcr     p15, 0, r5, c13, c0, 0          @ PID
++      mcr     p15, 0, r6, c3, c0, 0           @ domain ID
++      mcr     p15, 0, r7, c2, c0, 0           @ translation table base addr
++      mcr     p15, 0, r8, c1, c1, 0           @ auxiliary control reg
++      b       resume_turn_on_mmu              @ cache align execution
++
++      .align 5
++resume_turn_on_mmu:
++      mcr     p15, 0, r9, c1, c0, 0           @ turn on MMU, caches, etc.
++
++      @ Let us ensure we jump to resume_after_mmu only when the mcr above
++      @ actually took effect.  They call it the "cpwait" operation.
++      mrc     p15, 0, r1, c2, c0, 0           @ queue a dependency on CP15
++      sub     pc, r2, r1, lsr #32             @ jump to virtual addr 
++      nop
++      nop
++      nop
++
++sleep_save_sp:
++      .word   0                               @ preserve stack phys ptr here
++
++      .text
++resume_after_mmu:
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++      bl      cpu_xscale_proc_init
++#endif
++      ldmfd   sp!, {r2, r3}
++      mar     acc0, r2, r3
++      ldmfd   sp!, {r4 - r12, pc}             @ return to caller
++
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/trizeps2.c
+@@ -0,0 +1,105 @@
++/*
++ *  linux/arch/arm/mach-pxa/trizeps2.c
++ *
++ *  Support for the Keith&Koep MT6N Development Platform.
++ *  
++ *  Author:   Luc De Cock
++ *  Created:  Jan 13, 2003
++ *  Copyright:        Teradyne DS, Ltd.
++ *  
++ *  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/init.h>
++#include <linux/major.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++
++#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 "generic.h"
++
++static unsigned long trizeps2_irq_en_mask;
++unsigned short trizeps2_bcr_shadow = 0x50; // 0x70
++
++
++static void __init trizeps2_init_irq(void)
++{
++      int irq;
++      
++      pxa_init_irq();
++
++      set_GPIO_IRQ_edge(GPIO_ETHERNET_IRQ, GPIO_RISING_EDGE);
++}
++
++static int __init trizeps2_init(void)
++{
++      /* Configure the BCR register */
++      unsigned short *bcr = (unsigned short *) TRIZEPS2_BCR_BASE;
++
++      *bcr = trizeps2_bcr_shadow;
++      return 0;
++}
++
++__initcall(trizeps2_init);
++
++static void __init
++fixup_trizeps2(struct machine_desc *desc, struct param_struct *params,
++              char **cmdline, struct meminfo *mi)
++{
++#ifdef TRIZEPS2_MEM_64MB
++      SET_BANK (0, 0xa0000000, 64*1024*1024);
++#else
++      SET_BANK (0, 0xa0000000, 32*1024*1024);
++#endif
++      mi->nr_banks      = 1;
++}
++
++static struct map_desc trizeps2_io_desc[] __initdata = {
++ /* virtual     physical    length      domain     r  w  c  b */
++  { 0xf0000000, 0x0e000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* BCR */
++  { 0xf0100000, 0x0c000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA STATUS */
++  { 0xf1000000, 0x0c800000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 IO */
++  { 0xf1100000, 0x0e000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* LAN91C96 Attr */
++  { 0xf2000000, 0x0d800000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* TTL-IO */
++  LAST_DESC
++};
++
++static void __init trizeps2_map_io(void)
++{
++      pxa_map_io();
++      iotable_init(trizeps2_io_desc);
++
++      /* This is for the SMC chip select */
++      set_GPIO_mode(GPIO79_nCS_3_MD);
++
++      /* setup sleep mode values */
++      PWER  = 0x00000002;
++      PFER  = 0x00000000;
++      PRER  = 0x00000002;
++      PGSR0 = 0x00008000;
++      PGSR1 = 0x003F0202;
++      PGSR2 = 0x0001C000;
++      PCFR |= PCFR_OPDE;
++}
++
++MACHINE_START(TRIZEPS2, "Keith-n-Koep MT6N Development Platform")
++      MAINTAINER("Luc De Cock")
++      BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
++      FIXUP(fixup_trizeps2)
++      MAPIO(trizeps2_map_io)
++      INITIRQ(trizeps2_init_irq)
++MACHINE_END
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb-char.c
+@@ -0,0 +1,719 @@
++/*
++ * (C) Copyright 2000-2001 Extenex Corporation
++ *
++ *    This program is free software; you can redistribute it and/or modify
++ *    it under the terms of the GNU General Public License as published by
++ *    the Free Software Foundation; either version 2 of the License, or
++ *    (at your option) any later version.
++ *
++ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *  usb-char.c
++ *
++ *  Miscellaneous character device interface for SA1100 USB function
++ *    driver.
++ *
++ *  Background:
++ *  The SA1100 function driver ported from the Compaq Itsy project
++ *  has an interface, usb-eth.c, to feed network packets over the
++ *  usb wire and into the Linux TCP/IP stack.
++ *
++ *  This file replaces that one with a simple character device
++ *  interface that allows unstructured "byte pipe" style reads and
++ *  writes over the USB bulk endpoints by userspace programs.
++ *
++ *  A new define, CONFIG_SA1100_USB_NETLINK, has been created that,
++ *  when set, (the default) causes the ethernet interface to be used.
++ *  When not set, this more pedestrian character interface is linked
++ *  in instead.
++ *
++ *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
++ *
++ *  ward.willats@extenex.com
++ *
++ *  To do:
++ *  - Can't dma into ring buffer directly with pci_map/unmap usb_recv
++ *    uses and get bytes out at the same time DMA is going on. Investigate:
++ *    a) changing usb_recv to use alloc_consistent() at client request; or
++ *    b) non-ring-buffer based data structures. In the meantime, I am using
++ *    a bounce buffer. Simple, but wasteful.
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/cache.h>
++#include <linux/poll.h>
++#include <linux/circ_buf.h>
++#include <linux/timer.h>
++
++#include <asm/io.h>
++#include <asm/semaphore.h>
++#include <asm/proc/page.h>
++#include <asm/mach-types.h>
++
++#include "usb-char.h"
++#include "pxa_usb.h"
++
++
++
++//////////////////////////////////////////////////////////////////////////////
++// Driver  Options
++//////////////////////////////////////////////////////////////////////////////
++
++#define VERSION       "0.4"
++
++
++#define VERBOSITY 1
++
++#if VERBOSITY
++# define      PRINTK(x, a...) printk (x, ## a)
++#else
++# define      PRINTK(x, a...) /**/
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Globals - Macros - Enums - Structures
++//////////////////////////////////////////////////////////////////////////////
++#ifndef MIN
++#define MIN( a, b ) ((a)<(b)?(a):(b))
++#endif
++
++typedef int bool; enum { false = 0, true = 1 };
++
++static const char pszMe[] = "usbchr: ";
++
++static wait_queue_head_t wq_read;
++static wait_queue_head_t wq_write;
++static wait_queue_head_t wq_poll;
++
++/* Serialze multiple writers onto the transmit hardware
++.. since we sleep the writer during transmit to stay in
++.. sync. (Multiple writers don't make much sense, but..) */
++static DECLARE_MUTEX( xmit_sem );
++
++// size of usb DATA0/1 packets. 64 is standard maximum
++// for bulk transport, though most hosts seem to be able
++// to handle larger.
++#define TX_PACKET_SIZE 64
++#define RX_PACKET_SIZE 64
++#define RBUF_SIZE  (4*PAGE_SIZE)
++
++static struct wcirc_buf {
++  char *buf;
++  int in;
++  int out;
++} rx_ring = { NULL, 0, 0 };
++
++static struct {
++       unsigned long  cnt_rx_complete;
++       unsigned long  cnt_rx_errors;
++       unsigned long  bytes_rx;
++       unsigned long  cnt_tx_timeouts;
++       unsigned long  cnt_tx_errors;
++       unsigned long  bytes_tx;
++} charstats;
++
++
++static char * tx_buf = NULL;
++static char * packet_buffer = NULL;
++static int sending = 0;
++static int usb_ref_count = 0;
++static int last_tx_result = 0;
++static int last_rx_result = 0;
++static int last_tx_size = 0;
++static struct timer_list tx_timer;
++
++//////////////////////////////////////////////////////////////////////////////
++// Prototypes
++//////////////////////////////////////////////////////////////////////////////
++static char *         what_the_f( int e );
++static void   free_txrx_buffers( void );
++static void     twiddle_descriptors( void );
++static void     free_string_descriptors( void ) ;
++static int      usbc_open( struct inode *pInode, struct file *pFile );
++static void     rx_done_callback_packet_buffer( int flag, int size );
++
++static void     tx_timeout( unsigned long );
++static void     tx_done_callback( int flag, int size );
++
++static ssize_t  usbc_read( struct file *, char *, size_t, loff_t * );
++static ssize_t  usbc_write( struct file *, const char *, size_t, loff_t * );
++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait );
++static int usbc_ioctl( struct inode *pInode, struct file *pFile,
++                       unsigned int nCmd, unsigned long argument );
++static int      usbc_close( struct inode *pInode, struct file *pFile );
++
++#ifdef CONFIG_SA1100_EXTENEX1
++static void     extenex_configured_notify_proc( void );
++#endif
++//////////////////////////////////////////////////////////////////////////////
++// Private Helpers
++//////////////////////////////////////////////////////////////////////////////
++
++static char * what_the_f( int e )
++{
++       char * p;
++       switch( e ) {
++       case 0:
++                p = "noErr";
++                break;
++       case -ENODEV:
++                p = "ENODEV - usb not in config state";
++                break;
++       case -EBUSY:
++                p = "EBUSY - another request on the hardware";
++                break;
++       case -EAGAIN:
++                p = "EAGAIN";
++                break;
++       case -EINTR:
++                p = "EINTR - interrupted\n";
++                break;
++       case -EPIPE:
++                p = "EPIPE - zero length xfer\n";
++                break;
++       default:
++                p = "????";
++                break;
++       }
++       return p;
++}
++
++static void free_txrx_buffers( void )
++{
++       if ( rx_ring.buf != NULL ) {
++                kfree( rx_ring.buf );
++                rx_ring.buf = NULL;
++       }
++       if ( packet_buffer != NULL ) {
++                kfree( packet_buffer );
++                packet_buffer = NULL;
++       }
++       if ( tx_buf != NULL ) {
++                kfree( tx_buf );
++                tx_buf = NULL;
++       }
++}
++
++/* twiddle_descriptors()
++ * It is between open() and start(). Setup descriptors.
++ */
++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;
++       pDesc->b.ep2.wMaxPacketSize = make_word_c( TX_PACKET_SIZE );
++       pDesc->b.ep2.bmAttributes   = USB_EP_BULK;
++
++         if ( machine_is_extenex1() ) {
++#ifdef CONFIG_SA1100_EXTENEX1
++                pDesc->dev.idVendor = make_word_c( 0xC9F );
++                pDesc->dev.idProduct = 1;
++                pDesc->dev.bcdDevice = make_word_c( 0x0001 );
++                pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED;
++                pDesc->b.cfg.MaxPower = 0;
++
++                pString = pxa_usb_kmalloc_string_descriptor( "Extenex" );
++                if ( pString ) {
++                         pxa_usb_set_string_descriptor( 1, pString );
++                         pDesc->dev.iManufacturer = 1;
++                }
++
++                pString = pxa_usb_kmalloc_string_descriptor( "Handheld Theater" );
++                if ( pString ) {
++                         pxa_usb_set_string_descriptor( 2, pString );
++                         pDesc->dev.iProduct = 2;
++                }
++
++                pString = pxa_usb_kmalloc_string_descriptor( "00000000" );
++                if ( pString ) {
++                         pxa_usb_set_string_descriptor( 3, pString );
++                         pDesc->dev.iSerialNumber = 3;
++                }
++
++                pString = pxa_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" );
++                if ( pString ) {
++                         pxa_usb_set_string_descriptor( 4, pString );
++                         pDesc->b.intf.iInterface = 4;
++                }
++                pxa_set_configured_callback( extenex_configured_notify_proc );
++#endif
++       }
++}
++
++static void free_string_descriptors( void )
++{
++       if ( machine_is_extenex1() ) {
++                string_desc_t * pString;
++                int i;
++                for( i = 1 ; i <= 4 ; i++ ) {
++                         pString = pxa_usb_get_string_descriptor( i );
++                         if ( pString )
++                                      kfree( pString );
++                }
++       }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// ASYNCHRONOUS
++//////////////////////////////////////////////////////////////////////////////
++static  void kick_start_rx( void )
++{
++       if ( usb_ref_count ) {
++                int total_space  = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                if ( total_space >= RX_PACKET_SIZE ) {
++                         pxa_usb_recv( packet_buffer,
++                                                              RX_PACKET_SIZE,
++                                                              rx_done_callback_packet_buffer
++                                                    );
++                }
++       }
++}
++/*
++ * rx_done_callback_packet_buffer()
++ * We have completed a DMA xfer into the temp packet buffer.
++ * Move to ring.
++ *
++ * flag values:
++ * on init,  -EAGAIN
++ * on reset, -EINTR
++ * on RPE, -EIO
++ * on short packet -EPIPE
++ */
++static void
++rx_done_callback_packet_buffer( int flag, int size )
++{
++       charstats.cnt_rx_complete++;
++
++       if ( flag == 0 || flag == -EPIPE ) {
++                size_t n;
++
++                charstats.bytes_rx += size;
++
++                n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                n = MIN( n, size );
++                size -= n;
++
++                memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n );
++                rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1);
++                memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size );
++                rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1);
++
++                wake_up_interruptible( &wq_read );
++                wake_up_interruptible( &wq_poll );
++
++                last_rx_result = 0;
++
++                kick_start_rx();
++
++       } else if ( flag != -EAGAIN ) {
++                charstats.cnt_rx_errors++;
++                last_rx_result = flag;
++                wake_up_interruptible( &wq_read );
++                wake_up_interruptible( &wq_poll );
++       }
++       else  /* init, start a read */
++                kick_start_rx();
++}
++
++
++static void tx_timeout( unsigned long unused )
++{
++      printk( "%stx timeout\n", pszMe );
++      pxa_usb_send_reset();
++      charstats.cnt_tx_timeouts++;
++}
++
++
++// on init, -EAGAIN
++// on reset, -EINTR
++// on TPE, -EIO
++static void tx_done_callback( int flags, int size )
++{
++       if ( flags == 0 )
++                charstats.bytes_tx += size;
++       else
++                charstats.cnt_tx_errors++;
++       last_tx_size = size;
++       last_tx_result = flags;
++       sending = 0;
++       wake_up_interruptible( &wq_write );
++       wake_up_interruptible( &wq_poll );
++}
++
++
++//////////////////////////////////////////////////////////////////////////////
++// Workers
++//////////////////////////////////////////////////////////////////////////////
++
++static int usbc_open( struct inode *pInode, struct file *pFile )
++{
++       int retval = 0;
++
++       PRINTK( KERN_DEBUG "%sopen()\n", pszMe );
++
++       /* start usb core */
++       retval = pxa_usb_open( "usb-char" );
++       if ( retval ) return retval;
++
++       /* allocate memory */
++       if ( usb_ref_count == 0 ) {
++                tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA );
++                if ( tx_buf == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++                rx_ring.buf =
++                      (char*) kmalloc( RBUF_SIZE, GFP_KERNEL );
++
++                if ( rx_ring.buf == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++
++                packet_buffer =
++                      (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA  );
++
++                if ( packet_buffer == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++                rx_ring.in = rx_ring.out = 0;
++                memset( &charstats, 0, sizeof( charstats ) );
++                sending = 0;
++                last_tx_result = 0;
++                last_tx_size = 0;
++       }
++
++       /* modify default descriptors */
++       twiddle_descriptors();
++
++       retval = pxa_usb_start();
++       if ( retval ) {
++                printk( "%sAGHH! Could not USB core\n", pszMe );
++                free_txrx_buffers();
++                return retval;
++       }
++       usb_ref_count++;   /* must do _before_ kick_start() */
++       MOD_INC_USE_COUNT;
++       kick_start_rx();
++       return 0;
++
++ malloc_fail:
++       free_txrx_buffers();
++       return -ENOMEM;
++}
++
++/*
++ * Read endpoint. Note that you can issue a read to an
++ * unconfigured endpoint. Eventually, the host may come along
++ * and configure underneath this module and data will appear.
++ */
++static ssize_t usbc_read( struct file *pFile, char *pUserBuffer,
++                        size_t stCount, loff_t *pPos )
++{
++       ssize_t retval;
++       int flags;
++       DECLARE_WAITQUEUE( wait, current );
++
++       PRINTK( KERN_DEBUG "%sread()\n", pszMe );
++
++       local_irq_save( flags );
++       if ( last_rx_result == 0 ) {
++                local_irq_restore( flags );
++       } else {  /* an error happended and receiver is paused */
++                local_irq_restore( flags );
++                last_rx_result = 0;
++                kick_start_rx();
++       }
++
++       add_wait_queue( &wq_read, &wait );
++       while( 1 ) {
++                ssize_t bytes_avail;
++                ssize_t bytes_to_end;
++
++                set_current_state( TASK_INTERRUPTIBLE );
++
++                /* snap ring buf state */
++                local_irq_save( flags );
++                bytes_avail  = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                local_irq_restore( flags );
++
++                if ( bytes_avail != 0 ) {
++                         ssize_t bytes_to_move = MIN( stCount, bytes_avail );
++                         retval = 0;          // will be bytes transfered
++                         if ( bytes_to_move != 0 ) {
++                                      size_t n = MIN( bytes_to_end, bytes_to_move );
++                                      if ( copy_to_user( pUserBuffer,
++                                                                         &rx_ring.buf[ rx_ring.out ],
++                                                                         n ) ) {
++                                               retval = -EFAULT;
++                                               break;
++                                      }
++                                      bytes_to_move -= n;
++                                      retval += n;
++                                      // might go 1 char off end, so wrap
++                                      rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1);
++                                      if ( copy_to_user( pUserBuffer + n,
++                                                                         &rx_ring.buf[ rx_ring.out ],
++                                                                         bytes_to_move )
++                                               ) {
++                                               retval = -EFAULT;
++                                               break;
++                                      }
++                                      rx_ring.out += bytes_to_move;           // cannot wrap
++                                      retval += bytes_to_move;
++                                      kick_start_rx();
++                         }
++                         break;
++                }
++                else if ( last_rx_result ) {
++                         retval = last_rx_result;
++                         break;
++                }
++                else if ( pFile->f_flags & O_NONBLOCK ) {  // no data, can't sleep
++                         retval = -EAGAIN;
++                         break;
++                }
++                else if ( signal_pending( current ) ) {   // no data, can sleep, but signal
++                         retval = -ERESTARTSYS;
++                         break;
++                }
++                schedule();                                                           // no data, can sleep
++       }
++       set_current_state( TASK_RUNNING );
++       remove_wait_queue( &wq_read, &wait );
++
++       if ( retval < 0 )
++                printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) );
++       return retval;
++}
++
++/*
++ * Write endpoint. This routine attempts to break the passed in buffer
++ * into usb DATA0/1 packet size chunks and send them to the host.
++ * (The lower-level driver tries to do this too, but easier for us
++ * to manage things here.)
++ *
++ * We are at the mercy of the host here, in that it must send an IN
++ * token to us to pull this data back, so hopefully some higher level
++ * protocol is expecting traffic to flow in that direction so the host
++ * is actually polling us. To guard against hangs, a 5 second timeout
++ * is used.
++ *
++ * This routine takes some care to only report bytes sent that have
++ * actually made it across the wire. Thus we try to stay in lockstep
++ * with the completion routine and only have one packet on the xmit
++ * hardware at a time. Multiple simultaneous writers will get
++ * "undefined" results.
++ *
++  */
++static ssize_t  usbc_write( struct file *pFile, const char * pUserBuffer,
++                                                       size_t stCount, loff_t *pPos )
++{
++       ssize_t retval = 0;
++       ssize_t stSent = 0;
++
++       DECLARE_WAITQUEUE( wait, current );
++
++       PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount );
++
++       down( &xmit_sem );  // only one thread onto the hardware at a time
++
++       while( stCount != 0 && retval == 0 ) {
++                int nThisTime  = MIN( TX_PACKET_SIZE, stCount );
++                copy_from_user( tx_buf, pUserBuffer, nThisTime );
++                sending = nThisTime;
++                retval = pxa_usb_send( tx_buf, nThisTime, tx_done_callback );
++                if ( retval < 0 ) {
++                         char * p = what_the_f( retval );
++                         printk( "%sCould not queue xmission. rc=%d - %s\n",
++                                         pszMe, retval, p );
++                         sending = 0;
++                         break;
++                }
++                /* now have something on the diving board */
++                add_wait_queue( &wq_write, &wait );
++                tx_timer.expires = jiffies + ( HZ * 5 );
++                add_timer( &tx_timer );
++                while( 1 ) {
++                         set_current_state( TASK_INTERRUPTIBLE );
++                         if ( sending == 0 ) {  /* it jumped into the pool */
++                                      del_timer( &tx_timer );
++                                      retval = last_tx_result;
++                                      if ( retval == 0 ) {
++                                               stSent          += last_tx_size;
++                                               pUserBuffer += last_tx_size;
++                                               stCount     -= last_tx_size;
++                                      }
++                                      else
++                                               printk( "%sxmission error rc=%d - %s\n",
++                                                               pszMe, retval, what_the_f(retval) );
++                                      break;
++                         }
++                         else if ( signal_pending( current ) ) {
++                                      del_timer( &tx_timer );
++                                      printk( "%ssignal\n", pszMe  );
++                                      retval = -ERESTARTSYS;
++                                      break;
++                         }
++                         schedule();
++                }
++                set_current_state( TASK_RUNNING );
++                remove_wait_queue( &wq_write, &wait );
++       }
++
++       up( &xmit_sem );
++
++       if ( 0 == retval )
++                retval = stSent;
++       return retval;
++}
++
++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait )
++{
++       unsigned int retval = 0;
++
++       PRINTK( KERN_DEBUG "%poll()\n", pszMe );
++
++       poll_wait( pFile, &wq_poll, pWait );
++
++       if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) )
++                retval |= POLLIN | POLLRDNORM;
++       if ( pxa_usb_xmitter_avail() )
++                retval |= POLLOUT | POLLWRNORM;
++       return retval;
++}
++
++static int usbc_ioctl( struct inode *pInode, struct file *pFile,
++                       unsigned int nCmd, unsigned long argument )
++{
++       int retval = 0;
++
++       switch( nCmd ) {
++
++       case USBC_IOC_FLUSH_RECEIVER:
++                pxa_usb_recv_reset();
++                rx_ring.in = rx_ring.out = 0;
++                break;
++
++       case USBC_IOC_FLUSH_TRANSMITTER:
++                pxa_usb_send_reset();
++                break;
++
++       case USBC_IOC_FLUSH_ALL:
++                pxa_usb_recv_reset();
++                rx_ring.in = rx_ring.out = 0;
++                pxa_usb_send_reset();
++                break;
++
++       default:
++                retval = -ENOIOCTLCMD;
++                break;
++
++       }
++       return retval;
++}
++
++
++static int usbc_close( struct inode *pInode, struct file * pFile )
++{
++      PRINTK( KERN_DEBUG "%sclose()\n", pszMe );
++      if ( --usb_ref_count == 0 ) {
++               down( &xmit_sem );
++               pxa_usb_stop();
++               free_txrx_buffers();
++               free_string_descriptors();
++               del_timer( &tx_timer );
++               pxa_usb_close();
++               up( &xmit_sem );
++      }
++    MOD_DEC_USE_COUNT;
++    return 0;
++}
++
++#ifdef CONFIG_SA1100_EXTENEX1
++#include "../../../drivers/char/ex_gpio.h"
++void extenex_configured_notify_proc( void )
++{
++       if ( exgpio_play_string( "440,1:698,1" ) == -EAGAIN )
++                printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe );
++}
++#endif
++//////////////////////////////////////////////////////////////////////////////
++// Initialization
++//////////////////////////////////////////////////////////////////////////////
++
++static struct file_operations usbc_fops = {
++              owner:      THIS_MODULE,
++              open:           usbc_open,
++              read:           usbc_read,
++              write:          usbc_write,
++              poll:           usbc_poll,
++              ioctl:          usbc_ioctl,
++              release:        usbc_close,
++};
++
++static struct miscdevice usbc_misc_device = {
++    USBC_MINOR, "usb_char", &usbc_fops
++};
++
++/*
++ * usbc_init()
++ */
++
++int __init usbc_init( void )
++{
++       int rc;
++
++       if ( (rc = misc_register( &usbc_misc_device )) != 0 ) {
++                printk( KERN_WARNING "%sCould not register device 10, "
++                                "%d. (%d)\n", pszMe, USBC_MINOR, rc );
++                return -EBUSY;
++       }
++
++       // initialize wait queues
++       init_waitqueue_head( &wq_read );
++       init_waitqueue_head( &wq_write );
++       init_waitqueue_head( &wq_poll );
++
++       // initialize tx timeout timer
++       init_timer( &tx_timer );
++       tx_timer.function = tx_timeout;
++
++        printk( KERN_INFO "USB Function Character Driver Interface"
++                                " - %s, (C) 2001, Extenex Corp.\n", VERSION
++                 );
++
++       return rc;
++}
++
++void __exit usbc_exit( void )
++{
++}
++
++EXPORT_NO_SYMBOLS;
++
++module_init(usbc_init);
++module_exit(usbc_exit);
++
++
++
++// end: usb-char.c
++
++
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb-char.h
+@@ -0,0 +1,34 @@
++/*
++ * Copyright (C) 2001 Extenex Corporation
++ *
++ * usb-char.h
++ *
++ * Character device emulation client for SA-1100 client usb core.
++ *
++ *
++ *
++ */
++#ifndef _USB_CHAR_H
++#define _USB_CHAR_H
++
++#define USBC_MAJOR 10      /* miscellaneous character device */
++#define USBC_MINOR 240     /* in the "reserved for local use" range */
++
++#define USBC_MAGIC 0x8E
++
++/* zap everything in receive ring buffer */
++#define USBC_IOC_FLUSH_RECEIVER    _IO( USBC_MAGIC, 0x01 )
++
++/* reset transmitter */
++#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 )
++
++/* do both of above */
++#define USBC_IOC_FLUSH_ALL         _IO( USBC_MAGIC, 0x03 )
++
++
++
++
++
++
++#endif /* _USB_CHAR_H */
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb-eth.c
+@@ -0,0 +1,479 @@
++/*
++ * Ethernet driver for the PXA USB client function
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * This code was loosely inspired by the original initial ethernet test driver
++ * Copyright (c) Compaq Computer Corporation, 1999
++ *
++ * 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.
++ *
++ * This is still work in progress...
++ *
++ * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com
++ * 09/03/2001 - Dropped 'framing' scheme, as it seems to cause a lot of problems with little benefit.
++ *            Now, since we do not know what size of packet we are receiving
++ *            last usb packet in sequence will always be less than max packet
++ *            receive endpoint can accept.
++ *            Now the only way to check correct start of frame is to compare
++ *            MAC address. Also now we are stalling on each receive error.
++ *
++ * 15/03/2001 - Using buffer to get data from UDC. DMA needs to have 8 byte
++ *            aligned buffer, but this breaks IP code (unaligned access).
++ *
++ * 01/04/2001 - stall endpoint operations appeared to be very unstable, so
++ *              they are disabled now.
++ *
++ * 03/06/2001 - Readded "zerocopy" receive path (tunable).
++ *
++ */
++
++// Define DMA_NO_COPY if you want data to arrive directly into the
++// receive network buffers, instead of arriving into bounce buffer
++// and then get copied to network buffer.
++// This does not work correctly right now.
++#undef DMA_NO_COPY
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/timer.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/random.h>
++
++#include "pxa_usb.h"
++
++
++#define ETHERNET_VENDOR_ID 0x49f
++#define ETHERNET_PRODUCT_ID 0x505A
++#define MAX_PACKET 32768
++#define MIN(a, b) (((a) < (b)) ? (a) : (b))
++
++// Should be global, so that insmod can change these
++int usb_rsize=64;
++int usb_wsize=64;
++
++static struct usbe_info_t {
++  struct net_device *dev;
++  u16 packet_id;
++  struct net_device_stats stats;
++} usbe_info;
++
++static char usb_eth_name[16] = "usbf";
++static struct net_device usb_eth_device;
++static struct sk_buff *cur_tx_skb, *next_tx_skb;
++static struct sk_buff *cur_rx_skb, *next_rx_skb;
++static volatile int terminating;
++#ifndef DMA_NO_COPY
++static char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary
++#endif
++
++static int usb_change_mtu (struct net_device *net, int new_mtu)
++{
++      if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET)
++              return -EINVAL;
++      // no second zero-length packet read wanted after mtu-sized packets
++      if (((new_mtu + sizeof (struct ethhdr)) % usb_rsize) == 0)
++              return -EDOM;
++
++      net->mtu = new_mtu;
++      return 0;
++}
++
++static struct sk_buff *
++usb_new_recv_skb(void)
++{
++      struct sk_buff *skb = alloc_skb( 2 + sizeof (struct ethhdr) + usb_eth_device.mtu,GFP_ATOMIC);
++
++      if (skb) {
++              skb_reserve(skb, 2);
++      }
++      return skb;
++}
++
++static u8 bcast_hwaddr[ETH_ALEN]={0xff,0xff,0xff,0xff,0xff,0xff};
++static void
++usb_recv_callback(int flag, int size)
++{
++      struct sk_buff *skb;
++
++      if (terminating)
++              return;
++
++      skb = cur_rx_skb;
++
++      /* flag validation */
++      if (flag == 0) {
++              if ( skb_tailroom (skb) < size ) { // hey! we are overloaded!!!
++                      usbe_info.stats.rx_over_errors++;
++                      goto error;
++              }
++#ifndef DMA_NO_COPY
++              memcpy(skb->tail,dmabuf,size);
++#endif
++              skb_put(skb, size);
++      } else {
++              if (flag == -EIO) {
++                      usbe_info.stats.rx_errors++;
++              }
++              goto error;
++      }
++      
++
++      /*
++       * If the real size of the packet is divisible by usb_rsize
++       * an extra byte will be added. Thus size == usb_rsize
++       * should only happen if more data is to come.
++       */
++      /* validate packet length */
++      if (size == usb_rsize ) {
++              /* packet not complete yet */
++              skb = NULL;
++      }
++
++      /*
++       * At this point skb is non null if we have a complete packet.
++       * If so take a fresh skb right away and restart USB receive without
++       * further delays, then process the packet.  Otherwise resume USB
++       * receive on the current skb and exit.
++       */
++
++      if (skb)
++              cur_rx_skb = next_rx_skb;
++#ifndef DMA_NO_COPY
++      pxa_usb_recv(dmabuf, usb_rsize,
++                      usb_recv_callback);
++#else
++      pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
++                      usb_recv_callback);
++#endif
++      if (!skb)
++              return;
++
++      next_rx_skb = usb_new_recv_skb();
++      if (!next_rx_skb) {
++              /*
++               * We can't aford loosing buffer space...
++               * So we drop the current packet and recycle its skb.
++               */
++              printk("%s: can't allocate new skb\n", __FUNCTION__);
++              usbe_info.stats.rx_dropped++;
++              skb_trim(skb, 0);
++              next_rx_skb = skb;
++              return;
++      }
++      if ( skb->len >= sizeof(struct ethhdr)) {
++              if (memcmp(skb->data,usb_eth_device.dev_addr,ETH_ALEN) && memcmp(skb->data,bcast_hwaddr,ETH_ALEN) ) {
++                      // This frame is not for us. nor it is broadcast
++                      usbe_info.stats.rx_frame_errors++;
++                      kfree_skb(skb);
++                      goto error;
++              }
++
++#if 0
++{
++        int i;
++
++        for (i = 0; i < skb->len; i++)
++      {
++                printk("%02X ", skb->data[i]);
++              if( (i%8)==7) printk("\n");
++      }
++        printk("...\n");
++}
++#endif
++
++      }
++
++      if (skb->len) {
++              int     status;
++// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
++
++              skb->dev = &usb_eth_device;
++              skb->protocol = eth_type_trans (skb, &usb_eth_device);
++              usbe_info.stats.rx_packets++;
++              usbe_info.stats.rx_bytes += skb->len;
++              skb->ip_summed = CHECKSUM_NONE;
++              status = netif_rx (skb);
++              if (status != NET_RX_SUCCESS)
++                      printk("netif_rx failed with code %d\n",status);
++      } else {
++error:
++//printk("ERROR... tailroom=%d size=%d len=%d flag=%d\n", skb_tailroom(skb), size, skb->len, flag);
++              /*
++               * Error due to HW addr mismatch, or IO error.
++               * Recycle the current skb and reset USB reception.
++               */
++              skb_trim(cur_rx_skb, 0);
++//            if ( flag == -EINTR || flag == -EAGAIN ) // only if we are coming out of stall
++#ifndef DMA_NO_COPY
++                      pxa_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
++#else
++                      pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback);
++#endif
++      }
++}
++
++
++static void
++usb_send_callback(int flag, int size)
++{
++      struct net_device *dev = usbe_info.dev;
++      struct net_device_stats *stats;
++      struct sk_buff *skb=cur_tx_skb;
++      int ret;
++
++      if (terminating)
++              return;
++
++      stats = &usbe_info.stats;
++      switch (flag) {
++          case 0:
++              stats->tx_packets++;
++              stats->tx_bytes += size;
++              break;
++          case -EIO:
++              stats->tx_errors++;
++              break;
++          default:
++              stats->tx_dropped++;
++              break;
++      }
++
++      cur_tx_skb = next_tx_skb;
++      next_tx_skb = NULL;
++      dev_kfree_skb_irq(skb);
++      if (!cur_tx_skb)
++              return;
++
++      dev->trans_start = jiffies;
++      ret = pxa_usb_send(cur_tx_skb->data, cur_tx_skb->len, usb_send_callback);
++      if (ret) {
++              /* If the USB core can't accept the packet, we drop it. */
++              dev_kfree_skb_irq(cur_tx_skb);
++              cur_tx_skb = NULL;
++              usbe_info.stats.tx_carrier_errors++;
++      }
++      netif_wake_queue(dev);
++}
++
++static int
++usb_eth_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      int ret;
++      long flags;
++
++      if (next_tx_skb) {
++              printk("%s: called with next_tx_skb != NULL\n", __FUNCTION__);
++              return 1;
++      }
++
++      if (skb_shared (skb)) {
++              struct sk_buff  *skb2 = skb_unshare(skb, GFP_ATOMIC);
++              if (!skb2) {
++                      usbe_info.stats.tx_dropped++;
++                      dev_kfree_skb(skb);
++                      return 1;
++              }
++              skb = skb2;
++      }
++
++      if ((skb->len % usb_wsize) == 0) {
++              skb->len++; // other side will ignore this one, anyway.
++      }
++
++      save_flags_cli(flags);
++      if (cur_tx_skb) {
++              next_tx_skb = skb;
++              netif_stop_queue(dev);
++      } else {
++              cur_tx_skb = skb;
++              dev->trans_start = jiffies;
++              ret = pxa_usb_send(skb->data, skb->len, usb_send_callback);
++              if (ret) {
++                      /* If the USB core can't accept the packet, we drop it. */
++                      dev_kfree_skb(skb);
++                      cur_tx_skb = NULL;
++                      usbe_info.stats.tx_carrier_errors++;
++              }
++      }
++      restore_flags(flags);
++      return 0;
++}
++
++static void
++usb_xmit_timeout(struct net_device *dev )
++{
++      pxa_usb_send_reset();
++      dev->trans_start = jiffies;
++      netif_wake_queue(dev);
++}
++
++
++static int
++usb_eth_open(struct net_device *dev)
++{
++      int rc;
++      rc = pxa_usb_open( "usb-eth" );
++      if ( rc == 0 ) {
++               string_desc_t * pstr;
++               desc_t * pd = pxa_usb_get_descriptor_ptr();
++
++               pd->b.ep1.wMaxPacketSize = make_word( usb_rsize );
++               pd->b.ep2.wMaxPacketSize = make_word( usb_wsize );
++               pd->dev.idVendor         = ETHERNET_VENDOR_ID;
++               pd->dev.idProduct     = ETHERNET_PRODUCT_ID;
++               pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" );
++               if ( pstr ) {
++                        pxa_usb_set_string_descriptor( 1, pstr );
++                        pd->dev.iProduct = 1;
++               }
++               rc = pxa_usb_start();
++      }
++
++      if( rc == 0)
++      {
++              terminating = 0;
++              cur_tx_skb = next_tx_skb = NULL;
++              cur_rx_skb = usb_new_recv_skb();
++              next_rx_skb = usb_new_recv_skb();
++              if (!cur_rx_skb || !next_rx_skb) {
++                      printk("%s: can't allocate new skb\n", __FUNCTION__);
++                      if (cur_rx_skb)
++                              kfree_skb(cur_rx_skb);
++                      if (next_rx_skb)
++                              kfree_skb(next_rx_skb);
++
++                      pxa_usb_stop();
++                      pxa_usb_close();
++                      return -ENOMEM;;
++              }
++
++              MOD_INC_USE_COUNT;
++#ifndef DMA_NO_COPY
++              pxa_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
++#else
++              pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
++                              usb_recv_callback);
++#endif
++      }
++
++      return rc;
++}
++
++static int
++usb_eth_release(struct net_device *dev)
++{
++      string_desc_t * pstr;
++
++      terminating = 1;
++      pxa_usb_send_reset();
++      pxa_usb_recv_reset();
++      if (cur_tx_skb)
++              kfree_skb(cur_tx_skb);
++      if (next_tx_skb)
++              kfree_skb(next_tx_skb);
++      if (cur_rx_skb)
++              kfree_skb(cur_rx_skb);
++      if (next_rx_skb)
++              kfree_skb(next_rx_skb);
++
++      pxa_usb_stop();
++      pxa_usb_close();
++      if ( (pstr = pxa_usb_get_string_descriptor(1)) != NULL )
++              kfree( pstr );
++
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static struct net_device_stats *
++usb_eth_stats(struct net_device *dev)
++{
++      struct usbe_info_t *priv =  (struct usbe_info_t*) dev->priv;
++      struct net_device_stats *stats=NULL;
++
++      if (priv)
++              stats = &priv->stats;
++      return stats;
++}
++
++static int
++usb_eth_probe(struct net_device *dev)
++{
++      u8 node_id [ETH_ALEN];
++
++      get_random_bytes (node_id, sizeof node_id);
++      node_id [0] &= 0xfe;    // clear multicast bit
++
++      /*
++       * Assign the hardware address of the board:
++       * generate it randomly, as there can be many such
++       * devices on the bus.
++       */
++      memcpy (dev->dev_addr, node_id, sizeof node_id);
++
++      dev->open = usb_eth_open;
++      dev->change_mtu = usb_change_mtu;
++      dev->stop = usb_eth_release;
++      dev->hard_start_xmit = usb_eth_xmit;
++      dev->get_stats = usb_eth_stats;
++      dev->watchdog_timeo = 1*HZ;
++      dev->tx_timeout = usb_xmit_timeout;
++      dev->priv = &usbe_info;
++
++      usbe_info.dev = dev;
++
++      /* clear the statistics */
++      memset(&usbe_info.stats, 0, sizeof(struct net_device_stats));
++
++      ether_setup(dev);
++      dev->flags &= ~IFF_MULTICAST;
++      dev->flags &= ~IFF_BROADCAST;
++      //dev->flags |= IFF_NOARP;
++
++      return 0;
++}
++
++#ifdef MODULE
++MODULE_PARM(usb_rsize, "1i");
++MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to pxa");
++MODULE_PARM(usb_wsize, "1i");
++MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from pxa to host");
++#endif
++
++static int __init
++usb_eth_init(void)
++{
++#ifndef DMA_NO_COPY
++      dmabuf = kmalloc( usb_rsize, GFP_KERNEL | GFP_DMA );
++      if (!dmabuf)
++              return -ENOMEM;
++#endif
++      strncpy(usb_eth_device.name, usb_eth_name, IFNAMSIZ);
++      usb_eth_device.init = usb_eth_probe;
++      if (register_netdev(&usb_eth_device) != 0)
++              return -EIO;
++
++      printk( KERN_INFO "USB Function Ethernet Driver Interface\n");
++
++      return 0;
++}
++
++static void __exit
++usb_eth_cleanup(void)
++{
++#ifndef DMA_NO_COPY
++      kfree(dmabuf);
++#endif
++      unregister_netdev(&usb_eth_device);
++}
++
++module_init(usb_eth_init);
++module_exit(usb_eth_cleanup);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_ctl.c
+@@ -0,0 +1,769 @@
++/*
++ *  Copyright (C) Compaq Computer Corporation, 1998, 1999
++ *  Copyright (C) Extenex Corporation, 2001
++ *  Copyright (C) Intrinsyc, Inc., 2002
++ *
++ *  PXA USB controller core driver.
++ *
++ *  This file provides interrupt routing and overall coordination
++ *  of the endpoints.
++ *
++ *  Please see:
++ *    linux/Documentation/arm/SA1100/SA1100_USB 
++ *  for more info.
++ *
++ *  02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100 usb_ctl.c
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/proc_fs.h>
++#include <linux/tqueue.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include "pxa_usb.h"
++#include "usb_ctl.h"
++
++//#define DEBUG 1
++
++#if DEBUG
++static unsigned int usb_debug = DEBUG;
++#else
++#define usb_debug 0     /* gcc will remove all the debug code for us */
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Prototypes
++//////////////////////////////////////////////////////////////////////////////
++
++int usbctl_next_state_on_event( int event );
++static void udc_int_hndlr(int, void *, struct pt_regs *);
++static void initialize_descriptors( void );
++static void soft_connect_hook( int enable );
++static void udc_disable(void);
++static void udc_enable(void);
++
++#if CONFIG_PROC_FS
++#define PROC_NODE_NAME "driver/pxausb"
++static int usbctl_read_proc(char *page, char **start, off_t off,
++                                                      int count, int *eof, void *data);
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Globals
++//////////////////////////////////////////////////////////////////////////////
++static const char pszMe[] = "usbctl: ";
++struct usb_info_t usbd_info;  /* global to ep0, usb_recv, usb_send */
++
++/* device descriptors */
++static desc_t desc;
++
++#define MAX_STRING_DESC 8
++static string_desc_t * string_desc_array[ MAX_STRING_DESC ];
++static string_desc_t sd_zero;  /* special sd_zero holds language codes */
++
++// called when configured
++static usb_notify_t configured_callback = NULL;
++
++enum {
++    kStateZombie              = 0,
++    kStateZombieSuspend               = 1,
++    kStateDefault             = 2,
++    kStateDefaultSuspend      = 3,
++    kStateAddr                        = 4,
++    kStateAddrSuspend         = 5,
++    kStateConfig              = 6,
++    kStateConfigSuspend               = 7
++};
++
++/*
++ * FIXME: The PXA UDC handles several host device requests without user 
++ * notification/intervention. The table could be collapsed quite a bit...
++ */
++static int device_state_machine[8][6] = {
++//              suspend               reset          resume         adddr       config        deconfig
++/* zombie */  { kStateZombieSuspend , kStateDefault, kStateZombie , kError    , kError      , kError },
++/* zom sus */ { kStateZombieSuspend , kStateDefault, kStateZombie , kError    , kError      , kError },
++/* default */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kStateAddr, kStateConfig, kError },
++/* def sus */ { kStateDefaultSuspend, kStateDefault, kStateDefault, kError    , kError      , kError },
++/* addr */    { kStateAddrSuspend   , kStateDefault, kStateAddr   , kError    , kStateConfig, kError },
++/* addr sus */{ kStateAddrSuspend   , kStateDefault, kStateAddr   , kError    , kError      , kError },
++/* config */  { kStateConfigSuspend , kStateDefault, kStateConfig , kError    , kError      , kStateDefault },
++/* cfg sus */ { kStateConfigSuspend , kStateDefault, kStateConfig , kError    , kError      , kError }
++};
++
++/* "device state" is the usb device framework state, as opposed to the
++   "state machine state" which is whatever the driver needs and is much
++   more fine grained
++*/
++static int sm_state_to_device_state[8] = { 
++//  zombie            zom suspend       
++USB_STATE_POWERED, USB_STATE_SUSPENDED, 
++//  default           default sus
++USB_STATE_DEFAULT, USB_STATE_SUSPENDED,
++//  addr              addr sus         
++USB_STATE_ADDRESS, USB_STATE_SUSPENDED, 
++//  config            config sus
++USB_STATE_CONFIGURED, USB_STATE_SUSPENDED
++};
++
++static char * state_names[8] =
++{ "zombie", "zombie suspended", 
++  "default", "default suspended",
++  "address", "address suspended", 
++  "configured", "config suspended"
++};
++
++static char * event_names[6] =
++{ "suspend", "reset", "resume",
++  "address assigned", "configure", "de-configure"
++};
++
++static char * device_state_names[] =
++{ "not attached", "attached", "powered", "default",
++  "address", "configured", "suspended" };
++
++static int sm_state = kStateZombie;
++
++//////////////////////////////////////////////////////////////////////////////
++// Async
++//////////////////////////////////////////////////////////////////////////////
++
++/* The UDCCR reg contains mask and interrupt status bits,
++ * so using '|=' isn't safe as it may ack an interrupt.
++ */
++
++void udc_set_mask_UDCCR( int mask )
++{
++      UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
++}
++
++void udc_clear_mask_UDCCR( int mask)
++{
++      UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
++}
++
++void udc_ack_int_UDCCR( int mask)
++{
++      /* udccr contains the bits we dont want to change */
++      __u32 udccr = UDCCR & UDCCR_MASK_BITS; 
++
++      UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
++}
++
++static void
++udc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      __u32 status = UDCCR;
++      __u32 ir0_status = USIR0;
++      __u32 ir1_status = USIR1;
++      __u32 uicr0 = UICR0;
++      __u32 uicr1 = UICR1;
++
++      //mask ints
++      udc_set_mask_UDCCR( UDCCR_REM | UDCCR_SRM);
++      UICR0 = 0xff;
++      UICR1 = 0xff;
++
++      if( usb_debug > 2)
++      {
++              printk("%s--- udc_int_hndlr\n"
++                 "UDCCR=0x%08x UDCCS0=0x%08x UDCCS1=0x%08x UDCCS2=0x%08x\n"
++                 "USIR0=0x%08x USIR1=0x%08x UICR0=0x%08x UICR1=0x%08x\n", 
++                  pszMe, status, UDCCS0, UDCCS1, UDCCS2, ir0_status, ir1_status, uicr0, uicr1);
++      }
++
++      /* SUSpend Interrupt Request */
++      if ( status & UDCCR_SUSIR )
++      {
++              udc_ack_int_UDCCR( UDCCR_SUSIR);
++              if( usb_debug) printk("%sSuspend...\n", pszMe);
++              usbctl_next_state_on_event( kEvSuspend );
++      }
++
++      /* RESume Interrupt Request */
++      if ( status & UDCCR_RESIR )
++      {
++              udc_ack_int_UDCCR( UDCCR_RESIR);
++              if( usb_debug) printk("%sResume...\n", pszMe);
++              usbctl_next_state_on_event( kEvResume );
++      }
++
++      /* ReSeT Interrupt Request - UDC has been reset */
++      if ( status & UDCCR_RSTIR )
++      {
++              /* clear the reset interrupt */
++              udc_ack_int_UDCCR( UDCCR_RSTIR);
++
++              /* check type of reset */
++              if( (UDCCR & UDCCR_UDA) == 0)
++              {
++                      /* reset assertion took place, nothing to do */
++                      if( usb_debug) printk("%sReset assertion...\n", pszMe);
++              }
++
++              /* ok, it's a reset negation, go on with reset */
++              else if ( usbctl_next_state_on_event( kEvReset ) != kError )
++              {
++                      /* starting reset sequence now... */
++                      if( usb_debug) printk("%sResetting\n", pszMe);
++
++                      ep0_reset();
++                      ep_bulk_in1_reset();
++                      ep_bulk_out1_reset();
++
++                      usbctl_next_state_on_event( kEvConfig );
++              }
++              else
++              {
++                      printk("%sUnexpected reset\n", pszMe);
++              }
++      }
++      else
++      {
++              /* ep0 int */
++              if (ir0_status & USIR0_IR0)
++                      ep0_int_hndlr();
++
++              /* transmit bulk */
++              if (ir0_status & USIR0_IR1)
++                      ep_bulk_in1_int_hndlr(ir0_status);
++
++              /* receive bulk */
++              if ( ir0_status & USIR0_IR2)
++                      ep_bulk_out1_int_hndlr(ir0_status);
++
++              while (UDCCS2 & UDCCS_BO_RNE)
++              {
++                      if( usb_debug) printk("More Bulk-out data...\n");
++                      ep_bulk_out1_int_hndlr(ir0_status);
++              }
++      }
++
++      UICR0 = uicr0;
++      UICR1 = uicr1;
++      udc_clear_mask_UDCCR( UDCCR_SRM | UDCCR_REM); /* enable suspend/resume, reset */
++
++      /* clear all endpoint ints */
++      USIR0 |= 0xff;
++      USIR1 |= 0xff;
++
++      if( usb_debug > 2)
++      {
++              printk("%sudc_int_hndlr\n"
++                     "UDCCR=0x%08x UDCCS0=0x%08x UDCCS1=0x%08x UDCCS2=0x%08x\n"
++                     "USIR0=0x%08x USIR1=0x%08x UICR0=0x%08x UICR1=0x%08x\n", 
++                      pszMe, UDCCR, UDCCS0, UDCCS1, UDCCS2, USIR0, USIR1, UICR0, UICR1);
++      }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Public Interface
++//////////////////////////////////////////////////////////////////////////////
++
++/* Open PXA usb core on behalf of a client, but don't start running */
++
++int
++pxa_usb_open( const char * client )
++{
++      if ( usbd_info.client_name != NULL )
++      {
++              printk( "%sUnable to register %s (%s already registered).\n", 
++                      pszMe, client, usbd_info.client_name );
++              return -EBUSY;
++      }
++
++      usbd_info.client_name = (char*) client;
++      memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t));
++      memset(string_desc_array, 0, sizeof(string_desc_array));
++
++      /* hack to start in zombie suspended state */
++      sm_state = kStateZombieSuspend;
++      usbd_info.state = USB_STATE_SUSPENDED;
++
++      /* create descriptors for enumeration */
++      initialize_descriptors();
++
++      printk( "%s%s registered.\n", pszMe, client );
++      return 0;
++}
++
++/* Start running. Must have called usb_open (above) first */
++int
++pxa_usb_start( void )
++{
++      if ( usbd_info.client_name == NULL ) {
++              printk( "%s%s - no client registered\n",
++                              pszMe, __FUNCTION__ );
++              return -EPERM;
++      }
++
++      /* start UDC internal machinery running */
++      udc_enable();
++      udelay( 100 );
++
++      /* flush DMA and fire through some -EAGAINs */
++      ep_bulk_out1_init( usbd_info.dmach_rx );
++      ep_bulk_in1_init( usbd_info.dmach_tx );
++
++      /* give endpoint notification we are starting */
++      ep_bulk_out1_state_change_notify( USB_STATE_SUSPENDED );
++      ep_bulk_in1_state_change_notify( USB_STATE_SUSPENDED );
++
++      /* enable any platform specific hardware */
++      soft_connect_hook( 1 );
++
++      /* enable suspend/resume, reset */
++      udc_clear_mask_UDCCR( UDCCR_SRM | UDCCR_REM); 
++      /* enable ep0, ep1, ep2 */
++      UICR0 &= ~(UICR0_IM0 | UICR0_IM1 | UICR0_IM2); 
++
++      if( usb_debug) printk( "%sStarted %s\n", pszMe, usbd_info.client_name );
++      return 0;
++}
++
++/* Stop USB core from running */
++int
++pxa_usb_stop( void )
++{
++      if ( usbd_info.client_name == NULL ) {
++              printk( "%s%s - no client registered\n",
++                              pszMe, __FUNCTION__ );
++              return -EPERM;
++      }
++      /* mask everything */
++      /* disable suspend/resume, reset */
++      udc_set_mask_UDCCR( UDCCR_SRM | UDCCR_REM); 
++      /* disable ep0, ep1, ep2 */
++      UICR0 |= (UICR0_IM0 | UICR0_IM1 | UICR0_IM2); 
++
++      ep_bulk_out1_reset();
++      ep_bulk_in1_reset();
++
++      udc_disable();
++      if( usb_debug) printk( "%sStopped %s\n", pszMe, usbd_info.client_name );
++      return 0;
++}
++
++/* Tell PXA core client is through using it */
++int
++pxa_usb_close( void )
++{
++       if ( usbd_info.client_name == NULL ) {
++                printk( "%s%s - no client registered\n",
++                                pszMe, __FUNCTION__ );
++                return -EPERM;
++       }
++       printk( "%s%s closed.\n", pszMe, (char*)usbd_info.client_name );
++       usbd_info.client_name = NULL;
++       return 0;
++}
++
++/* set a proc to be called when device is configured */
++usb_notify_t pxa_set_configured_callback( usb_notify_t func )
++{
++       usb_notify_t retval = configured_callback;
++       configured_callback = func;
++       return retval;
++}
++
++/*====================================================
++ * Descriptor Manipulation.
++ * Use these between open() and start() above to setup
++ * the descriptors for your device.
++ *
++ */
++
++/* get pointer to static default descriptor */
++desc_t *
++pxa_usb_get_descriptor_ptr( void ) { return &desc; }
++
++/* optional: set a string descriptor */
++int
++pxa_usb_set_string_descriptor( int i, string_desc_t * p )
++{
++       int retval;
++       if ( i < MAX_STRING_DESC ) {
++                string_desc_array[i] = p;
++                retval = 0;
++       } else {
++                retval = -EINVAL;
++       }
++       return retval;
++}
++
++/* optional: get a previously set string descriptor */
++string_desc_t *
++pxa_usb_get_string_descriptor( int i )
++{
++       return ( i < MAX_STRING_DESC )
++                  ? string_desc_array[i]
++                  : NULL;
++}
++
++
++/* optional: kmalloc and unicode up a string descriptor */
++string_desc_t *
++pxa_usb_kmalloc_string_descriptor( const char * p )
++{
++       string_desc_t * pResult = NULL;
++
++       if ( p ) {
++                int len = strlen( p );
++                int uni_len = len * sizeof( __u16 );
++                pResult = (string_desc_t*) kmalloc( uni_len + 2, GFP_KERNEL ); /* ugh! */
++                if ( pResult != NULL ) {
++                         int i;
++                         pResult->bLength = uni_len + 2;
++                         pResult->bDescriptorType = USB_DESC_STRING;
++                         for( i = 0; i < len ; i++ ) {
++                                      pResult->bString[i] = make_word( (__u16) p[i] );
++                         }
++                }
++       }
++       return pResult;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Exports to rest of driver
++//////////////////////////////////////////////////////////////////////////////
++
++/* called by the int handler here and the two endpoint files when interesting
++   .."events" happen */
++
++int
++usbctl_next_state_on_event( int event )
++{
++      int next_state = device_state_machine[ sm_state ][ event ];
++      if ( next_state != kError )
++      {
++              int next_device_state = sm_state_to_device_state[ next_state ];
++              if( usb_debug) printk( "%s%s --> [%s] --> %s. Device in %s state.\n",
++                              pszMe, state_names[ sm_state ], event_names[ event ],
++                              state_names[ next_state ], device_state_names[ next_device_state ] );
++
++              sm_state = next_state;
++              if ( usbd_info.state != next_device_state )
++              {
++                      if ( configured_callback != NULL
++                               &&
++                               next_device_state == USB_STATE_CONFIGURED
++                               &&
++                               usbd_info.state != USB_STATE_SUSPENDED
++                         ) {
++                        configured_callback();
++                      }
++                      usbd_info.state = next_device_state;
++
++                      ep_bulk_out1_state_change_notify( next_device_state );
++                      ep_bulk_in1_state_change_notify( next_device_state );
++              }
++      }
++#if 1
++      else
++              printk( "%s%s --> [%s] --> ??? is an error.\n",
++                              pszMe, state_names[ sm_state ], event_names[ event ] );
++#endif
++      return next_state;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Private Helpers
++//////////////////////////////////////////////////////////////////////////////
++
++/* setup default descriptors */
++
++static void
++initialize_descriptors(void)
++{
++      desc.dev.bLength               = sizeof( device_desc_t );
++      desc.dev.bDescriptorType       = USB_DESC_DEVICE;
++      desc.dev.bcdUSB                = 0x100; /* 1.0 */
++      desc.dev.bDeviceClass          = 0xFF;  /* vendor specific */
++      desc.dev.bDeviceSubClass       = 0;
++      desc.dev.bDeviceProtocol       = 0;
++      desc.dev.bMaxPacketSize0       = 16;    /* ep0 max fifo size */
++      desc.dev.idVendor              = 0;     /* vendor ID undefined */
++      desc.dev.idProduct             = 0; /* product */
++      desc.dev.bcdDevice             = 0; /* vendor assigned device release num */
++      desc.dev.iManufacturer         = 0;     /* index of manufacturer string */
++      desc.dev.iProduct              = 0; /* index of product description string */
++      desc.dev.iSerialNumber         = 0;     /* index of string holding product s/n */
++      desc.dev.bNumConfigurations    = 1;
++
++      desc.b.cfg.bLength             = sizeof( config_desc_t );
++      desc.b.cfg.bDescriptorType     = USB_DESC_CONFIG;
++      desc.b.cfg.wTotalLength        = make_word_c( sizeof(struct cdb) );
++      desc.b.cfg.bNumInterfaces      = 1;
++      desc.b.cfg.bConfigurationValue = 1;
++      desc.b.cfg.iConfiguration      = 0;
++      desc.b.cfg.bmAttributes        = USB_CONFIG_BUSPOWERED;
++      desc.b.cfg.MaxPower            = USB_POWER( 500 );
++
++      desc.b.intf.bLength            = sizeof( intf_desc_t );
++      desc.b.intf.bDescriptorType    = USB_DESC_INTERFACE;
++      desc.b.intf.bInterfaceNumber   = 0; /* unique intf index*/
++      desc.b.intf.bAlternateSetting  = 0;
++      desc.b.intf.bNumEndpoints      = 2;
++      desc.b.intf.bInterfaceClass    = 0xFF; /* vendor specific */
++      desc.b.intf.bInterfaceSubClass = 0;
++      desc.b.intf.bInterfaceProtocol = 0;
++      desc.b.intf.iInterface         = 0;
++
++/*
++ * FIXME...
++ * The host usbnet driver expects EP1=out EP2=in. On the PXA UDC EP1=in, EP2=out
++ */
++      desc.b.ep1.bLength             = sizeof( ep_desc_t );
++      desc.b.ep1.bDescriptorType     = USB_DESC_ENDPOINT;
++      desc.b.ep1.bEndpointAddress    = USB_EP_ADDRESS( 1, USB_IN );
++      desc.b.ep1.bmAttributes        = USB_EP_BULK;
++      desc.b.ep1.wMaxPacketSize      = make_word_c( 64 );
++      desc.b.ep1.bInterval           = 0;
++
++      desc.b.ep2.bLength             = sizeof( ep_desc_t );
++      desc.b.ep2.bDescriptorType     = USB_DESC_ENDPOINT;
++      desc.b.ep2.bEndpointAddress    = USB_EP_ADDRESS( 2, USB_OUT );
++      desc.b.ep2.bmAttributes        = USB_EP_BULK;
++      desc.b.ep2.wMaxPacketSize      = make_word_c( 64 );
++      desc.b.ep2.bInterval           = 0;
++
++// FIXME: Add support for all endpoint...
++
++      /* set language */
++      /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */
++      sd_zero.bDescriptorType = USB_DESC_STRING;
++      sd_zero.bLength         = sizeof( string_desc_t );
++      sd_zero.bString[0]      = make_word_c( 0x409 ); /* American English */
++      pxa_usb_set_string_descriptor( 0, &sd_zero );
++}
++
++/* soft_connect_hook()
++ * Some devices have platform-specific circuitry to make USB
++ * not seem to be plugged in, even when it is. This allows
++ * software to control when a device 'appears' on the USB bus
++ * (after Linux has booted and this driver has loaded, for
++ * example). If you have such a circuit, control it here.
++ */
++static void
++soft_connect_hook( int enable )
++{
++}
++
++/* disable the UDC at the source */
++static void
++udc_disable(void)
++{
++      soft_connect_hook( 0 );
++      /* clear UDC-enable */
++      udc_clear_mask_UDCCR( UDCCR_UDE); 
++
++        /* Disable clock for USB device */
++        CKEN &= ~CKEN11_USB;
++}
++
++
++/*  enable the udc at the source */
++static void
++udc_enable(void)
++{
++        /* Enable clock for USB device */
++        CKEN |= CKEN11_USB;
++
++      /* try to clear these bits before we enable the udc */
++      udc_ack_int_UDCCR( UDCCR_SUSIR);
++      udc_ack_int_UDCCR( UDCCR_RSTIR);
++      udc_ack_int_UDCCR( UDCCR_RESIR);
++
++      /* set UDC-enable */
++      udc_set_mask_UDCCR( UDCCR_UDE); 
++      if( (UDCCR & UDCCR_UDA) == 0)
++      {
++              /* There's a reset on the bus,
++               * clear the interrupt bit and keep going
++               */
++              udc_ack_int_UDCCR( UDCCR_RSTIR);
++      }
++      
++      /* "USB test mode" to work around errata 40-42 (stepping a0, a1) 
++       * which could result in missing packets and interrupts. 
++       * Supposedly this turns off double buffering for all endpoints.
++       */
++      if( usb_debug) printk( "USB RES1=%x RES2=%x RES3=%x\n", UDC_RES1, UDC_RES2, UDC_RES3);
++      UDC_RES1 = 0x00;
++      UDC_RES2 = 0x00;
++      if( usb_debug) printk( "USB RES1=%x RES2=%x RES3=%x\n", UDC_RES1, UDC_RES2, UDC_RES3);
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Proc Filesystem Support
++//////////////////////////////////////////////////////////////////////////////
++
++#if CONFIG_PROC_FS
++
++#define SAY( fmt, args... )  p += sprintf(p, fmt, ## args )
++#define SAYV(  num )         p += sprintf(p, num_fmt, "Value", num )
++#define SAYC( label, yn )    p += sprintf(p, yn_fmt, label, yn )
++#define SAYS( label, v )     p += sprintf(p, cnt_fmt, label, v )
++
++static int usbctl_read_proc(char *page, char **start, off_t off,
++                          int count, int *eof, void *data)
++{
++       const char * num_fmt   = "%25.25s: %8.8lX\n";
++       const char * cnt_fmt   = "%25.25s: %lu\n";
++       const char * yn_fmt    = "%25.25s: %s\n";
++       const char * yes       = "YES";
++       const char * no        = "NO";
++       unsigned long v;
++       char * p = page;
++       int len;
++
++       SAY( "PXA USB Controller Core\n" );
++       SAY( "Active Client: %s\n", usbd_info.client_name ? usbd_info.client_name : "none");
++       SAY( "USB state: %s (%s) %d\n",
++                device_state_names[ sm_state_to_device_state[ sm_state ] ],
++                state_names[ sm_state ],
++                sm_state );
++
++       SAYS( "ep0 bytes read", usbd_info.stats.ep0_bytes_read );
++       SAYS( "ep0 bytes written", usbd_info.stats.ep0_bytes_written );
++       SAYS( "ep0 FIFO read failures", usbd_info.stats.ep0_fifo_write_failures );
++       SAYS( "ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures );
++
++       SAY( "\n" );
++
++       v = UDCCR;
++       SAY( "\nUDC Control Register\n" );
++       SAYV( v );
++       SAYC( "UDC Enabled",                ( v & UDCCR_UDE ) ? yes : no );
++       SAYC( "UDC Active",                ( v & UDCCR_UDA ) ? yes : no );
++       SAYC( "Suspend/Resume interrupts masked", ( v & UDCCR_SRM ) ? yes : no );
++       SAYC( "Reset interrupts masked",   ( v & UDCCR_REM ) ? yes : no );
++       SAYC( "Reset pending",      ( v & UDCCR_RSTIR ) ? yes : no );
++       SAYC( "Suspend pending",    ( v & UDCCR_SUSIR ) ? yes : no );
++       SAYC( "Resume pending",     ( v & UDCCR_RESIR ) ? yes : no );
++
++       len = ( p - page ) - off;
++       if ( len < 0 )
++                len = 0;
++       *eof = ( len <=count ) ? 1 : 0;
++       *start = page + off;
++       return len;
++}
++
++#endif  /* CONFIG_PROC_FS */
++
++#if 0
++static void irq_handler(int channel, void *data, struct pt_regs *regs)
++{
++      if( channel == usbd_info.dmach_rx)
++      {
++          printk( "USB receive DMA\n");
++      }
++      else if( channel == usbd_info.dmach_tx)
++      {
++          printk( "USB transmit DMA\n");
++      }
++      else
++      {
++          printk( "USB unknown DMA channel\n");
++      }
++}
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Module Initialization and Shutdown
++//////////////////////////////////////////////////////////////////////////////
++/*
++ * usbctl_init()
++ * Module load time. Allocate dma and interrupt resources. Setup /proc fs
++ * entry. Leave UDC disabled.
++ */
++int __init usbctl_init( void )
++{
++      int retval = 0;
++
++      udc_disable();
++
++      memset( &usbd_info, 0, sizeof( usbd_info ) );
++
++#if CONFIG_PROC_FS
++      create_proc_read_entry ( PROC_NODE_NAME, 0, NULL, usbctl_read_proc, NULL);
++#endif
++
++#if 0
++      /* setup rx dma */
++      usbd_info.dmach_rx = pxa_request_dma("USB receive", DMA_PRIO_MEDIUM, irq_handler, 0 /*data; DMA_Ser0UDCRd*/);
++      if (usbd_info.dmach_rx < 0) {
++              printk("%sunable to register for rx dma rc=%d\n", pszMe, usbd_info.dmach_rx );
++              goto err_rx_dma;
++      }
++
++      /* setup tx dma */
++      usbd_info.dmach_tx = pxa_request_dma("USB receive", DMA_PRIO_MEDIUM, irq_handler, 0 /*data; DMA_Ser0UDCRd*/);
++      if (usbd_info.dmach_tx < 0) {
++              printk("%sunable to register for tx dma rc=%d\n",pszMe,usbd_info.dmach_tx);
++              goto err_tx_dma;
++      }
++#endif
++
++      /* now allocate the IRQ. */
++      retval = request_irq(IRQ_USB, udc_int_hndlr, SA_INTERRUPT, "PXA USB core", NULL);
++      if (retval) {
++              printk("%sCouldn't request USB irq rc=%d\n",pszMe, retval);
++              goto err_irq;
++      }
++
++      printk( "PXA USB Controller Core Initialized\n");
++      return 0;
++
++err_irq:
++#if 0
++      pxa_free_dma(usbd_info.dmach_tx);
++      usbd_info.dmach_tx = 0;
++err_tx_dma:
++      pxa_free_dma(usbd_info.dmach_rx);
++      usbd_info.dmach_rx = 0;
++err_rx_dma:
++#endif
++      return retval;
++}
++/*
++ * usbctl_exit()
++ * Release DMA and interrupt resources
++ */
++void __exit usbctl_exit( void )
++{
++      printk("Unloading PXA USB Controller\n");
++
++      udc_disable();
++
++#if CONFIG_PROC_FS
++      remove_proc_entry ( PROC_NODE_NAME, NULL);
++#endif
++
++      pxa_free_dma(usbd_info.dmach_rx);
++      pxa_free_dma(usbd_info.dmach_tx);
++      free_irq(IRQ_USB, NULL);
++}
++
++module_init( usbctl_init );
++module_exit( usbctl_exit );
++
++EXPORT_SYMBOL( pxa_usb_open );
++EXPORT_SYMBOL( pxa_usb_start );
++EXPORT_SYMBOL( pxa_usb_stop );
++EXPORT_SYMBOL( pxa_usb_close );
++EXPORT_SYMBOL( pxa_usb_get_descriptor_ptr );
++EXPORT_SYMBOL( pxa_usb_set_string_descriptor );
++EXPORT_SYMBOL( pxa_usb_get_string_descriptor );
++EXPORT_SYMBOL( pxa_usb_kmalloc_string_descriptor );
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_ctl.h
+@@ -0,0 +1,89 @@
++/*
++ *  Copyright (C) Compaq Computer Corporation, 1998, 1999
++ *  Copyright (C) Extenex Corporation 2001
++ *  Copyright (C) Intrinsyc, Inc., 2002
++ *
++ *  usb_ctl.h
++ *
++ *  PRIVATE interface used to share info among components of the PXA USB
++ *  core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core
++ *  should use pxa_usb.h.
++ *
++ *  02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100 usb_ctl.h
++ *
++ */
++
++#ifndef _USB_CTL_H
++#define _USB_CTL_H
++
++/* Interrupt mask bits and UDC enable bit */
++#define UDCCR_MASK_BITS         (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)
++
++/*
++ * These states correspond to those in the USB specification v1.0
++ * in chapter 8, Device Framework.
++ */
++enum { 
++      USB_STATE_NOTATTACHED   =0,
++      USB_STATE_ATTACHED      =1,
++      USB_STATE_POWERED       =2,
++      USB_STATE_DEFAULT       =3,
++      USB_STATE_ADDRESS       =4,
++      USB_STATE_CONFIGURED    =5,
++      USB_STATE_SUSPENDED     =6
++};
++
++struct usb_stats_t {
++       unsigned long ep0_fifo_write_failures;
++       unsigned long ep0_bytes_written;
++       unsigned long ep0_fifo_read_failures;
++       unsigned long ep0_bytes_read;
++};
++
++struct usb_info_t
++{
++       char * client_name;
++       dmach_t dmach_tx, dmach_rx;
++       int state;
++       unsigned char address;
++       struct usb_stats_t stats;
++};
++
++/* in usb_ctl.c */
++extern struct usb_info_t usbd_info;
++
++/*
++ * Function Prototypes
++ */
++enum { 
++      kError          =-1,
++      kEvSuspend      =0,
++      kEvReset        =1,
++      kEvResume       =2,
++      kEvAddress      =3,
++      kEvConfig       =4,
++      kEvDeConfig     =5 
++};
++int usbctl_next_state_on_event( int event );
++
++/* endpoint zero */
++void ep0_reset(void);
++void ep0_int_hndlr(void);
++
++/* receiver */
++void ep_bulk_out1_state_change_notify( int new_state );
++int  ep_bulk_out1_recv(void);
++int  ep_bulk_out1_init(int chn);
++void ep_bulk_out1_int_hndlr(int status);
++void ep_bulk_out1_reset(void);
++void ep_bulk_out1_stall(void);
++
++/* xmitter */
++void ep_bulk_in1_state_change_notify( int new_state );
++void ep_bulk_in1_reset(void);
++int  ep_bulk_in1_init(int chn);
++void ep_bulk_in1_int_hndlr(int status);
++void ep_bulk_in1_stall(void);
++
++#endif /* _USB_CTL_H */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_ep0.c
+@@ -0,0 +1,556 @@
++/*
++ *  Copyright (C) Extenex Corporation 2001
++ *  Copyright (C) Compaq Computer Corporation, 1998, 1999
++ *  Copyright (C) Intrinsyc, Inc., 2002
++ *
++ *  PXA USB controller driver - Endpoint zero management
++ *
++ *  Please see:
++ *    linux/Documentation/arm/SA1100/SA1100_USB 
++ *  for more info.
++ *
++ *  02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100 usb_ctl.c
++ * 
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/tqueue.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/irq.h>
++
++#include "pxa_usb.h"  /* public interface */
++#include "usb_ctl.h"  /* private stuff */
++#include "usb_ep0.h"
++
++
++// 1 == lots of trace noise,  0 = only "important' stuff
++#define VERBOSITY 0
++
++enum { true = 1, false = 0 };
++typedef int bool;
++#ifndef MIN
++#define MIN( a, b ) ((a)<(b)?(a):(b))
++#endif
++
++#if 1 && !defined( ASSERT )
++#  define ASSERT(expr) \
++if(!(expr)) { \
++      printk( "Assertion failed! %s,%s,%s,line=%d\n",\
++#expr,__FILE__,__FUNCTION__,__LINE__); \
++}
++#else
++#  define ASSERT(expr)
++#endif
++
++#if VERBOSITY
++#define PRINTKD(fmt, args...) printk( fmt , ## args)
++#else
++#define PRINTKD(fmt, args...)
++#endif
++
++static EP0_state ep0_state = EP0_IDLE;
++
++/***************************************************************************
++  Prototypes
++ ***************************************************************************/
++/* "setup handlers" -- the main functions dispatched to by the
++   .. isr. These represent the major "modes" of endpoint 0 operation */
++static void sh_setup_begin(void);                             /* setup begin (idle) */
++static void sh_write( void );                                 /* writing data */
++static int  read_fifo( usb_dev_request_t * p );
++static void write_fifo( void );
++static void get_descriptor( usb_dev_request_t * pReq );
++static void queue_and_start_write( void * p, int req, int act );
++
++/***************************************************************************
++  Inline Helpers
++ ***************************************************************************/
++
++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }
++
++/* print string descriptor */
++static inline void psdesc( string_desc_t * p )
++{
++      int i;
++      int nchars = ( p->bLength - 2 ) / sizeof( __u16 );
++      printk( "'" );
++      for( i = 0 ; i < nchars ; i++ ) {
++              printk( "%c", (char) p->bString[i] );
++      }
++      printk( "'\n" );
++}
++
++#if VERBOSITY
++/* "pcs" == "print control status" */
++static inline void pcs( void )
++{
++      __u32 foo = UDCCS0;
++      printk( "%08x: %s %s %s %s %s %s\n",
++                      foo,
++                      foo & UDCCS0_SA   ? "SA" : "",
++                      foo & UDCCS0_OPR  ? "OPR" : "",
++                      foo & UDCCS0_RNE  ? "RNE" : "",
++                      foo & UDCCS0_SST  ? "SST" : "",
++                      foo & UDCCS0_FST  ? "FST" : "",
++                      foo & UDCCS0_DRWF ? "DRWF" : ""
++            );
++}
++static inline void preq( usb_dev_request_t * pReq )
++{
++      static char * tnames[] = { "dev", "intf", "ep", "oth" };
++      static char * rnames[] = { "std", "class", "vendor", "???" };
++      char * psz;
++      switch( pReq->bRequest ) {
++              case GET_STATUS:        psz = "get stat"; break;
++              case CLEAR_FEATURE:     psz = "clr feat"; break;
++              case SET_FEATURE:       psz = "set feat"; break;
++              case SET_ADDRESS:       psz = "set addr"; break;
++              case GET_DESCRIPTOR:    psz = "get desc"; break;
++              case SET_DESCRIPTOR:    psz = "set desc"; break;
++              case GET_CONFIGURATION: psz = "get cfg"; break;
++              case SET_CONFIGURATION: psz = "set cfg"; break;
++              case GET_INTERFACE:     psz = "get intf"; break;
++              case SET_INTERFACE:     psz = "set intf"; break;
++              case SYNCH_FRAME:       psz = "synch frame"; break;
++              default:                psz = "unknown"; break;
++      }
++      printk( "- [%s: %s req to %s. dir=%s]\n", psz,
++                      rnames[ (pReq->bmRequestType >> 5) & 3 ],
++                      tnames[ pReq->bmRequestType & 3 ],
++                      ( pReq->bmRequestType & 0x80 ) ? "in" : "out" );
++}
++
++#else
++static inline void pcs( void ){}
++static inline void preq( usb_dev_request_t *x){}
++#endif
++
++/***************************************************************************
++  Globals
++ ***************************************************************************/
++static const char pszMe[] = "usbep0: ";
++
++/* pointer to current setup handler */
++static void (*current_handler)(void) = sh_setup_begin;
++
++/* global write struct to keep write
++   ..state around across interrupts */
++static struct {
++      unsigned char *p;
++      int bytes_left;
++} wr;
++
++/***************************************************************************
++  Public Interface
++ ***************************************************************************/
++
++/* reset received from HUB (or controller just went nuts and reset by itself!)
++   so udc core has been reset, track this state here  */
++void ep0_reset(void)
++{
++      PRINTKD( "%sep0_reset\n", pszMe);
++      /* reset state machine */
++      current_handler = sh_setup_begin;
++      wr.p = NULL;
++      wr.bytes_left = 0;
++      usbd_info.address=0;
++}
++
++/* handle interrupt for endpoint zero */
++void ep0_int_hndlr( void )
++{
++      PRINTKD( "%sep0_int_hndlr\n", pszMe);
++      pcs();
++      (*current_handler)();
++}
++
++/***************************************************************************
++  Setup Handlers
++ ***************************************************************************/
++/*
++ * sh_setup_begin()
++ * This setup handler is the "idle" state of endpoint zero. It looks for OPR
++ * (OUT packet ready) to see if a setup request has been been received from the
++ * host. 
++ *
++ */
++static void sh_setup_begin( void )
++{
++      usb_dev_request_t req;
++      int request_type;
++      int n;
++      __u32 cs_reg_in = UDCCS0;
++
++      PRINTKD( "%ssh_setup_begin\n", pszMe);
++
++      /* Be sure out packet ready, otherwise something is wrong */
++      if ( (cs_reg_in & UDCCS0_OPR) == 0 ) {
++              /* we can get here early...if so, we'll int again in a moment  */
++              PRINTKD( "%ssetup begin: no OUT packet available. Exiting\n", pszMe );
++              goto sh_sb_end;
++      }
++
++      if( ((cs_reg_in & UDCCS0_SA) == 0) && (ep0_state == EP0_IN_DATA_PHASE))
++      {
++              PRINTKD( "%ssetup begin: premature status\n", pszMe );
++
++              /* premature status, reset tx fifo and go back to idle state*/
++              UDCCS0 = UDCCS0_OPR | UDCCS0_FTF;
++
++              ep0_state = EP0_IDLE;
++              return;
++      }
++
++      if( (UDCCS0 & UDCCS0_RNE) == 0)
++      {
++              /* zero-length OUT? */
++              printk( "%ssetup begin: zero-length OUT?\n", pszMe );
++              goto sh_sb_end;
++      }
++
++      /* read the setup request */
++      n = read_fifo( &req );
++      if ( n != sizeof( req ) ) {
++              printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
++                              " Stalling out...\n",
++                              pszMe, sizeof( req ), n );
++              /* force stall, serviced out */
++              UDCCS0 = UDCCS0_FST;
++              goto sh_sb_end;
++      }
++
++      /* Is it a standard request? (not vendor or class request) */
++      request_type = type_code_from_request( req.bmRequestType );
++      if ( request_type != 0 ) {
++              printk( "%ssetup begin: unsupported bmRequestType: %d ignored\n",
++                              pszMe, request_type );
++              goto sh_sb_end;
++      }
++
++#if VERBOSITY
++      {
++              unsigned char * pdb = (unsigned char *) &req;
++              PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
++                              pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
++                     );
++              preq( &req );
++      }
++#endif
++
++      /* Handle it */
++      switch( req.bRequest ) {
++
++              case SET_ADDRESS:
++                      PRINTKD( "%sSET_ADDRESS handled by UDC\n", pszMe);
++                      break;
++#if 0 /* NOT_NEEDED */
++
++              case SET_FEATURE:
++                      PRINTKD( "%sSET_FEATURE handled by UDC\n", pszMe);
++                      break;
++
++              case CLEAR_FEATURE:
++                      PRINTKD( "%sCLEAR_FEATURE handled by UDC\n", pszMe);
++                      break;
++
++              case GET_CONFIGURATION:
++                      PRINTKD( "%sGET_CONFIGURATION handled by UDC\n", pszMe );
++                      break;
++
++              case GET_STATUS:
++                      PRINTKD( "%s%sGET_STATUS handled by UDC\n", pszMe );
++                      break;
++
++              case GET_INTERFACE:
++                      PRINTKD( "%sGET_INTERFACE handled by UDC\n", pszMe);
++                      break;
++
++              case SYNCH_FRAME:
++                      PRINTKD( "%sSYNCH_FRAME handled by UDC\n", pszMe );
++                      break;
++#endif
++
++              case GET_DESCRIPTOR:
++                      PRINTKD( "%sGET_DESCRIPTOR\n", pszMe );
++                      get_descriptor( &req );
++                      break;
++
++              case SET_INTERFACE:
++                      PRINTKD( "%sSET_INTERFACE TODO...\n", pszMe);
++                      break;
++
++              case SET_DESCRIPTOR:
++                      PRINTKD( "%sSET_DESCRIPTOR TODO...\n", pszMe );
++                      break;
++
++              case SET_CONFIGURATION:
++                      PRINTKD( "%sSET_CONFIGURATION %d\n", pszMe, req.wValue);
++
++/*
++ * FIXME: Something is not quite right here... I only ever get a 
++ * de-configure from the host. Ignoring it for now, since usb
++ * ethernet won't do anything unless usb is 'configured'.
++ *
++ */
++#if 0
++                      switch( req.wValue)
++                      {
++                              case 0:
++                                      /* configured */
++                                      usbctl_next_state_on_event( kEvConfig );
++                                      break;
++                              case 1:
++                                      /* de-configured */
++                                      usbctl_next_state_on_event( kEvDeConfig );
++                                      break;
++                              default:
++                                      PRINTKD( "%sSET_CONFIGURATION: unknown configuration value (%d)\n", pszMe, req.wValue);
++                                      break;
++                      }
++#endif
++                      break;
++              default :
++                      printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
++                      break;
++      } /* switch( bRequest ) */
++
++sh_sb_end:
++      return;
++}
++
++/*
++ * sh_write()
++ * 
++ * Due to UDC bugs we push everything into the fifo in one go.
++ * Using interrupts just didn't work right...
++ * This should be ok, since control request are small.
++ */
++static void sh_write()
++{
++      PRINTKD( "sh_write\n" );
++      do
++      {
++          write_fifo();
++      } while( ep0_state != EP0_END_XFER);
++}
++
++/***************************************************************************
++  Other Private Subroutines
++ ***************************************************************************/
++/*
++ * queue_and_start_write()
++ * data == data to send
++ * req == bytes host requested
++ * act == bytes we actually have
++ *
++ * Sets up the global "wr"-ite structure and load the outbound FIFO 
++ * with data.
++ *
++ */
++static void queue_and_start_write( void * data, int req, int act )
++{
++      PRINTKD( "write start: bytes requested=%d actual=%d\n", req, act);
++
++      wr.p = (unsigned char*) data;
++      wr.bytes_left = MIN( act, req );
++
++      ep0_state = EP0_IN_DATA_PHASE;
++      sh_write();
++
++      return;
++}
++/*
++ * write_fifo()
++ * Stick bytes in the endpoint zero FIFO.
++ *
++ */
++static void write_fifo( void )
++{
++      int bytes_this_time = MIN( wr.bytes_left, EP0_FIFO_SIZE );
++      int bytes_written = 0;
++
++      while( bytes_this_time-- ) {
++//            PRINTKD( "%2.2X ", *wr.p );
++              UDDR0 = *wr.p++;
++              bytes_written++;
++      }
++      wr.bytes_left -= bytes_written;
++
++      usbd_info.stats.ep0_bytes_written += bytes_written;
++
++      if( (wr.bytes_left==0))
++      {
++              wr.p = NULL;                            /* be anal */
++
++              if(bytes_written < EP0_FIFO_SIZE)
++              {
++                      int count;
++                      int udccs0;
++
++                      /* We always end the transfer with a short or zero length packet */
++                      ep0_state = EP0_END_XFER;
++                      current_handler = sh_setup_begin;
++
++                      /* Let the packet go... */
++                      UDCCS0 = UDCCS0_IPR;
++
++                      /* Wait until we get to status-stage, then ack.
++                       *
++                       * When the UDC sets the UDCCS0[OPR] bit, an interrupt
++                       * is supposed to be generated (see 12.5.1 step 14ff, PXA Dev Manual).   
++                       * That approach didn't work out. Usually a new SETUP command was
++                       * already in the fifo. I tried many approaches but was always losing 
++                       * at least some OPR interrupts. Thus the polling below...
++                       */
++                      count = 1000;
++                      udccs0 = UDCCS0;
++                      do
++                      {
++                              if( (UDCCS0 & UDCCS0_OPR)) 
++                              {
++                                      /* clear OPR, generate ack */
++                                      UDCCS0 = UDCCS0_OPR;
++                                      break;
++                              }
++                              count--;        
++                              udelay(1);
++                      } while( count);
++
++                      PRINTKD( "write fifo: count=%d UDCCS0=%x UDCCS0=%x\n", count, udccs0, UDCCS0);
++              }
++      }
++      /* something goes poopy if I dont wait here ... */
++      udelay(500);
++
++      PRINTKD( "write fifo: bytes sent=%d, bytes left=%d\n", bytes_written, wr.bytes_left);
++}
++
++/*
++ * read_fifo()
++ * Read bytes out of FIFO and put in request.
++ * Called to do the initial read of setup requests
++ * from the host. Return number of bytes read.
++ *
++ */
++static int read_fifo( usb_dev_request_t * request )
++{
++      int bytes_read = 0;
++      unsigned char * pOut = (unsigned char*) request;
++
++      int udccs0 = UDCCS0;
++
++      if( (udccs0 & SETUP_READY) == SETUP_READY)
++      {
++              /* ok it's a setup command */
++              while( UDCCS0 & UDCCS0_RNE)
++              {
++                      if( bytes_read >= sizeof( usb_dev_request_t))
++                      {
++                              /* We've already read enought o fill usb_dev_request_t.
++                               * Our tummy is full. Go barf... 
++                               */
++                              printk( "%sread_fifo(): read failure\n", pszMe );
++                              usbd_info.stats.ep0_fifo_read_failures++;
++                              break;
++                      }
++
++                      *pOut++ = UDDR0;
++                      bytes_read++;
++              }
++      }
++      PRINTKD( "read_fifo %d bytes\n", bytes_read );
++
++      /* clear SA & OPR */
++      UDCCS0 = SETUP_READY;
++
++      usbd_info.stats.ep0_bytes_read += bytes_read;
++      return bytes_read;
++}
++
++/*
++ * get_descriptor()
++ * Called from sh_setup_begin to handle data return
++ * for a GET_DESCRIPTOR setup request.
++ */
++static void get_descriptor( usb_dev_request_t * pReq )
++{
++      string_desc_t * pString;
++      ep_desc_t * pEndpoint = 0;
++
++      desc_t * pDesc = pxa_usb_get_descriptor_ptr();
++      int type = pReq->wValue >> 8;
++      int idx  = pReq->wValue & 0xFF;
++
++//    PRINTKD( "%sget_descriptor for %d\n", pszMe, type );
++      switch( type ) {
++              case USB_DESC_DEVICE:
++                      queue_and_start_write( &pDesc->dev,
++                                      pReq->wLength,
++                                      pDesc->dev.bLength );
++                      break;
++
++                      // return config descriptor buffer, cfg, intf, 2 ep
++              case USB_DESC_CONFIG:
++                      queue_and_start_write( &pDesc->b,
++                                      pReq->wLength,
++                                      sizeof( struct cdb ) );
++                      break;
++
++                      // not quite right, since doesn't do language code checking
++              case USB_DESC_STRING:
++                      pString = pxa_usb_get_string_descriptor( idx );
++                      if ( pString ) {
++                              if ( idx != 0 ) {  // if not language index
++                                      printk( "%sReturn string %d: ", pszMe, idx );
++                                      psdesc( pString );
++                              }
++                              queue_and_start_write( pString,
++                                              pReq->wLength,
++                                              pString->bLength );
++                      }
++                      else {
++                              printk("%sunkown string index %d Stall.\n", pszMe, idx );
++                      }
++                      break;
++
++              case USB_DESC_INTERFACE:
++                      if ( idx == pDesc->b.intf.bInterfaceNumber ) {
++                              queue_and_start_write( &pDesc->b.intf,
++                                              pReq->wLength,
++                                              pDesc->b.intf.bLength );
++                      }
++                      break;
++
++              case USB_DESC_ENDPOINT: /* correct? 21Feb01ww */
++                      if ( idx == 1 )
++                              pEndpoint = &pDesc->b.ep1; //[BULK_IN1];
++                      else if ( idx == 2 )
++                              pEndpoint = &pDesc->b.ep2; //[BULK_OUT1];
++                      else
++                              pEndpoint = NULL;
++                      if ( pEndpoint ) {
++                              queue_and_start_write( pEndpoint,
++                                              pReq->wLength,
++                                              pEndpoint->bLength );
++                      } else {
++                              printk("%sunkown endpoint index %d Stall.\n", pszMe, idx );
++                      }
++                      break;
++
++
++              default :
++                      printk("%sunknown descriptor type %d. Stall.\n", pszMe, type );
++                      break;
++
++      }
++}
++
++/* end usb_ep0.c - who needs this comment? */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_ep0.h
+@@ -0,0 +1,66 @@
++/*
++ *  Copyright (C) Intrinsyc, Inc., 2002
++ *
++ *  usb_ep0.h - PXA USB controller driver.
++ *              Endpoint zero management
++ *
++ *  Please see:
++ *    linux/Documentation/arm/SA1100/SA1100_USB
++ *  for details.
++ *
++ *  02-May-2002
++ *   Frank Becker (Intrinsyc) - 
++ * 
++ */
++
++#ifndef __USB_EP0_H
++#define __USB_EP0_H
++
++#define EP0_FIFO_SIZE 16
++#define SETUP_READY (UDCCS0_SA | UDCCS0_OPR)
++
++/*================================================
++ * USB Protocol Stuff
++ */
++
++/* Request Codes   */
++enum { 
++      GET_STATUS              =0,
++      CLEAR_FEATURE           =1,
++      /* reserved             =2 */
++      SET_FEATURE             =3,
++      /* reserved             =4 */
++      SET_ADDRESS             =5,        
++      GET_DESCRIPTOR          =6,
++      SET_DESCRIPTOR          =7,
++      GET_CONFIGURATION       =8,
++      SET_CONFIGURATION       =9,
++      GET_INTERFACE           =10,
++      SET_INTERFACE           =11,
++      SYNCH_FRAME             =12
++};
++
++typedef enum {
++      EP0_IDLE,
++      EP0_IN_DATA_PHASE,
++      EP0_END_XFER,
++      EP0_OUT_DATA_PHASE
++} EP0_state;
++
++/* USB Device Requests */
++typedef struct
++{
++      __u8 bmRequestType;
++      __u8 bRequest;
++      __u16 wValue;
++      __u16 wIndex;
++      __u16 wLength;
++} usb_dev_request_t  __attribute__ ((packed));
++
++/* Data extraction from usb_request_t fields */
++enum { 
++      kTargetDevice   =0,
++      kTargetInterface=1,
++      kTargetEndpoint =2 
++};
++#endif
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_recv.c
+@@ -0,0 +1,173 @@
++/*
++ * Generic receive layer for the PXA USB client function
++ *
++ * This code was loosely inspired by the original version which was
++ * Copyright (c) Compaq Computer Corporation, 1998-1999
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * 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.
++ *
++ * 02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100 usb_recv.c
++ * 
++ * TODO: Add support for DMA.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++
++#include "pxa_usb.h"
++#include "usb_ctl.h"
++
++#if DEBUG
++static unsigned int usb_debug = DEBUG;
++#else
++#define usb_debug 0     /* gcc will remove all the debug code for us */
++#endif
++
++static char *ep_bulk_out1_buf;
++static int   ep_bulk_out1_len;
++static int   ep_bulk_out1_remain;
++static usb_callback_t ep_bulk_out1_callback;
++static int rx_pktsize;
++
++static void
++ep_bulk_out1_start(void)
++{
++      /* disable DMA */
++      UDCCS2 &= ~UDCCS_BO_DME;
++
++      /* enable interrupts for endpoint 2 (bulk out) */
++        UICR0 &= ~UICR0_IM2;
++}
++
++static void
++ep_bulk_out1_done(int flag)
++{
++      int size = ep_bulk_out1_len - ep_bulk_out1_remain;
++
++      if (!ep_bulk_out1_len)
++              return;
++
++      ep_bulk_out1_len = 0;
++      if (ep_bulk_out1_callback) {
++              ep_bulk_out1_callback(flag, size);
++      }
++}
++
++void
++ep_bulk_out1_state_change_notify( int new_state )
++{
++}
++
++void
++ep_bulk_out1_stall( void )
++{
++      /* SET_FEATURE force stall at UDC */
++      UDCCS2 |= UDCCS_BO_FST;
++}
++
++int
++ep_bulk_out1_init(int chn)
++{
++      desc_t * pd = pxa_usb_get_descriptor_ptr();
++      rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
++      ep_bulk_out1_done(-EAGAIN);
++      return 0;
++}
++
++void
++ep_bulk_out1_reset(void)
++{
++      desc_t * pd = pxa_usb_get_descriptor_ptr();
++      rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
++      UDCCS2 &= ~UDCCS_BO_FST;
++      ep_bulk_out1_done(-EINTR);
++}
++
++void
++ep_bulk_out1_int_hndlr(int udcsr)
++{
++      int status = UDCCS2;
++      if( usb_debug) printk("ep_bulk_out1_int_hndlr: UDCCS2=%x\n", status);
++
++      if( (status & (UDCCS_BO_RNE | UDCCS_BO_RSP)) == UDCCS_BO_RSP)
++      {
++              /* zero-length packet */
++      }
++
++      if( status & UDCCS_BO_RNE)
++      {
++              int len;
++              int i;
++              char *buf = ep_bulk_out1_buf + ep_bulk_out1_len - ep_bulk_out1_remain;
++
++              /* bytes in FIFO */
++              len = (UBCR2 & 0xff) +1;
++              
++              if( usb_debug) printk("usb_recv: "
++                      "len=%d out1_len=%d out1_remain=%d\n",
++                      len,ep_bulk_out1_len,ep_bulk_out1_remain);
++
++              if( len > ep_bulk_out1_remain)
++              {
++                      /* FIXME: if this happens, we need a temporary overflow buffer */
++                      printk("usb_recv: Buffer overwrite warning...\n");
++                      len = ep_bulk_out1_remain;
++              }
++
++              /* read data out of fifo */
++              for( i=0; i<len; i++)
++              {
++                      *buf++ = UDDR2 & 0xff;
++              }
++
++              ep_bulk_out1_remain -= len;
++              ep_bulk_out1_done((len) ? 0 : -EPIPE);
++      }
++
++      /* ack RPC - FIXME: '|=' we may ack SST here, too */
++      UDCCS2 |= UDCCS_BO_RPC;
++      return;
++}
++
++int
++pxa_usb_recv(char *buf, int len, usb_callback_t callback)
++{
++      int flags;
++
++      if (ep_bulk_out1_len)
++              return -EBUSY;
++
++      local_irq_save(flags);
++      ep_bulk_out1_buf = buf;
++      ep_bulk_out1_len = len;
++      ep_bulk_out1_callback = callback;
++      ep_bulk_out1_remain = len;
++      ep_bulk_out1_start();
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++void
++pxa_usb_recv_reset(void)
++{
++      ep_bulk_out1_reset();
++}
++
++void
++pxa_usb_recv_stall(void)
++{
++      ep_bulk_out1_stall();
++}
++
++EXPORT_SYMBOL(pxa_usb_recv_stall);
++EXPORT_SYMBOL(pxa_usb_recv);
++EXPORT_SYMBOL(pxa_usb_recv_reset);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-pxa/usb_send.c
+@@ -0,0 +1,190 @@
++/*
++ * Generic xmit layer for the PXA USB client function
++ *
++ * This code was loosely inspired by the original version which was
++ * Copyright (c) Compaq Computer Corporation, 1998-1999
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * 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.
++ *
++ * 02-May-2002
++ *   Frank Becker (Intrinsyc) - derived from sa1100 usb_send.c
++ *
++ * TODO: Add support for DMA.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++#include <asm/byteorder.h>
++
++#include "pxa_usb.h"
++#include "usb_ctl.h"
++
++#if DEBUG
++static unsigned int usb_debug = DEBUG;
++#else
++#define usb_debug 0     /* gcc will remove all the debug code for us */
++#endif
++
++static char *ep_bulk_in1_buf;
++static int   ep_bulk_in1_len;
++static int   ep_bulk_in1_remain;
++static usb_callback_t ep_bulk_in1_callback;
++static int tx_pktsize;
++
++/* device state is changing, async */
++void
++ep_bulk_in1_state_change_notify( int new_state )
++{
++}
++
++/* set feature stall executing, async */
++void
++ep_bulk_in1_stall( void )
++{
++      UDCCS1 |= UDCCS_BI_FST;
++}
++
++static void
++ep_bulk_in1_send_packet(void)
++{
++      int i;
++      char *buf = ep_bulk_in1_buf + ep_bulk_in1_len - ep_bulk_in1_remain;
++      int out_size = tx_pktsize;
++
++      if( usb_debug) printk( "ep_bulk_in1_send_packet: UICR0=%x UDCCS1=%x\n", UICR0, UDCCS1);
++
++      if( out_size > ep_bulk_in1_remain) 
++      {
++              out_size = ep_bulk_in1_remain;
++      }
++
++      for( i=0; i<out_size; i++)
++      {
++              UDDR1 = *buf++; 
++      }
++
++      UDCCS1 = UDCCS_BI_TPC;
++      if( out_size < tx_pktsize)
++      {
++              /* short packet */
++              UDCCS1 = UDCCS_BI_TSP;
++      }
++      ep_bulk_in1_remain -= out_size;
++
++      if( usb_debug) printk( "ep_bulk_in1_send_packet: "
++              "UICR0=%x UDCCS1=%x send bytes=%d left=%d\n", 
++              UICR0, UDCCS1, out_size, ep_bulk_in1_remain);
++}
++
++static void
++ep_bulk_in1_start(void)
++{
++      if (!ep_bulk_in1_len)
++              return;
++
++        UICR0 &= ~UICR0_IM1;
++
++      ep_bulk_in1_send_packet();
++}
++
++static void
++ep_bulk_in1_done(int flag)
++{
++      int size = ep_bulk_in1_len - ep_bulk_in1_remain;
++      if (ep_bulk_in1_len) {
++              ep_bulk_in1_len = 0;
++              if (ep_bulk_in1_callback)
++                      ep_bulk_in1_callback(flag, size);
++      }
++}
++
++int
++ep_bulk_in1_init(int chn)
++{
++      desc_t * pd = pxa_usb_get_descriptor_ptr();
++      tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize );
++      ep_bulk_in1_done(-EAGAIN);
++      return 0;
++}
++
++void
++ep_bulk_in1_reset(void)
++{
++      desc_t * pd = pxa_usb_get_descriptor_ptr();
++      tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize );
++      UDCCS1 &= ~UDCCS_BI_FST;
++      ep_bulk_in1_done(-EINTR);
++}
++
++void
++ep_bulk_in1_int_hndlr(int usir0)
++{
++      int status = UDCCS1;
++
++      if (ep_bulk_in1_remain != 0) {
++              /* more data to go */
++              ep_bulk_in1_start();
++      } else {
++              if( status & UDCCS_BI_TPC)
++              {
++                      UDCCS1 = UDCCS_BI_TPC;
++              }
++              ep_bulk_in1_done(0);
++      }
++}
++
++int
++pxa_usb_send(char *buf, int len, usb_callback_t callback)
++{
++      int flags;
++
++      if( usb_debug) printk( "pxa_usb_send: "
++              "data len=%d state=%d blen=%d\n", 
++              len, usbd_info.state, ep_bulk_in1_len);
++
++      if (usbd_info.state != USB_STATE_CONFIGURED)
++              return -ENODEV;
++
++      if (ep_bulk_in1_len)
++              return -EBUSY;
++
++      local_irq_save(flags);
++      ep_bulk_in1_buf = buf;
++      ep_bulk_in1_len = len;
++      ep_bulk_in1_callback = callback;
++      ep_bulk_in1_remain = len;
++      ep_bulk_in1_start();
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++
++void
++pxa_usb_send_reset(void)
++{
++      ep_bulk_in1_reset();
++}
++
++int 
++pxa_usb_xmitter_avail( void )
++{
++      if (usbd_info.state != USB_STATE_CONFIGURED)
++              return -ENODEV;
++      if (ep_bulk_in1_len)
++              return -EBUSY;
++      return 0;
++}
++
++
++EXPORT_SYMBOL(pxa_usb_xmitter_avail);
++EXPORT_SYMBOL(pxa_usb_send);
++EXPORT_SYMBOL(pxa_usb_send_reset);
+--- linux-2.4.27/arch/arm/mach-sa1100/sa1111-ohci.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mach-sa1100/sa1111-ohci.c
+@@ -54,7 +54,7 @@
+        *  address as its return value, and the DMA address via
+        *  the dma_addr_t pointer.
+        */
+-      vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf);
++      vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf, 0);
+       SADTSA = (unsigned long)dma_buf;
+       SADTCA = 4;
+--- linux-2.4.27/arch/arm/mach-sa1100/sa1111.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mach-sa1100/sa1111.c
+@@ -243,9 +243,15 @@
+        * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
+        * (SA-1110 Developer's Manual, section 9.1.2.1)
+        */
++#if CONFIG_ARCH_SA1100
+       GAFR |= GPIO_32_768kHz;
+       GPDR |= GPIO_32_768kHz;
+       TUCR = TUCR_3_6864MHz;
++#elif CONFIG_ARCH_PXA
++      set_GPIO_mode(GPIO11_3_6MHz_MD);
++#else
++#error missing clock setup
++#endif
+       /*
+        * Turn VCO on, and disable PLL Bypass.
+@@ -300,6 +306,8 @@
+       SBI_SMCR = smcr;
+ }
++#ifdef CONFIG_ARCH_SA1100
++
+ /*
+  * Disable the memory bus request/grant signals on the SA1110 to
+  * ensure that we don't receive spurious memory requests.  We set
+@@ -341,5 +349,7 @@
+       local_irq_restore(flags);
+ }
++#endif
++
+ EXPORT_SYMBOL(sa1111_wake);
+ EXPORT_SYMBOL(sa1111_doze);
+--- linux-2.4.27/arch/arm/mm/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mm/Makefile
+@@ -44,6 +44,7 @@
+ p-$(CONFIG_CPU_ARM1026)       += proc-arm1026.o
+ p-$(CONFIG_CPU_SA110) += proc-sa110.o
+ p-$(CONFIG_CPU_SA1100)        += proc-sa110.o
++p-$(CONFIG_CPU_XSCALE)        += proc-xscale.o
+ # Integrator follows "new style"
+ # Soon, others will do too, and we can get rid of this
+--- linux-2.4.27/arch/arm/mm/consistent.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mm/consistent.c
+@@ -37,7 +37,8 @@
+  *
+  * Note that this does *not* zero the allocated area!
+  */
+-void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
++void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle,
++                     unsigned long cache_flags)
+ {
+       struct page *page, *end, *free;
+       unsigned long order;
+@@ -55,7 +56,7 @@
+               goto no_page;
+       *dma_handle = page_to_bus(page);
+-      ret = __ioremap(page_to_pfn(page) << PAGE_SHIFT, size, 0);
++      ret = __ioremap(page_to_pfn(page) << PAGE_SHIFT, size, cache_flags);
+       if (!ret)
+               goto no_remap;
+@@ -106,7 +107,7 @@
+ #endif
+               gfp |= GFP_DMA;
+-      return consistent_alloc(gfp, size, handle);
++      return consistent_alloc(gfp, size, handle, 0);
+ }
+ /*
+--- linux-2.4.27/arch/arm/mm/init.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mm/init.c
+@@ -49,6 +49,9 @@
+ static unsigned long totalram_pages;
+ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+ extern char _stext, _text, _etext, _end, __init_begin, __init_end;
++#ifdef CONFIG_XIP_KERNEL
++extern char _endtext, _sdata;
++#endif
+ extern unsigned long phys_initrd_start;
+ extern unsigned long phys_initrd_size;
+@@ -347,7 +350,11 @@
+        * Register the kernel text and data with bootmem.
+        * Note that this can only be in node 0.
+        */
++#ifdef CONFIG_XIP_KERNEL
++      reserve_bootmem_node(pgdat, __pa(&_sdata), &_end - &_sdata);
++#else
+       reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext);
++#endif
+ #ifdef CONFIG_CPU_32
+       /*
+@@ -601,8 +608,13 @@
+       unsigned int codepages, datapages, initpages;
+       int i, node;
++#ifndef CONFIG_XIP_KERNEL
+       codepages = &_etext - &_text;
+       datapages = &_end - &_etext;
++#else
++      codepages = &_endtext - &_text;
++      datapages = &_end - &_sdata;
++#endif
+       initpages = &__init_end - &__init_begin;
+       high_memory = (void *)__va(meminfo.end);
+@@ -658,11 +670,13 @@
+ void free_initmem(void)
+ {
++#ifndef CONFIG_XIP_KERNEL
+       if (!machine_is_integrator()) {
+               free_area((unsigned long)(&__init_begin),
+                         (unsigned long)(&__init_end),
+                         "init");
+       }
++#endif
+ }
+ #ifdef CONFIG_BLK_DEV_INITRD
+--- linux-2.4.27/arch/arm/mm/mm-armv.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/arch/arm/mm/mm-armv.c
+@@ -356,6 +356,19 @@
+       p ++;
+ #endif
++#ifdef CONFIG_XIP_KERNEL
++      p->physical   = KERNEL_XIP_BASE_PHYS;
++      p->virtual    = KERNEL_XIP_BASE_VIRT;
++      p->length     = PGDIR_SIZE * 8;
++      p->domain     = DOMAIN_KERNEL;
++      p->prot_read  = 0;      /* r=0, b=0 --> read-only for kernel mode */
++      p->prot_write = 0;
++      p->cacheable  = 1;
++      p->bufferable = 1;
++
++      p ++;
++#endif
++
+       /*
+        * Go through the initial mappings, but clear out any
+        * pgdir entries that are not in the description.
+@@ -386,7 +399,7 @@
+       init_maps->prot_read  = 0;
+       init_maps->prot_write = 0;
+       init_maps->cacheable  = 1;
+-      init_maps->bufferable = 0;
++      init_maps->bufferable = 1;
+       create_mapping(init_maps);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mm/proc-xscale.S
+@@ -0,0 +1,1086 @@
++/*
++ *  linux/arch/arm/mm/proc-xscale.S
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  November 2000
++ *  Copyright:        (C) 2000, 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.
++ *
++ * MMU functions for the Intel XScale CPUs
++ *
++ * 2001 Aug 21:       
++ *    some contributions by Brett Gaines <brett.w.gaines@intel.com>
++ *    Copyright 2001 by Intel Corp.
++ *
++ * 2001 Sep 08:
++ *    Completely revisited, many important fixes
++ *    Nicolas Pitre <nico@cam.org>
++ */
++
++#include <linux/config.h>
++#include <linux/linkage.h>
++#include <asm/assembler.h>
++#include <asm/constants.h>
++#include <asm/procinfo.h>
++#include <asm/hardware.h>
++#include <asm/proc/pgtable.h>
++
++/*
++ * Some knobs for cache allocation policy.
++ * Allocate on write may or may not be beneficial depending on the memory
++ * usage pattern of your main application.  Write through cache is definitely
++ * a performance loss in most cases, but might be used for special purposes.
++ */
++#define PMD_CACHE_WRITE_ALLOCATE 1
++#define PTE_CACHE_WRITE_ALLOCATE 1
++#define CACHE_WRITE_THROUGH 0
++
++/*
++ * There are errata that say that dirty status bits in the cache may get
++ * corrupted. The workaround significantly affects performance, and the bug
++ * _might_ just not be that visible or critical to you, so it is configurable.
++ * Let's hope a future core revision will tell us this was only a bad dream.
++ * But in the mean time the risk and tradeoff is yours to decide....
++ */
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++#undef CACHE_WRITE_THROUGH
++#define CACHE_WRITE_THROUGH 1
++#endif
++
++/* 
++ * This is the maximum size of an area which will be flushed.  If the area
++ * is larger than this, then we flush the whole cache
++ */
++#define MAX_AREA_SIZE 32768
++
++/*
++ * the cache line size of the I and D cache
++ */
++#define CACHELINESIZE 32
++
++/*
++ * the size of the data cache
++ */
++#define CACHESIZE     32768
++
++/*
++ * and the page size
++ */
++#define PAGESIZE      4096
++
++/*
++ * Virtual address used to allocate the cache when flushed
++ *
++ * This must be an address range which is _never_ used.  It should 
++ * apparently have a mapping in the corresponding page table for 
++ * compatibility with future CPUs that _could_ require it.  For instance we
++ * don't care.
++ *
++ * This must be aligned on a 2*CACHESIZE boundary.  The code selects one of
++ * the 2 areas in alternance each time the clean_d_cache macro is used.
++ * Without this the XScale core exhibits cache eviction problems and no one
++ * knows why.  
++ *
++ * Reminder: the vector table is located at 0xffff0000-0xffff0fff.
++ */
++#define CLEAN_ADDR    0xfffe0000
++
++/*
++ * This macro is used to wait for a CP15 write and is needed
++ * when we have to ensure that the last operation to the co-pro
++ * was completed before continuing with operation.
++ */
++      .macro  cpwait, rd
++      mrc     p15, 0, \rd, c2, c0, 0          @ arbitrary read of cp15
++      mov     \rd, \rd                        @ wait for completion
++      sub     pc, pc, #4                      @ flush instruction pipeline
++      .endm   
++
++      .macro  cpwait_ret, lr, rd
++      mrc     p15, 0, \rd, c2, c0, 0          @ arbitrary read of cp15
++      sub     pc, \lr, \rd, LSR #32           @ wait for completion and
++                                              @ flush instruction pipeline
++      .endm
++
++#if !CACHE_WRITE_THROUGH
++
++/*
++ * This macro cleans the entire dcache using line allocate.
++ * The main loop has been unrolled to reduce loop overhead.
++ * rd and rs are two scratch registers.
++ */
++      .macro  clean_d_cache, rd, rs
++      ldr     \rs, =clean_addr
++      ldr     \rd, [\rs]
++      eor     \rd, \rd, #CACHESIZE
++      str     \rd, [\rs]
++      add     \rs, \rd, #CACHESIZE
++1:    mcr     p15, 0, \rd, c7, c2, 5          @ allocate D cache line
++      add     \rd, \rd, #CACHELINESIZE
++      mcr     p15, 0, \rd, c7, c2, 5          @ allocate D cache line
++      add     \rd, \rd, #CACHELINESIZE
++      mcr     p15, 0, \rd, c7, c2, 5          @ allocate D cache line
++      add     \rd, \rd, #CACHELINESIZE
++      mcr     p15, 0, \rd, c7, c2, 5          @ allocate D cache line
++      add     \rd, \rd, #CACHELINESIZE
++      teq     \rd, \rs
++      bne     1b
++      .endm
++
++      .macro  clean_d_line,   rd
++      mcr     p15, 0, \rd, c7, c10, 1
++      .endm
++
++      .data
++clean_addr:   .word   CLEAN_ADDR
++
++#else
++
++/*
++ * If cache is write-through, there is no need to clean it.
++ * Simply invalidating will do.
++ */
++
++      .macro  clean_d_cache, rd, rs
++      mcr     p15, 0, \rd, c7, c6, 0
++      .endm
++
++      /* let's try to skip this needless operations at least within loops */
++      .macro  clean_d_line,   rd
++      .endm
++
++#endif
++
++      .text
++
++/*
++ * cpu_xscale_data_abort()
++ *
++ * obtain information about current aborted instruction.
++ * Note: we read user space.  This means we might cause a data
++ * abort here if the I-TLB and D-TLB aren't seeing the same
++ * picture.  Unfortunately, this does happen.  We live with it.
++ *
++ *  r2 = address of aborted instruction
++ *  r3 = saved SPSR
++ *
++ * Returns:
++ *  r0 = address of abort
++ *  r1 = FSR, bit 11 = write
++ *  r3 = corrupted
++ */
++      .align  5
++ENTRY(cpu_xscale_data_abort)
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r0, c6, c0, 0           @ get FAR
++      ldr     r3, [r2]                        @ read aborted instruction
++      bic     r1, r1, #1 << 11                @ clear bits 11 of FSR
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
++      mov     pc, lr
++
++/*
++ * cpu_xscale_check_bugs()
++ */
++ENTRY(cpu_xscale_check_bugs)
++      mrs     ip, cpsr
++      bic     ip, ip, #F_BIT
++      msr     cpsr, ip
++      mov     pc, lr
++
++#ifndef CONFIG_XSCALE_CACHE_ERRATA
++/*
++ * cpu_xscale_proc_init()
++ *
++ * Nothing too exciting at the moment
++ */
++ENTRY(cpu_xscale_proc_init)
++      mov     pc, lr
++#else
++/*
++ * We enable the cache here, but we make sure all the status bits for dirty
++ * lines are cleared as well (see PXA250 erratum #120).
++ */
++ENTRY(cpu_xscale_proc_init)
++      @ enable data cache
++      ldr     r0, cr_p
++      ldmia   r0, {r1, r2}
++      orr     r1, r1, #0x4
++      orr     r2, r2, #0x4
++      stmia   r0, {r1, r2}
++      mcr     p15, 0, r1, c1, c0, 0
++      cpwait  r0
++
++      @ invalidate data cache
++      mcr     p15, 0, r0, c7, c6, 0
++
++      @ fill main cache with write-through lines
++      bic     r0, pc, #0x1f
++      add     r1, r0, #CACHESIZE
++1:    ldr     r2, [r0], #32
++      cmp     r0, r1
++      bne     1b
++
++      @ enable test feature to force all fills to the mini-cache
++      mov     r1, #0x8
++      mcr     p15, 0, r1, c15, c15, 3
++
++      @ fill mini-cache with write-through lines (2kbytes, 64 lines)
++      add     r1, r0, #2048
++2:    ldr     r2, [r0], #32
++      cmp     r0, r1
++      bne     2b
++
++      @ disable test feature to force all fills to the mini-cache
++      mov     r1, #0x0
++      mcr     p15, 0, r1, c15, c15, 3
++
++      @ invalidate data cache again
++      mcr     p15, 0, r1, c7, c6, 0
++      mov     pc, lr
++
++cr_p: .long   SYMBOL_NAME(cr_alignment)
++#endif
++
++/*
++ * cpu_xscale_proc_fin()
++ */
++ENTRY(cpu_xscale_proc_fin)
++      str     lr, [sp, #-4]!
++      mov     r0, #F_BIT|I_BIT|SVC_MODE
++      msr     cpsr_c, r0
++      mrc     p15, 0, r0, c1, c0, 0           @ ctrl register
++      bic     r0, r0, #0x1800                 @ ...IZ...........
++      bic     r0, r0, #0x0006                 @ .............CA.
++      mcr     p15, 0, r0, c1, c0, 0           @ disable caches
++      bl      cpu_xscale_cache_clean_invalidate_all   @ clean caches
++      ldr     pc, [sp], #4
++
++/*
++ * cpu_xscale_reset(loc)
++ *
++ * Perform a soft reset of the system.  Put the CPU into the
++ * same state as it would be if it had been reset, and branch
++ * to what would be the reset vector.
++ *
++ * loc: location to jump to for soft reset
++ */
++      .align  5
++ENTRY(cpu_xscale_reset)
++      mov     r1, #F_BIT|I_BIT|SVC_MODE
++      msr     cpsr_c, r1                      @ reset CPSR
++      mrc     p15, 0, r1, c1, c0, 0           @ ctrl register
++      bic     r1, r1, #0x0086                 @ ........B....CA.
++      bic     r1, r1, #0x1900                 @ ...IZ..S........
++      mcr     p15, 0, r1, c1, c0, 0           @ ctrl register
++      mcr     p15, 0, ip, c7, c7, 0           @ invalidate I,D caches & BTB
++      bic     r1, r1, #0x0001                 @ ...............M
++      mcr     p15, 0, r1, c1, c0, 0           @ ctrl register
++      @ CAUTION: MMU turned off from this point. We count on the pipeline 
++      @ already containing those two last instructions to survive.
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I & D TLBs
++      mov     pc, r0
++
++/*
++ * cpu_xscale_do_idle(type)
++ *
++ * Cause the processor to idle
++ *
++ * type: 
++ *   0 = slow idle
++ *   1 = fast idle
++ *   2 = switch to slow processor clock
++ *   3 = switch to fast processor clock
++ *
++ * For now we do nothing but go to idle mode for every case
++ *
++ * XScale supports clock switching, but using idle mode support
++ * allows external hardware to react to system state changes.
++ */
++      .align  5
++
++ENTRY(cpu_xscale_do_idle)
++      mov     r0, #1
++      mcr     p14, 0, r0, c7, c0, 0           @ Go to IDLE
++      mov     pc, lr
++
++/* ================================= CACHE ================================ */
++
++/*
++ * cpu_xscale_cache_clean_invalidate_all (void)
++ *
++ * clean and invalidate all cache lines
++ *
++ * Note:
++ *  1. We should preserve r0 at all times.
++ *  2. Even if this function implies cache "invalidation" by its name,
++ *     we don't need to actually use explicit invalidation operations
++ *     since the goal is to discard all valid references from the cache
++ *     and the cleaning of it already has that effect.
++ *  3. Because of 2 above and the fact that kernel space memory is always
++ *     coherent across task switches there is no need to worry about
++ *     inconsistencies due to interrupts, ence no irq disabling.
++ */
++      .align  5
++ENTRY(cpu_xscale_cache_clean_invalidate_all)
++      mov     r2, #1
++cpu_xscale_cache_clean_invalidate_all_r2:
++      clean_d_cache r0, r1
++      teq     r2, #0
++      mcrne   p15, 0, ip, c7, c5, 0           @ Invalidate I cache & BTB
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/*
++ * cpu_xscale_cache_clean_invalidate_range(start, end, flags)
++ *
++ * clean and invalidate all cache lines associated with this area of memory
++ *
++ * start: Area start address
++ * end:   Area end address
++ * flags: nonzero for I cache as well
++ */
++      .align  5
++ENTRY(cpu_xscale_cache_clean_invalidate_range)
++      bic     r0, r0, #CACHELINESIZE - 1      @ round down to cache line
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      bhi     cpu_xscale_cache_clean_invalidate_all_r2
++1:    clean_d_line r0                         @ Clean D cache line
++      mcr     p15, 0, r0, c7, c6, 1           @ Invalidate D cache line
++      add     r0, r0, #CACHELINESIZE
++      cmp     r0, r1
++      blo     1b
++      teq     r2, #0
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      moveq   pc, lr
++      sub     r0, r0, r3
++1:    mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      cmp     r0, r1
++      blo     1b
++      mcr     p15, 0, ip, c7, c5, 6           @ Invalidate BTB
++      mov     pc, lr 
++
++/*
++ * cpu_xscale_flush_ram_page(page)
++ *
++ * clean all cache lines associated with this memory page
++ *
++ * page: page to clean
++ */
++      .align  5
++ENTRY(cpu_xscale_flush_ram_page)
++#if !CACHE_WRITE_THROUGH
++      mov     r1, #PAGESIZE
++1:    mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      subs    r1, r1, #2 * CACHELINESIZE
++      bne     1b
++#endif
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/* ================================ D-CACHE =============================== */
++
++/*
++ * cpu_xscale_dcache_invalidate_range(start, end)
++ *
++ * throw away all D-cached data in specified region without an obligation
++ * to write them back.  Note however that on XScale we must clean all
++ * entries also due to hardware errata (80200 A0 & A1 only).
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_xscale_dcache_invalidate_range)
++      mrc     p15, 0, r2, c0, c0, 0           @ Read part no.
++      eor     r2, r2, #0x69000000
++      eor     r2, r2, #0x00052000             @ 80200 XX part no.
++      bics    r2, r2, #0x1                    @ Clear LSB in revision field
++      moveq   r2, #0
++      beq     cpu_xscale_cache_clean_invalidate_range @ An 80200 A0 or A1
++
++      tst     r0, #CACHELINESIZE - 1
++      mcrne   p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      tst     r1, #CACHELINESIZE - 1
++      mcrne   p15, 0, r1, c7, c10, 1          @ Clean D cache line
++      bic     r0, r0, #CACHELINESIZE - 1      @ round down to cache line
++1:    mcr     p15, 0, r0, c7, c6, 1           @ Invalidate D cache line
++      add     r0, r0, #CACHELINESIZE
++      cmp     r0, r1
++      blo     1b
++      mov     pc, lr
++
++/*
++ * cpu_xscale_dcache_clean_range(start, end)
++ *
++ * For the specified virtual address range, ensure that all caches contain
++ * clean data, such that peripheral accesses to the physical RAM fetch
++ * correct data.
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_xscale_dcache_clean_range)
++#if !CACHE_WRITE_THROUGH
++      bic     r0, r0, #CACHELINESIZE - 1
++      sub     r2, r1, r0
++      cmp     r2, #MAX_AREA_SIZE
++      movhi   r2, #0
++      bhi     cpu_xscale_cache_clean_invalidate_all_r2
++
++1:    mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      cmp     r0, r1
++      blo     1b
++#endif
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/*
++ * cpu_xscale_clean_dcache_page(page)
++ *
++ * Cleans a single page of dcache so that if we have any future aliased
++ * mappings, they will be consistent at the time that they are created.
++ *
++ * Note:
++ *  1. we don't need to flush the write buffer in this case. [really? -Nico]
++ *  2. we don't invalidate the entries since when we write the page
++ *     out to disk, the entries may get reloaded into the cache.
++ */
++      .align  5
++ENTRY(cpu_xscale_dcache_clean_page)
++#if !CACHE_WRITE_THROUGH
++      mov     r1, #PAGESIZE
++1:    mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      add     r0, r0, #CACHELINESIZE
++      subs    r1, r1, #4 * CACHELINESIZE
++      bne     1b
++#endif
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/*
++ * cpu_xscale_dcache_clean_entry(addr)
++ *
++ * Clean the specified entry of any caches such that the MMU
++ * translation fetches will obtain correct data.
++ *
++ * addr: cache-unaligned virtual address
++ */
++      .align  5
++ENTRY(cpu_xscale_dcache_clean_entry)
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/* ================================ I-CACHE =============================== */
++
++/*
++ * cpu_xscale_icache_invalidate_range(start, end)
++ *
++ * invalidate a range of virtual addresses from the Icache
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ *
++ * Note: This is vaguely defined as supposed to bring the dcache and the 
++ *       icache in sync by the way this function is used.
++ */
++      .align  5
++ENTRY(cpu_xscale_icache_invalidate_range)
++      bic     r0, r0, #CACHELINESIZE - 1
++1:    clean_d_line r0                         @ Clean D cache line
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      cmp     r0, r1
++      blo     1b
++      mcr     p15, 0, ip, c7, c5, 6           @ Invalidate BTB
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/*
++ * cpu_xscale_icache_invalidate_page(page)
++ *
++ * invalidate all Icache lines associated with this area of memory
++ *
++ * page: page to invalidate
++ */
++      .align  5
++ENTRY(cpu_xscale_icache_invalidate_page)
++      mov     r1, #PAGESIZE
++1:    mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I cache line
++      add     r0, r0, #CACHELINESIZE
++      subs    r1, r1, #4 * CACHELINESIZE
++      bne     1b
++      mcr     p15, 0, r0, c7, c5, 6           @ Invalidate BTB
++      mov     pc, lr
++
++/* ================================ CACHE LOCKING============================ 
++ *
++ * The XScale MicroArchitecture implements support for locking entries into
++ * the data and instruction cache.  The following functions implement the core
++ * low level instructions needed to accomplish the locking.  The developer's
++ * manual states that the code that performs the locking must be in non-cached
++ * memory.  To accomplish this, the code in xscale-cache-lock.c copies the
++ * following functions from the cache into a non-cached memory region that
++ * is allocated through consistent_alloc().
++ *
++ */
++      .align  5
++/*
++ * xscale_icache_lock
++ *
++ * r0: starting address to lock
++ * r1: end address to lock
++ */
++ENTRY(xscale_icache_lock)
++
++iLockLoop:
++      bic     r0, r0, #CACHELINESIZE - 1
++      mcr     p15, 0, r0, c9, c1, 0   @ lock into cache
++      cmp     r0, r1                  @ are we done?
++      add     r0, r0, #CACHELINESIZE  @ advance to next cache line
++      bls     iLockLoop
++      mov     pc, lr
++
++/*
++ * xscale_icache_unlock
++ */
++ENTRY(xscale_icache_unlock)
++      mcr     p15, 0, r0, c9, c1, 1   @ Unlock icache
++      mov     pc, lr
++      
++/*
++ * xscale_dcache_lock
++ *
++ * r0: starting address to lock
++ * r1: end address to lock
++ */
++ENTRY(xscale_dcache_lock)
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     r2, #1
++      mcr     p15, 0, r2, c9, c2, 0   @ Put dcache in lock mode
++      cpwait  ip                      @ Wait for completion
++
++      mrs     r2, cpsr
++      orr     r3, r2, #F_BIT | I_BIT
++dLockLoop:
++      msr     cpsr_c, r3
++      mcr     p15, 0, r0, c7, c10, 1  @ Write back line if it is dirty 
++      mcr     p15, 0, r0, c7, c6, 1   @ Flush/invalidate line
++      msr     cpsr_c, r2
++      ldr     ip, [r0], #CACHELINESIZE @ Preload 32 bytes into cache from 
++                                      @ location [r0]. Post-increment
++                                      @ r3 to next cache line
++      cmp     r0, r1                  @ Are we done?
++      bls     dLockLoop
++
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     r2, #0
++      mcr     p15, 0, r2, c9, c2, 0   @ Get out of lock mode
++      cpwait_ret lr, ip
++
++/*
++ * xscale_dcache_unlock
++ */
++ENTRY(xscale_dcache_unlock)
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mcr     p15, 0, ip, c9, c2, 1   @ Unlock cache
++      mov     pc, lr
++
++/*
++ * Needed to determine the length of the code that needs to be copied.
++ */
++      .align  5
++ENTRY(xscale_cache_dummy)
++      mov     pc, lr
++
++/* ================================== TLB ================================= */
++
++/*
++ * cpu_xscale_tlb_invalidate_all()
++ *
++ * Invalidate all TLB entries
++ */
++      .align  5
++ENTRY(cpu_xscale_tlb_invalidate_all)
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I & D TLBs
++      cpwait_ret lr, ip
++
++/*
++ * cpu_xscale_tlb_invalidate_range(start, end)
++ *
++ * invalidate TLB entries covering the specified range
++ *
++ * start: range start address
++ * end:   range end address
++ */
++      .align  5
++ENTRY(cpu_xscale_tlb_invalidate_range)
++      bic     r0, r0, #(PAGESIZE - 1) & 0x00ff
++      bic     r0, r0, #(PAGESIZE - 1) & 0xff00
++      sub     r3, r1, r0
++      cmp     r3, #256 * PAGESIZE             @ arbitrary, should be tuned
++      bhi     cpu_xscale_tlb_invalidate_all
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++1:    mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
++      add     r0, r0, #PAGESIZE
++      cmp     r0, r1
++      blo     1b
++      cpwait_ret lr, ip
++
++/*
++ * cpu_xscale_tlb_invalidate_page(page, flags)
++ *
++ * invalidate the TLB entries for the specified page.
++ *
++ * page:  page to invalidate
++ * flags: non-zero if we include the I TLB
++ */
++      .align  5
++ENTRY(cpu_xscale_tlb_invalidate_page)
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      teq     r1, #0
++      mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcrne   p15, 0, r3, c8, c5, 1           @ invalidate I TLB entry
++      cpwait_ret lr, ip
++
++/* ================================ TLB LOCKING==============================
++ *
++ * The XScale MicroArchitecture implements support for locking entries into
++ * the Instruction and Data TLBs.  The following functions provide the
++ * low level support for supporting these under Linux.  xscale-lock.c 
++ * implements some higher level management code.  Most of the following
++ * is taken straight out of the Developer's Manual.
++ */
++
++/*
++ * Lock I-TLB entry
++ *
++ * r0: Virtual address to translate and lock
++ */
++      .align  5
++ENTRY(xscale_itlb_lock)
++      mrs     r2, cpsr
++      orr     r3, r2, #F_BIT | I_BIT
++      msr     cpsr_c, r3                      @ Disable interrupts
++      mcr     p15, 0, r0, c8, c5, 1           @ Invalidate I-TLB entry
++      mcr     p15, 0, r0, c10, c4, 0          @ Translate and lock
++      msr     cpsr_c, r2                      @ Restore interrupts
++      cpwait_ret lr, ip
++
++/*
++ * Lock D-TLB entry
++ *
++ * r0: Virtual address to translate and lock
++ */
++      .align  5
++ENTRY(xscale_dtlb_lock)
++      mrs     r2, cpsr
++      orr     r3, r2, #F_BIT | I_BIT
++      msr     cpsr_c, r3                      @ Disable interrupts
++      mcr     p15, 0, r0, c8, c6, 1           @ Invalidate D-TLB entry
++      mcr     p15, 0, r0, c10, c8, 0          @ Translate and lock
++      msr     cpsr_c, r2                      @ Restore interrupts
++      cpwait_ret lr, ip
++
++/*
++ * Unlock all I-TLB entries
++ */
++      .align  5
++ENTRY(xscale_itlb_unlock)
++      mcr     p15, 0, ip, c10, c4, 1          @ Unlock I-TLB
++      mcr     p15, 0, ip, c8, c5, 0           @ Invalidate I-TLB
++      cpwait_ret lr, ip
++
++/*
++ * Unlock all D-TLB entries
++ */
++ENTRY(xscale_dtlb_unlock)
++      mcr     p15, 0, ip, c10, c8, 1          @ Unlock D-TBL
++      mcr     p15, 0, ip, c8, c6, 0           @ Invalidate D-TLB
++      cpwait_ret lr, ip
++
++/* =============================== PageTable ============================== */
++
++/*
++ * cpu_xscale_set_pgd(pgd)
++ *
++ * Set the translation base pointer to be as described by pgd.
++ *
++ * pgd: new page tables
++ */
++      .align  5
++ENTRY(cpu_xscale_set_pgd)
++      clean_d_cache r1, r2
++      mcr     p15, 0, ip, c7, c5, 0           @ Invalidate I cache & BTB
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mcr     p15, 0, r0, c2, c0, 0           @ load page table pointer
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I & D TLBs
++      cpwait_ret lr, ip
++
++/*
++ * cpu_xscale_set_pmd(pmdp, pmd)
++ *
++ * Set a level 1 translation table entry, and clean it out of
++ * any caches such that the MMUs can load it correctly.
++ *
++ * pmdp: pointer to PMD entry
++ * pmd:  PMD value to store
++ */
++      .align  5
++ENTRY(cpu_xscale_set_pmd)
++#if PMD_CACHE_WRITE_ALLOCATE && !CACHE_WRITE_THROUGH
++      and     r2, r1, #PMD_TYPE_MASK|PMD_SECT_CACHEABLE|PMD_SECT_BUFFERABLE
++      cmp     r2, #PMD_TYPE_SECT|PMD_SECT_CACHEABLE|PMD_SECT_BUFFERABLE
++      orreq   r1, r1, #PMD_SECT_TEX(1)
++#elif CACHE_WRITE_THROUGH
++      and     r2, r1, #PMD_TYPE_MASK|PMD_SECT_CACHEABLE|PMD_SECT_BUFFERABLE
++      cmp     r2, #PMD_TYPE_SECT|PMD_SECT_CACHEABLE|PMD_SECT_BUFFERABLE
++      biceq   r1, r1, #PMD_SECT_BUFFERABLE
++#endif
++      str     r1, [r0]
++      mov     ip, #0
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++/*
++ * cpu_xscale_set_pte(ptep, pte)
++ *
++ * Set a PTE and flush it out
++ *
++ * Errata 40: must set memory to write-through for user read-only pages.
++ */
++      .align  5
++ENTRY(cpu_xscale_set_pte)
++      str     r1, [r0], #-1024                @ linux version
++
++      bic     r2, r1, #0xff0
++      orr     r2, r2, #PTE_TYPE_EXT           @ extended page
++
++      eor     r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
++
++      tst     r3, #L_PTE_USER | L_PTE_EXEC    @ User or Exec?
++      orrne   r2, r2, #PTE_EXT_AP_URO_SRW     @ yes -> user r/o, system r/w
++
++      tst     r3, #L_PTE_WRITE | L_PTE_DIRTY  @ Write and Dirty?
++      orreq   r2, r2, #PTE_EXT_AP_UNO_SRW     @ yes -> user n/a, system r/w
++                                              @ combined with user -> user r/w
++
++      @
++      @ Handle the X bit.  We want to set this bit for the minicache
++      @ (U = E = B = W = 0, C = 1) or when write allocate is enabled,
++      @ and we have a writeable, cacheable region.  If we ignore the
++      @ U and E bits, we can allow user space to use the minicache as
++      @ well.
++      @
++      @  X = C & ~W & ~B
++      @      | C & W & B & write_allocate
++      @
++      eor     ip, r1, #L_PTE_CACHEABLE
++      tst     ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE
++#if PTE_CACHE_WRITE_ALLOCATE && !CACHE_WRITE_THROUGH
++      eorne   ip, r1, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE
++      tstne   ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE
++#endif
++      orreq   r2, r2, #PTE_EXT_TEX(1)
++
++#if CACHE_WRITE_THROUGH
++      tst     r1, #L_PTE_CACHEABLE
++      bicne   r2, r2, #L_PTE_BUFFERABLE       @ clear B only if C is set
++#else
++      @
++      @ Errata 40: The B bit must be cleared for a user read-only
++      @ cacheable page.
++      @
++      @  B = B & ~((U|E) & C & ~W)
++      @
++      and     ip, r1, #L_PTE_USER | L_PTE_EXEC | L_PTE_WRITE | L_PTE_CACHEABLE
++      teq     ip, #L_PTE_USER | L_PTE_CACHEABLE
++      teqne   ip, #L_PTE_EXEC | L_PTE_CACHEABLE
++      teqne   ip, #L_PTE_USER | L_PTE_EXEC | L_PTE_CACHEABLE
++      biceq   r2, r2, #PTE_BUFFERABLE
++#endif
++
++      tst     r3, #L_PTE_PRESENT | L_PTE_YOUNG        @ Present and Young?
++      movne   r2, #0                          @ no -> fault
++
++      str     r2, [r0]                        @ hardware version
++
++      @ We try to map 64K page entries when possible.  
++      @ We do that for kernel space only since the usage pattern from
++      @ the setting of VM area is quite simple.  User space is not worth
++      @ the implied complexity because of ever randomly changing PTEs 
++      @ (page aging, swapout, etc) requiring constant coherency checks.
++      @ Since PTEs are usually set in increasing order, we test the
++      @ possibility for a large page only when given the last PTE of a
++      @ 64K boundary.
++      tsteq   r1, #L_PTE_USER
++      andeq   r1, r0, #(15 << 2)
++      teqeq   r1, #(15 << 2)
++      beq     1f
++
++      mov     ip, #0
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++      @ See if we have 16 identical PTEs but with consecutive base addresses
++1:    bic     r3, r2, #0x0000f000
++      mov     r1, #0x0000f000
++2:    eor     r2, r2, r3
++      teq     r2, r1
++      bne     4f
++      subs    r1, r1, #0x00001000
++      ldr     r2, [r0, #-4]!
++      bne     2b
++      eors    r2, r2, r3
++      bne     4f
++
++      @ Now create our LARGE PTE from the current EXT one.
++      bic     r3, r3, #PTE_TYPE_MASK
++      orr     r3, r3, #PTE_TYPE_LARGE
++      and     r2, r3, #0x30                   @ EXT_AP --> LARGE_AP0
++      orr     r2, r2, r2, lsl #2              @ add LARGE_AP1
++      orr     r2, r2, r2, lsl #4              @ add LARGE_AP3 + LARGE_AP2
++      and     r1, r3, #0x3c0                  @ EXT_TEX
++      bic     r3, r3, #0x3c0
++      orr     r2, r2, r1, lsl #(12 - 6)       @ --> LARGE_TEX
++      orr     r2, r2, r3                      @ add remaining bits
++
++      @ then put it in the pagetable
++      mov     r3, r2
++3:    strd    r2, [r0], #8
++      tst     r0, #(15 << 2)
++      bne     3b
++
++      @ Then sync the 2 corresponding cache lines
++      sub     r0, r0, #(16 << 2)
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++4:    orr     r0, r0, #(15 << 2)
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D cache line
++      mov     ip, #0
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mov     pc, lr
++
++      .ltorg
++
++cpu_manu_name:
++      .asciz  "Intel"
++
++cpu_80200_name:
++      .asciz  "XScale-80200"
++
++cpu_pxa210_name:
++      .asciz  "XScale-PXA210"
++
++cpu_pxa250_name:
++      .asciz  "XScale-PXA250"
++
++cpu_pxa255_name:
++      .asciz  "XScale-PXA255"
++
++      .align
++
++      .section ".text.init", #alloc, #execinstr
++
++__xscale_setup:
++      mov     r0, #F_BIT|I_BIT|SVC_MODE
++      msr     cpsr_c, r0
++      mcr     p15, 0, ip, c7, c7, 0           @ invalidate I, D caches & BTB
++      mcr     p15, 0, ip, c7, c10, 4          @ Drain Write (& Fill) Buffer
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I, D TLBs
++      mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
++      mov     r0, #0x1f                       @ Domains 0, 1 = client
++      mcr     p15, 0, r0, c3, c0, 0           @ load domain access register
++      mov     r0, #1                          @ Allow user space to access
++      mcr     p15, 0, r0, c15, c1, 0          @ ... CP 0 only.
++#if CACHE_WRITE_THROUGH
++      mov     r0, #0x20
++#else
++      mov     r0, #0x00
++#endif
++      mcr     p15, 0, r0, c1, c1, 0           @ set auxiliary control reg
++      mrc     p15, 0, r0, c1, c0, 0           @ get control register
++      bic     r0, r0, #0x0200                 @ ......R.........
++      bic     r0, r0, #0x0082                 @ ........B.....A.
++      orr     r0, r0, #0x0005                 @ .............C.M
++      orr     r0, r0, #0x3900                 @ ..VIZ..S........
++#ifdef CONFIG_XSCALE_CACHE_ERRATA
++      bic     r0, r0, #0x0004                 @ see cpu_xscale_proc_init
++#endif
++      mov     pc, lr
++
++      .text
++
++/*
++ * Purpose : Function pointers used to access above functions - all calls
++ *         come through these
++ */
++
++      .type   xscale_processor_functions, #object
++ENTRY(xscale_processor_functions)
++      .word   cpu_xscale_data_abort
++      .word   cpu_xscale_check_bugs
++      .word   cpu_xscale_proc_init
++      .word   cpu_xscale_proc_fin
++      .word   cpu_xscale_reset
++      .word   cpu_xscale_do_idle
++
++      /* cache */
++      .word   cpu_xscale_cache_clean_invalidate_all
++      .word   cpu_xscale_cache_clean_invalidate_range
++      .word   cpu_xscale_flush_ram_page
++
++      /* dcache */
++      .word   cpu_xscale_dcache_invalidate_range
++      .word   cpu_xscale_dcache_clean_range
++      .word   cpu_xscale_dcache_clean_page
++      .word   cpu_xscale_dcache_clean_entry
++
++      /* icache */
++      .word   cpu_xscale_icache_invalidate_range
++      .word   cpu_xscale_icache_invalidate_page
++
++      /* tlb */
++      .word   cpu_xscale_tlb_invalidate_all
++      .word   cpu_xscale_tlb_invalidate_range
++      .word   cpu_xscale_tlb_invalidate_page
++
++      /* pgtable */
++      .word   cpu_xscale_set_pgd
++      .word   cpu_xscale_set_pmd
++      .word   cpu_xscale_set_pte
++      .size   xscale_processor_functions, . - xscale_processor_functions
++
++      .type   cpu_80200_info, #object
++cpu_80200_info:
++      .long   cpu_manu_name
++      .long   cpu_80200_name
++      .size   cpu_80200_info, . - cpu_80200_info
++
++      .type   cpu_pxa210_info, #object
++cpu_pxa210_info:
++      .long   cpu_manu_name
++      .long   cpu_pxa210_name
++      .size   cpu_pxa210_info, . - cpu_pxa210_info
++
++      .type   cpu_pxa250_info, #object
++cpu_pxa250_info:
++      .long   cpu_manu_name
++      .long   cpu_pxa250_name
++      .size   cpu_pxa250_info, . - cpu_pxa250_info
++
++      .type   cpu_pxa255_info, #object
++cpu_pxa255_info:
++      .long   cpu_manu_name
++      .long   cpu_pxa255_name
++      .size   cpu_pxa255_info, . - cpu_pxa255_info
++
++      .type   cpu_arch_name, #object
++cpu_arch_name:
++      .asciz  "armv5te"
++      .size   cpu_arch_name, . - cpu_arch_name
++
++      .type   cpu_elf_name, #object
++cpu_elf_name:
++      .asciz  "v5"
++      .size   cpu_elf_name, . - cpu_elf_name
++      .align
++
++      .section ".proc.info", #alloc, #execinstr
++
++      .type   __80200_proc_info,#object
++__80200_proc_info:
++      .long   0x69052000
++      .long   0xfffffff0
++#if CACHE_WRITE_THROUGH
++      .long   0x00000c0a
++#else
++      .long   0x00000c0e
++#endif
++      b       __xscale_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE
++      .long   cpu_80200_info
++      .long   xscale_processor_functions
++      .size   __80200_proc_info, . - __80200_proc_info
++
++      .type   __pxa210_proc_info,#object
++__pxa210_proc_info:
++      .long   0x69052120
++      .long   0xfffff3f0
++#if CACHE_WRITE_THROUGH
++      .long   0x00000c0a
++#else
++      .long   0x00000c0e
++#endif
++      b       __xscale_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE
++      .long   cpu_pxa210_info
++      .long   xscale_processor_functions
++      .size   __pxa210_proc_info, . - __pxa210_proc_info
++
++      .type   __pxa250_proc_info,#object
++__pxa250_proc_info:
++      .long   0x69052100
++      .long   0xfffff7f0
++#if CACHE_WRITE_THROUGH
++      .long   0x00000c0a
++#else
++      .long   0x00000c0e
++#endif
++      b       __xscale_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE
++      .long   cpu_pxa250_info
++      .long   xscale_processor_functions
++      .size   __pxa250_proc_info, . - __pxa250_proc_info
++
++      .type   __pxa255_proc_info,#object
++__pxa255_proc_info:
++      .long   0x69052d00
++      .long   0xfffffff0
++#if CACHE_WRITE_THROUGH
++      .long   0x00000c0a
++#else
++      .long   0x00000c0e
++#endif
++      b       __xscale_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_XSCALE
++      .long   cpu_pxa255_info
++      .long   xscale_processor_functions
++      .size   __pxa255_proc_info, . - __pxa255_proc_info
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/vmlinux-armv-xip.lds.in
+@@ -0,0 +1,125 @@
++/*
++ * ld script to make ARM Linux kernel
++ *
++ * (C) Copyright 2001 Lineo Japan, Inc.
++ *
++ * May be copied or modified under the terms of the GNU General Public
++ * License.  See linux/COPYING for more information.
++ *
++ * Based on arch/arm/vmlinux-armv.lds.in
++ *
++ * taken from the i386 version by Russell King
++ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
++ */
++OUTPUT_ARCH(arm)
++ENTRY(stext)
++SECTIONS
++{
++      . = TEXTADDR;
++      .init : {                       /* Init code and data           */
++              _stext = .;
++              __init_begin = .;
++                      *(.text.init)
++              __proc_info_begin = .;
++                      *(.proc.info)
++              __proc_info_end = .;
++              __arch_info_begin = .;
++                      *(.arch.info)
++              __arch_info_end = .;
++              __tagtable_begin = .;
++                      *(.taglist)
++              __tagtable_end = .;
++              . = ALIGN(16);
++              __setup_start = .;
++                      *(.setup.init)
++              __setup_end = .;
++              __initcall_start = .;
++                      *(.initcall.init)
++              __initcall_end = .;
++              . = ALIGN(4096);
++              __init_end = .;
++      }
++
++      /DISCARD/ : {                   /* Exit code and data           */
++              *(.text.exit)
++              *(.data.exit)
++              *(.exitcall.exit)
++      }
++
++      .text : {                       /* Real text segment            */
++              _text = .;              /* Text and read-only data      */
++                      *(.text)
++                      *(.fixup)
++                      *(.gnu.warning)
++                      *(.text.lock)   /* out-of-line lock text */
++                      *(.rodata)
++                      *(.rodata.*)
++                      *(.glue_7)
++                      *(.glue_7t)
++                      *(.kstrtab)
++              *(.got)                 /* Global offset table          */
++              *(.got.plt)
++
++              _etext = .;             /* End of text section          */
++      }
++
++      . = ALIGN(16);
++      __ex_table : {                  /* Exception table              */
++              __start___ex_table = .;
++                      *(__ex_table)
++              __stop___ex_table = .;
++      }
++
++      __ksymtab : {                   /* Kernel symbol table          */
++              __start___ksymtab = .;
++                      *(__ksymtab)
++              __stop___ksymtab = .;
++      }
++
++      _endtext = .;
++
++      . = DATAADDR;
++
++      _sdata = .;
++
++      . = ALIGN(8192);
++
++      .data : {
++              /*
++               * first, the init task union, aligned
++               * to an 8192 byte boundary.
++               */
++              *(.init.task)
++
++              /*
++               * then the cacheline aligned data
++               */
++              . = ALIGN(32);
++              *(.data.cacheline_aligned)
++
++              /*
++               * and the usual data section
++               */
++              *(.data)
++              CONSTRUCTORS
++
++              *(.data.init)
++
++              _edata = .;
++      }
++
++      .bss : {
++              __bss_start = .;        /* BSS                          */
++              *(.bss)
++              *(COMMON)
++              _end = . ;
++      }
++                                      /* Stabs debugging sections.    */
++      .stab 0 : { *(.stab) }
++      .stabstr 0 : { *(.stabstr) }
++      .stab.excl 0 : { *(.stab.excl) }
++      .stab.exclstr 0 : { *(.stab.exclstr) }
++      .stab.index 0 : { *(.stab.index) }
++      .stab.indexstr 0 : { *(.stab.indexstr) }
++      .comment 0 : { *(.comment) }
++}
+--- linux-2.4.27/drivers/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/Makefile
+@@ -25,6 +25,7 @@
+ subdir-$(CONFIG_NUBUS)                += nubus
+ subdir-$(CONFIG_TC)           += tc
+ subdir-$(CONFIG_VT)           += video
++subdir-$(CONFIG_MMC)          += mmc
+ subdir-$(CONFIG_MAC)          += macintosh
+ subdir-$(CONFIG_PPC32)                += macintosh
+ subdir-$(CONFIG_USB)          += usb
+--- linux-2.4.27/drivers/char/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/char/Config.in
+@@ -253,6 +253,7 @@
+       dep_tristate '  DC21285 watchdog' CONFIG_21285_WATCHDOG $CONFIG_FOOTBRIDGE
+       dep_tristate '  NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG $CONFIG_ARCH_NETWINDER
+       dep_tristate '  SA1100 watchdog' CONFIG_SA1100_WATCHDOG $CONFIG_ARCH_SA1100
++      dep_tristate '  PXA250/210 watchdog' CONFIG_SA1100_WATCHDOG $CONFIG_ARCH_PXA
+       dep_tristate '  EPXA watchdog' CONFIG_EPXA_WATCHDOG $CONFIG_ARCH_CAMELOT
+       dep_tristate '  Omaha watchdog' CONFIG_OMAHA_WATCHDOG $CONFIG_ARCH_OMAHA
+       dep_tristate '  AT91RM9200 watchdog' CONFIG_AT91_WATCHDOG $CONFIG_ARCH_AT91RM9200
+@@ -334,6 +335,9 @@
+ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+    tristate 'SA1100 Real Time Clock' CONFIG_SA1100_RTC
+ fi
++if [ "$CONFIG_ARCH_PXA" = "y" ]; then
++   tristate 'PXA250/210 Real Time Clock' CONFIG_PXA_RTC
++fi
+ if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then
+    tristate 'Omaha Real Time Clock' CONFIG_OMAHA_RTC
+ fi
+@@ -416,4 +420,8 @@
+    dep_tristate 'HP OB600 C/CT Pop-up mouse support' CONFIG_OBMOUSE $CONFIG_INPUT_MOUSEDEV
+ fi
++if [ "$CONFIG_ARCH_TRIZEPS2" = "y" ]; then
++   tristate ' MT6N TTL I/O suport' CONFIG_TRIZEPS2_TTLIO
++fi
++
+ endmenu
+--- linux-2.4.27/drivers/char/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/char/Makefile
+@@ -281,6 +281,7 @@
+ obj-$(CONFIG_MIPS_RTC) += mips_rtc.o
+ obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
+ obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o
++obj-$(CONFIG_PXA_RTC) += sa1100-rtc.o
+ obj-$(CONFIG_OMAHA_RTC) += omaha-rtc.o
+ ifeq ($(CONFIG_PPC),)
+   obj-$(CONFIG_NVRAM) += nvram.o
+--- /dev/null
++++ linux-2.4.27/drivers/char/mt6n_ttl.c
+@@ -0,0 +1,316 @@
++/*
++ *    Trizeps-2 MT6N development board TTL-IO interface for Linux     
++ *
++ *    Copyright (C) 2003 Luc De Cock
++ *
++ *    This driver allows use of the TTL-IO interface on the MT6N
++ *    from user space. It exports the /dev/ttlio interface supporting
++ *    some ioctl() and also the /proc/driver/ttlio pseudo-file
++ *    for status information.
++ *
++ *    The ioctls can be used to set individual TTL output lines.
++ *    Only ioctls are supported.
++ *
++ *    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.
++ *
++ *    Based on other minimal char device drivers, like Alan's
++ *    watchdog, Ted's random, Paul's rtc, etc. etc.
++ *
++ *    1.00    Luc De Cock: initial version.
++ */
++
++#define TTLIO_VERSION         "1.00"
++
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/miscdevice.h>
++#include <linux/fcntl.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++
++/* Writing to the register sets the output lines
++*  Reading from the register returns the status of the input lines
++*/
++static unsigned short *ttlio_base = (unsigned short *) TRIZEPS2_TTLIO_BASE;
++static unsigned short ttlio_shadow = 0;
++
++/* interrupt stuff */
++static struct fasync_struct *ttlio_async_queue;
++static DECLARE_WAIT_QUEUE_HEAD(ttlio_wait);
++static int ttlio_irq_arrived = 0;
++static spinlock_t ttlio_lock;
++static unsigned short ttlio_in = 0;
++static volatile unsigned long teller = 0;
++
++
++static int ttlio_ioctl(struct inode *inode, struct file *file,
++                     unsigned int cmd, unsigned long arg);
++
++static int ttlio_read_proc(char *page, char **start, off_t off,
++                           int count, int *eof, void *data);
++
++
++static void ttlio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      ttlio_in = *ttlio_base;
++
++      ttlio_irq_arrived = 1;
++      teller++;
++      
++      /* wake up the waiting process */
++      wake_up_interruptible(&ttlio_wait);
++      kill_fasync(&ttlio_async_queue, SIGIO, POLL_IN);
++}
++
++/*
++ *    Now all the various file operations that we export.
++ */
++
++static int ttlio_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
++                     unsigned long arg)
++{
++      unsigned long ttlio_val;
++
++      switch (cmd) {
++      case TTLIO_RESET:       /* clear all lines */
++      {
++              *ttlio_base = 0;
++              return 0;
++      }
++      case TTLIO_GET:         /* get state of TTL input lines */
++      {
++              ttlio_val = *ttlio_base;
++              return put_user(ttlio_val, (unsigned long *)arg);
++      }
++      case TTLIO_SET:         /* set state of TTL output lines */
++      {
++              unsigned long user_val;
++              if (copy_from_user(&user_val, arg, sizeof(unsigned long)))
++                      return -EFAULT;
++              ttlio_shadow |= (unsigned short) user_val;
++              *ttlio_base = ttlio_shadow;
++              return 0;
++      }
++      case TTLIO_UNSET:       /* unset (clear) state of TTL output lines */
++      {
++              unsigned long user_val;
++              if (copy_from_user(&user_val, arg, sizeof(unsigned long)))
++                      return -EFAULT;
++              ttlio_shadow &= ~((unsigned short) user_val);
++              *ttlio_base = ttlio_shadow;
++              return 0;
++      }
++      case 100:               /* get counter */
++      {
++              return put_user(teller, (unsigned long *)arg);
++      }
++      case 101:               /* reset counter */
++      {
++              teller = 0;
++              return 0;
++      }
++      default:
++              return -ENOTTY;
++      }
++      return 0;
++}
++
++static ssize_t ttlio_read(struct file *file, char *buf,
++                        size_t count, loff_t *ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      unsigned short data;
++      ssize_t retval;
++
++      if (count < sizeof(unsigned short))
++              return -EINVAL;
++
++      if (file->f_flags & O_NONBLOCK) {
++              spin_lock_irq(&ttlio_lock);
++              data = *ttlio_base;
++              spin_unlock_irq(&ttlio_lock);
++              retval = put_user(data, (unsigned short *) buf); 
++              if (!retval)
++                      retval = sizeof(unsigned short);
++              return retval;
++      }
++      /* blocking read: wait for interrupt */
++      add_wait_queue(&ttlio_wait, &wait);
++      set_current_state(TASK_INTERRUPTIBLE);
++      for (;;) {
++              spin_lock_irq(&ttlio_lock);
++              data = *ttlio_base;
++              if (ttlio_irq_arrived) {
++                      ttlio_irq_arrived = 0;
++                      break;
++              }
++              spin_unlock_irq(&ttlio_lock);
++
++              if (signal_pending(current)) {
++                      retval = -ERESTARTSYS;
++                      goto out;
++              }
++              schedule();
++      }
++
++      spin_unlock_irq(&ttlio_lock);
++      retval = put_user(data, (unsigned short *)buf);
++      if (!retval)
++              retval = sizeof(unsigned short);
++
++out:
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&ttlio_wait, &wait);
++      return retval;
++}
++
++static ssize_t ttlio_write(struct file *file,
++                         const char *buf, size_t count, loff_t *ppos)
++{
++      unsigned short content;
++
++      if (count < sizeof(unsigned short))
++              return -EINVAL;
++      
++      if (copy_from_user (&content, buf, sizeof(unsigned short)))
++              return -EFAULT;
++
++      ttlio_shadow = content;
++      *ttlio_base = ttlio_shadow;
++
++      *ppos += sizeof(unsigned short);
++
++      return sizeof(unsigned short);
++}
++
++static int ttlio_open(struct inode *inode, struct file *file)
++{
++      ttlio_irq_arrived = 0;
++      return 0;
++}
++
++static int ttlio_fasync(int fd, struct file *filp, int on)
++{
++      return fasync_helper(fd, filp, on, &ttlio_async_queue);
++}
++
++static unsigned int ttlio_poll(struct file *file, poll_table *wait)
++{
++      poll_wait(file, &ttlio_wait, wait);
++      return ttlio_irq_arrived ? 0 : POLLIN | POLLRDNORM;
++}
++
++static loff_t ttlio_llseek(struct file *file, loff_t offset, int origin)
++{
++      return -ESPIPE;
++}
++
++/*
++ *    The various file operations we support.
++ */
++
++static struct file_operations ttlio_fops = {
++      owner:          THIS_MODULE,
++      llseek:         ttlio_llseek,
++      read:           ttlio_read,
++      poll:           ttlio_poll,
++      write:          ttlio_write,
++      ioctl:          ttlio_ioctl,
++      open:           ttlio_open,
++      fasync:         ttlio_fasync,
++};
++
++static struct miscdevice ttlio_dev = {
++      TTLIO_MINOR,
++      "ttlio",
++      &ttlio_fops
++};
++
++static int __init ttlio_init(void)
++{
++      printk(KERN_INFO "MT6N TTL-I/O driver (release %s)\n",
++                      TTLIO_VERSION);
++      
++      misc_register(&ttlio_dev);
++      create_proc_read_entry ("driver/ttlio", 0, 0, ttlio_read_proc, NULL);
++
++      set_GPIO_IRQ_edge(GPIO_TTLIO_IRQ, GPIO_FALLING_EDGE);
++      if (request_irq(TTLIO_IRQ, ttlio_interrupt, SA_INTERRUPT, "ttlio irq", NULL)) {
++              printk(KERN_ERR "ttlio: irq %d already in use\n", TTLIO_IRQ);
++              return 1;
++      }
++      return 0;
++}
++
++static void __exit ttlio_exit(void)
++{
++      free_irq(TTLIO_IRQ, NULL);
++      remove_proc_entry ("driver/ttlio", NULL);
++      misc_deregister(&ttlio_dev);
++}
++
++module_init(ttlio_init);
++module_exit(ttlio_exit);
++EXPORT_NO_SYMBOLS;
++
++/*
++ *    Info exported via "/proc/driver/ttlio".
++ */
++
++static int ttlio_proc_output(char *buf)
++{
++      char *p;
++      unsigned short val;
++      int i;
++
++      p = buf;
++
++      p += sprintf(p, "input : ");
++      /* write the state of the input lines */
++      val = *ttlio_base;
++      for (i = 0; i < 8*sizeof(unsigned short); i++) {
++              *p++ = (val & 1) ? '1' : '0';
++              val >>= 1;
++      }
++      *p = 0;
++      p += sprintf(p, "\noutput: ");
++      /* write the state of the output lines */
++      val = ttlio_shadow;
++      for (i = 0; i < 8*sizeof(unsigned short); i++) {
++              *p++ = (val & 1) ? '1' : '0';
++              val >>= 1;
++      }
++      *p = 0;
++      p += sprintf(p, "\n");
++
++      return  p - buf;
++}
++
++static int ttlio_read_proc(char *page, char **start, off_t off,
++                           int count, int *eof, void *data)
++{
++        int len = ttlio_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;
++}
++
++MODULE_AUTHOR("Luc De Cock");
++MODULE_DESCRIPTION("MT6N TTL-I/O driver");
++MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/char/sa1100-rtc.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/char/sa1100-rtc.c
+@@ -1,5 +1,6 @@
+ /*
+  *    Real Time Clock interface for Linux on StrongARM SA1100
++ *    and XScale PXA250/210.
+  *
+  *    Copyright (c) 2000 Nils Faerber
+  *
+@@ -470,5 +471,5 @@
+ module_exit(rtc_exit);
+ MODULE_AUTHOR("Nils Faerber <nils@@kernelconcepts.de>");
+-MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)");
++MODULE_DESCRIPTION("SA1100/PXA Realtime Clock Driver (RTC)");
+ EXPORT_NO_SYMBOLS;
+--- linux-2.4.27/drivers/char/sa1100_wdt.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/char/sa1100_wdt.c
+@@ -1,5 +1,5 @@
+ /*
+- *    Watchdog driver for the SA11x0
++ *    Watchdog driver for the SA11x0/PXA
+  *
+  *      (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+  *          Based on SoftDog driver by Alan Cox <alan@redhat.com>
+@@ -35,13 +35,20 @@
+ #define TIMER_MARGIN  60              /* (secs) Default is 1 minute */
+-static int sa1100_margin = TIMER_MARGIN;      /* in seconds */
++static int timer_margin = TIMER_MARGIN;       /* in seconds */
+ static int sa1100wdt_users;
+ static int pre_margin;
+ #ifdef MODULE
+-MODULE_PARM(sa1100_margin,"i");
++MODULE_PARM(timer_margin,"i");
+ #endif
++static void sa1100dog_ping( void)
++{
++      /* reload counter with (new) margin */
++      pre_margin=3686400 * timer_margin;
++      OSMR3 = OSCR + pre_margin;
++}
++
+ /*
+  *    Allow only one person to hold it open
+  */
+@@ -51,9 +58,7 @@
+       if(test_and_set_bit(1,&sa1100wdt_users))
+               return -EBUSY;
+       MOD_INC_USE_COUNT;
+-      /* Activate SA1100 Watchdog timer */
+-      pre_margin=3686400 * sa1100_margin;
+-      OSMR3 = OSCR + pre_margin;
++      sa1100dog_ping();
+       OSSR = OSSR_M3;
+       OWER = OWER_WME;
+       OIER |= OIER_E3;
+@@ -93,8 +98,11 @@
+       unsigned int cmd, unsigned long arg)
+ {
+       static struct watchdog_info ident = {
+-              identity: "SA1100 Watchdog",
++              identity: "PXA/SA1100 Watchdog",
++              options: WDIOF_SETTIMEOUT,
++              firmware_version: 0,
+       };
++      int new_margin;
+       switch(cmd){
+       default:
+@@ -108,6 +116,16 @@
+       case WDIOC_KEEPALIVE:
+               OSMR3 = OSCR + pre_margin;
+               return 0;
++      case WDIOC_SETTIMEOUT:
++              if (get_user(new_margin, (int *)arg))
++                      return -EFAULT;
++              if (new_margin < 1)
++                      return -EINVAL;
++              timer_margin = new_margin;
++              sa1100dog_ping();
++              /* Fall */
++      case WDIOC_GETTIMEOUT:
++              return put_user(timer_margin, (int *)arg);
+       }
+ }
+@@ -123,7 +141,11 @@
+ static struct miscdevice sa1100dog_miscdev=
+ {
+       WATCHDOG_MINOR,
+-      "SA1100 watchdog",
++#if defined(CONFIG_SA1100_WATCHDOG)
++      "SA1100_watchdog",
++#elif defined(CONFIG_PXA_WATCHDOG)
++      "PXA_watchdog",
++#endif
+       &sa1100dog_fops
+ };
+@@ -136,7 +158,7 @@
+       if (ret)
+               return ret;
+-      printk("SA1100 Watchdog Timer: timer margin %d sec\n", sa1100_margin);
++      printk("SA1100/PXA Watchdog Timer: timer margin %d sec\n", timer_margin);
+       return 0;
+ }
+--- linux-2.4.27/drivers/char/serial.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/char/serial.c
+@@ -133,6 +133,16 @@
+ #endif
+ #endif
++#ifdef CONFIG_ARCH_PXA
++#define pxa_port(x) ((x) == PORT_PXA)
++#define pxa_buggy_port(x) ({ \
++      int cpu_ver; asm("mrc%? p15, 0, %0, c0, c0" : "=r" (cpu_ver)); \
++      ((x) == PORT_PXA && (cpu_ver & ~1) == 0x69052100); })
++#else
++#define pxa_port(x) (0)
++#define pxa_buggy_port(x) (0)
++#endif
++
+ /* Set of debugging defines */
+ #undef SERIAL_DEBUG_INTR
+@@ -311,6 +321,7 @@
+       { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO |
+                 UART_STARTECH },
+       { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, 
++      { "PXA UART", 32, UART_CLEAR_FIFO | UART_USE_FIFO },
+       { 0, 0}
+ };
+@@ -424,6 +435,9 @@
+       case SERIAL_IO_MEM:
+               return readb((unsigned long) info->iomem_base +
+                            (offset<<info->iomem_reg_shift));
++      case SERIAL_IO_MEM32:
++              return readl((unsigned long) info->iomem_base +
++                           (offset<<info->iomem_reg_shift));
+       default:
+               return inb(info->port + offset);
+       }
+@@ -443,6 +457,10 @@
+               writeb(value, (unsigned long) info->iomem_base +
+                             (offset<<info->iomem_reg_shift));
+               break;
++      case SERIAL_IO_MEM32:
++              writel(value, (unsigned long) info->iomem_base +
++                            (offset<<info->iomem_reg_shift));
++              break;
+       default:
+               outb(value, info->port+offset);
+       }
+@@ -1306,6 +1324,16 @@
+       }
+ #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;
++                      case (long)&STUART: CKEN |= CKEN5_STUART; break;
++              }
++      }
++#endif
++
+       /*
+        * Clear the FIFO buffers and disable them
+        * (they will be reenabled in change_speed())
+@@ -1403,6 +1431,8 @@
+       {
+               if (state->irq != 0)
+                       info->MCR |= UART_MCR_OUT2;
++              if (pxa_buggy_port(state->type) && state->irq != 0)
++                      info->MCR ^= UART_MCR_OUT2;
+       }
+       info->MCR |= ALPHA_KLUDGE_MCR;          /* Don't ask */
+       serial_outp(info, UART_MCR, info->MCR);
+@@ -1411,6 +1441,8 @@
+        * Finally, enable interrupts
+        */
+       info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
++      if (pxa_port(state->type))
++              info->IER |= UART_IER_UUE | UART_IER_RTOIE;
+       serial_outp(info, UART_IER, info->IER); /* enable interrupts */
+       
+ #ifdef CONFIG_SERIAL_MANY_PORTS
+@@ -1542,6 +1574,8 @@
+       } else
+ #endif
+               info->MCR &= ~UART_MCR_OUT2;
++              if (pxa_buggy_port(state->type))
++                      info->MCR ^= UART_MCR_OUT2;
+       info->MCR |= ALPHA_KLUDGE_MCR;          /* Don't ask */
+       
+       /* disable break condition */
+@@ -1567,6 +1601,20 @@
+               state->baud_base = SERIAL_RSA_BAUD_BASE_LO;
+ #endif
+       
++#ifdef CONFIG_ARCH_PXA
++      if (state->type == PORT_PXA
++#ifdef CONFIG_SERIAL_CONSOLE
++          && sercons.index != info->line
++#endif
++         ) {
++              switch ((long)state->iomem_base) {
++                      case (long)&FFUART: CKEN &= ~CKEN6_FFUART; break;
++                      case (long)&BTUART: CKEN &= ~CKEN7_BTUART; break;
++                      case (long)&STUART: CKEN &= ~CKEN5_STUART; break;
++              }
++      }
++#endif
++
+       (void)serial_in(info, UART_RX);    /* read data port to reset things */
+       
+@@ -1857,6 +1905,8 @@
+       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);
+ }
+@@ -1933,6 +1983,11 @@
+           && !(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;
+ }
+@@ -1990,6 +2045,8 @@
+               /* 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);
+       }
+ }
+@@ -5517,7 +5574,6 @@
+       for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+               state->magic = SSTATE_MAGIC;
+               state->line = i;
+-              state->type = PORT_UNKNOWN;
+               state->custom_divisor = 0;
+               state->close_delay = 5*HZ/10;
+               state->closing_wait = 30*HZ;
+@@ -5531,14 +5587,18 @@
+               state->irq = irq_cannonicalize(state->irq);
+               if (state->hub6)
+                       state->io_type = SERIAL_IO_HUB6;
+-              if (state->port && check_region(state->port,8))
++              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)
++              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)
+@@ -5858,6 +5918,8 @@
+        */
+       ier = serial_in(info, UART_IER);
+       serial_out(info, UART_IER, 0x00);
++      if (pxa_port(info->state->type))
++              serial_out(info, UART_IER, UART_IER_UUE);
+       /*
+        *      Now, do each character
+@@ -6009,6 +6071,8 @@
+       serial_out(info, UART_DLM, quot >> 8);          /* MS of divisor */
+       serial_out(info, UART_LCR, cval);               /* reset DLAB */
+       serial_out(info, UART_IER, 0);
++      if (pxa_port(info->state->type))
++              serial_out(info, UART_IER, UART_IER_UUE);
+       serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
+       /*
+--- linux-2.4.27/drivers/i2c/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/i2c/Config.in
+@@ -54,6 +54,11 @@
+       fi
+    fi
++   if [ "$CONFIG_ARCH_PXA" = "y" ]; then
++      dep_tristate 'PXA I2C Algorithm' CONFIG_I2C_PXA_ALGO $CONFIG_I2C
++      dep_tristate 'PXA I2C Adapter'   CONFIG_I2C_PXA_ADAP $CONFIG_I2C_PXA_ALGO
++   fi
++
+    if [ "$CONFIG_ALL_PPC" = "y" ] ; then
+       dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C
+    fi
+--- linux-2.4.27/drivers/i2c/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/i2c/Makefile
+@@ -6,7 +6,7 @@
+ export-objs   := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o \
+                  i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \
+-                 i2c-proc.o
++                 i2c-proc.o i2c-algo-pxa.o
+ # Init order: core, chardev, bit adapters, pcf adapters
+@@ -34,6 +34,10 @@
+ obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o i2c-sibyte.o
+ obj-$(CONFIG_I2C_MAX1617)     += i2c-max1617.o
+ obj-$(CONFIG_I2C_ALGO_SGI)    += i2c-algo-sgi.o
++# PXA adapters
++obj-$(CONFIG_I2C_PXA_ALGO)    += i2c-algo-pxa.o
++obj-$(CONFIG_I2C_PXA_ADAP)    += i2c-adap-pxa.o
++
+ # This is needed for automatic patch generation: sensors code starts here
+ # This is needed for automatic patch generation: sensors code ends here
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-adap-pxa.c
+@@ -0,0 +1,396 @@
++/*
++ *  i2c_adap_pxa.c
++ *
++ *  I2C adapter for the PXA I2C bus access.
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Apr 2002: Initial version [CS]
++ *    Jun 2002: Properly seperated algo/adap [FB]
++ *    Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem]
++ *    Jan 2003: added limited signal handling [Kai-Uwe Bloem]
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-id.h>
++#include <linux/init.h>
++#include <linux/time.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/arch/irqs.h>              /* for IRQ_I2C */
++
++#include "i2c-pxa.h"
++
++/*
++ * Set this to zero to remove all debug statements via dead code elimination.
++ */
++//#define DEBUG       1
++
++#if DEBUG
++static unsigned int i2c_debug = DEBUG;
++#else
++#define i2c_debug     0
++#endif
++
++static int irq = 0;
++static volatile int i2c_pending = 0;             /* interrupt pending when 1 */
++static volatile int bus_error = 0;
++static volatile int tx_finished = 0;
++static volatile int rx_finished = 0;
++
++static wait_queue_head_t i2c_wait;
++static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte);
++
++/* place a byte in the transmit register */
++static void i2c_pxa_write_byte(u8 value)
++{
++      IDBR = value;
++}
++
++/* read byte in the receive register */
++static u8 i2c_pxa_read_byte(void)
++{
++      return (u8) (0xff & IDBR);
++}
++
++static void i2c_pxa_start(void)
++{
++      unsigned long icr = ICR;
++      icr |= ICR_START;
++      icr &= ~(ICR_STOP | ICR_ALDIE | ICR_ACKNAK);
++      ICR = icr;
++
++      bus_error=0;            /* clear any bus_error from previous txfers */
++      tx_finished=0;          /* clear rx and tx interrupts from previous txfers */
++      rx_finished=0;
++      i2c_pending = 0;
++}
++
++static void i2c_pxa_repeat_start(void)
++{
++      unsigned long icr = ICR;
++      icr |= ICR_START;
++      icr &= ~(ICR_STOP | ICR_ALDIE);
++      ICR = icr;
++
++      bus_error=0;            /* clear any bus_error from previous txfers */
++      tx_finished=0;          /* clear rx and tx interrupts from previous txfers */
++      rx_finished=0;
++      i2c_pending = 0;
++}
++
++static void i2c_pxa_stop(void)
++{
++      unsigned long icr = ICR;
++      icr |= ICR_STOP;
++      icr &= ~(ICR_START);
++      ICR = icr;
++}
++
++static void i2c_pxa_midbyte(void)
++{
++      unsigned long icr = ICR;
++      icr &= ~(ICR_START | ICR_STOP);
++      ICR = icr;
++}
++
++static void i2c_pxa_abort(void)
++{
++      unsigned long timeout = jiffies + HZ/4;
++
++#ifdef PXA_ABORT_MA
++      while ((long)(timeout - jiffies) > 0 && (ICR & ICR_TB)) {
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(1);
++      }
++
++      ICR |= ICR_MA;
++      udelay(100);
++#else
++      while ((long)(timeout - jiffies) > 0 && (IBMR & 0x1) == 0) {
++              i2c_pxa_transfer( 1, I2C_RECEIVE, 1);
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(1);
++      }
++#endif
++      ICR &= ~(ICR_MA | ICR_START | ICR_STOP);
++}
++
++static int i2c_pxa_wait_bus_not_busy( void)
++{
++      int timeout = DEF_TIMEOUT;
++
++      while (timeout-- && (ISR & ISR_IBB)) {
++              udelay(100); /* wait for 100 us */
++      }
++
++      return (timeout<=0);
++}
++
++static void i2c_pxa_wait_for_ite(void){
++      unsigned long flags;
++      if (irq > 0) {
++              save_flags_cli(flags);
++              if (i2c_pending == 0) {
++                      interruptible_sleep_on_timeout(&i2c_wait, I2C_SLEEP_TIMEOUT );
++              }
++              i2c_pending = 0;
++              restore_flags(flags);
++      } else {
++              udelay(100);
++      }
++}
++
++static int i2c_pxa_wait_for_int( int wait_type)
++{
++      int timeout = DEF_TIMEOUT;
++#ifdef DEBUG
++      if (bus_error)
++              printk(KERN_INFO"i2c_pxa_wait_for_int: Bus error on enter\n");
++      if (rx_finished)
++              printk(KERN_INFO"i2c_pxa_wait_for_int: Receive interrupt on enter\n");
++      if (tx_finished)
++              printk(KERN_INFO"i2c_pxa_wait_for_int: Transmit interrupt on enter\n");
++#endif
++
++      if (wait_type == I2C_RECEIVE){         /* wait on receive */
++
++              do {
++                      i2c_pxa_wait_for_ite();
++              } while (!(rx_finished) && timeout-- && !signal_pending(current));
++
++#ifdef DEBUG
++              if (timeout<0){
++                      if (tx_finished)
++                              printk("Error: i2c-algo-pxa.o: received a tx"
++                                              " interrupt while waiting on a rx in wait_for_int");
++              }
++#endif
++      } else {                  /* wait on transmit */
++
++              do {
++                      i2c_pxa_wait_for_ite();
++              } while (!(tx_finished) && timeout-- && !signal_pending(current));
++
++#ifdef DEBUG
++              if (timeout<0){
++                      if (rx_finished)
++                              printk("Error: i2c-algo-pxa.o: received a rx"
++                                      " interrupt while waiting on a tx in wait_for_int");
++              }
++#endif
++      }
++
++      udelay(ACK_DELAY);      /* this is needed for the bus error */
++
++      tx_finished=0;
++      rx_finished=0;
++
++      if (bus_error){
++              bus_error=0;
++              if( i2c_debug > 2)printk("wait_for_int: error - no ack.\n");
++              return BUS_ERROR;
++      }
++
++      if (signal_pending(current)) {
++              return (-ERESTARTSYS);
++      } else if (timeout < 0) {
++              if( i2c_debug > 2)printk("wait_for_int: timeout.\n");
++              return(-EIO);
++      } else
++              return(0);
++}
++
++static void i2c_pxa_transfer( int lastbyte, int receive, int midbyte)
++{
++      if( lastbyte)
++      {
++              if( receive==I2C_RECEIVE) ICR |= ICR_ACKNAK;
++              i2c_pxa_stop();
++      }
++      else if( midbyte)
++      {
++              i2c_pxa_midbyte();
++      }
++      ICR |= ICR_TB;
++}
++
++static void i2c_pxa_reset( void)
++{
++#ifdef DEBUG
++      printk("Resetting I2C Controller Unit\n");
++#endif
++
++      /* abort any transfer currently under way */
++      i2c_pxa_abort();
++
++      /* reset according to 9.8 */
++      ICR = ICR_UR;
++      ISR = I2C_ISR_INIT;
++      ICR &= ~ICR_UR;
++
++      /* set the global I2C clock on */
++      CKEN |= CKEN14_I2C;
++
++      /* set our slave address */
++      ISAR = I2C_PXA_SLAVE_ADDR;
++
++      /* set control register values */
++      ICR = I2C_ICR_INIT;
++
++      /* clear any leftover states from prior transmissions */
++      i2c_pending = rx_finished = tx_finished = bus_error = 0;
++
++      /* enable unit */
++      ICR |= ICR_IUE;
++      udelay(100);
++}
++
++static void i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs)
++{
++      int status, wakeup = 0;
++      status = (ISR);
++
++      if (status & ISR_BED){
++              (ISR) |= ISR_BED;
++              bus_error=ISR_BED;
++              wakeup = 1;
++      }
++      if (status & ISR_ITE){
++              (ISR) |= ISR_ITE;
++              tx_finished=ISR_ITE;
++              wakeup = 1;
++      }
++      if (status & ISR_IRF){
++              (ISR) |= ISR_IRF;
++              rx_finished=ISR_IRF;
++              wakeup = 1;
++      }
++      if (wakeup) {
++              i2c_pending = 1;
++              wake_up_interruptible(&i2c_wait);
++      }
++}
++
++static int i2c_pxa_resource_init( void)
++{
++      init_waitqueue_head(&i2c_wait);
++
++      if (request_irq(IRQ_I2C, &i2c_pxa_handler, SA_INTERRUPT, "I2C_PXA", 0) < 0) {
++              irq = 0;
++              if( i2c_debug)
++                      printk(KERN_INFO "I2C: Failed to register I2C irq %i\n", IRQ_I2C);
++              return -ENODEV;
++      }else{
++              irq = IRQ_I2C;
++              enable_irq(irq);
++      }
++      return 0;
++}
++
++static void i2c_pxa_resource_release( void)
++{
++      if( irq > 0)
++      {
++              disable_irq(irq);
++              free_irq(irq,0);
++              irq=0;
++      }
++}
++
++static void i2c_pxa_inc_use(struct i2c_adapter *adap)
++{
++#ifdef MODULE
++      MOD_INC_USE_COUNT;
++#endif
++}
++
++static void i2c_pxa_dec_use(struct i2c_adapter *adap)
++{
++#ifdef MODULE
++      MOD_DEC_USE_COUNT;
++#endif
++}
++
++static int i2c_pxa_client_register(struct i2c_client *client)
++{
++      return 0;
++}
++
++static int i2c_pxa_client_unregister(struct i2c_client *client)
++{
++      return 0;
++}
++
++static struct i2c_algo_pxa_data i2c_pxa_data = {
++      write_byte:             i2c_pxa_write_byte,
++      read_byte:              i2c_pxa_read_byte,
++
++      start:                  i2c_pxa_start,
++      repeat_start:           i2c_pxa_repeat_start,
++      stop:                   i2c_pxa_stop,
++      abort:                  i2c_pxa_abort,
++
++      wait_bus_not_busy:      i2c_pxa_wait_bus_not_busy,
++      wait_for_interrupt:     i2c_pxa_wait_for_int,
++      transfer:               i2c_pxa_transfer,
++      reset:                  i2c_pxa_reset,
++
++      udelay:                 10,
++      timeout:                DEF_TIMEOUT,
++};
++
++static struct i2c_adapter i2c_pxa_ops = {
++      name:                   "PXA-I2C-Adapter",
++      id:                     I2C_ALGO_PXA,
++      algo_data:              &i2c_pxa_data,
++      inc_use:                i2c_pxa_inc_use,
++      dec_use:                i2c_pxa_dec_use,
++      client_register:        i2c_pxa_client_register,
++      client_unregister:      i2c_pxa_client_unregister,
++      retries:                2,
++};
++
++extern int i2c_pxa_add_bus(struct i2c_adapter *);
++extern int i2c_pxa_del_bus(struct i2c_adapter *);
++
++static int __init i2c_adap_pxa_init(void)
++{
++      if( i2c_pxa_resource_init() == 0) {
++
++              if (i2c_pxa_add_bus(&i2c_pxa_ops) < 0) {
++                      i2c_pxa_resource_release();
++                      printk(KERN_INFO "I2C: Failed to add bus\n");
++                      return -ENODEV;
++              }
++      } else {
++              return -ENODEV;
++      }
++
++      printk(KERN_INFO "I2C: Successfully added bus\n");
++
++      return 0;
++}
++
++static void i2c_adap_pxa_exit(void)
++{
++      i2c_pxa_del_bus( &i2c_pxa_ops);
++      i2c_pxa_resource_release();
++
++      printk(KERN_INFO "I2C: Successfully removed bus\n");
++}
++
++module_init(i2c_adap_pxa_init);
++module_exit(i2c_adap_pxa_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-algo-pxa.c
+@@ -0,0 +1,376 @@
++/*
++ *  i2c-algo-pxa.c
++ *
++ *  I2C algorithm for the PXA I2C bus access.
++ *  Byte driven algorithm similar to pcf.
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Apr 2002: Initial version [CS]
++ *    Jun 2002: Properly seperated algo/adap [FB]
++ *    Jan 2003: added limited signal handling [Kai-Uwe Bloem]
++ *    Jan 2003: allow SMBUS_QUICK as valid msg [FB]
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/module.h>
++
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/i2c.h>          /* struct i2c_msg and others */
++#include <linux/i2c-id.h>
++
++#include "i2c-pxa.h"
++
++/*
++ * Set this to zero to remove all the debug statements via dead code elimination.
++ */
++//#define DEBUG               1
++
++#if DEBUG
++static unsigned int i2c_debug = DEBUG;
++#else
++#define i2c_debug     0
++#endif
++
++static int pxa_scan = 1;
++
++static int i2c_pxa_valid_messages( struct i2c_msg msgs[], int num)
++{
++      int i;
++      if (num < 1 || num > MAX_MESSAGES){
++              if( i2c_debug)
++                      printk(KERN_INFO "Invalid number of messages (max=%d, num=%d)\n",
++                              MAX_MESSAGES, num);
++              return -EINVAL;
++      }
++
++      /* check consistency of our messages */
++      for (i=0;i<num;i++){
++              if (&msgs[i]==NULL){
++                      if( i2c_debug) printk(KERN_INFO "Msgs is NULL\n");
++                      return -EINVAL;
++              } else {
++                      if (msgs[i].len < 0 || msgs[i].buf == NULL){
++                              if( i2c_debug)printk(KERN_INFO "Length is less than zero");
++                              return -EINVAL;
++                      }
++              }
++      }
++
++      return 1;
++}
++
++static int i2c_pxa_readbytes(struct i2c_adapter *i2c_adap, char *buf,
++                      int count, int last)
++{
++
++      int i, timeout=0;
++      struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
++
++      /* increment number of bytes to read by one -- read dummy byte */
++      for (i = 0; i <= count; i++) {
++              if (i!=0){
++                      /* set ACK to NAK for last received byte ICR[ACKNAK] = 1
++                         only if not a repeated start */
++
++                      if ((i == count) && last) {
++                              adap->transfer( last, I2C_RECEIVE, 0);
++                      }else{
++                              adap->transfer( 0, I2C_RECEIVE, 1);
++                      }
++
++                      timeout = adap->wait_for_interrupt(I2C_RECEIVE);
++
++#ifdef DEBUG
++                      if (timeout==BUS_ERROR){
++                              printk(KERN_INFO "i2c_pxa_readbytes: bus error -> forcing reset\n");
++                              adap->reset();
++                              return I2C_RETRY;
++                      } else
++#endif
++                      if (timeout == -ERESTARTSYS) {
++                              adap->abort();
++                              return timeout;
++                      } else
++                      if (timeout){
++#ifdef DEBUG
++                              printk(KERN_INFO "i2c_pxa_readbytes: timeout -> forcing reset\n");
++#endif
++                              adap->reset();
++                              return I2C_RETRY;
++                      }
++
++              }
++
++              if (i) {
++                      buf[i - 1] = adap->read_byte();
++              } else {
++                      adap->read_byte(); /* dummy read */
++              }
++      }
++      return (i - 1);
++}
++
++static int i2c_pxa_sendbytes(struct i2c_adapter *i2c_adap, const char *buf,
++                         int count, int last)
++{
++
++      struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
++      int wrcount, timeout;
++
++      for (wrcount=0; wrcount<count; ++wrcount) {
++
++              adap->write_byte(buf[wrcount]);
++              if ((wrcount==(count-1)) && last) {
++                      adap->transfer( last, I2C_TRANSMIT, 0);
++              }else{
++                      adap->transfer( 0, I2C_TRANSMIT, 1);
++              }
++
++              timeout = adap->wait_for_interrupt(I2C_TRANSMIT);
++
++#ifdef DEBUG
++              if (timeout==BUS_ERROR) {
++                      printk(KERN_INFO "i2c_pxa_sendbytes: bus error -> forcing reset.\n");
++                      adap->reset();
++                      return I2C_RETRY;
++              } else
++#endif
++              if (timeout == -ERESTARTSYS) {
++                      adap->abort();
++                      return timeout;
++              } else
++              if (timeout) {
++#ifdef DEBUG
++                      printk(KERN_INFO "i2c_pxa_sendbytes: timeout -> forcing reset\n");
++#endif
++                      adap->reset();
++                      return I2C_RETRY;
++              }
++      }
++      return (wrcount);
++}
++
++
++static inline int i2c_pxa_set_ctrl_byte(struct i2c_algo_pxa_data * adap, struct i2c_msg *msg)
++{
++      u16 flags = msg->flags;
++      u8 addr;
++      addr = (u8) ( (0x7f & msg->addr) << 1 );
++      if (flags & I2C_M_RD )
++              addr |= 1;
++      if (flags & I2C_M_REV_DIR_ADDR )
++              addr ^= 1;
++      adap->write_byte(addr);
++      return 0;
++}
++
++static int i2c_pxa_do_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
++{
++      struct i2c_algo_pxa_data * adap;
++      struct i2c_msg *pmsg=NULL;
++      int i;
++      int ret=0, timeout;
++
++      adap = i2c_adap->algo_data;
++
++      timeout = adap->wait_bus_not_busy();
++
++      if (timeout) {
++              return I2C_RETRY;
++      }
++
++      for (i = 0;ret >= 0 && i < num; i++) {
++              int last = i + 1 == num;
++              pmsg = &msgs[i];
++
++              ret = i2c_pxa_set_ctrl_byte(adap,pmsg);
++
++              /* Send START */
++              if (i == 0) {
++                      adap->start();
++              }else{
++                      adap->repeat_start();
++              }
++
++              adap->transfer(0, I2C_TRANSMIT, 0);
++
++              /* Wait for ITE (transmit empty) */
++              timeout = adap->wait_for_interrupt(I2C_TRANSMIT);
++
++#ifdef DEBUG
++              /* Check for ACK (bus error) */
++              if (timeout==BUS_ERROR){
++                      printk(KERN_INFO "i2c_pxa_do_xfer: bus error -> forcing reset\n");
++                      adap->reset();
++                      return I2C_RETRY;
++              } else
++#endif
++              if (timeout == -ERESTARTSYS) {
++                      adap->abort();
++                      return timeout;
++              } else
++              if (timeout) {
++#ifdef DEBUG
++                      printk(KERN_INFO "i2c_pxa_do_xfer: timeout -> forcing reset\n");
++#endif
++                      adap->reset();
++                      return I2C_RETRY;
++              }
++/* FIXME: handle arbitration... */
++#if 0
++              /* Check for bus arbitration loss */
++              if (adap->arbitration_loss()){
++                      printk("Arbitration loss detected \n");
++                      adap->reset();
++                      return I2C_RETRY;
++              }
++#endif
++
++              /* Read */
++              if (pmsg->flags & I2C_M_RD) {
++                      /* read bytes into buffer*/
++                      ret = i2c_pxa_readbytes(i2c_adap, pmsg->buf, pmsg->len, last);
++#if DEBUG > 2
++                      if (ret != pmsg->len) {
++                              printk(KERN_INFO"i2c_pxa_do_xfer: read %d/%d bytes.\n",
++                                      ret, pmsg->len);
++                      } else {
++                              printk(KERN_INFO"i2c_pxa_do_xfer: read %d bytes.\n",ret);
++                      }
++#endif
++              } else { /* Write */
++                      ret = i2c_pxa_sendbytes(i2c_adap, pmsg->buf, pmsg->len, last);
++#if DEBUG > 2
++                      if (ret != pmsg->len) {
++                              printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d/%d bytes.\n",
++                                      ret, pmsg->len);
++                      } else {
++                              printk(KERN_INFO"i2c_pxa_do_xfer: wrote %d bytes.\n",ret);
++                      }
++#endif
++              }
++      }
++
++      if (ret<0){
++              return ret;
++      }else{
++              return i;
++      }
++}
++
++static int i2c_pxa_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
++{
++      int retval = i2c_pxa_valid_messages( msgs, num);
++      if( retval > 0)
++      {
++              int i;
++              for (i=i2c_adap->retries; i>=0; i--){
++                      int retval = i2c_pxa_do_xfer(i2c_adap,msgs,num);
++                      if (retval!=I2C_RETRY){
++                              return retval;
++                      }
++                      if( i2c_debug)printk(KERN_INFO"Retrying transmission \n");
++                      udelay(100);
++              }
++              if( i2c_debug)printk(KERN_INFO"Retried %i times\n",i2c_adap->retries);
++              return -EREMOTEIO;
++
++      }
++      return retval;
++}
++
++struct i2c_algorithm i2c_pxa_algorithm  = {
++      name:                   "PXA-I2C-Algorithm",
++      id:                     I2C_ALGO_PXA,
++      master_xfer:            i2c_pxa_xfer,
++      smbus_xfer:             NULL,
++      slave_send:             NULL,
++      slave_recv:             NULL,
++      algo_control:           NULL,
++};
++
++/*
++ * registering functions to load algorithms at runtime
++ */
++int i2c_pxa_add_bus(struct i2c_adapter *i2c_adap)
++{
++      struct i2c_algo_pxa_data *adap = i2c_adap->algo_data;
++
++      printk(KERN_INFO"I2C: Adding %s.\n", i2c_adap->name);
++
++      i2c_adap->algo = &i2c_pxa_algorithm;
++
++      MOD_INC_USE_COUNT;
++
++      /* register new adapter to i2c module... */
++      i2c_add_adapter(i2c_adap);
++
++      adap->reset();
++
++      /* scan bus */
++      if (pxa_scan) {
++              int i;
++              printk(KERN_INFO "I2C: Scanning bus ");
++              for (i = 0x02; i < 0xff; i+=2) {
++                      if( i==(I2C_PXA_SLAVE_ADDR<<1)) continue;
++
++                      if (adap->wait_bus_not_busy()) {
++                              printk(KERN_INFO "I2C: scanning bus %s - TIMEOUTed.\n",
++                                              i2c_adap->name);
++                              return -EIO;
++                      }
++                      adap->write_byte(i);
++                      adap->start();
++                      adap->transfer(0, I2C_TRANSMIT, 0);
++
++                      if ((adap->wait_for_interrupt(I2C_TRANSMIT) != BUS_ERROR)) {
++                              printk("(%02x)",i>>1);
++                              adap->abort();
++                      } else {
++//                            printk(".");
++                              adap->stop();
++                      }
++                      udelay(adap->udelay);
++              }
++              printk("\n");
++      }
++      return 0;
++}
++
++int i2c_pxa_del_bus(struct i2c_adapter *i2c_adap)
++{
++      int res;
++      if ((res = i2c_del_adapter(i2c_adap)) < 0)
++              return res;
++
++      MOD_DEC_USE_COUNT;
++
++      printk(KERN_INFO "I2C: Removing %s.\n", i2c_adap->name);
++
++      return 0;
++}
++
++static int __init i2c_algo_pxa_init (void)
++{
++      printk(KERN_INFO "I2C: PXA algorithm module loaded.\n");
++      return 0;
++}
++
++EXPORT_SYMBOL(i2c_pxa_add_bus);
++EXPORT_SYMBOL(i2c_pxa_del_bus);
++
++MODULE_PARM(pxa_scan, "i");
++MODULE_PARM_DESC(pxa_scan, "Scan for active chips on the bus");
++
++MODULE_AUTHOR("Intrinsyc Software Inc.");
++MODULE_LICENSE("GPL");
++
++module_init(i2c_algo_pxa_init);
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-pxa.h
+@@ -0,0 +1,76 @@
++/*
++ *  i2c_pxa.h
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ */
++#ifndef _I2C_PXA_H_
++#define _I2C_PXA_H_
++
++struct i2c_algo_pxa_data
++{
++        void (*write_byte) (u8 value);
++        u8   (*read_byte) (void);
++        void (*start) (void);
++        void (*repeat_start) (void);
++        void (*stop) (void);
++        void (*abort) (void);
++        int  (*wait_bus_not_busy) (void);
++        int  (*wait_for_interrupt) (int wait_type);
++        void (*transfer) (int lastbyte, int receive, int midbyte);
++        void (*reset) (void);
++
++      int udelay;
++      int timeout;
++};
++
++#define DEF_TIMEOUT             3
++#define BUS_ERROR               (-EREMOTEIO)
++#define ACK_DELAY               0       /* time to delay before checking bus error */
++#define MAX_MESSAGES            65536   /* maximum number of messages to send */
++
++#define I2C_SLEEP_TIMEOUT       2       /* time to sleep for on i2c transactions */
++#define I2C_RETRY               (-2000) /* an error has occurred retry transmit */
++#define I2C_TRANSMIT          1
++#define I2C_RECEIVE           0
++#define I2C_PXA_SLAVE_ADDR      0x1    /* slave pxa unit address */
++#define I2C_ICR_INIT            (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */
++/* ICR initialize bit values 
++*                       
++*  15. FM       0 (100 Khz operation)
++*  14. UR       0 (No unit reset)
++*  13. SADIE    0 (Disables the unit from interrupting on slave addresses 
++*                                       matching its slave address)
++*  12. ALDIE    0 (Disables the unit from interrupt when it loses arbitration 
++*                                       in master mode)
++*  11. SSDIE    0 (Disables interrupts from a slave stop detected, in slave mode)  
++*  10. BEIE     1 (Enable interrupts from detected bus errors, no ACK sent)
++*  9.  IRFIE    1 (Enable interrupts from full buffer received)
++*  8.  ITEIE    1 (Enables the I2C unit to interrupt when transmit buffer empty)
++*  7.  GCD      1 (Disables i2c unit response to general call messages as a slave) 
++*  6.  IUE      0 (Disable unit until we change settings)
++*  5.  SCLE     1 (Enables the i2c clock output for master mode (drives SCL)   
++*  4.  MA       0 (Only send stop with the ICR stop bit)
++*  3.  TB       0 (We are not transmitting a byte initially)
++*  2.  ACKNAK   0 (Send an ACK after the unit receives a byte)
++*  1.  STOP     0 (Do not send a STOP)
++*  0.  START    0 (Do not send a START)
++*
++*/
++
++#define I2C_ISR_INIT            0x7FF  /* status register init */
++/* I2C status register init values 
++ *
++ * 10. BED      1 (Clear bus error detected)
++ * 9.  SAD      1 (Clear slave address detected)
++ * 7.  IRF      1 (Clear IDBR Receive Full)
++ * 6.  ITE      1 (Clear IDBR Transmit Empty)
++ * 5.  ALD      1 (Clear Arbitration Loss Detected)
++ * 4.  SSD      1 (Clear Slave Stop Detected)
++ */
++
++#endif
+--- linux-2.4.27/drivers/misc/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/Config.in
+@@ -13,5 +13,6 @@
+ dep_tristate 'Support for UCB1200 / UCB1300' CONFIG_MCP_UCB1200 $CONFIG_MCP
+ dep_tristate '  Audio / Telephony interface support' CONFIG_MCP_UCB1200_AUDIO $CONFIG_MCP_UCB1200 $CONFIG_SOUND
+ dep_tristate '  Touchscreen interface support' CONFIG_MCP_UCB1200_TS $CONFIG_MCP_UCB1200
++dep_tristate '  UCB1400 Touchscreen support' CONFIG_MCP_UCB1400_TS $CONFIG_ARCH_PXA $CONFIG_SOUND
+ endmenu
+--- linux-2.4.27/drivers/misc/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/Makefile
+@@ -11,13 +11,15 @@
+ O_TARGET := misc.o
+-export-objs                   := mcp-core.o mcp-sa1100.o ucb1x00-core.o
++export-objs                   := mcp-core.o mcp-sa1100.o mcp-pxa.o \
++                                 ucb1x00-core.o 
+-obj-$(CONFIG_MCP)             += mcp-core.o
+-obj-$(CONFIG_MCP_SA1100)      += mcp-sa1100.o
++obj-$(CONFIG_MCP_SA1100)      += mcp-core.o mcp-sa1100.o
+ obj-$(CONFIG_MCP_UCB1200)     += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_AUDIO)       += ucb1x00-audio.o
+ obj-$(CONFIG_MCP_UCB1200_TS)  += ucb1x00-ts.o
++obj-$(CONFIG_MCP_UCB1400_TS)  += mcp-pxa.o ucb1x00-core.o ucb1x00-ts.o
++obj-$(CONFIG_PXA_CERF_PDA)    += cerf_ucb1400gpio.o
+ include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/misc/cerf_ucb1400gpio.c
+@@ -0,0 +1,189 @@
++/*
++ *  cerf_ucb1400gpio.c
++ *
++ *  UCB1400 GPIO control stuff for the cerf.
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Mar 2002: Initial version [FB]
++ *    Jun 2002: Removed ac97 dependency [FB]
++ * 
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++#include "ucb1x00.h"
++
++/*
++ * Set this to zero to remove all the debug statements via
++ * dead code elimination.
++ */
++#define DEBUGGING       0
++
++#if DEBUGGING
++static unsigned int ucb_debug = DEBUGGING;
++#else
++#define ucb_debug       0
++#endif
++
++#define UP    1
++#define DOWN  0
++
++/* -- -- */
++
++void cerf_ucb1400gpio_lcd_enable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      if( ucb_debug > 2) printk( KERN_INFO "Enabling LCD.\n");
++      /* Enable [not] LCD_RESET to enable the LCD display */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_LCD_RESET);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_LCD_RESET, 0);
++
++      /* Enable the Contrast circuit */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_CONT_ENA);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_CONT_ENA, 0);
++}
++
++void cerf_ucb1400gpio_lcd_disable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      if( ucb_debug > 2) printk( KERN_INFO "Disabling LCD.\n");
++      /* Disable the Contrast circuit  */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_CONT_ENA);
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_ENA);
++
++      /* Disable [not] LCD_RESET to enable the LCD display */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_LCD_RESET);
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_LCD_RESET);
++}
++
++void cerf_ucb1400gpio_lcd_contrast_step( int direction)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++        // Assert the chip select and the up modifier
++        ucb1x00_io_set_dir( ucb, 0, 
++                      (UCB1400_GPIO_CONT_CS |
++                         UCB1400_GPIO_CONT_DOWN |
++                         UCB1400_GPIO_CONT_INC));
++
++      if( direction == DOWN)
++      {
++              if( ucb_debug > 3)
++                      printk(KERN_INFO "cerf_ucb1400gpio_lcd_contrast_step: "
++                              "stepping up\n");
++              //goin' up
++              ucb1x00_io_write( ucb, UCB1400_GPIO_CONT_DOWN, 0);
++      }
++      else
++      {
++              if( ucb_debug > 3)
++                      printk(KERN_INFO "cerf_ucb1400gpio_lcd_contrast_step: "
++                              "stepping down\n");
++              //goin' down
++              ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_DOWN);
++      }
++
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_CS);
++
++        // Assert the line up, down then up again
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_INC);
++        udelay(1);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_CONT_INC, 0);
++        udelay(1);
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_INC);
++
++        // Deassert the chip select and the up modifier
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_CONT_DOWN);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_CONT_CS, 0);
++}
++
++/* -- -- */
++
++void cerf_ucb1400gpio_irda_enable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      printk( KERN_INFO "Enabling IRDA.\n");
++      /* Enable IRDA (active low) */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_IRDA_ENABLE);
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_IRDA_ENABLE);
++}
++
++void cerf_ucb1400gpio_irda_disable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      printk( KERN_INFO "Disabling IRDA.\n");
++      /* Disable IRDA (active low) */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_IRDA_ENABLE);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_IRDA_ENABLE, 0);
++}
++
++/* -- -- */
++
++void cerf_ucb1400gpio_bt_enable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      printk( KERN_INFO "Enabling Bluetooth.\n");
++      /* Enable BT (active low) */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_BT_ENABLE);
++      ucb1x00_io_write( ucb, 0, UCB1400_GPIO_BT_ENABLE);
++}
++
++void cerf_ucb1400gpio_bt_disable( void)
++{
++      struct ucb1x00 * ucb = ucb1x00_get();
++      printk( KERN_INFO "Disabling Bluetooth.\n");
++      /* Disable BT (active low) */
++      ucb1x00_io_set_dir( ucb, 0, UCB1400_GPIO_BT_ENABLE);
++      ucb1x00_io_write( ucb, UCB1400_GPIO_BT_ENABLE, 0);
++}
++
++/* -- -- */
++
++/* -- Enable Bluetooth and IRDA automatically via pseudo module -- */
++#if defined(CONFIG_BLUEZ) || defined(CONFIG_IRDA)
++static int __init cerf_ucb1400gpio_module_init (void)
++{
++#ifdef CONFIG_BLUEZ
++        cerf_ucb1400gpio_bt_enable();
++#endif
++
++#ifdef CONFIG_IRDA
++        cerf_ucb1400gpio_irda_enable();
++#endif
++      return 0;
++}
++
++static void __exit cerf_ucb1400gpio_module_exit (void)
++{
++#ifdef CONFIG_BLUEZ
++        cerf_ucb1400gpio_bt_disable();
++#endif
++
++#ifdef CONFIG_IRDA
++        cerf_ucb1400gpio_irda_disable();
++#endif
++}
++
++module_init(cerf_ucb1400gpio_module_init);
++module_exit(cerf_ucb1400gpio_module_exit);
++#endif
++
+--- /dev/null
++++ linux-2.4.27/drivers/misc/mcp-pxa.c
+@@ -0,0 +1,57 @@
++/*
++ *  linux/drivers/misc/mcp-pxa.c
++ *
++ *  2002-01-10 Jeff Sutherland <jeffs@accelent.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 of the License.
++ *
++ * NOTE: This is a quick hack to gain access to the aclink codec's
++ *       touch screen facility.  Its audio is handled by a separate
++ *       (non-mcp) driver at the present time.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/ac97_codec.h>
++
++#include "mcp.h"
++
++
++extern int pxa_ac97_get(struct ac97_codec **codec);
++extern void pxa_ac97_put(void);
++
++
++struct mcp *mcp_get(void)
++{
++      struct ac97_codec *codec;
++      if (pxa_ac97_get(&codec) < 0)
++              return NULL;
++      return (struct mcp *)codec;
++}
++
++void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
++{
++      struct ac97_codec *codec = (struct ac97_codec *)mcp;
++      codec->codec_write(codec, reg, val);
++}
++
++unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
++{
++      struct ac97_codec *codec = (struct ac97_codec *)mcp;
++      return codec->codec_read(codec, reg);
++}
++
++void mcp_enable(struct mcp *mcp)
++{
++      /* 
++       * Should we do something here to make sure the aclink
++       * codec is alive???
++       * A: not for now  --NP
++      */
++}
++
++void mcp_disable(struct mcp *mcp)
++{
++}
+--- linux-2.4.27/drivers/misc/mcp.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/mcp.h
+@@ -10,16 +10,22 @@
+ #ifndef MCP_H
+ #define MCP_H
++#ifdef CONFIG_ARCH_SA1100
++#include <asm/dma.h>
++#endif
++
+ struct mcp {
+       struct module   *owner;
+       spinlock_t      lock;
+       int             use_count;
+       unsigned int    sclk_rate;
+       unsigned int    rw_timeout;
++#ifdef CONFIG_ARCH_SA1100
+       dma_device_t    dma_audio_rd;
+       dma_device_t    dma_audio_wr;
+       dma_device_t    dma_telco_rd;
+       dma_device_t    dma_telco_wr;
++#endif
+       void            (*set_telecom_divisor)(struct mcp *, unsigned int);
+       void            (*set_audio_divisor)(struct mcp *, unsigned int);
+       void            (*reg_write)(struct mcp *, unsigned int, unsigned int);
+--- linux-2.4.27/drivers/misc/ucb1x00-core.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/ucb1x00-core.c
+@@ -23,12 +23,18 @@
+ #include <linux/errno.h>
+ #include <linux/interrupt.h>
+ #include <linux/pm.h>
++#include <linux/tqueue.h>
++#include <linux/config.h>
+-#include <asm/dma.h>
+-#include <asm/hardware.h>
+ #include <asm/irq.h>
+ #include <asm/mach-types.h>
++
++#ifdef CONFIG_ARCH_SA1100
++#include <asm/arch/assabet.h>
+ #include <asm/arch/shannon.h>
++#endif
++
++#include <asm/hardware.h>
+ #include "ucb1x00.h"
+@@ -155,6 +161,10 @@
+  *
+  *    If called for a synchronised ADC conversion, it may sleep
+  *    with the ADC semaphore held.
++ *    
++ *    See ucb1x00.h for definition of the UCB_ADC_DAT macro.  It
++ *    addresses a bug in the ucb1200/1300 which, of course, Philips
++ *    decided to finally fix in the ucb1400 ;-) -jws
+  */
+ unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
+ {
+@@ -218,22 +228,75 @@
+  * Since we need to read an internal register, we must re-enable
+  * SIBCLK to talk to the chip.  We leave the clock running until
+  * we have finished processing all interrupts from the chip.
++ *
++ * A restriction with interrupts exists when using the ucb1400, as
++ * the codec read/write routines may sleep while waiting for codec
++ * access completion and uses semaphores for access control to the
++ * AC97 bus.  A complete codec read cycle could take  anywhere from
++ * 60 to 100uSec so we *definitely* don't want to spin inside the
++ * interrupt handler waiting for codec access.  So, we handle the
++ * interrupt by scheduling a RT kernel thread to run in process
++ * context instead of interrupt context.
+  */
+-static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs)
++
++static int ucb1x00_thread(void *_ucb)
+ {
+-      struct ucb1x00 *ucb = devid;
++      struct task_struct *tsk = current;
++      DECLARE_WAITQUEUE(wait, tsk);
++      struct ucb1x00 *ucb = _ucb;
+       struct ucb1x00_irq *irq;
+       unsigned int isr, i;
+-      ucb1x00_enable(ucb);
+-      isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
+-      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
+-      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++      ucb->rtask = tsk;
+-      for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++)
+-              if (isr & 1 && irq->fn)
+-                      irq->fn(i, irq->devid);
+-      ucb1x00_disable(ucb);
++      daemonize();
++      reparent_to_init();
++      tsk->tty = NULL;
++      tsk->policy = SCHED_FIFO;
++      tsk->rt_priority = 1;
++      strcpy(tsk->comm, "kUCB1x00d");
++
++      /* only want to receive SIGKILL */
++      spin_lock_irq(&tsk->sigmask_lock);
++      siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
++      recalc_sigpending(tsk);
++      spin_unlock_irq(&tsk->sigmask_lock);
++
++      add_wait_queue(&ucb->irq_wait, &wait);
++      set_task_state(tsk, TASK_INTERRUPTIBLE);
++      complete(&ucb->complete);
++
++      for (;;) {
++              if (signal_pending(tsk))
++                      break;
++              enable_irq(ucb->irq);
++              schedule();
++
++              ucb1x00_enable(ucb);
++              isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++              for (i = 0, irq = ucb->irq_handler;
++                   i < 16 && isr; 
++                   i++, isr >>= 1, irq++)
++                      if (isr & 1 && irq->fn)
++                              irq->fn(i, irq->devid);
++              ucb1x00_disable(ucb);
++
++              set_task_state(tsk, TASK_INTERRUPTIBLE);
++      }
++
++      remove_wait_queue(&ucb->irq_wait, &wait);
++      ucb->rtask = NULL;
++      complete_and_exit(&ucb->complete, 0);
++}
++
++static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs)
++{
++      struct ucb1x00 *ucb = devid;
++      disable_irq(irqnr);
++      wake_up(&ucb->irq_wait);
+ }
+ /**
+@@ -291,6 +354,11 @@
+               spin_lock_irqsave(&ucb->lock, flags);
+               ucb1x00_enable(ucb);
++
++              /* This prevents spurious interrupts on the UCB1400 */
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 1 << idx);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
+               if (edges & UCB_RISING) {
+                       ucb->irq_ris_enbl |= 1 << idx;
+                       ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
+@@ -456,6 +524,7 @@
+       unsigned int irq_gpio_pin = 0;
+       int irq, default_irq = NO_IRQ;
++#ifdef CONFIG_ARCH_SA1100
+       if (machine_is_adsbitsy())
+               default_irq = IRQ_GPCIN4;
+@@ -514,12 +583,40 @@
+       }
+ #endif
++#endif /* CONFIG_ARCH_SA1100 */
++
++#ifdef CONFIG_ARCH_PXA_IDP
++      if (machine_is_pxa_idp()) {
++              default_irq = TOUCH_PANEL_IRQ;
++              irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ);
++              GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
++      }
++#endif
++
++#ifdef CONFIG_ARCH_TRIZEPS2
++      if (machine_is_trizeps2()) {
++              default_irq = TOUCH_PANEL_IRQ;
++              irq_gpio_pin = IRQ_TO_GPIO_2_80(TOUCH_PANEL_IRQ);
++              GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
++      }
++#endif
++
++
++#ifdef CONFIG_PXA_CERF_PDA
++      if (machine_is_pxa_cerf()) {
++              irq_gpio_pin = CERF_GPIO_UCB1400_IRQ;
++      }
++#endif
++
+       /*
+        * Eventually, this will disappear.
+        */
+       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
+       irq = ucb1x00_detect_irq(ucb);
+       if (irq != NO_IRQ) {
+               if (default_irq != NO_IRQ && irq != default_irq)
+@@ -541,21 +638,7 @@
+ struct ucb1x00 *my_ucb;
+-/**
+- *    ucb1x00_get - get the UCB1x00 structure describing a chip
+- *    @ucb: UCB1x00 structure describing chip
+- *
+- *    Return the UCB1x00 structure describing a chip.
+- *
+- *    FIXME: Currently very noddy indeed, which currently doesn't
+- *    matter since we only support one chip.
+- */
+-struct ucb1x00 *ucb1x00_get(void)
+-{
+-      return my_ucb;
+-}
+-
+-static int __init ucb1x00_init(void)
++static int ucb1x00_init_helper(void)
+ {
+       struct mcp *mcp;
+       unsigned int id;
+@@ -568,23 +651,28 @@
+       mcp_enable(mcp);
+       id = mcp_reg_read(mcp, UCB_ID);
+-      if (id != UCB_ID_1200 && id != UCB_ID_1300) {
++      if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_1400) {
+               printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+               goto out;
+       }
++      /* distinguish between UCB1400 revs 1B and 2A */
++      if (id == UCB_ID_1400 && mcp_reg_read(mcp, 0x00) == 0x002a)
++              id = UCB_ID_1400_BUGGY;
++
+       my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL);
+       ret = -ENOMEM;
+       if (!my_ucb)
+               goto out;
++#ifdef CONFIG_ARCH_SA1100
+       if (machine_is_shannon()) {
+               /* reset the codec */
+               GPDR |= SHANNON_GPIO_CODEC_RESET;
+               GPCR = SHANNON_GPIO_CODEC_RESET;
+               GPSR = SHANNON_GPIO_CODEC_RESET;
+-
+       }
++#endif
+       memset(my_ucb, 0, sizeof(struct ucb1x00));
+@@ -599,13 +687,12 @@
+       if (ret)
+               goto out;
++      init_waitqueue_head(&my_ucb->irq_wait);
+       ret = request_irq(my_ucb->irq, ucb1x00_irq, 0, "UCB1x00", my_ucb);
+       if (ret) {
+               printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
+                       my_ucb->irq, ret);
+-              kfree(my_ucb);
+-              my_ucb = NULL;
+-              goto out;
++              goto irq_err;
+       }
+ #ifdef CONFIG_PM
+@@ -616,16 +703,55 @@
+               my_ucb->pmdev->data = my_ucb;
+ #endif
++      init_completion(&my_ucb->complete);
++      ret = kernel_thread(ucb1x00_thread, my_ucb, CLONE_FS | CLONE_FILES);
++      if (ret >= 0) {
++              wait_for_completion(&my_ucb->complete);
++              ret = 0;
++              goto out;
++      }
++
++      free_irq(my_ucb->irq, my_ucb);
++irq_err:
++      kfree(my_ucb);
++      my_ucb = NULL;
+ out:
+       mcp_disable(mcp);
+ no_mcp:
+       return ret;
+ }
++/**
++ *    ucb1x00_get - get the UCB1x00 structure describing a chip
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Return the UCB1x00 structure describing a chip.
++ *
++ *    FIXME: Currently very noddy indeed, which currently doesn't
++ *    matter since we only support one chip.
++ */
++struct ucb1x00 *ucb1x00_get(void)
++{
++      if( !my_ucb) ucb1x00_init_helper();
++
++      return my_ucb;
++}
++
++static int __init ucb1x00_init(void)
++{
++      /* check if driver is already initialized */
++      if( my_ucb) return 0;
++
++      return ucb1x00_init_helper();
++}
++
+ static void __exit ucb1x00_exit(void)
+ {
++      send_sig(SIGKILL, my_ucb->rtask, 1);
++      wait_for_completion(&my_ucb->complete);
+       free_irq(my_ucb->irq, my_ucb);
+       kfree(my_ucb);
++      my_ucb = 0;
+ }
+ module_init(ucb1x00_init);
+--- linux-2.4.27/drivers/misc/ucb1x00-ts.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/ucb1x00-ts.c
+@@ -35,7 +35,11 @@
+ /*
+  * Define this if you want the UCB1x00 stuff to talk to the input layer
+  */
++#ifdef CONFIG_INPUT
++#define USE_INPUT
++#else
+ #undef USE_INPUT
++#endif
+ #ifndef USE_INPUT
+@@ -73,7 +77,7 @@
+       struct pm_dev           *pmdev;
+ #endif
+-      wait_queue_head_t       irq_wait;
++      struct semaphore        irq_wait;
+       struct semaphore        sem;
+       struct completion       init_exit;
+       struct task_struct      *rtask;
+@@ -259,6 +263,11 @@
+       input_report_abs(&ts->idev, ABS_PRESSURE, pressure);
+ }
++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
++{
++      input_report_abs(&ts->idev, ABS_PRESSURE, 0);
++}
++
+ static int ucb1x00_ts_open(struct input_dev *idev)
+ {
+       struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
+@@ -304,10 +313,15 @@
+  */
+ static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
+ {
+-      ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
+-                      UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
+-                      UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
+-                      UCB_TS_CR_MODE_INT);
++      if (ts->ucb->id == UCB_ID_1400_BUGGY)
++              ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++                              UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
++                              UCB_TS_CR_MODE_INT);
++      else
++              ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++                              UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
++                              UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
++                              UCB_TS_CR_MODE_INT);
+ }
+ /*
+@@ -397,13 +411,13 @@
+ /*
+  * This is a RT kernel thread that handles the ADC accesses
+  * (mainly so we can use semaphores in the UCB1200 core code
+- * to serialise accesses to the ADC).
++ * to serialise accesses to the ADC).  The UCB1400 access
++ * functions are expected to be able to sleep as well.
+  */
+ static int ucb1x00_thread(void *_ts)
+ {
+       struct ucb1x00_ts *ts = _ts;
+       struct task_struct *tsk = current;
+-      DECLARE_WAITQUEUE(wait, tsk);
+       int valid;
+       ts->rtask = tsk;
+@@ -429,10 +443,8 @@
+       valid = 0;
+-      add_wait_queue(&ts->irq_wait, &wait);
+       for (;;) {
+               unsigned int x, y, p, val;
+-              signed long timeout;
+               ts->restart = 0;
+@@ -457,8 +469,6 @@
+               val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+               if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) {
+-                      set_task_state(tsk, TASK_INTERRUPTIBLE);
+-
+                       ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+                       ucb1x00_disable(ts->ucb);
+@@ -471,7 +481,15 @@
+                               valid = 0;
+                       }
+-                      timeout = MAX_SCHEDULE_TIMEOUT;
++                      /*
++                       * Since ucb1x00_enable_irq() might sleep due
++                       * to the way the UCB1400 regs are accessed, we
++                       * can't use set_task_state() before that call,
++                       * and not changing state before enabling the
++                       * interrupt is racy.  A semaphore solves all
++                       * those issues quite nicely.
++                       */
++                      down_interruptible(&ts->irq_wait);
+               } else {
+                       ucb1x00_disable(ts->ucb);
+@@ -486,16 +504,13 @@
+                       }
+                       set_task_state(tsk, TASK_INTERRUPTIBLE);
+-                      timeout = HZ / 100;
++                      schedule_timeout(HZ / 100);
+               }
+-              schedule_timeout(timeout);
+               if (signal_pending(tsk))
+                       break;
+       }
+-      remove_wait_queue(&ts->irq_wait, &wait);
+-
+       ts->rtask = NULL;
+       ucb1x00_ts_evt_clear(ts);
+       complete_and_exit(&ts->init_exit, 0);
+@@ -509,7 +524,7 @@
+ {
+       struct ucb1x00_ts *ts = id;
+       ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
+-      wake_up(&ts->irq_wait);
++      up(&ts->irq_wait);
+ }
+ static int ucb1x00_ts_startup(struct ucb1x00_ts *ts)
+@@ -525,7 +540,7 @@
+       if (ts->rtask)
+               panic("ucb1x00: rtask running?");
+-      init_waitqueue_head(&ts->irq_wait);
++      sema_init(&ts->irq_wait, 0);
+       ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
+       if (ret < 0)
+               goto out;
+@@ -585,7 +600,7 @@
+                * after sleep.
+                */
+               ts->restart = 1;
+-              wake_up(&ts->irq_wait);
++              up(&ts->irq_wait);
+       }
+       return 0;
+ }
+--- linux-2.4.27/drivers/misc/ucb1x00.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/misc/ucb1x00.h
+@@ -10,8 +10,47 @@
+ #ifndef UCB1200_H
+ #define UCB1200_H
++#ifdef CONFIG_ARCH_PXA
++
++/* ucb1400 aclink register mappings: */
++
++#define UCB_IO_DATA   0x5a
++#define UCB_IO_DIR    0x5c
++#define UCB_IE_RIS    0x5e
++#define UCB_IE_FAL    0x60
++#define UCB_IE_STATUS 0x62
++#define UCB_IE_CLEAR  0x62
++#define UCB_TS_CR     0x64
++#define UCB_ADC_CR    0x66
++#define UCB_ADC_DATA  0x68
++#define UCB_ID                0x7e /* 7c is mfr id, 7e part id (from aclink spec) */
++
++#define UCB_ADC_DAT(x)                ((x) & 0x3ff)
++
++#else
++
++/* ucb1x00 SIB register mappings: */
++
+ #define UCB_IO_DATA   0x00
+ #define UCB_IO_DIR    0x01
++#define UCB_IE_RIS    0x02
++#define UCB_IE_FAL    0x03
++#define UCB_IE_STATUS 0x04
++#define UCB_IE_CLEAR  0x04
++#define UCB_TC_A      0x05
++#define UCB_TC_B      0x06
++#define UCB_AC_A      0x07
++#define UCB_AC_B      0x08
++#define UCB_TS_CR     0x09
++#define UCB_ADC_CR    0x0a
++#define UCB_ADC_DATA  0x0b
++#define UCB_ID                0x0c
++#define UCB_MODE      0x0d
++
++#define UCB_ADC_DAT(x)                (((x) & 0x7fe0) >> 5)
++
++#endif
++
+ #define UCB_IO_0              (1 << 0)
+ #define UCB_IO_1              (1 << 1)
+@@ -24,10 +63,6 @@
+ #define UCB_IO_8              (1 << 8)
+ #define UCB_IO_9              (1 << 9)
+-#define UCB_IE_RIS    0x02
+-#define UCB_IE_FAL    0x03
+-#define UCB_IE_STATUS 0x04
+-#define UCB_IE_CLEAR  0x04
+ #define UCB_IE_ADC            (1 << 11)
+ #define UCB_IE_TSPX           (1 << 12)
+ #define UCB_IE_TSMX           (1 << 13)
+@@ -36,11 +71,9 @@
+ #define UCB_IRQ_TSPX          12
+-#define UCB_TC_A      0x05
+ #define UCB_TC_A_LOOP         (1 << 7)        /* UCB1200 */
+ #define UCB_TC_A_AMPL         (1 << 7)        /* UCB1300 */
+-#define UCB_TC_B      0x06
+ #define UCB_TC_B_VOICE_ENA    (1 << 3)
+ #define UCB_TC_B_CLIP         (1 << 4)
+ #define UCB_TC_B_ATT          (1 << 6)
+@@ -49,14 +82,11 @@
+ #define UCB_TC_B_IN_ENA               (1 << 14)
+ #define UCB_TC_B_OUT_ENA      (1 << 15)
+-#define UCB_AC_A      0x07
+-#define UCB_AC_B      0x08
+ #define UCB_AC_B_LOOP         (1 << 8)
+ #define UCB_AC_B_MUTE         (1 << 13)
+ #define UCB_AC_B_IN_ENA               (1 << 14)
+ #define UCB_AC_B_OUT_ENA      (1 << 15)
+-#define UCB_TS_CR     0x09
+ #define UCB_TS_CR_TSMX_POW    (1 << 0)
+ #define UCB_TS_CR_TSPX_POW    (1 << 1)
+ #define UCB_TS_CR_TSMY_POW    (1 << 2)
+@@ -72,7 +102,6 @@
+ #define UCB_TS_CR_TSPX_LOW    (1 << 12)
+ #define UCB_TS_CR_TSMX_LOW    (1 << 13)
+-#define UCB_ADC_CR    0x0a
+ #define UCB_ADC_SYNC_ENA      (1 << 0)
+ #define UCB_ADC_VREFBYP_CON   (1 << 1)
+ #define UCB_ADC_INP_TSPX      (0 << 2)
+@@ -87,15 +116,13 @@
+ #define UCB_ADC_START         (1 << 7)
+ #define UCB_ADC_ENA           (1 << 15)
+-#define UCB_ADC_DATA  0x0b
+ #define UCB_ADC_DAT_VAL               (1 << 15)
+-#define UCB_ADC_DAT(x)                (((x) & 0x7fe0) >> 5)
+-#define UCB_ID                0x0c
+ #define UCB_ID_1200           0x1004
+ #define UCB_ID_1300           0x1005
++#define UCB_ID_1400           0x4304
++#define UCB_ID_1400_BUGGY     0x4303  /* fake ID */
+-#define UCB_MODE      0x0d
+ #define UCB_MODE_DYN_VFLAG_ENA        (1 << 12)
+ #define UCB_MODE_AUD_OFF_CAN  (1 << 13)
+@@ -115,6 +142,9 @@
+       unsigned int            irq;
+       struct semaphore        adc_sem;
+       spinlock_t              io_lock;
++      wait_queue_head_t       irq_wait;
++      struct completion       complete;
++      struct task_struct      *rtask;
+       u16                     id;
+       u16                     io_dir;
+       u16                     io_out;
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/Config.in
+@@ -0,0 +1,12 @@
++mainmenu_option next_comment
++comment 'MMC device drivers'
++tristate 'Multi Media Card support' CONFIG_MMC
++if [ "$CONFIG_MMC" = "y" -o "$CONFIG_MMC" = "m" ]; then
++   dep_tristate 'PXA250 MMC driver' CONFIG_MMC_PXA  $CONFIG_MMC
++   dep_tristate 'MMC block driver' CONFIG_MMC_BLOCK  $CONFIG_MMC
++   if [ "$CONFIG_MMC_BLOCK" = "y" -o "$CONFIG_MMC_BLOCK" = "m" ]; then
++      bool '  MMC partitioning support' CONFIG_MMC_PARTITIONS
++   fi
++fi                                   
++endmenu
++
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/Makefile
+@@ -0,0 +1,14 @@
++#
++# Makefile for MMC drivers
++#
++
++export-objs   := mmc_core.o
++
++obj-$(CONFIG_MMC)     += mmc_core.o # mmc_test.o
++obj-$(CONFIG_MMC_BLOCK)       += mmc_block.o
++obj-$(CONFIG_MMC_PXA) += mmc_pxa.o
++# EXTRA_CFLAGS += -DCONFIG_MMC_DEBUG -DCONFIG_MMC_DEBUG_VERBOSE=2
++
++O_TARGET := mmcdrivers.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/error.h
+@@ -0,0 +1,70 @@
++/*
++ *  linux/include/linux/mmc/error.h 
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: error.h,v 0.2 2002/07/11 16:27:01 ted Exp ted $
++ *
++ *  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 __MMC_ERROR_H__
++#define __MMC_ERROR_H__
++
++/* MMC protocol card error codes */
++#define MMC_CARD_STATUS_OUT_OF_RANGE (1<<31)
++#define MMC_CARD_STATUS_ADDRESS_ERROR (1<<30)
++#define MMC_CARD_STATUS_BLOCK_LEN_ERROR (1<<29)
++#define MMC_CARD_STATUS_ERASE_SEQ_ERROR (1<<28)
++#define MMC_CARD_STATUS_ERASE_PARAM (1<<27)
++#define MMC_CARD_STATUS_WP_VIOLATION (1<<26)
++#define MMC_CARD_STATUS_CARD_IS_LOCKED (1<<25)
++#define MMC_CARD_STATUS_LOCK_UNLOCK_FAILED (1<<24)
++#define MMC_CARD_STATUS_COM_CRC_ERROR (1<<23)
++#define MMC_CARD_STATUS_ILLEGAL_COMMAND (1<<22)
++#define MMC_CARD_STATUS_CARD_ECC_FAILED (1<<21)
++#define MMC_CARD_STATUS_CC_ERROR (1<<20)
++#define MMC_CARD_STATUS_ERROR (1<<19)
++#define MMC_CARD_STATUS_UNDERRUN (1<<18)
++#define MMC_CARD_STATUS_OVERRUN (1<<17)
++#define MMC_CARD_STATUS_CID_CSD_OVERWRITE (1<<16)
++#define MMC_CARD_STATUS_ERASE_RESET (1<<13)
++
++#define MMC_ERROR( fmt, args... ) printk( KERN_ERR "%s(): " fmt, __FUNCTION__, ##args )
++
++/* 
++ * Error codes returned by MMC subsystem functions and
++ * error reporting function prototypes 
++ */
++enum _mmc_error {
++/* controller errors */
++      MMC_ERROR_GENERIC = -10000,
++      MMC_ERROR_CRC_WRITE_ERROR = -10001,
++      MMC_ERROR_CRC_READ_ERROR = -10002,
++      MMC_ERROR_RES_CRC_ERROR = -10003,
++      MMC_ERROR_READ_TIME_OUT = -10004,
++      MMC_ERROR_TIME_OUT_RESPONSE = -10005,
++      MMC_ERROR_INVAL = -10006,
++/* protocol errors reported in card status (R1 response) */           
++      MMC_ERROR_OUT_OF_RANGE = -10007,
++      MMC_ERROR_ADDRESS_ERROR = -10008,
++      MMC_ERROR_BLOCK_LEN_ERROR = -10009,
++      MMC_ERROR_ERASE_SEQ_ERROR = -10010,
++      MMC_ERROR_ERASE_PARAM = -10011,
++      MMC_ERROR_WP_VIOLATION = -10012,
++      MMC_ERROR_CARD_IS_LOCKED = -10013,
++      MMC_ERROR_LOCK_UNLOCK_FAILED = -10014,
++      MMC_ERROR_COM_CRC_ERROR = -10015,
++      MMC_ERROR_ILLEGAL_COMMAND = -10016,
++      MMC_ERROR_CARD_ECC_FAILED = -10017,
++      MMC_ERROR_CC_ERROR = -10018,
++      MMC_ERROR_ERROR = -10019,
++      MMC_ERROR_UNDERRUN = -10020,
++      MMC_ERROR_OVERRUN = -10021,
++      MMC_ERROR_CID_CSD_OVERWRITE = -10022,
++      /* FIXME: incomplete */
++      MMC_ERROR_ERASE_RESET = -10025
++};
++#endif /* __MMC_ERROR_H__ */
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc.h
+@@ -0,0 +1,463 @@
++/*
++ *  linux/drivers/mmc/mmc.h
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *  
++ *  $Id: mmc.h,v 0.3.1.8 2002/09/18 12:58:00 ted Exp ted $
++ *  
++ *  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 __MMC_P_H__
++#define __MMC_P_H__
++
++#ifdef __KERNEL__
++
++#include <linux/types.h>
++#include <linux/slab.h>
++
++#include <linux/spinlock.h>
++
++#ifdef CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif
++
++#include <asm/semaphore.h>
++#include <mmc/types.h>
++#include <mmc/mmc.h>
++
++#include "types.h"
++
++#include "error.h"
++
++#define MMC_CONTROLLERS_MAX (4)
++#define MMC_CARDS_MAX (16)
++
++/* test device */
++#define MMC_TEST_MAJOR (240)
++#define MMC_TEST_TRANSFER_MODE_DEFAULT MMC_TRANSFER_MODE_BLOCK_SINGLE
++
++/* block device */
++#define MMC_BLOCK_MAJOR (241) /* FIXME: MMC_MAJOR */
++#define MMC_BLOCK_DEVICES_MAX (1<<MINORBITS) /* FIXME */
++#define MMC_BLOCK_SECT_SIZE (512) /* FIXME */
++#define MMC_BLOCK_PARTNBITS (3)
++
++/* Device minor number encoding:
++ *   [7:6] - host
++ *   [5:3] - card slot number
++ *   [2:0] - partition number 
++ */
++#define MMC_MINOR_HOST_SHIFT (6)
++#define MMC_MINOR_CARD_MASK (0x07)
++
++/*
++ *  MMC controller abstraction
++ */
++enum _mmc_controller_state {
++      MMC_CONTROLLER_ABSENT = 0,
++      MMC_CONTROLLER_FOUND,
++      MMC_CONTROLLER_INITIALIZED,
++      MMC_CONTROLLER_UNPLUGGED
++};
++
++enum _mmc_dir {
++      MMC_READ = 1,
++      MMC_WRITE
++};
++
++enum _mmc_buftype {
++      MMC_USER = 1,
++      MMC_KERNEL
++};
++
++struct _mmc_data_transfer_req_rec {
++      mmc_dir_t cmd; /* read or write operation requested */
++      mmc_transfer_mode_t mode; /* requested data transfer mode */
++      mmc_buftype_t type; /* whether supplied buffer resides in user or kernel space */
++      char *buf; /* poiner to the caller's buffer */
++      ssize_t cnt; /* number of bytes to transfer */
++      loff_t addr; /* card address */
++      ssize_t blksz; /* block size as for CSD[READ_BL_LEN] or CSD[WRITE_BL_LEN] */
++      ssize_t nob; /* number of blocks to transfer */
++};
++
++struct _mmc_controller_tmpl_rec {
++      struct module *owner;   /* driver module */
++      char name[16];
++
++      const ssize_t block_size_max; /* max acceptable block size */
++      const ssize_t nob_max; /* max blocks per one data transfer */
++
++      int (*probe)( mmc_controller_t ); /* hardware probe */
++      int (*init)( mmc_controller_t ); /* initialize, e.g. request irq, DMA and allocate buffers */
++      void (*remove)( mmc_controller_t ); /* free resources */
++#if 0 /* CONFIG_HOTPLUG */
++      void (*attach)( void ); /|* controller hotplug callbacks *|/
++      void (*detach)( void );
++#endif
++#ifdef CONFIG_PM 
++      int (*suspend)( mmc_controller_t ); /* power management callbacks */
++      void (*resume)( mmc_controller_t );
++#endif
++      
++/* MMC protocol macros, v3.4, p.120 */
++      int (*init_card_stack)( mmc_controller_t );
++      int (*update_acq)( mmc_controller_t ); /* update card stack management data */
++      int (*single_card_acq)( mmc_controller_t );
++      int (*check_card_stack)( mmc_controller_t );
++      int (*setup_card)( mmc_controller_t, mmc_card_t );
++      int (*stream_read)( mmc_controller_t, mmc_data_transfer_req_t );
++      int (*read_block)( mmc_controller_t,  mmc_data_transfer_req_t ); 
++      int (*read_mblock)( mmc_controller_t, mmc_data_transfer_req_t  );
++      int (*stream_write)( mmc_controller_t, mmc_data_transfer_req_t  );
++      int (*write_block)( mmc_controller_t, mmc_data_transfer_req_t );
++      int (*write_mblock)( mmc_controller_t, mmc_data_transfer_req_t  );
++/* TODO:
++      int (*sg_io)( mmc_controller_t, sg_list_t );
++*/
++/* TODO: 
++ *    1) erase group macros 
++ *       int (*erase_group)( mmc_controller_t, mmc_erase_group_info_t );
++ *    2) write protection macros;
++ *       int (*set_write_prot)( mmc_controller_t, mmc_write_protection_info_t )
++ *    3) lock/password management macros;
++ */
++};
++
++#ifndef MMC_CTRLR_BLKSZ_DEFAULT 
++#define MMC_CTRLR_BLKSZ_DEFAULT (512)
++#endif
++
++#ifndef MMC_CTRLR_NOB_DEFAULT 
++#define MMC_CTRLR_NOB_DEFAULT (1)
++#endif
++
++struct _mmc_card_rec {
++/* public card interface */
++      struct _mmc_card_info_rec info; /* see <linux/mmc/mmc.h> */
++
++/* private kernel specific data */
++      mmc_state_t state;  /* card's state as per last operation */
++      mmc_card_t next;     /* link to the stack */
++      mmc_controller_t ctrlr;         /* back reference to the controller */
++      int usage;                      /* reference count */
++      int slot;                       /* card's number for device reference */
++/* TODO: async I/O queue */
++#ifdef CONFIG_PROC_FS
++      proc_dir_entry_t proc;
++      char proc_name[16];
++#endif
++      unsigned long card_data[0]      /* card specific data */
++      __attribute__((aligned (sizeof(unsigned long))));
++};
++
++struct _mmc_card_stack_rec {
++      mmc_card_t first;       /* first card on the stack */
++      mmc_card_t last;        /* last card on the stack */
++      mmc_card_t selected;    /* currently selected card */
++      int ncards;
++};
++
++struct _mmc_controller_rec {
++      mmc_controller_state_t state; /* found, initialized, unplugged... */
++      int usage;                   /* reference count */
++      int slot;                    /* host's number for device reference */
++      semaphore_t io_sem;          /* I/O serialization */
++      rwsemaphore_t update_sem;    /* card stack check/update serialization */
++
++      mmc_controller_tmpl_t tmpl; /* methods provided by the driver */
++      mmc_card_stack_rec_t stack; /* card stack management data */
++
++      u32 rca_next;  /* next RCA to assign */
++      int slot_next; /* next slot number to assign */
++#ifdef CONFIG_PROC_FS
++      char proc_name[16];
++      proc_dir_entry_t proc;
++#endif
++      unsigned long host_data[0]  /* driver can request some extra space */
++      __attribute__((aligned (sizeof(unsigned long))));
++};
++
++/*
++ * MMC core interface
++ */
++enum _mmc_reg_type {
++      MMC_REG_TYPE_USER = 1,
++      MMC_REG_TYPE_HOST,
++      MMC_REG_TYPE_CARD
++};
++
++struct _mmc_notifier_rec {
++      struct _mmc_notifier_rec *next;
++      mmc_notifier_fn_t add;
++      mmc_notifier_fn_t remove;
++};
++
++enum _mmc_response {
++      MMC_NORESPONSE = 1,
++      MMC_R1,
++      MMC_R2,
++      MMC_R3,
++      MMC_R4,
++      MMC_R5
++};
++
++#undef EXTERN
++#ifndef __MMC_CORE_IMPLEMENTATION__
++#define EXTERN extern
++#else
++#define EXTERN /* empty */
++#endif
++
++EXTERN void *mmc_register( mmc_reg_type_t, void *, size_t ); 
++EXTERN void mmc_unregister( mmc_reg_type_t, void * );
++EXTERN int mmc_update_card_stack( int );
++
++EXTERN mmc_card_t mmc_get_card( int, int );/* get reference to the card */
++EXTERN void mmc_put_card( mmc_card_t );  /* release card reference */
++
++EXTERN int mmc_notify_add( mmc_card_t ); /* user notification */
++EXTERN int mmc_notify_remove( mmc_card_t );
++
++EXTERN ssize_t mmc_read( mmc_card_t, mmc_transfer_mode_t, char *, size_t, loff_t * ); /* generic read */
++EXTERN ssize_t mmc_write( mmc_card_t, mmc_transfer_mode_t, const char *, size_t, loff_t * ); /* generic write */
++EXTERN int mmc_ioctl( mmc_card_t, unsigned int, unsigned long ); /* generic ioctl */ 
++/*
++ * TODO: [?m.b. ioctl()] to erase, lock and write protect
++ *    1) mmc_erase
++ *    2) mmc_write_prot
++ *    3) mmc_lock
++ */
++#undef EXTERN
++
++static inline mmc_card_t __mmc_card_alloc( size_t extra )
++{
++      mmc_card_t ret = kmalloc( sizeof( mmc_card_rec_t ) + extra, GFP_KERNEL );
++      
++      if ( ret ) {
++              memset( ret, 0, sizeof( mmc_card_rec_t ) + extra );
++      }
++      
++      return ret;
++}
++
++static inline void __mmc_card_free( mmc_card_t card )
++{
++      if ( card ) {
++              kfree( card );
++      }
++}
++
++static inline mmc_card_stack_t __mmc_card_stack_init( mmc_card_stack_t stack )
++{
++      mmc_card_stack_t ret = NULL;
++      if ( stack ) {
++              memset( stack, 0, sizeof( mmc_card_stack_rec_t ) );
++              ret = stack;
++      }
++      return ret;
++}
++
++static inline mmc_card_stack_t __mmc_card_stack_add( mmc_card_stack_t stack, mmc_card_t card )
++{
++      mmc_card_stack_t ret = NULL;
++      
++      if ( stack && card ) {
++              card->next = NULL;
++              
++              if ( stack->first ) {
++                      stack->last->next = card;
++                      stack->last = card;
++              } else 
++                      stack->first = stack->last = card;
++              
++              ++stack->ncards;
++              ret = stack;
++      }
++      return ret;
++}
++
++static inline mmc_card_stack_t __mmc_card_stack_remove( mmc_card_stack_t stack, mmc_card_t card )
++{
++      mmc_card_stack_t ret = NULL;
++      register mmc_card_t prev;
++      int found = FALSE;
++
++      if ( !stack || !card )
++              goto error;
++      
++      if ( stack->ncards > 0 ) {
++              if ( stack->first == card ) {
++                      stack->first = stack->first->next;
++                      if ( stack->last == card )
++                              stack->last = stack->last->next;
++                      found = TRUE;           
++              } else {
++                      for ( prev = stack->first; prev; prev = prev->next )
++                              if ( prev->next == card ) {
++                                      found = TRUE;
++                                      break;
++                              }
++                      if ( found ) {
++                              if ( prev->next == stack->last )
++                                      stack->last = prev->next;
++                              prev->next = prev->next->next;
++                      }
++              }
++              if ( found ) {
++                      --stack->ncards;
++                      ret = stack;
++              }
++      }
++error:
++      return ret;
++}
++
++static inline void __mmc_card_stack_free( mmc_card_stack_t stack )
++{
++      mmc_card_t card, next;
++      
++      if ( stack && (stack->ncards > 0) ) {
++              card = stack->first;
++              while ( card ) {
++                      next = card->next;
++                      kfree( card );
++                      card = next;
++              }
++              __mmc_card_stack_init( stack );
++      }
++}
++
++static inline int __mmc_card_stack_foreach( mmc_card_stack_t stack, mmc_notifier_fn_t fn, int unplugged_also )
++{
++      int ret = 0;
++      register mmc_card_t card = NULL;
++
++      if ( stack && fn ) {
++              for ( card = stack->first; card; card = card->next ) 
++                      if ( (card->state != MMC_CARD_STATE_UNPLUGGED) 
++                                      || unplugged_also )
++                              if ( fn( card ) ) {
++                                      ret = -card->slot;
++                                      break;
++                              }
++      }
++      
++      return ret;     
++}
++
++/*
++ * Debugging macros
++ */
++#ifdef CONFIG_MMC_DEBUG
++
++#define MMC_DEBUG_LEVEL0 (0)     /* major */
++#define MMC_DEBUG_LEVEL1 (1)
++#define MMC_DEBUG_LEVEL2 (2)     /* device */
++#define MMC_DEBUG_LEVEL3 (3)     /* protocol */
++#define MMC_DEBUG_LEVEL4 (4)     /* everything */
++
++#define MMC_DEBUG(n, args...) \
++if (n <=  CONFIG_MMC_DEBUG_VERBOSE) { \
++      printk(KERN_INFO __FUNCTION__ "(): " args); \
++}
++#define __ENTER0( )   MMC_DEBUG( MMC_DEBUG_LEVEL2, "entry\n" );
++#define __LEAVE0( )   MMC_DEBUG( MMC_DEBUG_LEVEL2, "exit\n" );
++#define __ENTER( format, args... )    MMC_DEBUG( MMC_DEBUG_LEVEL2, "entry: " format "\n", args );
++#define __LEAVE( format, args... )    MMC_DEBUG( MMC_DEBUG_LEVEL2, "exit: " format "\n", args );
++
++#define MMC_DUMP_CSD( card ) MMC_DEBUG( MMC_DEBUG_LEVEL3, \
++"CSD register:\n" \
++"    csd_structure=%u\n" \
++"    spec_vers=%u\n" \
++"    taac=%x\n" \
++"    nsac=%x\n" \
++"    tran_speed=%x\n" \
++"    ccc=%x\n" \
++"    read_bl_len=%u\n" \
++"    read_bl_partial=%u\n" \
++"    write_blk_misalign=%u\n" \
++"    read_blk_misalign=%u\n" \
++"    dsr_imp=%u\n" \
++"    c_size=%u\n" \
++"    vdd_r_curr_min=%u\n" \
++"    vdd_r_curr_max=%u\n" \
++"    vdd_w_curr_min=%u\n" \
++"    vdd_w_curr_max=%u\n" \
++"    c_size_mult=%u\n" \
++"    erase_grp_size=%u\n" \
++"    erase_grp_mult=%u\n" \
++"    wp_grp_size=%u\n" \
++"    wp_grp_enable=%u\n" \
++"    default_ecc=%u\n" \
++"    r2w_factor=%u\n" \
++"    write_bl_len=%u\n" \
++"    write_bl_partial=%u\n" \
++"    content_prot_app=%u\n" \
++"    file_format_grp=%u\n" \
++"    copy=%u\n" \
++"    perm_write_protect=%d\n" \
++"    tmp_write_protect=%d\n" \
++"    file_format=%d\n" \
++"    ecc=%d\n", \
++card->info.csd.csd_structure, \
++card->info.csd.spec_vers, \
++card->info.csd.taac, \
++card->info.csd.nsac, \
++card->info.csd.tran_speed, \
++card->info.csd.ccc, \
++card->info.csd.read_bl_len, \
++card->info.csd.read_bl_partial, \
++card->info.csd.write_blk_misalign, \
++card->info.csd.read_blk_misalign, \
++card->info.csd.dsr_imp, \
++card->info.csd.c_size, \
++card->info.csd.vdd_r_curr_min, \
++card->info.csd.vdd_r_curr_max, \
++card->info.csd.vdd_w_curr_min, \
++card->info.csd.vdd_w_curr_max, \
++card->info.csd.c_size_mult, \
++card->info.csd.erase_grp_size, \
++card->info.csd.erase_grp_mult, \
++card->info.csd.wp_grp_size, \
++card->info.csd.wp_grp_enable, \
++card->info.csd.default_ecc, \
++card->info.csd.r2w_factor, \
++card->info.csd.write_bl_len, \
++card->info.csd.write_bl_partial, \
++card->info.csd.content_prot_app, \
++card->info.csd.file_format_grp, \
++card->info.csd.copy, \
++card->info.csd.perm_write_protect, \
++card->info.csd.tmp_write_protect, \
++card->info.csd.file_format, \
++card->info.csd.ecc );
++
++#else /* CONFIG_MMC_DEBUG */
++#define MMC_DEBUG(n, args...) /* empty */
++#define __ENTER0( )   /* empty */
++#define __LEAVE0( )   /* empty */
++#define __ENTER( args... )    /* empty */
++#define __LEAVE( args... )    /* empty */
++#define MMC_DUMP_CSD( card ) /* empty */
++#endif /* CONFIG_MMC_DEBUG */
++
++/* 
++ * Miscellaneous defines
++ */
++#ifndef MMC_DUMP_R1
++#define MMC_DUMP_R1( ctrlr ) /* empty */
++#endif
++#ifndef MMC_DUMP_R2
++#define MMC_DUMP_R2( ctrlr ) /* empty */
++#endif
++#ifndef MMC_DUMP_R3
++#define MMC_DUMP_R3( ctrlr ) /* empty */
++#endif
++
++#endif /* __KERNEL__ */
++
++#endif /* __MMC_P_H__ */
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc_block.c
+@@ -0,0 +1,989 @@
++/*
++ *  linux/drivers/mmc/mmc_block.c 
++ *     driver for the block device on the MMC card
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: mmc_block.c,v 0.3.1.16 2002/09/27 17:36:09 ted Exp ted $
++ *
++ *  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/version.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/devfs_fs_kernel.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/hdreg.h>
++#include <linux/blkpg.h>
++#include <asm/uaccess.h>
++
++#include <mmc/types.h>
++#include <mmc/mmc.h>
++
++#include "types.h"
++#include "mmc.h"
++#include "error.h"
++
++#define MAJOR_NR MMC_BLOCK_MAJOR
++#define MAJOR_NAME "mmc"
++#define DEVICE_NAME "mmc_block"
++#define DEVICE_REQUEST mmc_block_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
++
++
++#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 MMC_BLOCK_RAW_DEVICE( device ) ((device>>MMC_BLOCK_PARTNBITS)<<MMC_BLOCK_PARTNBITS)
++#define MMC_BLOCK_MKDEV( host, slot ) \
++      MKDEV( MMC_BLOCK_MAJOR, \
++      (host<<MMC_MINOR_HOST_SHIFT) \
++      | (slot<<MMC_BLOCK_PARTNBITS) )
++
++typedef struct _mmc_block_device mmc_block_device_rec_t;
++typedef struct _mmc_block_device *mmc_block_device_t;
++
++struct _mmc_block_device {
++      mmc_card_t card;
++      int host;
++      int slot;
++      kdev_t rdev;
++      int usage;
++      semaphore_t sem;
++};
++
++static int mmc_block_blk_sizes[1<<MINORBITS];
++static int mmc_block_blk_blksizes[1<<MINORBITS];
++static int mmc_block_hardsect_sizes[1<<MINORBITS];
++static struct hd_struct mmc_block_partitions[1<<MINORBITS];
++
++/* Accessed under device table lock */
++static gendisk_rec_t mmc_block_gendisk = {
++      major:          MMC_BLOCK_MAJOR,
++      major_name:     MAJOR_NAME,
++      minor_shift:    MMC_BLOCK_PARTNBITS,
++      max_p:          (1<<MMC_BLOCK_PARTNBITS),
++      sizes:          mmc_block_blk_sizes,
++      part:           mmc_block_partitions
++};
++
++static mmc_block_device_rec_t mmc_block_device[1<<MINORBITS];
++static rwsemaphore_t mmc_block_device_sem;
++
++static inline void __mmc_block_rdlock_devices( void )
++{
++      down_read( &mmc_block_device_sem );
++}
++
++static inline void __mmc_block_rdunlock_devices( void )
++{
++      up_read( &mmc_block_device_sem );
++}
++
++static inline void __mmc_block_wrlock_devices( void )
++{
++      down_write( &mmc_block_device_sem );
++}
++
++static inline void __mmc_block_wrunlock_devices( void )
++{
++      up_write( &mmc_block_device_sem );
++}
++
++static inline void __mmc_block_lock_device( kdev_t rdev )
++{
++      __mmc_block_rdlock_devices();
++      down( &mmc_block_device[MINOR( rdev )].sem );
++}
++
++static inline void __mmc_block_unlock_device( kdev_t rdev )
++{
++      up( &mmc_block_device[MINOR( rdev )].sem );
++      __mmc_block_rdunlock_devices();
++}
++
++static inline void __mmc_block_device_init( int minor )
++{
++      mmc_block_device_t dev = &mmc_block_device[minor];
++      
++      dev->usage = 0;
++      dev->card = NULL;
++      dev->host = minor >> MMC_MINOR_HOST_SHIFT;
++      dev->slot = (minor & MMC_MINOR_CARD_MASK)>>MMC_BLOCK_PARTNBITS;
++      dev->rdev = MKDEV( MMC_BLOCK_MAJOR, minor );
++}
++
++static inline int __mmc_block_validate_device( kdev_t rdev )
++{
++      int ret = -1;
++      int minor = MINOR( rdev );
++      
++      if ( mmc_block_device[minor].card 
++                      && (mmc_block_gendisk.part[minor].nr_sects > 0) )
++              ret = 0;
++
++      return ret;
++}
++
++static inline int __mmc_block_invalidate_card( mmc_card_t card, int invalidate )
++{
++      int ret = 0;
++      kdev_t start;
++      int minor;
++
++      __ENTER( "card = 0x%p", card );
++      
++      if ( card && card->ctrlr ) {
++              register int i;
++              
++              start = MMC_BLOCK_MKDEV( card->ctrlr->slot, card->slot );
++              minor = MINOR( start );
++
++              __mmc_block_wrlock_devices();
++              for ( i = mmc_block_gendisk.max_p - 1; i >= 0; --i ) {
++                      if ( invalidate )
++                              invalidate_device( start + i, 0 );
++                      
++                      __mmc_block_device_init( minor + i );
++                      
++                      mmc_block_gendisk.part[minor + i].nr_sects = 0;
++                      mmc_block_gendisk.part[minor + i].start_sect = 0;
++              }
++              __mmc_block_wrunlock_devices();
++      }
++      
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static inline int mmc_block_invalidate_card( int host, int slot, int invalidate )
++{
++      int ret = 0;
++      kdev_t start;
++      int minor;
++
++      __ENTER( "host=%d slot=%d", host, slot );
++      
++      if ( (host >= 0) && (slot >= 0) ) {
++              register int i;
++              mmc_card_t card = NULL;
++              
++              start = MMC_BLOCK_MKDEV( host, slot );
++              minor = MINOR( start );
++
++              __mmc_block_wrlock_devices();
++              for ( i = mmc_block_gendisk.max_p - 1; i >= 0; --i ) {
++                      if ( !card ) 
++                              card = mmc_block_device[minor + i].card;
++                      
++                      if ( invalidate )
++                              invalidate_device( start + i, 0 );
++                      
++                      __mmc_block_device_init( minor + i );
++                      
++                      mmc_block_gendisk.part[minor + i].nr_sects = 0;
++                      mmc_block_gendisk.part[minor + i].start_sect = 0;
++              }
++              if ( card )
++                      mmc_put_card( card );
++              __mmc_block_wrunlock_devices();
++      }
++      
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++/* Get device reference locked for writing */
++static inline mmc_block_device_t __mmc_block_get_device( kdev_t rdev )
++{
++      mmc_block_device_t ret = NULL;
++      u8 minor = MINOR( rdev );
++      int host_no, card_no;
++
++      __ENTER( "rdev=%x:%x", MAJOR( rdev ), MINOR( rdev ) );
++
++      host_no = minor >> MMC_MINOR_HOST_SHIFT;
++      if ( host_no >= MMC_CONTROLLERS_MAX )
++              goto error;
++
++      card_no = (minor & MMC_MINOR_CARD_MASK)>>MMC_BLOCK_PARTNBITS;
++      if ( card_no >= MMC_CARDS_MAX )
++              goto error;
++      
++      __mmc_block_lock_device( rdev );
++      if ( __mmc_block_validate_device( rdev ) ) {
++              __mmc_block_unlock_device( rdev );      
++              goto error;
++      }
++
++      ret = &mmc_block_device[minor];
++      MMC_DEBUG( MMC_DEBUG_LEVEL2, "(%x:%x) card=%p, dusage=%d\n",
++                      MAJOR( ret->rdev ), MINOR( ret->rdev ), 
++                      ret->card, ret->usage );
++error:
++      __LEAVE( "ret=0x%p", ret );
++      return ret;
++}
++
++/* Unlocks the device */
++static inline void __mmc_block_put_device( mmc_block_device_t dev )
++{
++      __ENTER0();
++      
++      if ( dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL2, "(%x:%x) card=%p, dusage=%d\n",
++                              MAJOR( dev->rdev ), MINOR( dev->rdev ), 
++                              dev->card, dev->usage );
++              __mmc_block_unlock_device( dev->rdev );
++      }
++      
++      __LEAVE0();
++}
++
++/* Atomically increases use count of the valid device */
++static inline mmc_block_device_t mmc_block_get_device( kdev_t rdev )
++{
++      mmc_block_device_t ret = NULL;
++      
++      __ENTER0();
++      
++      ret = __mmc_block_get_device( rdev );
++      if ( !ret )
++              goto error;
++      
++      ret->usage++;
++      __mmc_block_put_device( ret );
++error:
++      __LEAVE( "ret=0x%p dusage=%d card=0x%p cusage=%d",
++                      ret, ret ? ret->usage : -1, 
++                      ret ? ret->card : NULL, 
++                      ret ? (ret->card ? ret->card->usage : -1) : -1 );
++      return ret;
++}
++
++/* Check is there references to the card */
++static inline int __mmc_block_check_card( kdev_t rdev ) 
++{
++      int ret = TRUE;
++      int start = MINOR( MMC_BLOCK_RAW_DEVICE( rdev ) );
++      register int i;
++
++      for ( i = 0; i < mmc_block_gendisk.max_p; i++ )
++              if ( mmc_block_device[start + i].usage > 0 ) {
++                      ret = FALSE;
++                      break;
++              }
++
++      return ret;
++}
++
++/* Atomically decreases device use count */
++static inline void mmc_block_put_device( mmc_block_device_t dev )
++{
++      __ENTER0();
++      
++      if ( dev ) {
++              int invalidate = FALSE;
++              
++              __mmc_block_get_device( dev->rdev );
++              if ( dev->usage > 0 )
++                      --dev->usage;
++              
++              if ( dev->usage ) {
++                      __mmc_block_put_device( dev );
++                      goto out;
++
++              } else {
++                      int host, slot;
++                      mmc_card_t card = NULL;
++                      
++                      invalidate = __mmc_block_check_card( dev->rdev );       
++                      if ( invalidate ) {
++                              host = dev->card->ctrlr->slot;
++                              slot = dev->card->slot;
++
++                              if ( dev->card ) {
++                                      card = dev->card;
++                                      mmc_put_card( dev->card );
++                                      dev->card = NULL;
++                              }
++                      }
++                      __mmc_block_put_device( dev );
++
++                      if ( invalidate )
++                              __mmc_block_invalidate_card( card, TRUE );
++              }
++              
++      }
++out:
++      __LEAVE0();
++}
++
++static int mmc_block_open( struct inode *inode, struct file *file )
++{
++      int ret = -ENODEV;
++      mmc_block_device_t dev = NULL;
++      
++      __ENTER0();
++
++      if ( !inode || !file )
++              goto error;
++      
++      BLK_INC_USE_COUNT;
++      
++      check_disk_change( inode->i_rdev );
++      
++      dev = mmc_block_get_device( inode->i_rdev );
++      if ( !dev )
++              goto error;
++      
++      dev = __mmc_block_get_device( inode->i_rdev );
++      if ( !dev )
++              goto error;
++      
++      if ( file->f_mode & FMODE_WRITE ) { /* FIXME */
++              if ( dev->usage > 1 ) {
++                      ret = -EBUSY;
++                      __mmc_block_put_device( dev );
++                      mmc_block_put_device( dev );
++                      goto error;
++              }
++      }
++      
++      __mmc_block_put_device( dev );
++      
++      if ( file )
++              file->private_data = dev;
++
++      ret = 0;
++      goto out;
++error:
++      BLK_DEC_USE_COUNT;
++out:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static int mmc_block_release( struct inode *inode, struct file *file )
++{
++      int ret = -EINVAL;
++      mmc_block_device_t dev = NULL;
++      
++      __ENTER( "inode=0x%p file=0x%p rdev=(%x:%x)", inode, file,
++                      inode ? MAJOR( inode->i_rdev ) : 0xff,
++                      inode ? MINOR( inode->i_rdev ) : 0xff );
++
++      if ( !file && !inode )
++              goto error;
++      
++      if ( file )
++              dev = file->private_data;
++      else
++              dev = __mmc_block_get_device( inode->i_rdev );
++
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "invalid device\n" );
++              goto error;
++      }
++      
++      if ( file ) {
++              mmc_block_put_device( dev );
++              file->private_data = NULL;
++
++      } else {
++              int invalidate = FALSE;
++              
++              if ( dev->usage > 0 )
++                      --dev->usage;
++              
++              if ( dev->usage ) {
++                      __mmc_block_put_device( dev );
++                      goto out;
++                      
++              } else {
++                      int host, slot;
++                      mmc_card_t card = NULL;
++                      
++                      invalidate = __mmc_block_check_card( dev->rdev );       
++                      if ( invalidate ) {
++                              host = dev->card->ctrlr->slot;
++                              slot = dev->card->slot;
++
++                              if ( dev->card ) {
++                                      card = dev->card;
++                                      mmc_put_card( dev->card );
++                                      dev->card = NULL;
++                              }
++                      }
++                      __mmc_block_put_device( dev );
++
++                      if ( invalidate )
++                              __mmc_block_invalidate_card( card, TRUE );
++      
++              }
++      }
++      
++out:
++      BLK_DEC_USE_COUNT;
++      ret = 0;
++error:
++      __LEAVE0();
++      return ret;
++}  
++
++static int mmc_block_check_disk_change( kdev_t rdev )
++{
++      int ret = 0;
++#if 0
++      mmc_block_device_t dev = &mmc_block_device[MINOR( rdev )];
++      
++      __mmc_block_lock_device( rdev );
++      if ( !dev->card )
++              ret = 1;
++      __mmc_block_unlock_device( rdev );
++#else
++      ret = 1;
++#endif
++      return ret;
++}
++
++static int mmc_block_revalidate( kdev_t rdev )
++{
++      int ret = 1;
++      mmc_card_t card;
++      mmc_block_device_t dev;
++      kdev_t start = MMC_BLOCK_RAW_DEVICE( rdev );
++      int minor = MINOR( start );
++      int host, slot;
++      int i;
++      
++      __ENTER0();
++
++      (void)mmc_update_card_stack( MINOR( start )>>MMC_MINOR_HOST_SHIFT );
++
++      __mmc_block_wrlock_devices();
++
++      dev = &mmc_block_device[minor];
++      host = dev->host;
++      slot = dev->slot;
++
++      if ( dev->card ) { /* card has not been changed actually */
++              __mmc_block_wrunlock_devices();
++              goto out;
++              
++      } else {
++              card = mmc_get_card( host, slot );
++              if ( !card ) {
++                      MMC_DEBUG( MMC_DEBUG_LEVEL2, "failed to get card: "
++                                      "host=%d, slot=%d\n", host, slot );
++                      __mmc_block_wrunlock_devices();
++                      goto error;
++              }
++              dev->card = card;
++      }
++      __mmc_block_wrunlock_devices();
++      /* FIXME */
++      __mmc_block_rdlock_devices(); /* handle the request for sector 0 */
++      grok_partitions( &mmc_block_gendisk, MINOR( start ),
++                       mmc_block_gendisk.max_p,
++                       card->info.capacity>>9 /* sectors */
++                     );
++      __mmc_block_rdunlock_devices();
++      /* FIXME */
++      __mmc_block_wrlock_devices();
++      for ( i = start + mmc_block_gendisk.max_p - 1; i >= 0; --i ) {
++              int minor = MINOR( i );
++              
++              dev = &mmc_block_device[minor];
++              if ( mmc_block_gendisk.part[minor].nr_sects > 0 )
++                      dev->card = card;
++      }
++      __mmc_block_wrunlock_devices();
++out:
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static void mmc_block_handle_request( void )
++{
++      struct request *request;
++      mmc_block_device_t dev;
++      mmc_card_t card;
++      char *buf;
++      loff_t pos;
++      unsigned int result = 0;
++
++      for (;;) {
++              int minor;
++              
++              INIT_REQUEST;
++              request = CURRENT;
++              spin_unlock_irq( &io_request_lock );
++              
++              minor = MINOR( request->rq_dev );
++              dev = __mmc_block_get_device( request->rq_dev );
++              if ( !dev ) {
++                      MMC_DEBUG( MMC_DEBUG_LEVEL2, "invalid device (%x:%x)\n",
++                                      MAJOR( request->rq_dev ), minor );
++                              
++                      goto end_req;
++              }
++              
++              card = dev->card;
++              (void)__mmc_block_put_device( dev );
++              
++              MMC_DEBUG( MMC_DEBUG_LEVEL2, 
++//            printk( KERN_INFO __FUNCTION__"(): "
++                              "request %p: cmd %i sec %li (nr. %li)\n", 
++                              CURRENT, CURRENT->cmd, CURRENT->sector, 
++                              CURRENT->current_nr_sectors );
++              
++              if ( request->current_nr_sectors >
++                              mmc_block_gendisk.part[minor].nr_sects )
++                      goto end_req;
++              
++              // Handle the request
++              // TODO: handle clusterred requests in multiple block transfer mode 
++              buf = request->buffer;
++              pos = (mmc_block_gendisk.part[minor].start_sect +
++                      request->sector) * MMC_BLOCK_SECT_SIZE;
++              
++              switch ( request->cmd )
++              {
++                      int i, ret;
++                      
++                      case READ:
++#if 0
++                              ret = mmc_read( card, 
++                                      (request->current_nr_sectors > 1) ?
++                                      MMC_TRANSFER_MODE_BLOCK_MULTIPLE :
++                                      MMC_TRANSFER_MODE_BLOCK_SINGLE,
++                                      buf, 
++                                      request->current_nr_sectors 
++                                           * MMC_BLOCK_SECT_SIZE, /* FIXME */
++                                      &pos );
++                              if ( ret < 0 ) 
++                                      goto end_req;
++                              
++#else
++                              for ( i = 0;
++                                    i < request->current_nr_sectors;
++                                    i++ ) {
++                                      ret = mmc_read( card,
++                                              MMC_TRANSFER_MODE_BLOCK_SINGLE,
++                                              buf,
++                                              MMC_BLOCK_SECT_SIZE, /* FIXME */
++                                              &pos );
++                                      if ( ret < 0 )
++                                              goto end_req;
++                                      else
++                                              buf += ret;
++                              }
++#endif
++                              result = 1;
++                              break;
++
++                      case WRITE:
++                      // TODO: Read only device
++#if 0
++                              ret = mmc_write( card, 
++                                      (request->current_nr_sectors > 1) ?
++                                      MMC_TRANSFER_MODE_BLOCK_MULTIPLE :
++                                      MMC_TRANSFER_MODE_BLOCK_SINGLE,
++                                      buf, 
++                                      request->current_nr_sectors 
++                                          * MMC_BLOCK_SECT_SIZE, /* FIXME */
++                                      &pos );
++                              if ( ret < 0 ) 
++                                      goto end_req;
++                              
++#else
++                              for ( i = 0;
++                                    i < request->current_nr_sectors;
++                                    i++ ) {
++                                      ret = mmc_write( card,
++                                              MMC_TRANSFER_MODE_BLOCK_SINGLE,
++                                              buf,
++                                              MMC_BLOCK_SECT_SIZE, /* FIXME */
++                                              &pos ); 
++                                      if ( ret < 0 )
++                                              goto end_req;
++                                      else
++                                              buf += ret;
++                              }
++#endif
++                              result = 1;
++                              break;
++              }
++
++end_req:
++              __LEAVE( "result=%d", result );
++              spin_lock_irq( &io_request_lock );
++              end_request( result );
++      }
++}
++
++static volatile int leaving = 0;
++static DECLARE_MUTEX_LOCKED( thread_sem );
++static DECLARE_WAIT_QUEUE_HEAD( thr_wq );
++static pid_t thr_id = -1;
++
++int mmc_block_thread( void *arg )
++{
++      struct task_struct *task = current;
++      DECLARE_WAITQUEUE(wait, task);
++
++      __ENTER0();
++      
++      task->session = 1;
++      task->pgrp = 1;
++      task->flags |= PF_MEMALLOC;
++      strcpy( task->comm, "mmcblockd" );
++      task->tty = NULL;
++      spin_lock_irq( &task->sigmask_lock );
++      sigfillset( &task->blocked );
++      recalc_sigpending( task );
++      spin_unlock_irq( &task->sigmask_lock );
++      exit_mm( task );
++      exit_files( task );
++      exit_sighand( task );
++      exit_fs( task );
++
++      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 );
++                      mmc_block_handle_request(); /* handle the request */
++                      spin_unlock_irq( &io_request_lock );
++              }
++      }
++
++      up( &thread_sem );
++      
++      __LEAVE0();
++      return 0;
++}
++
++#if LINUX_VERSION_CODE < 0x20300
++#define RQFUNC_ARG void
++#else
++#define RQFUNC_ARG request_queue_t *q
++#endif
++
++static void mmc_block_request( RQFUNC_ARG )
++{
++      wake_up( &thr_wq );
++}
++
++static int mmc_block_ioctl( struct inode * inode, struct file * file,
++                    unsigned int cmd, unsigned long arg )
++{
++      int ret = -ENODEV;
++      mmc_block_device_t dev;
++      mmc_card_t card;
++      int minor;
++      __ENTER0();
++      
++      if ( !inode || !file ) {
++              ret = -EINVAL;
++              goto error;
++      }
++      minor = MINOR( inode->i_rdev );
++      
++      dev = __mmc_block_get_device( inode->i_rdev );
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "invalid device\n" );
++              goto error;
++      }
++      
++      card = dev->card; 
++      __mmc_block_put_device( dev );
++      
++      switch ( cmd ) {
++      case BLKGETSIZE:   /* Return device size */
++              {
++                      unsigned long value;
++                      
++                      __mmc_block_rdlock_devices();
++                      value = mmc_block_gendisk.part[minor].nr_sects;
++                      __mmc_block_rdunlock_devices();
++                      
++                      if ( put_user( value, (unsigned long *) arg) ) {
++                              ret = -EFAULT;
++                              goto error;
++                      }
++              }
++              break;
++
++#ifdef BLKGETSIZE64
++      case BLKGETSIZE64:
++              {
++                      unsigned long value;
++                      
++                      __mmc_block_rdlock_devices();
++                      value = mmc_block_gendisk.part[minor].nr_sects;
++                      __mmc_block_rdunlock_devices();
++                      
++                      if ( put_user( (u64)value, (u64 *) arg) ) {
++                              ret = -EFAULT;
++                              goto error;
++                      }
++              }
++              break;
++#endif
++
++      case HDIO_GETGEO:
++              {
++                      struct hd_geometry geo;
++                      
++                      ret = !access_ok( VERIFY_WRITE, arg, sizeof( geo ) );
++                      if ( ret ) {
++                              ret = -EFAULT;
++                              goto error;
++                      }
++
++                      geo.heads = 1;
++                      geo.sectors = 1;
++
++                      __mmc_block_rdlock_devices();
++                      geo.cylinders = mmc_block_gendisk.part[minor].nr_sects;
++                      geo.start = mmc_block_gendisk.part[minor].start_sect;
++                      __mmc_block_rdunlock_devices();
++
++                      if ( copy_to_user( (int *)arg, &geo, sizeof( geo ) ) ) {
++                              ret = -EFAULT;
++                              goto error;
++                      }
++              }
++              break;
++              
++      case BLKRRPART:
++              if ( !capable( CAP_SYS_ADMIN ) ) {
++                      ret = -EACCES;
++                      goto error;
++              }
++              (void)mmc_block_revalidate( inode->i_rdev );
++              break;
++              
++      default:
++              ret = blk_ioctl( inode->i_rdev, cmd, arg );
++              goto out;
++      }
++
++      ret = 0;
++error:        
++out:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++#if LINUX_VERSION_CODE < 0x20326
++static struct file_operations mmc_block_fops =
++{
++      open:                   mmc_block_open,
++      ioctl:                  mmc_block_ioctl,
++      release:                mmc_block_release,
++      check_media_change:     mmc_block_check_disk_change,
++      revalidate:             mmc_block_revalidate,
++      read:                   block_read,
++      write:                  block_write
++};
++#else
++static struct block_device_operations mmc_block_fops = 
++{
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
++      owner:                  THIS_MODULE,
++#endif
++      open:                   mmc_block_open,
++      release:                mmc_block_release,
++      ioctl:                  mmc_block_ioctl,
++      check_media_change:     mmc_block_check_disk_change,
++      revalidate:             mmc_block_revalidate
++};
++#endif
++
++
++static int mmc_block_notify_add( mmc_card_t card )
++{
++      int ret = -1;
++      mmc_block_device_t dev;
++      kdev_t start;
++      int minor;
++
++      __ENTER0();
++      
++      if ( !card || !card->ctrlr ) 
++              goto error;
++      
++      start = MMC_BLOCK_MKDEV( card->ctrlr->slot, card->slot );
++      dev = &mmc_block_device[MINOR( start )];
++              
++      __mmc_block_wrlock_devices();
++      if ( !dev->card ) {
++              dev->card = card;
++              ret = 0;
++      }
++      __mmc_block_wrunlock_devices();
++
++      if ( !ret ) {
++              int i;
++
++              /* allow to read partition table */
++              __mmc_block_rdlock_devices(); 
++              grok_partitions( &mmc_block_gendisk, MINOR( start ),
++                       mmc_block_gendisk.max_p,
++                       card->info.capacity>>9 /* sectors */
++              );
++              __mmc_block_rdunlock_devices();
++              
++              __mmc_block_wrlock_devices();
++              for ( i = start + mmc_block_gendisk.max_p - 1; i >= 0; --i ) {
++                      minor = MINOR( i );
++                      dev = &mmc_block_device[minor];
++                      if ( mmc_block_gendisk.part[minor].nr_sects > 0 )
++                              dev->card = card;
++              }
++              __mmc_block_wrunlock_devices();
++      }
++error:        
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++
++static int mmc_block_notify_remove( mmc_card_t card )
++{
++      int ret = -1;
++      
++      __ENTER( "card=0x%p", card );
++      
++      if ( card && card->ctrlr )
++              ret = __mmc_block_invalidate_card( card, FALSE );
++      
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static mmc_notifier_rec_t mmc_block_notifier = {
++      add: mmc_block_notify_add,
++      remove: mmc_block_notify_remove
++};
++
++static int __init mmc_block_module_init( void )
++{
++      int ret = -ENODEV;
++      int i;
++
++      __ENTER0();
++      
++      init_rwsem( &mmc_block_device_sem );
++      
++      if ( devfs_register_blkdev( MAJOR_NR, MAJOR_NAME, &mmc_block_fops ) ) {
++              MMC_ERROR( "Can't allocate major number %d for MMC block devices.\n", MMC_BLOCK_MAJOR );
++              ret = -EAGAIN;
++              goto error;
++      }
++      
++      for ( i = 0; i < (1<<MINORBITS); i++ ) {
++              __mmc_block_device_init( i );
++              init_MUTEX( &mmc_block_device[i].sem );
++
++              /* We fill it in at open() time. */
++              mmc_block_blk_sizes[i] = 0;
++              mmc_block_blk_blksizes[i] = BLOCK_SIZE;
++              mmc_block_hardsect_sizes[i] = 0;
++      }
++      
++      init_waitqueue_head( &thr_wq );
++      /* Allow the block size to default to BLOCK_SIZE. */
++      blksize_size[MAJOR_NR] = mmc_block_blk_blksizes;
++      hardsect_size[MAJOR_NR] = mmc_block_hardsect_sizes;
++      /* Gendisk stuff */
++      memset( mmc_block_partitions, 0, sizeof( mmc_block_partitions ) );
++      add_gendisk( &mmc_block_gendisk );
++
++/* FIXME: per controller request queue, I/O and card stack update threads */  
++      blk_init_queue( BLK_DEFAULT_QUEUE( MAJOR_NR ), &mmc_block_request );
++      thr_id = kernel_thread( mmc_block_thread, NULL, 
++                      CLONE_FS|CLONE_FILES|CLONE_SIGHAND );
++
++      if ( !mmc_register( MMC_REG_TYPE_USER, &mmc_block_notifier, 0 ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "failed to register with MMC core\n" );
++              goto error;
++      }
++
++      ret = 0;
++      goto out;
++error:
++      if ( thr_id != -1 ) {
++/* quit the thread */
++              leaving = 1;
++              wake_up(&thr_wq);
++      
++              down(&thread_sem);
++      }
++      blksize_size[MAJOR_NR] = NULL;
++      blk_size[MAJOR_NR] = NULL;
++      hardsect_size[MAJOR_NR] = NULL;
++out:  
++      __LEAVE0();
++      return ret;
++}
++
++static void __exit mmc_block_module_cleanup( void )
++{
++/* quit the thread */
++      leaving = 1;
++      wake_up(&thr_wq);
++      
++      down(&thread_sem);
++      
++      mmc_unregister( MMC_REG_TYPE_USER, &mmc_block_notifier );
++      del_gendisk( &mmc_block_gendisk );
++      devfs_unregister_blkdev( MAJOR_NR, MAJOR_NAME );
++      
++      blk_cleanup_queue( BLK_DEFAULT_QUEUE( MAJOR_NR ) );
++      blksize_size[MAJOR_NR] = NULL;
++      blk_size[MAJOR_NR] = NULL;
++      hardsect_size[MAJOR_NR] = NULL;
++}
++
++EXPORT_NO_SYMBOLS;
++
++module_init( mmc_block_module_init );
++module_exit( mmc_block_module_cleanup );
++
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc_core.c
+@@ -0,0 +1,1124 @@
++/*
++ *  linux/drivers/mmc/mmc_core.c 
++ *    MultiMediaCard subsystem core implementation
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: mmc_core.c,v 0.3.1.14 2002/09/27 17:36:09 ted Exp ted $
++ *
++ *  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/version.h>
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++
++#include <linux/slab.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include <mmc/types.h>
++#include <mmc/mmc.h>
++#include <mmc/ioctl.h>
++
++#include "types.h"
++
++#define __MMC_CORE_IMPLEMENTATION__
++#include "mmc.h"
++
++/* MMC controllers registered in the system */ 
++static mmc_controller_t mmc_controller[MMC_CONTROLLERS_MAX];
++static int mmc_ncontrollers = 0;
++static rwsemaphore_t mmc_controller_sem; /* controller table lock */
++#ifdef CONFIG_PM
++static struct pm_dev *mmc_pm_dev = NULL;
++#endif
++
++/* users' notification list */
++static mmc_notifier_t mmc_notifier = NULL;
++static rwsemaphore_t mmc_notifier_sem; /* notifiers' list lock */ 
++#ifdef CONFIG_PROC_FS
++static proc_dir_entry_t mmc_proc_dir = NULL;
++#endif
++
++/************************************************
++ * service function prototypes and declarations *
++ ************************************************/
++static inline int mmc_acquire_io( mmc_controller_t ctrlr, mmc_card_t card )
++{
++      int ret = -EIO;
++
++      __ENTER0();
++      
++      if ( !card || !ctrlr ) {
++              ret = -EINVAL;
++              goto error;
++      }
++#ifdef CONFIG_HOTPLUG
++/* TODO: account for controller removal */
++#endif
++      down( &ctrlr->io_sem );
++#if 0 
++      down_read( &ctrlr->update_sem ); /* FIXME */
++      if ( card->state != MMC_CARD_STATE_UNPLUGGED )
++              ret = 0;
++      up_read( &ctrlr->update_sem );
++      
++      if ( ret )
++              up( &ctrlr->io_sem );
++#else
++      ret = 0;
++#endif
++
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static inline void mmc_release_io( mmc_controller_t ctrlr, mmc_card_t card )
++{
++      __ENTER0();
++#ifdef CONFIG_HOTPLUG
++/* TODO: account for controller removal */
++#endif
++      if ( !card && !ctrlr ) { /* FIXME */
++              MMC_DEBUG( MMC_DEBUG_LEVEL2, "bad card reference\n" );
++              goto error;
++      }
++      up( &ctrlr->io_sem );   
++error:
++      __LEAVE0();
++}
++
++/* TODO: there should be a separate context to be awaken 
++ * by the card intertion interrupt; called under ctrlr->update_sem
++ * held down by now */
++static int __mmc_update_card_stack( mmc_controller_t ctrlr )
++{
++      int ret = -1;
++      mmc_card_t card, prev;
++      
++      __ENTER0();
++      
++      if ( !ctrlr || !ctrlr->tmpl )
++              goto error;
++      
++      /* check unplugged cards first... */
++      if ( (ret = ctrlr->tmpl->check_card_stack( ctrlr )) )
++              goto error;
++      
++      /* unregister unplugged cards and free 'em immediately */
++      if ( ctrlr->stack.ncards > 0 ) {
++              prev = ctrlr->stack.first;
++              /* process the stack tail first */
++              if ( prev->next ) {
++                      card = prev->next; 
++                      while ( card ) {
++                              if ( card->state == MMC_CARD_STATE_UNPLUGGED ) {
++                                      if ( ctrlr->stack.selected == card )
++                                              ctrlr->stack.selected = NULL;
++#ifdef CONFIG_PROC_FS
++                                      if ( card->proc ) {
++                                              remove_proc_entry( card->proc_name, ctrlr->proc );
++                                              card->proc = NULL;
++                                      }
++#endif
++                                      ctrlr->slot_next = card->slot; /* FIXME */
++                                      prev->next = card->next;
++                                      if ( ctrlr->stack.last == card )
++                                              ctrlr->stack.last = prev;
++                                      /* FIXME: controller use count */
++                                      mmc_notify_remove( card );
++                                      --ctrlr->stack.ncards;
++                                      if ( (ctrlr->usage > 0) && ctrlr->tmpl->owner ) {
++                                              --ctrlr->usage;
++                                              MMC_DEBUG( MMC_DEBUG_LEVEL2,
++                                                      "'%s' use count "
++                                                      "decreased (%d)\n",
++                                                      ctrlr->tmpl->name,
++                                                      ctrlr->usage );
++                                              __MOD_DEC_USE_COUNT( 
++                                                      ctrlr->tmpl->owner );
++                                      }
++                                      __mmc_card_free( card );
++                              
++                                      card = prev->next;
++                              }
++                      }
++              }
++              /* then the head */             
++              card = ctrlr->stack.first;
++              if ( card && (card->state == MMC_CARD_STATE_UNPLUGGED) ) {
++                      if ( ctrlr->stack.selected == card )
++                              ctrlr->stack.selected = NULL;
++#ifdef CONFIG_PROC_FS
++                      if ( card->proc ) {
++                              remove_proc_entry( card->proc_name, ctrlr->proc );
++                              card->proc = NULL;
++                      }
++#endif
++                      ctrlr->slot_next = card->slot; /* FIXME */
++                      mmc_notify_remove( card ); /* FIXME: should unregister here */
++                      ctrlr->stack.first = card->next;
++                      if ( ctrlr->stack.last == card )
++                              ctrlr->stack.last = NULL;
++                      /* FIXME: controller use count */
++                      --ctrlr->stack.ncards;
++                      if ( (ctrlr->usage > 0) && ctrlr->tmpl->owner ) {
++                              --ctrlr->usage;
++                              MMC_DEBUG( MMC_DEBUG_LEVEL2, "'%s' use count "
++                                      "decreased (%d)\n", ctrlr->tmpl->name,
++                                      ctrlr->usage );
++                              __MOD_DEC_USE_COUNT( ctrlr->tmpl->owner );
++                      }
++                      __mmc_card_free( card );
++              }
++      }
++      MMC_DEBUG( MMC_DEBUG_LEVEL2, "after stack check: ncards=%d"
++                      " first=0x%x last=0x%x\n", ctrlr->stack.ncards,
++                      ctrlr->stack.first, ctrlr->stack.last );
++      /* ...then add newly inserted ones */
++      if ( (ret = ctrlr->tmpl->update_acq( ctrlr )) )
++              goto error;
++      /* ret = 0; */
++error:                        
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++/*
++ *    1) check error code returned by controller; it's up to
++ *       controller to detect error conditions reported by the card
++ *       and to abort data transfer requests properly (e.g. send
++ *       CMD12(STOP_TRANSMISSION) to abort ADDRESS_ERROR multiple
++ *       block transfers)
++ *    2) arrange for card stack update when necessary
++ *       (all pending i/o requests must be held pending,
++ *       update procedure must start immediately after 
++ *       error has been detected)
++ */
++static inline int __mmc_check_error( mmc_card_t card, int err )
++{
++      int ret = -EIO;
++      mmc_controller_t ctrlr;
++      
++      __ENTER0();
++
++      if ( !card || !card->ctrlr )
++              goto error;
++      
++      ctrlr = card->ctrlr;
++      
++      if ( err < 0 ) {
++              switch ( err ) {
++              /* bus error occurred */
++              case MMC_ERROR_CRC_WRITE_ERROR:
++              case MMC_ERROR_CRC_READ_ERROR:
++              case MMC_ERROR_RES_CRC_ERROR:
++              case MMC_ERROR_READ_TIME_OUT:
++              case MMC_ERROR_TIME_OUT_RESPONSE:
++                      down_write( &ctrlr->update_sem ); /* FIXME */
++                      if ( !__mmc_update_card_stack( ctrlr ) )
++                              ret = -ENXIO;
++                      up_write( &ctrlr->update_sem );
++                      break;
++              }
++      } else
++              ret = err;
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static inline void __mmc_free_controller( mmc_controller_t ctrlr )
++{
++      if ( ctrlr ) {
++              if ( ctrlr->stack.ncards > 0 )
++                      __mmc_card_stack_free( &ctrlr->stack );
++              kfree( ctrlr );
++      }
++}
++
++#ifdef CONFIG_PROC_FS
++static int mmc_proc_read_card_info( char *page, char **start, off_t off, int count, int *eof, void *data )
++{
++      int ret = -EINVAL;
++      mmc_card_t card = (mmc_card_t)data;
++      char *cp = page;
++      
++      if ( !card )
++              goto error;
++      
++      down_read( &card->ctrlr->update_sem );
++/* TODO: proc report
++ * Type: RO, RW or IO (by CCC)
++ * MID: 0x%02x card->info.cid.mid
++ * OID: 0x%04x card->info.cid.oid
++ * PNM: %s card->info.pnm
++ * PRV: %s card->info.prv
++ * PSN: 0x%08x card->info.cid.psn
++ * MDT: %s card->info.mdt
++ * Capacity: card->info.capacity (Bytes)
++ */
++#if 1
++      cp += sprintf( cp, "Capacity: %dKb.\n\n", (card->info.capacity>>10) );
++#else /* TODO */
++      cp += sprintf( cp, "Type    : %s\n", card->info.type );
++      cp += sprintf( cp, "MID     : 0x%02x\n", card->info.cid.mid );
++      cp += sprintf( cp, "OID     : 0x%04x\n", card->info.cid.oid );
++      cp += sprintf( cp, "PNM     : %s\n", card->info.pnm );
++      cp += sprintf( cp, "PRV     : %s\n", card->info.prv );
++      cp += sprintf( cp, "PSN     : 0x%08x\n", card->info.cid.psn );
++      cp += sprintf( cp, "MDT     : %s\n", card->info.mdt );
++      cp += sprintf( cp, "Capacity: %dKB\n", 
++                      (card->info.capacity>>10) );
++#endif
++      up_read( &card->ctrlr->update_sem );
++      
++      ret = cp - page;
++error:
++      return ret;
++}
++#endif
++
++/*************************************
++ * MMC core interface implementation *
++ *************************************/
++int mmc_notify_add( mmc_card_t card )
++{
++      int ret = 0;
++      mmc_notifier_t notifier;
++      
++      __ENTER0();
++      if ( card ) {
++              for ( notifier = mmc_notifier; notifier;
++                    notifier = notifier->next )
++                      if ( notifier->add )
++                              if ( (ret = notifier->add( card )) )
++                                      break;
++      }
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++EXPORT_SYMBOL( mmc_notify_add );
++
++int mmc_notify_remove( mmc_card_t card )
++{
++      int ret = 0;
++      mmc_notifier_t notifier;
++      
++      __ENTER0();
++      if ( card ) {
++              for ( notifier = mmc_notifier; notifier; 
++                    notifier = notifier->next )
++                      if ( notifier->remove )
++                              if ( (ret = notifier->remove( card )) )
++                                      break;
++      }
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++EXPORT_SYMBOL( mmc_notify_remove );
++
++int mmc_update_card_stack( int host )
++{
++      int ret = -EINVAL;
++      mmc_controller_t ctrlr;
++
++      __ENTER0();
++      
++      if ( (host < 0) || (host >= MMC_CONTROLLERS_MAX) )
++              goto error;
++      
++      down_read( &mmc_controller_sem );
++      if ( (ctrlr = mmc_controller[host]) ) {
++              down_write( &ctrlr->update_sem );
++              (void)__mmc_update_card_stack( ctrlr );
++              up_write( &ctrlr->update_sem );
++      }
++      up_read( &mmc_controller_sem );
++      ret = 0;
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++EXPORT_SYMBOL( mmc_update_card_stack );
++
++ssize_t mmc_read( mmc_card_t card, mmc_transfer_mode_t mode, char *buf, size_t size, loff_t *paddr )
++{
++      ssize_t ret = -EIO;
++      mmc_controller_t ctrlr; 
++      mmc_data_transfer_req_rec_t transfer; 
++              
++      if ( !paddr ) {
++              ret = -EINVAL;
++              goto error;
++      }
++      
++      if ( !card ) {
++              ret = -ENODEV;
++              goto error;
++      }
++
++      __ENTER( "card=%p usage=%d mode=%d buf=%p size=%d addr=%x",
++              card, card->usage, mode, buf, size, *paddr );
++      
++      ctrlr = card->ctrlr;
++      if ( (ret = mmc_acquire_io( ctrlr, card )) )
++              goto error;
++      
++      memset( &transfer, 0, sizeof( mmc_data_transfer_req_rec_t ) );
++      transfer.cmd = MMC_READ;
++      transfer.mode = mode;
++      transfer.type = MMC_USER; /* FIXME: buffer cache */
++      transfer.buf = buf;
++      transfer.addr = *paddr;
++      transfer.cnt = size;
++
++/* max block size defined by CSD[read_bl_len] */
++      transfer.blksz = card->info.read_bl_len;
++      transfer.nob = size / transfer.blksz;
++      if ( (size - (transfer.nob * transfer.blksz)) > 0 )
++              transfer.nob++;
++
++/* TODO: controller may restrict maximum block size; set block size
++ * and number of blocks that their accumulated length fit to
++ * CSD[READ_BL_LEN] not to bother with block misalignment in multiple
++ * block transfers */
++      ctrlr = card->ctrlr;
++      if ( transfer.blksz > ctrlr->tmpl->block_size_max ) { 
++              ret = -EINVAL; /* FIXME */
++              goto error;
++      }
++              
++      if ( ctrlr->stack.selected != card ) {
++              if ( (ret = ctrlr->tmpl->setup_card( ctrlr, card )) )
++                      goto err_mmc;           
++              ctrlr->stack.selected = card;
++      }
++      
++      switch( mode ) {
++              case MMC_TRANSFER_MODE_STREAM:
++                      if ( !ctrlr->tmpl->stream_read ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++/* TODO: The max clock frequency for stream read operation is given by
++   the following formula:
++      max speed = min ( TRAN_SPEED, 8*2^(READ_BL_LEN) - NSAC/TAAC )
++
++   If the card is not able to sustain data transfer it will set the
++   UNDERRUN error bit in the status register, abort the transmission
++   and wait in the Data state for a stop command 
++ */
++                      ret = ctrlr->tmpl->stream_read( ctrlr, &transfer );
++                      break;
++
++              case MMC_TRANSFER_MODE_BLOCK_SINGLE:
++                      if ( !ctrlr->tmpl->read_block ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++/* TODO: buffer size and data alignment (v3.4, p.29): 
++      if CSD[READ_BL_PARTIAL] is set, smaller blocks whose starting
++      and ending address are entirely contained within one physical
++      block (as defined by CSD[READ_BL_LEN]) may also be transmitted
++ */
++                      transfer.type = MMC_KERNEL; /* FIXME */
++                      ret = ctrlr->tmpl->read_block( ctrlr, &transfer );
++                      break;
++                      
++              case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:
++                      if ( !ctrlr->tmpl->read_mblock ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++                      
++                      if ( transfer.nob > ctrlr->tmpl->nob_max ) {
++                              ret = -EINVAL;
++                              goto error;
++                      }
++/* TODO: buffer size and data alignment (v3.4, p.29): 
++      if the host uses patrial blocks whose accumulated length is
++      not block aligned and block misalignment is not allowed, the
++      card should detect a block misalignment error condition at the
++      beginning of the first misaligned block
++ */
++                      transfer.type = MMC_KERNEL; /* FIXME */
++                      ret = ctrlr->tmpl->read_mblock( card->ctrlr, &transfer );
++                      break;
++              
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "request for unknown transfer type\n" );
++                      ret = -EINVAL;
++      }
++err_mmc:
++      ret = __mmc_check_error( card, ret );
++      if ( ret >= 0 ) {
++              ret = size - transfer.cnt;
++              *paddr += ret;
++      }
++err_down:
++      mmc_release_io( ctrlr, card );
++error:
++      __LEAVE("ret=%d", ret);
++      return ret;
++}
++EXPORT_SYMBOL( mmc_read );
++
++ssize_t mmc_write( mmc_card_t card, mmc_transfer_mode_t mode, const char *buf, size_t size, loff_t *paddr )
++{
++      ssize_t ret = -ESPIPE;
++      mmc_controller_t ctrlr;
++      mmc_data_transfer_req_rec_t transfer; 
++              
++      if ( !paddr ) {
++              ret = -EINVAL;
++              goto error;
++      }
++      
++      if ( !card ) {
++              ret = -ENODEV;
++              goto error;
++      }
++
++      __ENTER( "card=%p usage=%d mode=%d buf=%p size=%d addr=%llx",
++              card, card->usage, mode, buf, size, *paddr );
++
++      ctrlr = card->ctrlr;    
++      if ( (ret = mmc_acquire_io( ctrlr, card )) )
++              goto error;
++      
++      memset( &transfer, 0, sizeof( mmc_data_transfer_req_rec_t ) );
++      transfer.cmd = MMC_WRITE;
++      transfer.mode = mode;
++      transfer.type = MMC_USER; /* FIXME: buffer cache */
++      transfer.buf = (char *)buf;
++      transfer.addr = *paddr;
++      transfer.cnt = size;
++
++/* max block size defined by CSD[write_bl_len] */
++      transfer.blksz = card->info.write_bl_len;
++      transfer.nob = size / transfer.blksz;
++      if ( (size - (transfer.nob * transfer.blksz)) > 0 )
++              transfer.nob++;
++
++/* TODO: controller may restrict maximum block size; set block size
++ * and number of blocks that their accumulated length fit to
++ * CSD[WRITE_BL_LEN] not to bother with block misalignment in multiple
++ * block transfers */
++      ctrlr = card->ctrlr;
++      if ( transfer.blksz > ctrlr->tmpl->block_size_max ) { 
++              ret = -EINVAL; /* FIXME */
++              goto error;
++      }
++              
++      if ( ctrlr->stack.selected != card ) {
++              if ( (ret = ctrlr->tmpl->setup_card( ctrlr, card )) )
++                      goto err_mmc;           
++              ctrlr->stack.selected = card;
++      }
++      
++      transfer.cmd = MMC_WRITE;
++      transfer.mode = mode;
++      transfer.type = MMC_USER;
++      switch( mode ) {
++              case MMC_TRANSFER_MODE_STREAM:
++                      if ( !ctrlr->tmpl->stream_write ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++                      ret = ctrlr->tmpl->stream_write( ctrlr, &transfer );
++                      break;
++
++              case MMC_TRANSFER_MODE_BLOCK_SINGLE:
++                      if ( !ctrlr->tmpl->write_block ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++                      transfer.type = MMC_KERNEL; /* FIXME */
++                      ret = ctrlr->tmpl->write_block( ctrlr, &transfer );
++                      break;
++                      
++              case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:
++                      if ( !ctrlr->tmpl->write_mblock ) {
++                              ret = -ENXIO;
++                              goto err_down;
++                      }
++                      transfer.type = MMC_KERNEL; /* FIXME */
++                      ret = ctrlr->tmpl->write_mblock( card->ctrlr, &transfer );
++                      break;
++              
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "request for unknown transfer type\n" );
++      }
++
++err_mmc:
++      ret = __mmc_check_error( card, ret ); /* FIXME */
++      if ( ret >= 0 ) {
++              ret = size - transfer.cnt;
++              *paddr += ret;
++      }
++err_down:
++      mmc_release_io( ctrlr, card );
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++EXPORT_SYMBOL( mmc_write );
++
++int mmc_ioctl( mmc_card_t card, unsigned int cmd, unsigned long arg )
++{
++      int ret = -EINVAL;
++      mmc_controller_t ctrlr;
++      
++      if ( !card ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "bad card reference\n" )
++              goto error;
++      }
++              
++      ctrlr = card->ctrlr;
++      if ( mmc_acquire_io( ctrlr, card ) ) {  
++              ret = -ENXIO;
++              goto error;
++      }
++
++      switch ( cmd ) {
++              case IOCMMCGCARDESC:
++                      if ( copy_to_user( (void *)arg, &card->info, sizeof( mmc_card_info_rec_t ) ) )
++                              ret = -EFAULT;
++                      break;
++/*    
++ * 1. TODO: erase region
++ * 2. TODO: set/unset write protection, lock/password 
++ */
++              default:
++                      ret = -ENOIOCTLCMD;
++      }
++      
++      mmc_release_io( ctrlr, card );
++error:
++      return ret;
++}
++EXPORT_SYMBOL( mmc_ioctl );
++
++/* 
++ * registry stuff 
++ */
++mmc_card_t mmc_get_card( int host, int slot )
++{
++      mmc_card_t ret = NULL;
++      mmc_controller_t ctrlr = NULL;
++      int found;
++
++      __ENTER( "host=%d, card=%d", host, slot );
++      
++      if ( ((host < 0) || (host >= MMC_CONTROLLERS_MAX)) 
++           && ((slot < 0) || (slot >= MMC_CARDS_MAX)) )
++              goto error;     
++
++      down_read( &mmc_controller_sem );
++      
++      if ( (ctrlr = mmc_controller[host]) ) {
++              down_write( &ctrlr->update_sem );
++              if ( ctrlr->stack.ncards > 0 ) {
++                      ret = ctrlr->stack.first;
++                      found = FALSE;
++                      while ( ret ) {
++                              if ( (ret->slot == slot) && (ret->state != 
++                                   MMC_CARD_STATE_UNPLUGGED) ) {
++                                      found = TRUE;
++                                      break;
++                              }
++                              ret = ret->next;
++                      }
++
++                      if ( found ) {
++                              if ( ctrlr->tmpl->owner ) {
++                                      ++ctrlr->usage;
++                                      MMC_DEBUG( MMC_DEBUG_LEVEL2, 
++                                          "'%s' use count increased (%d)\n", 
++                                          ctrlr->tmpl->name, ctrlr->usage );
++                                      __MOD_INC_USE_COUNT( ctrlr->tmpl->owner );
++                              }
++                              ++ret->usage;
++                      } else
++                              ret = NULL;
++              }
++              up_write( &ctrlr->update_sem ); 
++      }
++      up_read( &mmc_controller_sem );
++error:
++      __LEAVE("ret=0x%p usage=%d", ret, ret ? ret->usage : -1 );
++      return ret;
++}
++EXPORT_SYMBOL( mmc_get_card );
++
++void mmc_put_card( mmc_card_t card )
++{
++      mmc_card_t tmp = NULL;
++      mmc_controller_t ctrlr;
++      int found;
++      
++      __ENTER( "card=0x%p", card );
++
++      if ( !card )
++              goto error;     
++
++      ctrlr = card->ctrlr;
++      
++      down_read( &mmc_controller_sem );
++      if ( !ctrlr || (ctrlr != mmc_controller[ctrlr->slot]) ) {
++              MMC_ERROR( "bad controller reference: ctrlr=0x%p\n", ctrlr );
++              goto err_down;
++      }
++      
++      down_write( &ctrlr->update_sem );
++      if ( ctrlr->stack.ncards > 0 ) {
++              tmp = ctrlr->stack.first;
++              found = FALSE;
++              while ( tmp ) {
++                      if ( tmp == card ) {
++                              found = TRUE;
++                              break;
++                      }
++                      tmp = tmp->next;
++              }
++
++              if ( found ) {
++                      if ( tmp->usage > 0 ) {
++                              --tmp->usage;
++                              MMC_DEBUG( MMC_DEBUG_LEVEL2, "usage=%d"
++                                      "owner=0x%p\n", tmp->usage, 
++                                      ctrlr->tmpl->owner );
++                              if ( !tmp->usage && (ctrlr->usage > 0) 
++                                   && ctrlr->tmpl->owner ) {
++                                      --ctrlr->usage;
++                                      MMC_DEBUG( MMC_DEBUG_LEVEL2, 
++                                              "'%s' use count "
++                                              "decreased (%d)\n", 
++                                              ctrlr->tmpl->name, 
++                                              ctrlr->usage );
++                                      __MOD_DEC_USE_COUNT( 
++                                                      ctrlr->tmpl->owner );
++                              }
++                      }
++              } else
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "bad card reference\n" );
++                      
++      }
++      up_write( &ctrlr->update_sem ); 
++err_down:
++      up_read( &mmc_controller_sem );
++error:
++      __LEAVE( "found=%d", found );
++      return;
++}
++EXPORT_SYMBOL( mmc_put_card );
++
++static inline void *mmc_register_user( mmc_notifier_t notifier )
++{
++      mmc_notifier_t ret = NULL, last = mmc_notifier;
++      
++      MOD_INC_USE_COUNT;
++      if ( notifier ) {
++              down_write( &mmc_notifier_sem );
++              
++              notifier->next = NULL;
++              if ( !last ) {
++                      mmc_notifier = notifier;
++                      ret = notifier; 
++              } else {
++                      while ( last->next ) {
++                              if ( last == notifier ) {
++                                      MOD_DEC_USE_COUNT;
++                                      break;
++                              }
++                              last = last->next;
++                      }
++                      if ( last != notifier ) {
++                              last->next = notifier;
++                              ret = notifier;
++                      }
++              }
++              up_write( &mmc_notifier_sem );
++      }
++/* notify new user about the cards present in the system */
++      if ( ret && ret->add ) {
++              int i;
++              
++              down_read( &mmc_controller_sem );
++              for ( i = 0; i < mmc_ncontrollers; i++ ) {
++                      mmc_controller_t ctrlr = mmc_controller[i];
++                      
++                      down_read( &ctrlr->update_sem ); /* FIXME */
++                      __mmc_card_stack_foreach( &ctrlr->stack, 
++                                      ret->add, FALSE );
++                      up_read( &ctrlr->update_sem ); /* FIXME */
++              }
++              up_read( &mmc_controller_sem );
++      }
++/* error: */
++      __LEAVE( "mmc_notifier=0x%p, mmc_notifier->next=0x%p",
++                      mmc_notifier, mmc_notifier ? mmc_notifier->next : NULL );
++      return ret;
++}
++
++static inline mmc_controller_t mmc_register_controller( mmc_controller_tmpl_t tmpl, size_t extra )
++{
++      mmc_controller_t ret = NULL;
++      int found;
++      int i;
++      
++      MOD_INC_USE_COUNT;
++      
++      down_write( &mmc_controller_sem );
++      
++      if ( mmc_ncontrollers >= MMC_CONTROLLERS_MAX ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "there're too many controllers\n" );
++              goto error;
++      }
++      
++      found = FALSE;
++      for ( i = 0; i < MMC_CONTROLLERS_MAX; i++ )
++              if ( !mmc_controller[i] ) {
++                      found = TRUE;
++                      break;
++              }
++      
++      if ( !found ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "there're no empty slots\n" );
++              goto error;
++      }
++
++      if ( !tmpl->init ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'init()'\n" );
++              goto error;
++      }
++      
++      if ( !tmpl->probe ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'probe()'\n" );
++              goto error;
++      }
++      
++      if ( !tmpl->init_card_stack ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'init_card_stack()'\n" );
++              goto error;
++      }
++      
++      if ( !tmpl->update_acq ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'update_acq()'\n" );
++              goto error;
++      }
++      
++      if ( !tmpl->check_card_stack ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'check_card_stack()'\n" );
++              goto error;
++      }
++      
++      if ( !tmpl->setup_card ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "host template lacks 'setup_card()'\n" );
++              goto error;
++      }
++      
++      ret = kmalloc( sizeof( mmc_controller_rec_t ) + extra, GFP_ATOMIC ); /* FIXME: ISA + GFP_DMA */
++      if ( !ret ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "out of memory\n" );
++              goto error;
++      }
++
++      memset( ret, 0, sizeof( mmc_controller_rec_t ) + extra );
++      
++      if ( (tmpl->probe( ret ) != 1) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "controller probe failed\n" );
++              goto err_free;
++      }
++      
++      if ( tmpl->init( ret ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "controller initialization failure\n" );
++              goto err_free;
++      }
++      
++      ret->state = MMC_CONTROLLER_FOUND;
++      ret->slot = i;
++      ret->tmpl = tmpl;
++      init_MUTEX( &ret->io_sem );
++      init_rwsem( &ret->update_sem );
++#ifdef CONFIG_PROC_FS
++      if ( mmc_proc_dir ) {
++              snprintf( ret->proc_name, sizeof( ret->proc_name ),
++                              "host%d", ret->slot );
++              ret->proc = proc_mkdir( ret->proc_name, mmc_proc_dir );
++      }
++#endif
++      
++/* initialize card stack */
++      if ( ret->tmpl->init_card_stack( ret ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "card stack initialization failure\n" );
++              if ( ret->tmpl->remove )
++                      ret->tmpl->remove( ret ); /* FIXME */
++              goto err_free;
++      }
++      
++      mmc_controller[ret->slot] = ret;
++      ++mmc_ncontrollers;
++
++/* notify users */
++      if ( ret->stack.ncards > 0 ) {
++              down_read( &mmc_notifier_sem );
++              if ( (i = __mmc_card_stack_foreach( &ret->stack, mmc_notify_add, FALSE ) ) < 0 )
++                      MMC_ERROR( "device add notification failed at slot %d\n", -i );
++              up_read( &mmc_notifier_sem );
++      }
++      goto out;
++      
++err_free:
++#ifdef CONFIG_PROC_FS
++      if ( ret->proc )
++              remove_proc_entry( ret->proc_name, mmc_proc_dir );
++#endif
++      kfree( ret );
++error:
++      ret = NULL;
++      MOD_DEC_USE_COUNT;
++out:  
++      up_write( &mmc_controller_sem );
++      return ret;
++}
++
++static inline mmc_card_t mmc_register_card( mmc_card_t card )
++{
++      mmc_card_t ret = NULL;
++      mmc_controller_t ctrlr;
++      
++      if ( !card || !card->ctrlr )
++              goto error;
++      
++      ctrlr = card->ctrlr;
++#ifdef CONFIG_PROC_FS
++      if ( ctrlr->proc ) {
++              snprintf( card->proc_name, sizeof( card->proc_name ),
++                                      "card%d", card->slot );
++              card->proc = create_proc_read_entry( card->proc_name,
++                                      0444, ctrlr->proc,
++                                      mmc_proc_read_card_info, card );
++      }
++#endif
++      mmc_notify_add( card );
++error:
++      return ret;
++}
++
++void *mmc_register( mmc_reg_type_t reg_type, void *tmpl, size_t extra )
++{
++      void *ret = NULL;
++
++      switch ( reg_type ) {
++              case MMC_REG_TYPE_CARD:
++                      ret = mmc_register_card( (mmc_card_t)tmpl );
++                      break;
++
++              case MMC_REG_TYPE_USER:
++                      ret = mmc_register_user( (mmc_notifier_t)tmpl );
++                      break;
++
++              case MMC_REG_TYPE_HOST:
++                      ret = mmc_register_controller( (mmc_controller_tmpl_t)tmpl, extra );
++                      break;
++              
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "register request for unknown type\n" );
++      }
++      
++      return ret;
++}
++EXPORT_SYMBOL( mmc_register );
++
++static inline void mmc_unregister_user( mmc_notifier_t notifier )
++{
++      mmc_notifier_t prev = mmc_notifier;
++      int found = FALSE;
++      
++      if ( notifier ) {
++              down_write( &mmc_notifier_sem );
++              
++              if ( mmc_notifier == notifier) {
++                      mmc_notifier = prev->next;
++                      found = TRUE;
++                      
++              } else if ( mmc_notifier ) {
++                      while( prev ) {
++                              if ( prev->next == notifier ) {
++                                      found = TRUE;
++                                      prev->next = prev->next->next;
++                                      break;
++                              }
++                              prev = prev->next;
++                      }
++              }
++              
++              if ( found ) {
++                      if ( notifier->remove ) {
++                              int i;
++              
++                              down_read( &mmc_controller_sem );
++                              for ( i = 0; i < mmc_ncontrollers; i++ ) {
++                                      mmc_controller_t ctrlr = 
++                                              mmc_controller[i];
++      
++                                      down_read( &ctrlr->update_sem );
++                                      __mmc_card_stack_foreach( &ctrlr->stack, notifier->remove, FALSE );
++                                      up_read( &ctrlr->update_sem );
++                              }
++                              up_read( &mmc_controller_sem );
++                      }
++              }
++              
++              up_write( &mmc_notifier_sem );
++      }
++      
++      MOD_DEC_USE_COUNT;
++}
++
++static inline void mmc_unregister_controller( mmc_controller_t ctrlr )
++{
++      if ( !ctrlr || (mmc_controller[ctrlr->slot] != ctrlr ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "bad unregister request\n" );
++              goto error;
++      }
++
++      down_write( &mmc_controller_sem );
++
++/* notify users */
++      if ( ctrlr->stack.ncards > 0 ) {
++              int slot;
++              
++              down_read( &mmc_notifier_sem );
++              if ( (slot = __mmc_card_stack_foreach( &ctrlr->stack, mmc_notify_remove, FALSE ) ) )
++                      MMC_ERROR( "device remove notification failed at slot %d\n", -slot );
++              up_read( &mmc_notifier_sem );
++      }
++
++#ifdef CONFIG_PROC_FS
++      if ( ctrlr->proc ) 
++              remove_proc_entry( ctrlr->proc_name, mmc_proc_dir );
++#endif
++
++      if ( ctrlr->tmpl && ctrlr->tmpl->remove )
++                      ctrlr->tmpl->remove( ctrlr );
++
++      mmc_controller[ctrlr->slot] = NULL;
++      --mmc_ncontrollers;
++
++      __mmc_free_controller( ctrlr ); 
++
++      up_write( &mmc_controller_sem );
++      MOD_DEC_USE_COUNT;
++error:
++      return;
++}
++
++void mmc_unregister( mmc_reg_type_t reg_type, void *tmpl )
++{
++      switch ( reg_type ) {
++              case MMC_REG_TYPE_USER:
++                      mmc_unregister_user( (mmc_notifier_t)tmpl );
++                      break;
++
++              case MMC_REG_TYPE_HOST:
++                      mmc_unregister_controller( (mmc_controller_t)tmpl );
++                      break;
++              
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "unregister request for unknown type\n" );
++      }
++}
++EXPORT_SYMBOL( mmc_unregister );
++
++#ifdef CONFIG_PM
++/* power management support */
++static int mmc_pm_callback( struct pm_dev *pmdev, pm_request_t pmreq, void *pmdata )
++{
++      int ret = -EINVAL;
++      mmc_controller_t ctrlr;
++      int i;
++      
++      __ENTER( "pmreq=%d", pmreq );
++      
++      down_read( &mmc_controller_sem );
++      
++      switch ( pmreq ) {
++      case PM_SUSPEND:
++              for ( ret = 0, i = 0; !ret && (i < mmc_ncontrollers); i++ ) {
++                      ctrlr = mmc_controller[i];
++                      if ( ctrlr->tmpl->suspend )
++                              ret = ctrlr->tmpl->suspend( ctrlr );
++              }
++              if ( !ret )
++                      break;
++              
++      case PM_RESUME:
++              for ( i = mmc_ncontrollers - 1; i >= 0; i-- ) {
++                      ctrlr = mmc_controller[i];
++                      if ( ctrlr->tmpl->resume )
++                              ctrlr->tmpl->resume( ctrlr );
++              }
++              ret = 0;
++              break;
++
++      default:
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "unsupported PM request %d\n", 
++                                      pmreq );
++      }
++
++      up_read( &mmc_controller_sem );
++/* error: */  
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++#endif
++
++/* kernel module stuff */
++static int __init mmc_core_module_init( void )
++{
++      int ret = -ENODEV;
++      
++      memset( &mmc_controller, 0, sizeof( mmc_controller ) );
++      
++      init_rwsem( &mmc_controller_sem );
++      init_rwsem( &mmc_notifier_sem );
++#ifdef CONFIG_PM
++      if ( !(mmc_pm_dev = pm_register( PM_UNKNOWN_DEV, 0, mmc_pm_callback )) )                MMC_DEBUG( MMC_DEBUG_LEVEL0, "failed to register PM callback\n" );
++#endif
++#ifdef CONFIG_PROC_FS
++      mmc_proc_dir = proc_mkdir( "mmc", NULL );
++#endif
++      ret = 0;
++/* error: */
++      return ret;
++}
++
++static void __exit mmc_core_module_cleanup( void )
++{
++#ifdef CONFIG_PROC_FS
++      if ( mmc_proc_dir )
++              remove_proc_entry( "mmc", NULL );
++#endif
++#ifdef CONFIG_PM
++      pm_unregister( mmc_pm_dev );
++#endif
++}
++
++module_init( mmc_core_module_init );
++module_exit( mmc_core_module_cleanup );
++
++MODULE_LICENSE( "GPL" );
++
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc_pxa.c
+@@ -0,0 +1,1902 @@
++/*
++ *  linux/drivers/mmc/mmc_pxa.c 
++ *      driver for Cotulla MMC controller 
++ *
++ *  Authors:    Vladimir Shebordaev, Igor Oblakov   
++ *  Copyright:  MontaVista Software Inc.
++ *
++ *  $Id: mmc_pxa.c,v 0.3.1.12 2002/09/25 19:25:48 ted Exp ted $
++ *
++ *  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/version.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/dma.h>
++
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++
++#include <mmc/types.h>
++#include <mmc/mmc.h>
++#include <mmc/ioctl.h>
++
++#include "types.h"
++#include "mmc.h"
++#include "mmc_pxa.h"
++
++static mmc_controller_t host = NULL;
++
++/* service routines */
++static inline int pxa_mmc_check_state( mmc_controller_t ctrlr, pxa_mmc_state_t state )
++{
++    int ret = -1;
++    pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++    
++    if ( hostdata->state != state ) {
++        //MMC_DEBUG( MMC_DEBUG_LEVEL3,  "state (%s vs %s)\n",  PXA_MMC_STATE_LABEL( hostdata->state ),  PXA_MMC_STATE_LABEL( state ) );
++        goto error;
++    }
++    ret = 0;
++error:          
++    return ret;
++}
++
++static inline void pxa_mmc_set_state( mmc_controller_t ctrlr, pxa_mmc_state_t state )
++{
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++    
++      hostdata->state = state;
++}
++
++static inline int pxa_mmc_init_completion( mmc_controller_t ctrlr, u32 mask )
++{
++      int ret = -1;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++    
++      if ( xchg( &hostdata->busy, 1 ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "another interrupt "
++                      "is already been expected\n" );
++              goto error;
++      }
++    
++#if CONFIG_MMC_DEBUG_IRQ 
++      hostdata->irqcnt = 1000;
++#endif
++      init_completion( &hostdata->completion );
++    
++      MMC_I_MASK = MMC_I_MASK_ALL & ~mask;
++      ret = 0;
++error:
++      return ret;
++}
++
++#if CONFIG_MMC_DEBUG_IRQ 
++static struct timer_list timer;
++static void wait_timeo( unsigned long arg ) {
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)arg;
++      hostdata->timeo = 1;
++      complete( &hostdata->completion );
++      return;
++}
++#endif
++
++static inline int pxa_mmc_wait_for_completion( mmc_controller_t ctrlr, u32 mask )
++{
++      int ret = -1;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++    
++      if ( !xchg( &hostdata->busy, 1 ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "there were no "
++                              "interrupt awaited for\n" );
++              goto error;
++      }
++
++#if CONFIG_MMC_DEBUG_IRQ
++      hostdata->timeo = 0;
++      del_timer( &timer );
++      timer.function = wait_timeo;
++      timer.expires = jiffies + 1UL*HZ;
++      timer.data = (unsigned long)hostdata;
++      add_timer( &timer );
++#endif
++      wait_for_completion( &hostdata->completion );
++#if CONFIG_MMC_DEBUG_IRQ
++      del_timer( &timer );
++      if ( hostdata->timeo ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "irq timed out: " "mask=%x stat=%x\n", mask, MMC_STAT );
++              goto error;
++      }
++#endif        
++      /*  verify interrupt */
++      if ( (mask == ~0UL) || !( hostdata->mmc_i_reg & ~mask ) ) 
++              ret = 0;
++    
++error:
++      xchg( &hostdata->busy, 0 ); 
++      return ret;
++}
++
++static inline int pxa_mmc_stop_bus_clock( mmc_controller_t ctrlr )
++{
++      int ret = -1;
++    
++      if ( !pxa_mmc_check_state( ctrlr, PXA_MMC_FSM_CLK_OFF ) )
++              goto out;
++              
++      if ( !pxa_mmc_check_state( ctrlr, PXA_MMC_FSM_BUFFER_IN_TRANSIT ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "BUFFER_IN_TRANSIT\n" );
++              goto error;
++      }
++      
++      if ( pxa_mmc_init_completion( ctrlr, MMC_I_MASK_CLK_IS_OFF ) )
++              goto error;
++    
++      MMC_STRPCL = MMC_STRPCL_STOP_CLK;
++      
++      if ( pxa_mmc_wait_for_completion( ctrlr, MMC_I_REG_CLK_IS_OFF ) )
++              goto error;
++    
++      //MMC_DEBUG( MMC_DEBUG_LEVEL3, "clock is off\n" );
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_CLK_OFF );
++out:
++      ret = 0;
++error:
++      return ret;
++}
++
++static inline int pxa_mmc_start_bus_clock( mmc_controller_t ctrlr )
++{
++      int ret = -1;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;    
++
++      if ( (hostdata->state != PXA_MMC_FSM_CLK_OFF)
++           && (hostdata->state != PXA_MMC_FSM_END_IO) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "illegal state %s\n", PXA_MMC_STATE_LABEL( hostdata->state ) );
++              goto error;
++      }
++    
++      MMC_STRPCL = MMC_STRPCL_START_CLK;
++      wmb();
++      //MMC_DEBUG( MMC_DEBUG_LEVEL3, "clock is on\n" ); 
++      ret = 0;
++error:
++      return ret;
++}
++
++/* 
++int pxa_mmc_complete_cmd( mmc_controller_t ctrlr, mmc_response_fmt_t response )
++
++Effects: initializes completion to wait for END_CMD_RES intr,
++         waits for intr to occur, checks controller and card status 
++Requiers: controller is in CLK_OFF state
++Modifies: moves controller to the END_CMD state
++Returns: 
++*/ 
++static mmc_error_t pxa_mmc_complete_cmd( mmc_controller_t ctrlr, mmc_response_fmt_t format, int send_abort )
++{
++      mmc_error_t ret = MMC_ERROR_GENERIC;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      int mask, nwords;
++      u32 status;
++    
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD%d(0x%04x%04x)\n", MMC_CMD & 0x3f, MMC_ARGH, MMC_ARGL);
++
++/* FIXME: check arguments */
++    
++      if ( (hostdata->state != PXA_MMC_FSM_CLK_OFF)
++           && (hostdata->state != PXA_MMC_FSM_END_IO) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "illegal state %s\n",
++                              PXA_MMC_STATE_LABEL( hostdata->state ) );
++              goto error;
++      }
++    
++      mask = MMC_I_MASK_END_CMD_RES;
++      if ( pxa_mmc_init_completion( ctrlr, mask ) )
++              goto error;
++    
++      MMC_PRTBUF = MMC_PRTBUF_BUF_FULL; 
++/* start the clock */
++      if ( pxa_mmc_start_bus_clock( ctrlr ) )
++              goto error;
++    
++/* wait for END_CMD_RES intr */
++      if ( pxa_mmc_wait_for_completion( ctrlr, MMC_I_REG_END_CMD_RES ) )
++              goto error;
++
++/* check status */
++      if ( hostdata->mmc_stat & MMC_STAT_TIME_OUT_RESPONSE ) {
++              // MMC_DEBUG(MMC_DEBUG_LEVEL3, "response timeout\n");
++              ret = MMC_ERROR_TIME_OUT_RESPONSE;
++              goto error;
++    
++      } else if ( hostdata->mmc_stat & MMC_STAT_READ_TIME_OUT ) {
++              // MMC_DEBUG(MMC_DEBUG_LEVEL3, "read timeout\n");
++              ret = MMC_ERROR_READ_TIME_OUT;
++              goto error;
++    
++      } else if ( hostdata->mmc_stat & MMC_STAT_RES_CRC_ERROR ) {
++              // MMC_DEBUG(MMC_DEBUG_LEVEL3, "response crc err\n");
++              ret = MMC_ERROR_RES_CRC_ERROR;
++              goto error;
++    
++      } else if ( hostdata->mmc_stat & MMC_STAT_CRC_READ_ERROR ) {
++              // MMC_DEBUG(MMC_DEBUG_LEVEL3, "read crc err\n");
++              ret = MMC_ERROR_CRC_READ_ERROR;
++              goto error;
++        
++      } else if ( hostdata->mmc_stat & MMC_STAT_CRC_WRITE_ERROR ) {
++              // MMC_DEBUG(MMC_DEBUG_LEVEL3, "write crc err\n");
++              ret = MMC_ERROR_CRC_WRITE_ERROR;
++              goto error;
++      }
++    
++      nwords = (format == MMC_NORESPONSE) ? 0 :
++              (format == MMC_R1) ? 3 : 
++              (format == MMC_R2) ? 8 :
++              (format == MMC_R3) ? 3 : 
++              -1;
++      ret = nwords;
++      if ( nwords > 0 ) {
++              register int i;
++
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "nwords=%d\n", nwords );
++              for ( i = nwords - 1; i >= 0 ; i-- ) {
++                      u32 res = MMC_RES;
++                      int ibase = i<<1;
++
++                      hostdata->mmc_res[ibase] = ((u8 *)&res)[0];
++                      hostdata->mmc_res[ibase + 1] = ((u8 *)&res)[1];
++                      --ret;
++              }
++#ifdef CONFIG_MMC_DEBUG
++              switch ( format ) {
++              case MMC_R1:
++                      MMC_DUMP_R1( ctrlr );
++                      break;
++              case MMC_R2:
++                      MMC_DUMP_R2( ctrlr );
++                      break;
++              case MMC_R3:
++                      MMC_DUMP_R3( ctrlr );
++                      break;
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                                      "unknown response format\n" );
++                      ret = MMC_ERROR_GENERIC;
++                      goto error;
++              }
++#endif
++
++/* check card status for R1(b) commands */
++              if ( format == MMC_R1 ) {
++                      u8 cmd;
++                      
++                      ((u8 *)&status)[0] = hostdata->mmc_res[1];
++                      ((u8 *)&status)[1] = hostdata->mmc_res[2];
++                      ((u8 *)&status)[2] = hostdata->mmc_res[3];
++                      ((u8 *)&status)[3] = hostdata->mmc_res[4];
++                      cmd = PXA_MMC_RESPONSE( ctrlr, 5 )&0x3f;
++                      MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                      //printk( KERN_INFO __FUNCTION__"(): "
++                              "cmd=%u status: 0x%08x\n",
++                              cmd, status );
++                      switch ( cmd ) {
++                      case 11:
++                      case 18:
++                      case 20:
++                      case 25:
++                              if ( !(status & 0x00000100) ) /* FIXME */
++                                      goto mmc_error;
++                      default:
++                              break;
++                      }
++                      if ( status & MMC_CARD_STATUS_OUT_OF_RANGE ) {
++                              ret = MMC_ERROR_OUT_OF_RANGE;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ADDRESS_ERROR ) {
++                              ret = MMC_ERROR_ADDRESS_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_BLOCK_LEN_ERROR ) {
++                              ret = MMC_ERROR_ADDRESS_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ERASE_SEQ_ERROR ) {
++                              ret = MMC_ERROR_ERASE_SEQ_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ERASE_PARAM ) {
++                              ret = MMC_ERROR_ERASE_PARAM;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_WP_VIOLATION ) {
++                              ret = MMC_ERROR_WP_VIOLATION;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_CARD_IS_LOCKED ) {
++                              ret = MMC_ERROR_CARD_IS_LOCKED;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_LOCK_UNLOCK_FAILED ) {
++                              ret = MMC_ERROR_LOCK_UNLOCK_FAILED;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_COM_CRC_ERROR ) {
++                              ret = MMC_ERROR_COM_CRC_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ILLEGAL_COMMAND ) {
++                              ret = MMC_ERROR_ILLEGAL_COMMAND;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_CARD_ECC_FAILED ) {
++                              ret = MMC_ERROR_CARD_ECC_FAILED;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_CC_ERROR ) {
++                              ret = MMC_ERROR_CC_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ERROR ) {
++                              ret = MMC_ERROR_ERROR;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_UNDERRUN ) {
++                              ret = MMC_ERROR_UNDERRUN;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_OVERRUN ) {
++                              ret = MMC_ERROR_OVERRUN;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_CID_CSD_OVERWRITE ) {
++                              ret = MMC_ERROR_CID_CSD_OVERWRITE;
++                              goto mmc_error;
++                      } else if ( status & MMC_CARD_STATUS_ERASE_RESET ) {
++                              ret = MMC_ERROR_ERASE_RESET;
++                              goto mmc_error;
++                      }
++              }
++      }
++    
++      if ( ret >= 0 )
++              pxa_mmc_set_state( ctrlr,  PXA_MMC_FSM_END_CMD );
++      goto out;
++mmc_error:
++#if 1
++      if ( send_abort ) {
++              /* send CMD12 to abort failed transfer */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++    
++              MMC_CMD = CMD(12); /* STOP_TRANSMISSION */
++              MMC_CMDAT = MMC_CMDAT_R1;
++              
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++              
++              ret = -EIO;
++              goto error;
++      }
++#endif
++error:
++      /* move controller to the IDLE state */
++      pxa_mmc_stop_bus_clock( ctrlr );
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_IDLE );
++out:
++      return ret;
++}
++
++/* 
++int pxa_mmc_complete_io( mmc_controller_t ctrlr, mmc_dir_t cmd, mmc_dir_t dir, mmc_transfer_mode_t mode )
++
++Effects: finilizes data transfer request  
++Reqires: controller is in the END_BUFFER state
++Modifies: moves controller to the IDLE state
++Returns: zero upon success or error condition code otherwise
++ */
++static mmc_error_t pxa_mmc_complete_io( mmc_controller_t ctrlr, mmc_dir_t dir, mmc_transfer_mode_t mode )
++{
++      int ret = MMC_ERROR_GENERIC; 
++
++      if ( pxa_mmc_check_state( ctrlr, PXA_MMC_FSM_END_IO ) )
++              goto error;
++    
++      switch ( mode ) {
++      case MMC_TRANSFER_MODE_STREAM: /* FIXME */
++              if ( dir == MMC_WRITE ) {
++              /* 1. wait for STOP_CMD intr */
++                      if ( (ret = pxa_mmc_init_completion( ctrlr,
++                                      MMC_I_MASK_STOP_CMD )) )
++                              goto error;
++                      if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                                      MMC_I_REG_STOP_CMD )) )
++                              goto error;
++              }
++              /* 2. send CMD12 */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++    
++              MMC_CMD = CMD(12); /* STOP_TRANSMISSION */
++              MMC_CMDAT = MMC_CMDAT_R1;
++              if ( dir == MMC_WRITE ) 
++                      MMC_CMDAT |= MMC_CMDAT_BUSY;
++            
++              /* 3. wait for CMD12 to complete */
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "ready for CMD12\n" );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++            
++              /* 4. wait for DATA_TRAN_DONE intr */
++              if ( (ret = pxa_mmc_init_completion( ctrlr,
++                              MMC_I_MASK_DATA_TRAN_DONE )) )
++                      goto error;
++              if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                              MMC_I_REG_DATA_TRAN_DONE )) )
++                      goto error;
++          
++              if ( dir == MMC_WRITE ) {
++              /* 5. wait for PRG_DONE intr */
++                      if ( (ret = pxa_mmc_init_completion( ctrlr,
++                                      MMC_I_MASK_PRG_DONE )) )
++                              goto error;
++                      if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                                      MMC_I_REG_PRG_DONE )) )
++                              goto error;
++              }
++              break;
++      case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:
++              /* 1. wait for DATA_TRAN done intr */
++              if ( (ret = pxa_mmc_init_completion( ctrlr,
++                              MMC_I_MASK_DATA_TRAN_DONE )) )
++                      goto error;
++              if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                              MMC_I_REG_DATA_TRAN_DONE )) )
++                      goto error;
++            
++              /* 2. send CMD12 */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++    
++              MMC_CMD = CMD(12); /* STOP_TRANSMISSION */
++              MMC_CMDAT = MMC_CMDAT_R1;
++              if ( dir == MMC_WRITE ) 
++                      MMC_CMDAT |= MMC_CMDAT_BUSY;
++            
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD12\n" );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++        
++              if ( dir == MMC_WRITE ) {
++              /* 3. wait for PRG_DONE intr */
++                      if ( (ret = pxa_mmc_init_completion( ctrlr,
++                                      MMC_I_MASK_PRG_DONE )) )
++                              goto error;
++                      if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                                      MMC_I_REG_PRG_DONE )) )
++                              goto error;
++              }
++              break;
++      case MMC_TRANSFER_MODE_BLOCK_SINGLE:
++              /* 1. wait for DATA_TRAN_DONE intr */
++              if ( (ret = pxa_mmc_init_completion( ctrlr,
++                              MMC_I_MASK_DATA_TRAN_DONE )) )
++                      goto error;
++              if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                              MMC_I_REG_DATA_TRAN_DONE )) )
++                      goto error;
++            
++              if ( dir == MMC_WRITE ) {
++              /* 2. wait for PRG_DONE intr */
++                      if ( (ret = pxa_mmc_init_completion( ctrlr,
++                                      MMC_I_MASK_PRG_DONE )) )
++                              goto error;
++                      if ( (ret = pxa_mmc_wait_for_completion( ctrlr, 
++                                      MMC_I_REG_PRG_DONE )) )
++                              goto error;
++              }
++              break;
++      default:
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "unknown transfer mode\n" );
++              goto error;
++      }
++/* move the controller to the IDLE state */ 
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_IDLE );
++
++      ret = 0;
++error:
++      return ret;
++}
++
++static inline int pxa_mmc_update_acq( mmc_controller_t ctrlr )
++{
++      int ret = -EINVAL;
++      pxa_mmc_hostdata_t hostdata = NULL;
++      mmc_card_t card = NULL;
++      mmc_card_stack_rec_t fake;
++      mmc_card_stack_t stack = &fake;
++      u16 argl = 0U, argh = 0U;
++      int ncards = 0;
++
++      if ( !ctrlr )
++              goto error;
++
++      hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      
++      __mmc_card_stack_init( stack );
++      
++      /* max open-drain mode frequency is 400kHZ */
++      MMC_CLKRT = MMC_CLKRT_0_3125MHZ;
++      MMC_RESTO = MMC_RES_TO_MAX; /* set response timeout */
++
++      /*  discover and add cards to the stack */  
++      /* I. bus operation condition setup */
++      /*      1) send CMD1 */
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto err_free;
++    
++      argl = 0x0000;
++      argh = 0x0004;
++
++      MMC_CMD = CMD(1);
++      MMC_ARGH = argh;
++      MMC_ARGL = argl; 
++    
++      MMC_CMDAT = MMC_CMDAT_BUSY|MMC_CMDAT_R3; 
++    
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD1(0x%04x%04x)\n", argh, argl );
++      ret = pxa_mmc_complete_cmd( ctrlr, MMC_R3, FALSE );
++      if ( !ret ) {
++              argh = (PXA_MMC_RESPONSE( ctrlr, 4 ) << 8) 
++                      | PXA_MMC_RESPONSE( ctrlr, 3 );
++              argl = (PXA_MMC_RESPONSE( ctrlr, 2 ) << 8)
++                      | PXA_MMC_RESPONSE( ctrlr, 1 );
++
++      } else if ( ret != MMC_ERROR_TIME_OUT_RESPONSE ) 
++              goto err_free;
++    
++      if ( !argh && !argl ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                      "assuming full voltage range support\n" );
++              argh = 0x00ff;
++              argl = 0xff00;
++      }
++    
++      /* 2) continuously send CMD1 'till there're busy cards */
++      for(;;) {
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto err_free;
++
++              MMC_CMD = CMD(1);
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++
++              MMC_CMDAT = MMC_CMDAT_BUSY|MMC_CMDAT_R3;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD1(0x%04x%04x)\n", argh, argl );
++              ret = pxa_mmc_complete_cmd( ctrlr, MMC_R3, FALSE );
++              if ( ret == MMC_ERROR_TIME_OUT_RESPONSE )
++                      break; 
++
++              else if ( !ret ) {
++                      /* busy state reported by LOW signal level 
++                       * (MMC v3.2, p.58)
++                       *
++                       * Thanks to Alexander Samoutin :)
++                       */
++                      if ( !(PXA_MMC_RESPONSE( ctrlr, 4 ) & 0x80) ) {
++                              MMC_DEBUG( MMC_DEBUG_LEVEL3, "busy state reported\n");
++                              udelay( 20 );
++                              continue;
++                      } else 
++                              break;
++              } else
++                      goto err_free;
++      }
++
++/* II. card identification: the cards in Ready state 
++ *     are the only expected to respond 
++ */     
++      for (;;) {
++              argh = 0U;
++              argl = 0U;
++    
++              /* 1) send CMD2 */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto err_free;
++        
++              MMC_CMD = CMD(2);
++              MMC_ARGH = 0x0003;
++              MMC_ARGL = 0xf300;
++              MMC_CMDAT = MMC_CMDAT_R2;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD2(0x%04x%04x)\n", argh, argl );
++              ret = pxa_mmc_complete_cmd( ctrlr, MMC_R2, FALSE );
++              if ( ret == MMC_ERROR_TIME_OUT_RESPONSE )
++                      break;
++        
++              else if ( ret ) /* bus error */
++                      goto err_free;
++        
++              /* TODO: store CID for the card */
++        
++              /* 2) assign RCA */
++              if ( !++ctrlr->rca_next ) /* overflow */
++                      ++ctrlr->rca_next;
++              argh = ctrlr->rca_next; 
++        
++              /* 3) send it to the card last responded (CMD3) */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto err_free;
++        
++              MMC_CMD = CMD(3);
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_CMDAT = MMC_CMDAT_R1;
++
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD3(0x%04x%04x)\n", argh, argl );
++              ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE );
++              if ( ret )   /* CMD3 failed */ 
++                      goto err_free;
++
++              card = __mmc_card_alloc( sizeof( pxa_mmc_card_data_rec_t ) );
++              if ( !card ) {
++                      MMC_ERROR( "out of memory\n" );
++                      goto err_free;
++              }
++
++              card->info.rca = argh;  
++              card->slot = ctrlr->slot_next++; /* FIXME: minor encoding */
++              card->ctrlr = ctrlr;
++        
++              if ( !__mmc_card_stack_add( stack, card ) )
++                      goto err_free;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL2, "added card: "
++                              "slot %d, RCA=0x%04x\n", card->slot, argh );
++              ++ncards;
++      }
++   
++      if ( ncards ) {
++/* III. read CSD registers of all cards; DSR support also reported there */
++              for ( card = stack->first; card; card = card->next ) {
++                      pxa_mmc_card_data_t card_data = 
++                              (pxa_mmc_card_data_t)card->card_data;
++          
++                      /* 1) send CMD9 */
++                      argh = card->info.rca;
++                      argl = 0U;
++        
++                      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                              goto err_free;
++        
++                      MMC_CMD = CMD(9);
++                      MMC_ARGH = argh;
++                      MMC_ARGL = argl;
++                      MMC_CMDAT = MMC_CMDAT_R2;
++        
++                      MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                              "CMD9(0x%04x%04x)\n", argh, argl );
++                      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R2, FALSE )) )
++                              goto err_free; 
++              
++                      memcpy( &card->info.csd, hostdata->mmc_res, 15 );
++                      MMC_DUMP_CSD( card );
++          
++                      card->info.read_bl_len = (1<<card->info.csd.read_bl_len);
++                      card->info.write_bl_len = (1<<card->info.csd.write_bl_len);
++                      card->info.capacity = (card->info.csd.c_size + 1) 
++                                      * (1<<(card->info.csd.c_size_mult + 2)) 
++                                      * card->info.read_bl_len;
++                      MMC_DEBUG( MMC_DEBUG_LEVEL2, "card capacity=%dMb\n", 
++                                      card->info.capacity>>20 );
++                      card->info.tran_speed = 20*1024; /* FIXME */
++                      card->info.transfer_mode = MMC_TRANSFER_MODE_BLOCK_SINGLE; 
++              /* 2) set bus operation freq */
++                      card_data->clkrt = pxa_mmc_clkrt( card->info.tran_speed );
++              /* 3) register card with MMC core */
++                      mmc_register( MMC_REG_TYPE_CARD, card, 0 );
++              }
++/* IV. set DSR registers of the cards */
++#if 0 /* TODO */
++              if ( card->info.csd.dsr_imp ) {
++                      set_dsr = TRUE;
++                      /*  calculate DSR */
++              }
++#endif
++      }
++#if 0 /* TODO */
++      if ( set_dsr ) {
++                      /* send CMD4 */
++      }
++#endif
++/* merge list of the newly inserted cards into controller card stack */
++      if ( !ctrlr->stack.ncards ) {
++              ctrlr->stack.first = stack->first;
++              ctrlr->stack.last = stack->last;
++      } else  
++              ctrlr->stack.last->next = stack->first;
++      
++      ctrlr->stack.ncards += stack->ncards;
++      
++      ret = 0;
++      goto out;
++err_free:
++      __mmc_card_stack_free( stack );
++error:
++out:
++      return ret;
++}
++
++/* MMC protocol macros: v3.4, p.120 */
++static int pxa_mmc_init_card_stack( mmc_controller_t ctrlr )
++{
++      int ret = -EIO;
++      u16 argl = 0U, argh = 0U;
++    
++      if ( !ctrlr || ctrlr->stack.ncards ) {
++              ret = -EINVAL;
++              goto error;
++      }
++
++      /* initialize stack */  
++      /*     1) send CMD0 */
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++
++      /* max open-drain mode frequency is 400kHZ */
++      MMC_CLKRT = MMC_CLKRT_0_3125MHZ;
++      MMC_RESTO = MMC_RES_TO_MAX; /* set response timeout */
++      MMC_SPI = MMC_SPI_DISABLE;
++
++      MMC_CMD = CMD(0); /* CMD0 with zero argument */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl; 
++      MMC_CMDAT = MMC_CMDAT_INIT;
++    
++      //MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD0(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_NORESPONSE, FALSE )) )
++              goto error;
++
++      /* update card stack */
++      if ( (ret = pxa_mmc_update_acq( ctrlr )) )
++              goto err_free;
++      
++      /* move the controller to the IDLE state */ 
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto err_free;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_IDLE );
++      
++      ret = 0;
++      MMC_DEBUG( MMC_DEBUG_LEVEL2, "ncards=%d\n", ctrlr->stack.ncards );
++      goto out;
++
++err_free:
++      __mmc_card_stack_free( &ctrlr->stack ); 
++error:
++out:
++      return ret;
++}
++
++static int pxa_mmc_check_card_stack( mmc_controller_t ctrlr )
++{
++      int ret = -1;
++      mmc_card_t card;
++      
++      if ( !ctrlr )
++              goto error;
++      
++      if ( ctrlr->stack.ncards > 0 )   {
++/* for each card in the stack: */
++              for( card = ctrlr->stack.first; card; card = card->next ) {
++                      u16 argh = card->info.rca;
++                      u16 argl = 0UL;
++                      
++/*    1) send CMD9( card->rca ) */
++                      if ( pxa_mmc_stop_bus_clock( ctrlr ) )
++                              goto error;
++                      
++                      /* SanDisk's cards do not respond to CMD9 */    
++                      MMC_CMD = CMD(13);
++                      MMC_ARGH = argh;
++                      MMC_ARGL = argl;
++                      MMC_CMDAT = MMC_CMDAT_R1;
++                      
++                      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD13(0x%04x%04x)\n", 
++                                      argh, argl );
++                      ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE );
++/*    2) if card responded, it is still there */
++                      if ( ret ) 
++                              card->state = MMC_CARD_STATE_UNPLUGGED;
++              }
++      }
++      ret = 0;        
++error:
++      return ret;
++}
++
++/* This procedure links the bus master with a single card
++ *  1)  cross checks with the internal stack management data if a card still
++ *      exists in the slot
++ *  2)  send CMD7( card->public.rca )
++ *  3)  setup data path and controller options 
++ */
++static int pxa_mmc_setup_card( mmc_controller_t ctrlr, mmc_card_t card ) 
++{
++      int ret = -ENODEV;
++      pxa_mmc_hostdata_t hostdata;
++      pxa_mmc_card_data_t card_data;
++      u16 argh = 0U;
++#ifdef CONFIG_MMC_DEBUG
++      u16 argl = 0U;
++#endif
++    
++      if ( !ctrlr || !card ) {
++              ret = -EINVAL;
++              goto error;
++      }
++    
++      if ( card->ctrlr != ctrlr ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "card is on another bus\n" );
++              goto error;
++      }
++    
++      hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      card_data = (pxa_mmc_card_data_t)card->card_data;
++
++      argh = card->info.rca;
++    
++/* select requested card */ 
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) ) 
++              goto error;
++        
++      MMC_CMD = CMD(7);
++      MMC_ARGH = argh;
++      MMC_CMDAT = MMC_CMDAT_R1;
++
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD7(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++              goto error;
++
++/* set controller options */
++#ifndef CONFIG_MMC_DEBUG
++      MMC_CLKRT = card_data->clkrt; 
++#endif    
++/* move the controller to the IDLE state */ 
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_IDLE );
++      
++      ret = 0;
++error:  
++      return ret;
++}
++
++static inline int pxa_mmc_iobuf_init( mmc_controller_t ctrlr, ssize_t cnt )
++{
++#ifdef PIO
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#endif 
++#ifndef PIO
++/* TODO */
++#else
++      hostdata->iobuf.buf.pos = hostdata->iobuf.iodata;
++      hostdata->iobuf.buf.cnt = cnt;
++#endif
++      return 0;
++}
++/* TODO: ssize_t pxa_mmc_read_buffer( mmc_controller_t ctrlr, ssize_t cnt )
++effects: reads at most cnt bytes from the card to the controller I/O buffer;
++       takes care of partial data transfers
++requieres: 
++modifies: ctrlr->iobuf
++returns: number of bytes actually transferred or negative error code if there were any errors
++ */
++ssize_t pxa_mmc_read_buffer( mmc_controller_t ctrlr, ssize_t cnt )
++{
++      ssize_t ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#ifndef PIO
++      register int ndesc;
++      int chan = hostdata->iobuf.buf.chan;
++      pxa_dma_desc *desc;
++#endif
++
++      if ( (hostdata->state != PXA_MMC_FSM_END_CMD) && (hostdata->state != PXA_MMC_FSM_END_BUFFER) ) {
++            goto error;
++      }
++      
++      if ( cnt > hostdata->iobuf.bufsz )
++              cnt = hostdata->iobuf.bufsz;
++
++      if ( (ret = pxa_mmc_iobuf_init( ctrlr, cnt )) )
++              goto error;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_BUFFER_IN_TRANSIT );
++#ifndef PIO
++      if ( pxa_mmc_init_completion( ctrlr, ~MMC_I_MASK_ALL ) ) /* FIXME */
++              goto error;
++      
++      if ( (desc = hostdata->iobuf.buf.last_read_desc) ) {
++              desc->ddadr &= ~DDADR_STOP;
++              desc->dcmd &= ~(DCMD_ENDIRQEN|DCMD_LENGTH);
++              desc->dcmd |= (1<<5);
++      }
++/* 1) setup descriptors for DMA transfer from the device */
++      ndesc = (cnt>>5) - 1; /* FIXME: partial read */
++      desc = &hostdata->iobuf.buf.read_desc[ndesc];
++      hostdata->iobuf.buf.last_read_desc = desc;      
++      /* TODO: partial read */
++      desc->ddadr |= DDADR_STOP;
++      desc->dcmd |= DCMD_ENDIRQEN;
++/* 2) start DMA channel */
++      DDADR( chan ) = hostdata->iobuf.buf.read_desc_phys_addr;
++      DCSR( chan ) |= DCSR_RUN;
++#else
++      if ( pxa_mmc_init_completion( ctrlr, MMC_I_MASK_RXFIFO_RD_REQ ) )
++              goto error;
++#endif
++
++      if ( pxa_mmc_wait_for_completion( ctrlr, ~0UL ) )
++              goto error;
++
++      if ( pxa_mmc_check_state( ctrlr, PXA_MMC_FSM_END_BUFFER ) )
++              goto error;
++
++      if ( !(hostdata->mmc_stat & MMC_STAT_ERRORS) ) /* FIXME */  
++              ret = cnt;
++error:
++      return ret;
++}
++
++ssize_t pxa_mmc_write_buffer( mmc_controller_t ctrlr, ssize_t cnt )
++{
++      ssize_t ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#ifndef PIO
++      register int ndesc;
++      int chan = hostdata->iobuf.buf.chan;
++      pxa_dma_desc *desc;
++#endif
++      
++      if ( (hostdata->state != PXA_MMC_FSM_END_CMD) 
++          && (hostdata->state != PXA_MMC_FSM_END_BUFFER) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "unexpected state (%s)\n",
++                      PXA_MMC_STATE_LABEL( hostdata->state ) ); 
++              goto error;
++      }
++    
++      if ( cnt > hostdata->iobuf.bufsz )
++              cnt = hostdata->iobuf.bufsz;
++      
++      if ( (ret = pxa_mmc_iobuf_init( ctrlr, cnt )) )
++              goto error;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_BUFFER_IN_TRANSIT );
++#ifndef PIO
++      if ( pxa_mmc_init_completion( ctrlr, ~MMC_I_MASK_ALL ) ) /* FIXME */
++              goto error;
++      if ( (desc = hostdata->iobuf.buf.last_write_desc) ) {
++              desc->ddadr &= ~DDADR_STOP;
++              desc->dcmd &= ~(DCMD_ENDIRQEN|DCMD_LENGTH);
++              desc->dcmd |= (1<<5);
++      }
++/* 1) setup descriptors for DMA transfer to the device */
++      ndesc = (cnt>>5) - 1; /* FIXME: partial write */
++      desc = &hostdata->iobuf.buf.write_desc[ndesc];
++      /* TODO: partial write */
++      hostdata->iobuf.buf.last_write_desc = desc;     
++      desc->ddadr |= DDADR_STOP;
++      desc->dcmd |= DCMD_ENDIRQEN;
++/* 2) start DMA channel */
++      DDADR( chan ) = hostdata->iobuf.buf.write_desc_phys_addr;
++      DCSR( chan ) |= DCSR_RUN;
++#else
++      if ( pxa_mmc_init_completion( ctrlr, MMC_I_MASK_TXFIFO_WR_REQ ) )
++              goto error;
++#endif
++      if ( pxa_mmc_wait_for_completion( ctrlr, ~0UL ) )
++              goto error;
++    
++      if ( pxa_mmc_check_state( ctrlr, PXA_MMC_FSM_END_BUFFER ) )
++              goto error;
++
++      if ( !(hostdata->mmc_stat & MMC_STAT_ERRORS) ) /* FIXME */  
++              ret = cnt;
++error:
++      return ret;
++}
++
++/* TODO: ssize_t pxa_mmc_copy_from_buffer( ctrlr, mmc_buftype_t to, char *buf, ssize_t cnt )
++effects: copies at most cnt bytes from the controller I/O buffer to the user or kernel buffer
++         pointed by buf
++requiers:
++modifies:
++returns: number of bytes actually transferred or negative error code if there were any errors
++ */ 
++ssize_t pxa_mmc_copy_from_buffer( mmc_controller_t ctrlr, mmc_buftype_t to, char *buf, ssize_t cnt )
++{
++      ssize_t ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++
++#ifndef PIO
++/* TODO: check that DMA channel is not running */
++#endif
++      switch ( to ) {
++      case MMC_USER:
++              if ( copy_to_user( buf, hostdata->iobuf.iodata, cnt ) ) {
++                      ret = -EFAULT;
++                      goto error;
++              }
++              break;
++        case MMC_KERNEL:
++              memcpy( buf, hostdata->iobuf.iodata, cnt );
++              break;
++        default:
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "unknown buffer type\n" );
++              goto error;
++      }
++      ret = cnt;
++error:
++      return ret;
++}
++
++ssize_t pxa_mmc_copy_to_buffer( mmc_controller_t ctrlr, mmc_buftype_t to, char *buf, ssize_t cnt )
++{
++      ssize_t ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#ifndef PIO
++/* check that DMA channel is not running */
++#endif
++      switch ( to ) {
++      case MMC_USER:
++              if ( copy_from_user( hostdata->iobuf.iodata, buf, cnt ) ) {
++                      ret = -EFAULT;
++                      goto error;
++              }
++              break;
++      case MMC_KERNEL:
++              memcpy( hostdata->iobuf.iodata, buf, cnt );
++              break;
++      default:
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "unknown buffer type\n" );
++              goto error;
++      }
++      ret = cnt;
++error:
++      return ret;
++}
++
++/* This procedure sequentally passes the data from the user buffer to the card */
++static int pxa_mmc_stream_read( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      u16 argh = 0UL, argl = 0UL;
++      ssize_t size = 0;
++
++      while ( transfer->cnt > 0 ) {
++              size = (transfer->cnt < hostdata->iobuf.blksz) ?
++                      transfer->cnt : hostdata->iobuf.blksz; 
++              /* 1. send CMD11 */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++
++              argh = transfer->addr >> 16;
++              argl = transfer->addr;
++              /* 2. setup controller registers to start stream data transfer */
++              MMC_CMD = CMD(11); /* READ_DAT_UNTIL_STOP */
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_NOB = 0xffff;
++              MMC_BLKLEN = size;
++              MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_STREAM|MMC_CMDAT_DATA_EN;
++#ifndef PIO
++              MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++              /* 3. wait for cmd to complete */
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD11(0x%04x%04x)\n", argh, argl );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, TRUE )) )
++                      goto error;     
++    
++              /* 4. transfer the data to the caller supplied buffer */
++              if ( (ret = pxa_mmc_read_buffer( ctrlr, size )) < 0 )
++                      goto error;
++
++              if ( (ret = pxa_mmc_copy_from_buffer( ctrlr, transfer->type, transfer->buf, ret )) < 0 )
++                      goto error;
++        
++              pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++              if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++                      goto error;
++              
++              transfer->buf += ret;
++              transfer->addr += ret;
++              transfer->cnt -= ret;
++      }
++      ret = 0;    
++error:
++      return ret;
++}
++
++/* This procedure reads a data block from a card at a given kernel address */ 
++static int pxa_mmc_read_block( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -ENODEV;
++      u16 argh = 0UL, argl = 0UL;
++
++/* send CMD16 (SET_BLOCK_LEN) when requested block size is not the default
++ * for the current card */  
++      if ( transfer->blksz != ctrlr->stack.selected->info.read_bl_len ) {
++              argh = transfer->blksz >> 16;
++              argl = transfer->blksz;
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) ) 
++                      goto error;
++    
++              MMC_CMD = CMD(16); /* SET_BLOCK_LEN */
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_CMDAT = MMC_CMDAT_R1;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                              "CMD16(0x%04x%04x)\n", argh, argl );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++      }
++    
++/* CMD17 (READ_SINGLE_BLOCK) */
++      argh = transfer->addr >> 16;
++      argl = transfer->addr;
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++      
++      MMC_CMD = CMD(17); /* READ_SINGLE_BLOCK */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl;
++      MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN;
++      MMC_NOB = 1;
++      MMC_BLKLEN = transfer->blksz;
++#ifndef PIO
++      MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++    
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD17(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++              goto error;
++    
++/* transfer the data to the caller supplied buffer */
++      if ( (ret = pxa_mmc_read_buffer( ctrlr, transfer->blksz )) < 0 )
++              goto error;
++
++      if ( (ret = pxa_mmc_copy_from_buffer( ctrlr, transfer->type, transfer->buf, ret )) < 0 )
++              goto error;
++        
++      transfer->buf += ret;
++      transfer->cnt -= ret;
++      transfer->nob -= 1;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++      if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++              goto error;
++    
++      ret = 0;    
++error:
++      return ret;
++}
++
++/* This procedure sequentally reads data blocks from
++ * a card to the user buffer. Controller options and block size 
++ * are already set by setup_card(). Data alignment and partial
++ * data accessibility assumed to be checked by mmc_core */
++static int pxa_mmc_read_mblock( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -EIO;
++      u16 argh = 0UL, argl = 0UL;
++
++/* send CMD16 (SET_BLOCK_LEN) when requested block size is not the default
++ * for the current card */  
++      if ( transfer->blksz != ctrlr->stack.selected->info.read_bl_len ) {
++              argh = transfer->blksz >> 16;
++              argl = transfer->blksz;
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++    
++              MMC_CMD = CMD(16); /* SET_BLOCK_LEN */
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_CMDAT = MMC_CMDAT_R1;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD16(0x%04x%04x)\n", argh, argl );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++      }
++    
++      argh = transfer->addr >> 16;
++      argl = transfer->addr;
++/* 1. stop bus clock */
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++
++/* 2. setup controller registers to start multiple block transfer */
++      MMC_CMD = CMD(18); /* READ_MULTIPLE_BLOCK */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl;
++      MMC_NOB = transfer->nob;
++      MMC_BLKLEN = transfer->blksz;
++      MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_READ|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN;
++#ifndef PIO
++      MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++
++/* 3. start clock */
++      if ( (ret = pxa_mmc_start_bus_clock( ctrlr )) )
++              goto error;
++    
++/* 4. wait for cmd to complete */
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD18(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, TRUE )) )
++              goto error;
++    
++/* 6. transfer the data to the caller supplied buffer */
++      while ( transfer->cnt > 0 ) {
++              if ( (ret = pxa_mmc_read_buffer( ctrlr, transfer->cnt )) < 0 )
++                      goto error;
++
++              if ( (ret = pxa_mmc_copy_from_buffer( ctrlr, transfer->type, transfer->buf, ret )) < 0 )
++                      goto error;
++        
++              transfer->buf += ret;
++              transfer->cnt -= ret;
++      }
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++      if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++              goto error;
++    
++      ret = 0;
++error:
++      return ret;
++}
++
++/* Sequentally writes the data from a user buffer to the card */
++static int pxa_mmc_stream_write( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -EIO;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      u16 argh = 0UL, argl = 0UL;
++      ssize_t size = 0;
++    
++      __ENTER( "transfer: cmd=%d mode=%d type=%d blksz=%d "
++               "nob=%d buf=%p cnt=%d addr=%Lx", transfer->cmd, 
++              transfer->mode, transfer->type, transfer->blksz, 
++              transfer->nob, transfer->buf, transfer->cnt, transfer->addr );
++
++      argh = transfer->addr >> 16;
++      argl = transfer->addr;
++/* 1. stop bus clock */
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) ) 
++              goto error;
++
++/* 2. setup controller registers to start stream data transfer */
++      MMC_CMD = CMD(20); /* WRITE_DAT_UNTIL_STOP */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl;
++      MMC_NOB = 0xffff;
++      MMC_BLKLEN = hostdata->iobuf.blksz;
++      MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_STREAM|MMC_CMDAT_DATA_EN;
++#ifndef PIO
++      MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++
++/* 3. wait for cmd to complete */
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD20(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, TRUE )) )
++              goto error;
++    
++/* 4. transfer the data to the caller supplied buffer */
++      while ( transfer->cnt > 0 ) {
++              size = (transfer->cnt < hostdata->iobuf.blksz) ?
++                      transfer->cnt : hostdata->iobuf.blksz; 
++              if ( (ret = pxa_mmc_copy_to_buffer( ctrlr, 
++                              transfer->type, transfer->buf, size )) < 0 )
++                      goto error;
++        
++              if ( (ret = pxa_mmc_write_buffer( ctrlr, ret )) < 0 )
++                      goto error;
++
++              transfer->buf += ret;
++              transfer->cnt -= ret;
++      }
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++      if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++              goto error;
++    
++      ret = 0;
++error:
++      return ret;
++}
++
++/* This procedure writes a data block to a card at a given address */
++static int pxa_mmc_write_block( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -ENODEV;
++      u16 argh = 0UL, argl = 0UL;
++
++/* send CMD16 (SET_BLOCK_LEN) when requested block size is not the default
++ * for the current card */  
++      if ( transfer->blksz != ctrlr->stack.selected->info.read_bl_len ) {
++              argh = transfer->blksz >> 16;
++              argl = transfer->blksz;
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) ) 
++                      goto error;
++    
++              MMC_CMD = CMD(16); /* SET_BLOCK_LEN */
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_CMDAT = MMC_CMDAT_R1;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD16(0x%04x%04x)\n", argh, argl );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++      }
++    
++/* CMD17 (READ_SINGLE_BLOCK) */
++      argh = transfer->addr >> 16;
++      argl = transfer->addr;
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++    
++      MMC_CMD = CMD(24); /* WRITE_BLOCK */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl;
++      MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN;
++#ifndef PIO
++      MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++      MMC_NOB = 1;
++      MMC_BLKLEN = transfer->blksz;
++    
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD24(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++              goto error;
++    
++/* transfer the data to the caller supplied buffer */
++      if ( (ret = pxa_mmc_copy_to_buffer( ctrlr, transfer->type, transfer->buf, transfer->cnt )) < 0 )
++              goto error;
++        
++      if ( (ret = pxa_mmc_write_buffer( ctrlr, ret )) < 0 )
++              goto error;
++
++      transfer->buf += ret;
++      transfer->cnt -= ret;
++      transfer->nob -= 1;
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++      if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++              goto error;
++    
++      ret = 0;    
++error:
++      return ret;
++}
++
++/* This procedure sequentally writes data blocks to a card at a given address */
++static ssize_t pxa_mmc_write_mblock( mmc_controller_t ctrlr, mmc_data_transfer_req_t transfer )
++{
++      int ret = -EIO;
++      u16 argh = 0UL, argl = 0UL;
++      
++/* send CMD16 (SET_BLOCK_LEN) when requested block size is not the default
++ * for the current card */  
++      if ( transfer->blksz != ctrlr->stack.selected->info.write_bl_len ) {
++              argh = transfer->blksz >> 16;
++              argl = transfer->blksz;
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++    
++              MMC_CMD = CMD(16); /* SET_BLOCK_LEN */
++              MMC_ARGH = argh;
++              MMC_ARGL = argl;
++              MMC_CMDAT = MMC_CMDAT_R1;
++        
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD16(0x%04x%04x)\n", argh, argl );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, FALSE )) )
++                      goto error;
++      }
++    
++      argh = transfer->addr >> 16;
++      argl = transfer->addr;
++/* 1. stop bus clock */
++      if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++              goto error;
++
++/* 2. setup controller registers to start multiple block transfer */
++      MMC_CMD = CMD(25); /* WRITE_MULTIPLE_BLOCK */
++      MMC_ARGH = argh;
++      MMC_ARGL = argl;
++      MMC_NOB = transfer->nob;
++      MMC_BLKLEN = transfer->blksz;
++      MMC_CMDAT = MMC_CMDAT_R1|MMC_CMDAT_WRITE|MMC_CMDAT_BLOCK|MMC_CMDAT_DATA_EN;
++#ifndef PIO
++      MMC_CMDAT |= MMC_CMDAT_MMC_DMA_EN; 
++#endif
++
++/* 3. start clock */
++      if ( (ret = pxa_mmc_start_bus_clock( ctrlr )) )
++              goto error;
++    
++/* 4. wait for cmd to complete */
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD25(0x%04x%04x)\n", argh, argl );
++      if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_R1, TRUE )) ) 
++              goto error;
++
++/* 6. transfer the data to the caller supplied buffer */
++      while ( transfer->cnt > 0 ) {
++              if ( (ret = pxa_mmc_copy_to_buffer( ctrlr, transfer->type,
++                              transfer->buf, transfer->cnt )) < 0 )
++                      goto error;
++        
++              if ( (ret = pxa_mmc_write_buffer( ctrlr, ret )) < 0 )
++                      goto error;
++
++              transfer->buf += ret;
++              transfer->cnt -= ret;
++      }
++    
++      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_IO );
++
++      if ( (ret = pxa_mmc_complete_io( ctrlr, transfer->cmd, transfer->mode )) )
++              goto error;
++    
++      ret = 0;    
++error:
++      return ret;
++}
++
++static void pxa_mmc_irq( int irq, void *dev_id, struct pt_regs *regs )
++{
++      mmc_controller_t ctrlr = (mmc_controller_t)dev_id;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#ifdef PIO
++      register int i, cnt;
++      register char *buf;
++#endif    
++
++      hostdata->mmc_i_reg = MMC_I_REG;
++      hostdata->mmc_stat = MMC_STAT;
++      hostdata->mmc_cmdat = MMC_CMDAT;
++#if 0
++      if (hostdata->mmc_i_reg != 0x0010) {
++      printk("IREG %08x", hostdata->mmc_i_reg);
++      if (hostdata->mmc_i_reg & 0x0001) printk(" DATA_TRAN_DONE");
++      if (hostdata->mmc_i_reg & 0x0002) printk(" PRG_DONE");
++      if (hostdata->mmc_i_reg & 0x0004) printk(" END_CMD");
++      if (hostdata->mmc_i_reg & 0x0008) printk(" STOP_CMD");
++      if (hostdata->mmc_i_reg & 0x0010) printk(" CLK_OFF");
++      if (hostdata->mmc_i_reg & 0x0020) printk(" RX_FIFO");
++      if (hostdata->mmc_i_reg & 0x0040) printk(" TX_FIFO");
++      printk("\nSTAT %08x", hostdata->mmc_stat);
++      if (hostdata->mmc_stat & 0x0001) printk(" READ_TO");
++      if (hostdata->mmc_stat & 0x0002) printk(" RESP_TO");
++      if (hostdata->mmc_stat & 0x0004) printk(" WR_CRC");
++      if (hostdata->mmc_stat & 0x0008) printk(" READ_CRC");
++      if (hostdata->mmc_stat & 0x0010) printk(" SPI_RD_TKN");
++      if (hostdata->mmc_stat & 0x0020) printk(" RESP_CRC");
++      if (hostdata->mmc_stat & 0x0040) printk(" TX_FIFO");
++      if (hostdata->mmc_stat & 0x0080) printk(" RX_FIFO");
++      if (hostdata->mmc_stat & 0x0100) printk(" CLK");
++      if (hostdata->mmc_stat & 0x0800) printk(" DATA_TRAN_DONE");
++      if (hostdata->mmc_stat & 0x1000) printk(" PRG_DONE");
++      if (hostdata->mmc_stat & 0x2000) printk(" END_CMD");
++      printk("\n");
++      }
++#endif
++
++#if CONFIG_MMC_DEBUG_IRQ 
++      if ( --hostdata->irqcnt <= 0 ) {
++              printk( KERN_INFO __FUNCTION__"(): irqcnt exceeded\n" );
++              goto complete;
++      }
++#endif
++      switch ( hostdata->state ) {
++      case PXA_MMC_FSM_IDLE:
++      case PXA_MMC_FSM_CLK_OFF:
++      case PXA_MMC_FSM_END_IO:
++      case PXA_MMC_FSM_END_BUFFER:
++      case PXA_MMC_FSM_END_CMD:
++              goto complete;
++#ifdef PIO   
++      case PXA_MMC_FSM_BUFFER_IN_TRANSIT:
++              if ( hostdata->mmc_stat & MMC_STAT_ERRORS )
++                      goto complete;
++        
++              buf = hostdata->iobuf.buf.pos;
++              cnt = (hostdata->iobuf.buf.cnt < 32) ? 
++                      hostdata->iobuf.buf.cnt : 32;
++              if ( hostdata->mmc_cmdat & MMC_CMDAT_WRITE ) {
++                      if ( !(hostdata->mmc_stat & MMC_STAT_XMIT_FIFO_EMPTY) )
++                              break;
++                      for ( i = 0; i < cnt; i++ ) 
++                              MMC_TXFIFO = *buf++;
++                      if ( cnt < 32 ) 
++                              MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL; 
++              } else { /* i.e. MMC_CMDAT_READ */
++                      if( !(hostdata->mmc_stat & MMC_STAT_RECV_FIFO_FULL) )
++                              break;
++                      for( i = 0; i < cnt; i++ ) 
++                              *buf++ = MMC_RXFIFO;
++              }
++        
++              hostdata->iobuf.buf.pos = buf;
++              hostdata->iobuf.buf.cnt -= i;
++              if ( hostdata->iobuf.buf.cnt <= 0 ) {
++                      pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_BUFFER );
++                      MMC_DEBUG( MMC_DEBUG_LEVEL3, "buffer transferred\n" );
++                      goto complete;
++              }
++              break; 
++#endif /* PIO */
++      default:
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "unexpected state %d\n", 
++                              hostdata->state );
++              goto complete;
++      }   
++      return; 
++complete:   
++      MMC_I_MASK = MMC_I_MASK_ALL;
++      complete( &hostdata->completion );
++      return;
++}
++
++#ifndef PIO
++static void pxa_mmc_dma_irq( int irq, void *dev_id, struct pt_regs *regs )
++{
++      mmc_controller_t ctrlr = (mmc_controller_t)dev_id;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      u32 dcsr;
++      u32 ddadr;
++      int chan = hostdata->iobuf.buf.chan;
++      
++      ddadr = DDADR( chan );
++      dcsr = DCSR( chan );
++      DCSR( chan ) = dcsr & ~DCSR_STOPIRQEN;
++      
++      MMC_DEBUG( MMC_DEBUG_LEVEL3, 
++                      "MMC DMA interrupt: chan=%d ddadr=0x%08x "
++                      "dcmd=0x%08x dcsr=0x%08x\n", 
++                      chan, ddadr, DCMD( chan ), dcsr );
++/* bus error */
++      if ( dcsr & DCSR_BUSERR ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "bus error on DMA channel %d\n",
++                              chan );
++              pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_ERROR );
++              goto complete;
++      }
++/* data transfer completed */
++      if ( dcsr & DCSR_ENDINTR ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "buffer transferred\n" );
++              pxa_mmc_set_state( ctrlr, PXA_MMC_FSM_END_BUFFER );
++              goto complete;
++      }
++      return;
++complete:
++      complete( &hostdata->completion );
++      return;
++}
++#endif
++
++
++static int pxa_mmc_init( mmc_controller_t ctrlr )
++{
++      int ret = -ENODEV;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++#ifndef PIO
++      register int i;
++      register pxa_dma_desc *desc;
++#endif
++
++/* hardware initialization */
++/* I. prepare to transfer data */
++/* 1. allocate buffer */
++#ifndef PIO
++      hostdata->iobuf.buf.read_desc = consistent_alloc( GFP_KERNEL,
++                              (PXA_MMC_IODATA_SIZE>>5)
++                              * sizeof( pxa_dma_desc ),
++                              &hostdata->iobuf.buf.read_desc_phys_addr, 0 );
++      if ( !hostdata->iobuf.buf.read_desc ) {
++              ret = -ENOMEM;
++              goto error;
++      }
++      hostdata->iobuf.buf.write_desc = consistent_alloc( GFP_KERNEL,
++                              (PXA_MMC_IODATA_SIZE>>5)
++                              * sizeof( pxa_dma_desc ),
++                              &hostdata->iobuf.buf.write_desc_phys_addr, 0 );
++      if ( !hostdata->iobuf.buf.write_desc ) {
++              ret = -ENOMEM;
++              goto error;
++      }
++      hostdata->iobuf.iodata = consistent_alloc( GFP_ATOMIC, 
++                                      PXA_MMC_IODATA_SIZE,
++                                      &hostdata->iobuf.buf.phys_addr, 0 );
++#else
++      hostdata->iobuf.iodata = kmalloc( PXA_MMC_IODATA_SIZE, GFP_ATOMIC );
++#endif
++      if ( !hostdata->iobuf.iodata ) {
++              ret = -ENOMEM;
++              goto error;
++      }
++/* 2. initialize iobuf */
++      hostdata->iobuf.blksz = PXA_MMC_BLKSZ_MAX;
++      hostdata->iobuf.bufsz = PXA_MMC_IODATA_SIZE;
++      hostdata->iobuf.nob = PXA_MMC_BLOCKS_PER_BUFFER;
++#ifndef PIO
++  /* request DMA channel */
++      if ( (hostdata->iobuf.buf.chan = pxa_request_dma( "MMC", DMA_PRIO_LOW,
++                                      pxa_mmc_dma_irq, ctrlr )) < 0 ) {
++              MMC_ERROR( "failed to request DMA channel\n" );
++              goto error;
++      }
++      
++      DRCMRRXMMC = hostdata->iobuf.buf.chan | DRCMR_MAPVLD;
++      DRCMRTXMMC = hostdata->iobuf.buf.chan | DRCMR_MAPVLD;
++      
++      for ( i = 0; i < ((PXA_MMC_IODATA_SIZE>>5) - 1); i++ ) {
++              desc = &hostdata->iobuf.buf.read_desc[i];
++              desc->ddadr = hostdata->iobuf.buf.read_desc_phys_addr
++                              + ((i + 1) * sizeof( pxa_dma_desc ));
++              desc->dsadr = MMC_RXFIFO_PHYS_ADDR;
++              desc->dtadr = hostdata->iobuf.buf.phys_addr + (i<<5);
++              desc->dcmd = DCMD_FLOWSRC|DCMD_INCTRGADDR
++                              |DCMD_WIDTH1|DCMD_BURST32|(1<<5);
++              
++              desc = &hostdata->iobuf.buf.write_desc[i];
++              desc->ddadr = hostdata->iobuf.buf.write_desc_phys_addr
++                              + ((i + 1) * sizeof( pxa_dma_desc ));
++              desc->dsadr = hostdata->iobuf.buf.phys_addr + (i<<5);
++              desc->dtadr = MMC_TXFIFO_PHYS_ADDR;
++              desc->dcmd = DCMD_FLOWTRG|DCMD_INCSRCADDR
++                              |DCMD_WIDTH1|DCMD_BURST32|(1<<5);
++      }
++      desc = &hostdata->iobuf.buf.read_desc[i];
++      desc->ddadr = (hostdata->iobuf.buf.read_desc_phys_addr +
++                              (i + 1) * sizeof( pxa_dma_desc))|DDADR_STOP;
++      desc->dsadr = MMC_RXFIFO_PHYS_ADDR;
++      desc->dtadr = hostdata->iobuf.buf.phys_addr + (i<<5);
++      desc->dcmd = DCMD_FLOWSRC|DCMD_INCTRGADDR
++                      |DCMD_WIDTH1|DCMD_BURST32|(1<<5);
++              
++      desc = &hostdata->iobuf.buf.write_desc[i];
++      desc->ddadr = (hostdata->iobuf.buf.write_desc_phys_addr +
++                              (i + 1) * sizeof( pxa_dma_desc))|DDADR_STOP;
++      desc->dsadr = hostdata->iobuf.buf.phys_addr + (i<<5);
++      desc->dtadr = MMC_TXFIFO_PHYS_ADDR;
++      desc->dcmd = DCMD_FLOWTRG|DCMD_INCSRCADDR
++                      |DCMD_WIDTH1|DCMD_BURST32|(1<<5);
++#endif
++/* II. MMC */
++/*  1) request irq */
++      if ( request_irq( IRQ_MMC, pxa_mmc_irq, 0, "MMC", ctrlr ) ) {
++              MMC_ERROR( "failed to request IRQ_MMC\n" );
++              goto error;
++      }
++    
++/*  2) initialize h/w and ctrlr */
++      set_GPIO_mode( GPIO6_MMCCLK_MD );
++      CKEN |= CKEN12_MMC; /* enable MMC unit clock */
++
++      ret = 0;
++      goto out;
++error:
++#ifndef PIO
++/* free DMA resources */
++      if ( hostdata->iobuf.buf.chan >= 0 ) {
++              DRCMRRXMMC = 0;
++              DRCMRTXMMC = 0;
++              pxa_free_dma( hostdata->iobuf.buf.chan );
++      }
++      if ( hostdata->iobuf.iodata )
++              consistent_free( hostdata->iobuf.iodata, 
++                               PXA_MMC_IODATA_SIZE,
++                               hostdata->iobuf.buf.phys_addr );
++      if ( hostdata->iobuf.buf.read_desc )    
++              consistent_free( hostdata->iobuf.buf.read_desc,
++                              (PXA_MMC_IODATA_SIZE>>5)
++                              * sizeof( pxa_dma_desc ),
++                              hostdata->iobuf.buf.read_desc_phys_addr );
++      if ( hostdata->iobuf.buf.write_desc )   
++              consistent_free( hostdata->iobuf.buf.write_desc,
++                              (PXA_MMC_IODATA_SIZE>>5)
++                              * sizeof( pxa_dma_desc ),
++                              hostdata->iobuf.buf.write_desc_phys_addr );
++#else
++      kfree( hostdata->iobuf.iodata );
++#endif
++out:
++      return ret; 
++}
++
++static void pxa_mmc_remove( mmc_controller_t ctrlr )
++{
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      
++/*  1) free buffer(s) */
++#ifndef PIO
++      consistent_free( hostdata->iobuf.iodata, PXA_MMC_IODATA_SIZE,
++                       hostdata->iobuf.buf.phys_addr );
++      consistent_free( hostdata->iobuf.buf.read_desc,
++                       (PXA_MMC_IODATA_SIZE>>5)
++                       * sizeof( pxa_dma_desc ),
++                       hostdata->iobuf.buf.read_desc_phys_addr );
++      consistent_free( hostdata->iobuf.buf.write_desc,
++                       (PXA_MMC_IODATA_SIZE>>5)
++                       * sizeof( pxa_dma_desc ),
++                       hostdata->iobuf.buf.write_desc_phys_addr );
++/*  2) release DMA channel */
++      if ( hostdata->iobuf.buf.chan >= 0 ) {
++              DRCMRRXMMC = 0;
++              DRCMRTXMMC = 0;
++              pxa_free_dma( hostdata->iobuf.buf.chan );
++      }
++#else
++      kfree( hostdata->iobuf.iodata );
++#endif
++/* II. MMC */
++/*  1) release irq */
++      free_irq( IRQ_MMC, ctrlr );
++      CKEN &= ~CKEN12_MMC; /* disable MMC unit clock */
++}
++
++static int pxa_mmc_probe( mmc_controller_t ctrlr )
++{
++      return 1;
++}
++
++#ifdef CONFIG_PM
++static int pxa_mmc_suspend( mmc_controller_t ctrlr )
++{
++      int ret = -EBUSY;
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++      
++      MMC_DEBUG( MMC_DEBUG_LEVEL2, "state=%s\n", 
++                      PXA_MMC_STATE_LABEL( hostdata->state ) );
++
++      if ( hostdata->state == PXA_MMC_FSM_IDLE ) {
++              /* save registers */
++              SAVED_MMC_CLKRT = MMC_CLKRT;
++              SAVED_MMC_RESTO = MMC_RESTO;
++              SAVED_MMC_SPI = MMC_SPI;
++              SAVED_DRCMRRXMMC = DRCMRRXMMC;
++              SAVED_DRCMRTXMMC = DRCMRTXMMC;
++
++#if 0 /* FIXME */
++              /* send CMD0 */
++              if ( (ret = pxa_mmc_stop_bus_clock( ctrlr )) )
++                      goto error;
++
++              MMC_CMD = CMD(0); /* CMD0 with zero argument */
++              MMC_ARGH = 0UL;
++              MMC_ARGL = 0UL; 
++              MMC_CMDAT = 0UL;
++    
++              MMC_DEBUG( MMC_DEBUG_LEVEL3, "CMD0(0x%04x%04x)\n", 0UL, 0UL );
++              if ( (ret = pxa_mmc_complete_cmd( ctrlr, MMC_NORESPONSE, 
++                                              FALSE )) ) 
++              {
++                      ret = -EIO;
++                      goto error;
++              }
++#endif                
++      
++              set_GPIO_mode( GPIO6_MMCCLK );
++              CKEN &= ~CKEN12_MMC; /* disable MMC unit clock */
++              
++              hostdata->suspended = TRUE;
++              ret = 0;
++      }
++error:
++      return ret;
++}
++
++static void pxa_mmc_resume( mmc_controller_t ctrlr )
++{
++      pxa_mmc_hostdata_t hostdata = (pxa_mmc_hostdata_t)ctrlr->host_data;
++
++      if ( hostdata->suspended == TRUE ) {
++              set_GPIO_mode( GPIO6_MMCCLK_MD );
++              CKEN |= CKEN12_MMC; /* enable MMC unit clock */
++              
++              /* restore registers */
++              MMC_CLKRT = SAVED_MMC_CLKRT;
++              MMC_RESTO = SAVED_MMC_RESTO;
++              MMC_SPI = SAVED_MMC_SPI;
++              DRCMRRXMMC = SAVED_DRCMRRXMMC;
++              DRCMRTXMMC = SAVED_DRCMRTXMMC;
++
++              hostdata->suspended = FALSE;
++
++              mmc_update_card_stack( ctrlr->slot ); /* FIXME */
++      }
++      
++      return;
++}
++#endif
++
++static mmc_controller_tmpl_rec_t pxa_mmc_controller_tmpl_rec = {
++      owner:                  THIS_MODULE,
++      name:                   "PXA250",
++      block_size_max:         PXA_MMC_BLKSZ_MAX,
++      nob_max:                PXA_MMC_NOB_MAX,
++      probe:                  pxa_mmc_probe,
++      init:                   pxa_mmc_init,
++      remove:                 __devexit_p( pxa_mmc_remove ),
++#ifdef CONFIG_PM
++      suspend:                pxa_mmc_suspend,
++      resume:                 pxa_mmc_resume,
++#endif /* CONFIG_PM */
++      update_acq:             pxa_mmc_update_acq,
++//    single_card_acq:        pxa_mmc_single_card_acq,
++      init_card_stack:        pxa_mmc_init_card_stack,
++      check_card_stack:       pxa_mmc_check_card_stack,
++      setup_card:             pxa_mmc_setup_card,
++      stream_read:            pxa_mmc_stream_read,
++      read_block:             pxa_mmc_read_block,
++      read_mblock:            pxa_mmc_read_mblock,
++      stream_write:           pxa_mmc_stream_write,
++      write_block:            pxa_mmc_write_block,
++      write_mblock:           pxa_mmc_write_mblock
++      /* TODO
++      sg_io:                  pxa_mmc_sg_io
++       */
++      /* TODO: 
++       *  erase, 
++       *  write protection,
++       *  lock/password management methods
++       */
++};
++
++static int __devinit mmc_pxa_module_init( void )
++{
++      int ret = -ENODEV;
++#ifdef CONFIG_ARCH_RAMSES
++      RAMSES_MMC_ON();
++      udelay(1000);
++#endif
++
++      host = mmc_register( MMC_REG_TYPE_HOST, &pxa_mmc_controller_tmpl_rec,
++                      sizeof( pxa_mmc_hostdata_rec_t ) );
++      if ( !host ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, 
++                              "failed to register with MMC core\n" );
++              goto error;
++      }
++      
++      ret = 0;
++error:
++      return ret;
++}
++
++static void __devexit mmc_pxa_module_cleanup( void )
++{
++      mmc_unregister( MMC_REG_TYPE_HOST, host );
++#ifdef CONFIG_ARCH_RAMSES
++      RAMSES_MMC_OFF();
++#endif
++}
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_LICENSE( "GPL" );
++
++module_init( mmc_pxa_module_init );
++module_exit( mmc_pxa_module_cleanup );
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc_pxa.h
+@@ -0,0 +1,278 @@
++/*
++ *  linux/drivers/mmc/mmc_pxa.h 
++ *
++ *  Author: Vladimir Shebordaev, Igor Oblakov   
++ *  Copyright:  MontaVista Software Inc.
++ *
++ *  $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $
++ *
++ *  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 __MMC_PXA_P_H__
++#define __MMC_PXA_P_H__
++
++#include <linux/completion.h>
++
++#define PIO
++
++/* PXA-250 MMC controller registers */
++
++/* MMC_STRPCL */
++#define MMC_STRPCL_STOP_CLK     (0x0001UL)
++#define MMC_STRPCL_START_CLK        (0x0002UL)
++
++/* MMC_STAT */
++#define MMC_STAT_END_CMD_RES        (0x0001UL << 13)
++#define MMC_STAT_PRG_DONE       (0x0001UL << 12)
++#define MMC_STAT_DATA_TRAN_DONE     (0x0001UL << 11)
++#define MMC_STAT_CLK_EN         (0x0001UL << 8)
++#define MMC_STAT_RECV_FIFO_FULL     (0x0001UL << 7)
++#define MMC_STAT_XMIT_FIFO_EMPTY    (0x0001UL << 6)
++#define MMC_STAT_RES_CRC_ERROR      (0x0001UL << 5)
++#define MMC_STAT_SPI_READ_ERROR_TOKEN   (0x0001UL << 4)
++#define MMC_STAT_CRC_READ_ERROR     (0x0001UL << 3)
++#define MMC_STAT_CRC_WRITE_ERROR    (0x0001UL << 2)
++#define MMC_STAT_TIME_OUT_RESPONSE  (0x0001UL << 1)
++#define MMC_STAT_READ_TIME_OUT      (0x0001UL)
++
++#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\
++        |MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\
++        |MMC_STAT_READ_TIME_OUT)
++
++/* MMC_CLKRT */
++#define MMC_CLKRT_20MHZ         (0x0000UL)
++#define MMC_CLKRT_10MHZ         (0x0001UL)
++#define MMC_CLKRT_5MHZ          (0x0002UL)
++#define MMC_CLKRT_2_5MHZ        (0x0003UL)
++#define MMC_CLKRT_1_25MHZ       (0x0004UL)
++#define MMC_CLKRT_0_625MHZ      (0x0005UL)
++#define MMC_CLKRT_0_3125MHZ     (0x0006UL)
++
++/* MMC_SPI */
++#define MMC_SPI_DISABLE         (0x00UL)
++#define MMC_SPI_EN          (0x01UL)
++#define MMC_SPI_CS_EN           (0x01UL << 2)
++#define MMC_SPI_CS_ADDRESS      (0x01UL << 3)
++#define MMC_SPI_CRC_ON          (0x01UL << 1)
++
++/* MMC_CMDAT */
++#define MMC_CMDAT_MMC_DMA_EN        (0x0001UL << 7)
++#define MMC_CMDAT_INIT          (0x0001UL << 6)
++#define MMC_CMDAT_BUSY          (0x0001UL << 5)
++#define MMC_CMDAT_STREAM        (0x0001UL << 4)
++#define MMC_CMDAT_BLOCK         (0x0000UL << 4)
++#define MMC_CMDAT_WRITE         (0x0001UL << 3)
++#define MMC_CMDAT_READ          (0x0000UL << 3)
++#define MMC_CMDAT_DATA_EN       (0x0001UL << 2)
++#define MMC_CMDAT_R1            (0x0001UL)
++#define MMC_CMDAT_R2            (0x0002UL)
++#define MMC_CMDAT_R3            (0x0003UL)
++
++/* MMC_RESTO */
++#define MMC_RES_TO_MAX          (0x007fUL) /* [6:0] */
++
++/* MMC_RDTO */
++#define MMC_READ_TO_MAX         (0x0ffffUL) /* [15:0] */
++
++/* MMC_BLKLEN */
++#define MMC_BLK_LEN_MAX         (0x03ffUL) /* [9:0] */
++
++/* MMC_PRTBUF */
++#define MMC_PRTBUF_BUF_PART_FULL       (0x01UL) 
++#define MMC_PRTBUF_BUF_FULL           (0x00UL    )
++
++/* MMC_I_MASK */
++#define MMC_I_MASK_TXFIFO_WR_REQ        (0x01UL << 6)
++#define MMC_I_MASK_RXFIFO_RD_REQ        (0x01UL << 5)
++#define MMC_I_MASK_CLK_IS_OFF           (0x01UL << 4)
++#define MMC_I_MASK_STOP_CMD         (0x01UL << 3)
++#define MMC_I_MASK_END_CMD_RES          (0x01UL << 2)
++#define MMC_I_MASK_PRG_DONE         (0x01UL << 1)
++#define MMC_I_MASK_DATA_TRAN_DONE       (0x01UL)
++#define MMC_I_MASK_ALL              (0x07fUL)
++
++
++/* MMC_I_REG */
++#define MMC_I_REG_TXFIFO_WR_REQ     (0x01UL << 6)
++#define MMC_I_REG_RXFIFO_RD_REQ     (0x01UL << 5)
++#define MMC_I_REG_CLK_IS_OFF        (0x01UL << 4)
++#define MMC_I_REG_STOP_CMD      (0x01UL << 3)
++#define MMC_I_REG_END_CMD_RES       (0x01UL << 2)
++#define MMC_I_REG_PRG_DONE      (0x01UL << 1)
++#define MMC_I_REG_DATA_TRAN_DONE    (0x01UL)
++#define MMC_I_REG_ALL           (0x007fUL)
++
++/* MMC_CMD */
++#define MMC_CMD_INDEX_MAX       (0x006fUL)  /* [5:0] */
++#define CMD(x)  (x)
++
++/* MMC_ARGH */
++/* MMC_ARGL */
++/* MMC_RES */
++/* MMC_RXFIFO */
++#define MMC_RXFIFO_PHYS_ADDR 0x41100040 //MMC_RXFIFO physical address
++/* MMC_TXFIFO */ 
++#define MMC_TXFIFO_PHYS_ADDR 0x41100044 //MMC_TXFIFO physical address
++
++/* implementation specific declarations */
++#define PXA_MMC_BLKSZ_MAX (1<<9) /* actually 1023 */
++#define PXA_MMC_NOB_MAX ((1<<16)-2)
++#define PXA_MMC_BLOCKS_PER_BUFFER (2)
++
++#define PXA_MMC_IODATA_SIZE (PXA_MMC_BLOCKS_PER_BUFFER*PXA_MMC_BLKSZ_MAX) /* 1K */
++
++typedef enum _pxa_mmc_fsm {         /* command processing FSM */
++    PXA_MMC_FSM_IDLE = 1,
++    PXA_MMC_FSM_CLK_OFF,
++    PXA_MMC_FSM_END_CMD,
++    PXA_MMC_FSM_BUFFER_IN_TRANSIT,
++    PXA_MMC_FSM_END_BUFFER, 
++    PXA_MMC_FSM_END_IO, 
++    PXA_MMC_FSM_END_PRG,
++    PXA_MMC_FSM_ERROR
++} pxa_mmc_state_t;
++
++#define PXA_MMC_STATE_LABEL( state ) (\
++    (state == PXA_MMC_FSM_IDLE) ? "IDLE" :\
++    (state == PXA_MMC_FSM_CLK_OFF) ? "CLK_OFF" :\
++    (state == PXA_MMC_FSM_END_CMD) ? "END_CMD" :\
++    (state == PXA_MMC_FSM_BUFFER_IN_TRANSIT) ? "IN_TRANSIT" :\
++    (state == PXA_MMC_FSM_END_BUFFER) ? "END_BUFFER" :\
++    (state == PXA_MMC_FSM_END_IO) ? "END_IO" :\
++    (state == PXA_MMC_FSM_END_PRG) ? "END_PRG" : "UNKNOWN" )
++
++typedef enum _pxa_mmc_result {
++    PXA_MMC_NORMAL = 0,
++    PXA_MMC_INVALID_STATE = -1,
++    PXA_MMC_TIMEOUT = -2,
++    PXA_MMC_ERROR = -3
++} pxa_mmc_result_t;
++
++typedef u32 pxa_mmc_clkrt_t;
++
++typedef char *pxa_mmc_iodata_t;
++#ifdef PIO
++typedef struct _pxa_mmc_piobuf_rec {
++    char *pos; /* current buffer position */
++    int   cnt; /* byte counter */
++} pxa_mmc_piobuf_rec_t, *pxa_mmc_piobuf_t;
++#else /* i.e. DMA */
++typedef struct _pxa_mmc_dmabuf_rec { /* TODO: buffer ring, DMA irq completion */
++      int chan; /* dma channel no */
++      dma_addr_t phys_addr; /* iodata physical address */
++      pxa_dma_desc *read_desc; /* input descriptor array virtual address */
++      pxa_dma_desc *write_desc; /* output descriptor array virtual address */
++      dma_addr_t read_desc_phys_addr; /* descriptor array physical address */
++      dma_addr_t write_desc_phys_addr; /* descriptor array physical address */
++      pxa_dma_desc *last_read_desc; /* last input descriptor 
++                                     * used by the previous transfer 
++                                     */
++      pxa_dma_desc *last_write_desc; /* last output descriptor
++                                      * used by the previous transfer 
++                                      */
++} pxa_mmc_dmabuf_rec_t, *pxa_mmc_dmabuf_t;
++#endif
++
++typedef struct _pxa_mmc_iobuf_rec {
++    ssize_t blksz; /* current block size in bytes */
++    ssize_t bufsz; /* buffer size for each transfer */
++    ssize_t nob; /* number of blocks pers buffer */ 
++#ifndef PIO 
++    pxa_mmc_dmabuf_rec_t buf; /* i.e. DMA buffer ring on the iodata */
++#else /* i.e. DMA */
++    pxa_mmc_piobuf_rec_t buf; /* PIO buffer accounting */
++#endif
++    pxa_mmc_iodata_t iodata; /* I/O data buffer */
++} pxa_mmc_iobuf_rec_t, *pxa_mmc_iobuf_t;
++
++typedef struct _pxa_mmc_hostdata_rec {
++    pxa_mmc_state_t state; /* FSM */
++#ifdef CONFIG_PM
++    int suspended;
++#endif
++    pxa_mmc_iobuf_rec_t iobuf; /* data transfer state */
++    
++    int busy;   /* atomic busy flag */
++    struct completion completion; /* completion */
++#if CONFIG_MMC_DEBUG_IRQ
++    int irqcnt;
++    int timeo;
++#endif
++    
++/* cached controller state */
++    u32 mmc_i_reg;  /* interrupt last requested */
++    u32 mmc_i_mask; /* mask to be set by intr handler */
++    u32 mmc_stat;   /* status register at the last intr */
++    u32 mmc_cmdat;  /* MMC_CMDAT at the last inr */
++    u8 mmc_res[16]; /* response to the last command in host order */ 
++    u32 saved_mmc_clkrt;
++    u32 saved_mmc_resto;
++    u32 saved_mmc_spi;
++    u32 saved_drcmrrxmmc;
++    u32 saved_drcmrtxmmc;
++
++/* controller options */
++    pxa_mmc_clkrt_t clkrt; /* current bus clock rate */
++} pxa_mmc_hostdata_rec_t, *pxa_mmc_hostdata_t;
++
++#define PXA_MMC_STATUS( ctrlr ) (((pxa_mmc_hostdata_t)ctrlr->host_data)->mmc_stat)
++#define PXA_MMC_RESPONSE( ctrlr, idx ) ((((pxa_mmc_hostdata_t)ctrlr->host_data)->mmc_res)[idx])
++#define PXA_MMC_CLKRT( ctrlr ) (((pxa_mmc_hostdata_t)ctrlr->host_data)->clkrt)  
++
++#define SAVED_MMC_CLKRT (hostdata->saved_mmc_clkrt)
++#define SAVED_MMC_RESTO (hostdata->saved_mmc_resto)
++#define SAVED_MMC_SPI (hostdata->saved_mmc_spi)
++#define SAVED_DRCMRRXMMC (hostdata->saved_drcmrrxmmc )
++#define SAVED_DRCMRTXMMC (hostdata->saved_drcmrtxmmc )
++
++static inline int pxa_mmc_clkrt( int speed )
++{
++    return MMC_CLKRT_20MHZ; /* TODO */
++}
++
++/* PXA MMC controller specific card data */
++typedef struct _pxa_mmc_card_data_rec { 
++    pxa_mmc_clkrt_t clkrt;   /* clock rate to be set for the card */
++} pxa_mmc_card_data_rec_t, *pxa_mmc_card_data_t;
++
++#ifdef CONFIG_MMC_DEBUG
++#undef MMC_DUMP_R1
++#undef MMC_DUMP_R2
++#undef MMC_DUMP_R3
++#define MMC_DUMP_R2( ctrlr ) MMC_DEBUG( MMC_DEBUG_LEVEL3, \
++"R2 response: %02x %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", \
++PXA_MMC_RESPONSE( ctrlr, 15 ), \
++PXA_MMC_RESPONSE( ctrlr, 14 ), \
++PXA_MMC_RESPONSE( ctrlr, 13 ), \
++PXA_MMC_RESPONSE( ctrlr, 12 ), \
++PXA_MMC_RESPONSE( ctrlr, 11 ), \
++PXA_MMC_RESPONSE( ctrlr, 10 ), \
++PXA_MMC_RESPONSE( ctrlr, 9 ), \
++PXA_MMC_RESPONSE( ctrlr, 8 ), \
++PXA_MMC_RESPONSE( ctrlr, 7 ), \
++PXA_MMC_RESPONSE( ctrlr, 6 ), \
++PXA_MMC_RESPONSE( ctrlr, 5 ), \
++PXA_MMC_RESPONSE( ctrlr, 4 ), \
++PXA_MMC_RESPONSE( ctrlr, 3 ), \
++PXA_MMC_RESPONSE( ctrlr, 2 ), \
++PXA_MMC_RESPONSE( ctrlr, 1 ), \
++PXA_MMC_RESPONSE( ctrlr, 0 ) );
++#define MMC_DUMP_R1( ctrlr ) MMC_DEBUG( MMC_DEBUG_LEVEL3, \
++"R1(b) response: %02x %02x%02x%02x%02x\n", \
++PXA_MMC_RESPONSE( ctrlr, 5 ), \
++PXA_MMC_RESPONSE( ctrlr, 4 ), \
++PXA_MMC_RESPONSE( ctrlr, 3 ), \
++PXA_MMC_RESPONSE( ctrlr, 2 ), \
++PXA_MMC_RESPONSE( ctrlr, 1 ) );
++#define MMC_DUMP_R3( ctrlr )  MMC_DEBUG( MMC_DEBUG_LEVEL3, \
++"R3 response: %02x %02x%02x%02x%02x\n", \
++PXA_MMC_RESPONSE( ctrlr, 5 ), \
++PXA_MMC_RESPONSE( ctrlr, 4 ), \
++PXA_MMC_RESPONSE( ctrlr, 3 ), \
++PXA_MMC_RESPONSE( ctrlr, 2 ), \
++PXA_MMC_RESPONSE( ctrlr, 1 ) );
++
++#endif
++#endif /* __MMC_PXA_P_H__ */
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/mmc_test.c
+@@ -0,0 +1,538 @@
++/*
++ *  linux/drivers/mmc/mmc_test.c 
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: mmc_test.c,v 0.4 2002/08/01 12:26:40 ted Exp ted $
++ * 
++ *  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/version.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++
++#include <linux/fs.h>
++#ifdef CONFIG_DEVFS_FS
++#include <linux/devfs_fs_kernel.h>
++#endif
++
++#include <asm/uaccess.h>
++
++#include <mmc/types.h>
++#include <mmc/mmc.h>
++#include <mmc/ioctl.h>
++
++#include "types.h"
++#include "mmc.h"
++
++typedef struct _mmc_test_device_rec mmc_test_device_rec_t;
++typedef struct _mmc_test_device_rec *mmc_test_device_t;
++
++struct _mmc_test_device_rec {
++      mmc_card_t card;
++      mmc_transfer_mode_t transfer_mode;
++      int usage;
++#ifdef CONFIG_DEVFS_FS
++      devfs_handle_t devfs_handle;
++#endif
++};
++
++/* MMC device table */
++static mmc_test_device_rec_t mmc_test_device[MMC_CONTROLLERS_MAX][MMC_CARDS_MAX];
++static DECLARE_MUTEX(mmc_test_device_mutex);
++
++static inline mmc_test_device_t __mmc_test_get_device( kdev_t rdev )
++{
++      mmc_test_device_t ret = NULL;
++      u8 minor = MINOR( rdev );
++      int host_no, card_no;
++
++      host_no = minor >> MMC_MINOR_HOST_SHIFT;
++      if ( host_no >= MMC_CONTROLLERS_MAX )
++              goto error;
++      
++      card_no = minor & MMC_MINOR_CARD_MASK;
++      if ( card_no >= MMC_CARDS_MAX )
++              goto error;
++      
++      ret = &mmc_test_device[host_no][card_no];
++      if ( !ret->card ) {
++              ret->card = mmc_get_card( host_no, card_no );
++              if ( !ret->card ) {
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "failed to get card: host=%d, card=%d\n", host_no, card_no );
++                      ret = NULL;
++                      goto error;
++              }
++              
++      }
++      ++ret->usage;
++      
++error:
++      return ret;
++}
++
++static inline void __mmc_test_put_device( mmc_test_device_t dev )
++{
++      mmc_put_card( dev->card );
++      --dev->usage;
++}
++
++static inline mmc_test_device_t mmc_test_get_device( kdev_t kdev )
++{
++      mmc_test_device_t ret = NULL;
++      
++      down( &mmc_test_device_mutex );
++      ret = __mmc_test_get_device( kdev );
++      up( &mmc_test_device_mutex );
++
++      return ret;
++}
++
++static inline void mmc_test_put_device( mmc_test_device_t dev )
++{
++      if ( dev ) {
++              down( &mmc_test_device_mutex );
++              __mmc_test_put_device( dev );
++              if ( !dev->usage ) {
++                      if ( dev->card ) {
++                              if ( dev->card->usage ) {
++                                      MMC_DEBUG( MMC_DEBUG_LEVEL0, 
++                                              "broken card reference\n" );
++                              }
++                              memset( dev, 0, sizeof( mmc_test_device_rec_t ) );
++                      }
++              }
++              up( &mmc_test_device_mutex );
++      }
++}
++
++static inline int mmc_test_set_transfer_mode( mmc_test_device_t dev, mmc_transfer_mode_t mode )
++{
++      int ret = -1;
++      
++      if ( dev ) {
++              down( &mmc_test_device_mutex );
++              dev->transfer_mode = mode;
++              ret = 0;
++              up( &mmc_test_device_mutex );
++      }
++      return ret;
++}
++
++static inline mmc_transfer_mode_t mmc_test_get_transfer_mode( mmc_test_device_t dev )
++{
++      mmc_transfer_mode_t ret = MMC_TRANSFER_MODE_UNDEFINED;
++      
++      if ( dev ) {
++              down( &mmc_test_device_mutex );
++              ret = dev->transfer_mode;
++              up( &mmc_test_device_mutex );
++      }
++      return ret;
++}
++
++static int mmc_test_open( struct inode *inode, struct file *file )
++{
++      int ret = -ENODEV;
++      mmc_test_device_t dev = NULL;
++      
++      MOD_INC_USE_COUNT;
++      
++      __ENTER0( );    
++      dev = mmc_test_get_device( inode->i_rdev );
++      if ( !dev || !dev->card ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "failed to acquire device\n" );
++              goto error;
++      }
++      
++      if ( dev->card->usage > 1 ) {
++              ret = -EBUSY;
++              goto error;
++      }
++      
++      dev->transfer_mode = MMC_TEST_TRANSFER_MODE_DEFAULT; /* FIXME: should check card CCC */
++      file->private_data = dev;
++      
++      __LEAVE0( );
++      return 0;
++error:
++      MOD_DEC_USE_COUNT;
++      mmc_test_put_device( dev );
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static int mmc_test_release( struct inode *inode, struct file *file )
++{
++      int ret = -ENODEV;
++      mmc_test_device_t dev = (mmc_test_device_t)file->private_data;
++      
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "file->private_data == NULL\n" );
++              goto error;
++      }
++
++      __ENTER( "host=%d, card=%d", dev->card->ctrlr->slot, dev->card->slot );
++      
++      file->private_data = NULL;
++      
++      mmc_test_put_device( dev );
++      MOD_DEC_USE_COUNT;
++      
++      ret = 0;
++error:        
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static ssize_t mmc_test_read( struct file *file, char *buf, size_t size, loff_t *ppos )
++{
++      ssize_t ret = -ENODEV;
++      ssize_t retsize = 0;
++      mmc_test_device_t dev = (mmc_test_device_t)file->private_data;
++
++      __ENTER( "host=%d, card=%d, size=%d", dev->card->ctrlr->slot, dev->card->slot, size );
++      
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "file->private_data == NULL\n" );
++              goto error;
++      }
++      
++      switch ( dev->transfer_mode ) {
++              char *mbuf;
++              
++              case MMC_TRANSFER_MODE_BLOCK_SINGLE:
++                      mbuf = kmalloc( 512, GFP_ATOMIC ); /* FIXME: actual read_bl_len or ctrlr->block_size_max whichever is less ), GFP_KERNEL */
++                      if ( !mbuf ) { 
++                              ret = -ENOMEM; 
++                              goto error; 
++                      }
++                      
++                      while( size > 0 ) {
++                              int lsize = (size > 512) ? 512 : size; 
++              
++                              MMC_DEBUG( MMC_DEBUG_LEVEL4, 
++                                              "before mmc_read mbuf=0x%x "
++                                              "lsize=%d ppos=0x%x *ppos=%d\n",
++                                              mbuf, lsize, ppos, *ppos );
++                              ret = mmc_read( dev->card, 
++                                              MMC_TRANSFER_MODE_BLOCK_SINGLE,
++                                              mbuf, lsize, ppos );
++                              if ( ret <= 0 )
++                                      break;
++                              
++                              /* Copy to user */
++                              if ( copy_to_user( buf, mbuf, ret ) ) {
++                                      ret = -EFAULT;
++                                      break;
++                              }
++                              retsize += ret;
++                              buf += ret;
++                              size -= ret;
++                      }
++                      
++                      if ( retsize > 0 )
++                              ret = retsize;
++                      kfree(mbuf);
++                      break;
++                      
++              case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:
++                      mbuf = kmalloc( 1024, GFP_ATOMIC ); /* FIXME */
++                      if ( !mbuf ) { 
++                              ret = -ENOMEM; 
++                              goto error; 
++                      }
++                      
++                      while( size > 0 ) {
++                              int lsize = (size > 1024) ? 1024 : size; 
++              
++                              MMC_DEBUG( MMC_DEBUG_LEVEL4, 
++                                              "before mmc_read mbuf=0x%x "
++                                              "lsize=%d ppos=0x%x *ppos=%d\n",
++                                              mbuf, lsize, ppos, *ppos );
++                              ret = mmc_read( dev->card, 
++                                      MMC_TRANSFER_MODE_BLOCK_MULTIPLE,
++                                      mbuf, lsize, ppos );
++                              if ( ret <= 0 )
++                                      break;
++                              
++                              /* Copy to user */
++                              if ( copy_to_user( buf, mbuf, ret ) ) {
++                                      ret = -EFAULT;
++                                      break;
++                              }
++                              retsize += ret;
++                              buf += ret;
++                              size -= ret;
++                      }
++                      
++                      if ( retsize > 0 )
++                              ret = retsize;
++                      kfree(mbuf);
++                      break;
++                      
++              case MMC_TRANSFER_MODE_STREAM:
++                      ret = mmc_read( dev->card, dev->transfer_mode,
++                                      buf, size, ppos );
++                      break;
++                      
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "invalid transfer mode\n" );
++      }
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static ssize_t mmc_test_write( struct file *file, const char *buf, size_t size, loff_t *ppos )
++{
++      ssize_t ret = -ENODEV;
++      mmc_test_device_t dev = (mmc_test_device_t)file->private_data;
++      int retsize=0;
++
++      __ENTER( "host=%d, card=%d, size=%d", dev->card->ctrlr->slot, dev->card->slot, size );
++      
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "file->private_data == NULL\n" );
++              goto error;
++      }
++      
++      switch ( dev->transfer_mode ) {
++              char *mbuf;
++              
++              case MMC_TRANSFER_MODE_BLOCK_SINGLE:
++                      mbuf = kmalloc( 512, GFP_ATOMIC ); /* FIXME: actual write_bl_len or ctrlr->block_size_max whichever is less, GFP_KERNEL */
++                      if ( !mbuf ) { 
++                              ret = -ENOMEM; 
++                              goto error; 
++                      }
++      
++                      while ( size > 0 ) {
++                              int lsize = ( size > 512 ) ? 512 : size;
++              
++                              /* Copy from user */
++                              if ( copy_from_user( mbuf, buf, lsize ) ) {
++                                      ret = -EFAULT;
++                                      break;
++                              }
++                              
++                              ret = mmc_write( dev->card,
++                                      MMC_TRANSFER_MODE_BLOCK_SINGLE, 
++                                      mbuf, lsize, ppos );
++                              if( ret <= 0 )
++                                      break;
++      
++                              retsize += ret;
++                              buf += ret;
++                              size -= ret;
++                      }
++                      
++                      if ( retsize > 0 )
++                              ret = retsize;
++                      
++                      kfree( mbuf );
++                      break; 
++                      
++              case MMC_TRANSFER_MODE_BLOCK_MULTIPLE:
++                      mbuf = kmalloc( 1024, GFP_ATOMIC ); /* FIXME */
++                      if ( !mbuf ) { 
++                              ret = -ENOMEM; 
++                              goto error; 
++                      }
++                      
++                      while( size > 0 ) {
++                              int lsize = (size > 1024) ? 1024 : size; 
++              
++                              MMC_DEBUG( MMC_DEBUG_LEVEL4, 
++                                              "before mmc_read mbuf=0x%x "
++                                              "lsize=%d ppos=0x%x *ppos=%d\n",
++                                              mbuf, lsize, ppos, *ppos );
++                              ret = mmc_write( dev->card, 
++                                      MMC_TRANSFER_MODE_BLOCK_MULTIPLE,
++                                      mbuf, lsize, ppos );
++                              if ( ret <= 0 )
++                                      break;
++                              
++                              /* Copy to user */
++                              if ( copy_to_user( (char *)buf, mbuf, ret ) ) {
++                                      ret = -EFAULT;
++                                      break;
++                              }
++                              retsize += ret;
++                              buf += ret;
++                              size -= ret;
++                      }
++                      
++                      if ( retsize > 0 )
++                              ret = retsize;
++                      kfree(mbuf);
++                      break;
++              case MMC_TRANSFER_MODE_STREAM:
++                      ret = mmc_write( dev->card, dev->transfer_mode,
++                                      buf, size, ppos );
++                      break;
++                      
++              default:
++                      MMC_DEBUG( MMC_DEBUG_LEVEL0, "invalid transfer mode\n" );
++      }
++error:
++      __LEAVE( "ret=%d", ret );
++      return ret;
++}
++
++static loff_t mmc_test_llseek( struct file *file, loff_t offset, int origin )
++{
++      loff_t ret = -ESPIPE;
++      mmc_test_device_t dev = (mmc_test_device_t)file->private_data;
++      mmc_card_t card;
++      
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "file->private_data == NULL\n" );
++              ret = -ENODEV;
++              goto error;
++      }
++      
++      __ENTER( "host=%d, card=%d, off=%ld, orig=%d", dev->card->ctrlr->slot, dev->card->slot, (long)offset, origin );
++      
++      card = dev->card;
++      
++      switch ( origin ) {
++              case SEEK_CUR:
++                      file->f_pos += offset;
++                      break;
++      
++              case SEEK_END:
++                      file->f_pos = card->info.capacity + offset;
++                      break;
++              
++              case SEEK_SET:
++                      file->f_pos = offset;
++                      break;
++                      
++              default:
++                      ret = -EINVAL;
++                      goto error;
++      }
++      
++      ret = file->f_pos;
++error:        
++      __LEAVE( "ret=%ld", (long)ret );
++      return ret;
++}
++
++static int mmc_test_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg )
++{
++      int ret = -ENODEV;
++      mmc_test_device_t dev = (mmc_test_device_t)file->private_data;
++
++      if ( !dev ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "file->private_data == NULL\n" );
++              goto error;
++      }
++      
++      switch ( cmd ) {
++              case IOCMMCSTRNSMODE:
++                      if ( get_user( ret, (int *)arg ) ) {
++                              ret = -EFAULT;
++                              goto error;
++                      }
++                      ret = mmc_test_set_transfer_mode( dev, ret );
++                      break;
++                      
++              case IOCMMCGTRNSMODE:
++                      ret = mmc_test_get_transfer_mode( dev );
++                      if ( put_user( ret, (int *)arg ) ) 
++                              ret = -EFAULT;
++                      break;
++              
++              default:
++                      ret = mmc_ioctl( dev->card, cmd, arg );
++      }
++      
++error:
++      return ret;
++}
++
++struct file_operations mmc_test_fops = {
++      owner:          THIS_MODULE,
++      open:           mmc_test_open,
++      release:        mmc_test_release,
++      read:           mmc_test_read,
++      write:          mmc_test_write,
++      ioctl:          mmc_test_ioctl,
++      llseek:         mmc_test_llseek
++};
++
++#ifdef CONFIG_DEVFS_FS
++static int mmc_test_add_card( mmc_card_t card ) /* TODO */
++{
++      int ret = -1;
++      __ENTER( "host=%d, card=%d", card->ctrlr->slot, card->slot );
++/* TODO: make kdev; register with devfs */
++      __LEAVE0( );
++      return ret;
++}
++
++static int mmc_test_remove_card( mmc_card_t card ) /* TODO */
++{
++      int ret = -1;
++      __ENTER( "host=%d, card=%d", card->ctrlr->slot, card->slot );
++/* TODO: make kdev; unregister with devfs */
++      __LEAVE0( );
++      return ret;
++}
++
++static mmc_notifier_rec_t mmc_test_notifier = {
++      add:    mmc_test_add_card,
++      remove: mmc_test_remove_card
++};
++#endif /* CONFIG_DEVFS_FS */
++
++static int __init mmc_test_module_init( void )
++{
++      int ret = -ENODEV;
++
++#ifdef CONFIG_DEVFS_FS
++      if ( !mmc_register( MMC_REG_TYPE_USER, &mmc_test_notifier, 0 ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, "failed to register with MMC core\n" );
++              goto error;
++      }
++#else
++      mmc_register( MMC_REG_TYPE_USER, NULL, 0 );
++      if ( register_chrdev( MMC_TEST_MAJOR, "mmc_test", &mmc_test_fops ) ) {
++              MMC_DEBUG( MMC_DEBUG_LEVEL0, 
++                              "failed to request device major number\n" );
++              mmc_unregister( MMC_REG_TYPE_USER, NULL );
++              goto error;
++      }
++#endif
++      
++      memset( mmc_test_device, 0, sizeof( mmc_test_device ) );
++      
++      ret = 0;
++error:
++      return ret;
++}
++
++static void __exit mmc_test_module_cleanup( void )
++{
++#ifdef CONFIG_DEVFS_FS
++      mmc_unregister( MMC_REG_TYPE_USER, &mmc_test_notifier );
++#else
++      mmc_unregister( MMC_REG_TYPE_USER, NULL );
++      unregister_chrdev( MMC_TEST_MAJOR, "mmc_test" );
++#endif
++}
++
++EXPORT_NO_SYMBOLS;
++
++module_init( mmc_test_module_init );
++module_exit( mmc_test_module_cleanup );
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/pm_test.c
+@@ -0,0 +1,29 @@
++/* Power Managment Test Module.  RTSoft Co. 2002 */ 
++
++/* The necessary header files */
++
++/* Standard in kernel modules */
++#include <linux/kernel.h>   /* We're doing kernel work */
++#include <linux/module.h>   /* Specifically, a module */
++#define CONFIG_PM
++#include <linux/pm.h>
++
++
++static int pmdata = -1;
++
++/* Initialize the module - register the proc file  */
++
++int init_module()
++{
++  pm_send_all(PM_SUSPEND,&pmdata);    
++  return(0);
++}
++
++
++/* Cleanup - unregister our file from /proc */
++void cleanup_module()
++{
++  pm_send_all(PM_RESUME,NULL);
++}
++
++MODULE_LICENSE( "GPL" );
+--- /dev/null
++++ linux-2.4.27/drivers/mmc/types.h
+@@ -0,0 +1,59 @@
++/*
++ *  linux/drivers/mmc/types.h
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *  $Id: types.h,v 0.5 2002/08/13 17:34:02 ted Exp ted $
++ *
++ *  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 __MMC_TYPES_P_H__
++#define __MMC_TYPES_P_H__
++
++#ifdef __KERNEL__
++#include <linux/kdev_t.h>
++
++typedef enum _mmc_reg_type mmc_reg_type_t;
++typedef enum _mmc_response mmc_response_fmt_t;
++
++/* MMC card private description */
++typedef struct _mmc_card_rec mmc_card_rec_t;
++typedef struct _mmc_card_rec *mmc_card_t;
++typedef enum _mmc_dir mmc_dir_t;
++typedef enum _mmc_buftype mmc_buftype_t;
++
++/* notifier declarations */
++typedef struct _mmc_notifier_rec mmc_notifier_rec_t;
++typedef struct _mmc_notifier_rec *mmc_notifier_t;
++
++typedef int (*mmc_notifier_fn_t) ( mmc_card_t );
++
++/* MMC card stack */
++typedef struct _mmc_card_stack_rec mmc_card_stack_rec_t;
++typedef struct _mmc_card_stack_rec *mmc_card_stack_t;
++
++typedef struct _mmc_data_transfer_req_rec mmc_data_transfer_req_rec_t;
++typedef struct _mmc_data_transfer_req_rec *mmc_data_transfer_req_t;
++
++/* MMC controller */
++typedef struct _mmc_controller_tmpl_rec mmc_controller_tmpl_rec_t;
++typedef struct _mmc_controller_tmpl_rec *mmc_controller_tmpl_t;
++
++typedef enum _mmc_controller_state mmc_controller_state_t;
++typedef struct _mmc_controller_rec mmc_controller_rec_t;
++typedef struct _mmc_controller_rec *mmc_controller_t;
++
++/* various kernel types */
++typedef struct semaphore semaphore_t;
++typedef struct rw_semaphore rwsemaphore_t;
++typedef struct proc_dir_entry proc_dir_entry_rec_t;
++typedef struct proc_dir_entry *proc_dir_entry_t;
++typedef struct gendisk gendisk_rec_t;
++typedef struct gendisk *gendisk_t;
++#endif /* __KERNEL__ */
++
++#endif /* __MMC_TYPES_P_H__ */
++
+--- linux-2.4.27/drivers/mtd/maps/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/mtd/maps/Config.in
+@@ -75,6 +75,7 @@
+ 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
+--- linux-2.4.27/drivers/mtd/maps/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/mtd/maps/Makefile
+@@ -15,6 +15,9 @@
+ 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
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/maps/lubbock.c
+@@ -0,0 +1,168 @@
++/*
++ * $Id:
++ *
++ * Map driver for the Lubbock developer platform.
++ *
++ * Author:    Nicolas Pitre
++ * Copyright: (C) 2001 MontaVista Software Inc.
++ * 
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++
++#define WINDOW_ADDR   0
++//#define WINDOW_ADDR         0x04000000
++#define WINDOW_SIZE   64*1024*1024
++
++static __u8 lubbock_read8(struct map_info *map, unsigned long ofs)
++{
++      return *(__u8 *)(map->map_priv_1 + ofs);
++}
++
++static __u16 lubbock_read16(struct map_info *map, unsigned long ofs)
++{
++      return *(__u16 *)(map->map_priv_1 + ofs);
++}
++
++static __u32 lubbock_read32(struct map_info *map, unsigned long ofs)
++{
++      return *(__u32 *)(map->map_priv_1 + ofs);
++}
++
++static void lubbock_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 lubbock_write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++      *(__u8 *)(map->map_priv_1 + adr) = d;
++}
++
++static void lubbock_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++      *(__u16 *)(map->map_priv_1 + adr) = d;
++}
++
++static void lubbock_write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++      *(__u32 *)(map->map_priv_1 + adr) = d;
++}
++
++static void lubbock_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 lubbock_map = {
++      name: "Lubbock flash",
++      size: WINDOW_SIZE,
++      read8:          lubbock_read8,
++      read16:         lubbock_read16,
++      read32:         lubbock_read32,
++      copy_from:      lubbock_copy_from,
++      write8:         lubbock_write8,
++      write16:        lubbock_write16,
++      write32:        lubbock_write32,
++      copy_to:        lubbock_copy_to
++};
++
++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
++      }
++};
++
++#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 int __init init_lubbock(void)
++{
++      struct mtd_partition *parts;
++      int nb_parts = 0;
++      int parsed_nr_parts = 0;
++      char *part_type = "static";
++
++      lubbock_map.buswidth = (BOOT_DEF & 1) ? 2 : 4;
++      printk( "Probing Lubbock flash at physical address 0x%08x (%d-bit buswidth)\n",
++              WINDOW_ADDR, lubbock_map.buswidth * 8 );
++      lubbock_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0);
++      if (!lubbock_map.map_priv_1) {
++              printk("Failed to ioremap\n");
++              return -EIO;
++      }
++      mymtd = do_map_probe("cfi_probe", &lubbock_map);
++      if (!mymtd) {
++              iounmap((void *)lubbock_map.map_priv_1);
++              return -ENXIO;
++      }
++      mymtd->module = THIS_MODULE;
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      if (parsed_nr_parts == 0) {
++              int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++
++              if (ret > 0) {
++                      part_type = "RedBoot";
++                      parsed_nr_parts = ret;
++              }
++      }
++#endif
++
++      if (parsed_nr_parts > 0) {
++              parts = parsed_parts;
++              nb_parts = parsed_nr_parts;
++      } else {
++              parts = lubbock_partitions;
++              nb_parts = NB_OF(lubbock_partitions);
++      }
++      if (nb_parts) {
++              printk(KERN_NOTICE "Using %s partition definition\n", part_type);
++              add_mtd_partitions(mymtd, parts, nb_parts);
++      } else {
++              add_mtd_device(mymtd);
++      }
++      return 0;
++}
++
++static void __exit cleanup_lubbock(void)
++{
++      if (mymtd) {
++              del_mtd_partitions(mymtd);
++              map_destroy(mymtd);
++              if (parsed_parts)
++                      kfree(parsed_parts);
++      }
++      if (lubbock_map.map_priv_1)
++              iounmap((void *)lubbock_map.map_priv_1);
++      return 0;
++}
++
++module_init(init_lubbock);
++module_exit(cleanup_lubbock);
++
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/maps/pxa_cerf.c
+@@ -0,0 +1,172 @@
++/*
++ *  Map driver for the PXA Cerf.
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2 as
++ *  published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++
++#define WINDOW_ADDR   0
++#if defined (CONFIG_PXA_CERF_FLASH_64MB)
++#define WINDOW_SIZE   64*1024*1024
++#elif defined (CONFIG_PXA_CERF_FLASH_32MB)
++#define WINDOW_SIZE   32*1024*1024
++#elif defined (CONFIG_PXA_CERF_FLASH_16MB)
++#define WINDOW_SIZE   16*1024*1024
++#endif
++#define BUSWIDTH      4
++
++static __u8 pxa_cerf_read8(struct map_info *map, unsigned long ofs)
++{
++      return *(__u8 *)(map->map_priv_1 + ofs);
++}
++
++static __u16 pxa_cerf_read16(struct map_info *map, unsigned long ofs)
++{
++      return *(__u16 *)(map->map_priv_1 + ofs);
++}
++
++static __u32 pxa_cerf_read32(struct map_info *map, unsigned long ofs)
++{
++      return *(__u32 *)(map->map_priv_1 + ofs);
++}
++
++static void pxa_cerf_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 pxa_cerf_write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++      *(__u8 *)(map->map_priv_1 + adr) = d;
++}
++
++static void pxa_cerf_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++      *(__u16 *)(map->map_priv_1 + adr) = d;
++}
++
++static void pxa_cerf_write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++      *(__u32 *)(map->map_priv_1 + adr) = d;
++}
++
++static void pxa_cerf_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 pxa_cerf_map = {
++      name: "PXA Cerf Flash",
++      size: WINDOW_SIZE,
++      buswidth: BUSWIDTH,
++      read8:          pxa_cerf_read8,
++      read16:         pxa_cerf_read16,
++      read32:         pxa_cerf_read32,
++      copy_from:      pxa_cerf_copy_from,
++      write8:         pxa_cerf_write8,
++      write16:        pxa_cerf_write16,
++      write32:        pxa_cerf_write32,
++      copy_to:        pxa_cerf_copy_to
++};
++
++static struct mtd_partition pxa_cerf_partitions[] = {
++      {
++              name:           "Bootloader",
++              size:           0x00040000,
++              offset:         0,
++              mask_flags:     MTD_WRITEABLE  /* force read-only */
++      },{
++              name:           "Partition Tables",
++              size:           0x00080000,
++              offset:         0x00040000,
++      },{
++              name:           "Kernel",
++              size:           0x00100000,
++              offset:         0x000C0000,
++      },{
++              name:           "Filesystem",
++              size:           WINDOW_SIZE-0x001C0000, //MTDPART_SIZ_FULL,
++              offset:         0x001C0000
++      }
++};
++
++#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 int __init init_pxa_cerf(void)
++{
++      struct mtd_partition *parts;
++      int nb_parts = 0;
++      int parsed_nr_parts = 0;
++      char *part_type = "static";
++
++      printk("Probing PXA Cerf flash at physical address 0x%08x\n", WINDOW_ADDR);
++      pxa_cerf_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0);
++      if (!pxa_cerf_map.map_priv_1) {
++              printk("Failed to ioremap\n");
++              return -EIO;
++      }
++      mymtd = do_map_probe("cfi_probe", &pxa_cerf_map);
++      if (!mymtd) {
++              iounmap((void *)pxa_cerf_map.map_priv_1);
++              return -ENXIO;
++      }
++      mymtd->module = THIS_MODULE;
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      if (parsed_nr_parts == 0) {
++              int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++
++              if (ret > 0) {
++                      part_type = "RedBoot";
++                      parsed_nr_parts = ret;
++              }
++      }
++#endif
++
++      if (parsed_nr_parts > 0) {
++              parts = parsed_parts;
++              nb_parts = parsed_nr_parts;
++      } else {
++              parts = pxa_cerf_partitions;
++              nb_parts = NB_OF(pxa_cerf_partitions);
++      }
++      if (nb_parts) {
++              printk(KERN_NOTICE "Using %s partition definition\n", part_type);
++              add_mtd_partitions(mymtd, parts, nb_parts);
++      } else {
++              add_mtd_device(mymtd);
++      }
++      return 0;
++}
++
++static void __exit cleanup_pxa_cerf(void)
++{
++      if (mymtd) {
++              del_mtd_partitions(mymtd);
++              map_destroy(mymtd);
++              if (parsed_parts)
++                      kfree(parsed_parts);
++      }
++      if (pxa_cerf_map.map_priv_1)
++              iounmap((void *)pxa_cerf_map.map_priv_1);
++      return;
++}
++
++module_init(init_pxa_cerf);
++module_exit(cleanup_pxa_cerf);
++
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/maps/trizeps2.c
+@@ -0,0 +1,172 @@
++/*
++ * $Id:
++ *
++ * Map driver for the Trizeps-2 module.
++ *
++ * Author:    Luc De Cock
++ * Copyright: (C) 2003 Teradyne DS, Ltd.
++ * 
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++
++#define WINDOW_ADDR   0
++#define WINDOW_SIZE   16*1024*1024
++
++static __u8 trizeps2_read8(struct map_info *map, unsigned long ofs)
++{
++      return *(__u8 *)(map->map_priv_1 + ofs);
++}
++
++static __u16 trizeps2_read16(struct map_info *map, unsigned long ofs)
++{
++      return *(__u16 *)(map->map_priv_1 + ofs);
++}
++
++static __u32 trizeps2_read32(struct map_info *map, unsigned long ofs)
++{
++      return *(__u32 *)(map->map_priv_1 + ofs);
++}
++
++static void trizeps2_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 trizeps2_write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++      *(__u8 *)(map->map_priv_1 + adr) = d;
++}
++
++static void trizeps2_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++      *(__u16 *)(map->map_priv_1 + adr) = d;
++}
++
++static void trizeps2_write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++      *(__u32 *)(map->map_priv_1 + adr) = d;
++}
++
++static void trizeps2_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 trizeps2_map = {
++      name:           "Trizeps-2 flash",
++      size:           WINDOW_SIZE,
++      read8:          trizeps2_read8,
++      read16:         trizeps2_read16,
++      read32:         trizeps2_read32,
++      copy_from:      trizeps2_copy_from,
++      write8:         trizeps2_write8,
++      write16:        trizeps2_write16,
++      write32:        trizeps2_write32,
++      copy_to:        trizeps2_copy_to
++};
++
++static struct mtd_partition trizeps2_partitions[] = {
++      {
++              name:           "Bootloader",
++              size:           0x00040000,
++              offset:         0,
++              mask_flags:     MTD_WRITEABLE  /* force read-only */
++      },{
++              name:           "Bootloader (backup)",
++              size:           0x00040000,
++              offset:         0x00040000,
++              mask_flags:     MTD_WRITEABLE  /* force read-only */
++      },{
++              name:           "Kernel",
++              size:           0x000C0000,
++              offset:         0x00080000,
++      },{
++              name:           "Filesystem",
++              size:           MTDPART_SIZ_FULL,
++              offset:         0x00140000
++      }
++};
++
++#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 int __init init_trizeps2(void)
++{
++      struct mtd_partition *parts;
++      int nb_parts = 0;
++      int parsed_nr_parts = 0;
++      char *part_type = "static";
++
++      trizeps2_map.buswidth = (BOOT_DEF & 1) ? 2 : 4;
++      printk( "Probing Trizeps-2 flash at physical address 0x%08x (%d-bit buswidth)\n",
++              WINDOW_ADDR, trizeps2_map.buswidth * 8 );
++      trizeps2_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0);
++      if (!trizeps2_map.map_priv_1) {
++              printk("Failed to ioremap\n");
++              return -EIO;
++      }
++      mymtd = do_map_probe("cfi_probe", &trizeps2_map);
++      if (!mymtd) {
++              iounmap((void *)trizeps2_map.map_priv_1);
++              return -ENXIO;
++      }
++      mymtd->module = THIS_MODULE;
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      if (parsed_nr_parts == 0) {
++              int ret = parse_redboot_partitions(mymtd, &parsed_parts);
++
++              if (ret > 0) {
++                      part_type = "RedBoot";
++                      parsed_nr_parts = ret;
++              }
++      }
++#endif
++
++      if (parsed_nr_parts > 0) {
++              parts = parsed_parts;
++              nb_parts = parsed_nr_parts;
++      } else {
++              parts = trizeps2_partitions;
++              nb_parts = NB_OF(trizeps2_partitions);
++      }
++      if (nb_parts) {
++              printk(KERN_NOTICE "Using %s partition definition\n", part_type);
++              add_mtd_partitions(mymtd, parts, nb_parts);
++      } else {
++              add_mtd_device(mymtd);
++      }
++      return 0;
++}
++
++static void __exit cleanup_trizeps2(void)
++{
++      if (mymtd) {
++              del_mtd_partitions(mymtd);
++              map_destroy(mymtd);
++              if (parsed_parts)
++                      kfree(parsed_parts);
++      }
++      if (trizeps2_map.map_priv_1)
++              iounmap((void *)trizeps2_map.map_priv_1);
++      return 0;
++}
++
++module_init(init_trizeps2);
++module_exit(cleanup_trizeps2);
++
+--- linux-2.4.27/drivers/net/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/net/Config.in
+@@ -125,6 +125,7 @@
+       dep_tristate '    SMC Ultra support' CONFIG_ULTRA $CONFIG_ISA
+       dep_tristate '    SMC Ultra32 EISA support' CONFIG_ULTRA32 $CONFIG_EISA
+       dep_tristate '    SMC 9194 support' CONFIG_SMC9194 $CONFIG_ISA
++      tristate     '    SMC 91C9x/91C1xx support' CONFIG_SMC91X
+    fi
+    bool '  Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL
+    if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then
+--- linux-2.4.27/drivers/net/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/net/Makefile
+@@ -137,6 +137,7 @@
+ obj-$(CONFIG_SK_G16) += sk_g16.o
+ obj-$(CONFIG_HP100) += hp100.o
+ obj-$(CONFIG_SMC9194) += smc9194.o
++obj-$(CONFIG_SMC91X) += smc91x.o
+ obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
+ obj-$(CONFIG_ARM_ETHERH) += 8390.o
+ obj-$(CONFIG_WD80x3) += wd.o 8390.o
+--- linux-2.4.27/drivers/net/cirrus.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/net/cirrus.c
+@@ -67,6 +67,9 @@
+ #elif CONFIG_ARCH_CDB89712
+ #     define CIRRUS_DEFAULT_IO ETHER_BASE + 0x300
+ #     define CIRRUS_DEFAULT_IRQ IRQ_EINT3
++#elif CONFIG_ARCH_CSB226
++#     define CIRRUS_DEFAULT_IO 0xF8000000
++#     define CIRRUS_DEFAULT_IRQ IRQ_GPIO(14) 
+ #else
+ #     define CIRRUS_DEFAULT_IO        0
+ #     define CIRRUS_DEFAULT_IRQ       0
+--- linux-2.4.27/drivers/net/irda/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/net/irda/Config.in
+@@ -42,5 +42,7 @@
+ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+    dep_tristate 'SA1100 Internal IR' CONFIG_SA1100_FIR $CONFIG_IRDA $CONFIG_EXPERIMENTAL
+ fi
+-
++if [ "$CONFIG_ARCH_PXA" = "y" ]; then
++   dep_tristate 'Intel PXA2xx Internal IR' CONFIG_PXA_FIR $CONFIG_IRDA $CONFIG_EXPERIMENTAL        
++fi
+ endmenu
+--- linux-2.4.27/drivers/net/irda/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/net/irda/Makefile
+@@ -16,6 +16,7 @@
+ obj-$(CONFIG_USB_IRDA)                += irda-usb.o
+ obj-$(CONFIG_NSC_FIR)         += nsc-ircc.o
+ obj-$(CONFIG_WINBOND_FIR)     += w83977af_ir.o
++obj-$(CONFIG_PXA_FIR)         += pxa_ir.o
+ obj-$(CONFIG_SA1100_FIR)      += sa1100_ir.o
+ obj-$(CONFIG_TOSHIBA_OLD)     += toshoboe.o
+ obj-$(CONFIG_TOSHIBA_FIR)     += donauboe.o
+--- /dev/null
++++ linux-2.4.27/drivers/net/irda/pxa_ir.c
+@@ -0,0 +1,1545 @@
++/*
++ *  linux/drivers/net/irda/pxa_ir.c
++ *
++ *  Author:
++ *  Alexey Lugovskoy RTSoft. 
++ *    lugovskoy@rtsoft.msk.ru
++ *
++ *  Dmitrij Frasenyak RTSoft. 
++ *      sed@mipt.sw.ru
++ *
++ * 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.
++ *
++ *  Infra-red SIR and FIR driver for the PXA 210/250 embedded microprocessors
++ *  Based on linux/drivers/net/irda/sa1100_ir.c
++ *
++ */
++
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/netdevice.h>
++#include <linux/slab.h>
++#include <linux/rtnetlink.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++
++#include <linux/pm.h>
++
++#include <net/irda/irda.h>
++#include <net/irda/irmod.h>
++#include <net/irda/wrapper.h>
++#include <net/irda/irda_device.h>
++
++#include <asm/irq.h>
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/arch/lubbock.h>
++
++
++static int rx_count = 0;
++static int tx_count = 0;
++
++/*
++ * Our netdevice.  There is only ever one of these.
++ */
++ 
++static struct net_device *netdev;
++
++struct pxa250_irda {
++      
++      unsigned char           open;
++
++      int                     speed;
++      int                     newspeed;
++
++      struct sk_buff          *txskb;
++      struct sk_buff          *rxskb;
++      
++
++      /* => FIR */
++      unsigned int            fir_irq;
++      int                     txdma_ch;
++      int                     rxdma_ch;
++      dma_addr_t              txbuf_dma;
++      dma_addr_t              rxbuf_dma;
++      void*                   txbuf_dma_virt;
++      void*                   rxbuf_dma_virt;
++      /* <= FIR*/
++      struct net_device_stats stats;
++      struct irlap_cb         *irlap;
++      struct pm_dev           *pmdev;
++      struct qos_info         qos;
++
++      /* => SIR */
++      iobuff_t                tx_buff;
++      iobuff_t                rx_buff;
++      /* <= SIR */
++};
++
++#define IS_FIR(si)            ((si)->speed >= 4000000)
++
++#define HPSIR_MAX_RXLEN               2050
++#define HPSIR_MAX_TXLEN               2050
++#define TXBUFF_MAX_SIZE               HPSIR_MAX_TXLEN
++#define SET_SIR_MODE            STISR = STISR_RCVEIR | STISR_XMITIR | STISR_XMODE
++
++/*
++ * If you want to disable debug information
++ * please uncomment line bellow
++ */
++
++#define PXA_FIR_DUMP_ENABLE
++#undef PXA_FIR_DUMP_ENABLE     
++
++
++#define PXA_FIR_DEBUG_ENABLE
++#undef PXA_FIR_DEBUG_ENABLE              
++
++#define PXA_FIR_IRQ_DEBUG_ENABLE
++#undef PXA_FIR_IRQ_DEBUG_ENABLE               
++
++#ifdef PXA_FIR_DEBUG_ENABLE
++#define __ECHO_IN printk(KERN_ERR "%s: enter\n",__FUNCTION__);
++#define __ECHO_OUT printk(KERN_ERR "%s: exit\n",__FUNCTION__);
++#define DBG(args...) printk(KERN_ERR __FUNCTION__"():"args);
++#else
++#define __ECHO_IN
++#define __ECHO_OUT
++#define DBG(args...)
++#endif
++
++#ifdef PXA_FIR_IRQ_DEBUG_ENABLE
++#define DBG_IRQ(args...) printk(KERN_ERR __FUNCTION__"():"args);
++#else
++#define DBG_IRQ(args...)
++#endif
++
++
++static int pxa250_irda_set_speed(struct net_device *dev,int speed);
++static void pxa250_start_rx_dma(struct net_device *dev);
++
++
++
++/**************************************************************************
++ *                    Misc FIR/SIR functions                            *
++ **************************************************************************/
++/*
++ * Allocate the receive buffer, unless it is already allocated.
++ */
++
++static int pxa250_irda_rx_alloc(struct pxa250_irda *si)
++{
++   __ECHO_IN;
++   
++   if (si->rxskb)
++      return 0;
++
++   si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
++
++   if (!si->rxskb) {
++      printk(KERN_ERR "pxa250_ir: out of memory for RX SKB\n");
++      return -ENOMEM;
++   }
++
++   /*
++    * Align any IP headers that may be contained
++    * within the frame.
++    */
++   skb_reserve(si->rxskb, 1);
++
++   __ECHO_OUT;
++
++   return 0;
++}
++
++
++
++/**************************************************************************
++ *                    FIR                                               *
++ **************************************************************************/
++
++
++
++
++static inline void pxa250_dma_stop(int ch)
++{
++   __ECHO_IN;
++
++   DCSR(ch) &= ~DCSR_RUN;
++
++   __ECHO_OUT;
++   
++}
++
++
++static void pxa250_ficp_rx_start(void)
++{
++   ICCR0 = 0;
++   ICCR2 =  1 << 2 | 0 << 3 ; 
++   ICCR0 = ICCR0_ITR ;
++   ICCR0 |= ICCR0_RIE |  ICCR0_RXE ; 
++}
++
++/*
++ * Change Alternative Function encoding
++ * Enable ICP unit
++ * Disabe STUART unit
++ * Enable IRQ unit clock;
++ * Configure direction of GPIO used by ICP
++ */
++
++
++static void pxa250_do_fir_GPIO_config(void)
++{
++   /*
++    * Modify GPIO 46 and 47 Alternate Function 
++    */
++
++   __ECHO_IN;
++
++   /*Switch AF*/
++   set_GPIO_mode (GPIO46_ICPRXD_MD);
++   set_GPIO_mode (GPIO47_ICPTXD_MD);
++
++   if (machine_is_lubbock())
++      LUB_MISC_WR |= 1 << 4;
++
++   /*init clock*/
++   CKEN |= CKEN13_FICP;
++
++   __ECHO_OUT;
++}
++
++/*
++ * Low level hardware configuration and startup.
++ */
++
++static int pxa250_fir_irda_startup(struct pxa250_irda *si)
++{
++
++      __ECHO_IN;
++
++      /*
++       * Disable STUART
++       */
++
++      STIER &= ~IER_UUE;
++
++      /*Disable STUART FIFO */
++      STFCR = 0;
++
++      /*
++       * Do low level configuration for HW AF and clock
++       */
++      pxa250_do_fir_GPIO_config();
++
++      __ECHO_OUT;
++      return 0;
++}
++
++
++/*
++ * Aieeeeee .. we should never get here :(
++ */
++static void pxa250_irda_rxdma_irq(int ch,void *id, struct pt_regs *regs)
++{
++   struct net_device *dev=id;
++   struct pxa250_irda *si=dev->priv;
++   u_int dcsr;
++
++
++   __ECHO_IN;
++
++   /* 
++    * Make sure that irq is our.
++    */
++
++   if ( ch != si->rxdma_ch )
++      /*just*/ return;
++
++   /*
++    * Check status 
++    */
++   dcsr = DCSR(ch);
++
++   DBG("DCSR=%x\n",dcsr);
++
++   if (dcsr &  DCSR_STOPSTATE )
++   {
++      DBG_IRQ("Chanel %d in stop state\n",ch);
++   }
++
++   if (dcsr &  DCSR_BUSERR )
++   {
++      /*
++       * BUS Error we must restart reception
++       */
++      
++      DBG("PXA IrDA: bus error interrupt on channel %d\n", ch);
++      DCSR(ch) |= DCSR_BUSERR;
++   }
++
++   if (dcsr &  DCSR_ENDINTR )
++   {
++      DBG("PXA IrDA: Normal end of dma channel %d - packet to big\n", ch);
++      DCSR(ch) |= DCSR_ENDINTR;
++   }
++
++   /* no mater what restart rx*/
++   pxa250_start_rx_dma(dev);
++   
++   return ;
++   
++}
++
++
++static void pxa250_irda_txdma_irq(int ch, void *id , struct pt_regs *regs)
++{
++   struct net_device *dev=id;
++   struct pxa250_irda *si=dev->priv;
++   struct sk_buff *skb = si->txskb;
++   u_int dcsr;
++
++
++   __ECHO_IN;
++   DBG_IRQ("transmit\n"); 
++   
++     
++   /* 
++    * Make sure that irq is our.
++    */
++
++   if ( ch != si->txdma_ch )
++      /*just*/ return;
++
++
++   /*
++    * Check status 
++    */
++   dcsr = DCSR(ch);
++
++   DBG("DCSR=%x",dcsr);
++
++   if (dcsr &  DCSR_STOPSTATE )
++   {
++      DBG("Chanel %d in stop state\n",ch);
++   }
++
++   if (dcsr &  DCSR_BUSERR )
++   {
++      DBG("PXA IrDA: bus error interrupt on channel %d\n", ch);
++      DCSR(ch) |= DCSR_BUSERR;
++      si->txskb = NULL;
++   }
++
++   if (dcsr &  DCSR_ENDINTR )
++   {
++      DBG("PXA IrDA: Normal end of dma channel %d\n", ch);
++      DCSR(ch) |= DCSR_ENDINTR;
++      si->txskb = NULL;
++   }
++
++   /*
++    * Account and free the packet.
++    */
++   if (skb)
++   {
++      si->stats.tx_packets ++;
++      si->stats.tx_bytes += skb->len;
++      dev_kfree_skb_irq(skb);
++   }
++
++      /*Disable transceiver and enable receiver*/
++
++      if (si->newspeed) {
++         pxa250_irda_set_speed(dev, si->newspeed);
++         si->newspeed = 0;
++      }
++
++      while (ICSR1 & ICSR1_TBY)
++         udelay(1);
++      
++      ICCR0 &= ~ICCR0_TXE;
++
++      
++      enable_irq(si->fir_irq);
++
++      ICCR0 |= ICCR0_RXE; 
++
++      /*
++       * Make sure that the TX queue is available for sending
++       * (for retries).  TX has priority over RX at all times.
++       */
++      netif_wake_queue(dev);
++      
++      __ECHO_OUT;
++}
++
++
++static void pxa250_start_rx_dma(struct net_device *dev)
++{
++   struct pxa250_irda *si = dev->priv;
++   int ch=si->rxdma_ch;
++
++   if (!si->rxskb) {
++      DBG("rx buffer went missing\n");
++/*        return; */
++   }
++
++   DCSR(ch)=0;
++   DCSR(ch)=DCSR_NODESC;
++   DSADR(ch) = __PREG(ICDR);
++   DTADR(ch) = si->rxbuf_dma; /* phisical address */;
++
++   /* We should never do END_IRQ.  !!!*/
++   DCMD(ch) = DCMD_ENDIRQEN| DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_BURST8 | DCMD_WIDTH1 | HPSIR_MAX_RXLEN;
++
++   /*
++    * All right information will be available as soon as we set RXE flag
++    */
++   
++   DCSR(ch) = DCSR_ENDINTR | DCSR_BUSERR;
++   DCSR(ch) = DCSR_RUN | DCSR_NODESC ;
++
++}
++
++
++
++
++static int pxa250_get_rx_len(struct pxa250_irda *si)
++{
++   /*
++    * DMA have to be stoped here
++    */
++
++   if ( ! (DCSR(si->rxdma_ch) & DCSR_STOPSTATE) )
++      printk("warning dma have to be stoped befor counting len\n");
++   
++   return ( HPSIR_MAX_RXLEN - ( DCMD(si->rxdma_ch) & DCMD_LENGTH ) );
++   
++}
++
++static void pxa250_irda_fir_error(struct net_device *dev)
++{
++   struct pxa250_irda *si = dev->priv;
++   struct sk_buff *skb = si->rxskb;
++   int len;
++   int stat,data;
++
++   __ECHO_IN;
++   
++      if (!skb)
++      {
++       printk("pxa250 fir_error: SKB is NULL!\n");
++       return;
++      }
++
++      /*
++       * Get the current data position.
++       */
++
++      len=pxa250_get_rx_len(si);
++      DBG("RXLEN=%d\n",len);
++      memcpy(skb->data, si->rxbuf_dma_virt, len);
++
++      do {
++       /*
++        * Read Status, and then Data.
++        */
++         stat = ICSR1;
++         rmb();
++         data = ICDR;
++         if (stat & (ICSR1_CRE | ICSR1_ROR)) {
++            si->stats.rx_errors++;
++            if (stat & ICSR1_CRE)
++               si->stats.rx_crc_errors++;
++            if (stat & ICSR1_ROR)
++               si->stats.rx_frame_errors++;
++         } else
++            skb->data[len++] = data;
++
++              /*
++               * If we hit the end of frame, there's
++               * no point in continuing.
++               */
++         if (stat & ICSR1_EOF)
++            break;
++      } while (ICSR0 & ICSR0_EIF);
++
++      if (stat & ICSR1_EOF) {
++         si->rxskb = NULL;
++
++         skb_put(skb, len);
++         skb->dev = dev;
++         skb->mac.raw = skb->data;
++         skb->protocol = htons(ETH_P_IRDA);
++         si->stats.rx_packets++;
++         si->stats.rx_bytes += len;
++
++         /*
++          * Before we pass the buffer up, allocate a new one.
++          */
++
++         si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC);
++
++         if (!si->rxskb) {
++            printk(KERN_ERR "pxa250_ir: out of memory for RX SKB\n");
++            return;
++         }
++
++         /*
++          * Align any IP headers that may be contained
++          * within the frame.
++          */
++         skb_reserve(si->rxskb, 1);
++
++         netif_rx(skb);
++      }
++}
++
++/*
++ * FIR format interrupt service routine.  We only have to
++ * handle RX events; transmit events go via the TX DMA irq handler.
++ *
++ * No matter what, we disable RX, process, and then restart RX.
++ */
++
++static void pxa250_irda_fir_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++   struct net_device *dev = dev_id;
++   struct pxa250_irda *si = dev->priv;
++   int status;
++   
++   /*
++    * Stop RX
++    */
++
++   __ECHO_IN;
++
++   pxa250_dma_stop(si->rxdma_ch);
++
++   
++   /*
++    * Framing error - we throw away the packet completely.
++    * Clearing RXE flushes the error conditions and data
++    * from the fifo.
++    */
++   status=ICSR0;
++   
++   if (status & (ICSR0_FRE | ICSR0_RAB)) {
++      DBG_IRQ("Framing error or RAB\n"); 
++      
++      si->stats.rx_errors++;
++
++      if (ICSR0 & ICSR0_FRE)
++       si->stats.rx_frame_errors++;
++
++      /* Clear RX fifo
++       * DMA will be cleared when we restart RX
++       * Should we check RNE after that? 
++       */
++
++      ICCR0 &= ~ICCR0_RXE;
++      
++      /*
++       * Clear selected status bits now, so we
++       * don't miss them next time around.
++       */
++      ICSR0 = status & (ICSR0_FRE | ICSR0_RAB);
++   }
++
++   
++   /*
++    * Deal with any receive errors.  The any of the lowest
++    * 8 bytes in the FIFO may contain an error.  We must read
++    * them one by one.  The "error" could even be the end of
++    * packet!
++    */
++   if (ICSR0 & ICSR0_EIF)
++      pxa250_irda_fir_error(dev);
++
++   /*
++    * No matter what happens, we must restart reception.
++    */
++
++   ICCR0 = 0;
++   pxa250_start_rx_dma(dev);
++   pxa250_ficp_rx_start();
++   __ECHO_OUT;
++}
++
++
++
++
++
++/**************************************************************************
++ *                    SIR                                               *
++ **************************************************************************/
++/*
++ * HP-SIR format interrupt service routines.
++ */
++static void pxa250_sir_transmit(struct net_device *dev)
++{
++   struct pxa250_irda *si = dev->priv;
++   
++   if (si->tx_buff.len) 
++   {
++      /* Disable receiver and  enable transmiter*/
++
++      
++      
++              STISR &= ~STISR_RCVEIR; 
++//            STISR |= STISR_XMITIR;
++
++                              
++              
++                disable_irq(dev->irq);
++              
++              do 
++              {
++
++                 if (STLSR & LSR_TDRQ)
++                 {
++                    STTHR = *si->tx_buff.data++;
++                    si->tx_buff.len -= 1;
++ 
++                    tx_count++;
++                 }
++                 
++                                       
++              } while (si->tx_buff.len);
++
++                              
++              if (si->tx_buff.len == 0) 
++              {
++                 
++                 
++                      si->stats.tx_packets++;
++                      si->stats.tx_bytes += si->tx_buff.data -
++                                            si->tx_buff.head;
++
++                      /*
++                       * We need to ensure that the transmitter has
++                       * finished.
++                       */
++                      
++                      do
++                      {
++                         udelay(1);
++                         
++                      }                               
++                      while ( ! (STLSR & LSR_TEMT) );
++                       
++                                              
++                      /*
++
++                       * Ok, we've finished transmitting.  Now enable
++                       * the receiver.  Sometimes we get a receive IRQ
++                       * immediately after a transmit...
++                       */
++
++                      if (si->newspeed)
++                      {
++                              pxa250_irda_set_speed(dev, si->newspeed);
++                              si->newspeed = 0;
++                      }
++
++                      /* I'm hungry! */
++                      netif_wake_queue(dev);
++              }
++              
++              enable_irq (dev->irq);
++                STIER = (IER_RAVIE | IER_UUE | IER_RTIOE);
++
++              STISR |= STISR_RCVEIR;
++//            STISR &= ~STISR_XMITIR;
++   }
++}
++
++static void pxa250_irda_hpsir_irq(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++
++      /*
++       * Deal with any receive errors first.  The bytes in error may be
++       * the only bytes in the receive FIFO, so we do this first.
++       */
++      __ECHO_IN; 
++      
++      while (STLSR & LSR_FIFOE)
++      {
++              int stat, data;
++
++              stat = STLSR; 
++              data = STRBR;
++               
++              
++                if (stat & (LSR_FE | LSR_OE | LSR_PE)) 
++              
++              {
++                      si->stats.rx_errors++;
++                      if (stat & LSR_FE) 
++                              si->stats.rx_frame_errors++;
++                      if (stat & LSR_OE) 
++                              si->stats.rx_fifo_errors++;
++                      
++              } else
++              {
++                 rx_count++;
++                 async_unwrap_char(dev, &si->stats, &si->rx_buff, data);
++                }
++              
++      }
++
++      /*
++       * We must clear certain bits.
++       */
++       
++      if (STLSR & (LSR_DR)) 
++      {
++              /*
++               * Fifo contains at least 1 character.
++               */
++              do
++              {
++                 int data;
++                 
++                 data = STRBR;
++                 
++                 async_unwrap_char(dev, &si->stats, &si->rx_buff,
++                                        data); /* was Ser2UTDR); Clo */
++                 rx_count++;
++                 
++              } while (STLSR & LSR_DR); 
++              
++              dev->last_rx = jiffies;
++      }
++
++      __ECHO_OUT; 
++}
++
++static void pxa250_sir_irda_shutdown(struct pxa250_irda *si)
++{
++
++   STIER = 0;
++   STFCR = 0;
++   STISR = 0;
++   CKEN &= ~CKEN5_STUART; 
++}
++
++
++/************************************************************************************/
++
++/*Low level init/uninstall function PM control and IrDA protocol stack registration */
++
++/*
++ * Set the IrDA communications speed.
++ * Interrupt have to be disabled here.
++ */
++
++static int pxa250_irda_startup(struct net_device *dev)
++{
++   
++
++   __ECHO_IN;
++
++   /*
++    * Ensure that the ports for this device are setup correctly.
++    */
++
++
++   set_GPIO_mode (GPIO46_STRXD_MD);
++   set_GPIO_mode (GPIO47_STTXD_MD);
++
++   STMCR = MCR_OUT2;
++   STLCR = LCR_WLS1 | LCR_WLS0;
++
++   SET_SIR_MODE;
++   CKEN |= CKEN5_STUART;
++   /* enable irq from stuart */
++   ICMR |= ( 1 << 20 );
++      
++   /*reset FIFO*/
++              
++/*    STFCR = FCR_TRFIFOE |  FCR_RESETTF | FCR_RESETRF;// | FCR_ITL_16;
++
++      STIER = IER_UUE | IER_RAVIE | IER_RTOIE;
++*/    
++   __ECHO_OUT;
++
++   return 0;
++      
++}
++
++
++#ifdef CONFIG_PM
++/*
++ * Suspend the IrDA interface.
++ */
++
++static int pxa250_irda_shutdown(struct pxa250_irda *si)
++{
++
++   pxa250_sir_irda_shutdown(si);
++   return 0;
++   
++}
++
++
++static int pxa250_irda_suspend(struct net_device *dev, int state)
++{
++      struct pxa250_irda *si = dev->priv;
++
++      if (si && si->open) {
++         /*
++          * Stop the transmit queue
++          */
++         if (IS_FIR(si))
++            return -1;
++
++         netif_stop_queue(dev);
++         disable_irq(dev->irq);
++         disable_irq(si->fir_irq);
++         pxa250_sir_irda_shutdown(si);
++      }
++
++      return 0;
++}
++
++/*
++ * Resume the IrDA interface.
++ */
++
++static int pxa250_irda_resume(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++
++      __ECHO_IN;
++      
++      if (si && si->open) {
++              /*
++               * If we missed a speed change, initialise at the new speed
++               * directly.  It is debatable whether this is actually
++               * required, but in the interests of continuing from where
++               * we left off it is desireable.  The converse argument is
++               * that we should re-negotiate at 9600 baud again.
++               */
++              if (si->newspeed) {
++                      si->speed = si->newspeed;
++                      si->newspeed = 0;
++              }
++
++              pxa250_irda_startup(dev);
++              enable_irq(dev->irq);
++
++              /*
++               * This automatically wakes up the queue
++               */
++              netif_wake_queue(dev);
++              pxa250_irda_set_speed(dev,si->speed = 9600);
++              
++      }
++
++      __ECHO_OUT;
++      return 0;
++}
++
++static int pxa250_irda_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      int ret;
++
++      
++      if (!dev->data)
++              return -EINVAL;
++
++
++      switch (rqst) {
++      case PM_SUSPEND:
++              ret = pxa250_irda_suspend((struct net_device *)dev->data,
++                                        (int)data);
++              break;
++
++      case PM_RESUME:
++              ret = pxa250_irda_resume((struct net_device *)dev->data);
++              break;
++
++      default:
++
++         ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++#endif
++
++
++
++
++static void pxa250_irda_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct net_device *dev = dev_id;
++      
++      pxa250_irda_hpsir_irq(dev);
++      
++}
++
++
++static int pxa250_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++      int speed = irda_get_next_speed(skb);
++      int mtt;
++      
++      __ECHO_IN; 
++
++      /*
++       * Does this packet contain a request to change the interface
++       * speed?  If so, remember it until we complete the transmission
++       * of this frame.
++       */
++      if (speed != si->speed && speed != -1)
++              si->newspeed = speed;
++
++      /*
++       * If this is an empty frame, we can bypass a lot.
++       */
++      if (skb->len == 0) {
++              if (si->newspeed) {
++                      si->newspeed = 0;
++                      pxa250_irda_set_speed(dev, speed);
++              }
++              dev_kfree_skb(skb);
++              return 0;
++      }
++
++
++      DBG("stop queue\n"); 
++      netif_stop_queue(dev);
++
++      if(!IS_FIR(si))
++      {
++         
++         si->tx_buff.data = si->tx_buff.head;
++         si->tx_buff.len  = async_wrap_skb(skb, si->tx_buff.data,
++                                                si->tx_buff.truesize);
++
++        
++         pxa250_sir_transmit(dev);
++
++      
++      
++         dev_kfree_skb(skb);
++
++         dev->trans_start = jiffies;
++
++         return 0;
++      }
++      else /* FIR */
++      {
++         DBG("Enter FIR transmit\n");
++         /*
++          * We must not be transmitting...
++          */
++         if (si->txskb)
++            BUG();
++
++                 disable_irq(si->fir_irq); 
++         
++         netif_stop_queue(dev);
++         DBG("queue stoped\n");
++         si->txskb = skb;
++
++         /* we could not just map so we'll need some triks */
++         /* skb->data may be not DMA capable -Sed- */
++
++
++         if (skb->len > TXBUFF_MAX_SIZE)
++         {
++            printk (KERN_ERR "skb data too large\n");
++            printk (KERN_ERR "len=%d",skb->len);
++            BUG();
++         }
++              
++
++         DBG("gonna copy %d bytes to txbuf\n",skb->len);
++
++         memcpy (si->txbuf_dma_virt, skb->data , skb->len);
++         
++         /* Actual sending ;must not be receiving !!! */
++         /* Write data and source address */
++
++         DBG("ICSR1 & RNE =%d\n",(ICSR1 & ICSR1_RNE) ? 1 : 0 );
++
++         /*Disable receiver and enable transifer */
++         ICCR0 &= ~ICCR0_RXE;      
++         
++         if (ICSR1 & ICSR1_TBY)
++            BUG();
++
++         ICCR0 |= ICCR0_TXE;  
++              
++         DBG("FICP status %x\n",ICSR0);
++
++         if (0){
++            int i;
++                 
++            DBG("sending packet\n");
++            for (i=0;i<skb->len;i++)
++               (i % 64) ? printk ("%2x ",skb->data[i]) : printk ("%2x \n",skb->data[i]) ;
++            DBG(" done\n");
++   
++         }
++         /*
++          * If we have a mean turn-around time, impose the specified
++          * specified delay.  We could shorten this by timing from
++          * the point we received the packet.
++          */
++         
++         mtt = irda_get_mtt(skb); 
++         if(mtt)    
++            udelay(mtt);    
++         
++         DCSR(si->txdma_ch)=0;
++         DCSR(si->txdma_ch)=DCSR_NODESC;
++         DSADR(si->txdma_ch) = si->txbuf_dma; /* phisic address */
++         DTADR(si->txdma_ch) = __PREG(ICDR);
++              
++         DCMD(si->txdma_ch) = DCMD_ENDIRQEN| DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_BURST8 | DCMD_WIDTH1 | skb->len;
++
++         DCSR(si->txdma_ch) = DCSR_ENDINTR | DCSR_BUSERR;
++         DCSR(si->txdma_ch) = DCSR_RUN | DCSR_NODESC ;
++
++         DBG("FICP status %x\n",ICSR0);
++
++         return 0;
++      }
++      
++}
++
++static int
++pxa250_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
++{
++      struct if_irda_req *rq = (struct if_irda_req *)ifreq;
++      struct pxa250_irda *si = dev->priv;
++      int ret = -EOPNOTSUPP;
++
++      __ECHO_IN;
++      
++      switch (cmd) {
++      case SIOCSBANDWIDTH:
++              if (capable(CAP_NET_ADMIN)) {
++                      /*
++                       * We are unable to set the speed if the
++                       * device is not running.
++                       */
++                      if (si->open) {
++                              ret = pxa250_irda_set_speed(dev,
++                                              rq->ifr_baudrate);
++                      } else {
++                              printk("pxa250_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
++                              ret = 0;
++                      }
++              }
++              break;
++
++      case SIOCSMEDIABUSY:
++              ret = -EPERM;
++              if (capable(CAP_NET_ADMIN)) {
++                      irda_device_set_media_busy(dev, TRUE);
++                      ret = 0;
++              }
++              break;
++
++      case SIOCGRECEIVING:
++              rq->ifr_receiving = IS_FIR(si) ? 0
++                                      : si->rx_buff.state != OUTSIDE_FRAME;
++              break;
++
++      default:
++              break;
++      }
++
++      __ECHO_OUT;
++
++      return ret;
++}
++
++static struct net_device_stats *pxa250_irda_stats(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++      return &si->stats;
++}
++
++static int pxa250_irda_start(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++      int err;
++      unsigned int flags;
++      
++
++      MOD_INC_USE_COUNT;
++
++      __ECHO_IN;
++      si->speed = 9600;
++
++      local_irq_save(flags);
++      
++      err = request_irq(si->fir_irq, pxa250_irda_fir_irq, 0,  dev->name, dev);
++      if (err)
++              goto err_fir_irq;
++
++      err = request_irq(dev->irq, pxa250_irda_irq, 0, dev->name, dev);
++      if (err)
++              goto err_irq;
++
++      /*
++       * The interrupt must remain disabled for now.
++       */
++      
++      disable_irq(dev->irq);
++      disable_irq(si->fir_irq);
++
++      local_irq_restore(flags);
++
++
++      /* Allocate DMA channel for receiver (not used) */
++      err = pxa_request_dma("IrDA receive", DMA_PRIO_LOW, pxa250_irda_rxdma_irq, dev);
++      if (err < 0 )
++         goto err_rx_dma;
++      si->rxdma_ch=err;
++
++      DRCMRRXICDR = DRCMR_MAPVLD | si->rxdma_ch;
++      
++
++      /* Allocate DMA channel for transmit */
++      err = pxa_request_dma("IrDA transmit", DMA_PRIO_LOW, pxa250_irda_txdma_irq , dev);
++      if (err < 0 )
++         goto err_tx_dma;
++
++      si->txdma_ch=err;
++
++      /*
++       * Make sure that ICP will be able 
++       * to assert the transmit dma request bit
++       * through the peripherals request bus (PREQ)
++       */
++      
++      DRCMRTXICDR = DRCMR_MAPVLD | si->txdma_ch;
++
++      DBG("rx(not used) channel=%d tx channel=%d\n",si->rxdma_ch,si->txdma_ch);
++      
++      /* 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);
++      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); 
++      if (! si->txbuf_dma_virt )
++              goto err_txbuf_dma;
++
++      /* Alocate skb for receiver */
++      err=pxa250_irda_rx_alloc(si);
++      if (err)
++         goto err_rx_alloc;
++      
++      /*
++       * Setup the serial port for the specified config.
++       */
++      err = pxa250_irda_startup(dev);
++      if (err)
++              goto err_startup;
++
++      pxa250_irda_set_speed(dev,si->speed = 9600);
++
++
++      /*
++       * Open a new IrLAP layer instance.
++       */
++      si->irlap = irlap_open(dev, &si->qos, "pxa250");
++      err = -ENOMEM;
++      if (!si->irlap)
++              goto err_irlap;
++
++      /*
++       * Now enable the interrupt and start the queue
++       */
++      si->open = 1;
++      enable_irq(dev->irq);
++      netif_start_queue(dev);
++      return 0;
++
++err_irlap:
++      si->open = 0;
++      pxa250_sir_irda_shutdown(si);
++err_startup:
++      dev_kfree_skb(si->rxskb);
++err_rx_alloc: 
++      consistent_free (si->txbuf_dma_virt,HPSIR_MAX_TXLEN,si->txbuf_dma);
++err_txbuf_dma:
++      consistent_free (si->rxbuf_dma_virt,HPSIR_MAX_RXLEN,si->rxbuf_dma);
++err_rxbuf_dma:
++      pxa_free_dma(si->txdma_ch);
++err_tx_dma:
++      pxa_free_dma(si->rxdma_ch);
++err_rx_dma:
++      free_irq(dev->irq, dev);
++err_irq:
++      free_irq(si->fir_irq, dev);
++err_fir_irq:  
++      MOD_DEC_USE_COUNT;
++      return err;
++}
++
++static int pxa250_irda_stop(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++      
++      printk(KERN_ERR "Irda stop... RX = %d TX = %d\n",rx_count,tx_count);
++
++      disable_irq(dev->irq);
++      disable_irq(si->fir_irq); 
++/*    pxa250_irda_shutdown(si); */
++
++      /*
++       * If we have been doing DMA receive, make sure we
++       * tidy that up cleanly.
++       */
++      if (si->rxskb) {
++              dev_kfree_skb(si->rxskb);
++              si->rxskb = NULL;
++      }
++
++      /* Stop IrLAP */
++      if (si->irlap) {
++              irlap_close(si->irlap);
++              si->irlap = NULL;
++      }
++
++      consistent_free (si->txbuf_dma_virt,HPSIR_MAX_TXLEN,si->txbuf_dma);
++      consistent_free (si->rxbuf_dma_virt,HPSIR_MAX_RXLEN,si->rxbuf_dma);
++      pxa_free_dma(si->txdma_ch);
++      pxa_free_dma(si->rxdma_ch);
++
++      netif_stop_queue(dev);
++      si->open = 0;
++
++      /*
++       * Free resources
++       */
++      free_irq(dev->irq, dev);
++      free_irq(si->fir_irq, dev);
++
++
++      MOD_DEC_USE_COUNT;
++
++      return 0;
++}
++
++static int pxa250_irda_init_iobuf(iobuff_t *io, int size)
++{
++      io->head = kmalloc(size, GFP_KERNEL | GFP_DMA);
++      if (io->head != NULL) {
++              io->truesize = size;
++              io->in_frame = FALSE;
++              io->state    = OUTSIDE_FRAME;
++              io->data     = io->head;
++      }
++      return io->head ? 0 : -ENOMEM;
++}
++
++
++
++
++static int pxa250_stop_fir(struct net_device *dev)
++{
++   struct pxa250_irda *si = dev->priv;
++   unsigned int flag;
++
++   save_flags(flag);
++   cli();
++   
++   pxa250_dma_stop(si->txdma_ch);
++   pxa250_dma_stop(si->rxdma_ch);
++
++   if (si->txskb)
++      dev_kfree_skb_irq(si->txskb);
++
++   ICCR0 &= ~(ICCR0_RXE | ICCR0_TXE );
++   disable_irq(si->fir_irq);
++   CKEN &= ~CKEN13_FICP;
++
++   restore_flags(flag);
++
++   return 0;
++}
++
++
++
++static int pxa250_irda_set_speed(struct net_device *dev, int speed)
++{
++   struct pxa250_irda *si = dev->priv;
++   int brd, ret = -EINVAL;
++   static int last_fir_speed=0;
++
++   __ECHO_IN;
++   
++
++
++   switch (speed) {
++      case 9600:      case 19200:     case 38400:
++      case 57600:     case 115200:
++         
++       /* Baud rate fixed - Clo */
++
++       /*
++        * FIXME
++        */
++       if (last_fir_speed) 
++       {
++
++          pxa250_stop_fir(dev);
++          set_GPIO_mode (GPIO46_STRXD_MD);
++          set_GPIO_mode (GPIO47_STTXD_MD);
++   
++          enable_irq(dev->irq);
++          netif_wake_queue(dev);
++          last_fir_speed=0;
++       }
++       
++
++       LUB_MISC_WR &= ~(1 << 4);
++
++       brd = 14745600 / (16 * speed); 
++
++       STLCR |= LCR_DLAB;
++
++       STDLH = brd >> 8; /* Clo: set Divisor Latch High */
++       STDLL = brd & 0xFF; /* Clo: set Devisor Latch Low */
++              
++       STLCR &= ~LCR_DLAB; /* Clo: clear DLAB bit */ 
++
++       STMCR = MCR_OUT2;
++
++       CKEN |= CKEN5_STUART;
++
++       ICMR |= ( 1 << 20 );
++              
++       STLCR = LCR_WLS1 | LCR_WLS0;
++
++       SET_SIR_MODE;
++              
++       STFCR = FCR_TRFIFOE |  FCR_RESETTF | FCR_RESETRF | FCR_ITL_1 ;// | FCR_ITL_16;
++
++       STIER = IER_UUE | IER_RAVIE | IER_RTIOE;
++
++       si->speed = speed;
++
++       ret = 0;
++       break;
++
++      case 4000000:
++
++       if (last_fir_speed)
++          goto speed_out;
++       
++       disable_irq(dev->irq);
++
++       pxa250_sir_irda_shutdown(si);
++       pxa250_fir_irda_startup(si);
++       pxa250_irda_rx_alloc(si);
++       ICCR0=0;
++       pxa250_start_rx_dma(dev);
++       pxa250_ficp_rx_start();
++       
++       enable_irq(si->fir_irq);
++       DBG("enable FIR \n");
++       si->speed = speed;
++
++       netif_wake_queue(dev);
++       last_fir_speed=1;
++speed_out:
++       
++       ret=0;
++       
++       break;
++
++      default:
++       break;
++   }
++   __ECHO_OUT;
++
++   return ret;
++}
++
++
++static int pxa250_irda_net_init(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++      unsigned int baudrate_mask;
++      int err = -ENOMEM;
++
++      si = kmalloc(sizeof(struct pxa250_irda), GFP_KERNEL);
++      if (!si)
++              goto out;
++
++      memset(si, 0, sizeof(*si));
++
++      /*
++       * Initialise the HP-SIR buffers
++       */
++
++      err = pxa250_irda_init_iobuf(&si->rx_buff, 14384);
++      if (err)
++              goto out;
++      err = pxa250_irda_init_iobuf(&si->tx_buff, 4000);
++      if (err)
++              goto out_free_rx;
++
++      si->fir_irq             = IRQ_ICP;
++      dev->priv = si;
++      dev->hard_start_xmit    = pxa250_irda_hard_xmit;
++      dev->open               = pxa250_irda_start;
++      dev->stop               = pxa250_irda_stop;
++      dev->do_ioctl           = pxa250_irda_ioctl;
++      dev->get_stats          = pxa250_irda_stats;
++
++      irda_device_setup(dev);
++      irda_init_max_qos_capabilies(&si->qos);
++
++      /*
++       * We support original IRDA up to 115k2. (we don't currently
++       * support 4Mbps).  Min Turn Time set to 1ms or greater.
++       */
++      baudrate_mask = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
++      baudrate_mask |= IR_4000000 << 8; 
++      si->qos.baud_rate.bits &= baudrate_mask;
++      si->qos.min_turn_time.bits = 7;
++
++      irda_qos_bits_to_value(&si->qos);
++
++#ifdef CONFIG_PM
++      /*
++       * Power-Management is optional.
++       */
++      si->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, pxa250_irda_pmproc);
++      if (si->pmdev)
++              si->pmdev->data = dev;
++#endif
++
++      return 0;
++
++      kfree(si->tx_buff.head);
++out_free_rx:
++      kfree(si->rx_buff.head);
++out:
++      kfree(si);
++
++      return err;
++}
++
++/*
++ * Remove all traces of this driver module from the kernel, so we can't be
++ * called.  Note that the device has already been stopped, so we don't have
++ * to worry about interrupts or dma.
++ */
++static void pxa250_irda_net_uninit(struct net_device *dev)
++{
++      struct pxa250_irda *si = dev->priv;
++
++      dev->hard_start_xmit    = NULL;
++      dev->open               = NULL;
++      dev->stop               = NULL;
++      dev->do_ioctl           = NULL;
++      dev->get_stats          = NULL;
++      dev->priv               = NULL;
++
++      pm_unregister(si->pmdev);
++
++      kfree(si->tx_buff.head);
++      kfree(si->rx_buff.head);
++      kfree(si);
++}
++
++static int __init pxa250_irda_init(void)
++{
++      struct net_device *dev;
++      int err;
++
++      /* STUART */
++      err = request_mem_region(__PREG(STRBR), 0x24, "IrDA") ? 0 : -EBUSY;
++      if (err)
++              goto err_mem_1;
++
++      /* FIR */
++      err = request_mem_region(__PREG(ICCR0), 0x1c, "IrDA") ? 0 : -EBUSY;
++      if (err)
++              goto err_mem_2;
++
++
++      rtnl_lock();
++      dev = dev_alloc("irda%d", &err);
++      if (dev) {
++              dev->irq    = IRQ_STUART;
++              dev->init   = pxa250_irda_net_init;
++              dev->uninit = pxa250_irda_net_uninit;
++
++              err = register_netdevice(dev);
++
++              if (err)
++                      kfree(dev);
++              else
++                      netdev = dev;
++      }
++      rtnl_unlock();
++
++      if (err) {
++              release_mem_region(__PREG(ICCR0), 0x1c);
++err_mem_2:
++              release_mem_region(__PREG(STRBR), 0x24);
++      }
++err_mem_1:
++      return err;
++}
++
++static void __exit pxa250_irda_exit(void)
++{
++      struct net_device *dev = netdev;
++
++      netdev = NULL;
++      if (dev) {
++              rtnl_lock();
++              unregister_netdevice(dev);
++              rtnl_unlock();
++      }
++
++        release_mem_region(__PREG(ICCR0), 0x1c);
++
++      release_mem_region(__PREG(STRBR), 0x24);
++
++      /*
++       * We now know that the netdevice is no longer in use, and all
++       * references to our driver have been removed.  The only structure
++       * which may still be present is the netdevice, which will get
++       * cleaned up by net/core/dev.c
++       */
++}
++
++module_init(pxa250_irda_init);
++module_exit(pxa250_irda_exit);
++
++MODULE_AUTHOR("Alexey Lugovskoy Frasenyak Dmitrij");
++MODULE_DESCRIPTION("PXA250 SIR/FIR");
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/net/smc91x.c
+@@ -0,0 +1,2123 @@
++/*------------------------------------------------------------------------
++ . smc91x.c
++ . This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices.
++ .
++ . Copyright (C) 1996 by Erik Stahlman
++ . Copyright (C) 2001 Standard Microsystems Corporation
++ .    Developed by Simple Network Magic Corporation
++ . Copyright (C) 2003 Monta Vista Software, Inc.
++ .    Unified SMC91x driver by Nicolas Pitre
++ .
++ . 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
++ .
++ . Arguments:
++ .    io      = for the base address
++ .    irq     = for the IRQ
++ .    nowait  = 0 for normal wait states, 1 eliminates additional wait states
++ .
++ . original author:
++ .    Erik Stahlman <erik@vt.edu>
++ .
++ . hardware multicast code:
++ .    Peter Cammaert <pc@denkart.be>
++ .
++ . contributors:
++ .    Daris A Nevil <dnevil@snmc.com>
++ .      Nicolas Pitre <nico@cam.org>
++ .
++ . History:
++ .   08/20/00  Arnaldo Melo       fix kfree(skb) in smc_hardware_send_packet
++ .   12/15/00  Christian Jullien  fix "Warning: kfree_skb on hard IRQ"
++ .   03/16/01  Daris A Nevil      modified smc9194.c for use with LAN91C111
++ .   08/22/01  Scott Anderson     merge changes from smc9194 to smc91111
++ .   08/21/01  Pramod B Bhardwaj  added support for RevB of LAN91C111
++ .   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.
++ ----------------------------------------------------------------------------*/
++
++static const char version[] =
++      "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre <nico@cam.org>\n";
++
++/* Debugging level */
++#ifndef SMC_DEBUG
++#define SMC_DEBUG             0
++#endif
++
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/errno.h>
++#include <linux/ioport.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++
++#include "smc91x.h"
++
++
++#ifdef CONFIG_ISA
++/*
++ . the LAN91C111 can be at any of the following port addresses.  To change,
++ . for a slightly different card, you can add it to the array.  Keep in
++ . mind that the array must end in zero.
++*/
++static unsigned int smc_portlist[] __initdata = {
++      0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
++      0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0
++};
++#endif  /* CONFIG_ISA */
++
++#ifndef SMC_IOADDR
++# define SMC_IOADDR           -1
++#endif
++static int io = SMC_IOADDR;
++
++#ifndef SMC_IRQ
++# define SMC_IRQ              -1
++#endif
++static int irq = SMC_IRQ;
++
++#ifndef SMC_NOWAIT
++# define SMC_NOWAIT           0
++#endif
++static int nowait = SMC_NOWAIT;
++
++MODULE_PARM(io, "i");
++MODULE_PARM(irq, "i");
++MODULE_PARM(nowait, "i");
++MODULE_PARM_DESC(io, "I/O base address");
++MODULE_PARM_DESC(irq, "IRQ number");
++MODULE_PARM_DESC(nowait, "set to 1 for no wait state");
++
++
++/*------------------------------------------------------------------------
++ .
++ . The internal workings of the driver.  If you are changing anything
++ . here with the SMC stuff, you should have the datasheet and know
++ . what you are doing.
++ .
++ -------------------------------------------------------------------------*/
++#define CARDNAME "LAN91x"
++
++// Use power-down feature of the chip
++#define POWER_DOWN            1
++
++/*
++ . Wait time for memory to be free.  This probably shouldn't be
++ . tuned that much, as waiting for this means nothing else happens
++ . in the system
++*/
++#define MEMORY_WAIT_TIME      16
++
++/*
++ . This selects whether TX packets are sent one by one to the SMC91x internal
++ . memory ans throttled until transmission completes.  This may prevent
++ . RX overruns a litle by keeping much of the memory free for RX packets
++ . 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
++
++
++/* store this information for the driver.. */
++struct smc_local {
++
++      // If I have to wait until memory is available to send
++      // a packet, I will store the skbuff here, until I get the
++      // desired memory.  Then, I'll send it out and free it.
++      struct sk_buff *saved_skb;
++
++      // these are things that the kernel wants me to keep, so users
++      // can find out semi-useless statistics of how well the card is
++      // performing
++      struct net_device_stats stats;
++
++      // version/revision of the SMC91x chip
++      int version;
++
++      // Set to true during the auto-negotiation sequence
++      int autoneg_active;
++
++      // Address of our PHY port
++      int     phyaddr;
++
++      // Type of PHY
++      int     phytype;
++
++      // Last contents of PHY Register 18
++      int     lastPhy18;
++
++      // Contains the current active transmission mode
++      int     tcr_cur_mode;
++
++      // Contains the current active receive mode
++      int     rcr_cur_mode;
++
++      // Contains the current active receive/phy mode
++      int     rpc_cur_mode;
++      int     ctl_autoneg;
++      int     ctl_rfduplx;
++      int     ctl_rspeed;
++
++#ifdef CONFIG_PM
++      struct  pm_dev* pm;
++#endif
++
++};
++
++
++#if SMC_DEBUG > 2
++#define PRINTK3(args...)  printk(args)
++#else
++#define PRINTK3(args...)  do { } while(0)
++#endif
++
++#if SMC_DEBUG > 1
++#define PRINTK2(args...)  printk(args)
++#else
++#define PRINTK2(args...)  do { } while(0)
++#endif
++
++#if SMC_DEBUG > 0
++#define PRINTK1(args...)  printk(args)
++#define PRINTK(args...)   printk(args)
++#else
++#define PRINTK1(args...)  do { } while(0)
++#define PRINTK(args...)   printk(KERN_DEBUG args)
++#endif
++
++#if SMC_DEBUG > 3
++static void PRINT_PKT(u_char *buf, int length)
++{
++      int i;
++      int remainder;
++      int lines;
++
++      lines = length / 16;
++      remainder = length % 16;
++
++      for (i = 0; i < lines ; i ++) {
++              int cur;
++              for (cur = 0; cur < 8; cur++) {
++                      u_char a, b;
++                      a = *buf++;
++                      b = *buf++;
++                      printk("%02x%02x ", a, b);
++              }
++              printk("\n");
++      }
++      for (i = 0; i < remainder/2 ; i++) {
++              u_char a, b;
++              a = *buf++;
++              b = *buf++;
++              printk("%02x%02x ", a, b );
++      }
++      printk("\n");
++}
++#else
++#define PRINT_PKT(x...)  do { } while(0)
++#endif
++
++
++/* this enables an interrupt in the interrupt mask register */
++#define SMC_ENABLE_INT(x) do {                                                \
++      unsigned long flags;                                            \
++      unsigned char mask;                                             \
++      local_irq_save(flags);                                          \
++      mask = SMC_GET_INT_MASK();                                      \
++      mask |= (x);                                                    \
++      SMC_SET_INT_MASK(mask);                                         \
++      local_irq_restore(flags);                                       \
++} while (0)
++
++/* this disables an interrupt from the interrupt mask register */
++#define SMC_DISABLE_INT(x) do {                                               \
++      unsigned long flags;                                            \
++      unsigned char mask;                                             \
++      local_irq_save(flags);                                          \
++      mask = SMC_GET_INT_MASK();                                      \
++      mask &= ~(x);                                                   \
++      SMC_SET_INT_MASK(mask);                                         \
++      local_irq_restore(flags);                                       \
++} while (0)
++
++/* wait while MMU is busy */
++#define SMC_WAIT_MMU_BUSY() do {                                      \
++      if (unlikely(SMC_GET_MMU_CMD() & MC_BUSY)) {                            \
++              unsigned long timeout = jiffies + 2;                    \
++              while (SMC_GET_MMU_CMD() & MC_BUSY) {                   \
++                      if (time_after(jiffies, timeout)) {             \
++                              printk("%s: timeout %s line %d\n",      \
++                                      dev->name, __FILE__, __LINE__); \
++                              break;                                  \
++                      }                                               \
++              }                                                       \
++      }                                                               \
++} while (0)
++
++
++/* this does a soft reset on the device */
++static void
++smc_reset(struct net_device *dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int phyaddr = lp->phyaddr;
++      int status;
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      /* This resets the registers mostly to defaults, but doesn't
++         affect EEPROM.  That seems unnecessary */
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_RCR( RCR_SOFTRST );
++
++      /* Setup the Configuration Register */
++      /* This is necessary because the CONFIG_REG is not affected */
++      /* by a soft reset */
++      SMC_SELECT_BANK( 1 );
++      SMC_SET_CONFIG( CONFIG_DEFAULT );
++
++      /* Setup for fast accesses if requested */
++      /* If the card/system can't handle it then there will */
++      /* be no recovery except for a hard reset or power cycle */
++      if (nowait)
++              SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT );
++
++#ifdef 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);
++#endif
++
++      /* this should pause enough for the chip to be happy */
++      udelay(1);
++
++      /* Disable transmit and receive functionality */
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_RCR( RCR_CLEAR );
++      SMC_SET_TCR( TCR_CLEAR );
++
++      /* set the control register to automatically
++         release successfully transmitted packets, to make the best
++         use out of our limited memory */
++      SMC_SELECT_BANK( 1 );
++#if ! THROTTLE_TX_PKTS
++      SMC_SET_CTL( SMC_GET_CTL() | CTL_AUTO_RELEASE );
++#else
++      SMC_SET_CTL( SMC_GET_CTL() & ~CTL_AUTO_RELEASE );
++#endif
++
++      /* Disable all interrupts */
++      SMC_SELECT_BANK( 2 );
++      SMC_SET_INT_MASK( 0 );
++
++      /* Reset the MMU */
++      SMC_SET_MMU_CMD( MC_RESET );
++      SMC_WAIT_MMU_BUSY();
++}
++
++/* Enable Interrupts, Receive, and Transmit */
++static void
++smc_enable(struct net_device *dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int mask;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      /* see the header file for options in TCR/RCR DEFAULT*/
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_TCR( lp->tcr_cur_mode );
++      SMC_SET_RCR( lp->rcr_cur_mode );
++
++      /* now, enable interrupts */
++      mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT;
++      if (lp->version >= 0x70)
++              mask |= IM_MDINT;
++      SMC_SELECT_BANK( 2 );
++      SMC_SET_INT_MASK( mask );
++}
++
++/* this puts the device in an inactive state */
++static void
++smc_shutdown(struct net_device *dev)
++{
++      int ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int phyaddr = lp->phyaddr;
++      int status;
++
++      PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);
++
++      /* no more interrupts for me */
++      SMC_SELECT_BANK( 2 );
++      SMC_SET_INT_MASK( 0 );
++
++      /* and tell the card to stay away from that nasty outside world */
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_RCR( RCR_CLEAR );
++      SMC_SET_TCR( TCR_CLEAR );
++
++#ifdef 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);
++
++      /* finally, shut the chip down */
++      SMC_SELECT_BANK( 1 );
++      SMC_SET_CONFIG( SMC_GET_CONFIG() & ~CONFIG_EPH_POWER_EN );
++#endif
++}
++
++/* This is the procedure to handle the receipt of a packet. */
++static inline void 
++smc_rcv(struct net_device *dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++      unsigned int packet_number, status, packet_len;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      packet_number = SMC_GET_RXFIFO();
++      if (unlikely(packet_number & RXFIFO_REMPTY)) {
++              PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);
++              return;
++      }
++
++      /* read from start of packet */
++      SMC_SET_PTR( PTR_READ | PTR_RCV | PTR_AUTOINC );
++
++      /* First two words are status and packet length */
++      SMC_GET_PKT_HDR(status, packet_len);
++      packet_len &= 0x07ff;  /* mask off top bits */
++      PRINTK2("%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
++              dev->name, packet_number, status,
++              packet_len, packet_len);
++
++      if (unlikely(status & RS_ERRORS)) {
++              lp->stats.rx_errors++;
++              if (status & RS_ALGNERR)
++                      lp->stats.rx_frame_errors++;
++              if (status & (RS_TOOSHORT | RS_TOOLONG))
++                      lp->stats.rx_length_errors++;
++              if (status & RS_BADCRC)
++                      lp->stats.rx_crc_errors++;
++      } else {
++              struct sk_buff *skb;
++              unsigned char *data;
++              unsigned int data_len;
++
++              /* set multicast stats */
++              if (status & RS_MULTICAST)
++                      lp->stats.multicast++;
++
++              /*
++               * Actual payload is packet_len - 4 (or 3 if odd byte).
++               * We want skb_reserve(2) and the final ctrl word
++               * (2 bytes, possibly containing the payload odd byte).
++               * Ence packet_len - 4 + 2 + 2.
++               */
++              skb = dev_alloc_skb(packet_len);
++              if (unlikely(skb == NULL)) {
++                      printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
++                              dev->name);
++                      lp->stats.rx_dropped++;
++                      goto done;
++              }
++
++              /* Align IP header to 32 bits */
++              skb_reserve(skb, 2);
++
++              /* BUG: the LAN91C111 rev A never sets this bit. Force it. */
++              if (lp->version == 0x90)
++                      status |= RS_ODDFRAME;
++
++              /*
++               * If odd length: packet_len - 3,
++               * otherwise packet_len - 4.
++               */
++              data_len = packet_len - ((status & RS_ODDFRAME) ? 3 : 4);
++              data = skb_put(skb, data_len);
++              SMC_PULL_DATA(data, packet_len - 2);
++
++              PRINT_PKT(data, packet_len - 2);
++
++              dev->last_rx = jiffies;
++              skb->dev = dev;
++              skb->protocol = eth_type_trans(skb, dev);
++              netif_rx(skb);
++              lp->stats.rx_packets++;
++              lp->stats.rx_bytes += data_len;
++      }
++
++done:
++      SMC_WAIT_MMU_BUSY();
++      SMC_SET_MMU_CMD( MC_RELEASE );
++}
++
++/*
++ * This is called to actually send a packet to the chip.
++ * Returns non-zero when successful.
++ */
++static void
++smc_hardware_send_packet(struct net_device *dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++      struct sk_buff *skb = lp->saved_skb;
++      unsigned int packet_no, len;
++      unsigned char *buf;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      if (unlikely(!skb)) {
++              printk ("%s: In XMIT with no packet to send\n", dev->name);
++              return;
++      }
++
++      packet_no = SMC_GET_AR();
++      if (unlikely(packet_no & AR_FAILED)) {
++              printk("%s: Memory allocation failed.\n", dev->name);
++              lp->saved_skb = NULL;
++              lp->stats.tx_errors++;
++              lp->stats.tx_fifo_errors++;
++              dev_kfree_skb_any(skb);
++              return;
++      }
++
++      /* point to the beginning of the packet */
++      SMC_SET_PN( packet_no );
++      SMC_SET_PTR( PTR_AUTOINC );
++
++      buf = skb->data;
++      len = skb->len;
++      PRINTK2("%s: TX PNR 0x%x lENGTH 0x%04x (%d) BUF 9x%p\n",
++              dev->name, packet_no, len, len, buf);
++      PRINT_PKT(buf, len);
++
++      /*
++       * Send the packet length ( +6 for status words, length, and ctl.
++       * The card will pad to 64 bytes with zeroes if packet is too small.
++       */
++      SMC_PUT_PKT_HDR(0, len + 6);
++
++      /* send the actual data */
++      SMC_PUSH_DATA(buf, len & ~1);
++
++      /* Send final ctl word with the last byte if there is one */
++      SMC_outw( ((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG );
++
++      /* and let the chipset deal with it */
++      SMC_SET_MMU_CMD( MC_ENQUEUE );
++      SMC_ACK_INT( IM_TX_EMPTY_INT );
++
++      dev->trans_start = jiffies;
++      dev_kfree_skb_any(skb);
++      lp->saved_skb = NULL;
++      lp->stats.tx_packets++;
++      lp->stats.tx_bytes += len;
++}
++
++/*
++ . Since I am not sure if I will have enough room in the chip's ram
++ . to store the packet, I call this routine which either sends it
++ . now, or set the card to generates an interrupt when ready
++ . for the packet.
++ */
++static int
++smc_hard_start_xmit( struct sk_buff * skb, struct net_device * dev )
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++      unsigned int numPages, poll_count, status, saved_bank;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      if (unlikely(lp->saved_skb != NULL)) {
++              /* THIS SHOULD NEVER HAPPEN. */
++              printk( KERN_CRIT
++                      "%s: Bad Craziness - sent packet while busy.\n",
++                      dev->name );
++              lp->stats.tx_errors++;
++              lp->stats.tx_aborted_errors++;
++              return 1;
++      }
++      lp->saved_skb = skb;
++
++      /*
++      ** The MMU wants the number of pages to be the number of 256 bytes
++      ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
++      **
++      ** The 91C111 ignores the size bits, but the code is left intact
++      ** for backwards and future compatibility.
++      **
++      ** Pkt size for allocating is data length +6 (for additional status
++      ** words, length and ctl!)
++      **
++      ** If odd size then last byte is included in ctl word.
++      */
++      numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
++      if (unlikely(numPages > 7)) {
++              printk("%s: Far too big packet error.\n", dev->name);
++              lp->saved_skb = NULL;
++              lp->stats.tx_errors++;
++              lp->stats.tx_dropped++;
++              dev_kfree_skb(skb);
++              return 0;
++      }
++
++      /* now, try to allocate the memory */
++      saved_bank = SMC_CURRENT_BANK();
++      SMC_SELECT_BANK( 2 );
++      SMC_SET_MMU_CMD( MC_ALLOC | numPages );
++
++      /*
++       * Poll the chip for a short amount of time in case the
++       * allocation succeeds quickly.
++       */
++      poll_count = MEMORY_WAIT_TIME;
++      do {
++              status = SMC_GET_INT();
++              if (status & IM_ALLOC_INT) {
++                      SMC_ACK_INT( IM_ALLOC_INT );
++                      break;
++              }
++      } while (--poll_count);
++
++      if (!poll_count) {
++              /* oh well, wait until the chip finds memory later */
++              netif_stop_queue(dev);
++              PRINTK2("%s: TX memory allocation deferred.\n", dev->name);
++              SMC_ENABLE_INT( IM_ALLOC_INT );
++      } else {
++              /* Send current packet immediately.. */
++#if THROTTLE_TX_PKTS
++              netif_stop_queue(dev);
++#endif
++              smc_hardware_send_packet(dev);
++              SMC_ENABLE_INT( IM_TX_INT | IM_TX_EMPTY_INT );
++      }
++
++      SMC_SELECT_BANK( saved_bank );
++      return 0;
++}
++
++/*
++ . This handles a TX interrupt, which is only called when an error
++ . relating to a packet is sent or CTL_AUTO_RELEASE is not set.
++*/
++static void
++smc_tx(struct net_device *dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned int saved_packet, packet_no, tx_status, pkt_len;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      /* If the TX FIFO is empty then nothing to do */
++      packet_no = SMC_GET_TXFIFO();
++      if (unlikely(packet_no & TXFIFO_TEMPTY)) {
++              PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name);
++              return;
++      }
++
++      /* select packet to read from */
++      saved_packet = SMC_GET_PN();
++      SMC_SET_PN( packet_no );
++
++      /* read the first word (status word) from this packet */
++      SMC_SET_PTR( PTR_AUTOINC | PTR_READ );
++      SMC_GET_PKT_HDR(tx_status, pkt_len);
++      PRINTK2("%s: TX STATUS 0x%04x PNR 0x%02x\n",
++              dev->name, tx_status, packet_no);
++
++      if (!(tx_status & TS_SUCCESS))
++              lp->stats.tx_errors++;
++      if (tx_status & TS_LOSTCAR)
++              lp->stats.tx_carrier_errors++;
++      if (tx_status & TS_LATCOL) {
++              printk( KERN_DEBUG
++                      "%s: Late collision occurred on last xmit.\n",
++                      dev->name);
++              lp->stats.tx_window_errors++;
++      }
++
++      /* kill the packet */
++      SMC_WAIT_MMU_BUSY();
++      SMC_SET_MMU_CMD( MC_FREEPKT );
++
++      /* Don't restore Packet Number Reg until busy bit is cleared */
++      SMC_WAIT_MMU_BUSY();
++      SMC_SET_PN( saved_packet );
++
++      /* re-enable transmit */
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_TCR( lp->tcr_cur_mode );
++      SMC_SELECT_BANK( 2 );
++}
++
++
++//---PHY CONTROL AND CONFIGURATION-----------------------------------------
++
++/*------------------------------------------------------------
++ . Debugging function for viewing MII Management serial bitstream
++ .-------------------------------------------------------------*/
++#if SMC_DEBUG > 3
++static void
++PRINT_MII_STREAM(u_char *bits, int size)
++{
++      int i;
++
++      printk("BIT#:");
++      for (i = 0; i < size; ++i)
++              printk("%d", i%10);
++
++      printk("\nMDOE:");
++      for (i = 0; i < size; ++i) {
++              if (bits[i] & MII_MDOE)
++                      printk("1");
++              else
++                      printk("0");
++      }
++
++      printk("\nMDO :");
++      for (i = 0; i < size; ++i) {
++              if (bits[i] & MII_MDO)
++                      printk("1");
++              else
++                      printk("0");
++      }
++
++      printk("\nMDI :");
++      for (i = 0; i < size; ++i) {
++              if (bits[i] & MII_MDI)
++                      printk("1");
++              else
++                      printk("0");
++      }
++
++      printk("\n");
++}
++#else
++#define PRINT_MII_STREAM(x...)
++#endif
++
++/*------------------------------------------------------------
++ . Reads a register from the MII Management serial interface
++ .-------------------------------------------------------------*/
++static int
++smc_read_phy_register(unsigned long ioaddr, int phyaddr, int phyreg)
++{
++      int oldBank;
++      int i, mask, mii_reg;
++      u_char bits[64];
++      int input_idx, phydata;
++      int clk_idx = 0;
++
++      // 32 consecutive ones on MDO to establish sync
++      for (i = 0; i < 32; ++i)
++              bits[clk_idx++] = MII_MDOE | MII_MDO;
++
++      // Start code <01>
++      bits[clk_idx++] = MII_MDOE;
++      bits[clk_idx++] = MII_MDOE | MII_MDO;
++
++      // Read command <10>
++      bits[clk_idx++] = MII_MDOE | MII_MDO;
++      bits[clk_idx++] = MII_MDOE;
++
++      // Output the PHY address, msb first
++      mask = 0x10;
++      for (i = 0; i < 5; ++i) {
++              if (phyaddr & mask)
++                      bits[clk_idx++] = MII_MDOE | MII_MDO;
++              else
++                      bits[clk_idx++] = MII_MDOE;
++
++              // Shift to next lowest bit
++              mask >>= 1;
++      }
++
++      // Output the phy register number, msb first
++      mask = 0x10;
++      for (i = 0; i < 5; ++i) {
++              if (phyreg & mask)
++                      bits[clk_idx++] = MII_MDOE | MII_MDO;
++              else
++                      bits[clk_idx++] = MII_MDOE;
++
++              // Shift to next lowest bit
++              mask >>= 1;
++      }
++
++      // Tristate and turnaround (2 bit times)
++      bits[clk_idx++] = 0;
++      //bits[clk_idx++] = 0;
++
++      // Input starts at this bit time
++      input_idx = clk_idx;
++
++      // Will input 16 bits
++      for (i = 0; i < 16; ++i)
++              bits[clk_idx++] = 0;
++
++      // Final clock bit
++      bits[clk_idx++] = 0;
++
++      // Save the current bank
++      oldBank = SMC_CURRENT_BANK();
++
++      // Select bank 3
++      SMC_SELECT_BANK( 3 );
++
++      // Get the current MII register value
++      mii_reg = SMC_GET_MII();
++
++      // Turn off all MII Interface bits
++      mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
++
++      // Clock all 64 cycles
++      for (i = 0; i < sizeof bits; ++i) {
++              // Clock Low - output data
++              SMC_SET_MII( mii_reg | bits[i] );
++              udelay(50);
++
++              // Clock Hi - input data
++              SMC_SET_MII( mii_reg | bits[i] | MII_MCLK );
++              udelay(50);
++              bits[i] |= SMC_GET_MII() & MII_MDI;
++      }
++
++      // Return to idle state
++      // Set clock to low, data to low, and output tristated
++      SMC_SET_MII( mii_reg );
++      udelay(50);
++
++      // Restore original bank select
++      SMC_SELECT_BANK( oldBank );
++
++      // Recover input data
++      phydata = 0;
++      for (i = 0; i < 16; ++i) {
++              phydata <<= 1;
++
++              if (bits[input_idx++] & MII_MDI)
++                      phydata |= 0x0001;
++      }
++
++      PRINTK3("%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
++              __FUNCTION__, phyaddr, phyreg, phydata);
++      PRINT_MII_STREAM(bits, sizeof(bits));
++
++      return phydata;
++}
++
++/*------------------------------------------------------------
++ . Writes a register to the MII Management serial interface
++ .-------------------------------------------------------------*/
++static void
++smc_write_phy_register( unsigned long ioaddr, int phyaddr,
++                      int phyreg, int phydata )
++{
++      int oldBank;
++      int i, mask, mii_reg;
++      u_char bits[65];
++      int clk_idx = 0;
++
++      // 32 consecutive ones on MDO to establish sync
++      for (i = 0; i < 32; ++i)
++              bits[clk_idx++] = MII_MDOE | MII_MDO;
++
++      // Start code <01>
++      bits[clk_idx++] = MII_MDOE;
++      bits[clk_idx++] = MII_MDOE | MII_MDO;
++
++      // Write command <01>
++      bits[clk_idx++] = MII_MDOE;
++      bits[clk_idx++] = MII_MDOE | MII_MDO;
++
++      // Output the PHY address, msb first
++      mask = 0x10;
++      for (i = 0; i < 5; ++i) {
++              if (phyaddr & mask)
++                      bits[clk_idx++] = MII_MDOE | MII_MDO;
++              else
++                      bits[clk_idx++] = MII_MDOE;
++
++              // Shift to next lowest bit
++              mask >>= 1;
++      }
++
++      // Output the phy register number, msb first
++      mask = 0x10;
++      for (i = 0; i < 5; ++i) {
++              if (phyreg & mask)
++                      bits[clk_idx++] = MII_MDOE | MII_MDO;
++              else
++                      bits[clk_idx++] = MII_MDOE;
++
++              // Shift to next lowest bit
++              mask >>= 1;
++      }
++
++      // Tristate and turnaround (2 bit times)
++      bits[clk_idx++] = 0;
++      bits[clk_idx++] = 0;
++
++      // Write out 16 bits of data, msb first
++      mask = 0x8000;
++      for (i = 0; i < 16; ++i) {
++              if (phydata & mask)
++                      bits[clk_idx++] = MII_MDOE | MII_MDO;
++              else
++                      bits[clk_idx++] = MII_MDOE;
++
++              // Shift to next lowest bit
++              mask >>= 1;
++      }
++
++      // Final clock bit (tristate)
++      bits[clk_idx++] = 0;
++
++      // Save the current bank
++      oldBank = SMC_CURRENT_BANK();
++
++      // Select bank 3
++      SMC_SELECT_BANK( 3 );
++
++      // Get the current MII register value
++      mii_reg = SMC_GET_MII();
++
++      // Turn off all MII Interface bits
++      mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
++
++      // Clock all cycles
++      for (i = 0; i < sizeof bits; ++i) {
++              // Clock Low - output data
++              SMC_SET_MII( mii_reg | bits[i] );
++              udelay(50);
++
++              // Clock Hi - input data
++              SMC_SET_MII( mii_reg | bits[i] | MII_MCLK );
++              udelay(50);
++              bits[i] |= SMC_GET_MII() & MII_MDI;
++      }
++
++      // Return to idle state
++      // Set clock to low, data to low, and output tristated
++      SMC_SET_MII( mii_reg );
++      udelay(50);
++
++      // Restore original bank select
++      SMC_SELECT_BANK( oldBank );
++
++      PRINTK3("%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
++              __FUNCTION__, phyaddr, phyreg, phydata);
++      PRINT_MII_STREAM(bits, sizeof(bits));
++}
++
++
++/*------------------------------------------------------------
++ . Finds and reports the PHY address
++ .-------------------------------------------------------------*/
++static int smc_detect_phy(struct net_device* dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++      int phy_id1, phy_id2;
++      int phyaddr;
++      int found = 0;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      // Scan all 32 PHY addresses if necessary
++      for (phyaddr = 0; phyaddr < 32; ++phyaddr) {
++              // Read the PHY identifiers
++              phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG);
++              phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG);
++
++              PRINTK3("%s: phy_id1=0x%x, phy_id2=0x%x\n",
++                      dev->name, phy_id1, phy_id2);
++
++              // Make sure it is a valid identifier
++              if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) &&
++                  (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) {
++                      if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) {
++                              // Save the PHY's address
++                              lp->phyaddr = phyaddr;
++                              found = 1;
++                              break;
++                      }
++              }
++      }
++
++      if (!found) {
++              PRINTK("%s: No PHY found\n", dev->name);
++              return(0);
++      }
++
++      // Set the PHY type
++      if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) ) {
++              lp->phytype = PHY_LAN83C183;
++              PRINTK("%s: PHY=LAN83C183 (LAN91C111 Internal)\n", dev->name);
++      }
++
++      if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) ) {
++              lp->phytype = PHY_LAN83C180;
++              PRINTK("%s: PHY=LAN83C180\n", dev->name);
++      }
++
++      return 1;
++}
++
++/*------------------------------------------------------------
++ . Waits the specified number of milliseconds - kernel friendly
++ .-------------------------------------------------------------*/
++static void
++smc_wait_ms(unsigned int ms)
++{
++      if (!in_interrupt()) {
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              schedule_timeout(1 + ms * HZ / 1000);
++      } else {
++              /* if this happens it must be fixed */
++              printk( KERN_WARNING "%s: busy wait while in interrupt!\n",
++                      __FUNCTION__);
++              mdelay(ms);
++      }
++}
++
++/*------------------------------------------------------------
++ . Sets the PHY to a configuration as determined by the user
++ .-------------------------------------------------------------*/
++static int
++smc_phy_fixed(struct net_device *dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int phyaddr = lp->phyaddr;
++      int my_fixed_caps, cfg1;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      // Enter Link Disable state
++      cfg1 = smc_read_phy_register(ioaddr, phyaddr, PHY_CFG1_REG);
++      cfg1 |= PHY_CFG1_LNKDIS;
++      smc_write_phy_register(ioaddr, phyaddr, PHY_CFG1_REG, cfg1);
++
++      // Set our fixed capabilities
++      // Disable auto-negotiation
++      my_fixed_caps = 0;
++
++      if (lp->ctl_rfduplx)
++              my_fixed_caps |= PHY_CNTL_DPLX;
++
++      if (lp->ctl_rspeed == 100)
++              my_fixed_caps |= PHY_CNTL_SPEED;
++
++      // Write our capabilities to the phy control register
++      smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, my_fixed_caps);
++
++      // Re-Configure the Receive/Phy Control register
++      SMC_SET_RPC( lp->rpc_cur_mode );
++
++      // Success
++      return(1);
++}
++
++/*------------------------------------------------------------
++ . Configures the specified PHY through the MII management interface
++ . using Autonegotiation.
++ . Calls smc_phy_fixed() if the user has requested a certain config.
++ .-------------------------------------------------------------*/
++static void
++smc_phy_configure(struct net_device* dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int timeout;
++      int phyaddr;
++      int my_phy_caps; // My PHY capabilities
++      int my_ad_caps; // My Advertised capabilities
++      int status;
++
++      PRINTK3("%s:smc_program_phy()\n", dev->name);
++
++      // Set the blocking flag
++      lp->autoneg_active = 1;
++
++      // Find the address and type of our phy
++      if (!smc_detect_phy(dev))
++              goto smc_phy_configure_exit;
++
++      // Get the detected phy address
++      phyaddr = lp->phyaddr;
++
++      // Reset the PHY, setting all other bits to zero
++      smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST);
++
++      // Wait for the reset to complete, or time out
++      timeout = 6; // Wait up to 3 seconds
++      while (timeout--) {
++              if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG)
++                  & PHY_CNTL_RST))
++                      // reset complete
++                      break;
++              smc_wait_ms(500); // wait 500 millisecs
++              if (signal_pending(current)) { // Exit anyway if signaled
++                      PRINTK("%s: PHY reset interrupted by signal\n",
++                              dev->name);
++                      timeout = 0;
++                      break;
++              }
++      }
++
++      if (timeout < 1) {
++              printk("%s: PHY reset timed out\n", dev->name);
++              goto smc_phy_configure_exit;
++      }
++
++      // Read PHY Register 18, Status Output
++      lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
++
++      // Enable PHY Interrupts (for register 18)
++      // Interrupts listed here are disabled
++      smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG,
++              PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
++              PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
++              PHY_INT_SPDDET | PHY_INT_DPLXDET);
++
++      /* Configure the Receive/Phy Control register */
++      SMC_SELECT_BANK( 0 );
++      SMC_SET_RPC( lp->rpc_cur_mode );
++
++      // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
++      my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
++
++      // If the user requested no auto neg, then go set his request
++      if (!(lp->ctl_autoneg)) {
++              smc_phy_fixed(dev);
++              goto smc_phy_configure_exit;
++      }
++
++      if( !( my_phy_caps & PHY_STAT_CAP_ANEG))
++      {
++              printk(KERN_INFO "Auto negotiation NOT supported\n");
++              smc_phy_fixed(dev);
++              goto smc_phy_configure_exit;
++      }
++
++      my_ad_caps  = PHY_AD_CSMA; // I am CSMA capable
++
++      if (my_phy_caps & PHY_STAT_CAP_T4)
++              my_ad_caps |= PHY_AD_T4;
++
++      if (my_phy_caps & PHY_STAT_CAP_TXF)
++              my_ad_caps |= PHY_AD_TX_FDX;
++
++      if (my_phy_caps & PHY_STAT_CAP_TXH)
++              my_ad_caps |= PHY_AD_TX_HDX;
++
++      if (my_phy_caps & PHY_STAT_CAP_TF)
++              my_ad_caps |= PHY_AD_10_FDX;
++
++      if (my_phy_caps & PHY_STAT_CAP_TH)
++              my_ad_caps |= PHY_AD_10_HDX;
++
++      // Disable capabilities not selected by our user
++      if (lp->ctl_rspeed != 100)
++              my_ad_caps &= ~(PHY_AD_T4|PHY_AD_TX_FDX|PHY_AD_TX_HDX);
++
++      if (!lp->ctl_rfduplx)
++              my_ad_caps &= ~(PHY_AD_TX_FDX|PHY_AD_10_FDX);
++
++      // Update our Auto-Neg Advertisement Register
++      smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps);
++
++      // Read the register back.  Without this, it appears that when
++      // auto-negotiation is restarted, sometimes it isn't ready and
++      // the link does not come up.
++      status = smc_read_phy_register(ioaddr, phyaddr, PHY_AD_REG);
++
++      PRINTK2("%s: phy caps=%x\n", dev->name, my_phy_caps);
++      PRINTK2("%s: phy advertised caps=%x\n", dev->name, my_ad_caps);
++
++      // Restart auto-negotiation process in order to advertise my caps
++      smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
++              PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
++
++      // Wait for the auto-negotiation to complete.  This may take from
++      // 2 to 3 seconds.
++      // Wait for the reset to complete, or time out
++      timeout = 20; // Wait up to 10 seconds
++      while (timeout--) {
++              status = smc_read_phy_register(ioaddr, phyaddr, PHY_RMT_REG);
++              if (status & PHY_AD_ACK)
++                      // auto-negotiate complete
++                      break;
++
++              smc_wait_ms(500); // wait 500 millisecs
++              if (signal_pending(current)) { // Exit anyway if signaled
++                      printk(KERN_DEBUG
++                              "%s: PHY auto-negotiate interrupted by signal\n",
++                              dev->name);
++                      timeout = 0;
++                      break;
++              }
++      }
++      status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
++
++      if (timeout < 1) {
++              PRINTK("%s: PHY auto-negotiate timed out\n", dev->name);
++      }
++
++      // Fail if we detected an auto-negotiate remote fault
++      if (status & PHY_STAT_REM_FLT) {
++              PRINTK("%s: PHY remote fault detected\n", dev->name);
++      }
++
++      // Wait for link. Once the link is up, phy18 should be up to date
++        timeout = 200;
++        do              
++        {
++                udelay(100);
++                status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
++        } while ( ((status & PHY_STAT_LINK)==0) && --timeout);
++
++        if (status & PHY_STAT_LINK)        
++        {                                      
++                PRINTK("%s: Ethernet Link Detected\n", dev->name); 
++        }       
++
++      // The smc_phy_interrupt() routine will be called to update lastPhy18
++
++      // Set our sysctl parameters to match auto-negotiation results
++      if ( lp->lastPhy18 & PHY_INT_SPDDET ) {
++              PRINTK("%s: PHY 100BaseT\n", dev->name);
++              lp->rpc_cur_mode |= RPC_SPEED;
++      } else {
++              PRINTK("%s: PHY 10BaseT\n", dev->name);
++              lp->rpc_cur_mode &= ~RPC_SPEED;
++      }
++
++      if ( lp->lastPhy18 & PHY_INT_DPLXDET ) {
++              PRINTK("%s: PHY Full Duplex\n", dev->name);
++              lp->rpc_cur_mode |= RPC_DPLX;
++              lp->tcr_cur_mode |= TCR_SWFDUP;
++      } else {
++              PRINTK("%s: PHY Half Duplex\n", dev->name);
++              lp->rpc_cur_mode &= ~RPC_DPLX;
++              lp->tcr_cur_mode &= ~TCR_SWFDUP;
++      }
++
++      // Re-Configure the Receive/Phy Control register and TCR
++      SMC_SET_RPC( lp->rpc_cur_mode );
++      SMC_SET_TCR( lp->tcr_cur_mode );
++
++smc_phy_configure_exit:
++      // Exit auto-negotiation
++      lp->autoneg_active = 0;
++}
++
++/*************************************************************************
++ . smc_phy_interrupt
++ .
++ . Purpose:  Handle interrupts relating to PHY register 18. This is
++ .  called from the "hard" interrupt handler.
++ .
++ ************************************************************************/
++static void
++smc_phy_interrupt(struct net_device* dev)
++{
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int phyaddr = lp->phyaddr;
++      int phy18;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      for(;;) {
++              // Read PHY Register 18, Status Output
++              phy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
++
++              // Exit if not more changes
++              if (phy18 == lp->lastPhy18)
++                      break;
++
++#if SMC_DEBUG > 1
++              PRINTK2("%s:     phy18=0x%04x\n", dev->name, phy18);
++              PRINTK2("%s: lastPhy18=0x%04x\n", dev->name, lp->lastPhy18);
++
++              // Handle events
++              if ((phy18 & PHY_INT_LNKFAIL) !=
++                              (lp->lastPhy18 & PHY_INT_LNKFAIL))
++                      PRINTK2("%s: PHY Link Fail=%x\n", dev->name,
++                                      phy18 & PHY_INT_LNKFAIL);
++
++              if ((phy18 & PHY_INT_LOSSSYNC) !=
++                              (lp->lastPhy18 & PHY_INT_LOSSSYNC))
++                      PRINTK2("%s: PHY LOSS SYNC=%x\n", dev->name,
++                                      phy18 & PHY_INT_LOSSSYNC);
++
++              if ((phy18 & PHY_INT_CWRD) != (lp->lastPhy18 & PHY_INT_CWRD))
++                      PRINTK2("%s: PHY INVALID 4B5B code=%x\n", dev->name,
++                                      phy18 & PHY_INT_CWRD);
++
++              if ((phy18 & PHY_INT_SSD) != (lp->lastPhy18 & PHY_INT_SSD))
++                      PRINTK2("%s: PHY No Start Of Stream=%x\n", dev->name,
++                                      phy18 & PHY_INT_SSD);
++
++              if ((phy18 & PHY_INT_ESD) != (lp->lastPhy18 & PHY_INT_ESD))
++
++                      PRINTK2("%s: PHY No End Of Stream=%x\n", dev->name,
++                                      phy18 & PHY_INT_ESD);
++
++              if ((phy18 & PHY_INT_RPOL) != (lp->lastPhy18 & PHY_INT_RPOL))
++                      PRINTK2("%s: PHY Reverse Polarity Detected=%x\n",
++                                      dev->name, phy18 & PHY_INT_RPOL);
++
++              if ((phy18 & PHY_INT_JAB) != (lp->lastPhy18 & PHY_INT_JAB))
++                      PRINTK2("%s: PHY Jabber Detected=%x\n", dev->name,
++                                      phy18 & PHY_INT_JAB);
++
++              if ((phy18 & PHY_INT_SPDDET) !=
++                              (lp->lastPhy18 & PHY_INT_SPDDET))
++                      PRINTK2("%s: PHY Speed Detect=%x\n", dev->name,
++                                      phy18 & PHY_INT_SPDDET);
++
++              if ((phy18 & PHY_INT_DPLXDET) !=
++                              (lp->lastPhy18 & PHY_INT_DPLXDET))
++                      PRINTK2("%s: PHY Duplex Detect=%x\n", dev->name,
++                                      phy18 & PHY_INT_DPLXDET);
++#endif
++              // Update the last phy 18 variable
++              lp->lastPhy18 = phy18;
++      }
++}
++
++//--- END PHY CONTROL AND CONFIGURATION-------------------------------------
++
++
++/*
++ * This is the main routine of the driver, to handle the device when
++ * it needs some attention.
++ */
++static void
++smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct net_device *dev = dev_id;
++      unsigned long ioaddr = dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      int status, mask, timeout, card_stats;
++      int saved_bank, saved_pointer;
++
++      PRINTK3("%s: %s\n", dev->name, __FUNCTION__);
++
++      saved_bank = SMC_CURRENT_BANK();
++      SMC_SELECT_BANK(2);
++      saved_pointer = SMC_GET_PTR();
++      mask = SMC_GET_INT_MASK();
++      SMC_SET_INT_MASK( 0 );
++
++      /* set a timeout value, so I don't stay here forever */
++      timeout = 8;
++
++      do {
++              status = SMC_GET_INT();
++
++              PRINTK2("%s: IRQ 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
++                      dev->name, status, mask,
++                      ({ int meminfo; SMC_SELECT_BANK(0);
++                         meminfo = SMC_GET_MIR();
++                         SMC_SELECT_BANK(2); meminfo; }),
++                      SMC_GET_FIFO());
++
++              status &= mask;
++              if (!status)
++                      break;
++
++              if (status & IM_RCV_INT) {
++                      PRINTK3("%s: RX irq\n", dev->name);
++                      smc_rcv(dev);
++              } else if (status & IM_TX_INT) {
++                      PRINTK3("%s: TX int\n", dev->name);
++                      smc_tx(dev);
++                      SMC_ACK_INT( IM_TX_INT );
++#if THROTTLE_TX_PKTS
++                      netif_wake_queue(dev);
++#endif
++              } else if (status & IM_ALLOC_INT) {
++                      PRINTK3("%s: Allocation irq\n", dev->name);
++                      smc_hardware_send_packet(dev);
++                      mask |= (IM_TX_INT | IM_TX_EMPTY_INT);
++                      mask &= ~IM_ALLOC_INT;
++#if ! THROTTLE_TX_PKTS
++                      netif_wake_queue(dev);
++#endif
++              } else if (status & IM_TX_EMPTY_INT) {
++                      PRINTK3("%s: TX empty\n", dev->name);
++                      mask &= ~IM_TX_EMPTY_INT;
++
++                      /* update stats */
++                      SMC_SELECT_BANK( 0 );
++                      card_stats = SMC_GET_COUNTER();
++                      SMC_SELECT_BANK( 2 );
++
++                      /* single collisions */
++                      lp->stats.collisions += card_stats & 0xF;
++                      card_stats >>= 4;
++
++                      /* multiple collisions */
++                      lp->stats.collisions += card_stats & 0xF;
++              } else if (status & IM_RX_OVRN_INT) {
++                      PRINTK1( "%s: RX overrun\n", dev->name);
++                      SMC_ACK_INT( IM_RX_OVRN_INT );
++                      lp->stats.rx_errors++;
++                      lp->stats.rx_fifo_errors++;
++              } else if (status & IM_EPH_INT) {
++                      PRINTK("%s: UNSUPPORTED: EPH INTERRUPT\n", dev->name);
++              } else if (status & IM_MDINT) {
++                      SMC_ACK_INT( IM_MDINT );
++                      smc_phy_interrupt(dev);
++              } else if (status & IM_ERCV_INT ) {
++                      SMC_ACK_INT( IM_ERCV_INT );
++                      PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name);
++              }
++      } while (--timeout);
++
++      /* restore register states */
++      SMC_SET_INT_MASK( mask );
++      SMC_SET_PTR( saved_pointer );
++      SMC_SELECT_BANK( saved_bank );
++
++      PRINTK3("%s: Interrupt done\n", dev->name);
++}
++
++/* Our watchdog timed out. Called by the networking layer */
++static void
++smc_timeout(struct net_device *dev)
++{
++      struct smc_local *lp    = (struct smc_local *)dev->priv;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      smc_reset(dev);
++      smc_enable(dev);
++
++#if 0
++      /* Reconfiguring the PHY doesn't seem like a bad idea here, but
++       * it introduced a problem.  Now that this is a timeout routine,
++       * we are getting called from within an interrupt context.
++       * smc_phy_configure() calls smc_wait_ms() which calls
++       * schedule_timeout() which calls schedule().  When schedule()
++       * is called from an interrupt context, it prints out
++       * "Scheduling in interrupt" and then calls BUG().  This is
++       * obviously not desirable.  This was worked around by removing
++       * the call to smc_phy_configure() here because it didn't seem
++       * absolutely necessary.  Ultimately, if smc_wait_ms() is
++       * supposed to be usable from an interrupt context (which it
++       * looks like it thinks it should handle), it should be fixed.
++       */
++      /* Reconfigure the PHY */
++      smc_phy_configure(dev);
++#endif
++
++      /* clear anything saved */
++      if (lp->saved_skb != NULL) {
++              dev_kfree_skb (lp->saved_skb);
++              lp->saved_skb = NULL;
++              lp->stats.tx_errors++;
++              lp->stats.tx_aborted_errors++;
++      }
++      dev->trans_start = jiffies;
++      netif_wake_queue(dev);
++}
++
++/*
++ * Finds the CRC32 of a set of bytes.
++ * (from Peter Cammaert's code)
++ */
++static int
++crc32(char *s, int length)
++{
++      /* indices */
++      int perByte;
++      int perBit;
++      /* crc polynomial for Ethernet */
++      const unsigned long poly = 0xedb88320;
++      /* crc value - preinitialized to all 1's */
++      unsigned long crc_value = 0xffffffff;
++
++      for ( perByte = 0; perByte < length; perByte ++ ) {
++              unsigned char   c;
++
++              c = *(s++);
++              for ( perBit = 0; perBit < 8; perBit++ ) {
++                      crc_value = (crc_value>>1)^
++                              (((crc_value^c)&0x01)?poly:0);
++                      c >>= 1;
++              }
++      }
++      return  crc_value;
++}
++
++/*
++ .    This sets the internal hardware table to filter out unwanted multicast
++ .    packets before they take up memory.
++ .
++ .    The SMC chip uses a hash table where the high 6 bits of the CRC of
++ .    address are the offset into the table.  If that bit is 1, then the
++ .    multicast packet is accepted.  Otherwise, it's dropped silently.
++ .
++ .    To use the 6 bits as an offset into the table, the high 3 bits are the
++ .    number of the 8 bit register, while the low 3 bits are the bit within
++ .    that register.
++ .
++ .    This routine is based very heavily on the one provided by Peter Cammaert.
++*/
++static void
++smc_setmulticast(unsigned long ioaddr, int count, struct dev_mc_list *addrs)
++{
++      int                     i;
++      unsigned char           multicast_table[ 8 ];
++      struct dev_mc_list      *cur_addr;
++
++      /* table for flipping the order of 3 bits */
++      static unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
++
++      /* start with a table of all zeros: reject all */
++      memset( multicast_table, 0, sizeof( multicast_table ) );
++
++      cur_addr = addrs;
++      for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next  ) {
++              int position;
++
++              /* do we have a pointer here? */
++              if ( !cur_addr )
++                      break;
++              /* make sure this is a multicast address - shouldn't this
++                 be a given if we have it here ? */
++              if ( !( *cur_addr->dmi_addr & 1 ) )
++                      continue;
++
++              /* only use the low order bits */
++              position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f;
++
++              /* do some messy swapping to put the bit in the right spot */
++              multicast_table[invert3[position&7]] |=
++                                      (1<<invert3[(position>>3)&7]);
++
++      }
++      /* now, the table can be loaded into the chipset */
++      SMC_SELECT_BANK( 3 );
++      SMC_SET_MCAST( multicast_table );
++}
++
++/*
++ . This routine will, depending on the values passed to it,
++ . either make it accept multicast packets, go into
++ . promiscuous mode ( for TCPDUMP and cousins ) or accept
++ . a select set of multicast packets
++*/
++static void smc_set_multicast_list(struct net_device *dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      SMC_SELECT_BANK(0);
++      if ( dev->flags & IFF_PROMISC ) {
++              PRINTK2("%s: RCR_PRMS\n", dev->name);
++              lp->rcr_cur_mode |= RCR_PRMS;
++              SMC_SET_RCR( lp->rcr_cur_mode );
++      }
++
++/* BUG?  I never disable promiscuous mode if multicasting was turned on.
++   Now, I turn off promiscuous mode, but I don't do anything to multicasting
++   when promiscuous mode is turned on.
++*/
++
++      /* Here, I am setting this to accept all multicast packets.
++         I don't need to zero the multicast table, because the flag is
++         checked before the table is
++      */
++      else if (dev->flags & IFF_ALLMULTI) {
++              lp->rcr_cur_mode |= RCR_ALMUL;
++              SMC_SET_RCR( lp->rcr_cur_mode );
++              PRINTK2("%s: RCR_ALMUL\n", dev->name);
++      }
++
++      /* We just get all multicast packets even if we only want them
++       . from one source.  This will be changed at some future
++       . point. */
++      else if (dev->mc_count )  {
++              /* support hardware multicasting */
++
++              /* be sure I get rid of flags I might have set */
++              lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
++              SMC_SET_RCR( lp->rcr_cur_mode );
++              /* NOTE: this has to set the bank, so make sure it is the
++                 last thing called.  The bank is set to zero at the top */
++              smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list );
++      } else  {
++              PRINTK2("%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);
++              lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
++              SMC_SET_RCR( lp->rcr_cur_mode );
++
++              /*
++                since I'm disabling all multicast entirely, I need to
++                clear the multicast list
++              */
++              SMC_SELECT_BANK( 3 );
++              SMC_CLEAR_MCAST();
++      }
++}
++
++
++/*
++ * Open and Initialize the board
++ *
++ * Set up everything, reset the card, etc ..
++ */
++static int
++smc_open(struct net_device *dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      unsigned long ioaddr = dev->base_addr;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      /* clear out all the junk that was put here before... */
++      memset(dev->priv, 0, sizeof(struct smc_local));
++
++      // 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->ctl_rspeed = 10;
++#else
++      lp->ctl_autoneg = 1;
++      lp->ctl_rfduplx = 1;
++      lp->ctl_rspeed = 100;
++#endif
++
++      SMC_SELECT_BANK(3);
++      lp->version = SMC_GET_REV() & 0xff;
++
++      /* reset the hardware */
++      smc_reset(dev);
++      smc_enable(dev);
++
++      SMC_SELECT_BANK( 1 );
++      SMC_SET_MAC_ADDR(dev->dev_addr);
++
++      /* Configure the PHY */
++      if (lp->version >= 0x70)
++              smc_phy_configure(dev);
++
++      netif_start_queue(dev);
++      return 0;
++}
++
++/*----------------------------------------------------
++ . smc_close
++ .
++ . this makes the board clean up everything that it can
++ . and not talk to the outside world.   Caused by
++ . an 'ifconfig ethX down'
++ .
++ -----------------------------------------------------*/
++static int
++smc_close(struct net_device *dev)
++{
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      netif_stop_queue(dev);
++
++      /* clear everything */
++      smc_shutdown(dev);
++
++      return 0;
++}
++
++/*------------------------------------------------------------
++ . Get the current statistics.
++ . This may be called with the card open or closed.
++ .-------------------------------------------------------------*/
++static struct net_device_stats *
++smc_query_statistics(struct net_device *dev)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++
++      PRINTK2("%s: %s\n", dev->name, __FUNCTION__);
++
++      return &lp->stats;
++}
++
++/*----------------------------------------------------------------------
++ . smc_findirq
++ .
++ . This routine has a simple purpose -- make the SMC chip generate an
++ . interrupt, so an auto-detect routine can detect it, and find the IRQ,
++ ------------------------------------------------------------------------
++*/
++int __init
++smc_findirq( unsigned long ioaddr )
++{
++      int timeout = 20;
++      unsigned long cookie;
++
++      PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);
++
++      cookie = probe_irq_on();
++
++      /*
++       * What I try to do here is trigger an ALLOC_INT. This is done
++       * by allocating a small chunk of memory, which will give an interrupt
++       * when done.
++       */
++
++      /* enable ALLOCation interrupts ONLY */
++      SMC_SELECT_BANK(2);
++      SMC_SET_INT_MASK( IM_ALLOC_INT );
++
++      /*
++       . Allocate 512 bytes of memory.  Note that the chip was just
++       . reset so all the memory is available
++      */
++      SMC_SET_MMU_CMD( MC_ALLOC | 1 );
++
++      /*
++       . Wait until positive that the interrupt has been generated
++      */
++      do {
++              int int_status;
++              udelay(10);
++              int_status = SMC_GET_INT();
++              if (int_status & IM_ALLOC_INT)
++                      break;          /* got the interrupt */
++      } while (--timeout);
++
++      /* there is really nothing that I can do here if timeout fails,
++         as autoirq_report will return a 0 anyway, which is what I
++         want in this case.   Plus, the clean up is needed in both
++         cases.  */
++
++      /* and disable all interrupts again */
++      SMC_SET_INT_MASK( 0 );
++
++      /* and return what I found */
++      return probe_irq_off(cookie);
++}
++
++/*----------------------------------------------------------------------
++ . Function: smc_probe( unsigned long ioaddr )
++ .
++ . Purpose:
++ .    Tests to see if a given ioaddr points to an SMC91x chip.
++ .    Returns a 0 on success
++ .
++ . Algorithm:
++ .    (1) see if the high byte of BANK_SELECT is 0x33
++ .    (2) compare the ioaddr with the base register's address
++ .    (3) see if I recognize the chip ID in the appropriate register
++ .
++ .---------------------------------------------------------------------
++ */
++/*---------------------------------------------------------------
++ . Here I do typical initialization tasks.
++ .
++ . o  Initialize the structure if needed
++ . o  print out my vanity message if not done so already
++ . o  print out what type of hardware is detected
++ . o  print out the ethernet address
++ . o  find the IRQ
++ . o  set up my private data
++ . o  configure the dev structure with my subroutines
++ . o  actually GRAB the irq.
++ . o  GRAB the region
++ .-----------------------------------------------------------------
++*/
++static int __init
++smc_probe(struct net_device *dev, unsigned long ioaddr)
++{
++      struct smc_local *lp = (struct smc_local *)dev->priv;
++      static int version_printed = 0;
++      int i, retval;
++      unsigned int val, revision_register;
++      const char *version_string;
++
++      PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);
++
++      /* Grab the region so that no one else tries to probe our ioports. */
++      if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name))
++              return -EBUSY;
++
++      /* First, see if the high byte is 0x33 */
++      val = SMC_CURRENT_BANK();
++      PRINTK2("%s: bank signature probe returned 0x%04x\n", CARDNAME, val);
++      if ( (val & 0xFF00) != 0x3300 ) {
++              if ( (val & 0xFF) == 0x33 ) {
++                      printk( KERN_WARNING
++                              "%s: Detected possible byte-swapped interface"
++                              " at IOADDR 0x%lx\n", CARDNAME, ioaddr);
++              }
++              retval = -ENODEV;
++              goto err_out;
++      }
++
++      /* The above MIGHT indicate a device, but I need to write to further
++              test this.  */
++      SMC_SELECT_BANK(0);
++      val = SMC_CURRENT_BANK();
++      if ( (val & 0xFF00 ) != 0x3300 ) {
++              retval = -ENODEV;
++              goto err_out;
++      }
++
++      /* well, we've already written once, so hopefully another time won't
++         hurt.  This time, I need to switch the bank register to bank 1,
++         so I can access the base address register */
++      SMC_SELECT_BANK(1);
++      val = SMC_GET_BASE();
++      val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
++      if ( (ioaddr & ((PAGE_SIZE-1)<<SMC_IO_SHIFT)) != val ) {
++              printk( "%s: IOADDR %lx doesn't match configuration (%x).\n",
++                      CARDNAME, ioaddr, val );
++      }
++
++      /*  check if the revision register is something that I recognize.
++          These might need to be added to later, as future revisions
++          could be added.  */
++      SMC_SELECT_BANK(3);
++      revision_register = SMC_GET_REV();
++      PRINTK2("%s: revision = 0x%04x\n", CARDNAME, revision_register);
++      version_string = chip_ids[ (revision_register >> 4) & 0xF];
++      if (!version_string || (revision_register & 0xff00) != 0x3300) {
++              /* I don't recognize this chip, so... */
++              printk( "%s: IO 0x%lx: Unrecognized revision register 0x%04x"
++                      ", Contact author.\n", CARDNAME,
++                      ioaddr, revision_register);
++
++              retval = -ENODEV;
++              goto err_out;
++      }
++
++      /* At this point I'll assume that the chip is an SMC91x. */
++      if (version_printed++ == 0)
++              printk("%s", version);
++
++      /* set the private data to zero by default */
++      memset(lp, 0, sizeof(struct smc_local));
++
++      /* fill in some of the fields */
++      dev->base_addr = ioaddr;
++      lp->version = revision_register & 0xff;
++
++      /* Get the MAC address */
++      SMC_SELECT_BANK( 1 );
++      SMC_GET_MAC_ADDR(dev->dev_addr);
++
++      /* now, reset the chip, and put it into a known state */
++      smc_reset( dev );
++
++      /*
++       . If dev->irq is 0, then the device has to be banged on to see
++       . what the IRQ is.
++       .
++       . This banging doesn't always detect the IRQ, for unknown reasons.
++       . a workaround is to reset the chip and try again.
++       .
++       . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
++       . be what is requested on the command line.   I don't do that, mostly
++       . because the card that I have uses a non-standard method of accessing
++       . the IRQs, and because this _should_ work in most configurations.
++       .
++       . Specifying an IRQ is done with the assumption that the user knows
++       . what (s)he is doing.  No checking is done!!!!
++       .
++      */
++      if ( dev->irq < 1 ) {
++              int     trials;
++
++              trials = 3;
++              while ( trials-- ) {
++                      dev->irq = smc_findirq( ioaddr );
++                      if ( dev->irq )
++                              break;
++                      /* kick the card and try again */
++                      smc_reset( dev );
++              }
++      }
++      if (dev->irq == 0 ) {
++              printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
++                      dev->name);
++              retval = -ENODEV;
++              goto err_out;
++      }
++      dev->irq = irq_cannonicalize(dev->irq);
++
++      /* now, print out the card info, in a short format.. */
++      printk( "%s: %s (rev %d) at %#lx IRQ %d%s%s\n",
++              dev->name, version_string, revision_register & 0x0f,
++              ioaddr, dev->irq, nowait ? " [nowait]" : "",
++              THROTTLE_TX_PKTS ? " [throttle_tx]" : "" );
++
++      /* Print the Ethernet address */
++      printk("%s: Ethernet addr: ", dev->name);
++      for (i = 0; i < 5; i++)
++              printk("%2.2x:", dev->dev_addr[i] );
++      printk("%2.2x\n", dev->dev_addr[5] );
++
++      /* Fill in the fields of the device structure with ethernet values. */
++      ether_setup(dev);
++
++      /* Grab the IRQ */
++              retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
++              if (retval) {
++              goto err_out;
++              }
++
++      dev->open                       = smc_open;
++      dev->stop                       = smc_close;
++      dev->hard_start_xmit            = smc_hard_start_xmit;
++      dev->tx_timeout                 = smc_timeout;
++      dev->watchdog_timeo             = HZ/10;
++      dev->get_stats                  = smc_query_statistics;
++      dev->set_multicast_list         = smc_set_multicast_list;
++
++      return 0;
++
++err_out:
++      release_region(ioaddr, SMC_IO_EXTENT);
++      return retval;
++}
++
++/*-------------------------------------------------------------------------
++ |
++ | smc_init( void )
++ |   Input parameters:
++ |    dev->base_addr == 0, try to find all possible locations
++ |    dev->base_addr > 0x1ff, this is the address to check
++ |    dev->base_addr == <anything else>, return failure code
++ |
++ |   Output:
++ |    0 --> there is a device
++ |    anything else, error
++ |
++ ---------------------------------------------------------------------------
++*/
++static struct net_device *global_dev = NULL;  /* needs to be fixed */
++
++#ifdef CONFIG_PM
++
++static int smc_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      unsigned long ioaddr = global_dev->base_addr;
++      struct smc_local *lp = (struct smc_local *)global_dev->priv;
++
++      switch (rqst) {
++      case PM_SUSPEND:
++              smc_shutdown(global_dev);
++              break;
++      case PM_RESUME:
++              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);
++              break;
++      }
++      return 0;
++}
++
++#endif
++
++static int __init
++smc_init(void)
++{
++      int ret;
++
++      PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__);
++
++#ifdef MODULE
++      if (io == -1)
++              printk( KERN_WARNING 
++                      "%s: You shouldn't use auto-probing with insmod!\n",
++                      CARDNAME );
++#endif
++
++      if (global_dev) {
++              printk("%s: already initialized.\n", CARDNAME);
++              return -EBUSY;
++      }
++
++      global_dev = init_etherdev(0, sizeof(struct smc_local));
++      if (!global_dev) {
++              printk("%s: could not allocate device.\n", CARDNAME);
++              return -ENOMEM;
++      }
++      SET_MODULE_OWNER(global_dev);
++
++      /* copy the parameters from insmod into the device structure */
++      if (io != -1)
++              global_dev->base_addr   = io;
++      if (irq != -1)
++              global_dev->irq = irq;
++
++#ifdef CONFIG_ISA
++      /*  try a specific location */
++      if (global_dev->base_addr > 0x1ff)
++              ret = smc_probe(global_dev, global_dev->base_addr);
++      else if (global_dev->base_addr != 0)
++              ret = -ENXIO;
++      else {
++              int i;
++
++              /* check every ethernet address */
++              for (i = 0; smc_portlist[i]; i++) {
++                      ret = smc_probe(global_dev, smc_portlist[i]);
++                      if (ret == 0)
++                              break;
++              }
++      }
++#elif defined(CONFIG_ARCH_LUBBOCK)
++      {
++              int ioaddr = LUBBOCK_ETH_VIRT + (0x300 << 2);
++              volatile int *attaddr = (int *)(LUBBOCK_ETH_VIRT + 0x100000);
++              unsigned long flags;
++
++              /* first reset, then enable the device. Sequence is critical */
++              local_irq_save(flags);
++              attaddr[ECOR] |= ECOR_RESET;
++              udelay(100);
++              attaddr[ECOR] &= ~ECOR_RESET;
++              attaddr[ECOR] |= ECOR_ENABLE;
++
++              /* force 16-bit mode */
++              attaddr[ECSR] &= ~ECSR_IOIS8;
++              mdelay(1);
++              local_irq_restore(flags);
++
++              global_dev->irq = LUBBOCK_ETH_IRQ;
++              ret = smc_probe(global_dev, ioaddr);
++      }
++#elif defined(CONFIG_ARCH_PXA_IDP)
++      {
++              int ioaddr = IDP_ETH_BASE + 0x300;
++              global_dev->irq = SMC_IRQ;
++              ret = smc_probe(global_dev, ioaddr);
++      }
++#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) {
++              printk(KERN_WARNING"%s: SMC91X_BASE_ADDR not set!\n", CARDNAME);
++              ret = -ENXIO;
++      } else {
++              void *ioaddr = ioremap(global_dev->base_addr, SMC_IO_EXTENT);
++              ret = smc_probe(global_dev, (unsigned long)ioaddr);
++              if (ret != 0)
++                      iounmap(ioaddr);
++      }
++#endif
++
++#ifdef SMC_USE_PXA_DMA
++      if (ret == 0) {
++              int dma = pxa_request_dma(global_dev->name, DMA_PRIO_LOW,
++                                        smc_pxa_dma_irq, NULL);
++              if (dma >= 0) {
++                      global_dev->dma = dma;
++                      PRINTK("%s: using DMA channel %d\n", global_dev->name, dma);
++              } else {
++                      global_dev->dma = -1;
++              }
++      }
++#endif
++
++#ifdef CONFIG_PM
++      if (ret == 0) {
++              struct smc_local *lp = (struct smc_local *)global_dev->priv;
++              lp->pm = pm_register(PM_SYS_UNKNOWN, 0x73393178, smc_pm_callback);
++      }
++#endif
++
++      if (ret != 0) {
++              printk("%s: not found.\n", CARDNAME);
++              kfree(global_dev->priv);
++              unregister_netdev(global_dev);
++              kfree(global_dev);
++      }
++
++      return ret;
++}
++
++static void __exit
++smc_cleanup(void)
++{
++      unregister_netdev(global_dev);
++#ifdef CONFIG_PM
++      {
++              struct smc_local *lp = (struct smc_local *)global_dev->priv;
++              pm_unregister(lp->pm);
++      }
++#endif
++      free_irq(global_dev->irq, global_dev);
++      release_region(global_dev->base_addr, SMC_IO_EXTENT);
++
++#ifndef CONFIG_ISA
++      iounmap((void *)global_dev->base_addr);
++#endif
++
++      kfree(global_dev);
++      global_dev = NULL;
++}
++
++module_init(smc_init);
++module_exit(smc_cleanup);
++
+--- /dev/null
++++ linux-2.4.27/drivers/net/smc91x.h
+@@ -0,0 +1,835 @@
++/*------------------------------------------------------------------------
++ . smc91x.h - macros for SMSC's 91C9x/91C1xx single-chip Ethernet device.
++ .
++ . Copyright (C) 1996 by Erik Stahlman
++ . Copyright (C) 2001 Standard Microsystems Corporation
++ .    Developed by Simple Network Magic Corporation
++ . Copyright (C) 2003 Monta Vista Software, Inc.
++ .    Unified SMC91x driver by Nicolas Pitre
++ .
++ . 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
++ .
++ . Information contained in this file was obtained from the LAN91C111
++ . manual from SMC.  To get a copy, if you really want one, you can find
++ . information under www.smsc.com.
++ .
++ . Authors
++ .    Erik Stahlman           <erik@vt.edu>
++ .    Daris A Nevil           <dnevil@snmc.com>
++ .    Nicolas Pitre           <nico@cam.org>
++ .
++ ---------------------------------------------------------------------------*/
++#ifndef _SMC91X_H_
++#define _SMC91X_H_
++
++
++/*
++ * Define your architecture specific configuration parameters here.
++ */
++
++#if   defined(CONFIG_SA1100_GRAPHICSCLIENT) || \
++      defined(CONFIG_SA1100_PFS168) || \
++      defined(CONFIG_SA1100_FLEXANET) || \
++      defined(CONFIG_SA1100_GRAPHICSMASTER) || \
++      defined(CONFIG_ARCH_LUBBOCK)
++
++/* We can only do 16-bit reads and writes in the static memory space. */
++#define SMC_CAN_USE_8BIT      0
++#define SMC_CAN_USE_16BIT     1
++#define SMC_CAN_USE_32BIT     0
++#define SMC_NOWAIT            1
++
++/* The first two address lines aren't connected... */
++#define SMC_IO_SHIFT          2
++
++#define SMC_inw(a, r)         readw((a) + (r))
++#define SMC_outw(v, a, r)     writew(v, (a) + (r))
++#define SMC_insw(a, r, p, l)  insw((a) + (r), p, l)
++#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
++
++#ifdef CONFIG_ARCH_LUBBOCK
++#define SMC_IOADDR            LUBBOCK_ETH_PHYS
++#endif
++
++#elif defined(CONFIG_ARCH_MAINSTONE) || defined(CONFIG_ARCH_PXA_IDP) || defined(CONFIG_ARCH_RAMSES)
++
++#ifdef CONFIG_ARCH_MAINSTONE
++#include <asm/arch/mainstone.h>
++#define SMC_IOADDR            (MST_ETH_PHYS + 0x300)
++#define SMC_IRQ                       MAINSTONE_IRQ(3)
++
++#elif CONFIG_ARCH_PXA_IDP
++#include <asm/arch/idp.h>
++#define SMC_IOADDR            (IDP_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
++#define SMC_CAN_USE_16BIT     1
++#define SMC_CAN_USE_32BIT     1
++#define SMC_IO_SHIFT          0
++#define SMC_NOWAIT            1
++#define SMC_USE_PXA_DMA               1
++
++#define SMC_inb(a, r)         readb((a) + (r))
++#define SMC_inw(a, r)         readw((a) + (r))
++#define SMC_inl(a, r)         readl((a) + (r))
++#define SMC_outb(v, a, r)     writeb(v, (a) + (r))
++#define SMC_outl(v, a, r)     writel(v, (a) + (r))
++#define SMC_insl(a, r, p, l)  insl((a) + (r), p, l)
++#define SMC_outsl(a, r, p, l) outsl((a) + (r), p, l)
++
++/* We actually can't write halfwords properly if not word aligned */
++static inline void
++SMC_outw(u16 val, unsigned long ioaddr, int reg)
++{
++      if (reg & 2) {
++              unsigned int v = val << 16;
++              v |= readl(ioaddr + (reg & ~2)) & 0xffff;
++              writel(v, ioaddr + (reg & ~2));
++      } else {
++              writew(val, ioaddr + reg);
++      }
++}
++
++#elif defined(CONFIG_ISA)
++
++#define SMC_CAN_USE_8BIT      1
++#define SMC_CAN_USE_16BIT     1
++#define SMC_CAN_USE_32BIT     0
++
++#define SMC_inb(a, r)         inb((a) + (r))
++#define SMC_inw(a, r)         inw((a) + (r))
++#define SMC_outb(v, a, r)     outb(v, (a) + (r))
++#define SMC_outw(v, a, r)     outw(v, (a) + (r))
++#define SMC_insw(a, r, p, l)  insw((a) + (r), p, l)
++#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
++
++#endif
++
++
++#ifdef SMC_USE_PXA_DMA
++/*
++ * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is
++ * always happening in irq context so no need to worry about races.  TX is
++ * different and probably not worth it for that reason, and not as critical
++ * as RX which can overrun memory and lose packets.
++ */
++#include <linux/pci.h>
++#include <asm/dma.h>
++
++#ifdef SMC_insl
++#undef SMC_insl
++#define SMC_insl(a, r, p, l)  smc_pxa_dma_insl(a, r, dev->dma, p, l)
++static inline void
++smc_pxa_dma_insl(u_long ioaddr, int reg, int dma, u_char *buf, int len)
++{
++      dma_addr_t dmabuf;
++
++      /* fallback if no DMA available */
++      if (dma == -1) {
++              insl(ioaddr + reg, buf, len);
++              return;
++      }
++
++      /* 64 bit alignment is required for memory to memory DMA */
++      if ((long)buf & 4) {
++              *((u32 *)buf)++ = SMC_inl(ioaddr, reg);
++              len--;
++      }
++
++      len *= 4;
++      dmabuf = pci_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE);
++      DCSR(dma) = DCSR_NODESC;
++      DTADR(dma) = dmabuf;
++      DSADR(dma) = SMC_IOADDR + reg;
++      DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
++                   DCMD_WIDTH4 | (DCMD_LENGTH & len));
++      DCSR(dma) = DCSR_NODESC | DCSR_RUN;
++      while (!(DCSR(dma) & DCSR_STOPSTATE));
++      DCSR(dma) = 0;
++      pci_unmap_single(NULL, dmabuf,len, PCI_DMA_FROMDEVICE);
++}
++#endif
++
++#ifdef SMC_insw
++#undef SMC_insw
++#define SMC_insw(a, r, p, l)  smc_pxa_dma_insw(a, r, dev->dma, p, l)
++static inline void
++smc_pxa_dma_insw(u_long ioaddr, int reg, int dma, u_char *buf, int len)
++{
++      dma_addr_t dmabuf;
++
++      /* fallback if no DMA available */
++      if (dma == -1) {
++              insw(ioaddr + reg, buf, len);
++              return;
++      }
++
++      /* 64 bit alignment is required for memory to memory DMA */
++      while ((long)buf & 6) {
++              *((u16 *)buf)++ = SMC_inw(ioaddr, reg);
++              len--;
++      }
++
++      len *= 2;
++      dmabuf = pci_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE);
++      DCSR(dma) = DCSR_NODESC;
++      DTADR(dma) = dmabuf;
++      DSADR(dma) = SMC_IOADDR + reg;
++      DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
++                   DCMD_WIDTH2 | (DCMD_LENGTH & len));
++      DCSR(dma) = DCSR_NODESC | DCSR_RUN;
++      while (!(DCSR(dma) & DCSR_STOPSTATE));
++      DCSR(dma) = 0;
++      pci_unmap_single(NULL, dmabuf,len, PCI_DMA_FROMDEVICE);
++}
++#endif
++
++static void
++smc_pxa_dma_irq(int dma, void *dummy, struct pt_regs *regs)
++{
++      DCSR(dma) = 0;
++}
++#endif  /* SMC_USE_PXA_DMA */
++
++
++/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */
++#ifndef SMC_IO_SHIFT
++#define SMC_IO_SHIFT  0
++#endif
++#define SMC_IO_EXTENT (16 << SMC_IO_SHIFT)
++
++
++/*
++ . Bank Select Register:
++ .
++ .            yyyy yyyy 0000 00xx
++ .            xx              = bank number
++ .            yyyy yyyy       = 0x33, for identification purposes.
++*/
++#define BANK_SELECT           (14 << SMC_IO_SHIFT)
++
++
++// Transmit Control Register
++/* BANK 0  */
++#define TCR_REG       SMC_REG(0x0000, 0)
++#define TCR_ENABLE    0x0001  // When 1 we can transmit
++#define TCR_LOOP      0x0002  // Controls output pin LBK
++#define TCR_FORCOL    0x0004  // When 1 will force a collision
++#define TCR_PAD_EN    0x0080  // When 1 will pad tx frames < 64 bytes w/0
++#define TCR_NOCRC     0x0100  // When 1 will not append CRC to tx frames
++#define TCR_MON_CSN   0x0400  // When 1 tx monitors carrier
++#define TCR_FDUPLX            0x0800  // When 1 enables full duplex operation
++#define TCR_STP_SQET  0x1000  // When 1 stops tx if Signal Quality Error
++#define TCR_EPH_LOOP  0x2000  // When 1 enables EPH block loopback
++#define TCR_SWFDUP    0x8000  // When 1 enables Switched Full Duplex mode
++
++#define TCR_CLEAR     0       /* do NOTHING */
++/* the default settings for the TCR register : */
++#define TCR_DEFAULT   (TCR_ENABLE | TCR_PAD_EN)
++
++
++// EPH Status Register
++/* BANK 0  */
++#define EPH_STATUS_REG        SMC_REG(0x0002, 0)
++#define ES_TX_SUC     0x0001  // Last TX was successful
++#define ES_SNGL_COL   0x0002  // Single collision detected for last tx
++#define ES_MUL_COL    0x0004  // Multiple collisions detected for last tx
++#define ES_LTX_MULT   0x0008  // Last tx was a multicast
++#define ES_16COL      0x0010  // 16 Collisions Reached
++#define ES_SQET               0x0020  // Signal Quality Error Test
++#define ES_LTXBRD     0x0040  // Last tx was a broadcast
++#define ES_TXDEFR     0x0080  // Transmit Deferred
++#define ES_LATCOL     0x0200  // Late collision detected on last tx
++#define ES_LOSTCARR   0x0400  // Lost Carrier Sense
++#define ES_EXC_DEF    0x0800  // Excessive Deferral
++#define ES_CTR_ROL    0x1000  // Counter Roll Over indication
++#define ES_LINK_OK    0x4000  // Driven by inverted value of nLNK pin
++#define ES_TXUNRN     0x8000  // Tx Underrun
++
++
++// Receive Control Register
++/* BANK 0  */
++#define RCR_REG               SMC_REG(0x0004, 0)
++#define RCR_RX_ABORT  0x0001  // Set if a rx frame was aborted
++#define RCR_PRMS      0x0002  // Enable promiscuous mode
++#define RCR_ALMUL     0x0004  // When set accepts all multicast frames
++#define RCR_RXEN      0x0100  // IFF this is set, we can receive packets
++#define RCR_STRIP_CRC 0x0200  // When set strips CRC from rx packets
++#define RCR_ABORT_ENB 0x0200  // When set will abort rx on collision
++#define RCR_FILT_CAR  0x0400  // When set filters leading 12 bit s of carrier
++#define RCR_SOFTRST   0x8000  // resets the chip
++
++/* the normal settings for the RCR register : */
++#define RCR_DEFAULT   (RCR_STRIP_CRC | RCR_RXEN)
++#define RCR_CLEAR     0x0     // set it to a base state
++
++
++// Counter Register
++/* BANK 0  */
++#define COUNTER_REG   SMC_REG(0x0006, 0)
++
++
++// Memory Information Register
++/* BANK 0  */
++#define MIR_REG               SMC_REG(0x0008, 0)
++
++
++// Receive/Phy Control Register
++/* BANK 0  */
++#define RPC_REG               SMC_REG(0x000A, 0)
++#define RPC_SPEED     0x2000  // When 1 PHY is in 100Mbps mode.
++#define RPC_DPLX      0x1000  // When 1 PHY is in Full-Duplex Mode
++#define RPC_ANEG      0x0800  // When 1 PHY is in Auto-Negotiate Mode
++#define RPC_LSXA_SHFT 5       // Bits to shift LS2A,LS1A,LS0A to lsb
++#define RPC_LSXB_SHFT 2       // Bits to get LS2B,LS1B,LS0B to lsb
++#define RPC_LED_100_10        (0x00)  // LED = 100Mbps OR's with 10Mbps link detect
++#define RPC_LED_RES   (0x01)  // LED = Reserved
++#define RPC_LED_10    (0x02)  // LED = 10Mbps link detect
++#define RPC_LED_FD    (0x03)  // LED = Full Duplex Mode
++#define RPC_LED_TX_RX (0x04)  // LED = TX or RX packet occurred
++#define RPC_LED_100   (0x05)  // LED = 100Mbps link dectect
++#define RPC_LED_TX    (0x06)  // LED = TX packet occurred
++#define RPC_LED_RX    (0x07)  // LED = RX packet occurred
++#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
++
++
++/* Bank 0 0x0C is reserved */
++
++// Bank Select Register
++/* All Banks */
++#define BSR_REG               0x000E
++
++
++// Configuration Reg
++/* BANK 1 */
++#define CONFIG_REG    SMC_REG(0x0000, 1)
++#define CONFIG_EXT_PHY        0x0200  // 1=external MII, 0=internal Phy
++#define CONFIG_GPCNTRL        0x0400  // Inverse value drives pin nCNTRL
++#define CONFIG_NO_WAIT        0x1000  // When 1 no extra wait states on ISA bus
++#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
++
++// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
++#define CONFIG_DEFAULT        (CONFIG_EPH_POWER_EN)
++
++
++// Base Address Register
++/* BANK 1 */
++#define BASE_REG      SMC_REG(0x0002, 1)
++
++
++// Individual Address Registers
++/* BANK 1 */
++#define ADDR0_REG     SMC_REG(0x0004, 1)
++#define ADDR1_REG     SMC_REG(0x0006, 1)
++#define ADDR2_REG     SMC_REG(0x0008, 1)
++
++
++// General Purpose Register
++/* BANK 1 */
++#define GP_REG                SMC_REG(0x000A, 1)
++
++
++// Control Register
++/* BANK 1 */
++#define CTL_REG               SMC_REG(0x000C, 1)
++#define CTL_RCV_BAD   0x4000 // When 1 bad CRC packets are received
++#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
++#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt
++#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt
++#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt
++#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
++#define CTL_RELOAD    0x0002 // When set reads EEPROM into registers
++#define CTL_STORE     0x0001 // When set stores registers into EEPROM
++
++
++// MMU Command Register
++/* BANK 2 */
++#define MMU_CMD_REG   SMC_REG(0x0000, 2)
++#define MC_BUSY               1       // When 1 the last release has not completed
++#define MC_NOP                (0<<5)  // No Op
++#define MC_ALLOC      (1<<5)  // OR with number of 256 byte packets
++#define MC_RESET      (2<<5)  // Reset MMU to initial state
++#define MC_REMOVE     (3<<5)  // Remove the current rx packet
++#define MC_RELEASE    (4<<5)  // Remove and release the current rx packet
++#define MC_FREEPKT    (5<<5)  // Release packet in PNR register
++#define MC_ENQUEUE    (6<<5)  // Enqueue the packet for transmit
++#define MC_RSTTXFIFO  (7<<5)  // Reset the TX FIFOs
++
++
++// Packet Number Register
++/* BANK 2 */
++#define PN_REG                SMC_REG(0x0002, 2)
++
++
++// Allocation Result Register
++/* BANK 2 */
++#define AR_REG                SMC_REG(0x0003, 2)
++#define AR_FAILED     0x80    // Alocation Failed
++
++
++// TX FIFO Ports Register
++/* BANK 2 */
++#define TXFIFO_REG    SMC_REG(0x0004, 2)
++#define TXFIFO_TEMPTY 0x80    // TX FIFO Empty
++
++// RX FIFO Ports Register
++/* BANK 2 */
++#define RXFIFO_REG    SMC_REG(0x0005, 2)
++#define RXFIFO_REMPTY 0x80    // RX FIFO Empty
++
++#define FIFO_REG      SMC_REG(0x0004, 2)
++
++// Pointer Register
++/* BANK 2 */
++#define PTR_REG               SMC_REG(0x0006, 2)
++#define PTR_RCV               0x8000 // 1=Receive area, 0=Transmit area
++#define PTR_AUTOINC   0x4000 // Auto increment the pointer on each access
++#define PTR_READ      0x2000 // When 1 the operation is a read
++
++
++// Data Register
++/* BANK 2 */
++#define DATA_REG      SMC_REG(0x0008, 2)
++
++
++// Interrupt Status/Acknowledge Register
++/* BANK 2 */
++#define INT_REG               SMC_REG(0x000C, 2)
++
++
++// Interrupt Mask Register
++/* BANK 2 */
++#define IM_REG                SMC_REG(0x000D, 2)
++#define IM_MDINT      0x80 // PHY MI Register 18 Interrupt
++#define IM_ERCV_INT   0x40 // Early Receive Interrupt
++#define IM_EPH_INT    0x20 // Set by Ethernet Protocol Handler section
++#define IM_RX_OVRN_INT        0x10 // Set by Receiver Overruns
++#define IM_ALLOC_INT  0x08 // Set when allocation request is completed
++#define IM_TX_EMPTY_INT       0x04 // Set if the TX FIFO goes empty
++#define IM_TX_INT     0x02 // Transmit Interrupt
++#define IM_RCV_INT    0x01 // Receive Interrupt
++
++
++// Multicast Table Registers
++/* BANK 3 */
++#define MCAST_REG1    SMC_REG(0x0000, 3)
++#define MCAST_REG2    SMC_REG(0x0002, 3)
++#define MCAST_REG3    SMC_REG(0x0004, 3)
++#define MCAST_REG4    SMC_REG(0x0006, 3)
++
++
++// Management Interface Register (MII)
++/* BANK 3 */
++#define MII_REG               SMC_REG(0x0008, 3)
++#define MII_MSK_CRS100        0x4000 // Disables CRS100 detection during tx half dup
++#define MII_MDOE      0x0008 // MII Output Enable
++#define MII_MCLK      0x0004 // MII Clock, pin MDCLK
++#define MII_MDI               0x0002 // MII Input, pin MDI
++#define MII_MDO               0x0001 // MII Output, pin MDO
++
++
++// Revision Register
++/* BANK 3 */
++/* ( hi: chip id   low: rev # ) */
++#define REV_REG               SMC_REG(0x000A, 3)
++
++
++// Early RCV Register
++/* BANK 3 */
++/* this is NOT on SMC9192 */
++#define ERCV_REG      SMC_REG(0x000C, 3)
++#define ERCV_RCV_DISCRD       0x0080 // When 1 discards a packet being received
++#define ERCV_THRESHOLD        0x001F // ERCV Threshold Mask
++
++
++// External Register
++/* BANK 7 */
++#define EXT_REG               SMC_REG(0x0000, 7)
++
++
++#define CHIP_9192     3
++#define CHIP_9194     4
++#define CHIP_9195     5
++#define CHIP_9196     6
++#define CHIP_91100    7
++#define CHIP_91100FD  8
++#define CHIP_91111FD  9
++
++static const char * chip_ids[ 16 ] =  {
++      NULL, NULL, NULL,
++      /* 3 */ "SMC91C90/91C92",
++      /* 4 */ "SMC91C94",
++      /* 5 */ "SMC91C95",
++      /* 6 */ "SMC91C96",
++      /* 7 */ "SMC91C100",
++      /* 8 */ "SMC91C100FD",
++      /* 9 */ "SMC91C11xFD",
++      NULL, NULL, NULL,
++      NULL, NULL, NULL};
++
++
++/*
++ . Transmit status bits
++*/
++#define TS_SUCCESS 0x0001
++#define TS_LOSTCAR 0x0400
++#define TS_LATCOL  0x0200
++#define TS_16COL   0x0010
++
++/*
++ . Receive status bits
++*/
++#define RS_ALGNERR    0x8000
++#define RS_BRODCAST   0x4000
++#define RS_BADCRC     0x2000
++#define RS_ODDFRAME   0x1000
++#define RS_TOOLONG    0x0800
++#define RS_TOOSHORT   0x0400
++#define RS_MULTICAST  0x0001
++#define RS_ERRORS     (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
++
++
++// PHY Types
++enum {
++      PHY_LAN83C183 = 1,      // LAN91C111 Internal PHY
++      PHY_LAN83C180
++};
++
++
++// PHY Register Addresses (LAN91C111 Internal PHY)
++
++// PHY Control Register
++#define PHY_CNTL_REG          0x00
++#define PHY_CNTL_RST          0x8000  // 1=PHY Reset
++#define PHY_CNTL_LPBK         0x4000  // 1=PHY Loopback
++#define PHY_CNTL_SPEED                0x2000  // 1=100Mbps, 0=10Mpbs
++#define PHY_CNTL_ANEG_EN      0x1000  // 1=Enable Auto negotiation
++#define PHY_CNTL_PDN          0x0800  // 1=PHY Power Down mode
++#define PHY_CNTL_MII_DIS      0x0400  // 1=MII 4 bit interface disabled
++#define PHY_CNTL_ANEG_RST     0x0200  // 1=Reset Auto negotiate
++#define PHY_CNTL_DPLX         0x0100  // 1=Full Duplex, 0=Half Duplex
++#define PHY_CNTL_COLTST               0x0080  // 1= MII Colision Test
++
++// PHY Status Register
++#define PHY_STAT_REG          0x01
++#define PHY_STAT_CAP_T4               0x8000  // 1=100Base-T4 capable
++#define PHY_STAT_CAP_TXF      0x4000  // 1=100Base-X full duplex capable
++#define PHY_STAT_CAP_TXH      0x2000  // 1=100Base-X half duplex capable
++#define PHY_STAT_CAP_TF               0x1000  // 1=10Mbps full duplex capable
++#define PHY_STAT_CAP_TH               0x0800  // 1=10Mbps half duplex capable
++#define PHY_STAT_CAP_SUPR     0x0040  // 1=recv mgmt frames with not preamble
++#define PHY_STAT_ANEG_ACK     0x0020  // 1=ANEG has completed
++#define PHY_STAT_REM_FLT      0x0010  // 1=Remote Fault detected
++#define PHY_STAT_CAP_ANEG     0x0008  // 1=Auto negotiate capable
++#define PHY_STAT_LINK         0x0004  // 1=valid link
++#define PHY_STAT_JAB          0x0002  // 1=10Mbps jabber condition
++#define PHY_STAT_EXREG                0x0001  // 1=extended registers implemented
++
++// PHY Identifier Registers
++#define PHY_ID1_REG           0x02    // PHY Identifier 1
++#define PHY_ID2_REG           0x03    // PHY Identifier 2
++
++// PHY Auto-Negotiation Advertisement Register
++#define PHY_AD_REG            0x04
++#define PHY_AD_NP             0x8000  // 1=PHY requests exchange of Next Page
++#define PHY_AD_ACK            0x4000  // 1=got link code word from remote
++#define PHY_AD_RF             0x2000  // 1=advertise remote fault
++#define PHY_AD_T4             0x0200  // 1=PHY is capable of 100Base-T4
++#define PHY_AD_TX_FDX         0x0100  // 1=PHY is capable of 100Base-TX FDPLX
++#define PHY_AD_TX_HDX         0x0080  // 1=PHY is capable of 100Base-TX HDPLX
++#define PHY_AD_10_FDX         0x0040  // 1=PHY is capable of 10Base-T FDPLX
++#define PHY_AD_10_HDX         0x0020  // 1=PHY is capable of 10Base-T HDPLX
++#define PHY_AD_CSMA           0x0001  // 1=PHY is capable of 802.3 CMSA
++
++// PHY Auto-negotiation Remote End Capability Register
++#define PHY_RMT_REG           0x05
++// Uses same bit definitions as PHY_AD_REG
++
++// PHY Configuration Register 1
++#define PHY_CFG1_REG          0x10
++#define PHY_CFG1_LNKDIS               0x8000  // 1=Rx Link Detect Function disabled
++#define PHY_CFG1_XMTDIS               0x4000  // 1=TP Transmitter Disabled
++#define PHY_CFG1_XMTPDN               0x2000  // 1=TP Transmitter Powered Down
++#define PHY_CFG1_BYPSCR               0x0400  // 1=Bypass scrambler/descrambler
++#define PHY_CFG1_UNSCDS               0x0200  // 1=Unscramble Idle Reception Disable
++#define PHY_CFG1_EQLZR                0x0100  // 1=Rx Equalizer Disabled
++#define PHY_CFG1_CABLE                0x0080  // 1=STP(150ohm), 0=UTP(100ohm)
++#define PHY_CFG1_RLVL0                0x0040  // 1=Rx Squelch level reduced by 4.5db
++#define PHY_CFG1_TLVL_SHIFT   2       // Transmit Output Level Adjust
++#define PHY_CFG1_TLVL_MASK    0x003C
++#define PHY_CFG1_TRF_MASK     0x0003  // Transmitter Rise/Fall time
++
++
++// PHY Configuration Register 2
++#define PHY_CFG2_REG          0x11
++#define PHY_CFG2_APOLDIS      0x0020  // 1=Auto Polarity Correction disabled
++#define PHY_CFG2_JABDIS               0x0010  // 1=Jabber disabled
++#define PHY_CFG2_MREG         0x0008  // 1=Multiple register access (MII mgt)
++#define PHY_CFG2_INTMDIO      0x0004  // 1=Interrupt signaled with MDIO pulseo
++
++// PHY Status Output (and Interrupt status) Register
++#define PHY_INT_REG           0x12    // Status Output (Interrupt Status)
++#define PHY_INT_INT           0x8000  // 1=bits have changed since last read
++#define PHY_INT_LNKFAIL               0x4000  // 1=Link Not detected
++#define PHY_INT_LOSSSYNC      0x2000  // 1=Descrambler has lost sync
++#define PHY_INT_CWRD          0x1000  // 1=Invalid 4B5B code detected on rx
++#define PHY_INT_SSD           0x0800  // 1=No Start Of Stream detected on rx
++#define PHY_INT_ESD           0x0400  // 1=No End Of Stream detected on rx
++#define PHY_INT_RPOL          0x0200  // 1=Reverse Polarity detected
++#define PHY_INT_JAB           0x0100  // 1=Jabber detected
++#define PHY_INT_SPDDET                0x0080  // 1=100Base-TX mode, 0=10Base-T mode
++#define PHY_INT_DPLXDET               0x0040  // 1=Device in Full Duplex
++
++// PHY Interrupt/Status Mask Register
++#define PHY_MASK_REG          0x13    // Interrupt Mask
++// Uses the same bit definitions as PHY_INT_REG
++
++
++/*
++ * SMC91C96 ethernet config and status registers.
++ * These are in the "attribute" space.
++ */
++#define ECOR                  0x8000
++#define ECOR_RESET            0x80
++#define ECOR_LEVEL_IRQ                0x40
++#define ECOR_WR_ATTRIB                0x04
++#define ECOR_ENABLE           0x01
++
++#define ECSR                  0x8002
++#define ECSR_IOIS8            0x20
++#define ECSR_PWRDWN           0x04
++#define ECSR_INT              0x02
++
++
++/*
++ * Macros to abstract register access according to the data bus
++ * capabilities.  Please try to use those and not the in/out primitives.
++ * Note: the following macros do *not* select the bank -- this must
++ * be done separately as needed in the main code.  The SMC_REG() macro
++ * only uses the bank argument for debugging purposes.
++ */
++
++#if SMC_DEBUG > 0
++#define SMC_REG(reg, bank)                                            \
++      ({                                                              \
++              int __b = SMC_CURRENT_BANK();                           \
++              if ((__b & ~0xf0) != (0x3300 | bank)) {                 \
++                      printk( "%s: bank reg screwed (0x%04x)\n",      \
++                              CARDNAME, __b );                        \
++                      BUG();                                          \
++              }                                                       \
++              reg<<SMC_IO_SHIFT;                                      \
++      })
++#else
++#define SMC_REG(reg, bank)    (reg<<SMC_IO_SHIFT)
++#endif
++
++#if SMC_CAN_USE_8BIT
++#define SMC_GET_PN()          SMC_inb( ioaddr, PN_REG )
++#define SMC_SET_PN(x)         SMC_outb( x, ioaddr, PN_REG )
++#define SMC_GET_AR()          SMC_inb( ioaddr, AR_REG )
++#define SMC_GET_TXFIFO()      SMC_inb( ioaddr, TXFIFO_REG )
++#define SMC_GET_RXFIFO()      SMC_inb( ioaddr, RXFIFO_REG )
++#define SMC_GET_INT()         SMC_inb( ioaddr, INT_REG )
++#define SMC_ACK_INT(x)                SMC_outb( x, ioaddr, INT_REG )
++#define SMC_GET_INT_MASK()    SMC_inb( ioaddr, IM_REG )
++#define SMC_SET_INT_MASK(x)   SMC_outb( x, ioaddr, IM_REG )
++#else
++#define SMC_GET_PN()          (SMC_inw( ioaddr, PN_REG ) & 0xFF)
++#define SMC_SET_PN(x)         SMC_outw( x, ioaddr, PN_REG )
++#define SMC_GET_AR()          (SMC_inw( ioaddr, PN_REG ) >> 8)
++#define SMC_GET_TXFIFO()      (SMC_inw( ioaddr, TXFIFO_REG ) & 0xFF)
++#define SMC_GET_RXFIFO()      (SMC_inw( ioaddr, TXFIFO_REG ) >> 8)
++#define SMC_GET_INT()         (SMC_inw( ioaddr, INT_REG ) & 0xFF)
++#define SMC_ACK_INT(x)                                                        \
++      do {                                                            \
++              unsigned long __flags;                                  \
++              int __mask;                                             \
++              local_irq_save(__flags);                                \
++              __mask = SMC_inw( ioaddr, INT_REG ) & ~0xff;            \
++              SMC_outw( __mask | (x), ioaddr, INT_REG );              \
++              local_irq_restore(__flags);                             \
++      } while (0)
++#define SMC_GET_INT_MASK()    (SMC_inw( ioaddr, INT_REG ) >> 8)
++#define SMC_SET_INT_MASK(x)   SMC_outw( (x) << 8, ioaddr, INT_REG )
++#endif
++
++#define SMC_CURRENT_BANK()    SMC_inw( ioaddr, BANK_SELECT )
++#define SMC_SELECT_BANK(x)    SMC_outw( x, ioaddr, BANK_SELECT )
++#define SMC_GET_BASE()                SMC_inw( ioaddr, BASE_REG )
++#define SMC_SET_BASE(x)               SMC_outw( x, ioaddr, BASE_REG )
++#define SMC_GET_CONFIG()      SMC_inw( ioaddr, CONFIG_REG )
++#define SMC_SET_CONFIG(x)     SMC_outw( x, ioaddr, CONFIG_REG )
++#define SMC_GET_COUNTER()     SMC_inw( ioaddr, COUNTER_REG )
++#define SMC_GET_CTL()         SMC_inw( ioaddr, CTL_REG )
++#define SMC_SET_CTL(x)                SMC_outw( x, ioaddr, CTL_REG )
++#define SMC_GET_MII()         SMC_inw( ioaddr, MII_REG )
++#define SMC_SET_MII(x)                SMC_outw( x, ioaddr, MII_REG )
++#define SMC_GET_MIR()         SMC_inw( ioaddr, MIR_REG )
++#define SMC_SET_MIR(x)                SMC_outw( x, ioaddr, MIR_REG )
++#define SMC_GET_MMU_CMD()     SMC_inw( ioaddr, MMU_CMD_REG )
++#define SMC_SET_MMU_CMD(x)    SMC_outw( x, ioaddr, MMU_CMD_REG )
++#define SMC_GET_FIFO()                SMC_inw( ioaddr, FIFO_REG )
++#define SMC_GET_PTR()         SMC_inw( ioaddr, PTR_REG )
++#define SMC_SET_PTR(x)                SMC_outw( x, ioaddr, PTR_REG )
++#define SMC_GET_RCR()         SMC_inw( ioaddr, RCR_REG )
++#define SMC_SET_RCR(x)                SMC_outw( x, ioaddr, RCR_REG )
++#define SMC_GET_REV()         SMC_inw( ioaddr, REV_REG )
++#define SMC_GET_RPC()         SMC_inw( ioaddr, RPC_REG )
++#define SMC_SET_RPC(x)                SMC_outw( x, ioaddr, RPC_REG )
++#define SMC_GET_TCR()         SMC_inw( ioaddr, TCR_REG )
++#define SMC_SET_TCR(x)                SMC_outw( x, ioaddr, TCR_REG )
++
++#ifndef SMC_GET_MAC_ADDR
++#define SMC_GET_MAC_ADDR(addr)                                                \
++      do {                                                            \
++              unsigned int __v;                                       \
++              __v = SMC_inw( ioaddr, ADDR0_REG );                     \
++              addr[0] = __v; addr[1] = __v >> 8;                      \
++              __v = SMC_inw( ioaddr, ADDR1_REG );                     \
++              addr[2] = __v; addr[3] = __v >> 8;                      \
++              __v = SMC_inw( ioaddr, ADDR2_REG );                     \
++              addr[4] = __v; addr[5] = __v >> 8;                      \
++      } while (0)
++#endif
++
++#define SMC_SET_MAC_ADDR(addr)                                                \
++      do {                                                            \
++              SMC_outw( addr[0]|(addr[1] << 8), ioaddr, ADDR0_REG );  \
++              SMC_outw( addr[2]|(addr[3] << 8), ioaddr, ADDR1_REG );  \
++              SMC_outw( addr[4]|(addr[5] << 8), ioaddr, ADDR2_REG );  \
++      } while (0)
++
++#define SMC_CLEAR_MCAST()                                             \
++      do {                                                            \
++              SMC_outw( 0, ioaddr, MCAST_REG1 );                      \
++              SMC_outw( 0, ioaddr, MCAST_REG2 );                      \
++              SMC_outw( 0, ioaddr, MCAST_REG3 );                      \
++              SMC_outw( 0, ioaddr, MCAST_REG4 );                      \
++      } while (0)
++#define SMC_SET_MCAST(x)                                              \
++      do {                                                            \
++              unsigned char *mt = (x);                                \
++              SMC_outw( mt[0] | (mt[1] << 8), ioaddr, MCAST_REG1 );   \
++              SMC_outw( mt[2] | (mt[3] << 8), ioaddr, MCAST_REG2 );   \
++              SMC_outw( mt[4] | (mt[5] << 8), ioaddr, MCAST_REG3 );   \
++              SMC_outw( mt[6] | (mt[7] << 8), ioaddr, MCAST_REG4 );   \
++      } while (0)
++
++#if SMC_CAN_USE_32BIT
++/*
++ * Some setups just can't write 8 or 16 bits reliably when not aligned
++ * to a 32 bit boundary.  I tell you that exists!
++ * We do the ones that can have their low parts written to 0 here.
++ */
++#undef SMC_SELECT_BANK
++#define SMC_SELECT_BANK(x)    SMC_outl( (x)<<16, ioaddr, 12<<SMC_IO_SHIFT )
++#undef SMC_SET_RPC
++#define SMC_SET_RPC(x)                SMC_outl( (x)<<16, ioaddr, SMC_REG(8, 0) )
++#undef SMC_SET_PN
++#define SMC_SET_PN(x)         SMC_outl( (x)<<16, ioaddr, SMC_REG(0, 2) )
++#undef SMC_SET_PTR
++#define SMC_SET_PTR(x)                SMC_outl( (x)<<16, ioaddr, SMC_REG(4, 2) )
++#endif
++
++#if SMC_CAN_USE_32BIT
++#define SMC_PUT_PKT_HDR(status, length)                                       \
++      SMC_outl( (status) | (length) << 16, ioaddr, DATA_REG )
++#define SMC_GET_PKT_HDR(status, length)                                       \
++      do {                                                            \
++              unsigned int __val = SMC_inl( ioaddr, DATA_REG );       \
++              (status) = __val & 0xffff;                              \
++              (length) = __val >> 16;                                 \
++      } while (0)
++#else
++#define SMC_PUT_PKT_HDR(status, length)                                       \
++      do {                                                            \
++              SMC_outw( status, ioaddr, DATA_REG );                   \
++              SMC_outw( length, ioaddr, DATA_REG );                   \
++      } while (0)
++#define SMC_GET_PKT_HDR(status, length)                                       \
++      do {                                                            \
++              (status) = SMC_inw( ioaddr, DATA_REG );                 \
++              (length) = SMC_inw( ioaddr, DATA_REG );                 \
++      } while (0)
++#endif
++
++#if SMC_CAN_USE_32BIT
++#define SMC_PUSH_DATA(p, l)                                           \
++      do {                                                            \
++              char *__ptr = (p);                                      \
++              int __len = (l);                                        \
++              if (__len >= 2 && (long)__ptr & 2) {                    \
++                      __len -= 2;                                     \
++                      SMC_outw( *((u16 *)__ptr)++, ioaddr, DATA_REG );\
++              }                                                       \
++              SMC_outsl( ioaddr, DATA_REG, __ptr, __len >> 2);        \
++              if (__len & 2) {                                        \
++                      __ptr += (__len & ~3);                          \
++                      SMC_outw( *((u16 *)__ptr), ioaddr, DATA_REG );  \
++              }                                                       \
++      } while (0)
++#define SMC_PULL_DATA(p, l)                                           \
++      do {                                                            \
++              char *__ptr = (p);                                      \
++              int __len = (l);                                        \
++              if ((long)__ptr & 2) {                                  \
++                      /*                                              \
++                       * We want 32bit alignment here.                \
++                       * Since some buses perform a full 32bit        \
++                       * fetch even for 16bit data we can't use       \
++                       * SMC_inw() here.  Back both source (on chip   \
++                       * and destination) pointers of 2 bytes.        \
++                       */                                             \
++                      (long)__ptr &= ~2;                              \
++                      __len += 2;                                     \
++                      SMC_SET_PTR( 2|PTR_READ|PTR_RCV|PTR_AUTOINC );  \
++              }                                                       \
++              __len += 2;                                             \
++              SMC_insl( ioaddr, DATA_REG, __ptr, __len >> 2);         \
++      } while (0)
++#elif SMC_CAN_USE_16BIT
++#define SMC_PUSH_DATA(p, l)   SMC_outsw( ioaddr, DATA_REG, p, (l) >> 1 )
++#define SMC_PULL_DATA(p, l)   SMC_insw ( ioaddr, DATA_REG, p, (l) >> 1 )
++#elif SMC_CAN_USE_8BIT
++#define SMC_PUSH_DATA(p, l)   SMC_outsb( ioaddr, DATA_REG, p, l )
++#define SMC_PULL_DATA(p, l)   SMC_insb ( ioaddr, DATA_REG, p, l )
++#endif
++
++#if ! SMC_CAN_USE_16BIT
++#define SMC_outw(x, ioaddr, reg)                                      \
++      do {                                                            \
++              unsigned int __val16 = (x);                             \
++              SMC_outb( __val16, ioaddr, reg );                       \
++              SMC_outb( __val16 >> 8, ioaddr, reg + 1 );              \
++      } while (0)
++#define SMC_inw(ioaddr, reg)                                          \
++      ({                                                              \
++              unsigned int __val16;                                   \
++              __val16 =  SMC_inb( ioaddr, reg );                      \
++              __val16 |= SMC_inb( ioaddr, reg + 1 ) << 8;             \
++              __val16;                                                \
++      })
++#endif
++
++
++#endif  /* _SMC91X_H_ */
+--- linux-2.4.27/drivers/pcmcia/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/pcmcia/Config.in
+@@ -45,6 +45,7 @@
+ if [ "$CONFIG_ARM" = "y" ]; then
+    dep_tristate '  CLPS6700 support' CONFIG_PCMCIA_CLPS6700 $CONFIG_ARCH_CLPS711X $CONFIG_PCMCIA
+    dep_tristate '  SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA
++   dep_tristate '  PXA250/210 support' CONFIG_PCMCIA_PXA $CONFIG_ARCH_PXA $CONFIG_PCMCIA
+ fi
+ endmenu
+--- linux-2.4.27/drivers/pcmcia/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/pcmcia/Makefile
+@@ -94,6 +94,11 @@
+ obj-$(CONFIG_PCMCIA_VRC4173)  += vrc4173_cardu.o
++subdir-$(CONFIG_PCMCIA_PXA)                   += pxa
++ifeq ($(CONFIG_PCMCIA_PXA),y)
++  obj-y                                       += pxa/pxa_cs.o
++endif
++
+ include $(TOPDIR)/Rules.make
+ pcmcia_core.o:  $(pcmcia_core-objs)
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/Makefile
+@@ -0,0 +1,18 @@
++#
++# Makefile for the Intel PXA250/210 PCMCIA driver
++#
++# 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).
++
++O_TARGET                      := pxa_cs.o
++
++obj-y                         := pxa.o
++obj-$(CONFIG_ARCH_LUBBOCK)    += lubbock.o
++obj-$(CONFIG_ARCH_PXA_IDP)    += pxa_idp.o
++obj-$(CONFIG_ARCH_TRIZEPS2)   += trizeps2.o
++obj-$(CONFIG_ARCH_PXA_CERF)   += ../sa1100_cerf.o
++
++obj-m                         := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/lubbock.c
+@@ -0,0 +1,329 @@
++/*
++ * linux/drivers/pcmcia/pxa/lubbock.c
++ *
++ * Author:    George Davis
++ * Created:   Jan 10, 2002
++ * Copyright: 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.
++ *
++ * Originally based upon linux/drivers/pcmcia/sa1100_neponset.c
++ *
++ * Lubbock PCMCIA specific routines.
++ *
++ */
++
++#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>
++#include <asm/hardware/sa1111.h>
++
++/*
++ * I'd really like to move the INTPOL stuff to arch/arm/mach-sa1100/sa1111.c
++ * ... and maybe even arch/arm/mach-pxa/sa1111.c now too!  : )
++ */
++#define SA1111_IRQMASK_LO(x)  (1 << (x - IRQ_SA1111_START))
++#define SA1111_IRQMASK_HI(x)  (1 << (x - IRQ_SA1111_START - 32))
++
++static int lubbock_pcmcia_init(struct pcmcia_init *init){
++  int return_val=0;
++
++  /* Set PCMCIA Socket 0 power to standby mode.
++   */
++  PA_DWR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3));
++
++  /* Set GPIO_A<3:0> to be outputs for PCMCIA (socket 0) power controller.
++   * Note that this is done only after first initializing GPIO_A<3:0>
++   * output state above to be certain that we drive signals to the same
++   * state as the pull-downs connected to these lines. The pull-downs are
++   * req'd to make sure PCMCIA power is OFF until we can get around to
++   * setting up the GPIO_A<3:0> state and direction.
++   */
++  PA_DDR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3));
++
++  /* Set CF Socket 1 power to standby mode. */
++  LUB_MISC_WR &= ~(GPIO_bit(15) | GPIO_bit(14));
++
++  INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) |
++           SA1111_IRQMASK_HI(S1_READY_NINT) |
++           SA1111_IRQMASK_HI(S0_CD_VALID) |
++           SA1111_IRQMASK_HI(S1_CD_VALID) |
++           SA1111_IRQMASK_HI(S0_BVD1_STSCHG) |
++           SA1111_IRQMASK_HI(S1_BVD1_STSCHG);
++
++#warning what if a request_irq fails?
++  return_val+=request_irq(S0_CD_VALID, init->handler, SA_INTERRUPT,
++                        "Lubbock PCMCIA (0) CD", NULL);
++  return_val+=request_irq(S1_CD_VALID, init->handler, SA_INTERRUPT,
++                        "Lubbock CF (1) CD", NULL);
++  return_val+=request_irq(S0_BVD1_STSCHG, init->handler, SA_INTERRUPT,
++                        "Lubbock PCMCIA (0) BVD1", NULL);
++  return_val+=request_irq(S1_BVD1_STSCHG, init->handler, SA_INTERRUPT,
++                        "Lubbock CF (1) BVD1", NULL);
++
++  return (return_val<0) ? -1 : 2;
++}
++
++static int lubbock_pcmcia_shutdown(void){
++
++  free_irq(S0_CD_VALID, NULL);
++  free_irq(S1_CD_VALID, NULL);
++  free_irq(S0_BVD1_STSCHG, NULL);
++  free_irq(S1_BVD1_STSCHG, NULL);
++
++  INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) |
++             SA1111_IRQMASK_HI(S1_CD_VALID) |
++             SA1111_IRQMASK_HI(S0_BVD1_STSCHG) |
++             SA1111_IRQMASK_HI(S1_BVD1_STSCHG));
++
++  return 0;
++}
++
++static int lubbock_pcmcia_socket_state(struct pcmcia_state_array
++                                      *state_array){
++  unsigned long status;
++  int return_val=1;
++
++  if(state_array->size<2) return -1;
++
++  memset(state_array->state, 0, 
++       (state_array->size)*sizeof(struct pcmcia_state));
++
++  status=PCSR;
++
++  state_array->state[0].detect=((status & PCSR_S0_DETECT)==0)?1:0;
++
++  state_array->state[0].ready=((status & PCSR_S0_READY)==0)?0:1;
++
++  state_array->state[0].bvd1=((status & PCSR_S0_BVD1)==0)?0:1;
++
++  state_array->state[0].bvd2=((status & PCSR_S0_BVD2)==0)?0:1;
++
++  state_array->state[0].wrprot=((status & PCSR_S0_WP)==0)?0:1;
++
++  state_array->state[0].vs_3v=((status & PCSR_S0_VS1)==0)?1:0;
++
++  state_array->state[0].vs_Xv=((status & PCSR_S0_VS2)==0)?1:0;
++
++  state_array->state[1].detect=((status & PCSR_S1_DETECT)==0)?1:0;
++
++  state_array->state[1].ready=((status & PCSR_S1_READY)==0)?0:1;
++
++  state_array->state[1].bvd1=((status & PCSR_S1_BVD1)==0)?0:1;
++
++  state_array->state[1].bvd2=((status & PCSR_S1_BVD2)==0)?0:1;
++
++  state_array->state[1].wrprot=((status & PCSR_S1_WP)==0)?0:1;
++
++  state_array->state[1].vs_3v=((status & PCSR_S1_VS1)==0)?1:0;
++
++  state_array->state[1].vs_Xv=((status & PCSR_S1_VS2)==0)?1:0;
++
++  return return_val;
++}
++
++static int lubbock_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
++
++  switch(info->sock){
++  case 0:
++    info->irq=S0_READY_NINT;
++    break;
++
++  case 1:
++    info->irq=S1_READY_NINT;
++    break;
++
++  default:
++    return -1;
++  }
++
++  return 0;
++}
++
++static int
++lubbock_pcmcia_configure_socket(unsigned int sock, socket_state_t *state)
++{
++  unsigned long flags, pccr, gpio, misc_wr, status;
++  int ret=1;
++
++  local_irq_save(flags);
++
++  pccr=PCCR;
++  gpio=PA_DWR;
++  misc_wr = LUB_MISC_WR;
++
++  /* Lubbock uses the Maxim MAX1602, with the following connections:
++   *
++   * Socket 0 (PCMCIA):
++   *  MAX1602 Lubbock         Register
++   *  Pin     Signal
++   *  -----   -------         ----------------------
++   *  A0VPP   S0_PWR0         SA-1111 GPIO A<0>
++   *  A1VPP   S0_PWR1         SA-1111 GPIO A<1>
++   *  A0VCC   S0_PWR2         SA-1111 GPIO A<2>
++   *  A1VCC   S0_PWR3         SA-1111 GPIO A<3>
++   *  VX      VCC
++   *  VY      +3.3V
++   *  12IN    +12V
++   *  CODE    +3.3V           Cirrus  Code, CODE = High (VY)
++   *
++   * Socket 1 (CF):
++   *  MAX1602 Lubbock         Register
++   *  Pin     Signal
++   *  -----   -------         ----------------------
++   *  A0VPP   GND             VPP is not connected
++   *  A1VPP   GND             VPP is not connected
++   *  A0VCC   S1_PWR0         MISC_WR<14>
++   *  A1VCC   S1_PWR0         MISC_WR<15>
++   *  VX      VCC
++   *  VY      +3.3V
++   *  12IN    GND             VPP is not connected
++   *  CODE    +3.3V           Cirrus  Code, CODE = High (VY)
++   *
++   */
++
++again:
++  switch(sock){
++  case 0:
++
++    switch(state->Vcc){
++    case 0:
++      pccr = (pccr & ~PCCR_S0_FLT);
++      gpio &= ~(GPIO_bit(2) | GPIO_bit(3));
++      break;
++
++    case 33:
++      pccr = (pccr & ~PCCR_S0_PSE) | PCCR_S0_FLT | PCCR_S0_PWAITEN;
++      gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(3);
++      break;
++
++    case 50:
++      pccr = (pccr | PCCR_S0_PSE | PCCR_S0_FLT | PCCR_S0_PWAITEN);
++      gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(2);
++      break;
++
++    default:
++      printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, state->Vcc);
++      ret = -1;
++      break;
++    }
++
++    switch(state->Vpp){
++    case 0:
++      gpio &= ~(GPIO_bit(0) | GPIO_bit(1));
++      break;
++
++    case 120:
++      gpio = (gpio & ~(GPIO_bit(0) | GPIO_bit(1))) | GPIO_bit(1);
++      break;
++
++    default:
++      /* REVISIT: I'm not sure about this? Is this correct?
++         Is it always safe or do we have potential problems
++         with bogus combinations of Vcc and Vpp settings? */
++      if(state->Vpp == state->Vcc)
++        gpio = (gpio & ~(GPIO_bit(0) | GPIO_bit(1))) | GPIO_bit(0);
++      else {
++      printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__, state->Vpp);
++      ret = -1;
++      break;
++      }
++    }
++
++    pccr = (state->flags&SS_RESET) ? (pccr|PCCR_S0_RST) : (pccr&~PCCR_S0_RST);
++
++    break;
++
++  case 1:
++    switch(state->Vcc){
++    case 0:
++      pccr = (pccr & ~PCCR_S1_FLT);
++      misc_wr &= ~((1 << 15) | (1 << 14));
++      break;
++
++    case 33:
++      pccr = (pccr & ~PCCR_S1_PSE) | PCCR_S1_FLT | PCCR_S1_PWAITEN;
++      misc_wr = (misc_wr & ~(1 << 15)) | (1 << 14);
++      gpio = (gpio & ~(GPIO_bit(2) | GPIO_bit(3))) | GPIO_bit(2);
++      break;
++
++    case 50:
++      pccr = (pccr | PCCR_S1_PSE | PCCR_S1_FLT | PCCR_S1_PWAITEN);
++      misc_wr = (misc_wr & ~(1 << 15)) | (1 << 14);
++      break;
++
++    default:
++      printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__, state->Vcc);
++      ret = -1;
++      break;
++    }
++
++    if(state->Vpp!=state->Vcc && state->Vpp!=0){
++      printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", __FUNCTION__, state->Vpp);
++      ret = -1;
++      break;
++    }
++
++    pccr = (state->flags&SS_RESET) ? (pccr|PCCR_S1_RST) : (pccr&~PCCR_S1_RST);
++
++    break;
++
++  default:
++    ret = -1;
++  }
++
++  if (ret >= 0) {
++    PCCR = pccr;
++    LUB_MISC_WR = misc_wr;
++    PA_DWR = gpio;
++  }
++
++  if (ret > 0) {
++    ret = 0;
++    /* 
++     * HACK ALERT: 
++     * We can't sense the voltage properly on Lubbock before actually
++     * applying some power to the socket (catch 22).
++     * Resense the socket Voltage Sense pins after applying socket power.
++     */
++    if (sock == 0)
++      status = PCSR & (PCSR_S0_VS1 | PCSR_S0_VS2);
++    else
++      status = PCSR & (PCSR_S1_VS1 | PCSR_S1_VS2);
++
++    if ((status == (PCSR_S0_VS1 | PCSR_S0_VS2)) && (state->Vcc == 33)) {
++      /* Switch to 5V,  Configure socket 0  with 5V voltage */
++      PA_DWR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3));
++      PA_DDR &= ~(GPIO_bit(0) | GPIO_bit(1) | GPIO_bit(2) | GPIO_bit(3));
++      state->Vcc = 50;
++      state->Vpp = 50;
++      goto again;
++    }
++    if ((status == (PCSR_S1_VS1 | PCSR_S1_VS2)) && (state->Vcc == 33)) {
++      /* Switch to 5V, Configure socket 1 with 5V voltage */
++      LUB_MISC_WR &= ~((1 << 15) | (1 << 14));
++      state->Vcc = 50;
++      state->Vpp = 50;
++      goto again;
++    }
++  }
++
++  local_irq_restore(flags);
++  return ret;
++}
++
++struct pcmcia_low_level lubbock_pcmcia_ops = { 
++  lubbock_pcmcia_init,
++  lubbock_pcmcia_shutdown,
++  lubbock_pcmcia_socket_state,
++  lubbock_pcmcia_get_irq_info,
++  lubbock_pcmcia_configure_socket
++};
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/pxa.c
+@@ -0,0 +1,1247 @@
++/*
++ * linux/drivers/pcmcia/pxa/pxa.c
++ *
++ * Author:    George Davis
++ * Created:   Jan 10, 2002
++ * Copyright: 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.
++ *
++ * Originally based upon linux/drivers/pcmcia/sa1100_generic.c
++ *
++ */
++
++/*======================================================================
++
++    Device driver for the PCMCIA control functionality of Intel
++    PXA250/210 microprocessors.
++
++    The contents of this file are subject to the Mozilla Public
++    License Version 1.1 (the "License"); you may not use this file
++    except in compliance with the License. You may obtain a copy of
++    the License at http://www.mozilla.org/MPL/
++
++    Software distributed under the License is distributed on an "AS
++    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++    implied. See the License for the specific language governing
++    rights and limitations under the License.
++
++    The initial developer of the original code is John G. Dorsey
++    <john+@cs.cmu.edu>.  Portions created by John G. Dorsey are
++    Copyright (C) 1999 John G. Dorsey.  All Rights Reserved.
++
++    Alternatively, the contents of this file may be used under the
++    terms of the GNU 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 MPL, 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 MPL or the GPL.
++    
++======================================================================*/
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/config.h>
++#include <linux/cpufreq.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/tqueue.h>
++#include <linux/timer.h>
++#include <linux/mm.h>
++#include <linux/notifier.h>
++#include <linux/proc_fs.h>
++#include <linux/version.h>
++#include <linux/cpufreq.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/ss.h>
++#include <pcmcia/bus_ops.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/arch/lubbock.h>
++
++#include "pxa.h"
++
++#ifdef PCMCIA_DEBUG
++static int pc_debug;
++#endif
++
++MODULE_AUTHOR("George Davis <davis_g@mvista.com>");
++MODULE_DESCRIPTION("Linux PCMCIA Card Services: PXA250/210 Socket Controller");
++
++/* This structure maintains housekeeping state for each socket, such
++ * as the last known values of the card detect pins, or the Card Services
++ * callback value associated with the socket:
++ */
++static struct pxa_pcmcia_socket 
++pxa_pcmcia_socket[PXA_PCMCIA_MAX_SOCK];
++
++static int pxa_pcmcia_socket_count;
++
++
++/* Returned by the low-level PCMCIA interface: */
++static struct pcmcia_low_level *pcmcia_low_level;
++
++/* Event poll timer structure */
++static struct timer_list poll_timer;
++
++
++/* Prototypes for routines which are used internally: */
++
++static int  pxa_pcmcia_driver_init(void);
++static void pxa_pcmcia_driver_shutdown(void);
++static void pxa_pcmcia_task_handler(void *data);
++static void pxa_pcmcia_poll_event(unsigned long data);
++static void pxa_pcmcia_interrupt(int irq, void *dev,
++                                  struct pt_regs *regs);
++static struct tq_struct pxa_pcmcia_task;
++
++#ifdef CONFIG_PROC_FS
++static int  pxa_pcmcia_proc_status(char *buf, char **start, off_t pos,
++                                    int count, int *eof, void *data);
++#endif
++
++
++/* Prototypes for operations which are exported to the
++ * new-and-impr^H^H^H^H^H^H^H^H^H^H in-kernel PCMCIA core:
++ */
++
++static int pxa_pcmcia_init(unsigned int sock);
++static int pxa_pcmcia_suspend(unsigned int sock);
++static int pxa_pcmcia_register_callback(unsigned int sock,
++                                         void (*handler)(void *, 
++                                                         unsigned int),
++                                         void *info);
++static int pxa_pcmcia_inquire_socket(unsigned int sock, 
++                                      socket_cap_t *cap);
++static int pxa_pcmcia_get_status(unsigned int sock, u_int *value);
++static int pxa_pcmcia_get_socket(unsigned int sock, 
++                                  socket_state_t *state);
++static int pxa_pcmcia_set_socket(unsigned int sock,
++                                  socket_state_t *state);
++static int pxa_pcmcia_get_io_map(unsigned int sock,
++                                  struct pccard_io_map *io);
++static int pxa_pcmcia_set_io_map(unsigned int sock,
++                                  struct pccard_io_map *io);
++static int pxa_pcmcia_get_mem_map(unsigned int sock,
++                                   struct pccard_mem_map *mem);
++static int pxa_pcmcia_set_mem_map(unsigned int sock,
++                                   struct pccard_mem_map *mem);
++#ifdef CONFIG_PROC_FS
++static void pxa_pcmcia_proc_setup(unsigned int sock,
++                                   struct proc_dir_entry *base);
++#endif
++
++static struct pccard_operations pxa_pcmcia_operations = {
++  pxa_pcmcia_init,
++  pxa_pcmcia_suspend,
++  pxa_pcmcia_register_callback,
++  pxa_pcmcia_inquire_socket,
++  pxa_pcmcia_get_status,
++  pxa_pcmcia_get_socket,
++  pxa_pcmcia_set_socket,
++  pxa_pcmcia_get_io_map,
++  pxa_pcmcia_set_io_map,
++  pxa_pcmcia_get_mem_map,
++  pxa_pcmcia_set_mem_map,
++#ifdef CONFIG_PROC_FS
++  pxa_pcmcia_proc_setup
++#endif
++};
++
++#ifdef CONFIG_CPU_FREQ
++/* forward declaration */
++static struct notifier_block pxa_pcmcia_notifier_block;
++#endif
++
++
++/* pxa_pcmcia_driver_init()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ *
++ * This routine performs a basic sanity check to ensure that this
++ * kernel has been built with the appropriate board-specific low-level
++ * PCMCIA support, performs low-level PCMCIA initialization, registers
++ * this socket driver with Card Services, and then spawns the daemon
++ * thread which is the real workhorse of the socket driver.
++ *
++ * Please see linux/Documentation/arm/SA1100/PCMCIA for more information
++ * on the low-level kernel interface.
++ *
++ * Returns: 0 on success, -1 on error
++ */
++static int __init pxa_pcmcia_driver_init(void){
++  servinfo_t info;
++  struct pcmcia_init pcmcia_init;
++  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);
++
++  CardServices(GetCardServicesInfo, &info);
++
++  if(info.Revision!=CS_RELEASE_CODE){
++    printk(KERN_ERR "Card Services release codes do not match\n");
++    return -1;
++  }
++
++  /* Setup GPIOs for PCMCIA/CF alternate function mode.
++   *
++   * It would be nice if set_GPIO_mode included support
++   * for driving GPIO outputs to default high/low state
++   * before programming GPIOs as outputs. Setting GPIO
++   * outputs to default high/low state via GPSR/GPCR
++   * before defining them as outputs should reduce
++   * the possibility of glitching outputs during GPIO
++   * setup. This of course assumes external terminators
++   * are present to hold GPIOs in a defined state.
++   *
++   * In the meantime, setup default state of GPIO
++   * outputs before we enable them as outputs.
++   */
++
++  GPSR(GPIO48_nPOE) = GPIO_bit(GPIO48_nPOE) |
++                      GPIO_bit(GPIO49_nPWE) |
++                      GPIO_bit(GPIO50_nPIOR) |
++                      GPIO_bit(GPIO51_nPIOW) |
++                      GPIO_bit(GPIO52_nPCE_1) |
++                      GPIO_bit(GPIO53_nPCE_2);
++
++  set_GPIO_mode(GPIO48_nPOE_MD);
++  set_GPIO_mode(GPIO49_nPWE_MD);
++  set_GPIO_mode(GPIO50_nPIOR_MD);
++  set_GPIO_mode(GPIO51_nPIOW_MD);
++  set_GPIO_mode(GPIO52_nPCE_1_MD);
++  set_GPIO_mode(GPIO53_nPCE_2_MD);
++  set_GPIO_mode(GPIO54_pSKTSEL_MD); /* REVISIT: s/b dependent on num sockets */
++  set_GPIO_mode(GPIO55_nPREG_MD);
++  set_GPIO_mode(GPIO56_nPWAIT_MD);
++  set_GPIO_mode(GPIO57_nIOIS16_MD);
++
++
++  if(machine_is_lubbock()){
++#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_CSB226)
++    pcmcia_low_level=&lubbock_pcmcia_ops;
++#endif
++  } else if (machine_is_pxa_idp()) {
++    pcmcia_low_level=&pxa_idp_pcmcia_ops;
++  } else if( machine_is_pxa_cerf()){
++    pcmcia_low_level=&cerf_pcmcia_ops;
++  } else if (machine_is_trizeps2()){
++#ifdef CONFIG_ARCH_TRIZEPS2
++    pcmcia_low_level=&trizeps2_pcmcia_ops;
++#endif
++  }
++
++  if (!pcmcia_low_level) {
++    printk(KERN_ERR "This hardware is not supported by the PXA250/210 Card Service driver\n");
++    return -ENODEV;
++  }
++
++  pcmcia_init.handler=pxa_pcmcia_interrupt;
++
++  if((pxa_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){
++    printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n");
++    return -EIO;
++  }
++
++  state_array.size=pxa_pcmcia_socket_count;
++  state_array.state=state;
++
++  /* Configure MECR based on the number of sockets present. */
++  if (pxa_pcmcia_socket_count == 2) {
++    MECR |= GPIO_bit(0);
++  } else {
++    MECR &= ~GPIO_bit(0);
++  }
++
++  if(pcmcia_low_level->socket_state(&state_array)<0){
++    printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
++    return -EIO;
++  }
++
++  /* Well, it looks good to go. So we can now enable the PCMCIA
++   * controller.
++   */
++  MECR |= GPIO_bit(1);
++
++  /* We need to initialize the MCXX registers to default values
++   * here because we're not guaranteed to see a SetIOMap operation
++   * at runtime.
++   */
++
++  clock = get_lclk_frequency_10khz();
++
++  for(i=0; i<pxa_pcmcia_socket_count; ++i){
++    pxa_pcmcia_socket[i].k_state=state[i];
++
++    /* This is an interim fix. Apparently, SetSocket is no longer
++     * called to initialize each socket (prior to the first detect
++     * event). For now, we'll just manually set up the mask.
++     */
++    pxa_pcmcia_socket[i].cs_state.csc_mask=SS_DETECT;
++
++    pxa_pcmcia_socket[i].virt_io=(i==0)?PCMCIA_IO_0_BASE:PCMCIA_IO_1_BASE;
++    pxa_pcmcia_socket[i].phys_attr=_PCMCIAAttr(i);
++    pxa_pcmcia_socket[i].phys_mem=_PCMCIAMem(i);
++
++    /* REVISIT: cleanup these macros */
++    //MCIO_SET(i, PXA_PCMCIA_IO_ACCESS, clock);
++    //MCATTR_SET(i, PXA_PCMCIA_5V_MEM_ACCESS, clock);
++    //MCMEM_SET(i, PXA_PCMCIA_5V_MEM_ACCESS, clock);
++
++    pxa_pcmcia_socket[i].speed_io=PXA_PCMCIA_IO_ACCESS;
++    pxa_pcmcia_socket[i].speed_attr=PXA_PCMCIA_ATTR_MEM_ACCESS;
++    pxa_pcmcia_socket[i].speed_mem=PXA_PCMCIA_5V_MEM_ACCESS;
++  }
++
++/* REVISIT: cleanup these macros */
++MCMEM0 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++MCMEM1 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++MCATT0 = ((pxa_mcxx_setup(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++MCATT1 = ((pxa_mcxx_setup(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++MCIO0 = ((pxa_mcxx_setup(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++MCIO1 = ((pxa_mcxx_setup(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++       | ((pxa_mcxx_asst(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++       | ((pxa_mcxx_hold(PXA_PCMCIA_IO_ACCESS, clock)
++              & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++
++#ifdef CONFIG_CPU_FREQ
++  if(cpufreq_register_notifier(&pxa_pcmcia_notifier_block) < 0){
++    printk(KERN_ERR "Unable to register CPU frequency change notifier\n");
++    return -ENXIO;
++  }
++#endif
++
++  /* Only advertise as many sockets as we can detect: */
++  if(register_ss_entry(pxa_pcmcia_socket_count, 
++                     &pxa_pcmcia_operations)<0){
++    printk(KERN_ERR "Unable to register socket service routine\n");
++    return -ENXIO;
++  }
++
++  /* Start the event poll timer.  It will reschedule by itself afterwards. */
++  pxa_pcmcia_poll_event(0);
++
++  DEBUG(1, "pxa_cs: initialization complete\n");
++
++  return 0;
++
++}  /* pxa_pcmcia_driver_init() */
++
++module_init(pxa_pcmcia_driver_init);
++
++
++/* pxa_pcmcia_driver_shutdown()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Invokes the low-level kernel service to free IRQs associated with this
++ * socket controller and reset GPIO edge detection.
++ */
++static void __exit pxa_pcmcia_driver_shutdown(void){
++
++  del_timer_sync(&poll_timer);
++  unregister_ss_entry(&pxa_pcmcia_operations);
++#ifdef CONFIG_CPU_FREQ
++  cpufreq_unregister_notifier(&pxa_pcmcia_notifier_block);
++#endif
++  pcmcia_low_level->shutdown();
++  flush_scheduled_tasks();
++
++  DEBUG(1, "pxa_cs: shutdown complete\n");
++}
++
++module_exit(pxa_pcmcia_driver_shutdown);
++
++
++/* pxa_pcmcia_init()
++ * ^^^^^^^^^^^^^^^^^^^^
++ * We perform all of the interesting initialization tasks in 
++ * pxa_pcmcia_driver_init().
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_init(unsigned int sock){
++  
++  DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, sock);
++
++  return 0;
++}
++
++
++/* pxa_pcmcia_suspend()
++ * ^^^^^^^^^^^^^^^^^^^^^^^
++ * We don't currently perform any actions on a suspend.
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_suspend(unsigned int sock)
++{
++  socket_state_t st;
++  int ret;
++
++  DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, sock);
++
++  st.Vcc = 0;
++  st.Vpp = 0;
++  st.flags = SS_RESET;
++
++  ret = pcmcia_low_level->configure_socket(sock, &st);
++
++  if (ret == 0)
++    pxa_pcmcia_socket[sock].cs_state = dead_socket;
++
++  return ret;
++}
++
++
++/* pxa_pcmcia_events()
++ * ^^^^^^^^^^^^^^^^^^^^^^
++ * Helper routine to generate a Card Services event mask based on
++ * state information obtained from the kernel low-level PCMCIA layer
++ * in a recent (and previous) sampling. Updates `prev_state'.
++ *
++ * Returns: an event mask for the given socket state.
++ */
++static inline unsigned pxa_pcmcia_events(struct pcmcia_state *state,
++                                          struct pcmcia_state *prev_state,
++                                          unsigned int mask,
++                                          unsigned int flags){
++  unsigned int events=0;
++
++  if(state->detect!=prev_state->detect){
++
++    DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect);
++
++    events|=mask&SS_DETECT;
++  }
++
++  if(state->ready!=prev_state->ready){
++
++    DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready);
++
++    events|=mask&((flags&SS_IOCARD)?0:SS_READY);
++  }
++
++  if(state->bvd1!=prev_state->bvd1){
++
++    DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1);
++
++    events|=mask&(flags&SS_IOCARD)?SS_STSCHG:SS_BATDEAD;
++  }
++
++  if(state->bvd2!=prev_state->bvd2){
++
++    DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2);
++
++    events|=mask&(flags&SS_IOCARD)?0:SS_BATWARN;
++  }
++
++  DEBUG(2, "events: %s%s%s%s%s%s\n",
++      (events==0)?"<NONE>":"",
++      (events&SS_DETECT)?"DETECT ":"",
++      (events&SS_READY)?"READY ":"",
++      (events&SS_BATDEAD)?"BATDEAD ":"",
++      (events&SS_BATWARN)?"BATWARN ":"",
++      (events&SS_STSCHG)?"STSCHG ":"");
++
++  *prev_state=*state;
++
++  return events;
++
++}  /* pxa_pcmcia_events() */
++
++
++/* pxa_pcmcia_task_handler()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Processes serviceable socket events using the "eventd" thread context.
++ *
++ * Event processing (specifically, the invocation of the Card Services event
++ * callback) occurs in this thread rather than in the actual interrupt
++ * handler due to the use of scheduling operations in the PCMCIA core.
++ */
++static void pxa_pcmcia_task_handler(void *data) {
++  struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK];
++  struct pcmcia_state_array state_array;
++  int i, events, all_events, irq_status;
++
++  DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__);
++
++  state_array.size=pxa_pcmcia_socket_count;
++  state_array.state=state;
++
++  do {
++
++    DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__);
++
++    if((irq_status=pcmcia_low_level->socket_state(&state_array))<0)
++      printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n");
++
++    all_events=0;
++
++    if(irq_status>0){
++
++      for(i=0; i<state_array.size; ++i, all_events|=events)
++      if((events=
++          pxa_pcmcia_events(&state[i],
++                               &pxa_pcmcia_socket[i].k_state,
++                               pxa_pcmcia_socket[i].cs_state.csc_mask,
++                               pxa_pcmcia_socket[i].cs_state.flags)))
++        if(pxa_pcmcia_socket[i].handler!=NULL)
++          pxa_pcmcia_socket[i].handler(pxa_pcmcia_socket[i].handler_info,
++                                          events);
++    }
++
++  } while(all_events);
++}  /* pxa_pcmcia_task_handler() */
++
++static struct tq_struct pxa_pcmcia_task = {
++      routine: pxa_pcmcia_task_handler
++};
++
++
++/* pxa_pcmcia_poll_event()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Let's poll for events in addition to IRQs since IRQ only is unreliable...
++ */
++static void pxa_pcmcia_poll_event(unsigned long dummy)
++{
++  DEBUG(3, "%s(): polling for events\n", __FUNCTION__);
++  poll_timer.function = pxa_pcmcia_poll_event;
++  poll_timer.expires = jiffies + PXA_PCMCIA_POLL_PERIOD;
++  add_timer(&poll_timer);
++  schedule_task(&pxa_pcmcia_task);
++}
++
++
++/* pxa_pcmcia_interrupt()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Service routine for socket driver interrupts (requested by the
++ * low-level PCMCIA init() operation via pxa_pcmcia_thread()).
++ * The actual interrupt-servicing work is performed by
++ * pxa_pcmcia_thread(), largely because the Card Services event-
++ * handling code performs scheduling operations which cannot be
++ * executed from within an interrupt context.
++ */
++static void pxa_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){
++  DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
++  schedule_task(&pxa_pcmcia_task);
++}
++
++
++/* pxa_pcmcia_register_callback()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the register_callback() operation for the in-kernel
++ * PCMCIA service (formerly SS_RegisterCallback in Card Services). If 
++ * the function pointer `handler' is not NULL, remember the callback 
++ * location in the state for `sock', and increment the usage counter 
++ * for the driver module. (The callback is invoked from the interrupt
++ * service routine, pxa_pcmcia_interrupt(), to notify Card Services
++ * of interesting events.) Otherwise, clear the callback pointer in the
++ * socket state and decrement the module usage count.
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_register_callback(unsigned int sock,
++                                         void (*handler)(void *,
++                                                         unsigned int),
++                                         void *info){
++  if(handler==NULL){
++    pxa_pcmcia_socket[sock].handler=NULL;
++    MOD_DEC_USE_COUNT;
++  } else {
++    MOD_INC_USE_COUNT;
++    pxa_pcmcia_socket[sock].handler=handler;
++    pxa_pcmcia_socket[sock].handler_info=info;
++  }
++
++  return 0;
++}
++
++
++/* pxa_pcmcia_inquire_socket()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the inquire_socket() operation for the in-kernel PCMCIA
++ * service (formerly SS_InquireSocket in Card Services). Of note is
++ * the setting of the SS_CAP_PAGE_REGS bit in the `features' field of
++ * `cap' to "trick" Card Services into tolerating large "I/O memory" 
++ * addresses. Also set is SS_CAP_STATIC_MAP, which disables the memory
++ * resource database check. (Mapped memory is set up within the socket
++ * driver itself.)
++ *
++ * In conjunction with the STATIC_MAP capability is a new field,
++ * `io_offset', recommended by David Hinds. Rather than go through
++ * the SetIOMap interface (which is not quite suited for communicating
++ * window locations up from the socket driver), we just pass up
++ * an offset which is applied to client-requested base I/O addresses
++ * in alloc_io_space().
++ *
++ * Returns: 0 on success, -1 if no pin has been configured for `sock'
++ */
++static int pxa_pcmcia_inquire_socket(unsigned int sock,
++                                      socket_cap_t *cap){
++  struct pcmcia_irq_info irq_info;
++
++  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  if(sock>=pxa_pcmcia_socket_count){
++    printk(KERN_ERR "pxa_cs: socket %u not configured\n", sock);
++    return -1;
++  }
++
++  /* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the
++   *   force_low argument to validate_mem() in rsrc_mgr.c -- since in
++   *   general, the mapped * addresses of the PCMCIA memory regions
++   *   will not be within 0xffff, setting force_low would be
++   *   undesirable.
++   *
++   * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory
++   *   resource database; we instead pass up physical address ranges
++   *   and allow other parts of Card Services to deal with remapping.
++   *
++   * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but
++   *   not 32-bit CardBus devices.
++   */
++  cap->features=(SS_CAP_PAGE_REGS  | SS_CAP_STATIC_MAP | SS_CAP_PCCARD);
++
++  irq_info.sock=sock;
++  irq_info.irq=-1;
++
++  if(pcmcia_low_level->get_irq_info(&irq_info)<0){
++    printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n",
++         sock);
++    return -1;
++  }
++
++  cap->irq_mask=0;
++  cap->map_size=PAGE_SIZE;
++  cap->pci_irq=irq_info.irq;
++  cap->io_offset=pxa_pcmcia_socket[sock].virt_io;
++
++  return 0;
++
++}  /* pxa_pcmcia_inquire_socket() */
++
++
++/* pxa_pcmcia_get_status()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the get_status() operation for the in-kernel PCMCIA
++ * service (formerly SS_GetStatus in Card Services). Essentially just
++ * fills in bits in `status' according to internal driver state or
++ * the value of the voltage detect chipselect register.
++ *
++ * As a debugging note, during card startup, the PCMCIA core issues
++ * three set_socket() commands in a row the first with RESET deasserted,
++ * the second with RESET asserted, and the last with RESET deasserted
++ * again. Following the third set_socket(), a get_status() command will
++ * be issued. The kernel is looking for the SS_READY flag (see
++ * setup_socket(), reset_socket(), and unreset_socket() in cs.c).
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_get_status(unsigned int sock,
++                                  unsigned int *status){
++  struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK];
++  struct pcmcia_state_array state_array;
++
++  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  state_array.size=pxa_pcmcia_socket_count;
++  state_array.state=state;
++
++  if((pcmcia_low_level->socket_state(&state_array))<0){
++    printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
++    return -1;
++  }
++
++  pxa_pcmcia_socket[sock].k_state=state[sock];
++
++  *status=state[sock].detect?SS_DETECT:0;
++
++  *status|=state[sock].ready?SS_READY:0;
++
++  /* The power status of individual sockets is not available
++   * explicitly from the hardware, so we just remember the state
++   * and regurgitate it upon request:
++   */
++  *status|=pxa_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0;
++
++  if(pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)
++    *status|=state[sock].bvd1?SS_STSCHG:0;
++  else {
++    if(state[sock].bvd1==0)
++      *status|=SS_BATDEAD;
++    else if(state[sock].bvd2==0)
++      *status|=SS_BATWARN;
++  }
++
++  *status|=state[sock].vs_3v?SS_3VCARD:0;
++
++  *status|=state[sock].vs_Xv?SS_XVCARD:0;
++
++  DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n",
++      (*status&SS_DETECT)?"DETECT ":"",
++      (*status&SS_READY)?"READY ":"", 
++      (*status&SS_BATDEAD)?"BATDEAD ":"",
++      (*status&SS_BATWARN)?"BATWARN ":"",
++      (*status&SS_POWERON)?"POWERON ":"",
++      (*status&SS_STSCHG)?"STSCHG ":"",
++      (*status&SS_3VCARD)?"3VCARD ":"",
++      (*status&SS_XVCARD)?"XVCARD ":"");
++
++  return 0;
++
++}  /* pxa_pcmcia_get_status() */
++
++
++/* pxa_pcmcia_get_socket()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the get_socket() operation for the in-kernel PCMCIA
++ * service (formerly SS_GetSocket in Card Services). Not a very 
++ * exciting routine.
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_get_socket(unsigned int sock,
++                                  socket_state_t *state){
++
++  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  /* This information was given to us in an earlier call to set_socket(),
++   * so we're just regurgitating it here:
++   */
++  *state=pxa_pcmcia_socket[sock].cs_state;
++
++  return 0;
++}
++
++
++/* pxa_pcmcia_set_socket()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the set_socket() operation for the in-kernel PCMCIA
++ * service (formerly SS_SetSocket in Card Services). We more or
++ * less punt all of this work and let the kernel handle the details
++ * of power configuration, reset, &c. We also record the value of
++ * `state' in order to regurgitate it to the PCMCIA core later.
++ *
++ * Returns: 0
++ */
++static int pxa_pcmcia_set_socket(unsigned int sock,
++                                  socket_state_t *state){
++
++  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  DEBUG(3, "\tmask:  %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n"
++      "\tVcc %d  Vpp %d  irq %d\n",
++      (state->csc_mask==0)?"<NONE>":"",
++      (state->csc_mask&SS_DETECT)?"DETECT ":"",
++      (state->csc_mask&SS_READY)?"READY ":"",
++      (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"",
++      (state->csc_mask&SS_BATWARN)?"BATWARN ":"",
++      (state->csc_mask&SS_STSCHG)?"STSCHG ":"",
++      (state->flags==0)?"<NONE>":"",
++      (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"",
++      (state->flags&SS_IOCARD)?"IOCARD ":"",
++      (state->flags&SS_RESET)?"RESET ":"",
++      (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"",
++      (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"",
++      state->Vcc, state->Vpp, state->io_irq);
++
++  if(pcmcia_low_level->configure_socket(sock, state)<0){
++    printk(KERN_ERR "Unable to configure socket %u\n", sock);
++    return -1;
++  }
++
++  pxa_pcmcia_socket[sock].cs_state=*state;
++  
++  return 0;
++
++}  /* pxa_pcmcia_set_socket() */
++
++
++/* pxa_pcmcia_get_io_map()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the get_io_map() operation for the in-kernel PCMCIA
++ * service (formerly SS_GetIOMap in Card Services). Just returns an
++ * I/O map descriptor which was assigned earlier by a set_io_map().
++ *
++ * Returns: 0 on success, -1 if the map index was out of range
++ */
++static int pxa_pcmcia_get_io_map(unsigned int sock,
++                                  struct pccard_io_map *map){
++
++  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  if(map->map>=MAX_IO_WIN){
++    printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
++         map->map);
++    return -1;
++  }
++
++  *map=pxa_pcmcia_socket[sock].io_map[map->map];
++
++  return 0;
++}
++
++
++/* pxa_pcmcia_set_io_map()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the set_io_map() operation for the in-kernel PCMCIA
++ * service (formerly SS_SetIOMap in Card Services). We configure
++ * the map speed as requested, but override the address ranges
++ * supplied by Card Services.
++ *
++ * Returns: 0 on success, -1 on error
++ */
++static int pxa_pcmcia_set_io_map(unsigned int sock,
++                                  struct pccard_io_map *map){
++  unsigned int clock, speed;
++  unsigned long mecr, start;
++
++  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  DEBUG(4, "\tmap %u  speed %u\n\tstart 0x%08lx  stop 0x%08lx\n"
++      "\tflags: %s%s%s%s%s%s%s%s\n",
++      map->map, map->speed, map->start, map->stop,
++      (map->flags==0)?"<NONE>":"",
++      (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
++      (map->flags&MAP_16BIT)?"16BIT ":"",
++      (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
++      (map->flags&MAP_0WS)?"0WS ":"",
++      (map->flags&MAP_WRPROT)?"WRPROT ":"",
++      (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"",
++      (map->flags&MAP_PREFETCH)?"PREFETCH ":"");
++
++  if(map->map>=MAX_IO_WIN){
++    printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
++         map->map);
++    return -1;
++  }
++
++  if(map->flags&MAP_ACTIVE){
++
++    speed=(map->speed>0)?map->speed:PXA_PCMCIA_IO_ACCESS;
++
++    clock = get_lclk_frequency_10khz();
++
++    pxa_pcmcia_socket[sock].speed_io=speed;
++
++    if (sock == 0) {
++      MCIO0 = ((pxa_mcxx_setup(speed, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++            | ((pxa_mcxx_asst(speed, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++            | ((pxa_mcxx_hold(speed, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++    } else {
++      MCIO1 = ((pxa_mcxx_setup(speed, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++            | ((pxa_mcxx_asst(speed, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++            | ((pxa_mcxx_hold(speed, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++    }
++
++    DEBUG(4, "%s(): FAST%u %lx  BSM%u %lx  BSA%u %lx  BSIO%u %lx\n",
++        __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock,
++        MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), 
++        sock, MECR_BSIO_GET(mecr, sock));
++
++  }
++
++  start=map->start;
++
++  if(map->stop==1)
++    map->stop=PAGE_SIZE-1;
++
++  map->start=pxa_pcmcia_socket[sock].virt_io;
++  map->stop=map->start+(map->stop-start);
++
++  pxa_pcmcia_socket[sock].io_map[map->map]=*map;
++
++  return 0;
++
++}  /* pxa_pcmcia_set_io_map() */
++
++
++/* pxa_pcmcia_get_mem_map()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the get_mem_map() operation for the in-kernel PCMCIA
++ * service (formerly SS_GetMemMap in Card Services). Just returns a
++ *  memory map descriptor which was assigned earlier by a
++ *  set_mem_map() request.
++ *
++ * Returns: 0 on success, -1 if the map index was out of range
++ */
++static int pxa_pcmcia_get_mem_map(unsigned int sock,
++                                   struct pccard_mem_map *map){
++
++  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  if(map->map>=MAX_WIN){
++    printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
++         map->map);
++    return -1;
++  }
++
++  *map=pxa_pcmcia_socket[sock].mem_map[map->map];
++
++  return 0;
++}
++
++
++/* pxa_pcmcia_set_mem_map()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the set_mem_map() operation for the in-kernel PCMCIA
++ * service (formerly SS_SetMemMap in Card Services). We configure
++ * the map speed as requested, but override the address ranges
++ * supplied by Card Services.
++ *
++ * Returns: 0 on success, -1 on error
++ */
++static int pxa_pcmcia_set_mem_map(unsigned int sock,
++                                   struct pccard_mem_map *map){
++  unsigned int clock, speed;
++  unsigned long mecr, start;
++
++  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  DEBUG(4, "\tmap %u  speed %u\n\tsys_start  %#lx\n"
++      "\tsys_stop   %#lx\n\tcard_start %#x\n"
++      "\tflags: %s%s%s%s%s%s%s%s\n",
++      map->map, map->speed, map->sys_start, map->sys_stop,
++      map->card_start, (map->flags==0)?"<NONE>":"",
++      (map->flags&MAP_ACTIVE)?"ACTIVE ":"",
++      (map->flags&MAP_16BIT)?"16BIT ":"",
++      (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"",
++      (map->flags&MAP_0WS)?"0WS ":"",
++      (map->flags&MAP_WRPROT)?"WRPROT ":"",
++      (map->flags&MAP_ATTRIB)?"ATTRIB ":"",
++      (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"");
++
++  if(map->map>=MAX_WIN){
++    printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__,
++         map->map);
++    return -1;
++  }
++
++  if(map->flags&MAP_ACTIVE){
++    /* When clients issue RequestMap, the access speed is not always
++     * properly configured:
++     */
++    if(map->speed > 0)
++      speed = map->speed;
++    else
++      switch(pxa_pcmcia_socket[sock].cs_state.Vcc){
++      case 33:
++      speed = PXA_PCMCIA_3V_MEM_ACCESS;
++      break;
++      default:
++      speed = PXA_PCMCIA_5V_MEM_ACCESS;
++      }
++
++    clock = get_lclk_frequency_10khz();
++
++    if(map->flags&MAP_ATTRIB){
++      if (sock == 0) {
++        MCATT0 = ((pxa_mcxx_setup(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++               | ((pxa_mcxx_asst(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++               | ((pxa_mcxx_hold(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++      } else {
++        MCATT1 = ((pxa_mcxx_setup(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++               | ((pxa_mcxx_asst(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++               | ((pxa_mcxx_hold(PXA_PCMCIA_ATTR_MEM_ACCESS, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++      }
++      pxa_pcmcia_socket[sock].speed_attr=speed;
++    } else {
++      if (sock == 0) {
++        MCMEM0 = ((pxa_mcxx_setup(speed, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++               | ((pxa_mcxx_asst(speed, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++               | ((pxa_mcxx_hold(speed, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++      } else {
++        MCMEM1 = ((pxa_mcxx_setup(speed, clock)
++                      & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
++               | ((pxa_mcxx_asst(speed, clock)
++                      & MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
++               | ((pxa_mcxx_hold(speed, clock)
++                      & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
++      }
++      pxa_pcmcia_socket[sock].speed_mem=speed;
++    }
++    DEBUG(4, "%s(): FAST%u %lx  BSM%u %lx  BSA%u %lx  BSIO%u %lx\n",
++        __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock,
++        MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), 
++        sock, MECR_BSIO_GET(mecr, sock));
++  }
++
++  start=map->sys_start;
++
++  if(map->sys_stop==0)
++    map->sys_stop=PAGE_SIZE-1;
++
++  map->sys_start=(map->flags & MAP_ATTRIB)?\
++    pxa_pcmcia_socket[sock].phys_attr:\
++    pxa_pcmcia_socket[sock].phys_mem;
++
++  map->sys_stop=map->sys_start+(map->sys_stop-start);
++
++  pxa_pcmcia_socket[sock].mem_map[map->map]=*map;
++
++  return 0;
++
++}  /* pxa_pcmcia_set_mem_map() */
++
++
++#if defined(CONFIG_PROC_FS)
++
++/* pxa_pcmcia_proc_setup()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the proc_setup() operation for the in-kernel PCMCIA
++ * service (formerly SS_ProcSetup in Card Services).
++ *
++ * Returns: 0 on success, -1 on error
++ */
++static void pxa_pcmcia_proc_setup(unsigned int sock,
++                                   struct proc_dir_entry *base){
++  struct proc_dir_entry *entry;
++
++  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
++
++  if((entry=create_proc_entry("status", 0, base))==NULL){
++    printk(KERN_ERR "Unable to install \"status\" procfs entry\n");
++    return;
++  }
++
++  entry->read_proc=pxa_pcmcia_proc_status;
++  entry->data=(void *)sock;
++}
++
++
++/* pxa_pcmcia_proc_status()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * Implements the /proc/bus/pccard/??/status file.
++ *
++ * Returns: the number of characters added to the buffer
++ */
++static int pxa_pcmcia_proc_status(char *buf, char **start, off_t pos,
++                                   int count, int *eof, void *data){
++  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 ":"",
++           pxa_pcmcia_socket[sock].k_state.ready?"ready ":"",
++           pxa_pcmcia_socket[sock].k_state.bvd1?"bvd1 ":"",
++           pxa_pcmcia_socket[sock].k_state.bvd2?"bvd2 ":"",
++           pxa_pcmcia_socket[sock].k_state.wrprot?"wrprot ":"",
++           pxa_pcmcia_socket[sock].k_state.vs_3v?"vs_3v ":"",
++           pxa_pcmcia_socket[sock].k_state.vs_Xv?"vs_Xv ":"");
++
++  p+=sprintf(p, "status   : %s%s%s%s%s%s%s%s%s\n",
++           pxa_pcmcia_socket[sock].k_state.detect?"SS_DETECT ":"",
++           pxa_pcmcia_socket[sock].k_state.ready?"SS_READY ":"",
++           pxa_pcmcia_socket[sock].cs_state.Vcc?"SS_POWERON ":"",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\
++           "SS_IOCARD ":"",
++           (pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD &&
++            pxa_pcmcia_socket[sock].k_state.bvd1)?"SS_STSCHG ":"",
++           ((pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 &&
++            (pxa_pcmcia_socket[sock].k_state.bvd1==0))?"SS_BATDEAD ":"",
++           ((pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)==0 &&
++            (pxa_pcmcia_socket[sock].k_state.bvd2==0))?"SS_BATWARN ":"",
++           pxa_pcmcia_socket[sock].k_state.vs_3v?"SS_3VCARD ":"",
++           pxa_pcmcia_socket[sock].k_state.vs_Xv?"SS_XVCARD ":"");
++
++  p+=sprintf(p, "mask     : %s%s%s%s%s\n",
++           pxa_pcmcia_socket[sock].cs_state.csc_mask&SS_DETECT?\
++           "SS_DETECT ":"",
++           pxa_pcmcia_socket[sock].cs_state.csc_mask&SS_READY?\
++           "SS_READY ":"",
++           pxa_pcmcia_socket[sock].cs_state.csc_mask&SS_BATDEAD?\
++           "SS_BATDEAD ":"",
++           pxa_pcmcia_socket[sock].cs_state.csc_mask&SS_BATWARN?\
++           "SS_BATWARN ":"",
++           pxa_pcmcia_socket[sock].cs_state.csc_mask&SS_STSCHG?\
++           "SS_STSCHG ":"");
++
++  p+=sprintf(p, "cs_flags : %s%s%s%s%s\n",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_PWR_AUTO?\
++           "SS_PWR_AUTO ":"",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD?\
++           "SS_IOCARD ":"",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_RESET?\
++           "SS_RESET ":"",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_SPKR_ENA?\
++           "SS_SPKR_ENA ":"",
++           pxa_pcmcia_socket[sock].cs_state.flags&SS_OUTPUT_ENA?\
++           "SS_OUTPUT_ENA ":"");
++
++  p+=sprintf(p, "Vcc      : %d\n", pxa_pcmcia_socket[sock].cs_state.Vcc);
++
++  p+=sprintf(p, "Vpp      : %d\n", pxa_pcmcia_socket[sock].cs_state.Vpp);
++
++  p+=sprintf(p, "irq      : %d\n", pxa_pcmcia_socket[sock].cs_state.io_irq);
++
++  p+=sprintf(p, "I/O      : %u (%u)\n", pxa_pcmcia_socket[sock].speed_io,
++             sock ?
++               pxa_pcmcia_cmd_time(clock,
++              ((MCIO1 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)) :
++               pxa_pcmcia_cmd_time(clock,
++              ((MCIO0 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)));
++
++  p+=sprintf(p, "attribute: %u (%u)\n", pxa_pcmcia_socket[sock].speed_attr,
++             sock ?
++               pxa_pcmcia_cmd_time(clock,
++              ((MCATT1 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)) :
++               pxa_pcmcia_cmd_time(clock,
++              ((MCATT0 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)));
++
++  p+=sprintf(p, "common   : %u (%u)\n", pxa_pcmcia_socket[sock].speed_mem,
++             sock ?
++               pxa_pcmcia_cmd_time(clock,
++              ((MCMEM1 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)) :
++               pxa_pcmcia_cmd_time(clock,
++              ((MCMEM0 >> MCXX_ASST_SHIFT) & MCXX_ASST_MASK)));
++
++  return p-buf;
++}
++
++#endif  /* defined(CONFIG_PROC_FS) */
++
++
++#ifdef CONFIG_CPU_FREQ
++
++/* pxa_pcmcia_update_mecr()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * When pxa_pcmcia_notifier() decides that a MECR adjustment (due
++ * to a core clock frequency change) is needed, this routine establishes
++ * new BS_xx values consistent with the clock speed `clock'.
++ */
++static void pxa_pcmcia_update_mecr(unsigned int clock){
++  unsigned int sock;
++
++  for(sock = 0; sock < PXA_PCMCIA_MAX_SOCK; ++sock){
++
++    // REVISIT: MCXX macros needed here
++    // MECR_BSIO_SET(mecr, sock,
++//              pxa_pcmcia_mecr_bs(pxa_pcmcia_socket[sock].speed_io,
++//                                    clock));
++    // MECR_BSA_SET(mecr, sock,
++//             pxa_pcmcia_mecr_bs(pxa_pcmcia_socket[sock].speed_attr,
++//                                   clock));
++    // MECR_BSM_SET(mecr, sock,
++//             pxa_pcmcia_mecr_bs(pxa_pcmcia_socket[sock].speed_mem,
++//                                   clock));
++  }
++}
++
++/* pxa_pcmcia_notifier()
++ * ^^^^^^^^^^^^^^^^^^^^^^^^
++ * When changing the processor core clock frequency, it is necessary
++ * to adjust the MECR timings accordingly. We've recorded the timings
++ * requested by Card Services, so this is just a matter of finding
++ * out what our current speed is, and then recomputing the new MECR
++ * values.
++ *
++ * Returns: 0 on success, -1 on error
++ */
++static int pxa_pcmcia_notifier(struct notifier_block *nb,
++                                unsigned long val, void *data){
++  struct cpufreq_info *ci = data;
++
++  switch(val){
++  case CPUFREQ_MINMAX:
++
++    break;
++
++  case CPUFREQ_PRECHANGE:
++
++    if(ci->new_freq > ci->old_freq){
++      DEBUG(2, "%s(): new frequency %u.%uMHz > %u.%uMHz, pre-updating\n",
++          __FUNCTION__,
++          ci->new_freq / 1000, (ci->new_freq / 100) % 10,
++          ci->old_freq / 1000, (ci->old_freq / 100) % 10);
++      pxa_pcmcia_update_mecr(ci->new_freq);
++    }
++
++    break;
++
++  case CPUFREQ_POSTCHANGE:
++
++    if(ci->new_freq < ci->old_freq){
++      DEBUG(2, "%s(): new frequency %u.%uMHz < %u.%uMHz, post-updating\n",
++          __FUNCTION__,
++          ci->new_freq / 1000, (ci->new_freq / 100) % 10,
++          ci->old_freq / 1000, (ci->old_freq / 100) % 10);
++      pxa_pcmcia_update_mecr(ci->new_freq);
++    }
++
++    break;
++
++  default:
++    printk(KERN_ERR "%s(): unknown CPU frequency event %lx\n", __FUNCTION__,
++         val);
++    return -1;
++
++  }
++
++  return 0;
++
++}
++
++static struct notifier_block pxa_pcmcia_notifier_block = {
++  notifier_call: pxa_pcmcia_notifier
++};
++
++#endif
++
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/pxa.h
+@@ -0,0 +1,233 @@
++/*
++ * linux/drivers/pcmcia/pxa/pxa.h
++ *
++ * Author:    George Davis
++ * Created:   Jan 10, 2002
++ * Copyright: 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.
++ *
++ * Originally based upon linux/drivers/pcmcia/sa1100_generic.h
++ *
++ */
++
++/*======================================================================
++
++    Device driver for the PCMCIA control functionality of Intel
++    PXA250/210 microprocessors.
++
++    The contents of this file are subject to the Mozilla Public
++    License Version 1.1 (the "License"); you may not use this file
++    except in compliance with the License. You may obtain a copy of
++    the License at http://www.mozilla.org/MPL/
++
++    Software distributed under the License is distributed on an "AS
++    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++    implied. See the License for the specific language governing
++    rights and limitations under the License.
++
++    The initial developer of the original code is John G. Dorsey
++    <john+@cs.cmu.edu>.  Portions created by John G. Dorsey are
++    Copyright (C) 1999 John G. Dorsey.  All Rights Reserved.
++
++    Alternatively, the contents of this file may be used under the
++    terms of the GNU 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 MPL, 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 MPL or the GPL.
++    
++======================================================================*/
++
++#if !defined(_PCMCIA_PXA_H)
++# define _PCMCIA_PXA_H
++
++#include <pcmcia/cs_types.h>
++#include <pcmcia/ss.h>
++#include <pcmcia/bulkmem.h>
++#include <pcmcia/cistpl.h>
++#include "../cs_internal.h"
++
++#include <asm/arch/pcmcia.h>
++
++
++/* MECR: Expansion Memory Configuration Register
++ * (SA-1100 Developers Manual, p.10-13; SA-1110 Developers Manual, p.10-24)
++ *
++ * MECR layout is:  
++ *
++ *   FAST1 BSM1<4:0> BSA1<4:0> BSIO1<4:0> FAST0 BSM0<4:0> BSA0<4:0> BSIO0<4:0>
++ *
++ * (This layout is actually true only for the SA-1110; the FASTn bits are
++ * reserved on the SA-1100.)
++ */
++
++#define MCXX_SETUP_MASK     (0x7f)
++#define MCXX_ASST_MASK      (0x1f)
++#define MCXX_HOLD_MASK      (0x3f)
++#define MCXX_SETUP_SHIFT    (0)
++#define MCXX_ASST_SHIFT     (7)
++#define MCXX_HOLD_SHIFT     (14)
++
++
++#define MECR_SET(mecr, sock, shift, mask, bs) \
++((mecr)=((mecr)&~(((mask)<<(shift))<<\
++                  ((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\
++        (((bs)<<(shift))<<((sock)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))
++
++#define MECR_GET(mecr, sock, shift, mask) \
++((((mecr)>>(((sock)==0)?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT))>>\
++ (shift))&(mask))
++
++#define MECR_BSIO_SET(mecr, sock, bs) \
++MECR_SET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK, (bs))
++
++#define MECR_BSIO_GET(mecr, sock) \
++MECR_GET((mecr), (sock), MECR_BSIO_SHIFT, MECR_BS_MASK)
++
++#define MECR_BSA_SET(mecr, sock, bs) \
++MECR_SET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK, (bs))
++
++#define MECR_BSA_GET(mecr, sock) \
++MECR_GET((mecr), (sock), MECR_BSA_SHIFT, MECR_BS_MASK)
++
++#define MECR_BSM_SET(mecr, sock, bs) \
++MECR_SET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK, (bs))
++
++#define MECR_BSM_GET(mecr, sock) \
++MECR_GET((mecr), (sock), MECR_BSM_SHIFT, MECR_BS_MASK)
++
++#define MECR_FAST_SET(mecr, sock, fast) \
++MECR_SET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK, (fast))
++
++#define MECR_FAST_GET(mecr, sock) \
++MECR_GET((mecr), (sock), MECR_FAST_SHIFT, MECR_FAST_MODE_MASK)
++
++
++/* This function implements the BS value calculation for setting the MECR
++ * using integer arithmetic:
++ */
++static inline unsigned int pxa_pcmcia_mecr_bs(unsigned int pcmcia_cycle_ns,
++                                               unsigned int cpu_clock_khz){
++  unsigned int t = ((pcmcia_cycle_ns * cpu_clock_khz) / 6) - 1000000;
++  return (t / 1000000) + (((t % 1000000) == 0) ? 0 : 1);
++}
++
++static inline u_int pxa_mcxx_hold(u_int pcmcia_cycle_ns,
++                                          u_int mem_clk_10khz){
++  u_int code = pcmcia_cycle_ns * mem_clk_10khz;
++  return (code / 300000) + ((code % 300000) ? 1 : 0);
++}
++
++static inline u_int pxa_mcxx_asst(u_int pcmcia_cycle_ns,
++                                          u_int mem_clk_10khz){
++  u_int code = pcmcia_cycle_ns * mem_clk_10khz;
++  return (code / 300000) + ((code % 300000) ? 1 : 0);
++}
++
++static inline u_int pxa_mcxx_setup(u_int pcmcia_cycle_ns,
++                                          u_int mem_clk_10khz){
++  u_int code = pcmcia_cycle_ns * mem_clk_10khz;
++  return (code / 100000) + ((code % 100000) ? 1 : 0) + 1;
++}
++
++/* This function returns the (approxmiate) command assertion period, in
++ * nanoseconds, for a given CPU clock frequency and MCXX_ASST value:
++ */
++
++static inline u_int pxa_pcmcia_cmd_time(u_int mem_clk_10khz,
++                                         u_int pcmcia_mcxx_asst){
++  return (300000 * (pcmcia_mcxx_asst + 1) / mem_clk_10khz);
++}
++
++
++/* SA-1100 PCMCIA Memory and I/O timing
++ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++ * The SA-1110 Developer's Manual, section 10.2.5, says the following:
++ *
++ *  "To calculate the recommended BS_xx value for each address space:
++ *   divide the command width time (the greater of twIOWR and twIORD,
++ *   or the greater of twWE and twOE) by processor cycle time; divide
++ *   by 2; divide again by 3 (number of BCLK's per command assertion);
++ *   round up to the next whole number; and subtract 1."
++ *
++ * The PC Card Standard, Release 7, section 4.13.4, says that twIORD
++ * has a minimum value of 165ns. Section 4.13.5 says that twIOWR has
++ * a minimum value of 165ns, as well. Section 4.7.2 (describing
++ * common and attribute memory write timing) says that twWE has a
++ * minimum value of 150ns for a 250ns cycle time (for 5V operation;
++ * see section 4.7.4), or 300ns for a 600ns cycle time (for 3.3V
++ * operation, also section 4.7.4). Section 4.7.3 says that taOE
++ * has a maximum value of 150ns for a 300ns cycle time (for 5V
++ * operation), or 300ns for a 600ns cycle time (for 3.3V operation).
++ *
++ * When configuring memory maps, Card Services appears to adopt the policy
++ * that a memory access time of "0" means "use the default." The default
++ * PCMCIA I/O command width time is 165ns. The default PCMCIA 5V attribute
++ * and memory command width time is 150ns; the PCMCIA 3.3V attribute and
++ * memory command width time is 300ns.
++ */
++ 
++/* The PXA 250 and PXA 210 Application Processors Developer's Manual
++ * was used to determine correct PXA_PCMCIA_IO_ACCES time
++ */
++  
++#define PXA_PCMCIA_IO_ACCESS      (165) 
++
++/* Default PC Card Common Memory timings*/
++                                         
++#define PXA_PCMCIA_5V_MEM_ACCESS  (250)
++#define PXA_PCMCIA_3V_MEM_ACCESS  (250)
++
++/* Atrribute Memory timing - must be constant via PC Card standart*/
++ 
++#define PXA_PCMCIA_ATTR_MEM_ACCESS (300)
++
++
++/* The socket driver actually works nicely in interrupt-driven form,
++ * so the (relatively infrequent) polling is "just to be sure."
++ */
++#define PXA_PCMCIA_POLL_PERIOD    (2*HZ)
++
++
++/* This structure encapsulates per-socket state which we might need to
++ * use when responding to a Card Services query of some kind.
++ */
++struct pxa_pcmcia_socket {
++  socket_state_t        cs_state;
++  struct pcmcia_state   k_state;
++  unsigned int          irq;
++  void                  (*handler)(void *, unsigned int);
++  void                  *handler_info;
++  pccard_io_map         io_map[MAX_IO_WIN];
++  pccard_mem_map        mem_map[MAX_WIN];
++  ioaddr_t              virt_io, phys_attr, phys_mem;
++  unsigned short        speed_io, speed_attr, speed_mem;
++};
++
++
++/* I/O pins replacing memory pins
++ * (PCMCIA System Architecture, 2nd ed., by Don Anderson, p.75)
++ *
++ * These signals change meaning when going from memory-only to 
++ * memory-or-I/O interface:
++ */
++#define iostschg bvd1
++#define iospkr   bvd2
++
++
++/*
++ * Declaration for all implementation specific low_level operations.
++ */
++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 trizeps2_pcmcia_ops;
++
++#endif  /* !defined(_PCMCIA_PXA_H) */
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/pxa_idp.c
+@@ -0,0 +1,297 @@
++/*
++ * linux/drivers/pcmcia/pxa/pxa_idp.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 Accelent Systems, Inc. All Rights Reserved
++ * 
++ * Platform specific routines for the Accelent PXA250 IDP, based on those
++ * first done for the Lubbock.
++ *
++ * Version 1.0 2002-05-02  Jeff Sutherland <jeffs@accelent.com>
++ *
++ */
++
++#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 
++pxa_idp_pcmcia_init(struct pcmcia_init *init)
++{
++      int return_val = 0;
++
++      /* Set PCMCIA Socket 0 power to standby mode.
++      *  PXA IDP has dedicated CPLD pins for all this stuff :-)
++      */
++      
++      /* both slots disabled, reset NOT active */
++      IDP_CPLD_PCCARD_EN = PCC0_ENABLE | PCC1_ENABLE;
++
++      IDP_CPLD_PCCARD_PWR = 0; //all power to both slots off
++
++      GPDR(IRQ_TO_GPIO_2_80(PCMCIA_S0_CD_VALID)) &=
++          ~GPIO_bit(IRQ_TO_GPIO_2_80(PCMCIA_S0_CD_VALID));
++      GPDR(IRQ_TO_GPIO_2_80(PCMCIA_S1_CD_VALID)) &=
++          ~GPIO_bit(IRQ_TO_GPIO_2_80(PCMCIA_S1_CD_VALID));
++
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(PCMCIA_S0_CD_VALID),
++                        GPIO_BOTH_EDGES);
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(PCMCIA_S1_CD_VALID),
++                        GPIO_BOTH_EDGES);
++
++      /* irq's for slots: */
++      GPDR(IRQ_TO_GPIO_2_80(PCMCIA_S0_RDYINT)) &=
++          ~GPIO_bit(IRQ_TO_GPIO_2_80(PCMCIA_S0_RDYINT));
++      GPDR(IRQ_TO_GPIO_2_80(PCMCIA_S1_RDYINT)) &=
++          ~GPIO_bit(IRQ_TO_GPIO_2_80(PCMCIA_S1_RDYINT));
++
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(PCMCIA_S0_RDYINT),
++                        GPIO_FALLING_EDGE);
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(PCMCIA_S1_RDYINT),
++                        GPIO_FALLING_EDGE);
++
++      return_val =
++          request_irq(PCMCIA_S0_CD_VALID, init->handler, SA_INTERRUPT,
++                      "PXA PCMCIA CD0", NULL);
++
++      if (return_val < 0)
++              return -1;
++      
++      return_val +=
++          request_irq(PCMCIA_S1_CD_VALID, init->handler, SA_INTERRUPT,
++                      "PXA PCMCIA CD1", NULL);
++      
++      if (return_val < 0) {
++              free_irq(PCMCIA_S0_CD_VALID, NULL);
++              return -1;
++      }
++
++      return 2;
++}
++
++static int
++pxa_idp_pcmcia_shutdown(void)
++{
++
++      free_irq(PCMCIA_S0_CD_VALID, NULL);
++      free_irq(PCMCIA_S1_CD_VALID, NULL);
++  
++      IDP_CPLD_PCCARD_EN = 0x03;      //disable slots
++      udelay(200);
++      IDP_CPLD_PCCARD_PWR = 0; //shut off all power
++
++      return 0;
++}
++
++static int
++pxa_idp_pcmcia_socket_state(struct pcmcia_state_array *state_array)
++{
++      unsigned long status;
++      int return_val = 1;
++      int i;
++      volatile unsigned long *stat_regs[2] = { &IDP_CPLD_PCCARD0_STATUS,
++              &IDP_CPLD_PCCARD1_STATUS
++      };
++
++      if (state_array->size < 2)
++              return -1;
++
++      memset(state_array->state, 0,
++             (state_array->size) * sizeof (struct pcmcia_state));
++      
++      for (i = 0; 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;
++      }
++
++      return return_val;
++}
++
++static int
++pxa_idp_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
++{
++      switch (info->sock) {
++          case 0:
++              info->irq = PCMCIA_S0_RDYINT;
++              break;
++
++          case 1:
++              info->irq = PCMCIA_S1_RDYINT;
++              break;
++
++          default:
++              return -1;
++      }
++
++      return 0;
++}
++
++static int
++pxa_idp_pcmcia_configure_socket(unsigned int sock, socket_state_t *state)
++{
++  /* The PXA Idp uses the Maxim MAX1602, with the following connections:
++   *
++   * Socket 0 (PCMCIA):
++   *  MAX1602 PXA_IDP         Register
++   *  Pin     Signal          IDP_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          IDP_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)
++   *
++   */
++
++      switch (sock) {
++          case 0:
++              switch (state->Vcc) {
++                  case 0:
++                      IDP_CPLD_PCCARD_EN |= PCC0_ENABLE; // disable socket
++                      udelay(200);
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC0_PWR2 | PCC0_PWR3);
++                      break;
++
++                  case 33:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC0_PWR2 | PCC0_PWR3);
++                      IDP_CPLD_PCCARD_PWR |= PCC0_PWR3;
++                      IDP_CPLD_PCCARD_EN &= ~PCC0_ENABLE; //turn it on
++                      break;
++
++                  case 50:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC0_PWR2 | PCC0_PWR3);
++                      IDP_CPLD_PCCARD_PWR |= PCC0_PWR2;
++                      IDP_CPLD_PCCARD_EN &= ~PCC0_ENABLE;
++                      break;
++
++                  default:
++                      printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
++                             __FUNCTION__, state->Vcc);
++                      return -1;
++              }
++
++              switch (state->Vpp) {
++                  case 0:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC0_PWR0 | PCC0_PWR1);
++                      break;
++
++                  case 120:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC0_PWR0 | PCC0_PWR1);
++                      IDP_CPLD_PCCARD_PWR |= PCC0_PWR1;
++                      break;
++
++                  default:
++                      if (state->Vpp == state->Vcc)
++                              IDP_CPLD_PCCARD_PWR =
++                                  (IDP_CPLD_PCCARD_PWR &
++                                   ~(PCC0_PWR0 | PCC0_PWR1)) | PCC0_PWR0;
++                      else {
++                              printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
++                                     __FUNCTION__, state->Vpp);
++                      return -1;
++              }
++          }
++
++              IDP_CPLD_PCCARD_EN =
++                  (state->flags & SS_RESET) ? (IDP_CPLD_PCCARD_EN | PCC0_RESET)
++                  : (IDP_CPLD_PCCARD_EN & ~PCC0_RESET);
++          break;
++
++      case 1:
++              switch (state->Vcc) {
++                  case 0:
++                      IDP_CPLD_PCCARD_EN |= PCC1_ENABLE; // disable socket
++                      udelay(200);
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3);
++                      break;
++
++                  case 33:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3);
++                      IDP_CPLD_PCCARD_PWR |= PCC1_PWR3;
++                      IDP_CPLD_PCCARD_EN &= ~PCC1_ENABLE; //turn it on
++                      break;
++
++                  case 50:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC1_PWR2 | PCC1_PWR3);
++                      IDP_CPLD_PCCARD_PWR |= PCC1_PWR2;
++                      IDP_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:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1);
++                      break;
++
++                  case 120:
++                      IDP_CPLD_PCCARD_PWR &= ~(PCC1_PWR0 | PCC1_PWR1);
++                      IDP_CPLD_PCCARD_PWR |= PCC1_PWR1;
++                      break;
++
++                  default:
++                      if (state->Vpp == state->Vcc)
++                              IDP_CPLD_PCCARD_PWR =
++                                  (IDP_CPLD_PCCARD_PWR &
++                                   ~(PCC1_PWR0 | PCC1_PWR1)) | PCC1_PWR0;
++                      else {
++                              printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
++                                     __FUNCTION__, state->Vpp);
++                              return -1;
++                      }
++              }
++              IDP_CPLD_PCCARD_EN = (state->flags & SS_RESET) ? (IDP_CPLD_PCCARD_EN | PCC1_RESET)
++                  : (IDP_CPLD_PCCARD_EN & ~PCC1_RESET);
++          break;
++      }
++      return 0;
++}
++
++struct pcmcia_low_level pxa_idp_pcmcia_ops = { 
++  pxa_idp_pcmcia_init,
++  pxa_idp_pcmcia_shutdown,
++  pxa_idp_pcmcia_socket_state,
++  pxa_idp_pcmcia_get_irq_info,
++  pxa_idp_pcmcia_configure_socket
++};
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/pxa/trizeps2.c
+@@ -0,0 +1,187 @@
++/*
++ * linux/drivers/pcmcia/pxa/trizeps2.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 Accelent Systems, Inc. All Rights Reserved
++ * 
++ * Platform specific routines for the Keith-n-Koep Trizeps-II, based on IDP
++ *
++ * Copyright (c) 2003 Teradyne DS, Ltd.
++ * Port to Trizeps-2 MT6N board by Luc De Cock
++ *
++ */
++
++#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 trizeps2_pcmcia_init(struct pcmcia_init *init)
++{
++      int return_val = 0;
++      unsigned short *bcr = (unsigned short *) TRIZEPS2_BCR_BASE;
++      unsigned short val;
++
++      /* reset the PCMCIA controller */
++      val = trizeps2_bcr_shadow | BCR_PCMCIA_RESET;
++      *bcr = val;
++      udelay(500);
++      /* un-reset it again */
++      trizeps2_bcr_shadow &= ~BCR_PCMCIA_RESET;
++      /* enable the PCMCIA buffer */
++      trizeps2_bcr_shadow &= ~(1 << 5);
++      *bcr = trizeps2_bcr_shadow;
++
++      GPDR(IRQ_TO_GPIO_2_80(PCMCIA_S_CD_VALID)) &=
++              ~GPIO_bit(IRQ_TO_GPIO_2_80(PCMCIA_S_CD_VALID));
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(PCMCIA_S_CD_VALID),
++                              PCMCIA_S_CD_VALID_EDGE);
++      GPDR(IRQ_TO_GPIO(PCMCIA_S_RDYINT)) &=
++              ~GPIO_bit(IRQ_TO_GPIO(PCMCIA_S_RDYINT));
++      set_GPIO_IRQ_edge(IRQ_TO_GPIO(PCMCIA_S_RDYINT),
++                              PCMCIA_S_RDYINT_EDGE);
++
++      return_val = request_irq(PCMCIA_S_CD_VALID, init->handler, SA_INTERRUPT,
++                      "PXA PCMCIA CD", NULL);
++      if (return_val < 0) {
++              return -1;
++      }
++      /* only 1 slot */
++      return 1;
++}
++
++static int trizeps2_pcmcia_shutdown(void)
++{
++      free_irq(PCMCIA_S_CD_VALID, NULL);
++
++      unsigned short *bcr = (unsigned short *) TRIZEPS2_BCR_BASE;
++      trizeps2_bcr_shadow |= (1 << 5); /* pcmcia buffer off */
++      *bcr = trizeps2_bcr_shadow;
++      trizeps2_bcr_shadow &= 0xFFF0; /* pcmcia control logic grounded */
++      *bcr = trizeps2_bcr_shadow;
++
++      return 0;
++}
++
++static int trizeps2_pcmcia_socket_state(struct pcmcia_state_array *state_array)
++{
++      unsigned long status;
++      int return_val = 1;
++      volatile unsigned short *stat_regs[1] = {
++              &TRIZEPS2_PCCARD_STATUS
++      };
++
++      if (state_array->size < 1)
++              return -1;
++
++      memset(state_array->state, 0,
++             (state_array->size) * sizeof (struct pcmcia_state));
++      
++      status = *stat_regs[0];
++
++              /* this one is a gpio */
++      state_array->state[0].detect = (PCC_DETECT) ? 0 : 1;
++      state_array->state[0].ready =  (PCC_READY) ? 1 : 0;
++      state_array->state[0].bvd1   = (status & PCC_BVD1) ? 1 : 0;
++      state_array->state[0].bvd2   = (status & PCC_BVD2) ? 1 : 0;
++      state_array->state[0].wrprot = 0; /* r/w all the time */
++      state_array->state[0].vs_3v  = (status & PCC_VS1) ? 0 : 1;
++      state_array->state[0].vs_Xv  = (status & PCC_VS2) ? 0 : 1;
++
++      return return_val;
++}
++
++static int trizeps2_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
++{
++      switch (info->sock) {
++          case 0:
++              info->irq = PCMCIA_S_RDYINT;
++              break;
++
++          default:
++              return -1;
++      }
++
++      return 0;
++}
++
++static int trizeps2_pcmcia_configure_socket(unsigned int sock, socket_state_t *state)
++{
++      unsigned short cntr_logic = trizeps2_bcr_shadow & 0xF;
++      unsigned short *bcr = (unsigned short *) TRIZEPS2_BCR_BASE;
++
++      /* configure Vcc and Vpp */
++      switch (sock) {
++          case 0:
++              switch (state->Vcc) {
++                  case 0:
++                      cntr_logic &= ~(PCC_3V | PCC_5V);
++                      break;
++
++                  case 33:
++                      cntr_logic &= ~(PCC_3V | PCC_5V);
++                      cntr_logic |= PCC_3V;
++                      break;
++
++                  case 50:
++                      cntr_logic &= ~(PCC_3V | PCC_5V);
++                      cntr_logic |= PCC_5V;
++                      break;
++
++                  default:
++                      printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
++                             __FUNCTION__, state->Vcc);
++                      return -1;
++              }
++
++              switch (state->Vpp) {
++                  case 0:
++                      cntr_logic &= ~(PCC_EN0 | PCC_EN1);
++                      break;
++
++                  case 120:
++                      cntr_logic &= ~(PCC_EN0 | PCC_EN1);
++                      cntr_logic |= PCC_EN1;
++                      break;
++
++                  default:
++                      if (state->Vpp == state->Vcc) {
++                              cntr_logic &= ~(PCC_EN0 | PCC_EN1);
++                              cntr_logic |= PCC_EN0;
++                      }
++                      else {
++                              printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
++                                     __FUNCTION__, state->Vpp);
++                              return -1;
++                      }
++          }
++          trizeps2_bcr_shadow &= ~(PCC_EN0 | PCC_EN1 | PCC_3V | PCC_5V |
++                                      BCR_PCMCIA_RESET);
++          trizeps2_bcr_shadow |= cntr_logic;
++          *bcr = trizeps2_bcr_shadow;
++          /* reset PCMCIA controller if requested */
++          trizeps2_bcr_shadow |=
++                  (state->flags & SS_RESET) ? BCR_PCMCIA_RESET : 0;
++          *bcr = trizeps2_bcr_shadow;
++          udelay(500);
++          break;
++      }
++      return 0;
++}
++
++struct pcmcia_low_level trizeps2_pcmcia_ops = { 
++  trizeps2_pcmcia_init,
++  trizeps2_pcmcia_shutdown,
++  trizeps2_pcmcia_socket_state,
++  trizeps2_pcmcia_get_irq_info,
++  trizeps2_pcmcia_configure_socket
++};
++
+--- linux-2.4.27/drivers/pcmcia/sa1100_cerf.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/pcmcia/sa1100_cerf.c
+@@ -7,15 +7,25 @@
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/delay.h>
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
+-#include "sa1100_generic.h"
+-#ifdef CONFIG_SA1100_CERF_CPLD
+-#define CERF_SOCKET   0
++#include <pcmcia/ss.h>
++#include <asm/arch/pcmcia.h>
++#include "sa1100_cerf.h"
++
++/*
++ * Set this to zero to remove all the debug statements via
++ * dead code elimination
++ */
++//#define DEBUGGING       1
++
++#if DEBUGGING
++static unsigned int pcmcia_debug = DEBUGGING;
+ #else
+-#define CERF_SOCKET   1
++#define pcmcia_debug 0     /* gcc will remove all the debug code for us */
+ #endif
+ static struct irqs {
+@@ -23,122 +33,178 @@
+       unsigned int gpio;
+       const char *str;
+ } irqs[] = {
+-      { IRQ_GPIO_CF_CD,   GPIO_CF_CD,   "CF_CD"   },
+-      { IRQ_GPIO_CF_BVD2, GPIO_CF_BVD2, "CF_BVD2" },
+-      { IRQ_GPIO_CF_BVD1, GPIO_CF_BVD1, "CF_BVD1" }
++      { PCMCIA_IRQ_CF_CD,   PCMCIA_GPIO_CF_CD_EDGE,   "CF_CD"   },
++      { PCMCIA_IRQ_CF_BVD2, PCMCIA_GPIO_CF_BVD2_EDGE, "CF_BVD2" },
++      { PCMCIA_IRQ_CF_BVD1, PCMCIA_GPIO_CF_BVD1_EDGE, "CF_BVD1" }
+ };
++static void cerf_pcmcia_reset( void)
++{
++      int i;
++
++      // Make sure SKTSEL is 0 (single slot)
++      set_GPIO_mode(54 | GPIO_OUT);
++      GPCR1 = GPIO_bit(54);
++      set_GPIO_mode(GPIO54_pSKTSEL_MD);
++
++      PCMCIA_GPCR = PCMCIA_GPIO_CF_RESET_MASK;
++      mdelay(300);
++
++      PCMCIA_GPSR = PCMCIA_GPIO_CF_RESET_MASK;
++      udelay(20);
++
++      PCMCIA_GPCR = PCMCIA_GPIO_CF_RESET_MASK;
++      mdelay(50);
++
++      for( i=0; i<10; i++)
++      {
++              if( cerf_pcmcia_level_ready()) break;
++              mdelay(100);
++      }
++}
++
+ static int cerf_pcmcia_init(struct pcmcia_init *init)
+ {
+-  int i, res;
++      int i, res;
+-  set_GPIO_IRQ_edge( GPIO_CF_IRQ, GPIO_FALLING_EDGE );
++      if( pcmcia_debug)
++              printk( KERN_INFO "cerf_pcmcia_init: enter\n");
+-  for (i = 0; i < ARRAY_SIZE(irqs); i++) {
+-    set_GPIO_IRQ_edge(irqs[i].gpio, GPIO_NO_EDGES);
+-    res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
+-                    irqs[i].str, NULL);
+-    if (res)
+-      goto irq_err;
+-  }
++      cerf_pcmcia_set_gpio_direction();
+-  return 2;
++      set_GPIO_IRQ_edge( PCMCIA_GPIO_CF_IRQ_EDGE, GPIO_FALLING_EDGE );
+- irq_err:
+-  printk(KERN_ERR "%s: Request for IRQ%d failed\n", __FUNCTION__, irqs[i].irq);
++      for (i = 0; i < ARRAY_SIZE(irqs); i++) {
+-  while (i--)
+-    free_irq(irqs[i].irq, NULL);
++              set_GPIO_IRQ_edge(irqs[i].gpio, GPIO_BOTH_EDGES);
+-  return -1;
++              res = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
++                              irqs[i].str, NULL);
++              if (res)
++                      goto irq_err;
++      }
++
++      printk( KERN_INFO "PCMCIA for Cerf: OK\n");
++
++      return CERF_SOCKET+1; /* last socket used +1 */
++
++irq_err:
++      printk(KERN_ERR "%s: Request for IRQ%d failed\n", 
++              __FUNCTION__, irqs[i].irq);
++
++      while (i--)
++              free_irq(irqs[i].irq, NULL);
++
++      return -1;
+ }
+ static int cerf_pcmcia_shutdown(void)
+ {
+-  int i;
++      int i;
++      if( pcmcia_debug)
++              printk( KERN_INFO "cerf_pcmcia_shutdown: enter\n");
+-  for (i = 0; i < ARRAY_SIZE(irqs); i++)
+-    free_irq(irqs[i].irq, NULL);
++      for (i = 0; i < ARRAY_SIZE(irqs); i++)
++              free_irq(irqs[i].irq, NULL);
+-  return 0;
++      return 0;
+ }
+-static int cerf_pcmcia_socket_state(struct pcmcia_state_array
+-                                     *state_array){
+-  unsigned long levels;
+-  int i = CERF_SOCKET;
++static int cerf_pcmcia_socket_state(struct pcmcia_state_array *state_array)
++{
++      int i = CERF_SOCKET;
+-  if(state_array->size<2) return -1;
++      if( pcmcia_debug > 3)
++              printk( KERN_INFO "cerf_pcmcia_socket_state: i=%d, size=%d\n",
++                              i, state_array->size);
+-  levels=GPLR;
++        memset(state_array->state, 0,
++                        (state_array->size)*sizeof(struct pcmcia_state));
+-  state_array->state[i].detect=((levels & GPIO_CF_CD)==0)?1:0;
+-  state_array->state[i].ready=(levels & GPIO_CF_IRQ)?1:0;
+-  state_array->state[i].bvd1=(levels & GPIO_CF_BVD1)?1:0;
+-  state_array->state[i].bvd2=(levels & GPIO_CF_BVD2)?1:0;
+-  state_array->state[i].wrprot=0;
+-  state_array->state[i].vs_3v=1;
+-  state_array->state[i].vs_Xv=0;
++      state_array->state[i].detect = cerf_pcmcia_level_detect();
++      state_array->state[i].ready  = cerf_pcmcia_level_ready();
++      state_array->state[i].bvd1   = cerf_pcmcia_level_bvd1();
++      state_array->state[i].bvd2   = cerf_pcmcia_level_bvd2();
++      state_array->state[i].wrprot=0;
++      state_array->state[i].vs_3v=1;
++      state_array->state[i].vs_Xv=0;
+-  return 1;
++      if( pcmcia_debug > 3)
++              printk( KERN_INFO "cerf_pcmcia_socket_state: "
++                      "detect=%d ready=%d bvd1=%d bvd2=%d\n", 
++                      state_array->state[i].detect,
++                      state_array->state[i].ready,
++                      state_array->state[i].bvd1,
++                      state_array->state[i].bvd2);
++
++      return 1;
+ }
+ static int cerf_pcmcia_get_irq_info(struct pcmcia_irq_info *info){
+-  if(info->sock>1) return -1;
++      if( pcmcia_debug)
++              printk( KERN_INFO "cerf_pcmcia_get_irq_info: "
++                              "sock=%d\n", info->sock);
+-  if (info->sock == CERF_SOCKET)
+-    info->irq=IRQ_GPIO_CF_IRQ;
++      if(info->sock>1) return -1;
+-  return 0;
++      if (info->sock == CERF_SOCKET)
++              info->irq=PCMCIA_IRQ_CF_IRQ;
++
++      if( pcmcia_debug)
++              printk( KERN_INFO "cerf_pcmcia_get_irq_info: irq=%d\n",info->irq);
++
++      return 0;
+ }
+-static int cerf_pcmcia_configure_socket(const struct pcmcia_configure
+-                                         *configure)
++static int cerf_pcmcia_configure_socket( unsigned int sock, socket_state_t *state)
+ {
+-  if(configure->sock>1)
+-    return -1;
++      if( pcmcia_debug)
++              printk( KERN_INFO "cerf_pcmcia_configure_socket:"
++                      "sock=%d vcc=%d flags=%x\n",
++                      sock, state->Vcc, state->flags);
+-  if (configure->sock != CERF_SOCKET)
+-    return 0;
++      if(sock>1)
++              return -1;
+-  switch(configure->vcc){
+-  case 0:
+-    break;
++      if (sock != CERF_SOCKET)
++              return 0;
+-  case 50:
+-  case 33:
+-#ifdef CONFIG_SA1100_CERF_CPLD
+-     GPCR = GPIO_PWR_SHUTDOWN;
++      switch(state->Vcc){
++              case 0:
++                      break;
++
++              case 50:
++              case 33:
++#if defined(CONFIG_SA1100_CERF_CPLD)
++                        PCMCIA_GPDR |= PCMCIA_PWR_SHUTDOWN;
++                        PCMCIA_GPCR |= PCMCIA_PWR_SHUTDOWN;
+ #endif
+-     break;
++                        /* voltage selected automatically */
++                      break;
+-  default:
+-    printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
+-         configure->vcc);
+-    return -1;
+-  }
++              default:
++                      printk(KERN_ERR "%s(): unrecognized Vcc %u\n", 
++                              __FUNCTION__, state->Vcc);
++                      return -1;
++      }
+-  if(configure->reset)
+-  {
+-#ifdef CONFIG_SA1100_CERF_CPLD
+-    GPSR = GPIO_CF_RESET;
+-#endif
+-  }
+-  else
+-  {
+-#ifdef CONFIG_SA1100_CERF_CPLD
+-    GPCR = GPIO_CF_RESET;
+-#endif
+-  }
++      if(state->flags&SS_RESET)
++      {
++              cerf_pcmcia_reset();
++      }
+-  return 0;
++      return 0;
+ }
++#ifdef CONFIG_SA1100_CERF
+ static int cerf_pcmcia_socket_init(int sock)
+ {
+   int i;
++  if( pcmcia_debug)
++      printk( KERN_INFO "cerf_pcmcia_socket_init: sock=%d\n",sock);
++
+   if (sock == CERF_SOCKET)
+     for (i = 0; i < ARRAY_SIZE(irqs); i++)
+       set_GPIO_IRQ_edge(irqs[i].gpio, GPIO_BOTH_EDGES);
+@@ -150,21 +216,26 @@
+ {
+   int i;
++  if( pcmcia_debug)
++      printk( KERN_INFO "cerf_pcmcia_socket_suspend: sock=%d\n",sock);
++
+   if (sock == CERF_SOCKET)
+     for (i = 0; i < ARRAY_SIZE(irqs); i++)
+       set_GPIO_IRQ_edge(irqs[i].gpio, GPIO_NO_EDGES);
+   return 0;
+ }
++#endif
+ struct pcmcia_low_level cerf_pcmcia_ops = { 
+-  init:                       cerf_pcmcia_init,
+-  shutdown:           cerf_pcmcia_shutdown,
+-  socket_state:               cerf_pcmcia_socket_state,
+-  get_irq_info:               cerf_pcmcia_get_irq_info,
+-  configure_socket:   cerf_pcmcia_configure_socket,
++init:                 cerf_pcmcia_init,
++shutdown:             cerf_pcmcia_shutdown,
++socket_state:         cerf_pcmcia_socket_state,
++get_irq_info:         cerf_pcmcia_get_irq_info,
++configure_socket:     cerf_pcmcia_configure_socket,
+-  socket_init:                cerf_pcmcia_socket_init,
+-  socket_suspend:     cerf_pcmcia_socket_suspend,
++#ifdef CONFIG_SA1100_CERF
++socket_init:          cerf_pcmcia_socket_init,
++socket_suspend:               cerf_pcmcia_socket_suspend,
++#endif
+ };
+-
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/sa1100_cerf.h
+@@ -0,0 +1,138 @@
++/*
++ * drivers/pcmcia/cerf.h
++ *
++ * PCMCIA implementation routines for CerfBoard
++ * Based off the Assabet.
++ *
++ */
++#ifndef _LINUX_PCMCIA_CERF_H
++#define _LINUX_PCMCIA_CERF_H
++
++#include <linux/config.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/arch/pcmcia.h>
++
++#ifdef CONFIG_PXA_CERF /* PXA */
++
++#define PCMCIA_GPCR GPCR0
++#define PCMCIA_GPSR GPSR0
++
++#define PCMCIA_GPIO_CF_CD    14
++#define PCMCIA_GPIO_CF_IRQ   13
++#define PCMCIA_GPIO_CF_RESET 12
++#ifdef CONFIG_PXA_CERF_PDA
++# define PCMCIA_GPIO_CF_BVD1  11
++# define PCMCIA_GPIO_CF_BVD2  10
++#elif defined( CONFIG_PXA_CERF_BOARD)
++# define PCMCIA_GPIO_CF_BVD1  32
++# define PCMCIA_GPIO_CF_BVD2  10
++#endif
++
++#define PCMCIA_GPIO_CF_CD_MASK    (GPIO_bit(PCMCIA_GPIO_CF_CD))
++#define PCMCIA_GPIO_CF_IRQ_MASK   (GPIO_bit(PCMCIA_GPIO_CF_IRQ))
++#define PCMCIA_GPIO_CF_RESET_MASK (GPIO_bit(PCMCIA_GPIO_CF_RESET))
++#define PCMCIA_GPIO_CF_BVD1_MASK  (GPIO_bit(PCMCIA_GPIO_CF_BVD1))
++#define PCMCIA_GPIO_CF_BVD2_MASK  (GPIO_bit(PCMCIA_GPIO_CF_BVD2))
++
++#define PCMCIA_GPIO_CF_CD_EDGE    PCMCIA_GPIO_CF_CD
++#define PCMCIA_GPIO_CF_IRQ_EDGE   PCMCIA_GPIO_CF_IRQ
++#define PCMCIA_GPIO_CF_RESET_EDGE PCMCIA_GPIO_CF_RESET
++#define PCMCIA_GPIO_CF_BVD1_EDGE  PCMCIA_GPIO_CF_BVD1
++#define PCMCIA_GPIO_CF_BVD2_EDGE  PCMCIA_GPIO_CF_BVD2
++
++#define PCMCIA_IRQ_CF_CD   IRQ_GPIO(PCMCIA_GPIO_CF_CD)
++#define PCMCIA_IRQ_CF_IRQ  IRQ_GPIO(PCMCIA_GPIO_CF_IRQ)
++#define PCMCIA_IRQ_CF_BVD1 IRQ_GPIO(PCMCIA_GPIO_CF_BVD1)
++#define PCMCIA_IRQ_CF_BVD2 IRQ_GPIO(PCMCIA_GPIO_CF_BVD2)
++
++#define PCMCIA_PWR_SHUTDOWN 0 /* not needed */
++#define CERF_SOCKET 0
++
++inline void cerf_pcmcia_set_gpio_direction(void)
++{
++      GPDR(PCMCIA_GPIO_CF_CD)   &= ~(PCMCIA_GPIO_CF_CD_MASK);
++      GPDR(PCMCIA_GPIO_CF_BVD1) &= ~(PCMCIA_GPIO_CF_BVD1_MASK);
++      GPDR(PCMCIA_GPIO_CF_BVD2) &= ~(PCMCIA_GPIO_CF_BVD2_MASK);
++      GPDR(PCMCIA_GPIO_CF_IRQ)  &= ~(PCMCIA_GPIO_CF_IRQ_MASK);
++      GPDR(PCMCIA_GPIO_CF_RESET)|=  (PCMCIA_GPIO_CF_RESET_MASK);
++}
++
++inline int cerf_pcmcia_level_detect( void)
++{
++      return ((GPLR(PCMCIA_GPIO_CF_CD)&PCMCIA_GPIO_CF_CD_MASK)==0)?1:0;
++}
++inline int cerf_pcmcia_level_ready( void)
++{
++      return (GPLR(PCMCIA_GPIO_CF_IRQ)&PCMCIA_GPIO_CF_IRQ_MASK)?1:0;
++}
++inline int cerf_pcmcia_level_bvd1( void)
++{
++      return (GPLR(PCMCIA_GPIO_CF_BVD1)&PCMCIA_GPIO_CF_BVD1_MASK)?1:0;
++}
++inline int cerf_pcmcia_level_bvd2( void)
++{
++      return (GPLR(PCMCIA_GPIO_CF_BVD2)&PCMCIA_GPIO_CF_BVD2_MASK)?1:0;
++}
++
++#elif defined(CONFIG_SA1100_CERF) /* SA1100 */
++
++#define PCMCIA_GPDR GPDR
++#define PCMCIA_GPCR GPCR
++#define PCMCIA_GPSR GPSR
++#define PCMCIA_GPLR GPLR
++
++#define PCMCIA_GPIO_CF_CD_MASK    GPIO_CF_CD
++#define PCMCIA_GPIO_CF_IRQ_MASK   GPIO_CF_IRQ
++#define PCMCIA_GPIO_CF_RESET_MASK GPIO_CF_RESET
++#define PCMCIA_GPIO_CF_BVD1_MASK  GPIO_CF_BVD1
++#define PCMCIA_GPIO_CF_BVD2_MASK  GPIO_CF_BVD2
++
++#define PCMCIA_GPIO_CF_CD_EDGE    PCMCIA_GPIO_CF_CD_MASK
++#define PCMCIA_GPIO_CF_IRQ_EDGE   PCMCIA_GPIO_CF_IRQ_MASK
++#define PCMCIA_GPIO_CF_RESET_EDGE PCMCIA_GPIO_CF_RESET_MASK
++#define PCMCIA_GPIO_CF_BVD1_EDGE  PCMCIA_GPIO_CF_BVD1_MASK
++#define PCMCIA_GPIO_CF_BVD2_EDGE  PCMCIA_GPIO_CF_BVD2_MASK
++
++#define PCMCIA_IRQ_CF_CD   IRQ_GPIO_CF_CD
++#define PCMCIA_IRQ_CF_IRQ  IRQ_GPIO_CF_IRQ
++#define PCMCIA_IRQ_CF_BVD1 IRQ_GPIO_CF_BVD1
++#define PCMCIA_IRQ_CF_BVD2 IRQ_GPIO_CF_BVD2
++
++#define PCMCIA_PWR_SHUTDOWN GPIO_PWR_SHUTDOWN
++
++#ifdef CONFIG_SA1100_CERF_CPLD
++#define CERF_SOCKET 0
++#else
++#define CERF_SOCKET 1
++#endif
++
++inline void cerf_pcmcia_set_gpio_direction(void)
++{
++      PCMCIA_GPDR &= ~(PCMCIA_GPIO_CF_CD_MASK |
++                       PCMCIA_GPIO_CF_BVD1_MASK |
++                       PCMCIA_GPIO_CF_BVD2_MASK |
++                       PCMCIA_GPIO_CF_IRQ_MASK);
++      PCMCIA_GPDR |=   PCMCIA_GPIO_CF_RESET_MASK;
++}
++
++inline int cerf_pcmcia_level_detect( void)
++{
++      return ((PCMCIA_GPLR & PCMCIA_GPIO_CF_CD_MASK)==0)?1:0;
++}
++inline int cerf_pcmcia_level_ready( void)
++{
++      return (PCMCIA_GPLR & PCMCIA_GPIO_CF_IRQ_MASK)?1:0;
++}
++inline int cerf_pcmcia_level_bvd1( void)
++{
++      return (PCMCIA_GPLR & PCMCIA_GPIO_CF_BVD1_MASK)?1:0;
++}
++inline int cerf_pcmcia_level_bvd2( void)
++{
++      return (PCMCIA_GPLR & PCMCIA_GPIO_CF_BVD2_MASK)?1:0;
++}
++
++#endif
++
++#endif
+--- linux-2.4.27/drivers/sound/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/sound/Config.in
+@@ -239,6 +239,7 @@
+       dep_tristate '    VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS
+    fi
+    dep_tristate '    Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER
++   dep_tristate '  Intel PXA250/210 AC97 audio' CONFIG_SOUND_PXA_AC97 $CONFIG_ARCH_PXA $CONFIG_SOUND
+ fi
+ dep_tristate '  TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C
+--- linux-2.4.27/drivers/sound/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/sound/Makefile
+@@ -11,7 +11,7 @@
+                   msnd.o opl3.o sb_common.o sequencer_syms.o \
+                   sound_core.o sound_syms.o uart401.o \
+                   nm256_audio.o ac97.o ac97_codec.o aci.o \
+-                  sa1100-audio.o
++                  sa1100-audio.o pxa-audio.o pxa-ac97.o
+ # Each configuration option enables a list of files.
+@@ -85,6 +85,7 @@
+ obj-$(CONFIG_SOUND_SA1111_UDA1341) += sa1111-uda1341.o
+ obj-$(CONFIG_SOUND_SA1111_AC97)       += sa1111-ac97.o ac97_codec.o
+ obj-$(CONFIG_SOUND_SA1100SSP) += sa1100ssp.o
++obj-$(CONFIG_SOUND_PXA_AC97)+= pxa-ac97.o pxa-audio.o ac97_codec.o
+ obj-$(CONFIG_SOUND_EMU10K1)   += ac97_codec.o
+ obj-$(CONFIG_SOUND_BCM_CS4297A)       += swarm_cs4297a.o
+ obj-$(CONFIG_SOUND_RME96XX)     += rme96xx.o
+--- linux-2.4.27/drivers/sound/ac97_codec.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/sound/ac97_codec.c
+@@ -155,6 +155,7 @@
+       {0x45838308, "ESS Allegro ES1988",      &null_ops},
+       {0x49434511, "ICE1232",                 &null_ops}, /* I hope --jk */
+       {0x4e534331, "National Semiconductor LM4549", &null_ops},
++      {0x50534304, "Philips UCB1400",         &default_ops},
+       {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
+       {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
+       {0x545200FF, "TriTech TR?????",         &tritech_m_ops},
+--- /dev/null
++++ linux-2.4.27/drivers/sound/pxa-ac97.c
+@@ -0,0 +1,370 @@
++/*
++ *  linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Aug 15, 2001
++ *  Copyright:        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.
++ *
++ *  AC97 GPIO Changes:-
++ *   In order to read/write codec GPIO bits using AC97 link slot 12,
++ *   all IO to AC97_GPIO_STATUS must be via the Xscale modem codec
++ *   address space. 
++ *     Liam Girdwood <liam.girdwood@wolfsonmicro.com>
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/ac97_codec.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++#include <asm/dma.h>
++
++#include "pxa-audio.h"
++
++static struct completion CAR_completion;
++static int waitingForMask;
++static DECLARE_MUTEX(CAR_mutex);
++
++static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg)
++{
++      u16 val = -1;
++
++      down(&CAR_mutex);
++      if (!(CAR & CAR_CAIP)) {
++              volatile u32 *reg_addr;
++              
++              // if we are reading the GPIO status then this is cached 
++              // in hardware so we don't need to read over the link.          
++              if (reg == AC97_GPIO_STATUS) {
++                      reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1);
++                      val = *reg_addr;
++                      return val;
++              } 
++              
++              reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
++
++              waitingForMask=GSR_SDONE;
++
++              init_completion(&CAR_completion);
++              (void)*reg_addr;                        //start read access across the ac97 link
++              wait_for_completion(&CAR_completion);
++
++              if (GSR & GSR_RDCS) {
++                      GSR |= GSR_RDCS;                //write a 1 to clear
++                      printk(KERN_CRIT __FUNCTION__": read codec register timeout.\n");
++              }
++
++              init_completion(&CAR_completion);
++              val = *reg_addr;                        //valid data now but we've just started another cycle...
++              wait_for_completion(&CAR_completion);
++
++      } else {
++              printk(KERN_CRIT __FUNCTION__": CAR_CAIP already set\n");
++      }
++      up(&CAR_mutex);
++      //printk("%s(0x%02x) = 0x%04x\n", __FUNCTION__, reg, val);
++      return val;
++}
++
++static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val)
++{
++      down(&CAR_mutex);
++      if (!(CAR & CAR_CAIP)) {
++              volatile u32 *reg_addr;
++              
++              // if we are writing to the codec GPIO using slot 12 
++              // then we have to write to the modem register space            
++              if (reg == AC97_GPIO_STATUS) {
++                      reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1);
++                      *reg_addr = val;
++                      return;
++              } 
++              
++              reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1);
++
++              waitingForMask=GSR_CDONE;
++              init_completion(&CAR_completion);
++              *reg_addr = val;
++              wait_for_completion(&CAR_completion);
++      } else {
++              printk(KERN_CRIT __FUNCTION__": CAR_CAIP already set\n");
++      }
++      up(&CAR_mutex);
++      //printk("%s(0x%02x, 0x%04x)\n", __FUNCTION__, reg, val);
++}
++
++static void pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++      int gsr = GSR;
++      GSR = gsr & (GSR_SDONE|GSR_CDONE);              //write a 1 to clear
++      if (gsr & waitingForMask)
++      {
++              complete(&CAR_completion);
++      }
++}
++
++static struct ac97_codec pxa_ac97_codec = {
++      codec_read:     pxa_ac97_read,
++      codec_write:    pxa_ac97_write,
++};
++
++static DECLARE_MUTEX(pxa_ac97_mutex);
++static int pxa_ac97_refcount;
++
++int pxa_ac97_get(struct ac97_codec **codec)
++{
++      int ret;
++
++      *codec = NULL;
++      down(&pxa_ac97_mutex);
++
++      if (!pxa_ac97_refcount) {
++              ret = request_irq(IRQ_AC97, pxa_ac97_irq, 0, "AC97", NULL);
++              if (ret)
++                      return ret;
++
++              CKEN |= CKEN2_AC97; 
++              set_GPIO_mode(GPIO31_SYNC_AC97_MD);
++              set_GPIO_mode(GPIO30_SDATA_OUT_AC97_MD);
++              set_GPIO_mode(GPIO28_BITCLK_AC97_MD);
++              set_GPIO_mode(GPIO29_SDATA_IN_AC97_MD);
++
++              GCR = 0;
++              udelay(10);
++              GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE;
++              while (!(GSR & GSR_PCR)) {
++                      schedule();
++              }
++
++              ret = ac97_probe_codec(&pxa_ac97_codec);
++              if (ret != 1) {
++                      free_irq(IRQ_AC97, NULL);
++                      GCR = GCR_ACLINK_OFF;
++                      CKEN &= ~CKEN2_AC97; 
++                      return ret;
++              }
++
++              // 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, 0x1ff7);
++              pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050);
++              pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030);
++      }
++
++      pxa_ac97_refcount++;
++      up(&pxa_ac97_mutex);
++      *codec = &pxa_ac97_codec;
++      return 0;
++}
++
++void pxa_ac97_put(void)
++{
++      down(&pxa_ac97_mutex);
++      pxa_ac97_refcount--;
++      if (!pxa_ac97_refcount) {
++              GCR = GCR_ACLINK_OFF;
++              CKEN &= ~CKEN2_AC97; 
++              free_irq(IRQ_AC97, NULL);
++      }
++      up(&pxa_ac97_mutex);
++}
++
++EXPORT_SYMBOL(pxa_ac97_get);
++EXPORT_SYMBOL(pxa_ac97_put);
++
++
++/*
++ * Audio Mixer stuff
++ */
++
++static audio_state_t ac97_audio_state;
++static audio_stream_t ac97_audio_in;
++
++static int mixer_ioctl( struct inode *inode, struct file *file,
++                      unsigned int cmd, unsigned long arg)
++{
++      int ret, val;
++
++      ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg);
++      if (ret)
++              return ret;
++
++      /* We must snoop for some commands to provide our own extra processing */
++      switch (cmd) {
++      case SOUND_MIXER_WRITE_RECSRC:
++              /*
++               * According to the PXA250 spec, mic-in should use different
++               * DRCMR and different AC97 FIFO.
++               * Unfortunately current UCB1400 versions (up to ver 2A) don't
++               * produce slot 6 for the audio input frame, therefore the PXA
++               * AC97 mic-in FIFO is always starved.
++               */
++#if 0
++              ret = get_user(val, (int *)arg);
++              if (ret)
++                      return ret;
++              pxa_audio_clear_buf(&ac97_audio_in);
++              *ac97_audio_in.drcmr = 0;
++              if (val & (1 << SOUND_MIXER_MIC)) {
++                      ac97_audio_in.dcmd = DCMD_RXMCDR;
++                      ac97_audio_in.drcmr = &DRCMRRXMCDR;
++                      ac97_audio_in.dev_addr = __PREG(MCDR);
++              } else {
++                      ac97_audio_in.dcmd = DCMD_RXPCDR;
++                      ac97_audio_in.drcmr = &DRCMRRXPCDR;
++                      ac97_audio_in.dev_addr = __PREG(PCDR);
++              }
++              if (ac97_audio_state.rd_ref)
++                      *ac97_audio_in.drcmr =
++                              ac97_audio_in.dma_ch | DRCMR_MAPVLD;
++#endif
++              break;
++      }
++      return 0;
++}
++
++static struct file_operations mixer_fops = {
++      ioctl:          mixer_ioctl,
++      llseek:         no_llseek,
++      owner:          THIS_MODULE
++};
++
++/*
++ * AC97 codec ioctls
++ */
++
++static int codec_adc_rate = 48000;
++static int codec_dac_rate = 48000;
++
++static int ac97_ioctl(struct inode *inode, struct file *file,
++                    unsigned int cmd, unsigned long arg)
++{
++      int ret;
++      long val;
++
++      switch(cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* FIXME: do we support mono? */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* FIXME: do we support mono? */
++              return put_user(2, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret)
++                      return ret;
++              if (file->f_mode & FMODE_READ)
++                      codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val);
++              if (file->f_mode & FMODE_WRITE)
++                      codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val);
++              /* fall through */
++
++      case SOUND_PCM_READ_RATE:
++              if (file->f_mode & FMODE_READ)
++                      val = codec_adc_rate;
++              if (file->f_mode & FMODE_WRITE)
++                      val = codec_dac_rate;
++              return put_user(val, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* FIXME: can we do other fmts? */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (As per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++      return 0;
++}
++
++
++/*
++ * Audio stuff
++ */
++
++static audio_stream_t ac97_audio_out = {
++      name:                   "AC97 audio out",
++      dcmd:                   DCMD_TXPCDR,
++      drcmr:                  &DRCMRTXPCDR,
++      dev_addr:               __PREG(PCDR),
++};
++
++static audio_stream_t ac97_audio_in = {
++      name:                   "AC97 audio in",
++      dcmd:                   DCMD_RXPCDR,
++      drcmr:                  &DRCMRRXPCDR,
++      dev_addr:               __PREG(PCDR),
++};
++
++static audio_state_t ac97_audio_state = {
++      output_stream:          &ac97_audio_out,
++      input_stream:           &ac97_audio_in,
++      client_ioctl:           ac97_ioctl,
++      sem:                    __MUTEX_INITIALIZER(ac97_audio_state.sem),
++};
++
++static int ac97_audio_open(struct inode *inode, struct file *file)
++{
++      return pxa_audio_attach(inode, file, &ac97_audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to pxa_audio_attach().
++ */
++
++static struct file_operations ac97_audio_fops = {
++      open:           ac97_audio_open,
++      owner:          THIS_MODULE
++};
++
++
++static int __init pxa_ac97_init(void)
++{
++      int ret;
++      struct ac97_codec *dummy;
++
++      ret = pxa_ac97_get(&dummy);
++      if (ret)
++              return ret;
++
++      ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1);
++      pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1);
++
++      return 0;
++}
++
++static void __exit pxa_ac97_exit(void)
++{
++      unregister_sound_dsp(ac97_audio_state.dev_dsp);
++      unregister_sound_mixer(pxa_ac97_codec.dev_mixer);
++      pxa_ac97_put();
++}
++
++
++module_init(pxa_ac97_init);
++module_exit(pxa_ac97_exit);
++
+--- /dev/null
++++ linux-2.4.27/drivers/sound/pxa-audio.c
+@@ -0,0 +1,853 @@
++/*
++ *  linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Aug 15, 2001
++ *  Copyright:        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/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/poll.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++#include <asm/dma.h>
++
++#include "pxa-audio.h"
++
++
++#define AUDIO_NBFRAGS_DEFAULT 8
++#define AUDIO_FRAGSIZE_DEFAULT        8192
++
++#define MAX_DMA_SIZE          4096
++#define DMA_DESC_SIZE         sizeof(pxa_dma_desc)
++
++
++/*
++ * This function frees all buffers
++ */
++#define audio_clear_buf pxa_audio_clear_buf
++
++void pxa_audio_clear_buf(audio_stream_t * s)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      int frag;
++
++      if (!s->buffers)
++              return;
++
++      /* Ensure DMA isn't running */
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      add_wait_queue(&s->stop_wq, &wait);
++      DCSR(s->dma_ch) = DCSR_STOPIRQEN;
++      schedule();
++      remove_wait_queue(&s->stop_wq, &wait);
++
++      /* free DMA buffers */
++      for (frag = 0; frag < s->nbfrags; frag++) {
++              audio_buf_t *b = &s->buffers[frag];
++              if (!b->master)
++                      continue;
++              consistent_free(b->data, b->master, b->dma_desc->dsadr);
++      }
++
++      /* free descriptor ring */
++      if (s->buffers->dma_desc)
++              consistent_free(s->buffers->dma_desc, 
++                              s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE,
++                              s->dma_desc_phys);
++
++      /* free buffer structure array */
++      kfree(s->buffers);
++      s->buffers = NULL;
++}
++
++/*
++ * This function allocates the DMA descriptor array and buffer data space
++ * according to the current number of fragments and fragment size.
++ */
++static int audio_setup_buf(audio_stream_t * s)
++{
++      pxa_dma_desc *dma_desc;
++      dma_addr_t dma_desc_phys;
++      int nb_desc, frag, i, buf_size = 0;
++      char *dma_buf = NULL;
++      dma_addr_t dma_buf_phys = 0;
++
++      if (s->buffers)
++              return -EBUSY;
++
++      /* Our buffer structure array */
++      s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
++      if (!s->buffers)
++              goto err;
++      memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags);
++
++      /* 
++       * Our DMA descriptor array:
++       * for Each fragment we have one checkpoint descriptor plus one 
++       * descriptor per MAX_DMA_SIZE byte data blocks.
++       */
++      nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags;
++      dma_desc = consistent_alloc(GFP_KERNEL,
++                                  nb_desc * DMA_DESC_SIZE,
++                                  &dma_desc_phys,
++                                  0);
++      if (!dma_desc)
++              goto err;
++      s->descs_per_frag = nb_desc / s->nbfrags;
++      s->buffers->dma_desc = dma_desc;
++      s->dma_desc_phys = dma_desc_phys;
++      for (i = 0; i < nb_desc - 1; i++)
++              dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE;
++      dma_desc[i].ddadr = dma_desc_phys;
++
++      /* Our actual DMA buffers */
++      for (frag = 0; frag < s->nbfrags; frag++) {
++              audio_buf_t *b = &s->buffers[frag];
++
++              /*
++               * Let's allocate non-cached memory for DMA buffers.
++               * We try to allocate all memory at once.
++               * If this fails (a common reason is memory fragmentation),
++               * then we'll try allocating smaller buffers.
++               */
++              if (!buf_size) {
++                      buf_size = (s->nbfrags - frag) * s->fragsize;
++                      do {
++                              dma_buf = consistent_alloc(GFP_KERNEL,
++                                                         buf_size, 
++                                                         &dma_buf_phys,
++                                                         0);
++                              if (!dma_buf)
++                                      buf_size -= s->fragsize;
++                      } while (!dma_buf && buf_size);
++                      if (!dma_buf)
++                              goto err;
++                      b->master = buf_size;
++                      memzero(dma_buf, buf_size);
++              }
++
++              /* 
++               * Set up our checkpoint descriptor.  Since the count 
++               * is always zero, we'll abuse the dsadr and dtadr fields
++               * just in case this one is picked up by the hardware
++               * while processing SOUND_DSP_GETPTR.
++               */
++              dma_desc->dsadr = dma_buf_phys;
++              dma_desc->dtadr = dma_buf_phys;
++              dma_desc->dcmd = DCMD_ENDIRQEN;
++              if (s->output && !s->mapped)
++                      dma_desc->ddadr |= DDADR_STOP;
++              b->dma_desc = dma_desc++;
++
++              /* set up the actual data descriptors */
++              for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) {
++                      dma_desc[i].dsadr = (s->output) ?
++                              (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr;
++                      dma_desc[i].dtadr = (s->output) ?
++                              s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE);
++                      dma_desc[i].dcmd = s->dcmd |
++                              ((s->fragsize < MAX_DMA_SIZE) ?
++                                      s->fragsize : MAX_DMA_SIZE);
++              }
++              dma_desc += i;
++
++              /* handle buffer pointers */
++              b->data = dma_buf;
++              dma_buf += s->fragsize;
++              dma_buf_phys += s->fragsize;
++              buf_size -= s->fragsize;
++      }
++
++      s->usr_frag = s->dma_frag = 0;
++      s->bytecount = 0;
++      s->fragcount = 0;
++      sema_init(&s->sem, (s->output) ? s->nbfrags : 0);
++      return 0;
++
++err:
++      printk("pxa-audio: unable to allocate audio memory\n ");
++      audio_clear_buf(s);
++      return -ENOMEM;
++}
++
++/*
++ * Our DMA interrupt handler
++ */
++static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs)
++{
++      audio_stream_t *s = dev_id;
++      u_int dcsr;
++
++      dcsr = DCSR(ch);
++      DCSR(ch) = dcsr & ~DCSR_STOPIRQEN;
++
++      if (!s->buffers) {
++              printk("AC97 DMA: wow... received IRQ for channel %d but no buffer exists\n", ch);
++              return;
++      }
++
++      if (dcsr & DCSR_BUSERR)
++              printk("AC97 DMA: bus error interrupt on channel %d\n", ch);
++
++      if (dcsr & DCSR_ENDINTR) {
++              u_long cur_dma_desc;
++              u_int cur_dma_frag;
++
++              /* 
++               * Find out which DMA desc is current.  Note that DDADR
++               * points to the next desc, not the current one.
++               */
++              cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE;
++
++              /*
++               * Let the compiler nicely optimize constant divisors into
++               * multiplications for the common cases which is much faster.
++               * Common cases: x = 1 + (1 << y) for y = [0..3]
++               */
++              switch (s->descs_per_frag) {
++              case 2:  cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break;
++              case 3:  cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break;
++              case 5:  cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break;
++              case 9:  cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break;
++              default: cur_dma_frag =
++                          cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE);
++              }
++
++              /* Account for possible wrap back of cur_dma_desc above */
++              if (cur_dma_frag >= s->nbfrags)
++                      cur_dma_frag = s->nbfrags - 1;
++
++              while (s->dma_frag != cur_dma_frag) {
++                      if (!s->mapped) {
++                              /* 
++                               * This fragment is done - set the checkpoint
++                               * descriptor to STOP until it is gets
++                               * processed by the read or write function.
++                               */
++                              s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP;
++                              up(&s->sem);
++                      }
++                      if (++s->dma_frag >= s->nbfrags)
++                              s->dma_frag = 0;
++
++                      /* Accounting */
++                      s->bytecount += s->fragsize;
++                      s->fragcount++;
++              }
++
++              /* ... and for polling processes */
++              wake_up(&s->frag_wq);
++      }
++
++      if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE))
++              wake_up(&s->stop_wq);
++}
++
++/*
++ * Validate and sets up buffer fragments, etc.
++ */
++static int audio_set_fragments(audio_stream_t *s, int val)
++{
++      if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN)
++              return -EBUSY;
++      if (s->buffers)
++              audio_clear_buf(s);
++      s->nbfrags = (val >> 16) & 0x7FFF;
++      val &= 0xffff;
++      if (val < 5)
++              val = 5;
++      if (val > 15)
++              val = 15;
++      s->fragsize = 1 << val;
++      if (s->nbfrags < 2)
++              s->nbfrags = 2;
++      if (s->nbfrags * s->fragsize > 256 * 1024)
++              s->nbfrags = 256 * 1024 / s->fragsize;
++      if (audio_setup_buf(s))
++              return -ENOMEM;
++      return val|(s->nbfrags << 16);
++}
++
++
++/*
++ * The fops functions
++ */
++
++static int audio_write(struct file *file, const char *buffer,
++                     size_t count, loff_t * ppos)
++{
++      const char *buffer0 = buffer;
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *s = state->output_stream;
++      int chunksize, ret = 0;
++
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++      if (s->mapped)
++              return -ENXIO;
++      if (!s->buffers && audio_setup_buf(s))
++              return -ENOMEM;
++
++      while (count > 0) {
++              audio_buf_t *b = &s->buffers[s->usr_frag];
++
++              /* Grab a fragment */
++              if (file->f_flags & O_NONBLOCK) {
++                      ret = -EAGAIN;
++                      if (down_trylock(&s->sem))
++                              break;
++              } else {
++                      ret = -ERESTARTSYS;
++                      if (down_interruptible(&s->sem))
++                              break;
++              }
++
++              /* Feed the current buffer */
++              chunksize = s->fragsize - b->offset;
++              if (chunksize > count)
++                      chunksize = count;
++              if (copy_from_user(b->data + b->offset, buffer, chunksize)) {
++                      up(&s->sem);
++                      return -EFAULT;
++              }
++              b->offset += chunksize;
++              buffer += chunksize;
++              count -= chunksize;
++              if (b->offset < s->fragsize) {
++                      up(&s->sem);
++                      break;
++              }
++
++              /* 
++               * Activate DMA on current buffer.
++               * We unlock this fragment's checkpoint descriptor and
++               * kick DMA if it is idle.  Using checkpoint descriptors
++               * allows for control operations without the need for 
++               * stopping the DMA channel if it is already running.
++               */
++              b->offset = 0;
++              b->dma_desc->ddadr &= ~DDADR_STOP;
++              if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
++                      DDADR(s->dma_ch) = b->dma_desc->ddadr;
++                      DCSR(s->dma_ch) = DCSR_RUN;
++              }
++
++              /* move the index to the next fragment */
++              if (++s->usr_frag >= s->nbfrags)
++                      s->usr_frag = 0;
++      }
++
++      if ((buffer - buffer0))
++              ret = buffer - buffer0;
++      return ret;
++}
++
++
++static int audio_read(struct file *file, char *buffer,
++                    size_t count, loff_t * ppos)
++{
++      char *buffer0 = buffer;
++      audio_state_t *state = file->private_data;
++      audio_stream_t *s = state->input_stream;
++      int chunksize, ret = 0;
++
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++      if (s->mapped)
++              return -ENXIO;
++      if (!s->buffers && audio_setup_buf(s))
++              return -ENOMEM;
++
++      while (count > 0) {
++              audio_buf_t *b = &s->buffers[s->usr_frag];
++
++              /* prime DMA */
++              if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
++                      DDADR(s->dma_ch) = 
++                              s->buffers[s->dma_frag].dma_desc->ddadr;
++                      DCSR(s->dma_ch) = DCSR_RUN;
++              }
++
++              /* Wait for a buffer to become full */
++              if (file->f_flags & O_NONBLOCK) {
++                      ret = -EAGAIN;
++                      if (down_trylock(&s->sem))
++                              break;
++              } else {
++                      ret = -ERESTARTSYS;
++                      if (down_interruptible(&s->sem))
++                              break;
++              }
++
++              /* Grab data from current buffer */
++              chunksize = s->fragsize - b->offset;
++              if (chunksize > count)
++                      chunksize = count;
++              if (copy_to_user(buffer, b->data + b->offset, chunksize)) {
++                      up(&s->sem);
++                      return -EFAULT;
++              }
++              b->offset += chunksize;
++              buffer += chunksize;
++              count -= chunksize;
++              if (b->offset < s->fragsize) {
++                      up(&s->sem);
++                      break;
++              }
++
++              /* 
++               * Make this buffer available for DMA again.
++               * We unlock this fragment's checkpoint descriptor and
++               * kick DMA if it is idle.  Using checkpoint descriptors
++               * allows for control operations without the need for 
++               * stopping the DMA channel if it is already running.
++               */
++              b->offset = 0;
++              b->dma_desc->ddadr &= ~DDADR_STOP;
++
++              /* move the index to the next fragment */
++              if (++s->usr_frag >= s->nbfrags)
++                      s->usr_frag = 0;
++      }
++
++      if ((buffer - buffer0))
++              ret = buffer - buffer0;
++      return ret;
++}
++
++
++static int audio_sync(struct file *file)
++{
++      audio_state_t *state = file->private_data;
++      audio_stream_t *s = state->output_stream;
++      audio_buf_t *b;
++      pxa_dma_desc *final_desc;
++      u_long dcmd_save = 0;
++      DECLARE_WAITQUEUE(wait, current);
++
++      if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
++              return 0;
++
++      /*
++       * Send current buffer if it contains data.  Be sure to send
++       * a full sample count.
++       */
++      final_desc = NULL;
++      b = &s->buffers[s->usr_frag];
++      if (b->offset &= ~3) {
++              final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE];
++              b->offset &= (MAX_DMA_SIZE-1);
++              dcmd_save = final_desc->dcmd;
++              final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN; 
++              final_desc->ddadr |= DDADR_STOP;
++              b->offset = 0;
++              b->dma_desc->ddadr &= ~DDADR_STOP;
++              if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
++                      DDADR(s->dma_ch) = b->dma_desc->ddadr;
++                      DCSR(s->dma_ch) = DCSR_RUN;
++              }
++      }
++
++      /* Wait for DMA to complete. */
++      set_current_state(TASK_INTERRUPTIBLE);
++#if 0
++      /* 
++       * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set
++       * along wotj DCSR_RUN.  Silicon bug?
++       */
++      add_wait_queue(&s->stop_wq, &wait);
++      DCSR(s->dma_ch) |= DCSR_STOPIRQEN;
++      schedule();
++#else 
++      add_wait_queue(&s->frag_wq, &wait);
++      while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) {
++              schedule();
++              set_current_state(TASK_INTERRUPTIBLE);
++      }
++#endif
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&s->frag_wq, &wait);
++
++      /* Restore the descriptor chain. */
++      if (final_desc) {
++              final_desc->dcmd = dcmd_save;
++              final_desc->ddadr &= ~DDADR_STOP;
++              b->dma_desc->ddadr |= DDADR_STOP;
++      }
++      return 0;
++}
++
++
++static unsigned int audio_poll(struct file *file,
++                             struct poll_table_struct *wait)
++{
++      audio_state_t *state = file->private_data;
++      audio_stream_t *is = state->input_stream;
++      audio_stream_t *os = state->output_stream;
++      unsigned int mask = 0;
++
++      if (file->f_mode & FMODE_READ) {
++              /* Start audio input if not already active */
++              if (!is->buffers && audio_setup_buf(is))
++                      return -ENOMEM;
++              if (DCSR(is->dma_ch) & DCSR_STOPSTATE) {
++                      DDADR(is->dma_ch) = 
++                              is->buffers[is->dma_frag].dma_desc->ddadr;
++                      DCSR(is->dma_ch) = DCSR_RUN;
++              }
++              poll_wait(file, &is->frag_wq, wait);
++      }
++
++      if (file->f_mode & FMODE_WRITE) {
++              if (!os->buffers && audio_setup_buf(os))
++                      return -ENOMEM;
++              poll_wait(file, &os->frag_wq, wait);
++      }
++
++      if (file->f_mode & FMODE_READ)
++              if (( is->mapped && is->bytecount > 0) ||
++                  (!is->mapped && atomic_read(&is->sem.count) > 0))
++                      mask |= POLLIN | POLLRDNORM;
++
++      if (file->f_mode & FMODE_WRITE)
++              if (( os->mapped && os->bytecount > 0) ||
++                  (!os->mapped && atomic_read(&os->sem.count) > 0))
++                      mask |= POLLOUT | POLLWRNORM;
++
++      return mask;
++}
++
++
++static int audio_ioctl( struct inode *inode, struct file *file,
++                      uint cmd, ulong arg)
++{
++      audio_state_t *state = file->private_data;
++      audio_stream_t *os = state->output_stream;
++      audio_stream_t *is = state->input_stream;
++      long val;
++
++      switch (cmd) {
++      case OSS_GETVERSION:
++              return put_user(SOUND_VERSION, (int *)arg);
++
++      case SNDCTL_DSP_GETBLKSIZE:
++              if (file->f_mode & FMODE_WRITE)
++                      return put_user(os->fragsize, (int *)arg);
++              else
++                      return put_user(is->fragsize, (int *)arg);
++
++      case SNDCTL_DSP_GETCAPS:
++              val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP;
++              if (is && os)
++                      val |= DSP_CAP_DUPLEX;
++              return put_user(val, (int *)arg);
++
++      case SNDCTL_DSP_SETFRAGMENT:
++              if (get_user(val, (long *) arg))
++                      return -EFAULT;
++              if (file->f_mode & FMODE_READ) {
++                      int ret = audio_set_fragments(is, val);
++                      if (ret < 0)
++                              return ret;
++                      ret = put_user(ret, (int *)arg);
++                      if (ret)
++                              return ret;
++              }
++              if (file->f_mode & FMODE_WRITE) {
++                      int ret = audio_set_fragments(os, val);
++                      if (ret < 0)
++                              return ret;
++                      ret = put_user(ret, (int *)arg);
++                      if (ret)
++                              return ret;
++              }
++              return 0;
++
++      case SNDCTL_DSP_SYNC:
++              return audio_sync(file);
++
++      case SNDCTL_DSP_SETDUPLEX:
++              return 0;
++
++      case SNDCTL_DSP_POST:
++              return 0;
++
++      case SNDCTL_DSP_GETTRIGGER:
++              val = 0;
++              if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN)
++                      val |= PCM_ENABLE_INPUT;
++              if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN)
++                      val |= PCM_ENABLE_OUTPUT;
++              return put_user(val, (int *)arg);
++
++      case SNDCTL_DSP_SETTRIGGER:
++              if (get_user(val, (int *)arg))
++                      return -EFAULT;
++              if (file->f_mode & FMODE_READ) {
++                      if (val & PCM_ENABLE_INPUT) {
++                              if (!is->buffers && audio_setup_buf(is))
++                                      return -ENOMEM;
++                              if (!(DCSR(is->dma_ch) & DCSR_RUN)) {
++                                      audio_buf_t *b = &is->buffers[is->dma_frag];
++                                      DDADR(is->dma_ch) = b->dma_desc->ddadr;
++                                      DCSR(is->dma_ch) = DCSR_RUN;
++                              }
++                      } else {
++                              DCSR(is->dma_ch) = 0;
++                      }
++              }
++              if (file->f_mode & FMODE_WRITE) {
++                      if (val & PCM_ENABLE_OUTPUT) {
++                              if (!os->buffers && audio_setup_buf(os))
++                                      return -ENOMEM;
++                              if (!(DCSR(os->dma_ch) & DCSR_RUN)) {
++                                      audio_buf_t *b = &os->buffers[os->dma_frag];
++                                      DDADR(os->dma_ch) = b->dma_desc->ddadr;
++                                      DCSR(os->dma_ch) = DCSR_RUN;
++                              }
++                      } else {
++                              DCSR(os->dma_ch) = 0;
++                      }
++              }
++              return 0;
++
++      case SNDCTL_DSP_GETOSPACE:
++      case SNDCTL_DSP_GETISPACE:
++          {
++              audio_buf_info inf = { 0, };
++              audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is;
++
++              if ((s == is && !(file->f_mode & FMODE_READ)) ||
++                  (s == os && !(file->f_mode & FMODE_WRITE)))
++                      return -EINVAL;
++              if (!s->buffers && audio_setup_buf(s))
++                      return -ENOMEM;
++              inf.bytes = atomic_read(&s->sem.count) * s->fragsize;
++              inf.bytes -= s->buffers[s->usr_frag].offset;
++              inf.fragments = inf.bytes / s->fragsize;
++              inf.fragsize = s->fragsize;
++              inf.fragstotal = s->nbfrags;
++              return copy_to_user((void *)arg, &inf, sizeof(inf));
++          }
++
++      case SNDCTL_DSP_GETOPTR:
++      case SNDCTL_DSP_GETIPTR:
++          {
++              count_info inf = { 0, };
++              audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
++              dma_addr_t ptr;
++              int bytecount, offset, flags;
++
++              if ((s == is && !(file->f_mode & FMODE_READ)) ||
++                  (s == os && !(file->f_mode & FMODE_WRITE)))
++                      return -EINVAL;
++              if (DCSR(s->dma_ch) & DCSR_RUN) {
++                      audio_buf_t *b;
++                      save_flags_cli(flags);
++                      ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch);
++                      b = &s->buffers[s->dma_frag];
++                      offset = ptr - b->dma_desc->dsadr;
++                      if (offset >= s->fragsize)
++                              offset = s->fragsize - 4;
++              } else {
++                      save_flags(flags);
++                      offset = 0;
++              }
++              inf.ptr = s->dma_frag * s->fragsize + offset;
++              bytecount = s->bytecount + offset;
++              s->bytecount = -offset;
++              inf.blocks = s->fragcount;
++              s->fragcount = 0;
++              restore_flags(flags);
++              if (bytecount < 0)
++                      bytecount = 0;
++              inf.bytes = bytecount;
++              return copy_to_user((void *)arg, &inf, sizeof(inf));
++          }
++
++      case SNDCTL_DSP_NONBLOCK:
++              file->f_flags |= O_NONBLOCK;
++              return 0;
++
++      case SNDCTL_DSP_RESET:
++              if (file->f_mode & FMODE_WRITE) 
++                      audio_clear_buf(os);
++              if (file->f_mode & FMODE_READ)
++                      audio_clear_buf(is);
++              return 0;
++
++      default:
++              return state->client_ioctl(inode, file, cmd, arg);
++      }
++
++      return 0;
++}
++
++
++static int audio_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      audio_state_t *state = file->private_data;
++      audio_stream_t *s;
++      unsigned long size, vma_addr;
++      int i, ret;
++
++      if (vma->vm_pgoff != 0)
++              return -EINVAL;
++
++      if (vma->vm_flags & VM_WRITE) {
++              if (!state->wr_ref)
++                      return -EINVAL;;
++              s = state->output_stream;
++      } else if (vma->vm_flags & VM_READ) {
++              if (!state->rd_ref)
++                      return -EINVAL;
++              s = state->input_stream;
++      } else return -EINVAL;
++
++      if (s->mapped)
++              return -EINVAL;
++      size = vma->vm_end - vma->vm_start;
++      if (size != s->fragsize * s->nbfrags)
++              return -EINVAL;
++      if (!s->buffers && audio_setup_buf(s))
++              return -ENOMEM;
++      vma_addr = vma->vm_start;
++      for (i = 0; i < s->nbfrags; i++) {
++              audio_buf_t *buf = &s->buffers[i];
++              if (!buf->master)
++                      continue;
++              ret = remap_page_range(vma_addr, buf->dma_desc->dsadr,
++                                     buf->master, vma->vm_page_prot);
++              if (ret)
++                      return ret;
++              vma_addr += buf->master;
++      }
++      for (i = 0; i < s->nbfrags; i++)
++              s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP;
++      s->mapped = 1;
++      return 0;
++}
++
++
++static int audio_release(struct inode *inode, struct file *file)
++{
++      audio_state_t *state = file->private_data;
++
++      down(&state->sem);
++
++      if (file->f_mode & FMODE_READ) {
++              audio_clear_buf(state->input_stream);
++              *state->input_stream->drcmr = 0;
++              pxa_free_dma(state->input_stream->dma_ch);
++              state->rd_ref = 0;
++      }
++
++      if (file->f_mode & FMODE_WRITE) {
++              audio_sync(file);
++              audio_clear_buf(state->output_stream);
++              *state->output_stream->drcmr = 0;
++              pxa_free_dma(state->output_stream->dma_ch);
++              state->wr_ref = 0;
++      }
++
++      up(&state->sem);
++      return 0;
++}
++
++
++int pxa_audio_attach(struct inode *inode, struct file *file,
++                       audio_state_t *state)
++{
++      audio_stream_t *is = state->input_stream;
++      audio_stream_t *os = state->output_stream;
++      int err;
++
++      down(&state->sem);
++
++      /* access control */
++      err = -ENODEV;
++      if ((file->f_mode & FMODE_WRITE) && !os)
++              goto out;
++      if ((file->f_mode & FMODE_READ) && !is)
++              goto out;
++      err = -EBUSY;
++      if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
++              goto out;
++      if ((file->f_mode & FMODE_READ) && state->rd_ref)
++              goto out;
++
++      /* request DMA channels */
++      if (file->f_mode & FMODE_WRITE) {
++              err = pxa_request_dma(os->name, DMA_PRIO_LOW, 
++                                        audio_dma_irq, os);
++              if (err < 0)
++                      goto out;
++              os->dma_ch = err;
++      }
++      if (file->f_mode & FMODE_READ) {
++              err = pxa_request_dma(is->name, DMA_PRIO_LOW,
++                                        audio_dma_irq, is);
++              if (err < 0) {
++                      if (file->f_mode & FMODE_WRITE) {
++                              *os->drcmr = 0;
++                              pxa_free_dma(os->dma_ch);
++                      }
++                      goto out;
++              }
++              is->dma_ch = err;
++      }
++
++      file->private_data      = state;
++      file->f_op->release     = audio_release;
++      file->f_op->write       = audio_write;
++      file->f_op->read        = audio_read;
++      file->f_op->mmap        = audio_mmap;
++      file->f_op->poll        = audio_poll;
++      file->f_op->ioctl       = audio_ioctl;
++      file->f_op->llseek      = no_llseek;
++
++      if ((file->f_mode & FMODE_WRITE)) {
++              state->wr_ref = 1;
++              os->fragsize = AUDIO_FRAGSIZE_DEFAULT;
++              os->nbfrags = AUDIO_NBFRAGS_DEFAULT;
++              os->output = 1;
++              os->mapped = 0;
++              init_waitqueue_head(&os->frag_wq);
++              init_waitqueue_head(&os->stop_wq);
++              *os->drcmr = os->dma_ch | DRCMR_MAPVLD;
++      }
++      if (file->f_mode & FMODE_READ) {
++              state->rd_ref = 1;
++              is->fragsize = AUDIO_FRAGSIZE_DEFAULT;
++              is->nbfrags = AUDIO_NBFRAGS_DEFAULT;
++              is->output = 0;
++              is->mapped = 0;
++              init_waitqueue_head(&is->frag_wq);
++              init_waitqueue_head(&is->stop_wq);
++              *is->drcmr = is->dma_ch | DRCMR_MAPVLD;
++      }
++
++      err = 0;
++
++out:
++      up(&state->sem);
++      return err;
++}
++
++EXPORT_SYMBOL(pxa_audio_attach);
++EXPORT_SYMBOL(pxa_audio_clear_buf);
++
+--- /dev/null
++++ linux-2.4.27/drivers/sound/pxa-audio.h
+@@ -0,0 +1,55 @@
++/*
++ *  linux/drivers/sound/pxa-audio.h -- audio interface for the Cotula chip
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Aug 15, 2001
++ *  Copyright:        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.
++ */
++
++typedef struct {
++      int offset;                     /* current buffer position */
++      char *data;                     /* actual buffer */
++      pxa_dma_desc *dma_desc; /* pointer to the starting desc */
++      int master;                     /* owner for buffer allocation, contain size whn true */
++} audio_buf_t;
++
++typedef struct {
++      char *name;                     /* stream identifier */
++      audio_buf_t *buffers;           /* pointer to audio buffer array */
++      u_int usr_frag;                 /* user fragment index */
++      u_int dma_frag;                 /* DMA fragment index */
++      u_int fragsize;                 /* fragment size */
++      u_int nbfrags;                  /* number of fragments */
++      u_int dma_ch;                   /* DMA channel number */
++      dma_addr_t dma_desc_phys;       /* phys addr of descriptor ring */
++      u_int descs_per_frag;           /* nbr descriptors per fragment */
++      int bytecount;                  /* nbr of processed bytes */
++      int fragcount;                  /* nbr of fragment transitions */
++      struct semaphore sem;           /* account for fragment usage */
++      wait_queue_head_t frag_wq;      /* for poll(), etc. */
++      wait_queue_head_t stop_wq;      /* for users of DCSR_STOPIRQEN */
++      u_long dcmd;                    /* DMA descriptor dcmd field */
++      volatile u32 *drcmr;            /* the DMA request channel to use */
++      u_long dev_addr;                /* device physical address for DMA */
++      int mapped:1;                   /* mmap()'ed buffers */
++      int output:1;                   /* 0 for input, 1 for output */
++} audio_stream_t;
++      
++typedef struct {
++      audio_stream_t *output_stream;
++      audio_stream_t *input_stream;
++      int dev_dsp;                    /* audio device handle */
++      int rd_ref:1;           /* open reference for recording */
++      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 */
++} audio_state_t;
++
++extern int pxa_audio_attach(struct inode *inode, struct file *file,
++                              audio_state_t *state);
++extern void pxa_audio_clear_buf(audio_stream_t *s);
++
+--- linux-2.4.27/drivers/sound/sa1100-audio.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/sound/sa1100-audio.c
+@@ -148,7 +148,8 @@
+                       do {
+                               dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,
+                                                         dmasize,
+-                                                        &dmaphys);
++                                                        &dmaphys,
++                                                        0);
+                               if (!dmabuf)
+                                       dmasize -= s->fragsize;
+                       } while (!dmabuf && dmasize);
+--- linux-2.4.27/drivers/video/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/video/Config.in
+@@ -50,6 +50,15 @@
+      if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF_CPLD" = "y" ]; then
+        bool 'Cerfboard Backlight (CerfPDA)' CONFIG_SA1100_CERF_LCD_BACKLIGHT
+      fi
++     tristate '  PXA LCD support' CONFIG_FB_PXA $CONFIG_ARCH_PXA
++     if [ "$CONFIG_FB_PXA" != "n" ]; then
++        choice 'LCD Bit Depth' \
++               "8-Bpp          CONFIG_FB_PXA_8BPP \
++                16-Bpp         CONFIG_FB_PXA_16BPP" Bit-Depth
++     fi
++     if [ "$CONFIG_FB_PXA" != "n" -a "$CONFIG_ARCH_LUBBOCK" = "y" ]; then
++      bool '  Lubbock QVGA LCD support instead of DSTN' CONFIG_FB_PXA_QVGA
++     fi
+    fi
+    dep_tristate '  CyberPro 2000/2010/5000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI
+    if [ "$CONFIG_APOLLO" = "y" ]; then
+@@ -295,7 +304,7 @@
+       if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \
+          "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \
+          "$CONFIG_FB_TX3912" = "y"  -o "$CONFIG_FB_CLPS711X" = "y" -o \
+-           "$CONFIG_FB_DBMX1" = "y" ]; then
++           "$CONFIG_FB_DBMX1" = "y" -o "$CONFIG_FB_PXA" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB2 y
+        define_tristate CONFIG_FBCON_CFB4 y
+       else
+@@ -329,7 +338,7 @@
+          "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" -o \
+          "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_HP300" = "y" -o \
+          "$CONFIG_FB_INTEL" = "y" -o \
+-           "$CONFIG_FB_DBMX1" = "y" ]; then
++           "$CONFIG_FB_DBMX1" = "y" -o "$CONFIG_FB_PXA" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB8 y
+       else
+        if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
+@@ -372,7 +381,7 @@
+          "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_SA1100" = "y" -o \
+          "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" -o \
+          "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" -o \
+-         "$CONFIG_FB_ANAKIN" = "y" -o \
++         "$CONFIG_FB_ANAKIN" = "y" -o "$CONFIG_FB_PXA" = "y" -o \
+            "$CONFIG_FB_DBMX1" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB16 y
+       else
+--- linux-2.4.27/drivers/video/Makefile~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/video/Makefile
+@@ -14,7 +14,7 @@
+                 fbcon-vga.o fbcon-iplan2p2.o fbcon-iplan2p4.o \
+                 fbcon-iplan2p8.o fbcon-vga-planes.o fbcon-cfb16.o \
+                 fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \
+-                fbcon-cfb8.o fbcon-mac.o fbcon-mfb.o \
++                fbcon-cfb8.o fbcon-mac.o fbcon-mfb.o pxafb.o \
+                 cyber2000fb.o sa1100fb.o fbcon-hga.o fbgen.o
+ # Each configuration option enables a list of files.
+@@ -129,6 +129,10 @@
+ obj-$(CONFIG_FB_BWTWO)            += bwtwofb.o
+ obj-$(CONFIG_FB_HGA)              += hgafb.o
+ obj-$(CONFIG_FB_SA1100)           += sa1100fb.o
++obj-$(CONFIG_FB_PXA)              += pxafb.o
++ifeq ($(CONFIG_PXA_CERF_PDA),y)
++   obj-$(CONFIG_FB_PXA)           += lcdctrl.o lcdctrl_cerf.o
++endif
+ obj-$(CONFIG_FB_DBMX1)            += dbmx1fb.o
+ obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
+ obj-$(CONFIG_FB_HIT)              += hitfb.o fbgen.o
+--- linux-2.4.27/drivers/video/fbmem.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/video/fbmem.c
+@@ -109,6 +109,7 @@
+ extern int chips_init(void);
+ extern int g364fb_init(void);
+ extern int sa1100fb_init(void);
++extern int pxafb_init(void);
+ extern int fm2fb_init(void);
+ extern int fm2fb_setup(char*);
+ extern int q40fb_init(void);
+@@ -305,6 +306,9 @@
+ #ifdef CONFIG_FB_SA1100
+       { "sa1100", sa1100fb_init, NULL },
+ #endif
++#ifdef CONFIG_FB_PXA
++      { "pxa", pxafb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_SUN3
+       { "sun3", sun3fb_init, sun3fb_setup },
+ #endif
+@@ -677,13 +681,13 @@
+ #elif defined(__i386__) || defined(__x86_64__)
+       if (boot_cpu_data.x86 > 3)
+               pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+-#elif defined(__arm__) || defined(__mips__)
++#elif defined(__mips__)
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ #elif defined(__sh__)
+       pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE;
+ #elif defined(__hppa__)
+       pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+-#elif defined(__ia64__)
++#elif defined(__ia64__) || defined(__arm__) 
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ #elif defined(__hppa__)
+       pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+--- /dev/null
++++ linux-2.4.27/drivers/video/lcdctrl.c
+@@ -0,0 +1,223 @@
++/*
++ *  lcdctrl.c
++ *
++ *  Generic LCD control for brightness, contrast, etc.
++ *  Device specific drivers implement a lcdctrl_device and
++ *  provides access to it via lcdctrl_device_get_ops().
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Mar 2002: Initial version [FB]
++ * 
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++#include <video/lcdctrl.h>
++
++/*
++ * Set this to zero to remove all the debug statements via
++ * dead code elimination.
++ */
++#define DEBUGGING       0
++
++#if DEBUGGING
++static unsigned int lcd_debug = DEBUGGING;
++#else
++#define lcd_debug     0
++#endif
++
++/* -- prototypes -- */
++
++static int lcdctrl_ioctl(struct inode * inode, struct file *filp,
++                       unsigned int cmd , unsigned long arg);
++static int lcdctrl_open(struct inode *inode, struct file *filp);
++static int lcdctrl_close(struct inode *inode, struct file *filp);
++
++/* -- variables -- */
++
++struct lcdctrl_device *lcd_device;
++
++static int intensity;
++static int brightness;
++static int contrast;
++
++static int enabled;
++static int sync_needed;
++static int chrdev_major;
++
++static struct file_operations lcdctrl_fops = {
++      ioctl:          lcdctrl_ioctl,
++      open:           lcdctrl_open,
++      release:        lcdctrl_close
++};
++
++/* -- ioctl -- */
++
++static int lcdctrl_ioctl(struct inode * inode, struct file *filp,
++                         unsigned int cmd , unsigned long arg)
++{
++      int ret;
++      ret = -EINVAL;
++
++      if( lcd_debug)
++          printk(KERN_INFO "lcdctrl_ioctl: cmd=%d, arg=%ld\n", cmd, arg);
++
++      switch(cmd)
++      {
++              case _LCDCTRL_IOCTL_ON:
++                      ret = lcdctrl_enable();
++                      break;
++              case _LCDCTRL_IOCTL_OFF:
++                      ret = lcdctrl_disable();  
++                      break;
++              case _LCDCTRL_IOCTL_INTENSITY:
++                      if ((arg >=0) && (arg <= 100))
++                              ret = lcdctrl_set_intensity(arg);
++                      break;
++              case _LCDCTRL_IOCTL_BRIGHTNESS:
++                      if ((arg >=0) && (arg <= 100))
++                              ret = lcdctrl_set_brightness(arg);
++                      break;
++              case _LCDCTRL_IOCTL_CONTRAST:
++                      if ((arg >=0) && (arg <= 100))
++                              ret = lcdctrl_set_contrast(arg, LCD_NO_SYNC);
++                      break;  
++              case _LCDCTRL_IOCTL_GET_BRIGHTNESS:
++                      ret = brightness;
++                      break;
++              case _LCDCTRL_IOCTL_GET_CONTRAST:
++                      ret = contrast;
++                      break;
++              case _LCDCTRL_IOCTL_GET_INTENSITY:
++                      ret = intensity;
++                      break;
++
++              default:
++                      printk(KERN_ERR "lcdctrl_ioctl: invalid ioctl\n");
++                      break;
++      }
++
++      return ret;
++}
++
++static int lcdctrl_open(struct inode *inode, struct file *filp)
++{
++//    MOD_INC_USE_COUNT;
++      return 0;
++}
++
++static int lcdctrl_close(struct inode *inode, struct file *filp)
++{
++//    MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++/* -- -- */
++
++int lcdctrl_enable( void)
++{
++      int result;
++
++      if( enabled) return 0;
++
++      result = lcd_device->enable();
++
++      lcdctrl_set_intensity( intensity);
++      lcdctrl_set_brightness( brightness);
++      lcdctrl_set_contrast( contrast, sync_needed);
++        sync_needed = LCD_NO_SYNC;
++
++      enabled = 1;
++      return result;
++}
++
++int lcdctrl_disable( void)
++{
++      enabled = 0;
++      return lcd_device->disable();
++}
++
++int lcdctrl_set_intensity( int i)
++{
++      intensity = i;
++      return lcd_device->set_intensity( i);
++}
++
++int lcdctrl_set_brightness( int b)
++{
++      brightness = b;
++      return lcd_device->set_brightness( b);
++}
++
++int lcdctrl_set_contrast( int c, int sync)
++{
++      contrast = c;
++      return lcd_device->set_contrast( c, sync);
++}
++
++int lcdctrl_get_intensity( void)
++{
++      return intensity;
++}
++
++int lcdctrl_get_brightness( void)
++{
++      return brightness;
++}
++
++int lcdctrl_get_contrast( void)
++{
++      return contrast;
++}
++
++/* -- -- */
++
++/* the device specific driver should implement this */
++struct lcdctrl_device *lcdctrl_device_get_ops(void);
++
++int lcdctrl_init( void)
++{
++      int ret;
++
++      lcd_device = lcdctrl_device_get_ops();
++
++      if( !lcd_device)
++      {
++              printk(KERN_ERR "lcdctrl_init: No lcd_device registered.\n");
++              return -EINVAL;
++      }
++
++      ret = lcd_device->init( &intensity, &brightness, &contrast);
++
++        sync_needed = LCD_SYNC_NEEDED;
++
++      if( ret == 0)
++      {
++              chrdev_major = 
++                      register_chrdev( 0,_LCD_CONTROL_NAME,&lcdctrl_fops);
++              if( lcd_debug)
++                      printk(KERN_INFO "lcdctrl_init: OK\n");
++      }
++      return ret;
++}
+--- /dev/null
++++ linux-2.4.27/drivers/video/lcdctrl_cerf.c
+@@ -0,0 +1,175 @@
++/*
++ *  lcdctrl_cerf.c
++ *
++ *  Cerf LCD control for brightness and contrast.
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Mar 2002: Initial version [FB]
++ * 
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/arch/cerf_ucb1400gpio.h>
++
++#include <video/lcdctrl.h>
++
++/*
++ * Set this to zero to remove all the debug statements via
++ * dead code elimination.
++ */
++#define DEBUGGING       0
++
++#if DEBUGGING
++static unsigned int lcd_debug = DEBUGGING;
++#else
++#define lcd_debug       0
++#endif
++
++#define LCD_MAX_INTENSITY      0
++#define LCD_MAX_BRIGHTNESS    15
++#define LCD_MAX_CONTRAST      100
++
++#define LCD_DEFAULT_INTENSITY  0
++#define LCD_DEFAULT_BRIGHTNESS        14*100/(LCD_MAX_BRIGHTNESS)
++#define LCD_DEFAULT_CONTRAST  90*100/(LCD_MAX_CONTRAST)
++
++#define UP    1
++#define DOWN  0
++
++/* -- prototypes -- */
++
++static int cerf_lcdctrl_init( int *intensity, int *brightness, int *contrast);
++static int cerf_lcdctrl_enable(void);
++static int cerf_lcdctrl_disable(void);
++static int cerf_lcdctrl_set_intensity( int i);
++static int cerf_lcdctrl_set_brightness( int b);
++static int cerf_lcdctrl_set_contrast( int c, int sync);
++
++static void cerf_lcdctrl_contrast_step( int direction);
++
++/* -- variables -- */
++
++static int dev_contrast;
++
++/* -- -- */
++
++static struct lcdctrl_device cerf_dev = {
++      init:           cerf_lcdctrl_init,
++      enable:         cerf_lcdctrl_enable,
++      disable:        cerf_lcdctrl_disable,
++      set_intensity:  cerf_lcdctrl_set_intensity,
++      set_brightness: cerf_lcdctrl_set_brightness,
++      set_contrast:   cerf_lcdctrl_set_contrast
++}; 
++
++static int cerf_lcdctrl_enable( void)
++{
++      cerf_ucb1400gpio_lcd_enable();
++
++      return 0;
++}
++
++static int cerf_lcdctrl_disable( void)
++{
++      cerf_ucb1400gpio_lcd_disable();
++
++      return 0;
++}
++
++static int cerf_lcdctrl_set_intensity( int i)
++{
++      int dev_intensity = LCD_MAX_INTENSITY*i/100;
++      if( lcd_debug)
++              printk(KERN_INFO "cerf_lcdctrl_set_intensity: "
++                      "dev_intensity = %d\n", dev_intensity);
++      return 0;
++}
++
++static int cerf_lcdctrl_set_brightness( int b)
++{
++      int dev_brightness = LCD_MAX_BRIGHTNESS*b/100;
++      outw( dev_brightness, CERF_PDA_CPLD+CERF_PDA_CPLD_BRIGHTNESS);
++      if( lcd_debug)
++              printk(KERN_INFO "cerf_lcdctrl_set_brightness: "
++                      "dev_brightness = %d\n", dev_brightness);
++      return 0;
++}
++
++static int cerf_lcdctrl_set_contrast( int c, int sync)
++{
++      int new_dev_contrast = LCD_MAX_CONTRAST*c/100;
++      int i;
++      int count;
++      int direction = UP;
++      if( sync == LCD_SYNC_NEEDED)
++      {
++              /* In order to sync we step down to the lowest contrast level */
++              for( i=0; i<LCD_MAX_CONTRAST; i++)
++                      cerf_lcdctrl_contrast_step(DOWN);
++              dev_contrast = 0;
++      }
++
++      count = new_dev_contrast - dev_contrast;
++      if( count < 0)
++      {
++              /* new contrast is lower then current setting */
++              direction = DOWN;
++              count = -count;
++      }
++
++      for( i=0; i<count; i++)
++              cerf_lcdctrl_contrast_step(direction);
++
++      if( lcd_debug)
++              printk(KERN_INFO "cerf_lcdctrl_set_contrast: "
++                      "dev_contrast = %d\n", new_dev_contrast);
++      dev_contrast = new_dev_contrast;
++
++      return 0;
++}
++
++/* -- -- */
++
++static void cerf_lcdctrl_contrast_step( int direction)
++{
++      cerf_ucb1400gpio_lcd_contrast_step( direction);
++}
++
++/* -- -- */
++
++static int cerf_lcdctrl_init( int *intensity, int *brightness, int *contrast)
++{
++      *intensity = LCD_DEFAULT_INTENSITY;
++      *brightness = LCD_DEFAULT_BRIGHTNESS;
++      *contrast = LCD_DEFAULT_CONTRAST;
++
++      if( lcd_debug)
++              printk(KERN_INFO "cerf_lcdctrl_init: OK\n");
++      return 0;
++}
++
++/* this is the hook for lcdctrl to access to the device specifics */
++struct lcdctrl_device *lcdctrl_device_get_ops(void)
++{
++      return &cerf_dev;
++}
+--- /dev/null
++++ linux-2.4.27/drivers/video/pxafb.c
+@@ -0,0 +1,1410 @@
++/*
++ *  linux/drivers/video/pxafb.c
++ *
++ *  Copyright (C) 1999 Eric A. Thomas
++ *   Based on acornfb.c Copyright (C) Russell King.
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ *            Intel PXA250/210 LCD Controller Frame Buffer Driver
++ *
++ * Please direct your questions and comments on this driver to the following
++ * email address:
++ *
++ *    linux-arm-kernel@lists.arm.linux.org.uk
++ *
++ *
++ * Code Status:
++ *
++ * 2001/08/03: <cbrake@accelent.com>
++ *      - Ported from SA1100 to PXA250
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/fb.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/init.h>
++#include <linux/notifier.h>
++#include <linux/cpufreq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++
++#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 */
++
++/*
++ * debugging?
++ */
++#define DEBUG 0
++/*
++ * Complain if VAR is out of range.
++ */
++#define DEBUG_VAR 1
++
++#undef ASSABET_PAL_VIDEO
++
++#include "pxafb.h"
++
++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, },
++      blue:   { offset: 0,  length: 5, },
++      transp: { offset: 0,  length: 0, },
++};
++
++static struct pxafb_mach_info pxa_fb_info __initdata = {
++      pixclock:       LCD_PIXCLOCK,   /* clock period in ps */
++      bpp:            LCD_BPP,
++      xres:           LCD_XRES,
++      yres:           LCD_YRES,
++      hsync_len:      LCD_HORIZONTAL_SYNC_PULSE_WIDTH,
++      vsync_len:      LCD_VERTICAL_SYNC_PULSE_WIDTH,
++      left_margin:    LCD_BEGIN_OF_LINE_WAIT_COUNT,
++      upper_margin:   LCD_BEGIN_FRAME_WAIT_COUNT,
++      right_margin:   LCD_END_OF_LINE_WAIT_COUNT,
++      lower_margin:   LCD_END_OF_FRAME_WAIT_COUNT,
++      sync:           LCD_SYNC,
++      lccr0:          LCD_LCCR0,
++      lccr3:          LCD_LCCR3
++};
++
++static struct pxafb_mach_info * __init
++pxafb_get_machine_info(struct pxafb_info *fbi)
++{
++      return &pxa_fb_info;
++}
++
++static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);
++static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
++
++static inline void pxafb_schedule_task(struct pxafb_info *fbi, u_int state)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++      /*
++       * We need to handle two requests being made at the same time.
++       * There are two important cases:
++       *  1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
++       *     We must perform the unblanking, which will do our REENABLE for us.
++       *  2. When we are blanking, but immediately unblank before we have
++       *     blanked.  We do the "REENABLE" thing here as well, just to be sure.
++       */
++      if (fbi->task_state == C_ENABLE && state == C_REENABLE)
++              state = (u_int) -1;
++      if (fbi->task_state == C_DISABLE && state == C_ENABLE)
++              state = C_REENABLE;
++
++      if (state != (u_int)-1) {
++              fbi->task_state = state;
++              schedule_task(&fbi->task);
++      }
++      local_irq_restore(flags);
++}
++
++/*
++ * Get the VAR structure pointer for the specified console
++ */
++static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      return (con == fbi->currcon || con == -1) ? &fbi->fb.var : &fb_display[con].var;
++}
++
++/*
++ * Get the DISPLAY structure pointer for the specified console
++ */
++static inline struct display *get_con_display(struct fb_info *info, int con)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      return (con < 0) ? fbi->fb.disp : &fb_display[con];
++}
++
++/*
++ * Get the CMAP pointer for the specified console
++ */
++static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      return (con == fbi->currcon || con == -1) ? &fbi->fb.cmap : &fb_display[con].cmap;
++}
++
++static inline u_int
++chan_to_field(u_int chan, struct fb_bitfield *bf)
++{
++      chan &= 0xffff;
++      chan >>= 16 - bf->length;
++      return chan << bf->offset;
++}
++
++static int
++pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
++                     u_int trans, struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      u_int val, ret = 1;
++
++      if (regno < fbi->palette_size) {
++              val = ((red >> 0) & 0xf800);
++              val |= ((green >> 5) & 0x07e0);
++              val |= ((blue >> 11) & 0x001f);
++
++              fbi->palette_cpu[regno] = val;
++              ret = 0;
++      }
++      return ret;
++}
++
++static int
++pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                 u_int trans, struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      u_int val;
++      int ret = 1;
++
++      /*
++       * If greyscale is true, then we convert the RGB value
++       * to greyscale no mater what visual we are using.
++       */
++      if (fbi->fb.var.grayscale)
++              red = green = blue = (19595 * red + 38470 * green +
++                                      7471 * blue) >> 16;
++
++      switch (fbi->fb.disp->visual) {
++      case FB_VISUAL_TRUECOLOR:
++      case FB_VISUAL_DIRECTCOLOR:
++              /*
++               * 12 or 16-bit True Colour.  We encode the RGB value
++               * according to the RGB bitfield information.
++               */
++              if (regno <= 16) {
++                      u16 *pal = fbi->fb.pseudo_palette;
++
++                      val  = chan_to_field(red, &fbi->fb.var.red);
++                      val |= chan_to_field(green, &fbi->fb.var.green);
++                      val |= chan_to_field(blue, &fbi->fb.var.blue);
++
++                      pal[regno] = val;
++                      ret = 0;
++              }
++              break;
++
++      case FB_VISUAL_PSEUDOCOLOR:
++              ret = pxafb_setpalettereg(regno, red, green, blue, trans, info);
++              break;
++      }
++
++      return ret;
++}
++
++/*
++ *  pxafb_decode_var():
++ *    Get the video params out of 'var'. If a value doesn't fit, round it up,
++ *    if it's too big, return -EINVAL.
++ *
++ *    Suggestion: Round up in the following order: bits_per_pixel, xres,
++ *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
++ *    bitfields, horizontal timing, vertical timing.
++ */
++static int pxafb_validate_var(struct fb_var_screeninfo *var,
++                               struct pxafb_info *fbi)
++{
++      int ret = -EINVAL;
++
++      if (var->xres < MIN_XRES)
++              var->xres = MIN_XRES;
++      if (var->yres < MIN_YRES)
++              var->yres = MIN_YRES;
++      if (var->xres > fbi->max_xres)
++              var->xres = fbi->max_xres;
++      if (var->yres > fbi->max_yres)
++              var->yres = fbi->max_yres;
++      var->xres_virtual =
++          var->xres_virtual < var->xres ? var->xres : var->xres_virtual;
++      var->yres_virtual =
++          var->yres_virtual < var->yres ? var->yres : var->yres_virtual;
++
++      DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
++      switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++      case 4:  ret = 0; break;
++#endif
++#ifdef FBCON_HAS_CFB8
++      case 8:  ret = 0; break;
++#endif
++#ifdef FBCON_HAS_CFB16
++      case 12:
++              /* make sure we are in passive mode */
++              if (!(fbi->lccr0 & LCCR0_PAS))
++                      ret = 0;
++              break;
++
++      case 16:
++              /* 
++               * 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;
++              break;
++#endif
++      default:
++              break;
++      }
++
++      return ret;
++}
++
++static inline void pxafb_set_truecolor(u_int is_true_color)
++{
++      DPRINTK("true_color = %d\n", is_true_color);
++}
++
++static void
++pxafb_hw_set_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi)
++{
++
++      fb_set_cmap(&fbi->fb.cmap, 1, pxafb_setcolreg, &fbi->fb);
++
++      /* Set board control register to handle new color depth */
++      pxafb_set_truecolor(var->bits_per_pixel >= 16);
++
++      pxafb_activate_var(var, fbi);
++
++}
++
++/*
++ * pxafb_set_var():
++ *    Set the user defined part of the display for the specified console
++ */
++static int
++pxafb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      struct fb_var_screeninfo *dvar = get_con_var(&fbi->fb, con);
++      struct display *display = get_con_display(&fbi->fb, con);
++      int err, chgvar = 0, rgbidx;
++
++      DPRINTK("set_var\n");
++
++      /*
++       * Decode var contents into a par structure, adjusting any
++       * out of range values.
++       */
++      err = pxafb_validate_var(var, fbi);
++      if (err)
++              return err;
++
++      if (var->activate & FB_ACTIVATE_TEST)
++              return 0;
++
++      if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
++              return -EINVAL;
++
++      if (dvar->xres != var->xres)
++              chgvar = 1;
++      if (dvar->yres != var->yres)
++              chgvar = 1;
++      if (dvar->xres_virtual != var->xres_virtual)
++              chgvar = 1;
++      if (dvar->yres_virtual != var->yres_virtual)
++              chgvar = 1;
++      if (dvar->bits_per_pixel != var->bits_per_pixel)
++              chgvar = 1;
++      if (con < 0)
++              chgvar = 0;
++
++      switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++      case 4:
++              if (fbi->cmap_static)
++                      display->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
++              else
++                      display->visual = FB_VISUAL_PSEUDOCOLOR;
++              display->line_length    = var->xres / 2;
++              display->dispsw         = &fbcon_cfb4;
++              rgbidx                  = RGB_8;
++              break;
++#endif
++#ifdef FBCON_HAS_CFB8
++      case 8:
++              if (fbi->cmap_static)
++                      display->visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
++              else
++                      display->visual = FB_VISUAL_PSEUDOCOLOR;
++              display->line_length    = var->xres;
++              display->dispsw         = &fbcon_cfb8;
++              rgbidx                  = RGB_8;
++              break;
++#endif
++#ifdef FBCON_HAS_CFB16
++      case 12:
++      case 16:
++              display->visual         = FB_VISUAL_TRUECOLOR;
++              display->line_length    = var->xres * 2;
++              display->dispsw         = &fbcon_cfb16;
++              display->dispsw_data    = fbi->fb.pseudo_palette;
++              rgbidx                  = RGB_16;
++              break;
++#endif
++      default:
++              rgbidx = 0;
++              display->dispsw = &fbcon_dummy;
++              break;
++      }
++
++      display->screen_base    = fbi->screen_cpu;
++      display->next_line      = display->line_length;
++      display->type           = fbi->fb.fix.type;
++      display->type_aux       = fbi->fb.fix.type_aux;
++      display->ypanstep       = fbi->fb.fix.ypanstep;
++      display->ywrapstep      = fbi->fb.fix.ywrapstep;
++      display->can_soft_blank = 1;
++      display->inverse        = 0;
++
++      *dvar                   = *var;
++      dvar->activate          &= ~FB_ACTIVATE_ALL;
++
++      /*
++       * Copy the RGB parameters for this display
++       * from the machine specific parameters.
++       */
++      dvar->red               = fbi->rgb[rgbidx]->red;
++      dvar->green             = fbi->rgb[rgbidx]->green;
++      dvar->blue              = fbi->rgb[rgbidx]->blue;
++      dvar->transp            = fbi->rgb[rgbidx]->transp;
++
++      DPRINTK("RGBT length = %d:%d:%d:%d\n",
++              dvar->red.length, dvar->green.length, dvar->blue.length,
++              dvar->transp.length);
++
++      DPRINTK("RGBT offset = %d:%d:%d:%d\n",
++              dvar->red.offset, dvar->green.offset, dvar->blue.offset,
++              dvar->transp.offset);
++
++      /*
++       * Update the old var.  The fbcon drivers still use this.
++       * Once they are using fbi->fb.var, this can be dropped.
++       */
++      display->var = *dvar;
++
++      /*
++       * If we are setting all the virtual consoles, also set the
++       * defaults used to create new consoles.
++       */
++      if (var->activate & FB_ACTIVATE_ALL)
++              fbi->fb.disp->var = *dvar;
++
++      /*
++       * If the console has changed and the console has defined
++       * a changevar function, call that function.
++       */
++      if (chgvar && info && fbi->fb.changevar)
++              fbi->fb.changevar(con);
++
++      /* If the current console is selected, activate the new var. */
++      if (con != fbi->currcon)
++              return 0;
++
++      pxafb_hw_set_var(dvar, fbi);
++
++      return 0;
++}
++
++static int
++__do_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++            struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      struct fb_cmap *dcmap = get_con_cmap(info, con);
++      int err = 0;
++
++      if (con == -1)
++              con = fbi->currcon;
++
++      /* no colormap allocated? (we always have "this" colour map allocated) */
++      if (con >= 0)
++              err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
++
++      if (!err && con == fbi->currcon)
++              err = fb_set_cmap(cmap, kspc, pxafb_setcolreg, info);
++
++      if (!err)
++              fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
++
++      return err;
++}
++
++static int
++pxafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++                struct fb_info *info)
++{
++      struct display *disp = get_con_display(info, con);
++
++      if (disp->visual == FB_VISUAL_TRUECOLOR)
++              return -EINVAL;
++
++      return __do_set_cmap(cmap, kspc, con, info);
++}
++
++static int
++pxafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++      struct display *display = get_con_display(info, con);
++
++      *fix = info->fix;
++
++      fix->line_length = display->line_length;
++      fix->visual      = display->visual;
++      return 0;
++}
++
++static int
++pxafb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      *var = *get_con_var(info, con);
++      return 0;
++}
++
++static int
++pxafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++      struct fb_cmap *dcmap = get_con_cmap(info, con);
++      fb_copy_cmap(dcmap, cmap, kspc ? 0 : 2);
++      return 0;
++}
++
++static struct fb_ops pxafb_ops = {
++      owner:          THIS_MODULE,
++      fb_get_fix:     pxafb_get_fix,
++      fb_get_var:     pxafb_get_var,
++      fb_set_var:     pxafb_set_var,
++      fb_get_cmap:    pxafb_get_cmap,
++      fb_set_cmap:    pxafb_set_cmap,
++};
++
++/*
++ *  pxafb_switch():       
++ *    Change to the specified console.  Palette and video mode
++ *      are changed to the console's stored parameters.
++ *
++ *    Uh oh, this can be called from a tasklet (IRQ)
++ */
++static int pxafb_switch(int con, struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      struct display *disp;
++      struct fb_cmap *cmap;
++
++      DPRINTK("con=%d info->modename=%s\n", con, fbi->fb.modename);
++
++      if (con == fbi->currcon)
++              return 0;
++
++      if (fbi->currcon >= 0) {
++              disp = fb_display + fbi->currcon;
++
++              /*
++               * Save the old colormap and video mode.
++               */
++              disp->var = fbi->fb.var;
++
++              if (disp->cmap.len)
++                      fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
++      }
++
++      fbi->currcon = con;
++      disp = fb_display + con;
++
++      /*
++       * Make sure that our colourmap contains 256 entries.
++       */
++      fb_alloc_cmap(&fbi->fb.cmap, 256, 0);
++
++      if (disp->cmap.len)
++              cmap = &disp->cmap;
++      else
++              cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
++
++      fb_copy_cmap(cmap, &fbi->fb.cmap, 0);
++
++      fbi->fb.var = disp->var;
++      fbi->fb.var.activate = FB_ACTIVATE_NOW;
++
++      pxafb_set_var(&fbi->fb.var, con, info);
++      return 0;
++}
++
++/*
++ * Formal definition of the VESA spec:
++ *  On
++ *    This refers to the state of the display when it is in full operation
++ *  Stand-By
++ *    This defines an optional operating state of minimal power reduction with
++ *    the shortest recovery time
++ *  Suspend
++ *    This refers to a level of power management in which substantial power
++ *    reduction is achieved by the display.  The display can have a longer 
++ *    recovery time from this state than from the Stand-by state
++ *  Off
++ *    This indicates that the display is consuming the lowest level of power
++ *    and is non-operational. Recovery from this state may optionally require
++ *    the user to manually power on the monitor
++ *
++ *  Now, the fbdev driver adds an additional state, (blank), where they
++ *  turn off the video (maybe by colormap tricks), but don't mess with the
++ *  video itself: think of it semantically between on and Stand-By.
++ *
++ *  So here's what we should do in our fbdev blank routine:
++ *
++ *    VESA_NO_BLANKING (mode 0)       Video on,  front/back light on
++ *    VESA_VSYNC_SUSPEND (mode 1)     Video on,  front/back light off
++ *    VESA_HSYNC_SUSPEND (mode 2)     Video on,  front/back light off
++ *    VESA_POWERDOWN (mode 3)         Video off, front/back light off
++ *
++ *  This will match the matrox implementation.
++ */
++/*
++ * pxafb_blank():
++ *    Blank the display by setting all palette values to zero.  Note, the 
++ *    12 and 16 bpp modes don't really use the palette, so this will not
++ *      blank the display in all modes.  
++ */
++static void pxafb_blank(int blank, struct fb_info *info)
++{
++      struct pxafb_info *fbi = (struct pxafb_info *)info;
++      int i;
++
++      DPRINTK("pxafb_blank: blank=%d info->modename=%s\n", blank,
++              fbi->fb.modename);
++
++      switch (blank) {
++      case VESA_POWERDOWN:
++      case VESA_VSYNC_SUSPEND:
++      case VESA_HSYNC_SUSPEND:
++              if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
++                  fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
++                      for (i = 0; i < fbi->palette_size; i++)
++                              pxafb_setpalettereg(i, 0, 0, 0, 0, info);
++              pxafb_schedule_task(fbi, C_DISABLE);
++              if (pxafb_blank_helper)
++                      pxafb_blank_helper(blank);
++              break;
++
++      case VESA_NO_BLANKING:
++              if (pxafb_blank_helper)
++                      pxafb_blank_helper(blank);
++              if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
++                  fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
++                      fb_set_cmap(&fbi->fb.cmap, 1, pxafb_setcolreg, info);
++              pxafb_schedule_task(fbi, C_ENABLE);
++      }
++}
++
++static int pxafb_updatevar(int con, struct fb_info *info)
++{
++      DPRINTK("entered\n");
++      return 0;
++}
++
++/*
++ * Calculate the PCD value from the clock rate (in picoseconds).
++ * We take account of the PPCR clock setting.
++ */
++static inline int get_pcd(unsigned int pixclock)
++{
++      unsigned int pcd;
++
++      if (pixclock) {
++              pcd = get_lclk_frequency_10khz() * pixclock;
++              pcd /= 100000000;
++              pcd += 1;       /* make up for integer math truncations */
++      } else {
++              printk(KERN_WARNING "Please convert me to use the PCD calculations\n");
++              pcd = 0;
++      }
++      return pcd;
++}
++
++/*
++ * pxafb_activate_var():
++ *    Configures LCD Controller based on entries in var parameter.  Settings are      
++ *      only written to the controller if changes were made.  
++ */
++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_long flags;
++
++      DPRINTK("Configuring PXA LCD\n");
++
++      DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n",
++              var->xres, var->hsync_len,
++              var->left_margin, var->right_margin);
++      DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n",
++              var->yres, var->vsync_len,
++              var->upper_margin, var->lower_margin);
++
++#if DEBUG_VAR
++      if (var->xres < 16        || var->xres > 1024)
++              printk(KERN_ERR "%s: invalid xres %d\n",
++                      fbi->fb.fix.id, var->xres);
++      if (var->hsync_len < 1    || var->hsync_len > 64)
++              printk(KERN_ERR "%s: invalid hsync_len %d\n",
++                      fbi->fb.fix.id, var->hsync_len);
++      if (var->left_margin < 1  || var->left_margin > 255)
++              printk(KERN_ERR "%s: invalid left_margin %d\n",
++                      fbi->fb.fix.id, var->left_margin);
++      if (var->right_margin < 1 || var->right_margin > 255)
++              printk(KERN_ERR "%s: invalid right_margin %d\n",
++                      fbi->fb.fix.id, var->right_margin);
++      if (var->yres < 1         || var->yres > 1024)
++              printk(KERN_ERR "%s: invalid yres %d\n",
++                      fbi->fb.fix.id, var->yres);
++      if (var->vsync_len < 1    || var->vsync_len > 64)
++              printk(KERN_ERR "%s: invalid vsync_len %d\n",
++                      fbi->fb.fix.id, var->vsync_len);
++      if (var->upper_margin < 0 || var->upper_margin > 255)
++              printk(KERN_ERR "%s: invalid upper_margin %d\n",
++                      fbi->fb.fix.id, var->upper_margin);
++      if (var->lower_margin < 0 || var->lower_margin > 255)
++              printk(KERN_ERR "%s: invalid lower_margin %d\n",
++                      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) +
++              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
++              |
++              (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);
++
++      DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
++      DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
++      DPRINTK("nlccr2 = 0x%08x\n", new_regs.lccr2);
++      DPRINTK("nlccr3 = 0x%08x\n", new_regs.lccr3);
++
++      /* Update shadow copy atomically */
++      local_irq_save(flags);
++
++      /* setup dma descriptors */
++      fbi->dmadesc_fblow_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 3*16);
++      fbi->dmadesc_fbhigh_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 2*16);
++      fbi->dmadesc_palette_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 1*16);
++
++      fbi->dmadesc_fblow_dma = fbi->palette_dma - 3*16;
++      fbi->dmadesc_fbhigh_dma = fbi->palette_dma - 2*16;
++      fbi->dmadesc_palette_dma = fbi->palette_dma - 1*16;
++
++      #define BYTES_PER_PANEL ((fbi->lccr0 & LCCR0_SDS) ? (var->xres * var->yres * var->bits_per_pixel / 8 / 2) : \
++                                                          (var->xres * var->yres * var->bits_per_pixel / 8))
++      
++      /* populate descriptors */
++      fbi->dmadesc_fblow_cpu->fdadr = fbi->dmadesc_fblow_dma;
++      fbi->dmadesc_fblow_cpu->fsadr = fbi->screen_dma + BYTES_PER_PANEL;
++      fbi->dmadesc_fblow_cpu->fidr  = 0;
++      fbi->dmadesc_fblow_cpu->ldcmd = BYTES_PER_PANEL;
++
++      fbi->fdadr1 = fbi->dmadesc_fblow_dma; /* only used in dual-panel mode */
++              
++      fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma;
++      fbi->dmadesc_fbhigh_cpu->fidr = 0;
++      fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL;
++
++      fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma;
++      fbi->dmadesc_palette_cpu->fidr  = 0;
++      fbi->dmadesc_palette_cpu->ldcmd = (fbi->palette_size * 2) | LDCMD_PAL;
++
++      if( var->bits_per_pixel < 12)
++      {
++              /* assume any mode with <12 bpp is palette driven */
++              fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
++              fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma;
++              fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */
++      }
++      else
++      {
++              /* palette shouldn't be loaded in true-color mode */
++              fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma;
++              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;
++      fbi->reg_lccr3 = new_regs.lccr3;
++      local_irq_restore(flags);
++
++      /*
++       * Only update the registers if the controller is enabled
++       * and something has changed.
++       */
++      if ((LCCR0 != fbi->reg_lccr0)       || (LCCR1 != fbi->reg_lccr1) ||
++          (LCCR2 != fbi->reg_lccr2)       || (LCCR3 != fbi->reg_lccr3) ||
++          (FDADR0 != fbi->fdadr0) || (FDADR1 != fbi->fdadr1))
++              pxafb_schedule_task(fbi, C_REENABLE);
++
++      return 0;
++}
++
++/*
++ * NOTE!  The following functions are purely helpers for set_ctrlr_state.
++ * Do not call them directly; set_ctrlr_state does the correct serialisation
++ * to ensure that things happen in the right way 100% of time time.
++ *    -- rmk
++ */
++
++/*
++ * FIXME: move LCD power stuff into pxafb_power_up_lcd()
++ * Also, I'm expecting that the backlight stuff should
++ * be handled differently.
++ */
++static void pxafb_backlight_on(struct pxafb_info *fbi)
++{
++      DPRINTK("backlight on\n");
++
++#ifdef CONFIG_ARCH_PXA_IDP
++      if(machine_is_pxa_idp()) {      
++              FB_BACKLIGHT_ON();
++      }
++#endif
++}
++
++/*
++ * FIXME: move LCD power stuf into pxafb_power_down_lcd()
++ * Also, I'm expecting that the backlight stuff should
++ * be handled differently.
++ */
++static void pxafb_backlight_off(struct pxafb_info *fbi)
++{
++      DPRINTK("backlight off\n");
++
++#ifdef CONFIG_ARCH_PXA_IDP
++      if(machine_is_pxa_idp()) {
++              FB_BACKLIGHT_OFF();
++      }
++#endif
++      
++}
++
++static void pxafb_power_up_lcd(struct pxafb_info *fbi)
++{
++      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
++}
++
++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
++
++}
++
++static void pxafb_setup_gpio(struct pxafb_info *fbi)
++{
++      unsigned int lccr0;
++
++      /*
++       * setup is based on type of panel supported
++       */
++
++      lccr0 = fbi->lccr0;
++
++      /* 4 bit interface */
++      if ((lccr0 & LCCR0_CMS) && (lccr0 & LCCR0_SDS) && !(lccr0 & LCCR0_DPD))
++      {
++              // bits 58-61
++              GPDR1 |= (0xf << 26);
++              GAFR1_U = (GAFR1_U & ~(0xff << 20)) | (0xaa << 20);
++
++              // bits 74-77
++              GPDR2 |= (0xf << 10);
++                GAFR2_L = (GAFR2_L & ~(0xff << 20)) | (0xaa << 20);
++      }
++
++        /* 8 bit interface */
++        else if (((lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_DPD))) ||
++                 (!(lccr0 & LCCR0_CMS) && !(lccr0 & LCCR0_PAS) && !(lccr0 & LCCR0_SDS)))
++        {
++              // bits 58-65
++                GPDR1 |= (0x3f << 26);
++                GPDR2 |= (0x3);
++
++                GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20);
++                GAFR2_L = (GAFR2_L & ~0xf) | (0xa);
++
++                // bits 74-77
++                GPDR2 |= (0xf << 10);
++                GAFR2_L = (GAFR2_L & ~(0xff << 20)) | (0xaa << 20);
++        }
++
++        /* 16 bit interface */
++        else if (!(lccr0 & LCCR0_CMS) && ((lccr0 & LCCR0_SDS) || (lccr0 & LCCR0_PAS)))
++        {
++              // bits 58-77
++                GPDR1 |= (0x3f << 26);
++                GPDR2 |= 0x00003fff;
++
++                GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20);
++                GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa;
++        }
++      else
++      {
++              printk( KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n");
++      }
++}
++
++static void pxafb_enable_controller(struct pxafb_info *fbi)
++{
++      DPRINTK("Enabling LCD controller\n");
++
++      /* Sequence from 11.7.10 */
++      LCCR3 = fbi->reg_lccr3;
++      LCCR2 = fbi->reg_lccr2;
++      LCCR1 = fbi->reg_lccr1;
++      LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB;
++
++      /* FIXME we used to have LCD power control here */
++
++      FDADR0 = fbi->fdadr0;
++      FDADR1 = fbi->fdadr1;
++      LCCR0 |= LCCR0_ENB;
++
++      DPRINTK("FDADR0 = 0x%08x\n", (unsigned int)FDADR0);
++      DPRINTK("FDADR1 = 0x%08x\n", (unsigned int)FDADR1);
++      DPRINTK("LCCR0 = 0x%08x\n", (unsigned int)LCCR0);
++      DPRINTK("LCCR1 = 0x%08x\n", (unsigned int)LCCR1);
++      DPRINTK("LCCR2 = 0x%08x\n", (unsigned int)LCCR2);
++      DPRINTK("LCCR3 = 0x%08x\n", (unsigned int)LCCR3);
++}
++
++static void pxafb_disable_controller(struct pxafb_info *fbi)
++{
++      DECLARE_WAITQUEUE(wait, current);
++
++      DPRINTK("Disabling LCD controller\n");
++
++      /* FIXME add power down GPIO stuff here */
++
++      add_wait_queue(&fbi->ctrlr_wait, &wait);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++
++      LCSR = 0xffffffff;      /* Clear LCD Status Register */
++      LCCR0 &= ~LCCR0_LDM;    /* Enable LCD Disable Done Interrupt */
++      LCCR0 &= ~LCCR0_ENB;    /* Disable LCD Controller */
++
++      schedule_timeout(20 * HZ / 1000);
++      current->state = TASK_RUNNING;
++      remove_wait_queue(&fbi->ctrlr_wait, &wait);
++}
++
++/*
++ *  pxafb_handle_irq: Handle 'LCD DONE' interrupts.
++ */
++static void pxafb_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct pxafb_info *fbi = dev_id;
++      unsigned int lcsr = LCSR;
++
++      if (lcsr & LCSR_LDD) {
++              LCCR0 |= LCCR0_LDM;
++              wake_up(&fbi->ctrlr_wait);
++      }
++
++      LCSR = lcsr;
++}
++
++/*
++ * This function must be called from task context only, since it will
++ * sleep when disabling the LCD controller, or if we get two contending
++ * processes trying to alter state.
++ */
++static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
++{
++      u_int old_state;
++
++      down(&fbi->ctrlr_sem);
++
++      old_state = fbi->state;
++
++      switch (state) {
++      case C_DISABLE_CLKCHANGE:
++              /*
++               * Disable controller for clock change.  If the
++               * controller is already disabled, then do nothing.
++               */
++              if (old_state != C_DISABLE) {
++                      fbi->state = state;
++                      pxafb_disable_controller(fbi);
++              }
++              break;
++
++      case C_DISABLE:
++              /*
++               * Disable controller
++               */
++              if (old_state != C_DISABLE) {
++                      fbi->state = state;
++
++                      pxafb_backlight_off(fbi);
++                      if (old_state != C_DISABLE_CLKCHANGE)
++                              pxafb_disable_controller(fbi);
++                      pxafb_power_down_lcd(fbi);
++              }
++              break;
++
++      case C_ENABLE_CLKCHANGE:
++              /*
++               * Enable the controller after clock change.  Only
++               * do this if we were disabled for the clock change.
++               */
++              if (old_state == C_DISABLE_CLKCHANGE) {
++                      fbi->state = C_ENABLE;
++                      pxafb_enable_controller(fbi);
++              }
++              break;
++
++      case C_REENABLE:
++              /*
++               * Re-enable the controller only if it was already
++               * enabled.  This is so we reprogram the control
++               * registers.
++               */
++              if (old_state == C_ENABLE) {
++                      pxafb_disable_controller(fbi);
++                      pxafb_setup_gpio(fbi);
++                      pxafb_enable_controller(fbi);
++              }
++              break;
++
++      case C_ENABLE:
++              /*
++               * Power up the LCD screen, enable controller, and
++               * turn on the backlight.
++               */
++              if (old_state != C_ENABLE) {
++                      fbi->state = C_ENABLE;
++                      pxafb_setup_gpio(fbi);
++                      pxafb_power_up_lcd(fbi);
++                      pxafb_enable_controller(fbi);
++                      pxafb_backlight_on(fbi);
++              }
++              break;
++      }
++      up(&fbi->ctrlr_sem);
++}
++
++/*
++ * Our LCD controller task (which is called when we blank or unblank)
++ * via keventd.
++ */
++static void pxafb_task(void *dummy)
++{
++      struct pxafb_info *fbi = dummy;
++      u_int state = xchg(&fbi->task_state, -1);
++
++      set_ctrlr_state(fbi, state);
++}
++
++#ifdef CONFIG_CPU_FREQ
++/*
++ * CPU clock speed change handler.  We need to adjust the LCD timing
++ * parameters when the CPU clock is adjusted by the power management
++ * subsystem.
++ */
++static int
++pxafb_clkchg_notifier(struct notifier_block *nb, unsigned long val,
++                       void *data)
++{
++      struct pxafb_info *fbi = TO_INF(nb, clockchg);
++      u_int pcd;
++
++      switch (val) {
++      case CPUFREQ_MINMAX:
++              /* todo: fill in min/max values */
++              break;
++
++      case CPUFREQ_PRECHANGE:
++              set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
++              break;
++
++      case CPUFREQ_POSTCHANGE:
++              pcd = get_pcd(fbi->fb.var.pixclock);
++              fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
++              set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
++              break;
++      }
++      return 0;
++}
++#endif
++
++#ifdef CONFIG_PM
++/*
++ * Power management hook.  Note that we won't be called from IRQ context,
++ * unlike the blank functions above, so we may sleep.
++ */
++static int
++pxafb_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
++{
++      struct pxafb_info *fbi = pm_dev->data;
++
++      DPRINTK("pm_callback: %d\n", req);
++
++      if (req == PM_SUSPEND || req == PM_RESUME) {
++              int state = (int)data;
++
++              if (state == 0) {
++                      /* Enter D0. */
++                      set_ctrlr_state(fbi, C_ENABLE);
++              } else {
++                      /* Enter D1-D3.  Disable the LCD controller.  */
++                      set_ctrlr_state(fbi, C_DISABLE);
++              }
++      }
++      DPRINTK("done\n");
++      return 0;
++}
++#endif
++
++/*
++ * pxafb_map_video_memory():
++ *      Allocates the DRAM memory for the frame buffer.  This buffer is  
++ *    remapped into a non-cached, non-buffered, memory region to  
++ *      allow palette and pixel writes to occur without flushing the 
++ *      cache.  Once this area is remapped, all virtual memory
++ *      access to the video memory should occur at the new region.
++ */
++static int __init pxafb_map_video_memory(struct pxafb_info *fbi)
++{
++      u_long palette_mem_size;
++
++      /*
++       * We reserve one page for the palette, plus the size
++       * of the framebuffer.
++       *
++       * layout of stuff in memory
++       *
++       *                fblow descriptor
++       *                fbhigh descriptor
++       *                palette descriptor
++       *                palette
++       *   page boundary->
++       *                frame buffer
++       */
++      fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
++      fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
++                                      &fbi->map_dma, PTE_BUFFERABLE);
++
++      if (fbi->map_cpu) {
++              fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE;
++              fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
++              fbi->fb.fix.smem_start = fbi->screen_dma;
++
++              fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;
++
++              palette_mem_size = fbi->palette_size * sizeof(u16);
++
++              DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
++
++              fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
++              fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
++
++      }
++
++      return fbi->map_cpu ? 0 : -ENOMEM;
++}
++
++/* Fake monspecs to fill in fbinfo structure */
++static struct fb_monspecs monspecs __initdata = {
++      30000, 70000, 50, 65, 0 /* Generic */
++};
++
++
++static struct pxafb_info * __init pxafb_init_fbinfo(void)
++{
++      struct pxafb_mach_info *inf;
++      struct pxafb_info *fbi;
++
++      fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(struct display) +
++                    sizeof(u16) * 16, GFP_KERNEL);
++      if (!fbi)
++              return NULL;
++
++      memset(fbi, 0, sizeof(struct pxafb_info) + sizeof(struct display));
++
++      fbi->currcon            = -1;
++
++      strcpy(fbi->fb.fix.id, PXA_NAME);
++
++      fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
++      fbi->fb.fix.type_aux    = 0;
++      fbi->fb.fix.xpanstep    = 0;
++      fbi->fb.fix.ypanstep    = 0;
++      fbi->fb.fix.ywrapstep   = 0;
++      fbi->fb.fix.accel       = FB_ACCEL_NONE;
++
++      fbi->fb.var.nonstd      = 0;
++      fbi->fb.var.activate    = FB_ACTIVATE_NOW;
++      fbi->fb.var.height      = -1;
++      fbi->fb.var.width       = -1;
++      fbi->fb.var.accel_flags = 0;
++      fbi->fb.var.vmode       = FB_VMODE_NONINTERLACED;
++
++      strcpy(fbi->fb.modename, PXA_NAME);
++      strcpy(fbi->fb.fontname, "Acorn8x8");
++
++      fbi->fb.fbops           = &pxafb_ops;
++      fbi->fb.changevar       = NULL;
++      fbi->fb.switch_con      = pxafb_switch;
++      fbi->fb.updatevar       = pxafb_updatevar;
++      fbi->fb.blank           = pxafb_blank;
++      fbi->fb.flags           = FBINFO_FLAG_DEFAULT;
++      fbi->fb.node            = -1;
++      fbi->fb.monspecs        = monspecs;
++      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);
++
++      fbi->max_xres                   = inf->xres;
++      fbi->fb.var.xres                = inf->xres;
++      fbi->fb.var.xres_virtual        = inf->xres;
++      fbi->max_yres                   = inf->yres;
++      fbi->fb.var.yres                = inf->yres;
++      fbi->fb.var.yres_virtual        = inf->yres;
++      fbi->max_bpp                    = inf->bpp;
++      fbi->fb.var.bits_per_pixel      = inf->bpp;
++      fbi->fb.var.pixclock            = inf->pixclock;
++      fbi->fb.var.hsync_len           = inf->hsync_len;
++      fbi->fb.var.left_margin         = inf->left_margin;
++      fbi->fb.var.right_margin        = inf->right_margin;
++      fbi->fb.var.vsync_len           = inf->vsync_len;
++      fbi->fb.var.upper_margin        = inf->upper_margin;
++      fbi->fb.var.lower_margin        = inf->lower_margin;
++      fbi->fb.var.sync                = inf->sync;
++      fbi->fb.var.grayscale           = inf->cmap_greyscale;
++      fbi->cmap_inverse               = inf->cmap_inverse;
++      fbi->cmap_static                = inf->cmap_static;
++      fbi->lccr0                      = inf->lccr0;
++      fbi->lccr3                      = inf->lccr3;
++      fbi->state                      = C_DISABLE;
++      fbi->task_state                 = (u_char)-1;
++      fbi->fb.fix.smem_len            = fbi->max_xres * fbi->max_yres *
++                                        fbi->max_bpp / 8;
++
++      init_waitqueue_head(&fbi->ctrlr_wait);
++      INIT_TQUEUE(&fbi->task, pxafb_task, fbi);
++      init_MUTEX(&fbi->ctrlr_sem);
++
++      return fbi;
++}
++
++int __init pxafb_init(void)
++{
++      struct pxafb_info *fbi;
++      int ret;
++
++      fbi = pxafb_init_fbinfo();
++      ret = -ENOMEM;
++      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)
++              goto failed;
++
++      ret = request_irq(IRQ_LCD, pxafb_handle_irq, SA_INTERRUPT,
++                        "LCD", fbi);
++      if (ret) {
++              printk(KERN_ERR "pxafb: failed in request_irq: %d\n", ret);
++              goto failed;
++      }
++
++      pxafb_set_var(&fbi->fb.var, -1, &fbi->fb);
++
++      ret = register_framebuffer(&fbi->fb);
++      if (ret < 0)
++              goto failed;
++
++#ifdef CONFIG_PM
++      /*
++       * Note that the console registers this as well, but we want to
++       * power down the display prior to sleeping.
++       */
++      fbi->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, pxafb_pm_callback);
++      if (fbi->pm)
++              fbi->pm->data = fbi;
++#endif
++#ifdef CONFIG_CPU_FREQ
++      fbi->clockchg.notifier_call = pxafb_clkchg_notifier;
++      cpufreq_register_notifier(&fbi->clockchg);
++#endif
++
++      /*
++       * Ok, now enable the LCD controller
++       */
++      set_ctrlr_state(fbi, C_ENABLE);
++
++      /* This driver cannot be unloaded at the moment */
++      MOD_INC_USE_COUNT;
++
++      return 0;
++
++failed:
++      if (fbi)
++              kfree(fbi);
++      return ret;
++}
++
++
++#ifdef MODULE
++module_init(pxafb_init);
++#endif
++
++MODULE_DESCRIPTION("loadable framebuffer driver for PXA");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/video/pxafb.h
+@@ -0,0 +1,238 @@
++/*
++ * linux/drivers/video/pxafb.h
++ *    -- Intel PXA250/210 LCD Controller Frame Buffer Device
++ *
++ *  Copyright (C) 1999 Eric A. Thomas
++ *   Based on acornfb.c Copyright (C) Russell King.
++ *
++ *  2001-08-03: Cliff Brake <cbrake@acclent.com>
++ *     - ported SA1100 code to PXA
++ *  
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file COPYING in the main directory of this archive
++ * for more details.
++ */
++
++/*
++ * These are the bitfields for each
++ * display depth that we support.
++ */
++struct pxafb_rgb {
++      struct fb_bitfield      red;
++      struct fb_bitfield      green;
++      struct fb_bitfield      blue;
++      struct fb_bitfield      transp;
++};
++
++/*
++ * This structure describes the machine which we are running on.
++ */
++struct pxafb_mach_info {
++      u_long          pixclock;
++
++      u_short         xres;
++      u_short         yres;
++
++      u_char          bpp;
++      u_char          hsync_len;
++      u_char          left_margin;
++      u_char          right_margin;
++
++      u_char          vsync_len;
++      u_char          upper_margin;
++      u_char          lower_margin;
++      u_char          sync;
++
++      u_int           cmap_greyscale:1,
++                      cmap_inverse:1,
++                      cmap_static:1,
++                      unused:29;
++
++      u_int           lccr0;
++      u_int           lccr3;
++};
++
++/* Shadows for LCD controller registers */
++struct pxafb_lcd_reg {
++      unsigned int lccr0;
++      unsigned int lccr1;
++      unsigned int lccr2;
++      unsigned int lccr3;
++};
++
++/* PXA LCD DMA descriptor */
++struct pxafb_dma_descriptor {
++      unsigned int fdadr;
++      unsigned int fsadr;
++      unsigned int fidr;
++      unsigned int ldcmd;
++};
++
++#define RGB_8 (0)
++#define RGB_16        (1)
++#define NR_RGB        2
++
++struct pxafb_info {
++      struct fb_info          fb;
++      signed int              currcon;
++
++      struct pxafb_rgb        *rgb[NR_RGB];
++
++      u_int                   max_bpp;
++      u_int                   max_xres;
++      u_int                   max_yres;
++
++      /*
++       * These are the addresses we mapped
++       * the framebuffer memory region to.
++       */
++
++      /* raw memory addresses */
++      dma_addr_t              map_dma;        /* physical */
++      u_char *                map_cpu;        /* virtual */
++      u_int                   map_size;
++
++      /* addresses of pieces placed in raw buffer */
++      u_char *                screen_cpu;     /* virtual address of frame buffer */
++      dma_addr_t              screen_dma;     /* physical address of frame buffer */
++      u16 *                   palette_cpu;    /* virtual address of palette memory */
++      dma_addr_t              palette_dma;    /* physical address of palette memory */
++      u_int                   palette_size;
++
++      /* DMA descriptors */
++      struct pxafb_dma_descriptor *   dmadesc_fblow_cpu;
++      dma_addr_t                              dmadesc_fblow_dma;
++      struct pxafb_dma_descriptor *   dmadesc_fbhigh_cpu;
++      dma_addr_t                              dmadesc_fbhigh_dma;
++      struct pxafb_dma_descriptor *   dmadesc_palette_cpu;
++      dma_addr_t                              dmadesc_palette_dma;
++
++      dma_addr_t              fdadr0;
++      dma_addr_t              fdadr1;
++
++      u_int                   lccr0;
++      u_int                   lccr3;
++      u_int                   cmap_inverse:1,
++                              cmap_static:1,
++                              unused:30;
++
++      u_int                   reg_lccr0;
++      u_int                   reg_lccr1;
++      u_int                   reg_lccr2;
++      u_int                   reg_lccr3;
++
++      volatile u_char         state;
++      volatile u_char         task_state;
++      struct semaphore        ctrlr_sem;
++      wait_queue_head_t       ctrlr_wait;
++      struct tq_struct        task;
++
++#ifdef CONFIG_PM
++      struct pm_dev           *pm;
++#endif
++#ifdef CONFIG_CPU_FREQ
++      struct notifier_block   clockchg;
++#endif
++};
++
++#define __type_entry(ptr,type,member) ((type *)((char *)(ptr)-offsetof(type,member)))
++
++#define TO_INF(ptr,member)    __type_entry(ptr,struct pxafb_info,member)
++
++/*
++ * These are the actions for set_ctrlr_state
++ */
++#define C_DISABLE             (0)
++#define C_ENABLE              (1)
++#define C_DISABLE_CLKCHANGE   (2)
++#define C_ENABLE_CLKCHANGE    (3)
++#define C_REENABLE            (4)
++
++#define PXA_NAME      "PXA"
++
++/*
++ *  Debug macros 
++ */
++#if DEBUG
++#  define DPRINTK(fmt, args...)       printk("%s: " fmt, __FUNCTION__ , ## args)
++#else
++#  define DPRINTK(fmt, args...)
++#endif
++
++/*
++ * Minimum X and Y resolutions
++ */
++#define MIN_XRES      64
++#define MIN_YRES      64
++
++/*
++ * Are we configured for 8 or 16 bits per pixel?
++ */
++#ifdef CONFIG_FB_PXA_8BPP
++#  define PXAFB_BPP           8
++#  define PXAFB_BPP_BITS      0x03
++#elif CONFIG_FB_PXA_16BPP
++#  define PXAFB_BPP           16
++#  define PXAFB_BPP_BITS      0x04
++#endif
++
++#if defined(CONFIG_ARCH_LUBBOCK)
++#define LCD_PIXCLOCK                  150000
++#define LCD_BPP                               PXAFB_BPP
++#ifdef CONFIG_FB_PXA_QVGA
++#define LCD_XRES                      320
++#define LCD_YRES                      240
++#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH       51
++#define LCD_VERTICAL_SYNC_PULSE_WIDTH 1
++#define LCD_BEGIN_OF_LINE_WAIT_COUNT  1
++#define LCD_BEGIN_FRAME_WAIT_COUNT    8
++#define LCD_END_OF_LINE_WAIT_COUNT    1
++#define LCD_END_OF_FRAME_WAIT_COUNT   1
++#define LCD_SYNC                      (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT)
++#define LCD_LCCR0                     0x003008F8
++#define LCD_LCCR3                     (0x0040FF0C | (PXAFB_BPP_BITS << 24))
++#else
++#define LCD_XRES                      640
++#define LCD_YRES                      480
++#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH       1
++#define LCD_VERTICAL_SYNC_PULSE_WIDTH 1
++#define LCD_BEGIN_OF_LINE_WAIT_COUNT  3
++#define LCD_BEGIN_FRAME_WAIT_COUNT    0
++#define LCD_END_OF_LINE_WAIT_COUNT    3
++#define LCD_END_OF_FRAME_WAIT_COUNT   0
++#define LCD_SYNC                      (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT)
++#define LCD_LCCR0                     0x0030087C
++#define LCD_LCCR3                     (0x0040FF0C | (PXAFB_BPP_BITS << 24))
++#endif
++
++#elif defined (CONFIG_ARCH_PXA_IDP)
++#define LCD_PIXCLOCK                  150000
++#define LCD_BPP                               PXAFB_BPP
++#define LCD_XRES                      640
++#define LCD_YRES                      480
++#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH       1
++#define LCD_VERTICAL_SYNC_PULSE_WIDTH 1
++#define LCD_BEGIN_OF_LINE_WAIT_COUNT  3
++#define LCD_BEGIN_FRAME_WAIT_COUNT    0
++#define LCD_END_OF_LINE_WAIT_COUNT    3
++#define LCD_END_OF_FRAME_WAIT_COUNT   0
++#define LCD_SYNC                      (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT)
++#define LCD_LCCR0                     0x0030087C
++#define LCD_LCCR3                     (0x0040FF0C | (PXAFB_BPP_BITS << 24))
++
++#elif defined CONFIG_PXA_CERF_PDA
++#define LCD_PIXCLOCK                  171521
++#define LCD_BPP                               PXAFB_BPP
++#define LCD_XRES                      240
++#define LCD_YRES                      320
++#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH       7
++#define LCD_VERTICAL_SYNC_PULSE_WIDTH 2
++#define LCD_BEGIN_OF_LINE_WAIT_COUNT  17
++#define LCD_BEGIN_FRAME_WAIT_COUNT    0
++#define LCD_END_OF_LINE_WAIT_COUNT    17
++#define LCD_END_OF_FRAME_WAIT_COUNT   0
++#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_PixClkDiv(0x12) | LCCR3_Bpp(PXAFB_BPP_BITS) | LCCR3_Acb(0x18))
++
++#endif
+--- linux-2.4.27/drivers/video/sa1100fb.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/drivers/video/sa1100fb.c
+@@ -2175,7 +2175,7 @@
+        */
+       fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
+       fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
+-                                      &fbi->map_dma);
++                                      &fbi->map_dma, PTE_BUFFERABLE);
+       if (fbi->map_cpu) {
+               fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE;
+--- linux-2.4.27/fs/Config.in~2.4.27-vrs1-pxa1
++++ linux-2.4.27/fs/Config.in
+@@ -51,6 +51,9 @@
+    int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
+ fi
+ tristate 'Compressed ROM file system support' CONFIG_CRAMFS
++dep_mbool '  Use linear addressing for cramfs' CONFIG_CRAMFS_LINEAR $CONFIG_CRAMFS
++dep_bool '    Support XIP on linear cramfs' CONFIG_CRAMFS_LINEAR_XIP $CONFIG_CRAMFS_LINEAR
++dep_bool '    Root file system on linear cramfs' CONFIG_ROOT_CRAMFS_LINEAR $CONFIG_CRAMFS_LINEAR
+ bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
+ define_bool CONFIG_RAMFS y
+--- linux-2.4.27/fs/cramfs/inode.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/fs/cramfs/inode.c
+@@ -4,11 +4,29 @@
+  * Copyright (C) 1999 Linus Torvalds.
+  *
+  * This file is released under the GPL.
+- */
+-
+-/*
++ *
+  * These are the VFS interfaces to the compressed rom filesystem.
+  * The actual compression is based on zlib, see the other files.
++ *
++ * Linear Addressing code
++ * Copyright (C) 2000 Shane Nay.
++ *
++ *   Allows you to have a linearly addressed cramfs filesystem.
++ *   Saves the need for buffer, and the munging of the buffer.
++ *   Savings a bit over 32k with default PAGE_SIZE, BUFFER_SIZE
++ *   etc.  Usefull on embedded platform with ROM :-).
++ *
++ *   Downsides- Currently linear addressed cramfs partitions
++ *   don't co-exist with block cramfs partitions.
++ *
++ * 28-Dec-2000: XIP mode for linear cramfs
++ * Copyright (C) 2000 Robert Leslie <rob@mars.org>
++ *
++ * Dynamic allocation of linear cramfs space by Nicolas Pitre
++ * Copyright (C) 2003 Monta Vista Software, Inc.
++ *
++ *   Linear cramfs now requires that you pass the physaddr= parameter to
++ *   the mount process.  Allows for multiple linear cramfs partitions.
+  */
+ #include <linux/module.h>
+@@ -16,10 +34,12 @@
+ #include <linux/pagemap.h>
+ #include <linux/init.h>
+ #include <linux/string.h>
++#include <linux/kernel.h>
+ #include <linux/locks.h>
+ #include <linux/blkdev.h>
+ #include <linux/cramfs_fs.h>
+ #include <asm/semaphore.h>
++#include <asm/io.h>
+ #include <asm/uaccess.h>
+@@ -28,6 +48,8 @@
+ #define CRAMFS_SB_BLOCKS u.cramfs_sb.blocks
+ #define CRAMFS_SB_FILES u.cramfs_sb.files
+ #define CRAMFS_SB_FLAGS u.cramfs_sb.flags
++#define CRAMFS_SB_LINEAR_PHYS_ADDR u.cramfs_sb.linear_phys_addr
++#define CRAMFS_SB_LINEAR_VIRT_ADDR u.cramfs_sb.linear_virt_addr
+ static struct super_operations cramfs_ops;
+ static struct inode_operations cramfs_dir_inode_operations;
+@@ -42,6 +64,74 @@
+ #define CRAMINO(x)    ((x)->offset?(x)->offset<<2:1)
+ #define OFFSET(x)     ((x)->i_ino)
++
++#ifdef CONFIG_CRAMFS_LINEAR_XIP
++
++static int cramfs_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      unsigned long address, length;
++      struct inode *inode = file->f_dentry->d_inode;
++      struct super_block *sb = inode->i_sb;
++
++      /* this is only used in the case of read-only maps for XIP */
++
++      if (vma->vm_flags & VM_WRITE)
++              return generic_file_mmap(file, vma);
++
++      if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
++              return -EINVAL;
++
++      address  = PAGE_ALIGN(sb->CRAMFS_SB_LINEAR_PHYS_ADDR + OFFSET(inode));
++      address += vma->vm_pgoff << PAGE_SHIFT;
++
++      length = vma->vm_end - vma->vm_start;
++
++      if (length > inode->i_size)
++              length = inode->i_size;
++
++      length = PAGE_ALIGN(length);
++
++
++#if 0
++      /* Doing the following makes it slower and more broken.  bdl */
++      /*
++       * Accessing memory above the top the kernel knows about or
++       * through a file pointer that was marked O_SYNC will be
++       * done non-cached.
++       */
++      vma->vm_page_prot =
++              __pgprot((pgprot_val(vma->vm_page_prot) & ~_CACHE_MASK)
++                      | _CACHE_UNCACHED);
++#endif
++
++      /*
++       * Don't dump addresses that are not real memory to a core file.
++       */
++      vma->vm_flags |= VM_IO;
++      flush_tlb_page(vma, address);
++      if (remap_page_range(vma->vm_start, address, length,
++                           vma->vm_page_prot))
++              return -EAGAIN;
++
++#ifdef DEBUG_CRAMFS_XIP
++      printk("cramfs_mmap: mapped %s at 0x%08lx, length %lu to vma 0x%08lx"
++              ", page_prot 0x%08lx\n",
++              file->f_dentry->d_name.name, address, length,
++              vma->vm_start, pgprot_val(vma->vm_page_prot));
++#endif
++
++      return 0;
++}
++
++static struct file_operations cramfs_linear_xip_fops = {
++      read:   generic_file_read,
++      mmap:   cramfs_mmap,
++};
++
++#define CRAMFS_INODE_IS_XIP(x) ((x)->i_mode & S_ISVTX)
++
++#endif
++
+ static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode)
+ {
+       struct inode * inode = new_inode(sb);
+@@ -60,7 +150,11 @@
+                  without -noleaf option. */
+               insert_inode_hash(inode);
+               if (S_ISREG(inode->i_mode)) {
++#ifdef CONFIG_CRAMFS_LINEAR_XIP
++                      inode->i_fop = CRAMFS_INODE_IS_XIP(inode) ? &cramfs_linear_xip_fops : &generic_ro_fops;
++#else
+                       inode->i_fop = &generic_ro_fops;
++#endif
+                       inode->i_data.a_ops = &cramfs_aops;
+               } else if (S_ISDIR(inode->i_mode)) {
+                       inode->i_op = &cramfs_dir_inode_operations;
+@@ -76,6 +170,18 @@
+       return inode;
+ }
++#ifdef CONFIG_CRAMFS_LINEAR
++/*
++ * Return a pointer to the block in the linearly addressed cramfs image.
++ */
++static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
++{
++      if (!len)
++              return NULL;
++      return (void*)(sb->CRAMFS_SB_LINEAR_VIRT_ADDR + offset);
++}
++
++#else /* Not linear addressing - aka regular block mode. */
+ /*
+  * We have our own block cache: don't fill up the buffer cache
+  * with the rom-image, because the way the filesystem is set
+@@ -192,19 +298,59 @@
+       }
+       return read_buffers[buffer] + offset;
+ }
+-
++#endif /* !CONFIG_CRAMFS_LINEAR */
+ static struct super_block * cramfs_read_super(struct super_block *sb, void *data, int silent)
+ {
++#ifndef CONFIG_CRAMFS_LINEAR
+       int i;
++#else
++      char *p;
++#endif
+       struct cramfs_super super;
+       unsigned long root_offset;
+       struct super_block * retval = NULL;
++#ifndef CONFIG_CRAMFS_LINEAR
+       /* Invalidate the read buffers on mount: think disk change.. */
+       for (i = 0; i < READ_BUFFERS; i++)
+               buffer_blocknr[i] = -1;
++#else
++
++      /*
++       * The physical location of the cramfs image is specified as
++       * a mount parameter.  This parameter is mandatory for obvious
++       * reasons.  Some validation is made on the phys address but this
++       * is not exhaustive and we count on the fact that someone using
++       * this feature is supposed to know what he/she's doing.
++       */
++      if (!data || !(p = strstr((char *)data, "physaddr="))) {
++              printk(KERN_ERR "cramfs: unknown physical address for linear cramfs image\n");
++              goto out;
++      }
++      sb->CRAMFS_SB_LINEAR_PHYS_ADDR = simple_strtoul(p + 9, NULL, 0);
++      if (sb->CRAMFS_SB_LINEAR_PHYS_ADDR & (PAGE_SIZE-1)) {
++              printk(KERN_ERR "cramfs: physical address 0x%lx for linear cramfs isn't aligned to a page boundary\n",
++                     sb->CRAMFS_SB_LINEAR_PHYS_ADDR);
++              goto out;
++      }
++      if (sb->CRAMFS_SB_LINEAR_PHYS_ADDR == 0) {
++              printk(KERN_ERR "cramfs: physical address for linear cramfs image can't be 0\n");
++              goto out;
++      }
++      printk(KERN_INFO "cramfs: checking physical address 0x%lx for linear cramfs image\n",
++             sb->CRAMFS_SB_LINEAR_PHYS_ADDR);
++
++      /* Map only one page for now.  Will remap it when fs size is known. */
++      sb->CRAMFS_SB_LINEAR_VIRT_ADDR =
++              ioremap(sb->CRAMFS_SB_LINEAR_PHYS_ADDR, PAGE_SIZE);
++      if (!sb->CRAMFS_SB_LINEAR_VIRT_ADDR) {
++              printk(KERN_ERR "cramfs: ioremap of the linear cramfs image failed\n");
++              goto out;
++      }
++#endif
++
+       down(&read_mutex);
+       /* Read the first block and get the superblock from it */
+       memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
+@@ -256,8 +402,26 @@
+       /* Set it all up.. */
+       sb->s_op = &cramfs_ops;
+       sb->s_root = d_alloc_root(get_cramfs_inode(sb, &super.root));
++
++#ifdef CONFIG_CRAMFS_LINEAR
++      /* Remap the whole filesystem now */
++      iounmap(sb->CRAMFS_SB_LINEAR_VIRT_ADDR);
++      printk(KERN_INFO "cramfs: linear cramfs image appears to be %lu KB in size\n",
++             sb->CRAMFS_SB_SIZE/1024);
++      sb->CRAMFS_SB_LINEAR_VIRT_ADDR =
++              ioremap(sb->CRAMFS_SB_LINEAR_PHYS_ADDR, sb->CRAMFS_SB_SIZE);
++      if (!sb->CRAMFS_SB_LINEAR_VIRT_ADDR) {
++              printk(KERN_ERR "cramfs: ioremap of the linear cramfs image failed\n");
++              goto out;
++      }
++#endif
++
+       retval = sb;
+ out:
++#ifdef CONFIG_CRAMFS_LINEAR
++      if (!retval && sb->CRAMFS_SB_LINEAR_VIRT_ADDR)
++              iounmap(sb->CRAMFS_SB_LINEAR_VIRT_ADDR);
++#endif
+       return retval;
+ }
+@@ -390,6 +554,18 @@
+       maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       bytes_filled = 0;
++#ifdef CONFIG_CRAMFS_LINEAR_XIP
++      if (page->index < maxblock && CRAMFS_INODE_IS_XIP(inode)) {
++              struct super_block *sb = inode->i_sb;
++              u32 blkptr_offset = PAGE_ALIGN(OFFSET(inode)) +
++                                  page->index * PAGE_CACHE_SIZE;
++              memcpy( page_address(page),
++                      (void*)(sb->CRAMFS_SB_LINEAR_VIRT_ADDR + blkptr_offset),
++                      PAGE_CACHE_SIZE );
++              bytes_filled = PAGE_CACHE_SIZE;
++              pgdata = kmap(page);
++      } else
++#endif
+       if (page->index < maxblock) {
+               struct super_block *sb = inode->i_sb;
+               u32 blkptr_offset = OFFSET(inode) + page->index*4;
+@@ -446,7 +622,11 @@
+       statfs:         cramfs_statfs,
+ };
++#ifndef CONFIG_CRAMFS_LINEAR
+ static DECLARE_FSTYPE_DEV(cramfs_fs_type, "cramfs", cramfs_read_super);
++#else
++static DECLARE_FSTYPE(cramfs_fs_type, "cramfs", cramfs_read_super, 0);
++#endif
+ static int __init init_cramfs_fs(void)
+ {
+--- /dev/null
++++ linux-2.4.27/fs/cramfs/mkcramfs.c
+@@ -0,0 +1,821 @@
++/*
++ * mkcramfs - make a cramfs file system, optionally with XIP files.
++ *
++ * Copyright (C) 1999-2001 Transmeta Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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 <sys/types.h>
++#include <stdio.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <sys/mman.h>
++#include <sys/fcntl.h>
++#include <dirent.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <string.h>
++#include <assert.h>
++#include <getopt.h>
++#include <linux/cramfs_fs.h>
++#include <zlib.h>
++
++#define PAD_SIZE 512          /* only 0 and 512 supported by kernel */
++
++static const char *progname = "mkcramfs";
++
++/* N.B. If you change the disk format of cramfs, please update fs/cramfs/README. */
++
++/* Input status of 0 to print help and exit without an error. */
++static void usage(int status)
++{
++      FILE *stream = status ? stderr : stdout;
++
++      fprintf(stream, "usage: %s [-h] [-e edition] [-i file] [-n name] dirname outfile\n"
++              " -h         print this help\n"
++              " -E         make all warnings errors (non-zero exit status)\n"
++              " -e edition set edition number (part of fsid)\n"
++              " -i file    insert a file image into the filesystem (requires >= 2.4.0)\n"
++              " -n name    set name of cramfs filesystem\n"
++              " -p         pad by %d bytes for boot code\n"
++              " -s         sort directory entries (old option, ignored)\n"
++              " -x         make marked files eXecute In Place\n"
++              " -z         make explicit holes (requires >= 2.3.39)\n"
++              " dirname    root of the filesystem to be compressed\n"
++              " outfile    output file\n", progname, PAD_SIZE);
++
++      exit(status);
++}
++
++#define PAGE_SIZE (4096)
++#define PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
++#define ROM_OFFSET 0
++#define ROM_ALIGN(x) (PAGE_ALIGN((x) + ROM_OFFSET) - ROM_OFFSET)
++#define PAGE_CACHE_SIZE (4096)
++/* The kernel assumes PAGE_CACHE_SIZE as block size. */
++static unsigned int blksize = PAGE_CACHE_SIZE;
++static long total_blocks = 0, total_nodes = 1; /* pre-count the root node */
++static int image_length = 0;
++
++/*
++ * If opt_holes is set, then mkcramfs can create explicit holes in the
++ * data, which saves 26 bytes per hole (which is a lot smaller a
++ * saving than most most filesystems).
++ *
++ * Note that kernels up to at least 2.3.39 don't support cramfs holes,
++ * which is why this is turned off by default.
++ */
++static int opt_edition = 0;
++static int opt_errors = 0;
++static int opt_holes = 0;
++static int opt_xip = 0;
++static int opt_pad = 0;
++static char *opt_image = NULL;
++static char *opt_name = NULL;
++
++static int warn_dev, warn_gid, warn_namelen, warn_skip, warn_size, warn_uid;
++
++#ifndef MIN
++# define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
++#endif
++
++/* In-core version of inode / directory entry. */
++struct entry {
++      /* stats */
++      char *name;
++      unsigned int mode, size, uid, gid;
++
++      /* FS data */
++      void *uncompressed;
++        /* points to other identical file */
++        struct entry *same;
++        unsigned int offset;            /* pointer to compressed data in archive */
++      unsigned int dir_offset;        /* Where in the archive is the directory entry? */
++
++      /* organization */
++      struct entry *child; /* null for non-directories and empty directories */
++      struct entry *next;
++};
++
++/*
++ * The longest file name component to allow for in the input directory tree.
++ * Ext2fs (and many others) allow up to 255 bytes.  A couple of filesystems
++ * allow longer (e.g. smbfs 1024), but there isn't much use in supporting
++ * >255-byte names in the input directory tree given that such names get
++ * truncated to 255 bytes when written to cramfs.
++ */
++#define MAX_INPUT_NAMELEN 255
++
++static int find_identical_file(struct entry *orig,struct entry *newfile)
++{
++        if(orig==newfile) return 1;
++        if(!orig) return 0;
++        if(orig->size==newfile->size && orig->uncompressed && !memcmp(orig->uncompressed,newfile->uncompressed,orig->size)) {
++                newfile->same=orig;
++                return 1;
++        }
++        return find_identical_file(orig->child,newfile) ||
++                   find_identical_file(orig->next,newfile);
++}
++
++static void eliminate_doubles(struct entry *root,struct entry *orig) {
++        if(orig) {
++                if(orig->size && orig->uncompressed)
++                      find_identical_file(root,orig);
++                eliminate_doubles(root,orig->child);
++                eliminate_doubles(root,orig->next);
++        }
++}
++
++/*
++ * We define our own sorting function instead of using alphasort which
++ * uses strcoll and changes ordering based on locale information.
++ */
++static int cramsort (const void *a, const void *b)
++{
++      return strcmp ((*(const struct dirent **) a)->d_name,
++                     (*(const struct dirent **) b)->d_name);
++}
++
++static unsigned int parse_directory(struct entry *root_entry, const char *name, struct entry **prev, loff_t *fslen_ub)
++{
++      struct dirent **dirlist;
++      int totalsize = 0, dircount, dirindex;
++      char *path, *endpath;
++      size_t len = strlen(name);
++
++      /* Set up the path. */
++      /* TODO: Reuse the parent's buffer to save memcpy'ing and duplication. */
++      path = malloc(len + 1 + MAX_INPUT_NAMELEN + 1);
++      if (!path) {
++              perror(NULL);
++              exit(8);
++      }
++      memcpy(path, name, len);
++      endpath = path + len;
++      *endpath = '/';
++      endpath++;
++
++        /* read in the directory and sort */
++        dircount = scandir(name, &dirlist, 0, cramsort);
++
++      if (dircount < 0) {
++              perror(name);
++              exit(8);
++      }
++
++      /* process directory */
++      for (dirindex = 0; dirindex < dircount; dirindex++) {
++              struct dirent *dirent;
++              struct entry *entry;
++              struct stat st;
++              int size;
++              size_t namelen;
++
++              dirent = dirlist[dirindex];
++
++              /* Ignore "." and ".." - we won't be adding them to the archive */
++              if (dirent->d_name[0] == '.') {
++                      if (dirent->d_name[1] == '\0')
++                              continue;
++                      if (dirent->d_name[1] == '.') {
++                              if (dirent->d_name[2] == '\0')
++                                      continue;
++                      }
++              }
++              namelen = strlen(dirent->d_name);
++              if (namelen > MAX_INPUT_NAMELEN) {
++                      fprintf(stderr,
++                              "Very long (%u bytes) filename `%s' found.\n"
++                              " Please increase MAX_INPUT_NAMELEN in mkcramfs.c and recompile.  Exiting.\n",
++                              namelen, dirent->d_name);
++                      exit(8);
++              }
++              memcpy(endpath, dirent->d_name, namelen + 1);
++
++              if (lstat(path, &st) < 0) {
++                      perror(endpath);
++                      warn_skip = 1;
++                      continue;
++              }
++              entry = calloc(1, sizeof(struct entry));
++              if (!entry) {
++                      perror(NULL);
++                      exit(8);
++              }
++              entry->name = strdup(dirent->d_name);
++              if (!entry->name) {
++                      perror(NULL);
++                      exit(8);
++              }
++              if (namelen > 255) {
++                      /* Can't happen when reading from ext2fs. */
++
++                      /* TODO: we ought to avoid chopping in half
++                         multi-byte UTF8 characters. */
++                      entry->name[namelen = 255] = '\0';
++                      warn_namelen = 1;
++              }
++              entry->mode = st.st_mode;
++              entry->size = st.st_size;
++              entry->uid = st.st_uid;
++              if (entry->uid >= 1 << CRAMFS_UID_WIDTH)
++                      warn_uid = 1;
++              entry->gid = st.st_gid;
++              if (entry->gid >= 1 << CRAMFS_GID_WIDTH)
++                      /* TODO: We ought to replace with a default
++                           gid instead of truncating; otherwise there
++                           are security problems.  Maybe mode should
++                           be &= ~070.  Same goes for uid once Linux
++                           supports >16-bit uids. */
++                      warn_gid = 1;
++              size = sizeof(struct cramfs_inode) + ((namelen + 3) & ~3);
++              *fslen_ub += size;
++              if (S_ISDIR(st.st_mode)) {
++                      entry->size = parse_directory(root_entry, path, &entry->child, fslen_ub);
++              } else if (S_ISREG(st.st_mode)) {
++                      /* TODO: We ought to open files in do_compress, one
++                         at a time, instead of amassing all these memory
++                         maps during parse_directory (which don't get used
++                         until do_compress anyway).  As it is, we tend to
++                         get EMFILE errors (especially if mkcramfs is run
++                         by non-root).
++
++                         While we're at it, do analagously for symlinks
++                         (which would just save a little memory). */
++                      int fd = open(path, O_RDONLY);
++                      if (fd < 0) {
++                              perror(path);
++                              warn_skip = 1;
++                              continue;
++                      }
++                      if (entry->size) {
++                              if ((entry->size >= 1 << CRAMFS_SIZE_WIDTH)) {
++                                      warn_size = 1;
++                                      entry->size = (1 << CRAMFS_SIZE_WIDTH) - 1;
++                              }
++
++                              entry->uncompressed = mmap(NULL, entry->size, PROT_READ, MAP_PRIVATE, fd, 0);
++                              if (-1 == (int) (long) entry->uncompressed) {
++                                      perror("mmap");
++                                      exit(8);
++                              }
++                      }
++                      close(fd);
++              } else if (S_ISLNK(st.st_mode)) {
++                      entry->uncompressed = malloc(entry->size);
++                      if (!entry->uncompressed) {
++                              perror(NULL);
++                              exit(8);
++                      }
++                      if (readlink(path, entry->uncompressed, entry->size) < 0) {
++                              perror(path);
++                              warn_skip = 1;
++                              continue;
++                      }
++              } else if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
++                      /* maybe we should skip sockets */
++                      entry->size = 0;
++              } else {
++                      entry->size = st.st_rdev;
++                      if (entry->size & -(1<<CRAMFS_SIZE_WIDTH))
++                              warn_dev = 1;
++              }
++
++              if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
++                      int blocks = ((entry->size - 1) / blksize + 1);
++
++                      /* block pointers & data expansion allowance + data */
++                      if(entry->size)
++                              *fslen_ub += (4+26)*blocks + entry->size + 3;
++                }
++
++              if (opt_xip && entry->mode & S_ISVTX) {
++                      /* worse case, depending on where the offsets falls,
++                       * a single XIP entry could expand the sizeof the
++                       * file system by 8k, since we're aligning the start
++                       * and end on page boundary.
++                       */
++                      *fslen_ub += 2*PAGE_CACHE_SIZE;
++              }
++
++              /* Link it into the list */
++              *prev = entry;
++              prev = &entry->next;
++              totalsize += size;
++      }
++      free(path);
++      free(dirlist);          /* allocated by scandir() with malloc() */
++      return totalsize;
++}
++
++/* Returns sizeof(struct cramfs_super), which includes the root inode. */
++static unsigned int write_superblock(struct entry *root, char *base, int size)
++{
++      struct cramfs_super *super = (struct cramfs_super *) base;
++      unsigned int offset = sizeof(struct cramfs_super) + image_length;
++
++      if (opt_pad) {
++              offset += opt_pad;
++      }
++
++      super->magic = CRAMFS_MAGIC;
++      super->flags = CRAMFS_FLAG_FSID_VERSION_2 | CRAMFS_FLAG_SORTED_DIRS;
++      if (opt_holes)
++              super->flags |= CRAMFS_FLAG_HOLES;
++      if (image_length > 0)
++              super->flags |= CRAMFS_FLAG_SHIFTED_ROOT_OFFSET;
++      super->size = size;
++      memcpy(super->signature, CRAMFS_SIGNATURE, sizeof(super->signature));
++
++      super->fsid.crc = crc32(0L, Z_NULL, 0);
++      super->fsid.edition = opt_edition;
++      super->fsid.blocks = total_blocks;
++      super->fsid.files = total_nodes;
++
++      memset(super->name, 0x00, sizeof(super->name));
++      if (opt_name)
++              strncpy(super->name, opt_name, sizeof(super->name));
++      else
++              strncpy(super->name, "Compressed", sizeof(super->name));
++
++      super->root.mode = root->mode;
++      super->root.uid = root->uid;
++      super->root.gid = root->gid;
++      super->root.size = root->size;
++      super->root.offset = offset >> 2;
++
++      return offset;
++}
++
++static void set_data_offset(struct entry *entry, char *base, unsigned long offset)
++{
++      struct cramfs_inode *inode = (struct cramfs_inode *) (base + entry->dir_offset);
++#ifdef DEBUG
++      assert ((offset & 3) == 0);
++#endif /* DEBUG */
++      if (offset >= (1 << (2 + CRAMFS_OFFSET_WIDTH))) {
++              fprintf(stderr, "filesystem too big.  Exiting.\n");
++              exit(8);
++      }
++      inode->offset = (offset >> 2);
++}
++
++
++/*
++ * We do a width-first printout of the directory
++ * entries, using a stack to remember the directories
++ * we've seen.
++ */
++#define MAXENTRIES (100)
++static unsigned int write_directory_structure(struct entry *entry, char *base, unsigned int offset)
++{
++      int stack_entries = 0;
++      struct entry *entry_stack[MAXENTRIES];
++
++      for (;;) {
++              int dir_start = stack_entries;
++              while (entry) {
++                      struct cramfs_inode *inode = (struct cramfs_inode *) (base + offset);
++                      size_t len = strlen(entry->name);
++
++                      entry->dir_offset = offset;
++
++                      inode->mode = entry->mode;
++                      inode->uid = entry->uid;
++                      inode->gid = entry->gid;
++                      inode->size = entry->size;
++                      inode->offset = 0;
++                      /* Non-empty directories, regfiles and symlinks will
++                         write over inode->offset later. */
++
++                      offset += sizeof(struct cramfs_inode);
++                      total_nodes++;  /* another node */
++                      memcpy(base + offset, entry->name, len);
++                      /* Pad up the name to a 4-byte boundary */
++                      while (len & 3) {
++                              *(base + offset + len) = '\0';
++                              len++;
++                      }
++                      inode->namelen = len >> 2;
++                      offset += len;
++
++                      /* TODO: this may get it wrong for chars >= 0x80.
++                         Most filesystems use UTF8 encoding for filenames,
++                         whereas the console is a single-byte character
++                         set like iso-latin-1. */
++                      printf("  %s\n", entry->name);
++                      if (entry->child) {
++                              if (stack_entries >= MAXENTRIES) {
++                                      fprintf(stderr, "Exceeded MAXENTRIES.  Raise this value in mkcramfs.c and recompile.  Exiting.\n");
++                                      exit(8);
++                              }
++                              entry_stack[stack_entries] = entry;
++                              stack_entries++;
++                      }
++                      entry = entry->next;
++              }
++
++              /*
++               * Reverse the order the stack entries pushed during
++                 * this directory, for a small optimization of disk
++                 * access in the created fs.  This change makes things
++                 * `ls -UR' order.
++               */
++              {
++                      struct entry **lo = entry_stack + dir_start;
++                      struct entry **hi = entry_stack + stack_entries;
++                      struct entry *tmp;
++
++                      while (lo < --hi) {
++                              tmp = *lo;
++                              *lo++ = *hi;
++                              *hi = tmp;
++                      }
++              }
++
++              /* Pop a subdirectory entry from the stack, and recurse. */
++              if (!stack_entries)
++                      break;
++              stack_entries--;
++              entry = entry_stack[stack_entries];
++
++              set_data_offset(entry, base, offset);
++              printf("'%s':\n", entry->name);
++              entry = entry->child;
++      }
++      return offset;
++}
++
++static int is_zero(char const *begin, unsigned len)
++{
++      if (opt_holes)
++              /* Returns non-zero iff the first LEN bytes from BEGIN are
++                 all NULs. */
++              return (len-- == 0 ||
++                      (begin[0] == '\0' &&
++                       (len-- == 0 ||
++                        (begin[1] == '\0' &&
++                         (len-- == 0 ||
++                          (begin[2] == '\0' &&
++                           (len-- == 0 ||
++                            (begin[3] == '\0' &&
++                             memcmp(begin, begin + 4, len) == 0))))))));
++      else
++              /* Never create holes. */
++              return 0;
++}
++
++static unsigned int do_xip(char *base, unsigned int offset,
++                         char const *name, char *uncompressed,
++                         unsigned int size)
++{
++      unsigned int start, end;
++
++      /* align to page boundary */
++
++      start = ROM_ALIGN(offset);
++      memset(base + offset, 0, start - offset);
++
++      memcpy(base + start, uncompressed, size);
++
++      /* pad to page boundary */
++
++      end = ROM_ALIGN(start + size);
++      memset(base + start + size, 0, end - (start + size));
++
++      printf("XIP (%u+%u bytes)\toffset %u\t%s\n",
++             size, (end - offset) - size, offset, name);
++
++      return end;
++}
++
++/*
++ * One 4-byte pointer per block and then the actual blocked
++ * output. The first block does not need an offset pointer,
++ * as it will start immediately after the pointer block;
++ * so the i'th pointer points to the end of the i'th block
++ * (i.e. the start of the (i+1)'th block or past EOF).
++ *
++ * Note that size > 0, as a zero-sized file wouldn't ever
++ * have gotten here in the first place.
++ */
++static unsigned int do_compress(char *base, unsigned int offset, char const *name, char *uncompressed, unsigned int size)
++{
++      unsigned long original_size = size;
++      unsigned long original_offset = offset;
++      unsigned long new_size;
++      unsigned long blocks = (size - 1) / blksize + 1;
++      unsigned long curr = offset + 4 * blocks;
++      int change;
++
++      total_blocks += blocks;
++
++      do {
++              unsigned long len = 2 * blksize;
++              unsigned int input = size;
++              if (input > blksize)
++                      input = blksize;
++              size -= input;
++              if (!is_zero (uncompressed, input)) {
++                      compress(base + curr, &len, uncompressed, input);
++                      curr += len;
++              }
++              uncompressed += input;
++
++              if (len > blksize*2) {
++                      /* (I don't think this can happen with zlib.) */
++                      printf("AIEEE: block \"compressed\" to > 2*blocklength (%ld)\n", len);
++                      exit(8);
++              }
++
++              *(u32 *) (base + offset) = curr;
++              offset += 4;
++      } while (size);
++
++      curr = (curr + 3) & ~3;
++      new_size = curr - original_offset;
++      /* TODO: Arguably, original_size in these 2 lines should be
++         st_blocks * 512.  But if you say that then perhaps
++         administrative data should also be included in both. */
++      change = new_size - original_size;
++      printf("%6.2f%% (%+d bytes)\toffset %lu\t%s\n",
++             (change * 100) / (double) original_size, change, original_offset, name);
++
++      return curr;
++}
++
++
++/*
++ * Traverse the entry tree, writing data for every item that has
++ * non-null entry->compressed (i.e. every symlink and non-empty
++ * regfile).
++ */
++static unsigned int write_data(struct entry *entry, char *base, unsigned int offset)
++{
++      do {
++              if (entry->uncompressed) {
++                        if(entry->same) {
++                                set_data_offset(entry, base, entry->same->offset);
++                                entry->offset=entry->same->offset;
++                        } else {
++                                set_data_offset(entry, base, offset);
++                                entry->offset=offset;
++                              if (opt_xip && entry->mode & S_ISVTX)
++                                      offset = do_xip(base, offset, entry->name, entry->uncompressed, entry->size);
++                              else
++                                offset = do_compress(base, offset, entry->name, entry->uncompressed, entry->size);
++                        }
++              }
++              else if (entry->child)
++                      offset = write_data(entry->child, base, offset);
++                entry=entry->next;
++      } while (entry);
++      return offset;
++}
++
++static unsigned int write_file(char *file, char *base, unsigned int offset)
++{
++      int fd;
++      char *buf;
++
++      fd = open(file, O_RDONLY);
++      if (fd < 0) {
++              perror(file);
++              exit(8);
++      }
++      buf = mmap(NULL, image_length, PROT_READ, MAP_PRIVATE, fd, 0);
++      memcpy(base + offset, buf, image_length);
++      munmap(buf, image_length);
++      close (fd);
++      /* Pad up the image_length to a 4-byte boundary */
++      while (image_length & 3) {
++              *(base + offset + image_length) = '\0';
++              image_length++;
++      }
++      return (offset + image_length);
++}
++
++/*
++ * Maximum size fs you can create is roughly 256MB.  (The last file's
++ * data must begin within 256MB boundary but can extend beyond that.)
++ *
++ * Note that if you want it to fit in a ROM then you're limited to what the
++ * hardware and kernel can support (64MB?).
++ */
++#define MAXFSLEN ((((1 << CRAMFS_OFFSET_WIDTH) - 1) << 2) /* offset */ \
++                + (1 << CRAMFS_SIZE_WIDTH) - 1 /* filesize */ \
++                + (1 << CRAMFS_SIZE_WIDTH) * 4 / PAGE_CACHE_SIZE /* block pointers */ )
++
++
++/*
++ * Usage:
++ *
++ *      mkcramfs directory-name outfile
++ *
++ * where "directory-name" is simply the root of the directory
++ * tree that we want to generate a compressed filesystem out
++ * of.
++ */
++int main(int argc, char **argv)
++{
++      struct stat st;         /* used twice... */
++      struct entry *root_entry;
++      char *rom_image;
++      ssize_t offset, written;
++      int fd;
++      /* initial guess (upper-bound) of required filesystem size */
++      loff_t fslen_ub = sizeof(struct cramfs_super);
++      char const *dirname, *outfile;
++      u32 crc = crc32(0L, Z_NULL, 0);
++      int c;                  /* for getopt */
++
++      total_blocks = 0;
++
++      if (argc)
++              progname = argv[0];
++
++      /* command line options */
++      while ((c = getopt(argc, argv, "hEe:i:n:psxz")) != EOF) {
++              switch (c) {
++              case 'h':
++                      usage(0);
++              case 'E':
++                      opt_errors = 1;
++                      break;
++              case 'e':
++                      opt_edition = atoi(optarg);
++                      break;
++              case 'i':
++                      opt_image = optarg;
++                      if (lstat(opt_image, &st) < 0) {
++                              perror(opt_image);
++                              exit(16);
++                      }
++                      image_length = st.st_size; /* may be padded later */
++                      fslen_ub += (image_length + 3); /* 3 is for padding */
++                      break;
++              case 'n':
++                      opt_name = optarg;
++                      break;
++              case 'p':
++                      opt_pad = PAD_SIZE;
++                      fslen_ub += PAD_SIZE;
++                      break;
++              case 's':
++                      /* old option, ignored */
++                      break;
++              case 'x':
++                      opt_xip = 1;
++                      break;
++              case 'z':
++                      opt_holes = 1;
++                      break;
++              }
++      }
++
++      if ((argc - optind) != 2)
++              usage(16);
++      dirname = argv[optind];
++      outfile = argv[optind + 1];
++
++      if (stat(dirname, &st) < 0) {
++              perror(dirname);
++              exit(16);
++      }
++      fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
++
++      root_entry = calloc(1, sizeof(struct entry));
++      if (!root_entry) {
++              perror(NULL);
++              exit(8);
++      }
++      root_entry->mode = st.st_mode;
++      root_entry->uid = st.st_uid;
++      root_entry->gid = st.st_gid;
++
++      root_entry->size = parse_directory(root_entry, dirname, &root_entry->child, &fslen_ub);
++
++      /* always allocate a multiple of blksize bytes because that's
++           what we're going to write later on */
++      fslen_ub = ((fslen_ub - 1) | (blksize - 1)) + 1;
++
++      if (fslen_ub > MAXFSLEN) {
++              fprintf(stderr,
++                      "warning: guestimate of required size (upper bound) is %LdMB, but maximum image size is %uMB.  We might die prematurely.\n",
++                      fslen_ub >> 20,
++                      MAXFSLEN >> 20);
++              fslen_ub = MAXFSLEN;
++      }
++
++        /* find duplicate files. TODO: uses the most inefficient algorithm
++           possible. */
++        eliminate_doubles(root_entry,root_entry);
++
++      /* TODO: Why do we use a private/anonymous mapping here
++           followed by a write below, instead of just a shared mapping
++           and a couple of ftruncate calls?  Is it just to save us
++           having to deal with removing the file afterwards?  If we
++           really need this huge anonymous mapping, we ought to mmap
++           in smaller chunks, so that the user doesn't need nn MB of
++           RAM free.  If the reason is to be able to write to
++           un-mmappable block devices, then we could try shared mmap
++           and revert to anonymous mmap if the shared mmap fails. */
++      rom_image = mmap(NULL, fslen_ub?fslen_ub:1, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++
++      if (-1 == (int) (long) rom_image) {
++              perror("ROM image map");
++              exit(8);
++      }
++
++      /* Skip the first opt_pad bytes for boot loader code */
++      offset = opt_pad;
++      memset(rom_image, 0x00, opt_pad);
++
++      /* Skip the superblock and come back to write it later. */
++      offset += sizeof(struct cramfs_super);
++
++      /* Insert a file image. */
++      if (opt_image) {
++              printf("Including: %s\n", opt_image);
++              offset = write_file(opt_image, rom_image, offset);
++      }
++
++      offset = write_directory_structure(root_entry->child, rom_image, offset);
++      printf("Directory data: %d bytes\n", offset);
++
++      offset = write_data(root_entry, rom_image, offset);
++
++      /* We always write a multiple of blksize bytes, so that
++           losetup works. */
++      offset = ((offset - 1) | (blksize - 1)) + 1;
++      printf("Everything: %d kilobytes\n", offset >> 10);
++
++      /* Write the superblock now that we can fill in all of the fields. */
++      write_superblock(root_entry, rom_image+opt_pad, offset);
++      printf("Super block: %d bytes\n", sizeof(struct cramfs_super));
++
++      /* Put the checksum in. */
++      crc = crc32(crc, (rom_image+opt_pad), (offset-opt_pad));
++      ((struct cramfs_super *) (rom_image+opt_pad))->fsid.crc = crc;
++      printf("CRC: %x\n", crc);
++
++      /* Check to make sure we allocated enough space. */
++      if (fslen_ub < offset) {
++              fprintf(stderr, "not enough space allocated for ROM image (%Ld allocated, %d used)\n",
++                      fslen_ub, offset);
++              exit(8);
++      }
++
++      written = write(fd, rom_image, offset);
++      if (written < 0) {
++              perror("ROM image");
++              exit(8);
++      }
++      if (offset != written) {
++              fprintf(stderr, "ROM image write failed (%d %d)\n", written, offset);
++              exit(8);
++      }
++
++      /* (These warnings used to come at the start, but they scroll off the
++           screen too quickly.) */
++      if (warn_namelen) /* (can't happen when reading from ext2fs) */
++              fprintf(stderr, /* bytes, not chars: think UTF8. */
++                      "warning: filenames truncated to 255 bytes.\n");
++      if (warn_skip)
++              fprintf(stderr, "warning: files were skipped due to errors.\n");
++      if (warn_size)
++              fprintf(stderr,
++                      "warning: file sizes truncated to %luMB (minus 1 byte).\n",
++                      1L << (CRAMFS_SIZE_WIDTH - 20));
++      if (warn_uid) /* (not possible with current Linux versions) */
++              fprintf(stderr,
++                      "warning: uids truncated to %u bits.  (This may be a security concern.)\n",
++                      CRAMFS_UID_WIDTH);
++      if (warn_gid)
++              fprintf(stderr,
++                      "warning: gids truncated to %u bits.  (This may be a security concern.)\n",
++                      CRAMFS_GID_WIDTH);
++      if (warn_dev)
++              fprintf(stderr,
++                      "WARNING: device numbers truncated to %u bits.  This almost certainly means\n"
++                      "that some device files will be wrong.\n",
++                      CRAMFS_OFFSET_WIDTH);
++      if (opt_errors &&
++          (warn_namelen||warn_skip||warn_size||warn_uid||warn_gid||warn_dev))
++              exit(8);
++      return 0;
++}
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/bitfield.h
+@@ -0,0 +1,113 @@
++/*
++ *    FILE            bitfield.h
++ *
++ *    Version         1.1
++ *    Author          Copyright (c) Marc A. Viredaz, 1998
++ *                    DEC Western Research Laboratory, Palo Alto, CA
++ *    Date            April 1998 (April 1997)
++ *    System          Advanced RISC Machine (ARM)
++ *    Language        C or ARM Assembly
++ *    Purpose         Definition of macros to operate on bit fields.
++ */
++
++
++
++#ifndef __BITFIELD_H
++#define __BITFIELD_H
++
++#ifndef __ASSEMBLY__
++#define UData(Data)   ((unsigned long) (Data))
++#else
++#define UData(Data)   (Data)
++#endif
++
++
++/*
++ * MACRO: Fld
++ *
++ * Purpose
++ *    The macro "Fld" encodes a bit field, given its size and its shift value
++ *    with respect to bit 0.
++ *
++ * Note
++ *    A more intuitive way to encode bit fields would have been to use their
++ *    mask. However, extracting size and shift value information from a bit
++ *    field's mask is cumbersome and might break the assembler (255-character
++ *    line-size limit).
++ *
++ * Input
++ *    Size            Size of the bit field, in number of bits.
++ *    Shft            Shift value of the bit field with respect to bit 0.
++ *
++ * Output
++ *    Fld             Encoded bit field.
++ */
++
++#define Fld(Size, Shft)       (((Size) << 16) + (Shft))
++
++
++/*
++ * MACROS: FSize, FShft, FMsk, FAlnMsk, F1stBit
++ *
++ * Purpose
++ *    The macros "FSize", "FShft", "FMsk", "FAlnMsk", and "F1stBit" return
++ *    the size, shift value, mask, aligned mask, and first bit of a
++ *    bit field.
++ *
++ * Input
++ *    Field           Encoded bit field (using the macro "Fld").
++ *
++ * Output
++ *    FSize           Size of the bit field, in number of bits.
++ *    FShft           Shift value of the bit field with respect to bit 0.
++ *    FMsk            Mask for the bit field.
++ *    FAlnMsk         Mask for the bit field, aligned on bit 0.
++ *    F1stBit         First bit of the bit field.
++ */
++
++#define FSize(Field)  ((Field) >> 16)
++#define FShft(Field)  ((Field) & 0x0000FFFF)
++#define FMsk(Field)   (((UData (1) << FSize (Field)) - 1) << FShft (Field))
++#define FAlnMsk(Field)        ((UData (1) << FSize (Field)) - 1)
++#define F1stBit(Field)        (UData (1) << FShft (Field))
++
++
++/*
++ * MACRO: FInsrt
++ *
++ * Purpose
++ *    The macro "FInsrt" inserts a value into a bit field by shifting the
++ *    former appropriately.
++ *
++ * Input
++ *    Value           Bit-field value.
++ *    Field           Encoded bit field (using the macro "Fld").
++ *
++ * Output
++ *    FInsrt          Bit-field value positioned appropriately.
++ */
++
++#define FInsrt(Value, Field) \
++                      (UData (Value) << FShft (Field))
++
++
++/*
++ * MACRO: FExtr
++ *
++ * Purpose
++ *    The macro "FExtr" extracts the value of a bit field by masking and
++ *    shifting it appropriately.
++ *
++ * Input
++ *    Data            Data containing the bit-field to be extracted.
++ *    Field           Encoded bit field (using the macro "Fld").
++ *
++ * Output
++ *    FExtr           Bit-field value.
++ */
++
++#define FExtr(Data, Field) \
++                      ((UData (Data) >> FShft (Field)) & FAlnMsk (Field))
++
++
++#endif /* __BITFIELD_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/cerf.h
+@@ -0,0 +1,177 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/cerf.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.
++ */
++
++/*
++ * Add CerfBoard Specifics here...
++ */
++
++/*
++ * Memory sizes
++ */
++
++#define CERF_RAM_BASE                 0xa0000000
++
++#ifdef CONFIG_PXA_CERF_RAM_128MB
++#define CERF_RAM_SIZE                 128*1024*1024
++
++#elif defined (CONFIG_PXA_CERF_RAM_64MB)
++#define CERF_RAM_SIZE                 64*1024*1024
++
++#elif defined (CONFIG_PXA_CERF_RAM_32MB)
++#define CERF_RAM_SIZE                 32*1024*1024
++
++#elif defined (CONFIG_PXA_CERF_RAM_16MB)
++#define CERF_RAM_SIZE                 16*1024*1024
++#endif
++
++/*
++ * CS memory timing via Static Memory Control Register (MSC0-2)
++ */
++
++#define MSC_CS(cs,val) ((val)<<((cs&1)<<4))
++
++#define MSC_RBUFF_SHIFT 15 
++#define MSC_RBUFF_SLOW (0)
++#define MSC_RBUFF_FAST (1)
++#define MSC_RBUFF(x) ((x)<<MSC_RBUFF_SHIFT)
++
++#define MSC_RRR_SHIFT 12
++#define MSC_RRR(x) ((x)<<MSC_RRR_SHIFT)
++
++#define MSC_RDN_SHIFT 8
++#define MSC_RDN(x) ((x)<<MSC_RDN_SHIFT)
++
++#define MSC_RDF_SHIFT 4
++#define MSC_RDF(x) ((x)<<MSC_RDF_SHIFT)
++
++#define MSC_RBW_SHIFT 3
++#define MSC_RBW(x) ((x)<<MSC_RBW_SHIFT)
++
++#define MSC_RT_SHIFT  0
++#define MSC_RT(x) ((x)<<MSC_RT_SHIFT)
++
++/*
++ * IO Pins for devices
++ */
++
++#define CERF_FLASH_BASE                       0xe8000000
++#define CERF_FLASH_SIZE                       0x02000000
++#define CERF_FLASH_PHYS                       PXA_CS0_PHYS
++
++#define CERF_ETH_BASE                 0xf0000000
++#define CERF_ETH_SIZE                 0x00100000
++#define CERF_ETH_PHYS                 PXA_CS1_PHYS
++
++#define CERF_BT_BASE                  0xf2000000
++#define CERF_BT_SIZE                  0x00100000
++#define CERF_BT_PHYS                  PXA_CS2_PHYS
++
++#define CERF_SERIAL_BASE              0xf3000000
++#define CERF_SERIAL_SIZE              0x00100000
++#define CERF_SERIAL_PHYS              PXA_CS3_PHYS
++
++#define CERF_CPLD_BASE                        0xf1000000
++#define CERF_CPLD_SIZE                        0x00100000
++#define CERF_CPLD_PHYS                        PXA_CS4_PHYS
++
++#define CERF_PDA_CPLD_WRCLRINT                (0x0)
++#define CERF_PDA_CPLD_BRIGHTNESS      (0x2)
++#define CERF_PDA_CPLD_KEYPAD_A                (0x6)
++#define CERF_PDA_CPLD_BATTFAULT               (0x8)
++#define CERF_PDA_CPLD_KEYPAD_B                (0xa)
++#define CERF_PDA_CPLD_SOUND_ENA               (0xc)
++
++#define CERF_PDA_SOUND_ENABLE         0x1
++#define CERF_PDA_DEFAULT_BRIGHTNESS   0x9
++
++/*
++ * Access functions (registers are 4-bit wide)
++ */
++
++#define CERF_PDA_CPLD CERF_CPLD_BASE
++
++#define CERF_PDA_CPLD_Get(x, y)      (*((char*)(CERF_PDA_CPLD + (x))) & (y))
++#define CERF_PDA_CPLD_Set(x, y, z)   (*((char*)(CERF_PDA_CPLD + (x))) = (*((char*)(CERF_PDA_CPLD + (x))) & ~(z)) | (y))
++#define CERF_PDA_CPLD_UnSet(x, y, z) (*((char*)(CERF_PDA_CPLD + (x))) = (*((char*)(CERF_PDA_CPLD + (x))) & ~(z)) & ~(y))
++
++/* 
++ * IO and IRQ settings for cs8900 ethernet chip
++ */
++#define CERF_ETH_IO           CERF_ETH_BASE
++#define CERF_ETH_IRQ          GPIO_2_80_TO_IRQ(21)
++
++/*
++ * We only have one LED on the XScale CerfPDA so only the
++ * time or idle should ever be selected.
++ */
++#define CERF_HEARTBEAT_LED 0x1
++#define CERF_SYS_BUSY_LED  0x2
++
++#define CERF_HEARTBEAT_LED_GPIO       16 // GPIO 4
++#define CERF_SYS_BUSY_LED_GPIO        16 // GPIO 4
++
++#define CERF_HEARTBEAT_LED_ON  (GPSR0 = CERF_HEARTBEAT_LED_GPIO)
++#define CERF_HEARTBEAT_LED_OFF (GPCR0 = CERF_HEARTBEAT_LED_GPIO)
++#define CERF_SYS_BUSY_LED_ON  (GPSR0 = CERF_SYS_BUSY_LED_GPIO)
++#define CERF_SYS_BUSY_LED_OFF (GPCR0 = CERF_SYS_BUSY_LED_GPIO)
++
++/*
++ * UCB 1400 gpio
++ */
++
++#define CERF_GPIO_UCB1400_IRQ 32
++
++#define UCB_IO_0                (1 << 0)
++#define UCB_IO_1                (1 << 1)
++#define UCB_IO_2                (1 << 2)
++#define UCB_IO_3                (1 << 3)
++#define UCB_IO_4                (1 << 4)
++#define UCB_IO_5                (1 << 5)
++#define UCB_IO_6                (1 << 6)
++#define UCB_IO_7                (1 << 7)
++#define UCB_IO_8                (1 << 8)
++#define UCB_IO_9                (1 << 9)
++
++#define UCB1400_GPIO_CONT_CS      UCB_IO_0
++#define UCB1400_GPIO_CONT_DOWN    UCB_IO_1
++#define UCB1400_GPIO_CONT_INC     UCB_IO_2
++#define UCB1400_GPIO_CONT_ENA     UCB_IO_3
++#define UCB1400_GPIO_LCD_RESET    UCB_IO_4
++#define UCB1400_GPIO_IRDA_ENABLE  UCB_IO_5
++#define UCB1400_GPIO_BT_ENABLE    UCB_IO_6
++#define UCB1400_GPIO_TEST_P1      UCB_IO_7
++#define UCB1400_GPIO_TEST_P2      UCB_IO_8
++#define UCB1400_GPIO_TEST_P3      UCB_IO_9
++
++/*
++ * IRQ for devices
++ */
++#define UCB1400_IRQ(x)          (NR_IRQS + 1 + (x))
++
++#define IRQ_UCB1400_IO0         UCB1400_IRQ(0)
++#define IRQ_UCB1400_IO1         UCB1400_IRQ(1)
++#define IRQ_UCB1400_IO2         UCB1400_IRQ(2)
++#define IRQ_UCB1400_IO3         UCB1400_IRQ(3)
++#define IRQ_UCB1400_IO4         UCB1400_IRQ(4)
++#define IRQ_UCB1400_IO5         UCB1400_IRQ(5)
++#define IRQ_UCB1400_IO6         UCB1400_IRQ(6)
++#define IRQ_UCB1400_IO7         UCB1400_IRQ(7)
++#define IRQ_UCB1400_IO8         UCB1400_IRQ(8)
++#define IRQ_UCB1400_IO9         UCB1400_IRQ(9)
++
++#define IRQ_UCB1400_CONT_CS     IRQ_UCB1400_IO0
++#define IRQ_UCB1400_CONT_DOWN   IRQ_UCB1400_IO1
++#define IRQ_UCB1400_CONT_INC    IRQ_UCB1400_IO2
++#define IRQ_UCB1400_CONT_ENA    IRQ_UCB1400_IO3
++#define IRQ_UCB1400_LCD_RESET   IRQ_UCB1400_IO4
++#define IRQ_UCB1400_IRDA_ENABLE IRQ_UCB1400_IO5
++#define IRQ_UCB1400_BT_ENABLE   IRQ_UCB1400_IO6
++#define IRQ_UCB1400_TEST_P1     IRQ_UCB1400_IO7
++#define IRQ_UCB1400_TEST_P2     IRQ_UCB1400_IO8
++#define IRQ_UCB1400_TEST_P3     IRQ_UCB1400_IO9
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/cerf_ucb1400gpio.h
+@@ -0,0 +1,30 @@
++/*
++ *  cerf_ucb1400gpio.h
++ *
++ *  UCB1400 GPIO control stuff for the cerf.
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Mar 2002: Initial version [FB]
++ * 
++ */
++/* -- lcd -- */
++extern void cerf_ucb1400gpio_lcd_enable( void);
++extern void cerf_ucb1400gpio_lcd_disable( void);
++extern void cerf_ucb1400gpio_lcd_contrast_step( int direction);
++
++/* -- irda -- */
++extern void cerf_ucb1400gpio_irda_enable( void);
++extern void cerf_ucb1400gpio_irda_disable( void);
++
++/* -- bt -- */
++extern void cerf_ucb1400gpio_bt_enable( void);
++extern void cerf_ucb1400gpio_bt_disable( void);
++
++/* -- init -- */
++extern int cerf_ucb1400gpio_init(void);
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/csb226.h
+@@ -0,0 +1,99 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/csb226.h
++ *
++ *  Author:   Robert Schwebel (stolen from lubbock.h)
++ *  Created:  Oct 30, 2002
++ *  Copyright:        Pengutronix
++ *  
++ * 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.
++ */
++
++#define CSB226_FPGA_PHYS      PXA_CS2_PHYS    
++
++#define CSB226_FPGA_VIRT      (0xf0000000)    /* phys 0x08000000 */
++#define CSB226_ETH_BASE               (0xf1000000)    /* phys 0x0c000000 */
++
++#define CSB226_P2V(x)         ((x) - CSB226_FPGA_PHYS + CSB226_FPGA_VIRT)
++#define CSB226_V2P(x)         ((x) - CSB226_FPGA_VIRT + CSB226_FPGA_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __CSB226_REG(x)     (*((volatile unsigned long *)CSB226_P2V(x)))
++#else
++#  define __CSB226_REG(x)     CSB226_P2V(x)
++#endif
++
++
++/* register physical addresses */
++#define _CSB226_MISC_WR               (CSB226_FPGA_PHYS + 0x080)
++#define _CSB226_MISC_RD               (CSB226_FPGA_PHYS + 0x090)
++#define _CSB226_IRQ_MASK_EN   (CSB226_FPGA_PHYS + 0x0C0)
++#define _CSB226_IRQ_SET_CLR   (CSB226_FPGA_PHYS + 0x0D0)
++#define _CSB226_GP            (CSB226_FPGA_PHYS + 0x100)
++
++
++
++/* register virtual addresses */
++
++#define CSB226_MISC_WR             __CSB226_REG(_CSB226_MISC_WR) 
++#define CSB226_MISC_RD             __CSB226_REG(_CSB226_MISC_RD)         
++#define CSB226_IRQ_MASK_EN         __CSB226_REG(_CSB226_IRQ_MASK_EN)
++#define CSB226_IRQ_SET_CLR         __CSB226_REG(_CSB226_IRQ_SET_CLR)             
++#define CSB226_GP                  __CSB226_REG(_CSB226_GP)      
++
++
++/* GPIOs */
++
++#define GPIO_CSB226_IRQ               0
++#define IRQ_GPIO_CSB226_IRQ   IRQ_GPIO0
++
++
++/*
++ * LED macros
++ */
++
++// #define LEDS_BASE LUB_DISC_BLNK_LED
++
++// 8 discrete leds available for general use:
++
++/*
++#define D28   0x1
++#define D27   0x2
++#define D26   0x4
++#define D25   0x8
++#define D24   0x10
++#define D23   0x20
++#define D22   0x40
++#define D21   0x80
++*/
++
++/* Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays so
++*  be sure to not monkey with them here.
++*/
++
++/*
++#define HEARTBEAT_LED D28
++#define SYS_BUSY_LED    D27
++#define HEXLEDS_BASE LUB_HEXLED
++
++#define HEARTBEAT_LED_ON  (LEDS_BASE &= ~HEARTBEAT_LED)
++#define HEARTBEAT_LED_OFF (LEDS_BASE |= HEARTBEAT_LED)
++#define SYS_BUSY_LED_OFF  (LEDS_BASE |= SYS_BUSY_LED)
++#define SYS_BUSY_LED_ON   (LEDS_BASE &= ~SYS_BUSY_LED)
++
++// use x = D26-D21 for these, please...
++#define DISCRETE_LED_ON(x) (LEDS_BASE &= ~(x))
++#define DISCRETE_LED_OFF(x) (LEDS_BASE |= (x))
++*/
++
++#ifndef __ASSEMBLY__
++
++//extern int hexled_val = 0;
++
++#endif
++
++/*
++#define BUMP_COUNTER (HEXLEDS_BASE = hexled_val++)
++#define DEC_COUNTER (HEXLEDS_BASE = hexled_val--)
++*/
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/dma.h
+@@ -0,0 +1,49 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/dma.h
++ *  
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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.
++ */
++#ifndef __ASM_ARCH_DMA_H
++#define __ASM_ARCH_DMA_H
++
++#define MAX_DMA_ADDRESS               0xffffffff
++
++/* No DMA as the rest of the world see it */
++#define MAX_DMA_CHANNELS      0
++
++/*
++ * Descriptor structure for PXA's DMA engine
++ * Note: this structure must always be aligned to a 16-byte boundary.
++ */
++
++typedef struct {
++      volatile u32 ddadr;     /* Points to the next descriptor + flags */
++      volatile u32 dsadr;     /* DSADR value for the current transfer */
++      volatile u32 dtadr;     /* DTADR value for the current transfer */
++      volatile u32 dcmd;      /* DCMD value for the current transfer */
++} pxa_dma_desc;
++
++/*
++ * DMA registration
++ */
++
++typedef enum {
++      DMA_PRIO_HIGH = 0,
++      DMA_PRIO_MEDIUM = 4,
++      DMA_PRIO_LOW = 8
++} pxa_dma_prio;
++
++int pxa_request_dma (char *name,
++                       pxa_dma_prio prio,
++                       void (*irq_handler)(int, void *, struct pt_regs *),
++                       void *data);
++
++void pxa_free_dma (int dma_ch);
++
++#endif /* _ASM_ARCH_DMA_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/hardware.h
+@@ -0,0 +1,142 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/hardware.h
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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.
++ */
++
++#ifndef __ASM_ARCH_HARDWARE_H
++#define __ASM_ARCH_HARDWARE_H
++
++#include <linux/config.h>
++#include <asm/mach-types.h>
++
++
++/*
++ * These are statically mapped PCMCIA IO space for designs using it as a
++ * generic IO bus, typically with ISA parts, hardwired IDE interfaces, etc.
++ * The actual PCMCIA code is mapping required IO region at run time.
++ */
++#define PCMCIA_IO_0_BASE      0xf6000000
++#define PCMCIA_IO_1_BASE      0xf7000000
++
++
++/*
++ * XIP kernel text mapping.
++ * Note: the exact virtual address is also specified in arch/arm/Makefile.
++ */
++#ifdef CONFIG_XIP_KERNEL
++#define KERNEL_XIP_BASE_PHYS  (CONFIG_XIP_PHYS_ADDR & 0xffe00000)
++#define KERNEL_XIP_BASE_VIRT  0xe8000000
++#endif
++
++
++/*
++ * We requires absolute addresses.
++ */
++#define PCIO_BASE             0
++
++/*
++ * Workarounds for at least 2 errata so far require this.
++ * The mapping is set in mach-pxa/generic.c.
++ */
++#define UNCACHED_PHYS_0               0xff000000
++#define UNCACHED_ADDR         UNCACHED_PHYS_0
++
++/*
++ * Intel PXA internal I/O mappings:
++ *
++ * 0x40000000 - 0x41ffffff <--> 0xf8000000 - 0xf9ffffff
++ * 0x44000000 - 0x45ffffff <--> 0xfa000000 - 0xfbffffff
++ * 0x48000000 - 0x49ffffff <--> 0xfc000000 - 0xfdffffff
++ */
++
++#define io_p2v(x)     ( ((x) | 0xbe000000) ^ (~((x) >> 1) & 0x06000000) )
++#define io_v2p( x )   ( ((x) & 0x41ffffff) ^ ( ((x) & 0x06000000) << 1) )
++
++#ifndef __ASSEMBLY__
++
++#if 0
++# define __REG(x)     (*((volatile u32 *)io_p2v(x)))
++#else
++/*
++ * This __REG() version gives the same results as the one above,  except
++ * that we are fooling gcc somehow so it generates far better and smaller
++ * assembly code for access to contigous registers.  It's a shame that gcc
++ * doesn't guess this by itself.
++ */
++#include <asm/types.h>
++typedef struct { volatile u32 offset[4096]; } __regbase;
++# define __REGP(x)    ((__regbase *)((x)&~4095))->offset[((x)&4095)>>2]
++# define __REG(x)     __REGP(io_p2v(x))
++#endif
++
++/* Let's kick gcc's ass again... */
++# define __REG2(x,y)  \
++      ( __builtin_constant_p(y) ? (__REG((x) + (y))) \
++                                : (*(volatile u32 *)((u32)&__REG(x) + (y))) )
++
++# define __PREG(x)    (io_v2p((u32)&(x)))
++
++#else
++
++# define __REG(x)     io_p2v(x)
++# define __PREG(x)    io_v2p(x)
++
++#endif
++
++#include "pxa-regs.h"
++
++#ifndef __ASSEMBLY__
++
++/*
++ * GPIO edge detection for IRQs:
++ * IRQs are generated on Falling-Edge, Rising-Edge, or both.
++ * This must be called *before* the corresponding IRQ is registered.
++ * Use this instead of directly setting GRER/GFER.
++ */
++#define GPIO_FALLING_EDGE       1
++#define GPIO_RISING_EDGE        2
++#define GPIO_BOTH_EDGES         3
++extern void set_GPIO_IRQ_edge( int gpio_nr, int edge_mask );
++
++/*
++ * Handy routine to set GPIO alternate functions
++ */
++extern void set_GPIO_mode( int gpio_mode );
++
++/*
++ * return current lclk frequency in units of 10kHz
++ */
++extern unsigned int get_lclk_frequency_10khz(void);
++
++/*
++ * return current clk frequency in units of 1kHz
++ */
++extern unsigned int get_clk_frequency_khz( int info);
++
++#endif
++
++
++/*
++ * Implementation specifics
++ */
++
++//#ifdef CONFIG_ARCH_LUBBOCK
++#include "lubbock.h"
++//#endif
++
++//#ifdef CONFIG_ARCH_PXA_IDP
++#include "idp.h"
++//#endif
++
++//#ifdef CONFIG_ARCH_PXA_CERF
++#include "cerf.h"
++//#endif
++
++#endif  /* _ASM_ARCH_HARDWARE_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/ide.h
+@@ -0,0 +1,59 @@
++/*
++ * linux/include/asm-arm/arch-pxa/ide.h
++ *
++ * Author:    George Davis
++ * Created:   Jan 10, 2002
++ * Copyright: 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.
++ *
++ *
++ * Originally based upon linux/include/asm-arm/arch-sa1100/ide.h
++ *
++ */
++
++#include <linux/config.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++
++
++/*
++ * Set up a hw structure for a specified data port, control port and IRQ.
++ * This should follow whatever the default interface uses.
++ */
++static __inline__ void
++ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
++{
++      ide_ioreg_t reg;
++
++      memset(hw, 0, sizeof(*hw));
++
++      reg = (ide_ioreg_t)data_port;
++
++      hw->io_ports[IDE_DATA_OFFSET] =  reg + 0;
++      hw->io_ports[IDE_ERROR_OFFSET] = reg + 1;
++      hw->io_ports[IDE_NSECTOR_OFFSET] = reg + 2;
++      hw->io_ports[IDE_SECTOR_OFFSET] = reg + 3;
++      hw->io_ports[IDE_LCYL_OFFSET] = reg + 4;
++      hw->io_ports[IDE_HCYL_OFFSET] = reg + 5;
++      hw->io_ports[IDE_SELECT_OFFSET] = reg + 6;
++      hw->io_ports[IDE_STATUS_OFFSET] = reg + 7;
++
++      hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
++
++      if (irq)
++              *irq = 0;
++}
++
++
++/*
++ * Register the standard ports for this architecture with the IDE driver.
++ */
++static __inline__ void
++ide_init_default_hwifs(void)
++{
++      /* Nothing to declare... */
++}
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/idp.h
+@@ -0,0 +1,468 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/idp.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) 2001 Cliff Brake, Accelent Systems Inc.
++ *
++ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
++ *             Initial code
++ * 
++ */
++
++
++/*
++ * Note: this file must be safe to include in assembly files
++ */
++
++/* comment out following if you have a rev01 board */
++#define PXA_IDP_REV02 1
++//#undef PXA_IDP_REV02
++
++#ifdef PXA_IDP_REV02
++
++//Use this as well for 0017-x004 and greater pcb's:
++#define PXA_IDP_REV04 1
++
++#define IDP_FLASH_PHYS                (PXA_CS0_PHYS)
++#define IDP_ALT_FLASH_PHYS    (PXA_CS1_PHYS)
++#define IDP_MEDIAQ_PHYS               (PXA_CS3_PHYS)
++#define IDP_IDE_PHYS          (PXA_CS5_PHYS + 0x03000000)
++#define IDP_ETH_PHYS          (PXA_CS5_PHYS + 0x03400000)
++#define IDP_COREVOLT_PHYS     (PXA_CS5_PHYS + 0x03800000)
++#define IDP_CPLD_PHYS         (PXA_CS5_PHYS + 0x03C00000)
++
++
++/*
++ * virtual memory map
++ */
++
++#define IDP_IDE_BASE          (0xf0000000)
++#define IDP_IDE_SIZE          (1*1024*1024)
++
++#define IDP_ETH_BASE          (IDP_IDE_BASE + IDP_IDE_SIZE)
++#define IDP_ETH_SIZE          (1*1024*1024)
++#define ETH_BASE              IDP_ETH_BASE //smc9194 driver compatibility issue
++
++#define IDP_COREVOLT_BASE     (IDP_ETH_BASE + IDP_ETH_SIZE)
++#define IDP_COREVOLT_SIZE     (1*1024*1024)
++
++#define IDP_CPLD_BASE         (IDP_COREVOLT_BASE + IDP_COREVOLT_SIZE)
++#define IDP_CPLD_SIZE         (1*1024*1024)
++
++#if (IDP_CPLD_BASE + IDP_CPLD_SIZE) > 0xfc000000
++#error Your custom IO space is getting a bit large !!
++#endif
++
++#define CPLD_P2V(x)           ((x) - IDP_CPLD_PHYS + IDP_CPLD_BASE)
++#define CPLD_V2P(x)           ((x) - IDP_CPLD_BASE + IDP_CPLD_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __CPLD_REG(x)               (*((volatile unsigned long *)CPLD_P2V(x)))
++#else
++#  define __CPLD_REG(x)               CPLD_P2V(x)
++#endif
++
++/* board level registers in the CPLD: (offsets from CPLD_BASE) */
++
++#define _IDP_CPLD_REV                 (IDP_CPLD_PHYS + 0x00)
++#define _IDP_CPLD_PERIPH_PWR          (IDP_CPLD_PHYS + 0x04)
++#define _IDP_CPLD_LED_CONTROL         (IDP_CPLD_PHYS + 0x08)
++#define _IDP_CPLD_KB_COL_HIGH         (IDP_CPLD_PHYS + 0x0C)
++#define _IDP_CPLD_KB_COL_LOW          (IDP_CPLD_PHYS + 0x10)
++#define _IDP_CPLD_PCCARD_EN           (IDP_CPLD_PHYS + 0x14)
++#define _IDP_CPLD_GPIOH_DIR           (IDP_CPLD_PHYS + 0x18)
++#define _IDP_CPLD_GPIOH_VALUE         (IDP_CPLD_PHYS + 0x1C)
++#define _IDP_CPLD_GPIOL_DIR           (IDP_CPLD_PHYS + 0x20)
++#define _IDP_CPLD_GPIOL_VALUE         (IDP_CPLD_PHYS + 0x24)
++#define _IDP_CPLD_PCCARD_PWR          (IDP_CPLD_PHYS + 0x28)
++#define _IDP_CPLD_MISC_CTRL           (IDP_CPLD_PHYS + 0x2C)
++#define _IDP_CPLD_LCD                 (IDP_CPLD_PHYS + 0x30)
++#define _IDP_CPLD_FLASH_WE            (IDP_CPLD_PHYS + 0x34)
++
++#define _IDP_CPLD_KB_ROW              (IDP_CPLD_PHYS + 0x50)
++#define _IDP_CPLD_PCCARD0_STATUS      (IDP_CPLD_PHYS + 0x54)
++#define _IDP_CPLD_PCCARD1_STATUS      (IDP_CPLD_PHYS + 0x58)
++#define _IDP_CPLD_MISC_STATUS         (IDP_CPLD_PHYS + 0x5C)
++
++/* FPGA register virtual addresses */
++
++#define IDP_CPLD_REV                  __CPLD_REG(_IDP_CPLD_REV)
++#define IDP_CPLD_PERIPH_PWR           __CPLD_REG(_IDP_CPLD_PERIPH_PWR)
++#define IDP_CPLD_LED_CONTROL          __CPLD_REG(_IDP_CPLD_LED_CONTROL)               
++#define IDP_CPLD_KB_COL_HIGH          __CPLD_REG(_IDP_CPLD_KB_COL_HIGH)       
++#define IDP_CPLD_KB_COL_LOW           __CPLD_REG(_IDP_CPLD_KB_COL_LOW)        
++#define IDP_CPLD_PCCARD_EN            __CPLD_REG(_IDP_CPLD_PCCARD_EN)         
++#define IDP_CPLD_GPIOH_DIR            __CPLD_REG(_IDP_CPLD_GPIOH_DIR)         
++#define IDP_CPLD_GPIOH_VALUE          __CPLD_REG(_IDP_CPLD_GPIOH_VALUE)
++#define IDP_CPLD_GPIOL_DIR            __CPLD_REG(_IDP_CPLD_GPIOL_DIR) 
++#define IDP_CPLD_GPIOL_VALUE          __CPLD_REG(_IDP_CPLD_GPIOL_VALUE)
++#define IDP_CPLD_PCCARD_PWR           __CPLD_REG(_IDP_CPLD_PCCARD_PWR)        
++#define IDP_CPLD_MISC_CTRL            __CPLD_REG(_IDP_CPLD_MISC_CTRL)         
++#define IDP_CPLD_LCD                  __CPLD_REG(_IDP_CPLD_LCD)               
++#define IDP_CPLD_FLASH_WE             __CPLD_REG(_IDP_CPLD_FLASH_WE)
++                                                                         
++#define IDP_CPLD_KB_ROW                       __CPLD_REG(_IDP_CPLD_KB_ROW)            
++#define IDP_CPLD_PCCARD0_STATUS               __CPLD_REG(_IDP_CPLD_PCCARD0_STATUS)
++#define IDP_CPLD_PCCARD1_STATUS               __CPLD_REG(_IDP_CPLD_PCCARD1_STATUS)    
++#define IDP_CPLD_MISC_STATUS          __CPLD_REG(_IDP_CPLD_MISC_STATUS)       
++
++
++/*
++ * Bit masks for various registers
++ */
++// IDP_CPLD_PCCARD_PWR
++#define PCC0_PWR0     (1 << 0)
++#define PCC0_PWR1     (1 << 1)
++#define PCC0_PWR2     (1 << 2)
++#define PCC0_PWR3     (1 << 3)
++#define PCC1_PWR0     (1 << 4)
++#define PCC1_PWR1     (1 << 5)
++#define PCC1_PWR2     (1 << 6)
++#define PCC1_PWR3     (1 << 7)
++
++// IDP_CPLD_PCCARD_EN
++#define PCC0_RESET    (1 << 6)
++#define PCC1_RESET    (1 << 7)
++#define PCC0_ENABLE   (1 << 0)
++#define PCC1_ENABLE   (1 << 1)
++
++// IDP_CPLD_PCCARDx_STATUS
++#define _PCC_WRPROT   (1 << 7) // 7-4 read as low true
++#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 PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
++
++/*
++ * Macros for LCD Driver
++ */
++
++#ifdef CONFIG_FB_PXA
++
++#define FB_BACKLIGHT_ON()     (IDP_CPLD_LCD |= (1<<1)) 
++#define FB_BACKLIGHT_OFF()    (IDP_CPLD_LCD &= ~(1<<1))
++
++#define FB_PWR_ON()           (IDP_CPLD_LCD |= (1<< 0))
++#define FB_PWR_OFF()          (IDP_CPLD_LCD &= ~(1<<0))
++
++#define FB_VLCD_ON()          (IDP_CPLD_LCD |= (1<<2)) 
++#define FB_VLCD_OFF()                 (IDP_CPLD_LCD &= ~(1<<2))
++
++#endif
++
++/* A listing of interrupts used by external hardware devices */
++
++#ifdef PXA_IDP_REV04
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(5)
++#define IDE_IRQ                               IRQ_GPIO(21) 
++#else
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(21)
++#define IDE_IRQ                               IRQ_GPIO(5)
++#endif
++
++#define TOUCH_PANEL_IRQ_EDGE          GPIO_FALLING_EDGE
++
++#define IDE_IRQ_EDGE                  GPIO_RISING_EDGE
++
++#define ETHERNET_IRQ                  IRQ_GPIO(4)
++#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
++
++#define IDE_IRQ_EDGE                  GPIO_RISING_EDGE
++
++#define PCMCIA_S0_CD_VALID            IRQ_GPIO(7)
++#define PCMCIA_S0_CD_VALID_EDGE               GPIO_BOTH_EDGES
++
++#define PCMCIA_S1_CD_VALID            IRQ_GPIO(8)
++#define PCMCIA_S1_CD_VALID_EDGE               GPIO_BOTH_EDGES
++
++#define PCMCIA_S0_RDYINT              IRQ_GPIO(19)
++#define PCMCIA_S1_RDYINT              IRQ_GPIO(22)
++
++/*
++ * Macros for LED Driver
++ */
++
++/* leds 0 = ON */
++#define IDP_HB_LED    (1<<5)  
++#define IDP_BUSY_LED  (1<<6)
++
++#define IDP_LEDS_MASK (IDP_HB_LED | IDP_BUSY_LED)
++
++#define IDP_WRITE_LEDS(value) (IDP_CPLD_LED_CONTROL = (IDP_CPLD_LED_CONTROL & (~(IDP_LEDS_MASK)) | value))
++
++/*
++ * macros for MTD driver
++ */
++
++#define FLASH_WRITE_PROTECT_DISABLE() ((IDP_CPLD_FLASH_WE) &= ~(0x1))
++#define FLASH_WRITE_PROTECT_ENABLE()  ((IDP_CPLD_FLASH_WE) |= (0x1))
++
++/*
++ * macros for matrix keyboard driver
++ */
++
++#define KEYBD_MATRIX_NUMBER_INPUTS    7
++#define KEYBD_MATRIX_NUMBER_OUTPUTS   14
++
++#define KEYBD_MATRIX_INVERT_OUTPUT_LOGIC      FALSE
++#define KEYBD_MATRIX_INVERT_INPUT_LOGIC               FALSE
++
++#define KEYBD_MATRIX_SETTLING_TIME_US                 100
++#define KEYBD_MATRIX_KEYSTATE_DEBOUNCE_CONSTANT               2
++
++#define KEYBD_MATRIX_SET_OUTPUTS(outputs) \
++{\
++      IDP_CPLD_KB_COL_LOW = outputs;\
++      IDP_CPLD_KB_COL_HIGH = outputs >> 7;\
++}
++
++#define KEYBD_MATRIX_GET_INPUTS(inputs) \
++{\
++      inputs = (IDP_CPLD_KB_ROW & 0x7f);\
++}
++
++//------------------------------------------------------------------------------
++
++#else  // must be rev 01
++
++/* -----------------------------------------------------------------------------
++ * following is for rev01 boards only
++ */
++
++#define IDP_FLASH_PHYS                (PXA_CS0_PHYS)
++#define IDP_ALT_FLASH_PHYS    (PXA_CS1_PHYS)
++#define IDP_MEDIAQ_PHYS               (PXA_CS3_PHYS)
++#define IDP_CTRL_PORT_PHYS    (PXA_CS5_PHYS + 0x02C00000)
++#define IDP_IDE_PHYS          (PXA_CS5_PHYS + 0x03000000)
++#define IDP_ETH_PHYS          (PXA_CS5_PHYS + 0x03400000)
++#define IDP_COREVOLT_PHYS     (PXA_CS5_PHYS + 0x03800000)
++#define IDP_CPLD_PHYS         (PXA_CS5_PHYS + 0x03C00000)
++
++
++/*
++ * virtual memory map
++ */
++
++#define IDP_CTRL_PORT_BASE    (0xf0000000)
++#define IDP_CTRL_PORT_SIZE    (1*1024*1024)
++
++#define IDP_IDE_BASE          (IDP_CTRL_PORT_BASE + IDP_CTRL_PORT_SIZE)
++#define IDP_IDE_SIZE          (1*1024*1024)
++
++#define IDP_ETH_BASE          (IDP_IDE_BASE + IDP_IDE_SIZE)
++#define IDP_ETH_SIZE          (1*1024*1024)
++
++#define IDP_COREVOLT_BASE     (IDP_ETH_BASE + IDP_ETH_SIZE)
++#define IDP_COREVOLT_SIZE     (1*1024*1024)
++
++#define IDP_CPLD_BASE         (IDP_COREVOLT_BASE + IDP_COREVOLT_SIZE)
++#define IDP_CPLD_SIZE         (1*1024*1024)
++
++#if (IDP_CPLD_BASE + IDP_CPLD_SIZE) > 0xfc000000
++#error Your custom IO space is getting a bit large !!
++#endif
++
++#define CPLD_P2V(x)           ((x) - IDP_CPLD_PHYS + IDP_CPLD_BASE)
++#define CPLD_V2P(x)           ((x) - IDP_CPLD_BASE + IDP_CPLD_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __CPLD_REG(x)               (*((volatile unsigned long *)CPLD_P2V(x)))
++#else
++#  define __CPLD_REG(x)               CPLD_P2V(x)
++#endif
++
++/* board level registers in the CPLD: (offsets from CPLD_BASE) */
++
++#define _IDP_CPLD_LED_CONTROL         (IDP_CPLD_PHYS + 0x00)
++#define _IDP_CPLD_PERIPH_PWR          (IDP_CPLD_PHYS + 0x04)
++#define _IDP_CPLD_CIR                 (IDP_CPLD_PHYS + 0x08)
++#define _IDP_CPLD_KB_COL_HIGH         (IDP_CPLD_PHYS + 0x0C)
++#define _IDP_CPLD_KB_COL_LOW          (IDP_CPLD_PHYS + 0x10)
++#define _IDP_CPLD_PCCARD_EN           (IDP_CPLD_PHYS + 0x14)
++#define _IDP_CPLD_GPIOH_DIR           (IDP_CPLD_PHYS + 0x18)
++#define _IDP_CPLD_GPIOH_VALUE         (IDP_CPLD_PHYS + 0x1C)
++#define _IDP_CPLD_GPIOL_DIR           (IDP_CPLD_PHYS + 0x20)
++#define _IDP_CPLD_GPIOL_VALUE         (IDP_CPLD_PHYS + 0x24)
++#define _IDP_CPLD_MISC                        (IDP_CPLD_PHYS + 0x28)
++#define _IDP_CPLD_PCCARD0_STATUS      (IDP_CPLD_PHYS + 0x2C)
++#define _IDP_CPLD_PCCARD1_STATUS      (IDP_CPLD_PHYS + 0x30)
++
++/* FPGA register virtual addresses */
++#define IDP_CPLD_LED_CONTROL          __CPLD_REG(_IDP_CPLD_LED_CONTROL)       /* write only */
++#define IDP_CPLD_PERIPH_PWR           __CPLD_REG(_IDP_CPLD_PERIPH_PWR)        /* write only */
++#define IDP_CPLD_CIR                  __CPLD_REG(_IDP_CPLD_CIR)               /* write only */
++#define IDP_CPLD_KB_COL_HIGH          __CPLD_REG(_IDP_CPLD_KB_COL_HIGH)       /* write only */
++#define IDP_CPLD_KB_COL_LOW           __CPLD_REG(_IDP_CPLD_KB_COL_LOW)        /* write only */
++#define IDP_CPLD_PCCARD_EN            __CPLD_REG(_IDP_CPLD_PCCARD_EN)         /* write only */
++#define IDP_CPLD_GPIOH_DIR            __CPLD_REG(_IDP_CPLD_GPIOH_DIR)         /* write only */
++#define IDP_CPLD_GPIOH_VALUE          __CPLD_REG(_IDP_CPLD_GPIOH_VALUE)       /* write only */
++#define IDP_CPLD_GPIOL_DIR            __CPLD_REG(_IDP_CPLD_GPIOL_DIR)         /* write only */
++#define IDP_CPLD_GPIOL_VALUE          __CPLD_REG(_IDP_CPLD_GPIOL_VALUE)       /* write only */
++#define IDP_CPLD_MISC                 __CPLD_REG(_IDP_CPLD_MISC)              /* read only */
++#define IDP_CPLD_PCCARD0_STATUS               __CPLD_REG(_IDP_CPLD_PCCARD0_STATUS)    /* read only */
++#define IDP_CPLD_PCCARD1_STATUS               __CPLD_REG(_IDP_CPLD_PCCARD1_STATUS)    /* read only */
++
++
++#ifndef __ASSEMBLY__
++
++/* shadow registers for write only registers */
++extern unsigned int idp_cpld_led_control_shadow;
++extern unsigned int idp_cpld_periph_pwr_shadow;
++extern unsigned int idp_cpld_cir_shadow;
++extern unsigned int idp_cpld_kb_col_high_shadow;
++extern unsigned int idp_cpld_kb_col_low_shadow;
++extern unsigned int idp_cpld_pccard_en_shadow;
++extern unsigned int idp_cpld_gpioh_dir_shadow;
++extern unsigned int idp_cpld_gpioh_value_shadow;
++extern unsigned int idp_cpld_gpiol_dir_shadow;
++extern unsigned int idp_cpld_gpiol_value_shadow;
++
++extern unsigned int idp_control_port_shadow;
++
++/* 
++ * macros to write to write only register
++ *
++ * none of these macros are protected from 
++ * multiple drivers using them in interrupt context.
++ */
++
++#define WRITE_IDP_CPLD_LED_CONTROL(value, mask) \
++{\
++      idp_cpld_led_control_shadow = ((value & mask) | (idp_cpld_led_control_shadow & ~mask));\
++      IDP_CPLD_LED_CONTROL = idp_cpld_led_control_shadow;\
++}
++#define WRITE_IDP_CPLD_PERIPH_PWR(value, mask) \
++{\
++      idp_cpld_periph_pwr_shadow = ((value & mask) | (idp_cpld_periph_pwr_shadow & ~mask));\
++      IDP_CPLD_PERIPH_PWR = idp_cpld_periph_pwr_shadow;\
++}
++#define WRITE_IDP_CPLD_CIR(value, mask) \
++{\
++      idp_cpld_cir_shadow = ((value & mask) | (idp_cpld_cir_shadow & ~mask));\
++      IDP_CPLD_CIR = idp_cpld_cir_shadow;\
++}
++#define WRITE_IDP_CPLD_KB_COL_HIGH(value, mask) \
++{\
++      idp_cpld_kb_col_high_shadow = ((value & mask) | (idp_cpld_kb_col_high_shadow & ~mask));\
++      IDP_CPLD_KB_COL_HIGH = idp_cpld_kb_col_high_shadow;\
++}
++#define WRITE_IDP_CPLD_KB_COL_LOW(value, mask) \
++{\
++      idp_cpld_kb_col_low_shadow = ((value & mask) | (idp_cpld_kb_col_low_shadow & ~mask));\
++      IDP_CPLD_KB_COL_LOW = idp_cpld_kb_col_low_shadow;\
++}
++#define WRITE_IDP_CPLD_PCCARD_EN(value, mask) \
++{\
++      idp_cpld_ = ((value & mask) | (idp_cpld_led_control_shadow & ~mask));\
++      IDP_CPLD_LED_CONTROL = idp_cpld_led_control_shadow;\
++}
++#define WRITE_IDP_CPLD_GPIOH_DIR(value, mask) \
++{\
++      idp_cpld_gpioh_dir_shadow = ((value & mask) | (idp_cpld_gpioh_dir_shadow & ~mask));\
++      IDP_CPLD_GPIOH_DIR = idp_cpld_gpioh_dir_shadow;\
++}
++#define WRITE_IDP_CPLD_GPIOH_VALUE(value, mask) \
++{\
++      idp_cpld_gpioh_value_shadow = ((value & mask) | (idp_cpld_gpioh_value_shadow & ~mask));\
++      IDP_CPLD_GPIOH_VALUE = idp_cpld_gpioh_value_shadow;\
++}
++#define WRITE_IDP_CPLD_GPIOL_DIR(value, mask) \
++{\
++      idp_cpld_gpiol_dir_shadow = ((value & mask) | (idp_cpld_gpiol_dir_shadow & ~mask));\
++      IDP_CPLD_GPIOL_DIR = idp_cpld_gpiol_dir_shadow;\
++}
++#define WRITE_IDP_CPLD_GPIOL_VALUE(value, mask) \
++{\
++      idp_cpld_gpiol_value_shadow = ((value & mask) | (idp_cpld_gpiol_value_shadow & ~mask));\
++      IDP_CPLD_GPIOL_VALUE = idp_cpld_gpiol_value_shadow;\
++}
++
++#define WRITE_IDP_CONTROL_PORT(value, mask) \
++{\
++      idp_control_port_shadow = ((value & mask) | (idp_control_port_shadow & ~mask));\
++      (*((volatile unsigned long *)IDP_CTRL_PORT_BASE)) = idp_control_port_shadow;\
++}
++
++#endif
++
++/* A listing of interrupts used by external hardware devices */
++
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(21)
++#define TOUCH_PANEL_IRQ_EGDE          GPIO_FALLING_EDGE
++
++#define ETHERNET_IRQ                  IRQ_GPIO(4)
++#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
++
++/*
++ * Bit masks for various registers
++ */
++
++
++/* control port */
++#define IDP_CONTROL_PORT_PCSLOT0_0    (1 << 0)
++#define IDP_CONTROL_PORT_PCSLOT0_1    (1 << 1)
++#define IDP_CONTROL_PORT_PCSLOT0_2    (1 << 2)
++#define IDP_CONTROL_PORT_PCSLOT0_3    (1 << 3)
++#define IDP_CONTROL_PORT_PCSLOT1_1    (1 << 4)
++#define IDP_CONTROL_PORT_PCSLOT1_2    (1 << 5)
++#define IDP_CONTROL_PORT_PCSLOT1_3    (1 << 6)
++#define IDP_CONTROL_PORT_PCSLOT1_4    (1 << 7)
++#define IDP_CONTROL_PORT_SERIAL1_EN   (1 << 9)
++#define IDP_CONTROL_PORT_SERIAL2_EN   (1 << 10)
++#define IDP_CONTROL_PORT_SERIAL3_EN   (1 << 11)
++#define IDP_CONTROL_PORT_IRDA_FIR     (1 << 12)
++#define IDP_CONTROL_PORT_IRDA_M0      (1 << 13)
++#define IDP_CONTROL_PORT_IRDA_M1      (1 << 14)
++#define IDP_CONTROL_PORT_I2S_PWR      (1 << 15)
++#define IDP_CONTROL_PORT_FLASH_WP     (1 << 19)
++#define IDP_CONTROL_PORT_MILL_EN      (1 << 20)
++#define IDP_CONTROL_PORT_LCD_PWR      (1 << 21)
++#define IDP_CONTROL_PORT_LCD_BKLEN    (1 << 22)
++#define IDP_CONTROL_PORT_LCD_ENAVLCD  (1 << 23)
++
++/*
++ * Macros for LCD Driver
++ */
++
++#ifdef CONFIG_FB_PXA
++
++#define FB_BACKLIGHT_ON() WRITE_IDP_CONTROL_PORT(IDP_CONTROL_PORT_LCD_BKLEN, IDP_CONTROL_PORT_LCD_BKLEN)
++#define FB_BACKLIGHT_OFF() WRITE_IDP_CONTROL_PORT(0, IDP_CONTROL_PORT_LCD_BKLEN)
++
++#define FB_PWR_ON() WRITE_IDP_CONTROL_PORT(IDP_CONTROL_PORT_LCD_PWR, IDP_CONTROL_PORT_LCD_PWR)
++#define FB_PWR_OFF() WRITE_IDP_CONTROL_PORT(0, IDP_CONTROL_PORT_LCD_PWR)
++
++#define FB_VLCD_ON() WRITE_IDP_CONTROL_PORT(IDP_CONTROL_PORT_LCD_ENAVLCD, IDP_CONTROL_PORT_LCD_ENAVLCD)
++#define FB_VLCD_OFF() WRITE_IDP_CONTROL_PORT(0, IDP_CONTROL_PORT_LCD_ENAVLCD)
++
++#endif
++
++
++/*
++ * Macros for LED Driver
++ */
++
++/* leds 0 = ON */
++#define IDP_HB_LED    0x1
++#define IDP_BUSY_LED  0x2
++
++#define IDP_LEDS_MASK (IDP_HB_LED | IDP_BUSY_LED)
++
++#define IDP_WRITE_LEDS(value)         WRITE_IDP_CPLD_LED_CONTROL(value, IDP_LEDS_MASK)
++
++/*
++ * macros for MTD driver
++ */
++
++#define FLASH_WRITE_PROTECT_DISABLE() WRITE_IDP_CONTROL_PORT(0, IDP_CONTROL_PORT_FLASH_WP)
++#define FLASH_WRITE_PROTECT_ENABLE()  WRITE_IDP_CONTROL_PORT(IDP_CONTROL_PORT_FLASH_WP, IDP_CONTROL_PORT_FLASH_WP)
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/innokom.h
+@@ -0,0 +1,47 @@
++/*
++ * linux/include/asm-arm/arch-pxa/innokom.h
++ *
++ * (c) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
++ *
++ * 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
++ */
++
++/* 
++ * GPIOs 
++ */
++#define GPIO_INNOKOM_RESET    3
++#define GPIO_INNOKOM_SW_UPDATE        11
++#define GPIO_INNOKOM_ETH      59
++
++/* 
++ * ethernet chip (SMSC91C111) 
++ */
++#define INNOKOM_ETH_PHYS      PXA_CS5_PHYS
++#define INNOKOM_ETH_BASE      (0xf0000000)    /* phys 0x14000000 */
++#define INNOKOM_ETH_SIZE      (1*1024*1024)
++#define INNOKOM_ETH_IRQ               IRQ_GPIO(GPIO_INNOKOM_ETH)
++#define INNOKOM_ETH_IRQ_EDGE  GPIO_RISING_EDGE
++
++/*
++ * virtual to physical conversion macros
++ */
++#define INNOKOM_P2V(x)                ((x) - INNOKOM_FPGA_PHYS + INNOKOM_FPGA_VIRT)
++#define INNOKOM_V2P(x)                ((x) - INNOKOM_FPGA_VIRT + INNOKOM_FPGA_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __INNOKOM_REG(x)    (*((volatile unsigned long *)INNOKOM_P2V(x)))
++#else
++#  define __INNOKOM_REG(x)    INNOKOM_P2V(x)
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/io.h
+@@ -0,0 +1,34 @@
++/*
++ * linux/include/asm-arm/arch-pxa/io.h
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Jun 15, 2001
++ * Copyright: 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.
++ */
++#ifndef __ASM_ARM_ARCH_IO_H
++#define __ASM_ARM_ARCH_IO_H
++
++#define IO_SPACE_LIMIT 0xffffffff
++
++/*
++ * We don't actually have real ISA nor PCI buses, but there is so many 
++ * drivers out there that might just work if we fake them...
++ */
++#define __io(a)                       (a)
++#define __mem_pci(a)          ((unsigned long)(a))
++#define __mem_isa(a)          ((unsigned long)(a))
++
++/*
++ * Generic virtual read/write
++ */
++#define __arch_getw(a)                (*(volatile unsigned short *)(a))
++#define __arch_putw(v,a)      (*(volatile unsigned short *)(a) = (v))
++
++#define iomem_valid_addr(iomem,sz)    (1)
++#define iomem_to_phys(iomem)          (iomem)
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/irq.h
+@@ -0,0 +1,19 @@
++/*
++ * linux/include/asm-arm/arch-pxa/irq.h
++ * 
++ * Author:    Nicolas Pitre
++ * Created:   Jun 15, 2001
++ * Copyright: 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.
++ */
++
++#define fixup_irq(x)  (x)
++
++/*
++ * This prototype is required for cascading of multiplexed interrupts.
++ * Since it doesn't exist elsewhere, we'll put it here for now.
++ */
++extern void do_IRQ(int irq, struct pt_regs *regs);
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/irqs.h
+@@ -0,0 +1,137 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/irqs.h
++ *  
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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.
++ */
++
++#define PXA_IRQ_SKIP  7       /* The first 7 IRQs are not yet used */
++#define PXA_IRQ(x)            ((x) - PXA_IRQ_SKIP)
++
++#define IRQ_HWUART    PXA_IRQ(7)      /* HWUART Transmit/Receive/Error */
++#define       IRQ_GPIO0       PXA_IRQ(8)      /* GPIO0 Edge Detect */
++#define       IRQ_GPIO1       PXA_IRQ(9)      /* GPIO1 Edge Detect */
++#define       IRQ_GPIO_2_80   PXA_IRQ(10)     /* GPIO[2-80] Edge Detect */
++#define       IRQ_USB         PXA_IRQ(11)     /* USB Service */
++#define       IRQ_PMU         PXA_IRQ(12)     /* Performance Monitoring Unit */
++#define       IRQ_I2S         PXA_IRQ(13)     /* I2S Interrupt */
++#define       IRQ_AC97        PXA_IRQ(14)     /* AC97 Interrupt */
++#define IRQ_ASSP      PXA_IRQ(15)     /* Audio SSP Service Request */
++#define IRQ_NSSP      PXA_IRQ(16)     /* Network SSP Service Request */
++#define       IRQ_LCD         PXA_IRQ(17)     /* LCD Controller Service Request */
++#define       IRQ_I2C         PXA_IRQ(18)     /* I2C Service Request */
++#define       IRQ_ICP         PXA_IRQ(19)     /* ICP Transmit/Receive/Error */
++#define       IRQ_STUART      PXA_IRQ(20)     /* STUART Transmit/Receive/Error */
++#define       IRQ_BTUART      PXA_IRQ(21)     /* BTUART Transmit/Receive/Error */
++#define       IRQ_FFUART      PXA_IRQ(22)     /* FFUART Transmit/Receive/Error*/
++#define       IRQ_MMC         PXA_IRQ(23)     /* MMC Status/Error Detection */
++#define       IRQ_SSP         PXA_IRQ(24)     /* SSP Service Request */
++#define       IRQ_DMA         PXA_IRQ(25)     /* DMA Channel Service Request */
++#define       IRQ_OST0        PXA_IRQ(26)     /* OS Timer match 0 */
++#define       IRQ_OST1        PXA_IRQ(27)     /* OS Timer match 1 */
++#define       IRQ_OST2        PXA_IRQ(28)     /* OS Timer match 2 */
++#define       IRQ_OST3        PXA_IRQ(29)     /* OS Timer match 3 */
++#define       IRQ_RTC1Hz      PXA_IRQ(30)     /* RTC HZ Clock Tick */
++#define       IRQ_RTCAlrm     PXA_IRQ(31)     /* RTC Alarm */
++
++#define GPIO_2_80_TO_IRQ(x)   \
++                      PXA_IRQ((x) - 2 + 32)
++#define IRQ_GPIO(x)   (((x) < 2) ? (IRQ_GPIO0 + (x)) : GPIO_2_80_TO_IRQ(x))
++
++#define IRQ_TO_GPIO_2_80(i)   \
++                      ((i) - PXA_IRQ(32) + 2)
++#define IRQ_TO_GPIO(i)        ((i) - (((i) > IRQ_GPIO1) ? IRQ_GPIO(2) - 2 : IRQ_GPIO(0)))
++
++#define       NR_IRQS         (IRQ_GPIO(80) + 1)
++
++#if defined(CONFIG_SA1111)
++
++#define IRQ_SA1111_START      (IRQ_GPIO(80) + 1)
++#define SA1111_IRQ(x)         (IRQ_SA1111_START + (x))
++
++#define IRQ_GPAIN0            SA1111_IRQ(0)
++#define IRQ_GPAIN1            SA1111_IRQ(1)
++#define IRQ_GPAIN2            SA1111_IRQ(2)
++#define IRQ_GPAIN3            SA1111_IRQ(3)
++#define IRQ_GPBIN0            SA1111_IRQ(4)
++#define IRQ_GPBIN1            SA1111_IRQ(5)
++#define IRQ_GPBIN2            SA1111_IRQ(6)
++#define IRQ_GPBIN3            SA1111_IRQ(7)
++#define IRQ_GPBIN4            SA1111_IRQ(8)
++#define IRQ_GPBIN5            SA1111_IRQ(9)
++#define IRQ_GPCIN0            SA1111_IRQ(10)
++#define IRQ_GPCIN1            SA1111_IRQ(11)
++#define IRQ_GPCIN2            SA1111_IRQ(12)
++#define IRQ_GPCIN3            SA1111_IRQ(13)
++#define IRQ_GPCIN4            SA1111_IRQ(14)
++#define IRQ_GPCIN5            SA1111_IRQ(15)
++#define IRQ_GPCIN6            SA1111_IRQ(16)
++#define IRQ_GPCIN7            SA1111_IRQ(17)
++#define IRQ_MSTXINT           SA1111_IRQ(18)
++#define IRQ_MSRXINT           SA1111_IRQ(19)
++#define IRQ_MSSTOPERRINT      SA1111_IRQ(20)
++#define IRQ_TPTXINT           SA1111_IRQ(21)
++#define IRQ_TPRXINT           SA1111_IRQ(22)
++#define IRQ_TPSTOPERRINT      SA1111_IRQ(23)
++#define SSPXMTINT     SA1111_IRQ(24)
++#define SSPRCVINT     SA1111_IRQ(25)
++#define SSPROR                SA1111_IRQ(26)
++#define AUDXMTDMADONEA        SA1111_IRQ(32)
++#define AUDRCVDMADONEA        SA1111_IRQ(33)
++#define AUDXMTDMADONEB        SA1111_IRQ(34)
++#define AUDRCVDMADONEB        SA1111_IRQ(35)
++#define AUDTFSR               SA1111_IRQ(36)
++#define AUDRFSR               SA1111_IRQ(37)
++#define AUDTUR                SA1111_IRQ(38)
++#define AUDROR                SA1111_IRQ(39)
++#define AUDDTS                SA1111_IRQ(40)
++#define AUDRDD                SA1111_IRQ(41)
++#define AUDSTO                SA1111_IRQ(42)
++#define USBPWR                SA1111_IRQ(43)
++#define NIRQHCIM      SA1111_IRQ(44)
++#define HCIBUFFACC    SA1111_IRQ(45)
++#define HCIRMTWKP     SA1111_IRQ(46)
++#define NHCIMFCIR     SA1111_IRQ(47)
++#define PORT_RESUME   SA1111_IRQ(48)
++#define S0_READY_NINT SA1111_IRQ(49)
++#define S1_READY_NINT SA1111_IRQ(50)
++#define S0_CD_VALID   SA1111_IRQ(51)
++#define S1_CD_VALID   SA1111_IRQ(52)
++#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 CONFIG_SA1111
++#define LUBBOCK_IRQ(x)        (SA1111_IRQ_MAX + 1 + (x))
++#else
++#define LUBBOCK_IRQ(x)        (IRQ_GPIO(80) + 1 + (x))
++#endif
++
++#define LUBBOCK_SD_IRQ                LUBBOCK_IRQ(0)
++#define LUBBOCK_SA1111_IRQ    LUBBOCK_IRQ(1)
++#define LUBBOCK_USB_IRQ               LUBBOCK_IRQ(2)
++#define LUBBOCK_ETH_IRQ               LUBBOCK_IRQ(3)
++#define LUBBOCK_UCB1400_IRQ   LUBBOCK_IRQ(4)
++#define LUBBOCK_BB_IRQ                LUBBOCK_IRQ(5)
++#define LUBBOCK_USB_DISC_IRQ  LUBBOCK_IRQ(6)  /* usb disconnect */
++#define LUBBOCK_LAST_IRQ      LUBBOCK_IRQ(6)
++
++#undef NR_IRQS
++#define NR_IRQS               (LUBBOCK_LAST_IRQ + 1)
++
++#endif        // CONFIG_ARCH_LUBBOCK
++
++
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/keyboard.h
+@@ -0,0 +1,29 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/keyboard.h
++ *
++ *  This file contains the architecture specific keyboard definitions
++ */
++
++#ifndef _PXA_KEYBOARD_H
++#define _PXA_KEYBOARD_H
++
++#include <linux/config.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++
++extern struct kbd_ops_struct *kbd_ops;
++
++#define kbd_disable_irq()     do { } while(0);
++#define kbd_enable_irq()      do { } while(0);
++
++extern int sa1111_kbd_init_hw(void);
++
++static inline void kbd_init_hw(void)
++{
++      if (machine_is_lubbock())
++              sa1111_kbd_init_hw();
++}
++
++
++#endif  /* _PXA_KEYBOARD_H */
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/lubbock.h
+@@ -0,0 +1,113 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/lubbock.h
++ *
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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.
++ */
++
++#define LUBBOCK_FPGA_PHYS     PXA_CS2_PHYS
++#define LUBBOCK_FPGA_VIRT     (0xf0000000)    /* phys 0x08000000 */
++#define LUBBOCK_ETH_PHYS      PXA_CS3_PHYS
++#define LUBBOCK_ETH_VIRT      (0xf1000000)
++#define LUBBOCK_SA1111_BASE   (0xf4000000)    /* phys 0x10000000 */
++
++#define LUB_P2V(x)            ((x) - LUBBOCK_FPGA_PHYS + LUBBOCK_FPGA_VIRT)
++#define LUB_V2P(x)            ((x) - LUBBOCK_FPGA_VIRT + LUBBOCK_FPGA_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __LUB_REG(x)                (*((volatile unsigned long *)LUB_P2V(x)))
++#else
++#  define __LUB_REG(x)                LUB_P2V(x)
++#endif
++
++/* board level registers in the CPLD: (offsets from CPLD_BASE) */
++
++#define WHOAMI                        0       // card ID's (see programmers manual)
++#define HEX_LED                       0x10    // R/W access to 8 7 segment displays
++#define DISC_BLNK_LED         0x40    // R/W [15-8] enables for hex leds, [7-0] discrete LEDs
++#define CONF_SWITCHES         0x50    // RO [1] flash wrt prot, [0] 0= boot from rom, 1= flash
++#define USER_SWITCHES         0x60    // RO [15-8] dip switches, [7-0] 2 hex encoding switches
++#define MISC_WR                       0x80    // R/W various system controls -see manual
++#define MISC_RD                       0x90    // RO various system status bits -see manual
++//#define LUB_IRQ_MASK_EN             0xC0    // R/W 0= mask, 1= enable of TS, codec, ethernet, USB, SA1111, and card det. irq's
++//#define LUB_IRQ_SET_CLR             0xD0    // R/W 1= set, 0 = clear IRQ's from TS, codec, etc...
++//#define LUB_GP                      0x100   // R/W [15-0] 16 bits of general purpose I/o for hacking
++
++
++/* FPGA register physical addresses */
++#define _LUB_WHOAMI           (LUBBOCK_FPGA_PHYS + 0x000)
++#define _LUB_HEXLED           (LUBBOCK_FPGA_PHYS + 0x010)
++#define _LUB_DISC_BLNK_LED    (LUBBOCK_FPGA_PHYS + 0x040)
++#define _LUB_CONF_SWITCHES    (LUBBOCK_FPGA_PHYS + 0x050)
++#define _LUB_USER_SWITCHES    (LUBBOCK_FPGA_PHYS + 0x060)
++#define _LUB_MISC_WR          (LUBBOCK_FPGA_PHYS + 0x080)
++#define _LUB_MISC_RD          (LUBBOCK_FPGA_PHYS + 0x090)
++#define _LUB_IRQ_MASK_EN      (LUBBOCK_FPGA_PHYS + 0x0C0)
++#define _LUB_IRQ_SET_CLR      (LUBBOCK_FPGA_PHYS + 0x0D0)
++#define _LUB_GP                       (LUBBOCK_FPGA_PHYS + 0x100)
++
++/* FPGA register virtual addresses */
++#define LUB_WHOAMI            __LUB_REG(_LUB_WHOAMI)
++#define LUB_HEXLED            __LUB_REG(_LUB_HEXLED)          
++#define LUB_DISC_BLNK_LED     __LUB_REG(_LUB_DISC_BLNK_LED)   
++#define LUB_CONF_SWITCHES     __LUB_REG(_LUB_CONF_SWITCHES)   
++#define LUB_USER_SWITCHES     __LUB_REG(_LUB_USER_SWITCHES)   
++#define LUB_MISC_WR           __LUB_REG(_LUB_MISC_WR) 
++#define LUB_MISC_RD           __LUB_REG(_LUB_MISC_RD)         
++#define LUB_IRQ_MASK_EN               __LUB_REG(_LUB_IRQ_MASK_EN)
++#define LUB_IRQ_SET_CLR               __LUB_REG(_LUB_IRQ_SET_CLR)             
++#define LUB_GP                        __LUB_REG(_LUB_GP)      
++
++/* GPIOs */
++
++#define GPIO_LUBBOCK_IRQ      0
++#define IRQ_GPIO_LUBBOCK_IRQ  IRQ_GPIO0
++
++
++/*
++ * LED macros
++ */
++
++#define LEDS_BASE LUB_DISC_BLNK_LED
++
++// 8 discrete leds available for general use:
++
++#define D28   0x1
++#define D27   0x2
++#define D26   0x4
++#define D25   0x8
++#define D24   0x10
++#define D23   0x20
++#define D22   0x40
++#define D21   0x80
++
++/* Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays so
++*  be sure to not monkey with them here.
++*/
++
++#define HEARTBEAT_LED D28
++#define SYS_BUSY_LED    D27
++#define HEXLEDS_BASE LUB_HEXLED
++
++#define HEARTBEAT_LED_ON  (LEDS_BASE &= ~HEARTBEAT_LED)
++#define HEARTBEAT_LED_OFF (LEDS_BASE |= HEARTBEAT_LED)
++#define SYS_BUSY_LED_OFF  (LEDS_BASE |= SYS_BUSY_LED)
++#define SYS_BUSY_LED_ON   (LEDS_BASE &= ~SYS_BUSY_LED)
++
++// use x = D26-D21 for these, please...
++#define DISCRETE_LED_ON(x) (LEDS_BASE &= ~(x))
++#define DISCRETE_LED_OFF(x) (LEDS_BASE |= (x))
++
++#ifndef __ASSEMBLY__
++
++//extern int hexled_val = 0;
++
++#endif
++
++#define BUMP_COUNTER (HEXLEDS_BASE = hexled_val++)
++#define DEC_COUNTER (HEXLEDS_BASE = hexled_val--)
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/memory.h
+@@ -0,0 +1,106 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/memory.h
++ * 
++ * 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.
++ */
++
++#ifndef __ASM_ARCH_MEMORY_H
++#define __ASM_ARCH_MEMORY_H
++
++
++/*
++ * Task size: 3GB
++ */
++#define TASK_SIZE     (0xc0000000UL)
++#define TASK_SIZE_26  (0x04000000UL)
++
++/*
++ * This decides where the kernel will search for a free chunk of vm
++ * space during mmap's.
++ */
++#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
++
++/*
++ * Page offset: 3GB
++ */
++#define PAGE_OFFSET   (0xc0000000UL)
++
++/*
++ * Physical DRAM offset.
++ */
++#define PHYS_OFFSET   (0xa0000000UL)
++
++/*
++ * physical vs virtual ram conversion
++ */
++#define __virt_to_phys__is_a_macro
++#define __phys_to_virt__is_a_macro
++#define __virt_to_phys(x)     ((x) - PAGE_OFFSET + PHYS_OFFSET)
++#define __phys_to_virt(x)     ((x) - PHYS_OFFSET + PAGE_OFFSET)
++
++/*
++ * Virtual view <-> DMA view memory address translations
++ * virt_to_bus: Used to translate the virtual address to an
++ *            address suitable to be passed to set_dma_addr
++ * bus_to_virt: Used to convert an address for DMA operations
++ *            to an address that the kernel can use.
++ */
++#define __virt_to_bus__is_a_macro
++#define __bus_to_virt__is_a_macro
++#define __virt_to_bus(x)       __virt_to_phys(x)
++#define __bus_to_virt(x)       __phys_to_virt(x)
++
++#ifdef CONFIG_DISCONTIGMEM
++/*
++ * The nodes are matched with the physical SDRAM banks as follows:
++ *
++ *    node 0:  0xa0000000-0xa3ffffff  -->  0xc0000000-0xc3ffffff
++ *    node 1:  0xa4000000-0xa7ffffff  -->  0xc4000000-0xc7ffffff
++ *    node 2:  0xa8000000-0xabffffff  -->  0xc8000000-0xcbffffff
++ *    node 3:  0xac000000-0xafffffff  -->  0xcc000000-0xcfffffff
++ */
++
++#define NR_NODES      4
++
++/*
++ * Given a kernel address, find the home node of the underlying memory.
++ */
++#define KVADDR_TO_NID(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> 26)
++
++/*
++ * Given a page frame number, convert it to a node id.
++ */
++#define PFN_TO_NID(pfn)               (((pfn) - PHYS_PFN_OFFSET) >> (26 - PAGE_SHIFT))
++
++/*
++ * Given a kaddr, ADDR_TO_MAPBASE finds the owning node of the memory
++ * and returns the mem_map of that node.
++ */
++#define ADDR_TO_MAPBASE(kaddr)        NODE_MEM_MAP(KVADDR_TO_NID(kaddr))
++
++/*
++ * Given a page frame number, find the owning node of the memory
++ * and returns the mem_map of that node.
++ */
++#define PFN_TO_MAPBASE(pfn)   NODE_MEM_MAP(PFN_TO_NID(pfn))
++
++/*
++ * Given a kaddr, LOCAL_MEM_MAP finds the owning node of the memory
++ * and returns the index corresponding to the appropriate page in the
++ * node's mem_map.
++ */
++#define LOCAL_MAP_NR(addr) \
++      (((unsigned long)(addr) & 0x03ffffff) >> PAGE_SHIFT)
++
++#else
++
++#define PFN_TO_NID(addr)      (0)
++
++#endif
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/param.h
+@@ -0,0 +1,3 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/param.h
++ */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/pcmcia.h
+@@ -0,0 +1,65 @@
++/*
++ * linux/include/asm-arm/arch-pxa/pcmcia.h
++ *
++ * Author:    George Davis
++ * Created:   Jan 10, 2002
++ * Copyright: 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.
++ *
++ *
++ * Originally based upon linux/include/asm-arm/arch-sa1100/pcmcia.h
++ *
++ */
++
++#ifndef _ASM_ARCH_PCMCIA
++#define _ASM_ARCH_PCMCIA
++
++
++/* Ideally, we'd support up to MAX_SOCK sockets, but PXA250 only
++ * provides support for a maximum of two.
++ */
++#define PXA_PCMCIA_MAX_SOCK   (2)
++
++
++#ifndef __ASSEMBLY__
++
++struct pcmcia_init {
++  void (*handler)(int irq, void *dev, struct pt_regs *regs);
++};
++
++struct pcmcia_state {
++  unsigned detect: 1,
++            ready: 1,
++             bvd1: 1,
++             bvd2: 1,
++           wrprot: 1,
++            vs_3v: 1,
++            vs_Xv: 1;
++};
++
++struct pcmcia_state_array {
++  unsigned int size;
++  struct pcmcia_state *state;
++};
++
++struct pcmcia_irq_info {
++  unsigned int sock;
++  unsigned int irq;
++};
++
++struct pcmcia_low_level {
++  int (*init)(struct pcmcia_init *);
++  int (*shutdown)(void);
++  int (*socket_state)(struct pcmcia_state_array *);
++  int (*get_irq_info)(struct pcmcia_irq_info *);
++  int (*configure_socket)(unsigned int, socket_state_t *);
++};
++
++extern struct pcmcia_low_level *pcmcia_low_level;
++
++#endif  /* __ASSEMBLY__ */
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/pxa-regs.h
+@@ -0,0 +1,1327 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/pxa-regs.h
++ *  
++ *  Author:   Nicolas Pitre
++ *  Created:  Jun 15, 2001
++ *  Copyright:        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.
++ */
++#ifndef _PXA_REGS_H_
++#define _PXA_REGS_H_
++
++#include "bitfield.h"
++
++
++// FIXME hack so that SA-1111.h will work [cb]
++
++#ifndef __ASSEMBLY__
++typedef unsigned short  Word16 ;
++typedef unsigned int    Word32 ;
++typedef Word32          Word ;
++typedef Word            Quad [4] ;
++typedef void            *Address ;
++typedef void            (*ExcpHndlr) (void) ;
++#endif
++
++/*
++ * PXA Chip selects
++ */
++
++#define PXA_CS0_PHYS  0x00000000
++#define PXA_CS1_PHYS  0x04000000
++#define PXA_CS2_PHYS  0x08000000
++#define PXA_CS3_PHYS  0x0C000000
++#define PXA_CS4_PHYS  0x10000000
++#define PXA_CS5_PHYS  0x14000000
++
++
++/*
++ * Personal Computer Memory Card International Association (PCMCIA) sockets
++ */
++
++#define PCMCIAPrtSp   0x04000000      /* PCMCIA Partition Space [byte]   */
++#define PCMCIASp      (4*PCMCIAPrtSp) /* PCMCIA Space [byte]             */
++#define PCMCIAIOSp    PCMCIAPrtSp     /* PCMCIA I/O Space [byte]         */
++#define PCMCIAAttrSp  PCMCIAPrtSp     /* PCMCIA Attribute Space [byte]   */
++#define PCMCIAMemSp   PCMCIAPrtSp     /* PCMCIA Memory Space [byte]      */
++
++#define PCMCIA0Sp     PCMCIASp        /* PCMCIA 0 Space [byte]           */
++#define PCMCIA0IOSp   PCMCIAIOSp      /* PCMCIA 0 I/O Space [byte]       */
++#define PCMCIA0AttrSp PCMCIAAttrSp    /* PCMCIA 0 Attribute Space [byte] */
++#define PCMCIA0MemSp  PCMCIAMemSp     /* PCMCIA 0 Memory Space [byte]    */
++
++#define PCMCIA1Sp     PCMCIASp        /* PCMCIA 1 Space [byte]           */
++#define PCMCIA1IOSp   PCMCIAIOSp      /* PCMCIA 1 I/O Space [byte]       */
++#define PCMCIA1AttrSp PCMCIAAttrSp    /* PCMCIA 1 Attribute Space [byte] */
++#define PCMCIA1MemSp  PCMCIAMemSp     /* PCMCIA 1 Memory Space [byte]    */
++
++#define _PCMCIA(Nb)                   /* PCMCIA [0..1]                   */ \
++                      (0x20000000 + (Nb)*PCMCIASp)
++#define _PCMCIAIO(Nb) _PCMCIA (Nb)    /* PCMCIA I/O [0..1]               */
++#define _PCMCIAAttr(Nb)                       /* PCMCIA Attribute [0..1]         */ \
++                      (_PCMCIA (Nb) + 2*PCMCIAPrtSp)
++#define _PCMCIAMem(Nb)                        /* PCMCIA Memory [0..1]            */ \
++                      (_PCMCIA (Nb) + 3*PCMCIAPrtSp)
++
++#define _PCMCIA0      _PCMCIA (0)     /* PCMCIA 0                        */
++#define _PCMCIA0IO    _PCMCIAIO (0)   /* PCMCIA 0 I/O                    */
++#define _PCMCIA0Attr  _PCMCIAAttr (0) /* PCMCIA 0 Attribute              */
++#define _PCMCIA0Mem   _PCMCIAMem (0)  /* PCMCIA 0 Memory                 */
++
++#define _PCMCIA1      _PCMCIA (1)     /* PCMCIA 1                        */
++#define _PCMCIA1IO    _PCMCIAIO (1)   /* PCMCIA 1 I/O                    */
++#define _PCMCIA1Attr  _PCMCIAAttr (1) /* PCMCIA 1 Attribute              */
++#define _PCMCIA1Mem   _PCMCIAMem (1)  /* PCMCIA 1 Memory                 */
++
++
++
++/*
++ * DMA Controller
++ */
++
++#define DCSR0         __REG(0x40000000)  /* DMA Control / Status Register for Channel 0 */
++#define DCSR1         __REG(0x40000004)  /* DMA Control / Status Register for Channel 1 */
++#define DCSR2         __REG(0x40000008)  /* DMA Control / Status Register for Channel 2 */
++#define DCSR3         __REG(0x4000000c)  /* DMA Control / Status Register for Channel 3 */
++#define DCSR4         __REG(0x40000010)  /* DMA Control / Status Register for Channel 4 */
++#define DCSR5         __REG(0x40000014)  /* DMA Control / Status Register for Channel 5 */
++#define DCSR6         __REG(0x40000018)  /* DMA Control / Status Register for Channel 6 */
++#define DCSR7         __REG(0x4000001c)  /* DMA Control / Status Register for Channel 7 */
++#define DCSR8         __REG(0x40000020)  /* DMA Control / Status Register for Channel 8 */
++#define DCSR9         __REG(0x40000024)  /* DMA Control / Status Register for Channel 9 */
++#define DCSR10                __REG(0x40000028)  /* DMA Control / Status Register for Channel 10 */
++#define DCSR11                __REG(0x4000002c)  /* DMA Control / Status Register for Channel 11 */
++#define DCSR12                __REG(0x40000030)  /* DMA Control / Status Register for Channel 12 */
++#define DCSR13                __REG(0x40000034)  /* DMA Control / Status Register for Channel 13 */
++#define DCSR14                __REG(0x40000038)  /* DMA Control / Status Register for Channel 14 */
++#define DCSR15                __REG(0x4000003c)  /* DMA Control / Status Register for Channel 15 */
++
++#define DCSR(x)               __REG2(0x40000000, (x) << 2)
++
++#define DCSR_RUN      (1 << 31)       /* Run Bit (read / write) */
++#define DCSR_NODESC   (1 << 30)       /* No-Descriptor Fetch (read / write) */
++#define DCSR_STOPIRQEN        (1 << 29)       /* Stop Interrupt Enable (read / write) */
++#define DCSR_REQPEND  (1 << 8)        /* Request Pending (read-only) */
++#define DCSR_STOPSTATE        (1 << 3)        /* Stop State (read-only) */
++#define DCSR_ENDINTR  (1 << 2)        /* End Interrupt (read / write) */
++#define DCSR_STARTINTR        (1 << 1)        /* Start Interrupt (read / write) */
++#define DCSR_BUSERR   (1 << 0)        /* Bus Error Interrupt (read / write) */
++
++#define DINT          __REG(0x400000f0)  /* DMA Interrupt Register */
++
++#define DRCMR0                __REG(0x40000100)  /* Request to Channel Map Register for DREQ 0 */
++#define DRCMR1                __REG(0x40000104)  /* Request to Channel Map Register for DREQ 1 */
++#define DRCMR2                __REG(0x40000108)  /* Request to Channel Map Register for I2S receive Request */
++#define DRCMR3                __REG(0x4000010c)  /* Request to Channel Map Register for I2S transmit Request */
++#define DRCMR4                __REG(0x40000110)  /* Request to Channel Map Register for BTUART receive Request */
++#define DRCMR5                __REG(0x40000114)  /* Request to Channel Map Register for BTUART transmit Request. */
++#define DRCMR6                __REG(0x40000118)  /* Request to Channel Map Register for FFUART receive Request */
++#define DRCMR7                __REG(0x4000011c)  /* Request to Channel Map Register for FFUART transmit Request */
++#define DRCMR8                __REG(0x40000120)  /* Request to Channel Map Register for AC97 microphone Request */
++#define DRCMR9                __REG(0x40000124)  /* Request to Channel Map Register for AC97 modem receive Request */
++#define DRCMR10               __REG(0x40000128)  /* Request to Channel Map Register for AC97 modem transmit Request */
++#define DRCMR11               __REG(0x4000012c)  /* Request to Channel Map Register for AC97 audio receive Request */
++#define DRCMR12               __REG(0x40000130)  /* Request to Channel Map Register for AC97 audio transmit Request */
++#define DRCMR13               __REG(0x40000134)  /* Request to Channel Map Register for SSP receive Request */
++#define DRCMR14               __REG(0x40000138)  /* Request to Channel Map Register for SSP transmit Request */
++#define DRCMR15               __REG(0x4000013c)  /* Reserved */
++#define DRCMR16               __REG(0x40000140)  /* Reserved */
++#define DRCMR17               __REG(0x40000144)  /* Request to Channel Map Register for ICP receive Request */
++#define DRCMR18               __REG(0x40000148)  /* Request to Channel Map Register for ICP transmit Request */
++#define DRCMR19               __REG(0x4000014c)  /* Request to Channel Map Register for STUART receive Request */
++#define DRCMR20               __REG(0x40000150)  /* Request to Channel Map Register for STUART transmit Request */
++#define DRCMR21               __REG(0x40000154)  /* Request to Channel Map Register for MMC receive Request */
++#define DRCMR22               __REG(0x40000158)  /* Request to Channel Map Register for MMC transmit Request */
++#define DRCMR23               __REG(0x4000015c)  /* Reserved */
++#define DRCMR24               __REG(0x40000160)  /* Reserved */
++#define DRCMR25               __REG(0x40000164)  /* Request to Channel Map Register for USB endpoint 1 Request */
++#define DRCMR26               __REG(0x40000168)  /* Request to Channel Map Register for USB endpoint 2 Request */
++#define DRCMR27               __REG(0x4000016C)  /* Request to Channel Map Register for USB endpoint 3 Request */
++#define DRCMR28               __REG(0x40000170)  /* Request to Channel Map Register for USB endpoint 4 Request */
++#define DRCMR29               __REG(0x40000174)  /* Reserved */
++#define DRCMR30               __REG(0x40000178)  /* Request to Channel Map Register for USB endpoint 6 Request */
++#define DRCMR31               __REG(0x4000017C)  /* Request to Channel Map Register for USB endpoint 7 Request */
++#define DRCMR32               __REG(0x40000180)  /* Request to Channel Map Register for USB endpoint 8 Request */
++#define DRCMR33               __REG(0x40000184)  /* Request to Channel Map Register for USB endpoint 9 Request */
++#define DRCMR34               __REG(0x40000188)  /* Reserved */
++#define DRCMR35               __REG(0x4000018C)  /* Request to Channel Map Register for USB endpoint 11 Request */
++#define DRCMR36               __REG(0x40000190)  /* Request to Channel Map Register for USB endpoint 12 Request */
++#define DRCMR37               __REG(0x40000194)  /* Request to Channel Map Register for USB endpoint 13 Request */
++#define DRCMR38               __REG(0x40000198)  /* Request to Channel Map Register for USB endpoint 14 Request */
++#define DRCMR39               __REG(0x4000019C)  /* Reserved */
++
++#define DRCMRRXSADR   DRCMR2
++#define DRCMRTXSADR   DRCMR3
++#define DRCMRRXBTRBR  DRCMR4
++#define DRCMRTXBTTHR  DRCMR5
++#define DRCMRRXFFRBR  DRCMR6
++#define DRCMRTXFFTHR  DRCMR7
++#define DRCMRRXMCDR   DRCMR8
++#define DRCMRRXMODR   DRCMR9
++#define DRCMRTXMODR   DRCMR10
++#define DRCMRRXPCDR   DRCMR11
++#define DRCMRTXPCDR   DRCMR12
++#define DRCMRRXSSDR   DRCMR13
++#define DRCMRTXSSDR   DRCMR14
++#define DRCMRRXICDR   DRCMR17
++#define DRCMRTXICDR   DRCMR18
++#define DRCMRRXSTRBR  DRCMR19
++#define DRCMRTXSTTHR  DRCMR20
++#define DRCMRRXMMC    DRCMR21
++#define DRCMRTXMMC    DRCMR22
++
++#define DRCMR_MAPVLD  (1 << 7)        /* Map Valid (read / write) */
++#define DRCMR_CHLNUM  0x0f            /* mask for Channel Number (read / write) */
++
++#define DDADR0                __REG(0x40000200)  /* DMA Descriptor Address Register Channel 0 */
++#define DSADR0                __REG(0x40000204)  /* DMA Source Address Register Channel 0 */
++#define DTADR0                __REG(0x40000208)  /* DMA Target Address Register Channel 0 */
++#define DCMD0         __REG(0x4000020c)  /* DMA Command Address Register Channel 0 */
++#define DDADR1                __REG(0x40000210)  /* DMA Descriptor Address Register Channel 1 */
++#define DSADR1                __REG(0x40000214)  /* DMA Source Address Register Channel 1 */
++#define DTADR1                __REG(0x40000218)  /* DMA Target Address Register Channel 1 */
++#define DCMD1         __REG(0x4000021c)  /* DMA Command Address Register Channel 1 */
++#define DDADR2                __REG(0x40000220)  /* DMA Descriptor Address Register Channel 2 */
++#define DSADR2                __REG(0x40000224)  /* DMA Source Address Register Channel 2 */
++#define DTADR2                __REG(0x40000228)  /* DMA Target Address Register Channel 2 */
++#define DCMD2         __REG(0x4000022c)  /* DMA Command Address Register Channel 2 */
++#define DDADR3                __REG(0x40000230)  /* DMA Descriptor Address Register Channel 3 */
++#define DSADR3                __REG(0x40000234)  /* DMA Source Address Register Channel 3 */
++#define DTADR3                __REG(0x40000238)  /* DMA Target Address Register Channel 3 */
++#define DCMD3         __REG(0x4000023c)  /* DMA Command Address Register Channel 3 */
++#define DDADR4                __REG(0x40000240)  /* DMA Descriptor Address Register Channel 4 */
++#define DSADR4                __REG(0x40000244)  /* DMA Source Address Register Channel 4 */
++#define DTADR4                __REG(0x40000248)  /* DMA Target Address Register Channel 4 */
++#define DCMD4         __REG(0x4000024c)  /* DMA Command Address Register Channel 4 */
++#define DDADR5                __REG(0x40000250)  /* DMA Descriptor Address Register Channel 5 */
++#define DSADR5                __REG(0x40000254)  /* DMA Source Address Register Channel 5 */
++#define DTADR5                __REG(0x40000258)  /* DMA Target Address Register Channel 5 */
++#define DCMD5         __REG(0x4000025c)  /* DMA Command Address Register Channel 5 */
++#define DDADR6                __REG(0x40000260)  /* DMA Descriptor Address Register Channel 6 */
++#define DSADR6                __REG(0x40000264)  /* DMA Source Address Register Channel 6 */
++#define DTADR6                __REG(0x40000268)  /* DMA Target Address Register Channel 6 */
++#define DCMD6         __REG(0x4000026c)  /* DMA Command Address Register Channel 6 */
++#define DDADR7                __REG(0x40000270)  /* DMA Descriptor Address Register Channel 7 */
++#define DSADR7                __REG(0x40000274)  /* DMA Source Address Register Channel 7 */
++#define DTADR7                __REG(0x40000278)  /* DMA Target Address Register Channel 7 */
++#define DCMD7         __REG(0x4000027c)  /* DMA Command Address Register Channel 7 */
++#define DDADR8                __REG(0x40000280)  /* DMA Descriptor Address Register Channel 8 */
++#define DSADR8                __REG(0x40000284)  /* DMA Source Address Register Channel 8 */
++#define DTADR8                __REG(0x40000288)  /* DMA Target Address Register Channel 8 */
++#define DCMD8         __REG(0x4000028c)  /* DMA Command Address Register Channel 8 */
++#define DDADR9                __REG(0x40000290)  /* DMA Descriptor Address Register Channel 9 */
++#define DSADR9                __REG(0x40000294)  /* DMA Source Address Register Channel 9 */
++#define DTADR9                __REG(0x40000298)  /* DMA Target Address Register Channel 9 */
++#define DCMD9         __REG(0x4000029c)  /* DMA Command Address Register Channel 9 */
++#define DDADR10               __REG(0x400002a0)  /* DMA Descriptor Address Register Channel 10 */
++#define DSADR10               __REG(0x400002a4)  /* DMA Source Address Register Channel 10 */
++#define DTADR10               __REG(0x400002a8)  /* DMA Target Address Register Channel 10 */
++#define DCMD10                __REG(0x400002ac)  /* DMA Command Address Register Channel 10 */
++#define DDADR11               __REG(0x400002b0)  /* DMA Descriptor Address Register Channel 11 */
++#define DSADR11               __REG(0x400002b4)  /* DMA Source Address Register Channel 11 */
++#define DTADR11               __REG(0x400002b8)  /* DMA Target Address Register Channel 11 */
++#define DCMD11                __REG(0x400002bc)  /* DMA Command Address Register Channel 11 */
++#define DDADR12               __REG(0x400002c0)  /* DMA Descriptor Address Register Channel 12 */
++#define DSADR12               __REG(0x400002c4)  /* DMA Source Address Register Channel 12 */
++#define DTADR12               __REG(0x400002c8)  /* DMA Target Address Register Channel 12 */
++#define DCMD12                __REG(0x400002cc)  /* DMA Command Address Register Channel 12 */
++#define DDADR13               __REG(0x400002d0)  /* DMA Descriptor Address Register Channel 13 */
++#define DSADR13               __REG(0x400002d4)  /* DMA Source Address Register Channel 13 */
++#define DTADR13               __REG(0x400002d8)  /* DMA Target Address Register Channel 13 */
++#define DCMD13                __REG(0x400002dc)  /* DMA Command Address Register Channel 13 */
++#define DDADR14               __REG(0x400002e0)  /* DMA Descriptor Address Register Channel 14 */
++#define DSADR14               __REG(0x400002e4)  /* DMA Source Address Register Channel 14 */
++#define DTADR14               __REG(0x400002e8)  /* DMA Target Address Register Channel 14 */
++#define DCMD14                __REG(0x400002ec)  /* DMA Command Address Register Channel 14 */
++#define DDADR15               __REG(0x400002f0)  /* DMA Descriptor Address Register Channel 15 */
++#define DSADR15               __REG(0x400002f4)  /* DMA Source Address Register Channel 15 */
++#define DTADR15               __REG(0x400002f8)  /* DMA Target Address Register Channel 15 */
++#define DCMD15                __REG(0x400002fc)  /* DMA Command Address Register Channel 15 */
++
++#define DDADR(x)      __REG2(0x40000200, (x) << 4)
++#define DSADR(x)      __REG2(0x40000204, (x) << 4)
++#define DTADR(x)      __REG2(0x40000208, (x) << 4)
++#define DCMD(x)               __REG2(0x4000020c, (x) << 4)
++
++#define DDADR_DESCADDR        0xfffffff0      /* Address of next descriptor (mask) */
++#define DDADR_STOP    (1 << 0)        /* Stop (read / write) */
++
++#define DCMD_INCSRCADDR       (1 << 31)       /* Source Address Increment Setting. */
++#define DCMD_INCTRGADDR       (1 << 30)       /* Target Address Increment Setting. */
++#define DCMD_FLOWSRC  (1 << 29)       /* Flow Control by the source. */
++#define DCMD_FLOWTRG  (1 << 28)       /* Flow Control by the target. */
++#define DCMD_STARTIRQEN       (1 << 22)       /* Start Interrupt Enable */
++#define DCMD_ENDIRQEN (1 << 21)       /* End Interrupt Enable */
++#define DCMD_ENDIAN   (1 << 18)       /* Device Endian-ness. */
++#define DCMD_BURST8   (1 << 16)       /* 8 byte burst */
++#define DCMD_BURST16  (2 << 16)       /* 16 byte burst */
++#define DCMD_BURST32  (3 << 16)       /* 32 byte burst */
++#define DCMD_WIDTH1   (1 << 14)       /* 1 byte width */
++#define DCMD_WIDTH2   (2 << 14)       /* 2 byte width (HalfWord) */
++#define DCMD_WIDTH4   (3 << 14)       /* 4 byte width (Word) */
++#define DCMD_LENGTH   0x01fff         /* length mask (max = 8K - 1) */
++
++/* default combinations */
++#define DCMD_RXPCDR   (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
++#define DCMD_RXMCDR   (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
++#define DCMD_TXPCDR   (DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH4)
++
++
++/*
++ * UARTs
++ */
++
++/* Full Function UART (FFUART) */
++#define FFUART                FFRBR
++#define FFRBR         __REG(0x40100000)  /* Receive Buffer Register (read only) */
++#define FFTHR         __REG(0x40100000)  /* Transmit Holding Register (write only) */
++#define FFIER         __REG(0x40100004)  /* Interrupt Enable Register (read/write) */
++#define FFIIR         __REG(0x40100008)  /* Interrupt ID Register (read only) */
++#define FFFCR         __REG(0x40100008)  /* FIFO Control Register (write only) */
++#define FFLCR         __REG(0x4010000C)  /* Line Control Register (read/write) */
++#define FFMCR         __REG(0x40100010)  /* Modem Control Register (read/write) */
++#define FFLSR         __REG(0x40100014)  /* Line Status Register (read only) */
++#define FFMSR         __REG(0x40100018)  /* Modem Status Register (read only) */
++#define FFSPR         __REG(0x4010001C)  /* Scratch Pad Register (read/write) */
++#define FFISR         __REG(0x40100020)  /* Infrared Selection Register (read/write) */
++#define FFDLL         __REG(0x40100000)  /* Divisor Latch Low Register (DLAB = 1) (read/write) */
++#define FFDLH         __REG(0x40100004)  /* Divisor Latch High Register (DLAB = 1) (read/write) */
++
++/* Bluetooth UART (BTUART) */
++#define BTUART                BTRBR
++#define BTRBR         __REG(0x40200000)  /* Receive Buffer Register (read only) */
++#define BTTHR         __REG(0x40200000)  /* Transmit Holding Register (write only) */
++#define BTIER         __REG(0x40200004)  /* Interrupt Enable Register (read/write) */
++#define BTIIR         __REG(0x40200008)  /* Interrupt ID Register (read only) */
++#define BTFCR         __REG(0x40200008)  /* FIFO Control Register (write only) */
++#define BTLCR         __REG(0x4020000C)  /* Line Control Register (read/write) */
++#define BTMCR         __REG(0x40200010)  /* Modem Control Register (read/write) */
++#define BTLSR         __REG(0x40200014)  /* Line Status Register (read only) */
++#define BTMSR         __REG(0x40200018)  /* Modem Status Register (read only) */
++#define BTSPR         __REG(0x4020001C)  /* Scratch Pad Register (read/write) */
++#define BTISR         __REG(0x40200020)  /* Infrared Selection Register (read/write) */
++#define BTDLL         __REG(0x40200000)  /* Divisor Latch Low Register (DLAB = 1) (read/write) */
++#define BTDLH         __REG(0x40200004)  /* Divisor Latch High Register (DLAB = 1) (read/write) */
++
++/* Standard UART (STUART) */
++#define STUART                STRBR
++#define STRBR         __REG(0x40700000)  /* Receive Buffer Register (read only) */
++#define STTHR         __REG(0x40700000)  /* Transmit Holding Register (write only) */
++#define STIER         __REG(0x40700004)  /* Interrupt Enable Register (read/write) */
++#define STIIR         __REG(0x40700008)  /* Interrupt ID Register (read only) */
++#define STFCR         __REG(0x40700008)  /* FIFO Control Register (write only) */
++#define STLCR         __REG(0x4070000C)  /* Line Control Register (read/write) */
++#define STMCR         __REG(0x40700010)  /* Modem Control Register (read/write) */
++#define STLSR         __REG(0x40700014)  /* Line Status Register (read only) */
++#define STMSR         __REG(0x40700018)  /* Reserved */
++#define STSPR         __REG(0x4070001C)  /* Scratch Pad Register (read/write) */
++#define STISR         __REG(0x40700020)  /* Infrared Selection Register (read/write) */
++#define STDLL         __REG(0x40700000)  /* Divisor Latch Low Register (DLAB = 1) (read/write) */
++#define STDLH         __REG(0x40700004)  /* Divisor Latch High Register (DLAB = 1) (read/write) */
++
++#define IER_DMAE      (1 << 7)        /* DMA Requests Enable */
++#define IER_UUE               (1 << 6)        /* UART Unit Enable */
++#define IER_NRZE      (1 << 5)        /* NRZ coding Enable */
++#define IER_RTIOE     (1 << 4)        /* Receiver Time Out Interrupt Enable */
++#define IER_MIE               (1 << 3)        /* Modem Interrupt Enable */
++#define IER_RLSE      (1 << 2)        /* Receiver Line Status Interrupt Enable */
++#define IER_TIE               (1 << 1)        /* Transmit Data request Interrupt Enable */
++#define IER_RAVIE     (1 << 0)        /* Receiver Data Available Interrupt Enable */
++
++#define IIR_FIFOES1   (1 << 7)        /* FIFO Mode Enable Status */
++#define IIR_FIFOES0   (1 << 6)        /* FIFO Mode Enable Status */
++#define IIR_TOD               (1 << 3)        /* Time Out Detected */
++#define IIR_IID2      (1 << 2)        /* Interrupt Source Encoded */
++#define IIR_IID1      (1 << 1)        /* Interrupt Source Encoded */
++#define IIR_IP                (1 << 0)        /* Interrupt Pending (active low) */
++
++#define FCR_ITL2      (1 << 7)        /* Interrupt Trigger Level */
++#define FCR_ITL1      (1 << 6)        /* Interrupt Trigger Level */
++#define FCR_RESETTF   (1 << 2)        /* Reset Transmitter FIFO */
++#define FCR_RESETRF   (1 << 1)        /* Reset Receiver FIFO */
++#define FCR_TRFIFOE   (1 << 0)        /* Transmit and Receive FIFO Enable */
++#define FCR_ITL_1     (0)
++#define FCR_ITL_8     (FCR_ITL1)
++#define FCR_ITL_16    (FCR_ITL2)
++#define FCR_ITL_32    (FCR_ITL2|FCR_ITL1)
++
++#define LCR_DLAB      (1 << 7)        /* Divisor Latch Access Bit */
++#define LCR_SB                (1 << 6)        /* Set Break */
++#define LCR_STKYP     (1 << 5)        /* Sticky Parity */
++#define LCR_EPS               (1 << 4)        /* Even Parity Select */
++#define LCR_PEN               (1 << 3)        /* Parity Enable */
++#define LCR_STB               (1 << 2)        /* Stop Bit */
++#define LCR_WLS1      (1 << 1)        /* Word Length Select */
++#define LCR_WLS0      (1 << 0)        /* Word Length Select */
++
++#define LSR_FIFOE     (1 << 7)        /* FIFO Error Status */
++#define LSR_TEMT      (1 << 6)        /* Transmitter Empty */
++#define LSR_TDRQ      (1 << 5)        /* Transmit Data Request */
++#define LSR_BI                (1 << 4)        /* Break Interrupt */
++#define LSR_FE                (1 << 3)        /* Framing Error */
++#define LSR_PE                (1 << 2)        /* Parity Error */
++#define LSR_OE                (1 << 1)        /* Overrun Error */
++#define LSR_DR                (1 << 0)        /* Data Ready */
++
++#define MCR_LOOP      (1 << 4)        */ 
++#define MCR_OUT2      (1 << 3)        /* force MSR_DCD in loopback mode */
++#define MCR_OUT1      (1 << 2)        /* force MSR_RI in loopback mode */
++#define MCR_RTS               (1 << 1)        /* Request to Send */
++#define MCR_DTR               (1 << 0)        /* Data Terminal Ready */
++
++#define MSR_DCD               (1 << 7)        /* Data Carrier Detect */
++#define MSR_RI                (1 << 6)        /* Ring Indicator */
++#define MSR_DSR               (1 << 5)        /* Data Set Ready */
++#define MSR_CTS               (1 << 4)        /* Clear To Send */
++#define MSR_DDCD      (1 << 3)        /* Delta Data Carrier Detect */
++#define MSR_TERI      (1 << 2)        /* Trailing Edge Ring Indicator */
++#define MSR_DDSR      (1 << 1)        /* Delta Data Set Ready */
++#define MSR_DCTS      (1 << 0)        /* Delta Clear To Send */
++
++/*
++ * IrSR (Infrared Selection Register)
++ */
++#define STISR_RXPL      (1 << 4)        /* Receive Data Polarity */
++#define STISR_TXPL      (1 << 3)        /* Transmit Data Polarity */
++#define STISR_XMODE     (1 << 2)        /* Transmit Pulse Width Select */
++#define STISR_RCVEIR    (1 << 1)        /* Receiver SIR Enable */
++#define STISR_XMITIR    (1 << 0)        /* Transmitter SIR Enable */
++
++
++/*
++ * I2C registers
++ */
++
++#define IBMR          __REG(0x40301680)  /* I2C Bus Monitor Register - IBMR */
++#define IDBR          __REG(0x40301688)  /* I2C Data Buffer Register - IDBR */
++#define ICR           __REG(0x40301690)  /* I2C Control Register - ICR */
++#define ISR           __REG(0x40301698)  /* I2C Status Register - ISR */
++#define ISAR          __REG(0x403016A0)  /* I2C Slave Address Register - ISAR */
++
++#define ICR_START     (1 << 0)           /* start bit */
++#define ICR_STOP      (1 << 1)           /* stop bit */
++#define ICR_ACKNAK    (1 << 2)           /* send ACK(0) or NAK(1) */
++#define ICR_TB                (1 << 3)           /* transfer byte bit */
++#define ICR_MA                (1 << 4)           /* master abort */
++#define ICR_SCLE      (1 << 5)           /* master clock enable */
++#define ICR_IUE               (1 << 6)           /* unit enable */
++#define ICR_GCD               (1 << 7)           /* general call disable */
++#define ICR_ITEIE     (1 << 8)           /* enable tx interrupts */
++#define ICR_IRFIE     (1 << 9)           /* enable rx interrupts */
++#define ICR_BEIE      (1 << 10)          /* enable bus error ints */
++#define ICR_SSDIE     (1 << 11)          /* slave STOP detected int enable */
++#define ICR_ALDIE     (1 << 12)          /* enable arbitration interrupt */
++#define ICR_SADIE     (1 << 13)          /* slave address detected int enable */
++#define ICR_UR                (1 << 14)          /* unit reset */
++
++#define ISR_RWM               (1 << 0)           /* read/write mode */
++#define ISR_ACKNAK    (1 << 1)           /* ack/nak status */
++#define ISR_UB                (1 << 2)           /* unit busy */
++#define ISR_IBB               (1 << 3)           /* bus busy */       
++#define ISR_SSD               (1 << 4)           /* slave stop detected */
++#define ISR_ALD               (1 << 5)           /* arbitration loss detected */
++#define ISR_ITE               (1 << 6)           /* tx buffer empty */
++#define ISR_IRF               (1 << 7)           /* rx buffer full */
++#define ISR_GCAD      (1 << 8)           /* general call address detected */
++#define ISR_SAD               (1 << 9)           /* slave address detected */
++#define ISR_BED               (1 << 10)          /* bus error no ACK/NAK */
++
++
++/*
++ * Serial Audio Controller
++ */
++
++/* FIXME: This clash with SA1111 defines */
++#ifndef CONFIG_SA1111
++#define SACR0         __REG(0x40400000)  /* Global Control Register */
++#define SACR1         __REG(0x40400004)  /* Serial Audio I 2 S/MSB-Justified Control Register */
++#define SASR0         __REG(0x4040000C)  /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */
++#define SAIMR         __REG(0x40400014)  /* Serial Audio Interrupt Mask Register */
++#define SAICR         __REG(0x40400018)  /* Serial Audio Interrupt Clear Register */
++#define SADIV         __REG(0x40400060)  /* Audio Clock Divider Register. */
++#define SADR          __REG(0x40400080)  /* Serial Audio Data Register (TX and RX FIFO access Register). */
++#endif
++
++
++/*
++ * AC97 Controller registers
++ */
++
++#define POCR          __REG(0x40500000)  /* PCM Out Control Register */
++#define POCR_FEIE     (1 << 3)        /* FIFO Error Interrupt Enable */
++
++#define PICR          __REG(0x40500004)  /* PCM In Control Register */
++#define PICR_FEIE     (1 << 3)        /* FIFO Error Interrupt Enable */
++
++#define MCCR          __REG(0x40500008)  /* Mic In Control Register */
++#define MCCR_FEIE     (1 << 3)        /* FIFO Error Interrupt Enable */
++
++#define GCR           __REG(0x4050000C)  /* Global Control Register */
++#define GCR_CDONE_IE  (1 << 19)       /* Command Done Interrupt Enable */
++#define GCR_SDONE_IE  (1 << 18)       /* Status Done Interrupt Enable */
++#define GCR_SECRDY_IEN        (1 << 9)        /* Secondary Ready Interrupt Enable */
++#define GCR_PRIRDY_IEN        (1 << 8)        /* Primary Ready Interrupt Enable */
++#define GCR_SECRES_IEN        (1 << 5)        /* Secondary Resume Interrupt Enable */
++#define GCR_PRIRES_IEN        (1 << 4)        /* Primary Resume Interrupt Enable */
++#define GCR_ACLINK_OFF        (1 << 3)        /* AC-link Shut Off */
++#define GCR_WARM_RST  (1 << 2)        /* AC97 Warm Reset */
++#define GCR_COLD_RST  (1 << 1)        /* AC'97 Cold Reset (0 = active) */
++#define GCR_GIE               (1 << 0)        /* Codec GPI Interrupt Enable */
++
++#define POSR          __REG(0x40500010)  /* PCM Out Status Register */
++#define POSR_FIFOE    (1 << 4)        /* FIFO error */
++
++#define PISR          __REG(0x40500014)  /* PCM In Status Register */
++#define PISR_FIFOE    (1 << 4)        /* FIFO error */
++
++#define MCSR          __REG(0x40500018)  /* Mic In Status Register */
++#define MCSR_FIFOE    (1 << 4)        /* FIFO error */
++
++#define GSR           __REG(0x4050001C)  /* Global Status Register */
++#define GSR_CDONE     (1 << 19)       /* Command Done */
++#define GSR_SDONE     (1 << 18)       /* Status Done */
++#define GSR_RDCS      (1 << 15)       /* Read Completion Status */
++#define GSR_BIT3SLT12 (1 << 14)       /* Bit 3 of slot 12 */
++#define GSR_BIT2SLT12 (1 << 13)       /* Bit 2 of slot 12 */
++#define GSR_BIT1SLT12 (1 << 12)       /* Bit 1 of slot 12 */
++#define GSR_SECRES    (1 << 11)       /* Secondary Resume Interrupt */
++#define GSR_PRIRES    (1 << 10)       /* Primary Resume Interrupt */
++#define GSR_SCR               (1 << 9)        /* Secondary Codec Ready */
++#define GSR_PCR               (1 << 8)        /*  Primary Codec Ready */
++#define GSR_MINT      (1 << 7)        /* Mic In Interrupt */
++#define GSR_POINT     (1 << 6)        /* PCM Out Interrupt */
++#define GSR_PIINT     (1 << 5)        /* PCM In Interrupt */
++#define GSR_MOINT     (1 << 2)        /* Modem Out Interrupt */
++#define GSR_MIINT     (1 << 1)        /* Modem In Interrupt */
++#define GSR_GSCI      (1 << 0)        /* Codec GPI Status Change Interrupt */
++
++#define CAR           __REG(0x40500020)  /* CODEC Access Register */
++#define CAR_CAIP      (1 << 0)        /* Codec Access In Progress */
++
++#define PCDR          __REG(0x40500040)  /* PCM FIFO Data Register */
++#define MCDR          __REG(0x40500060)  /* Mic-in FIFO Data Register */
++
++#define MOCR          __REG(0x40500100)  /* Modem Out Control Register */
++#define MOCR_FEIE     (1 << 3)        /* FIFO Error */
++
++#define MICR          __REG(0x40500108)  /* Modem In Control Register */
++#define MICR_FEIE     (1 << 3)        /* FIFO Error */
++
++#define MOSR          __REG(0x40500110)  /* Modem Out Status Register */
++#define MOSR_FIFOE    (1 << 4)        /* FIFO error */
++
++#define MISR          __REG(0x40500118)  /* Modem In Status Register */
++#define MISR_FIFOE    (1 << 4)        /* FIFO error */
++
++#define MODR          __REG(0x40500140)  /* Modem FIFO Data Register */
++
++#define PAC_REG_BASE  __REG(0x40500200)  /* Primary Audio Codec */
++#define SAC_REG_BASE  __REG(0x40500300)  /* Secondary Audio Codec */
++#define PMC_REG_BASE  __REG(0x40500400)  /* Primary Modem Codec */
++#define SMC_REG_BASE  __REG(0x40500500)  /* Secondary Modem Codec */
++
++
++/*
++ * USB Device Controller
++ */
++#define UDC_RES1      __REG(0x40600004)  /* UDC Undocumented - Reserved1 */
++#define UDC_RES2      __REG(0x40600008)  /* UDC Undocumented - Reserved2 */
++#define UDC_RES3      __REG(0x4060000C)  /* UDC Undocumented - Reserved3 */
++
++#define UDCCR         __REG(0x40600000)  /* UDC Control Register */
++#define UDCCR_UDE     (1 << 0)        /* UDC enable */
++#define UDCCR_UDA     (1 << 1)        /* UDC active */
++#define UDCCR_RSM     (1 << 2)        /* Device resume */
++#define UDCCR_RESIR   (1 << 3)        /* Resume interrupt request */
++#define UDCCR_SUSIR   (1 << 4)        /* Suspend interrupt request */
++#define UDCCR_SRM     (1 << 5)        /* Suspend/resume interrupt mask */
++#define UDCCR_RSTIR   (1 << 6)        /* Reset interrupt request */
++#define UDCCR_REM     (1 << 7)        /* Reset interrupt mask */
++
++#define UDCCS0                __REG(0x40600010)  /* UDC Endpoint 0 Control/Status Register */
++#define UDCCS0_OPR    (1 << 0)        /* OUT packet ready */
++#define UDCCS0_IPR    (1 << 1)        /* IN packet ready */
++#define UDCCS0_FTF    (1 << 2)        /* Flush Tx FIFO */
++#define UDCCS0_DRWF   (1 << 3)        /* Device remote wakeup feature */
++#define UDCCS0_SST    (1 << 4)        /* Sent stall */
++#define UDCCS0_FST    (1 << 5)        /* Force stall */
++#define UDCCS0_RNE    (1 << 6)        /* Receive FIFO no empty */
++#define UDCCS0_SA     (1 << 7)        /* Setup active */
++
++/* Bulk IN - Endpoint 1,6,11 */
++#define UDCCS1                __REG(0x40600014)  /* UDC Endpoint 1 (IN) Control/Status Register */
++#define UDCCS6                __REG(0x40600028)  /* UDC Endpoint 6 (IN) Control/Status Register */
++#define UDCCS11               __REG(0x4060003C)  /* UDC Endpoint 11 (IN) Control/Status Register */
++
++#define UDCCS_BI_TFS  (1 << 0)        /* Transmit FIFO service */
++#define UDCCS_BI_TPC  (1 << 1)        /* Transmit packet complete */
++#define UDCCS_BI_FTF  (1 << 2)        /* Flush Tx FIFO */
++#define UDCCS_BI_TUR  (1 << 3)        /* Transmit FIFO underrun */
++#define UDCCS_BI_SST  (1 << 4)        /* Sent stall */
++#define UDCCS_BI_FST  (1 << 5)        /* Force stall */
++#define UDCCS_BI_TSP  (1 << 7)        /* Transmit short packet */
++
++/* Bulk OUT - Endpoint 2,7,12 */
++#define UDCCS2                __REG(0x40600018)  /* UDC Endpoint 2 (OUT) Control/Status Register */
++#define UDCCS7                __REG(0x4060002C)  /* UDC Endpoint 7 (OUT) Control/Status Register */
++#define UDCCS12               __REG(0x40600040)  /* UDC Endpoint 12 (OUT) Control/Status Register */
++
++#define UDCCS_BO_RFS  (1 << 0)        /* Receive FIFO service */
++#define UDCCS_BO_RPC  (1 << 1)        /* Receive packet complete */
++#define UDCCS_BO_DME  (1 << 3)        /* DMA enable */
++#define UDCCS_BO_SST  (1 << 4)        /* Sent stall */
++#define UDCCS_BO_FST  (1 << 5)        /* Force stall */
++#define UDCCS_BO_RNE  (1 << 6)        /* Receive FIFO not empty */
++#define UDCCS_BO_RSP  (1 << 7)        /* Receive short packet */
++
++/* Isochronous IN - Endpoint 3,8,13 */
++#define UDCCS3                __REG(0x4060001C)  /* UDC Endpoint 3 (IN) Control/Status Register */
++#define UDCCS8                __REG(0x40600030)  /* UDC Endpoint 8 (IN) Control/Status Register */
++#define UDCCS13               __REG(0x40600044)  /* UDC Endpoint 13 (IN) Control/Status Register */
++
++#define UDCCS_II_TFS  (1 << 0)        /* Transmit FIFO service */
++#define UDCCS_II_TPC  (1 << 1)        /* Transmit packet complete */
++#define UDCCS_II_FTF  (1 << 2)        /* Flush Tx FIFO */
++#define UDCCS_II_TUR  (1 << 3)        /* Transmit FIFO underrun */
++#define UDCCS_II_TSP  (1 << 7)        /* Transmit short packet */
++
++/* Isochronous OUT - Endpoint 4,9,14 */
++#define UDCCS4                __REG(0x40600020)  /* UDC Endpoint 4 (OUT) Control/Status Register */
++#define UDCCS9                __REG(0x40600034)  /* UDC Endpoint 9 (OUT) Control/Status Register */
++#define UDCCS14               __REG(0x40600048)  /* UDC Endpoint 14 (OUT) Control/Status Register */
++
++#define UDCCS_IO_RFS  (1 << 0)        /* Receive FIFO service */
++#define UDCCS_IO_RPC  (1 << 1)        /* Receive packet complete */
++#define UDCCS_IO_ROF  (1 << 3)        /* Receive overflow */
++#define UDCCS_IO_DME  (1 << 3)        /* DMA enable */
++#define UDCCS_IO_RNE  (1 << 6)        /* Receive FIFO not empty */
++#define UDCCS_IO_RSP  (1 << 7)        /* Receive short packet */
++
++/* Interrupt IN - Endpoint 5,10,15 */
++#define UDCCS5                __REG(0x40600024)  /* UDC Endpoint 5 (Interrupt) Control/Status Register */
++#define UDCCS10               __REG(0x40600038)  /* UDC Endpoint 10 (Interrupt) Control/Status Register */
++#define UDCCS15               __REG(0x4060004C)  /* UDC Endpoint 15 (Interrupt) Control/Status Register */
++
++#define UDCCS_INT_TFS (1 << 0)        /* Transmit FIFO service */
++#define UDCCS_INT_TPC (1 << 1)        /* Transmit packet complete */
++#define UDCCS_INT_FTF (1 << 2)        /* Flush Tx FIFO */
++#define UDCCS_INT_TUR (1 << 3)        /* Transmit FIFO underrun */
++#define UDCCS_INT_SST (1 << 4)        /* Sent stall */
++#define UDCCS_INT_FST (1 << 5)        /* Force stall */
++#define UDCCS_INT_TSP (1 << 7)        /* Transmit short packet */
++
++#define UFNRH         __REG(0x40600060)  /* UDC Frame Number Register High */
++#define UFNRL         __REG(0x40600064)  /* UDC Frame Number Register Low */
++#define UBCR2         __REG(0x40600068)  /* UDC Byte Count Reg 2 */
++#define UBCR4         __REG(0x4060006c)  /* UDC Byte Count Reg 4 */
++#define UBCR7         __REG(0x40600070)  /* UDC Byte Count Reg 7 */
++#define UBCR9         __REG(0x40600074)  /* UDC Byte Count Reg 9 */
++#define UBCR12                __REG(0x40600078)  /* UDC Byte Count Reg 12 */
++#define UBCR14                __REG(0x4060007c)  /* UDC Byte Count Reg 14 */
++#define UDDR0         __REG(0x40600080)  /* UDC Endpoint 0 Data Register */
++#define UDDR1         __REG(0x40600100)  /* UDC Endpoint 1 Data Register */
++#define UDDR2         __REG(0x40600180)  /* UDC Endpoint 2 Data Register */
++#define UDDR3         __REG(0x40600200)  /* UDC Endpoint 3 Data Register */
++#define UDDR4         __REG(0x40600400)  /* UDC Endpoint 4 Data Register */
++#define UDDR5         __REG(0x406000A0)  /* UDC Endpoint 5 Data Register */
++#define UDDR6         __REG(0x40600600)  /* UDC Endpoint 6 Data Register */
++#define UDDR7         __REG(0x40600680)  /* UDC Endpoint 7 Data Register */
++#define UDDR8         __REG(0x40600700)  /* UDC Endpoint 8 Data Register */
++#define UDDR9         __REG(0x40600900)  /* UDC Endpoint 9 Data Register */
++#define UDDR10                __REG(0x406000C0)  /* UDC Endpoint 10 Data Register */
++#define UDDR11                __REG(0x40600B00)  /* UDC Endpoint 11 Data Register */
++#define UDDR12                __REG(0x40600B80)  /* UDC Endpoint 12 Data Register */
++#define UDDR13                __REG(0x40600C00)  /* UDC Endpoint 13 Data Register */
++#define UDDR14                __REG(0x40600E00)  /* UDC Endpoint 14 Data Register */
++#define UDDR15                __REG(0x406000E0)  /* UDC Endpoint 15 Data Register */
++
++#define UICR0         __REG(0x40600050)  /* UDC Interrupt Control Register 0 */
++
++#define UICR0_IM0     (1 << 0)        /* Interrupt mask ep 0 */
++#define UICR0_IM1     (1 << 1)        /* Interrupt mask ep 1 */
++#define UICR0_IM2     (1 << 2)        /* Interrupt mask ep 2 */
++#define UICR0_IM3     (1 << 3)        /* Interrupt mask ep 3 */
++#define UICR0_IM4     (1 << 4)        /* Interrupt mask ep 4 */
++#define UICR0_IM5     (1 << 5)        /* Interrupt mask ep 5 */
++#define UICR0_IM6     (1 << 6)        /* Interrupt mask ep 6 */
++#define UICR0_IM7     (1 << 7)        /* Interrupt mask ep 7 */
++
++#define UICR1         __REG(0x40600054)  /* UDC Interrupt Control Register 1 */
++
++#define UICR1_IM8     (1 << 0)        /* Interrupt mask ep 8 */
++#define UICR1_IM9     (1 << 1)        /* Interrupt mask ep 9 */
++#define UICR1_IM10    (1 << 2)        /* Interrupt mask ep 10 */
++#define UICR1_IM11    (1 << 3)        /* Interrupt mask ep 11 */
++#define UICR1_IM12    (1 << 4)        /* Interrupt mask ep 12 */
++#define UICR1_IM13    (1 << 5)        /* Interrupt mask ep 13 */
++#define UICR1_IM14    (1 << 6)        /* Interrupt mask ep 14 */
++#define UICR1_IM15    (1 << 7)        /* Interrupt mask ep 15 */
++
++#define USIR0         __REG(0x40600058)  /* UDC Status Interrupt Register 0 */
++
++#define USIR0_IR0     (1 << 0)        /* Interrup request ep 0 */
++#define USIR0_IR1     (1 << 1)        /* Interrup request ep 1 */
++#define USIR0_IR2     (1 << 2)        /* Interrup request ep 2 */
++#define USIR0_IR3     (1 << 3)        /* Interrup request ep 3 */
++#define USIR0_IR4     (1 << 4)        /* Interrup request ep 4 */
++#define USIR0_IR5     (1 << 5)        /* Interrup request ep 5 */
++#define USIR0_IR6     (1 << 6)        /* Interrup request ep 6 */
++#define USIR0_IR7     (1 << 7)        /* Interrup request ep 7 */
++
++#define USIR1         __REG(0x4060005C)  /* UDC Status Interrupt Register 1 */
++
++#define USIR1_IR8     (1 << 0)        /* Interrup request ep 8 */
++#define USIR1_IR9     (1 << 1)        /* Interrup request ep 9 */
++#define USIR1_IR10    (1 << 2)        /* Interrup request ep 10 */
++#define USIR1_IR11    (1 << 3)        /* Interrup request ep 11 */
++#define USIR1_IR12    (1 << 4)        /* Interrup request ep 12 */
++#define USIR1_IR13    (1 << 5)        /* Interrup request ep 13 */
++#define USIR1_IR14    (1 << 6)        /* Interrup request ep 14 */
++#define USIR1_IR15    (1 << 7)        /* Interrup request ep 15 */
++
++
++/*
++ * Fast Infrared Communication Port
++ */
++
++#define ICCR0         __REG(0x40800000)  /* ICP Control Register 0 */
++#define ICCR1         __REG(0x40800004)  /* ICP Control Register 1 */
++#define ICCR2         __REG(0x40800008)  /* ICP Control Register 2 */
++#define ICDR          __REG(0x4080000c)  /* ICP Data Register */
++#define ICSR0         __REG(0x40800014)  /* ICP Status Register 0 */
++#define ICSR1         __REG(0x40800018)  /* ICP Status Register 1 */
++
++#define ICCR0_AME       (1 << 7)           /* Adress match enable */
++#define ICCR0_TIE       (1 << 6)           /* Transmit FIFO interrupt enable */ 
++#define ICCR0_RIE       (1 << 5)           /* Recieve FIFO interrupt enable */
++#define ICCR0_RXE       (1 << 4)           /* Receive enable */
++#define ICCR0_TXE       (1 << 3)           /* Transmit enable */
++#define ICCR0_TUS       (1 << 2)           /* Transmit FIFO underrun select */
++#define ICCR0_LBM       (1 << 1)           /* Loopback mode */
++#define ICCR0_ITR       (1 << 0)           /* IrDA transmission */
++
++#define ICSR0_FRE       (1 << 5)           /* Framing error */
++#define ICSR0_RFS       (1 << 4)           /* Receive FIFO service request */
++#define ICSR0_TFS       (1 << 3)           /* Transnit FIFO service request */
++#define ICSR0_RAB       (1 << 2)           /* Receiver abort */
++#define ICSR0_TUR       (1 << 1)           /* Trunsmit FIFO underun */
++#define ICSR0_EIF       (1 << 0)           /* End/Error in FIFO */
++
++#define ICSR1_ROR       (1 << 6)           /* Receiver FIFO underrun  */
++#define ICSR1_CRE       (1 << 5)           /* CRC error */
++#define ICSR1_EOF       (1 << 4)           /* End of frame */
++#define ICSR1_TNF       (1 << 3)           /* Transmit FIFO not full */
++#define ICSR1_RNE       (1 << 2)           /* Receive FIFO not empty */
++#define ICSR1_TBY       (1 << 1)           /* Tramsmiter busy flag */
++#define ICSR1_RSY       (1 << 0)           /* Recevier synchronized flag */
++
++
++/*
++ * Real Time Clock
++ */
++
++#define RCNR          __REG(0x40900000)  /* RTC Count Register */
++#define RTAR          __REG(0x40900004)  /* RTC Alarm Register */
++#define RTSR          __REG(0x40900008)  /* RTC Status Register */
++#define RTTR          __REG(0x4090000C)  /* RTC Timer Trim Register */
++
++#define RTSR_HZE      (1 << 3)        /* HZ interrupt enable */
++#define RTSR_ALE      (1 << 2)        /* RTC alarm interrupt enable */
++#define RTSR_HZ               (1 << 1)        /* HZ rising-edge detected */
++#define RTSR_AL               (1 << 0)        /* RTC alarm detected */
++
++
++/*
++ * OS Timer & Match Registers
++ */
++
++#define OSMR0         __REG(0x40A00000)  /* */
++#define OSMR1         __REG(0x40A00004)  /* */
++#define OSMR2         __REG(0x40A00008)  /* */
++#define OSMR3         __REG(0x40A0000C)  /* */
++#define OSCR          __REG(0x40A00010)  /* OS Timer Counter Register */
++#define OSSR          __REG(0x40A00014)  /* OS Timer Status Register */
++#define OWER          __REG(0x40A00018)  /* OS Timer Watchdog Enable Register */
++#define OIER          __REG(0x40A0001C)  /* OS Timer Interrupt Enable Register */
++
++#define OSSR_M3               (1 << 3)        /* Match status channel 3 */
++#define OSSR_M2               (1 << 2)        /* Match status channel 2 */
++#define OSSR_M1               (1 << 1)        /* Match status channel 1 */
++#define OSSR_M0               (1 << 0)        /* Match status channel 0 */
++
++#define OWER_WME      (1 << 0)        /* Watchdog Match Enable */
++
++#define OIER_E3               (1 << 3)        /* Interrupt enable channel 3 */
++#define OIER_E2               (1 << 2)        /* Interrupt enable channel 2 */
++#define OIER_E1               (1 << 1)        /* Interrupt enable channel 1 */
++#define OIER_E0               (1 << 0)        /* Interrupt enable channel 0 */
++
++
++/*
++ * Pulse Width Modulator
++ */
++
++#define PWM_CTRL0     __REG(0x40B00000)  /* PWM 0 Control Register */
++#define PWM_PWDUTY0   __REG(0x40B00004)  /* PWM 0 Duty Cycle Register */
++#define PWM_PERVAL0   __REG(0x40B00008)  /* PWM 0 Period Control Register */
++
++#define PWM_CTRL1     __REG(0x40C00000)  /* PWM 1Control Register */
++#define PWM_PWDUTY1   __REG(0x40C00004)  /* PWM 1 Duty Cycle Register */
++#define PWM_PERVAL1   __REG(0x40C00008)  /* PWM 1 Period Control Register */
++
++
++/*
++ * Interrupt Controller
++ */
++
++#define ICIP          __REG(0x40D00000)  /* Interrupt Controller IRQ Pending Register */
++#define ICMR          __REG(0x40D00004)  /* Interrupt Controller Mask Register */
++#define ICLR          __REG(0x40D00008)  /* Interrupt Controller Level Register */
++#define ICFP          __REG(0x40D0000C)  /* Interrupt Controller FIQ Pending Register */
++#define ICPR          __REG(0x40D00010)  /* Interrupt Controller Pending Register */
++#define ICCR          __REG(0x40D00014)  /* Interrupt Controller Control Register */
++
++
++/*
++ * General Purpose I/O
++ */
++
++#define GPLR0         __REG(0x40E00000)  /* GPIO Pin-Level Register GPIO<31:0> */
++#define GPLR1         __REG(0x40E00004)  /* GPIO Pin-Level Register GPIO<63:32> */
++#define GPLR2         __REG(0x40E00008)  /* GPIO Pin-Level Register GPIO<80:64> */
++
++#define GPDR0         __REG(0x40E0000C)  /* GPIO Pin Direction Register GPIO<31:0> */
++#define GPDR1         __REG(0x40E00010)  /* GPIO Pin Direction Register GPIO<63:32> */
++#define GPDR2         __REG(0x40E00014)  /* GPIO Pin Direction Register GPIO<80:64> */
++
++#define GPSR0         __REG(0x40E00018)  /* GPIO Pin Output Set Register GPIO<31:0> */
++#define GPSR1         __REG(0x40E0001C)  /* GPIO Pin Output Set Register GPIO<63:32> */
++#define GPSR2         __REG(0x40E00020)  /* GPIO Pin Output Set Register GPIO<80:64> */
++
++#define GPCR0         __REG(0x40E00024)  /* GPIO Pin Output Clear Register GPIO<31:0> */
++#define GPCR1         __REG(0x40E00028)  /* GPIO Pin Output Clear Register GPIO <63:32> */
++#define GPCR2         __REG(0x40E0002C)  /* GPIO Pin Output Clear Register GPIO <80:64> */
++
++#define GRER0         __REG(0x40E00030)  /* GPIO Rising-Edge Detect Register GPIO<31:0> */
++#define GRER1         __REG(0x40E00034)  /* GPIO Rising-Edge Detect Register GPIO<63:32> */
++#define GRER2         __REG(0x40E00038)  /* GPIO Rising-Edge Detect Register GPIO<80:64> */
++
++#define GFER0         __REG(0x40E0003C)  /* GPIO Falling-Edge Detect Register GPIO<31:0> */
++#define GFER1         __REG(0x40E00040)  /* GPIO Falling-Edge Detect Register GPIO<63:32> */
++#define GFER2         __REG(0x40E00044)  /* GPIO Falling-Edge Detect Register GPIO<80:64> */
++
++#define GEDR0         __REG(0x40E00048)  /* GPIO Edge Detect Status Register GPIO<31:0> */
++#define GEDR1         __REG(0x40E0004C)  /* GPIO Edge Detect Status Register GPIO<63:32> */
++#define GEDR2         __REG(0x40E00050)  /* GPIO Edge Detect Status Register GPIO<80:64> */
++
++#define GAFR0_L               __REG(0x40E00054)  /* GPIO Alternate Function Select Register GPIO<15:0> */
++#define GAFR0_U               __REG(0x40E00058)  /* GPIO Alternate Function Select Register GPIO<31:16> */
++#define GAFR1_L               __REG(0x40E0005C)  /* GPIO Alternate Function Select Register GPIO<47:32> */
++#define GAFR1_U               __REG(0x40E00060)  /* GPIO Alternate Function Select Register GPIO<63:48> */
++#define GAFR2_L               __REG(0x40E00064)  /* GPIO Alternate Function Select Register GPIO<79:64> */
++#define GAFR2_U               __REG(0x40E00068)  /* GPIO Alternate Function Select Register GPIO 80 */
++
++/* More handy macros.  The argument is a literal GPIO number. */
++
++#define GPIO_bit(x)   (1 << ((x) & 0x1f))
++#define GPLR(x)               __REG2(0x40E00000, ((x) & 0x60) >> 3)
++#define GPDR(x)               __REG2(0x40E0000C, ((x) & 0x60) >> 3)
++#define GPSR(x)               __REG2(0x40E00018, ((x) & 0x60) >> 3)
++#define GPCR(x)               __REG2(0x40E00024, ((x) & 0x60) >> 3)
++#define GRER(x)               __REG2(0x40E00030, ((x) & 0x60) >> 3)
++#define GFER(x)               __REG2(0x40E0003C, ((x) & 0x60) >> 3)
++#define GEDR(x)               __REG2(0x40E00048, ((x) & 0x60) >> 3)
++#define GAFR(x)               __REG2(0x40E00054, ((x) & 0x70) >> 2)
++
++/* GPIO alternate function assignments */
++
++#define GPIO1_RST             1       /* reset */
++#define GPIO6_MMCCLK          6       /* MMC Clock */
++#define GPIO8_48MHz           7       /* 48 MHz clock output */
++#define GPIO8_MMCCS0          8       /* MMC Chip Select 0 */
++#define GPIO9_MMCCS1          9       /* MMC Chip Select 1 */
++#define GPIO10_RTCCLK         10      /* real time clock (1 Hz) */
++#define GPIO11_3_6MHz         11      /* 3.6 MHz oscillator out */
++#define GPIO12_32KHz          12      /* 32 kHz out */
++#define GPIO13_MBGNT          13      /* memory controller grant */
++#define GPIO14_MBREQ          14      /* alternate bus master request */
++#define GPIO15_nCS_1          15      /* chip select 1 */
++#define GPIO16_PWM0           16      /* PWM0 output */
++#define GPIO17_PWM1           17      /* PWM1 output */
++#define GPIO18_RDY            18      /* Ext. Bus Ready */
++#define GPIO19_DREQ1          19      /* External DMA Request */
++#define GPIO20_DREQ0          20      /* External DMA Request */
++#define GPIO23_SCLK           23      /* SSP clock */
++#define GPIO24_SFRM           24      /* SSP Frame */
++#define GPIO25_STXD           25      /* SSP transmit */
++#define GPIO26_SRXD           26      /* SSP receive */
++#define GPIO27_SEXTCLK                27      /* SSP ext_clk */
++#define GPIO28_BITCLK         28      /* AC97/I2S bit_clk */
++#define GPIO29_SDATA_IN               29      /* AC97 Sdata_in0 / I2S Sdata_in */
++#define GPIO30_SDATA_OUT      30      /* AC97/I2S Sdata_out */
++#define GPIO31_SYNC           31      /* AC97/I2S sync */
++#define GPIO32_SDATA_IN1      32      /* AC97 Sdata_in1 */
++#define GPIO33_nCS_5          33      /* chip select 5 */
++#define GPIO34_FFRXD          34      /* FFUART receive */
++#define GPIO34_MMCCS0         34      /* MMC Chip Select 0 */
++#define GPIO35_FFCTS          35      /* FFUART Clear to send */
++#define GPIO36_FFDCD          36      /* FFUART Data carrier detect */
++#define GPIO37_FFDSR          37      /* FFUART data set ready */
++#define GPIO38_FFRI           38      /* FFUART Ring Indicator */
++#define GPIO39_MMCCS1         39      /* MMC Chip Select 1 */
++#define GPIO39_FFTXD          39      /* FFUART transmit data */
++#define GPIO40_FFDTR          40      /* FFUART data terminal Ready */
++#define GPIO41_FFRTS          41      /* FFUART request to send */
++#define GPIO42_BTRXD          42      /* BTUART receive data */
++#define GPIO43_BTTXD          43      /* BTUART transmit data */
++#define GPIO44_BTCTS          44      /* BTUART clear to send */
++#define GPIO45_BTRTS          45      /* BTUART request to send */
++#define GPIO46_ICPRXD         46      /* ICP receive data */
++#define GPIO46_STRXD          46      /* STD_UART receive data */
++#define GPIO47_ICPTXD         47      /* ICP transmit data */
++#define GPIO47_STTXD          47      /* STD_UART transmit data */
++#define GPIO48_nPOE           48      /* Output Enable for Card Space */
++#define GPIO49_nPWE           49      /* Write Enable for Card Space */
++#define GPIO50_nPIOR          50      /* I/O Read for Card Space */
++#define GPIO51_nPIOW          51      /* I/O Write for Card Space */
++#define GPIO52_nPCE_1         52      /* Card Enable for Card Space */
++#define GPIO53_nPCE_2         53      /* Card Enable for Card Space */
++#define GPIO53_MMCCLK         53      /* MMC Clock */
++#define GPIO54_MMCCLK         54      /* MMC Clock */
++#define GPIO54_pSKTSEL                54      /* Socket Select for Card Space */
++#define GPIO55_nPREG          55      /* Card Address bit 26 */
++#define GPIO56_nPWAIT         56      /* Wait signal for Card Space */
++#define GPIO57_nIOIS16                57      /* Bus Width select for I/O Card Space */
++#define GPIO58_LDD_0          58      /* LCD data pin 0 */
++#define GPIO59_LDD_1          59      /* LCD data pin 1 */
++#define GPIO60_LDD_2          60      /* LCD data pin 2 */
++#define GPIO61_LDD_3          61      /* LCD data pin 3 */
++#define GPIO62_LDD_4          62      /* LCD data pin 4 */
++#define GPIO63_LDD_5          63      /* LCD data pin 5 */
++#define GPIO64_LDD_6          64      /* LCD data pin 6 */
++#define GPIO65_LDD_7          65      /* LCD data pin 7 */
++#define GPIO66_LDD_8          66      /* LCD data pin 8 */
++#define GPIO66_MBREQ          66      /* alternate bus master req */
++#define GPIO67_LDD_9          67      /* LCD data pin 9 */
++#define GPIO67_MMCCS0         67      /* MMC Chip Select 0 */
++#define GPIO68_LDD_10         68      /* LCD data pin 10 */
++#define GPIO68_MMCCS1         68      /* MMC Chip Select 1 */
++#define GPIO69_LDD_11         69      /* LCD data pin 11 */
++#define GPIO69_MMCCLK         69      /* MMC_CLK */
++#define GPIO70_LDD_12         70      /* LCD data pin 12 */
++#define GPIO70_RTCCLK         70      /* Real Time clock (1 Hz) */
++#define GPIO71_LDD_13         71      /* LCD data pin 13 */
++#define GPIO71_3_6MHz         71      /* 3.6 MHz Oscillator clock */
++#define GPIO72_LDD_14         72      /* LCD data pin 14 */
++#define GPIO72_32kHz          72      /* 32 kHz clock */
++#define GPIO73_LDD_15         73      /* LCD data pin 15 */
++#define GPIO73_MBGNT          73      /* Memory controller grant */
++#define GPIO74_LCD_FCLK               74      /* LCD Frame clock */
++#define GPIO75_LCD_LCLK               75      /* LCD line clock */
++#define GPIO76_LCD_PCLK               76      /* LCD Pixel clock */
++#define GPIO77_LCD_ACBIAS     77      /* LCD AC Bias */
++#define GPIO78_nCS_2          78      /* chip select 2 */
++#define GPIO79_nCS_3          79      /* chip select 3 */
++#define GPIO80_nCS_4          80      /* chip select 4 */
++
++/* GPIO alternate function mode & direction */
++
++#define GPIO_IN                       0x000
++#define GPIO_OUT              0x080
++#define GPIO_ALT_FN_1_IN      0x100
++#define GPIO_ALT_FN_1_OUT     0x180
++#define GPIO_ALT_FN_2_IN      0x200
++#define GPIO_ALT_FN_2_OUT     0x280
++#define GPIO_ALT_FN_3_IN      0x300
++#define GPIO_ALT_FN_3_OUT     0x380
++#define GPIO_MD_MASK_NR               0x07f
++#define GPIO_MD_MASK_DIR      0x080
++#define GPIO_MD_MASK_FN               0x300
++
++#define GPIO1_RTS_MD          ( 1 | GPIO_ALT_FN_1_IN)
++#define GPIO6_MMCCLK_MD               ( 6 | GPIO_ALT_FN_1_OUT)
++#define GPIO8_48MHz_MD                ( 8 | GPIO_ALT_FN_1_OUT)
++#define GPIO8_MMCCS0_MD               ( 8 | GPIO_ALT_FN_1_OUT)
++#define GPIO9_MMCCS1_MD               ( 9 | GPIO_ALT_FN_1_OUT)
++#define GPIO10_RTCCLK_MD      (10 | GPIO_ALT_FN_1_OUT)
++#define GPIO11_3_6MHz_MD      (11 | GPIO_ALT_FN_1_OUT)
++#define GPIO12_32KHz_MD               (12 | GPIO_ALT_FN_1_OUT)
++#define GPIO13_MBGNT_MD               (13 | GPIO_ALT_FN_2_OUT)
++#define GPIO14_MBREQ_MD               (14 | GPIO_ALT_FN_1_IN)
++#define GPIO15_nCS_1_MD               (15 | GPIO_ALT_FN_2_OUT)
++#define GPIO16_PWM0_MD                (16 | GPIO_ALT_FN_2_OUT)
++#define GPIO17_PWM1_MD                (17 | GPIO_ALT_FN_2_OUT)
++#define GPIO18_RDY_MD         (18 | GPIO_ALT_FN_1_IN)
++#define GPIO19_DREQ1_MD               (19 | GPIO_ALT_FN_1_IN)
++#define GPIO20_DREQ0_MD               (20 | GPIO_ALT_FN_1_IN)
++#define GPIO23_SCLK_md                (23 | GPIO_ALT_FN_2_OUT)
++#define GPIO24_SFRM_MD                (24 | GPIO_ALT_FN_2_OUT)
++#define GPIO25_STXD_MD                (25 | GPIO_ALT_FN_2_OUT)
++#define GPIO26_SRXD_MD                (26 | GPIO_ALT_FN_1_IN)
++#define GPIO27_SEXTCLK_MD     (27 | GPIO_ALT_FN_1_IN)
++#define GPIO28_BITCLK_AC97_MD (28 | GPIO_ALT_FN_1_IN)
++#define GPIO28_BITCLK_I2S_MD  (28 | GPIO_ALT_FN_2_IN)
++#define GPIO29_SDATA_IN_AC97_MD       (29 | GPIO_ALT_FN_1_IN)
++#define GPIO29_SDATA_IN_I2S_MD        (29 | GPIO_ALT_FN_2_IN)
++#define GPIO30_SDATA_OUT_AC97_MD      (30 | GPIO_ALT_FN_2_OUT)
++#define GPIO30_SDATA_OUT_I2S_MD       (30 | GPIO_ALT_FN_1_OUT)
++#define GPIO31_SYNC_AC97_MD   (31 | GPIO_ALT_FN_2_OUT)
++#define GPIO31_SYNC_I2S_MD    (31 | GPIO_ALT_FN_1_OUT)
++#define GPIO32_SDATA_IN1_AC97_MD      (32 | GPIO_ALT_FN_1_IN)
++#define GPIO33_nCS_5_MD               (33 | GPIO_ALT_FN_2_OUT)
++#define GPIO34_FFRXD_MD               (34 | GPIO_ALT_FN_1_IN)
++#define GPIO34_MMCCS0_MD      (34 | GPIO_ALT_FN_2_OUT)
++#define GPIO35_FFCTS_MD               (35 | GPIO_ALT_FN_1_IN)
++#define GPIO36_FFDCD_MD               (36 | GPIO_ALT_FN_1_IN)
++#define GPIO37_FFDSR_MD               (37 | GPIO_ALT_FN_1_IN)
++#define GPIO38_FFRI_MD                (38 | GPIO_ALT_FN_1_IN)
++#define GPIO39_MMCCS1_MD      (39 | GPIO_ALT_FN_1_OUT)
++#define GPIO39_FFTXD_MD               (39 | GPIO_ALT_FN_2_OUT)
++#define GPIO40_FFDTR_MD               (40 | GPIO_ALT_FN_2_OUT)
++#define GPIO41_FFRTS_MD               (41 | GPIO_ALT_FN_2_OUT)
++#define GPIO42_BTRXD_MD               (42 | GPIO_ALT_FN_1_IN)
++#define GPIO43_BTTXD_MD               (43 | GPIO_ALT_FN_2_OUT)
++#define GPIO44_BTCTS_MD               (44 | GPIO_ALT_FN_1_IN)
++#define GPIO45_BTRTS_MD               (45 | GPIO_ALT_FN_2_OUT)
++#define GPIO46_ICPRXD_MD      (46 | GPIO_ALT_FN_1_IN)
++#define GPIO46_STRXD_MD               (46 | GPIO_ALT_FN_2_IN)
++#define GPIO47_ICPTXD_MD      (47 | GPIO_ALT_FN_2_OUT)
++#define GPIO47_STTXD_MD               (47 | GPIO_ALT_FN_1_OUT)
++#define GPIO48_nPOE_MD                (48 | GPIO_ALT_FN_2_OUT)
++#define GPIO49_nPWE_MD                (49 | GPIO_ALT_FN_2_OUT)
++#define GPIO50_nPIOR_MD               (50 | GPIO_ALT_FN_2_OUT)
++#define GPIO51_nPIOW_MD               (51 | GPIO_ALT_FN_2_OUT)
++#define GPIO52_nPCE_1_MD      (52 | GPIO_ALT_FN_2_OUT)
++#define GPIO53_nPCE_2_MD      (53 | GPIO_ALT_FN_2_OUT)
++#define GPIO53_MMCCLK_MD      (53 | GPIO_ALT_FN_1_OUT)
++#define GPIO54_MMCCLK_MD      (54 | GPIO_ALT_FN_1_OUT)
++#define GPIO54_pSKTSEL_MD     (54 | GPIO_ALT_FN_2_OUT)
++#define GPIO55_nPREG_MD               (55 | GPIO_ALT_FN_2_OUT)
++#define GPIO56_nPWAIT_MD      (56 | GPIO_ALT_FN_1_IN)
++#define GPIO57_nIOIS16_MD     (57 | GPIO_ALT_FN_1_IN)
++#define GPIO58_LDD_0_MD               (58 | GPIO_ALT_FN_2_OUT)
++#define GPIO59_LDD_1_MD               (59 | GPIO_ALT_FN_2_OUT)
++#define GPIO60_LDD_2_MD               (60 | GPIO_ALT_FN_2_OUT)
++#define GPIO61_LDD_3_MD               (61 | GPIO_ALT_FN_2_OUT)
++#define GPIO62_LDD_4_MD               (62 | GPIO_ALT_FN_2_OUT)
++#define GPIO63_LDD_5_MD               (63 | GPIO_ALT_FN_2_OUT)
++#define GPIO64_LDD_6_MD               (64 | GPIO_ALT_FN_2_OUT)
++#define GPIO65_LDD_7_MD               (65 | GPIO_ALT_FN_2_OUT)
++#define GPIO66_LDD_8_MD               (66 | GPIO_ALT_FN_2_OUT)
++#define GPIO66_MBREQ_MD               (66 | GPIO_ALT_FN_1_IN)
++#define GPIO67_LDD_9_MD               (67 | GPIO_ALT_FN_2_OUT)
++#define GPIO67_MMCCS0_MD      (67 | GPIO_ALT_FN_1_OUT)
++#define GPIO68_LDD_10_MD      (68 | GPIO_ALT_FN_2_OUT)
++#define GPIO68_MMCCS1_MD      (68 | GPIO_ALT_FN_1_OUT)
++#define GPIO69_LDD_11_MD      (69 | GPIO_ALT_FN_2_OUT)
++#define GPIO69_MMCCLK_MD      (69 | GPIO_ALT_FN_1_OUT)
++#define GPIO70_LDD_12_MD      (70 | GPIO_ALT_FN_2_OUT)
++#define GPIO70_RTCCLK_MD      (70 | GPIO_ALT_FN_1_OUT)
++#define GPIO71_LDD_13_MD      (71 | GPIO_ALT_FN_2_OUT)
++#define GPIO71_3_6MHz_MD      (71 | GPIO_ALT_FN_1_OUT)
++#define GPIO72_LDD_14_MD      (72 | GPIO_ALT_FN_2_OUT)
++#define GPIO72_32kHz_MD               (72 | GPIO_ALT_FN_1_OUT)
++#define GPIO73_LDD_15_MD      (73 | GPIO_ALT_FN_2_OUT)
++#define GPIO73_MBGNT_MD               (73 | GPIO_ALT_FN_1_OUT)
++#define GPIO74_LCD_FCLK_MD    (74 | GPIO_ALT_FN_2_OUT)
++#define GPIO75_LCD_LCLK_MD    (75 | GPIO_ALT_FN_2_OUT)
++#define GPIO76_LCD_PCLK_MD    (76 | GPIO_ALT_FN_2_OUT)
++#define GPIO77_LCD_ACBIAS_MD  (77 | GPIO_ALT_FN_2_OUT)
++#define GPIO78_nCS_2_MD               (78 | GPIO_ALT_FN_2_OUT)
++#define GPIO79_nCS_3_MD               (79 | GPIO_ALT_FN_2_OUT)
++#define GPIO80_nCS_4_MD               (80 | GPIO_ALT_FN_2_OUT)
++
++
++/*
++ * Power Manager
++ */
++
++#define PMCR          __REG(0x40F00000)  /* Power Manager Control Register */
++#define PSSR          __REG(0x40F00004)  /* Power Manager Sleep Status Register */
++#define PSPR          __REG(0x40F00008)  /* Power Manager Scratch Pad Register */
++#define PWER          __REG(0x40F0000C)  /* Power Manager Wake-up Enable Register */
++#define PRER          __REG(0x40F00010)  /* Power Manager GPIO Rising-Edge Detect Enable Register */
++#define PFER          __REG(0x40F00014)  /* Power Manager GPIO Falling-Edge Detect Enable Register */
++#define PEDR          __REG(0x40F00018)  /* Power Manager GPIO Edge Detect Status Register */
++#define PCFR          __REG(0x40F0001C)  /* Power Manager General Configuration Register */
++#define PGSR0         __REG(0x40F00020)  /* Power Manager GPIO Sleep State Register for GP[31-0] */
++#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 PSSR_RDH      (1 << 5)        /* Read Disable Hold */
++#define PSSR_PH               (1 << 4)        /* Peripheral Control Hold */
++#define PSSR_VFS      (1 << 2)        /* VDD Fault Status */
++#define PSSR_BFS      (1 << 1)        /* Battery Fault Status */
++#define PSSR_SSS      (1 << 0)        /* Software Sleep Status */
++
++#define PCFR_DS               (1 << 3)        /* Deep Sleep Mode */
++#define PCFR_FS               (1 << 2)        /* Float Static Chip Selects */
++#define PCFR_FP               (1 << 1)        /* Float PCMCIA controls */
++#define PCFR_OPDE     (1 << 0)        /* 3.6864 MHz oscillator power-down enable */
++
++#define RCSR_GPR      (1 << 3)        /* GPIO Reset */
++#define RCSR_SMR      (1 << 2)        /* Sleep Mode */
++#define RCSR_WDR      (1 << 1)        /* Watchdog Reset */
++#define RCSR_HWR      (1 << 0)        /* Hardware Reset */
++
++
++/*
++ * SSP Serial Port Registers
++ */
++
++#define SSCR0         __REG(0x41000000)  /* SSP Control Register 0 */
++#define SSCR1         __REG(0x41000004)  /* SSP Control Register 1 */
++#define SSSR          __REG(0x41000008)  /* SSP Status Register */
++#define SSITR         __REG(0x4100000C)  /* SSP Interrupt Test Register */
++#define SSDR          __REG(0x41000010)  /* (Write / Read) SSP Data Write Register/SSP Data Read Register */
++
++
++/*
++ * MultiMediaCard (MMC) controller
++ */
++
++#define MMC_STRPCL    __REG(0x41100000)  /* Control to start and stop MMC clock */
++#define MMC_STAT      __REG(0x41100004)  /* MMC Status Register (read only) */
++#define MMC_CLKRT     __REG(0x41100008)  /* MMC clock rate */
++#define MMC_SPI               __REG(0x4110000c)  /* SPI mode control bits */
++#define MMC_CMDAT     __REG(0x41100010)  /* Command/response/data sequence control */
++#define MMC_RESTO     __REG(0x41100014)  /* Expected response time out */
++#define MMC_RDTO      __REG(0x41100018)  /* Expected data read time out */
++#define MMC_BLKLEN    __REG(0x4110001c)  /* Block length of data transaction */
++#define MMC_NOB               __REG(0x41100020)  /* Number of blocks, for block mode */
++#define MMC_PRTBUF    __REG(0x41100024)  /* Partial MMC_TXFIFO FIFO written */
++#define MMC_I_MASK    __REG(0x41100028)  /* Interrupt Mask */
++#define MMC_I_REG     __REG(0x4110002c)  /* Interrupt Register (read only) */
++#define MMC_CMD               __REG(0x41100030)  /* Index of current command */
++#define MMC_ARGH      __REG(0x41100034)  /* MSW part of the current command argument */
++#define MMC_ARGL      __REG(0x41100038)  /* LSW part of the current command argument */
++#define MMC_RES               __REG(0x4110003c)  /* Response FIFO (read only) */
++#define MMC_RXFIFO    __REG(0x41100040)  /* Receive FIFO (read only) */
++#define MMC_TXFIFO    __REG(0x41100044)  /* Transmit FIFO (write only) */
++
++
++/*
++ * Core Clock
++ */
++
++#define CCCR          __REG(0x41300000)  /* Core Clock Configuration Register */
++#define CKEN          __REG(0x41300004)  /* Clock Enable Register */
++#define OSCC          __REG(0x41300008)  /* Oscillator Configuration Register */
++
++#define CCCR_N_MASK   0x0380          /* Run Mode Frequency to Turbo Mode Frequency Multiplier */
++#define CCCR_M_MASK   0x0060          /* Memory Frequency to Run Mode Frequency Multiplier */
++#define CCCR_L_MASK   0x001f          /* Crystal Frequency to Memory Frequency Multiplier */
++
++#define CKEN16_LCD    (1 << 16)       /* LCD Unit Clock Enable */
++#define CKEN14_I2C    (1 << 14)       /* I2C Unit Clock Enable */
++#define CKEN13_FICP   (1 << 13)       /* FICP Unit Clock Enable */
++#define CKEN12_MMC    (1 << 12)       /* MMC Unit Clock Enable */
++#define CKEN11_USB    (1 << 11)       /* USB Unit Clock Enable */
++#define CKEN8_I2S     (1 << 8)        /* I2S Unit Clock Enable */
++#define CKEN7_BTUART  (1 << 7)        /* BTUART Unit Clock Enable */
++#define CKEN6_FFUART  (1 << 6)        /* FFUART Unit Clock Enable */
++#define CKEN5_STUART  (1 << 5)        /* STUART Unit Clock Enable */
++#define CKEN3_SSP     (1 << 3)        /* SSP Unit Clock Enable */
++#define CKEN2_AC97    (1 << 2)        /* AC97 Unit Clock Enable */
++#define CKEN1_PWM1    (1 << 1)        /* PWM1 Clock Enable */
++#define CKEN0_PWM0    (1 << 0)        /* PWM0 Clock Enable */
++
++#define OSCC_OON      (1 << 1)        /* 32.768kHz OON (write-once only bit) */
++#define OSCC_OOK      (1 << 0)        /* 32.768kHz OOK (read-only bit) */
++
++
++/*
++ * LCD
++ */
++
++#define LCCR0         __REG(0x44000000)  /* LCD Controller Control Register 0 */
++#define LCCR1         __REG(0x44000004)  /* LCD Controller Control Register 1 */
++#define LCCR2         __REG(0x44000008)  /* LCD Controller Control Register 2 */
++#define LCCR3         __REG(0x4400000C)  /* LCD Controller Control Register 3 */
++#define DFBR0         __REG(0x44000020)  /* DMA Channel 0 Frame Branch Register */
++#define DFBR1         __REG(0x44000024)  /* DMA Channel 1 Frame Branch Register */
++#define LCSR          __REG(0x44000038)  /* LCD Controller Status Register */
++#define LIIDR         __REG(0x4400003C)  /* LCD Controller Interrupt ID Register */
++#define TMEDRGBR      __REG(0x44000040)  /* TMED RGB Seed Register */
++#define TMEDCR                __REG(0x44000044)  /* TMED Control Register */
++
++#define FDADR0                __REG(0x44000200)  /* DMA Channel 0 Frame Descriptor Address Register */
++#define FSADR0                __REG(0x44000204)  /* DMA Channel 0 Frame Source Address Register */
++#define FIDR0         __REG(0x44000208)  /* DMA Channel 0 Frame ID Register */
++#define LDCMD0                __REG(0x4400020C)  /* DMA Channel 0 Command Register */
++#define FDADR1                __REG(0x44000210)  /* DMA Channel 1 Frame Descriptor Address Register */
++#define FSADR1                __REG(0x44000214)  /* DMA Channel 1 Frame Source Address Register */
++#define FIDR1         __REG(0x44000218)  /* DMA Channel 1 Frame ID Register */
++#define LDCMD1                __REG(0x4400021C)  /* DMA Channel 1 Command Register */
++
++#define LCCR0_ENB     (1 << 0)        /* LCD Controller enable */
++#define LCCR0_CMS     (1 << 1)        /* Color = 0, Monochrome = 1 */
++#define LCCR0_SDS     (1 << 2)        /* Single Panel = 0, Dual Panel = 1 */
++#define LCCR0_LDM     (1 << 3)        /* LCD Disable Done Mask */
++#define LCCR0_SFM     (1 << 4)        /* Start of frame mask */
++#define LCCR0_IUM     (1 << 5)        /* Input FIFO underrun mask */
++#define LCCR0_EFM     (1 << 6)        /* End of Frame mask */
++#define LCCR0_PAS     (1 << 7)        /* Passive = 0, Active = 1 */
++#define LCCR0_BLE     (1 << 8)        /* Little Endian = 0, Big Endian = 1 */
++#define LCCR0_DPD     (1 << 9)        /* Double Pixel mode, 4 pixel value = 0, 8 pixle values = 1 */
++#define LCCR0_DIS     (1 << 10)       /* LCD Disable */
++#define LCCR0_QDM     (1 << 11)       /* LCD Quick Disable mask */
++#define LCCR0_PDD     (0xff << 12)    /* Palette DMA request delay */
++#define LCCR0_PDD_S   12
++#define LCCR0_BM      (1 << 20)       /* Branch mask */
++#define LCCR0_OUM     (1 << 21)       /* Output FIFO underrun mask */
++
++#define LCCR1_PPL       Fld (10, 0)      /* Pixels Per Line - 1 */
++#define LCCR1_DisWdth(Pixel)            /* Display Width [1..800 pix.]  */ \
++                        (((Pixel) - 1) << FShft (LCCR1_PPL))
++
++#define LCCR1_HSW       Fld (6, 10)     /* Horizontal Synchronization     */
++#define LCCR1_HorSnchWdth(Tpix)         /* Horizontal Synchronization     */ \
++                                        /* pulse Width [1..64 Tpix]       */ \
++                        (((Tpix) - 1) << FShft (LCCR1_HSW))
++
++#define LCCR1_ELW       Fld (8, 16)     /* End-of-Line pixel clock Wait    */
++                                        /* count - 1 [Tpix]                */
++#define LCCR1_EndLnDel(Tpix)            /*  End-of-Line Delay              */ \
++                                        /*  [1..256 Tpix]                  */ \
++                        (((Tpix) - 1) << FShft (LCCR1_ELW))
++
++#define LCCR1_BLW       Fld (8, 24)     /* Beginning-of-Line pixel clock   */
++                                        /* Wait count - 1 [Tpix]           */
++#define LCCR1_BegLnDel(Tpix)            /*  Beginning-of-Line Delay        */ \
++                                        /*  [1..256 Tpix]                  */ \
++                        (((Tpix) - 1) << FShft (LCCR1_BLW))
++
++
++#define LCCR2_LPP       Fld (10, 0)     /* Line Per Panel - 1              */
++#define LCCR2_DisHght(Line)             /*  Display Height [1..1024 lines] */ \
++                        (((Line) - 1) << FShft (LCCR2_LPP))
++
++#define LCCR2_VSW       Fld (6, 10)     /* Vertical Synchronization pulse  */
++                                        /* Width - 1 [Tln] (L_FCLK)        */
++#define LCCR2_VrtSnchWdth(Tln)          /*  Vertical Synchronization pulse */ \
++                                        /*  Width [1..64 Tln]              */ \
++                        (((Tln) - 1) << FShft (LCCR2_VSW))
++
++#define LCCR2_EFW       Fld (8, 16)     /* End-of-Frame line clock Wait    */
++                                        /* count [Tln]                     */
++#define LCCR2_EndFrmDel(Tln)            /*  End-of-Frame Delay             */ \
++                                        /*  [0..255 Tln]                   */ \
++                        ((Tln) << FShft (LCCR2_EFW))
++
++#define LCCR2_BFW       Fld (8, 24)     /* Beginning-of-Frame line clock   */
++                                        /* Wait count [Tln]                */
++#define LCCR2_BegFrmDel(Tln)            /*  Beginning-of-Frame Delay       */ \
++                                        /*  [0..255 Tln]                   */ \
++                        ((Tln) << FShft (LCCR2_BFW))
++
++#if 0
++#define LCCR3_PCD     (0xff)          /* Pixel clock divisor */
++#define LCCR3_ACB     (0xff << 8)     /* AC Bias pin frequency */
++#define LCCR3_ACB_S   8
++#endif
++
++#define LCCR3_API     (0xf << 16)     /* AC Bias pin trasitions per interrupt */
++#define LCCR3_API_S   16
++#define LCCR3_VSP     (1 << 20)       /* vertical sync polarity */
++#define LCCR3_HSP     (1 << 21)       /* horizontal sync polarity */
++#define LCCR3_PCP     (1 << 22)       /* pixel clock polarity */
++#define LCCR3_OEP     (1 << 23)       /* output enable polarity */
++#if 0
++#define LCCR3_BPP     (7 << 24)       /* bits per pixel */
++#define LCCR3_BPP_S   24
++#endif
++#define LCCR3_DPC     (1 << 27)       /* double pixel clock mode */
++
++
++#define LCCR3_PCD       Fld (8, 0)      /* Pixel Clock Divisor */
++#define LCCR3_PixClkDiv(Div)            /* Pixel Clock Divisor */ \
++                        (((Div) << FShft (LCCR3_PCD)))
++
++
++#define LCCR3_BPP       Fld (3, 24)     /* Bit Per Pixel */
++#define LCCR3_Bpp(Bpp)                  /* Bit Per Pixel */ \
++                        (((Bpp) << FShft (LCCR3_BPP)))
++
++#define LCCR3_ACB       Fld (8, 8)      /* AC Bias */
++#define LCCR3_Acb(Acb)                  /* BAC Bias */ \
++                        (((Acb) << FShft (LCCR3_ACB)))
++
++#define LCCR3_HorSnchH  (LCCR3_HSP*0)   /*  Horizontal Synchronization     */
++                                        /*  pulse active High              */
++#define LCCR3_HorSnchL  (LCCR3_HSP*1)   /*  Horizontal Synchronization     */
++
++#define LCCR3_VrtSnchH  (LCCR3_VSP*0)   /*  Vertical Synchronization pulse */
++                                        /*  active High                    */
++#define LCCR3_VrtSnchL  (LCCR3_VSP*1)   /*  Vertical Synchronization pulse */
++                                        /*  active Low                     */
++
++#define LCSR_LDD      (1 << 0)        /* LCD Disable Done */
++#define LCSR_SOF      (1 << 1)        /* Start of frame */
++#define LCSR_BER      (1 << 2)        /* Bus error */
++#define LCSR_ABC      (1 << 3)        /* AC Bias count */
++#define LCSR_IUL      (1 << 4)        /* input FIFO underrun Lower panel */
++#define LCSR_IUU      (1 << 5)        /* input FIFO underrun Upper panel */
++#define LCSR_OU               (1 << 6)        /* output FIFO underrun */
++#define LCSR_QD               (1 << 7)        /* quick disable */
++#define LCSR_EOF      (1 << 8)        /* end of frame */
++#define LCSR_BS               (1 << 9)        /* branch status */
++#define LCSR_SINT     (1 << 10)       /* subsequent interrupt */
++
++#define LDCMD_PAL     (1 << 26)       /* instructs DMA to load palette buffer */
++
++#define LCSR_LDD      (1 << 0)        /* LCD Disable Done */
++#define LCSR_SOF      (1 << 1)        /* Start of frame */
++#define LCSR_BER      (1 << 2)        /* Bus error */
++#define LCSR_ABC      (1 << 3)        /* AC Bias count */
++#define LCSR_IUL      (1 << 4)        /* input FIFO underrun Lower panel */
++#define LCSR_IUU      (1 << 5)        /* input FIFO underrun Upper panel */
++#define LCSR_OU               (1 << 6)        /* output FIFO underrun */
++#define LCSR_QD               (1 << 7)        /* quick disable */
++#define LCSR_EOF      (1 << 8)        /* end of frame */
++#define LCSR_BS               (1 << 9)        /* branch status */
++#define LCSR_SINT     (1 << 10)       /* subsequent interrupt */
++
++#define LDCMD_PAL     (1 << 26)       /* instructs DMA to load palette buffer */
++
++/*
++ * Memory controller
++ */
++
++#define MDCNFG                __REG(0x48000000)  /* SDRAM Configuration Register 0 */
++#define MDREFR                __REG(0x48000004)  /* SDRAM Refresh Control Register */
++#define MSC0          __REG(0x48000008)  /* Static Memory Control Register 0 */
++#define MSC1          __REG(0x4800000C)  /* Static Memory Control Register 1 */
++#define MSC2          __REG(0x48000010)  /* Static Memory Control Register 2 */
++#define MECR          __REG(0x48000014)  /* Expansion Memory (PCMCIA/Compact Flash) Bus Configuration */
++#define SXLCR         __REG(0x48000018)  /* LCR value to be written to SDRAM-Timing Synchronous Flash */
++#define SXCNFG                __REG(0x4800001C)  /* Synchronous Static Memory Control Register */
++#define SXMRS         __REG(0x48000024)  /* MRS value to be written to Synchronous Flash or SMROM */
++#define MCMEM0                __REG(0x48000028)  /* Card interface Common Memory Space Socket 0 Timing */
++#define MCMEM1                __REG(0x4800002C)  /* Card interface Common Memory Space Socket 1 Timing */
++#define MCATT0                __REG(0x48000030)  /* Card interface Attribute Space Socket 0 Timing Configuration */
++#define MCATT1                __REG(0x48000034)  /* Card interface Attribute Space Socket 1 Timing Configuration */
++#define MCIO0         __REG(0x48000038)  /* Card interface I/O Space Socket 0 Timing Configuration */
++#define MCIO1         __REG(0x4800003C)  /* Card interface I/O Space Socket 1 Timing Configuration */
++#define MDMRS         __REG(0x48000040)  /* MRS value to be written to SDRAM */
++#define BOOT_DEF      __REG(0x48000044)  /* Read-Only Boot-Time Register. Contains BOOT_SEL and PKG_SEL */
++
++#define MDREFR_K2FREE (1 << 25)       /* SDRAM Free-Running Control */
++#define MDREFR_K1FREE (1 << 24)       /* SDRAM Free-Running Control */
++#define MDREFR_K0FREE (1 << 23)       /* SDRAM Free-Running Control */
++#define MDREFR_SLFRSH (1 << 22)       /* SDRAM Self-Refresh Control/Status */
++#define MDREFR_APD    (1 << 20)       /* SDRAM/SSRAM Auto-Power-Down Enable */
++#define MDREFR_K2DB2  (1 << 19)       /* SDCLK2 Divide by 2 Control/Status */
++#define MDREFR_K2RUN  (1 << 18)       /* SDCLK2 Run Control/Status */
++#define MDREFR_K1DB2  (1 << 17)       /* SDCLK1 Divide by 2 Control/Status */
++#define MDREFR_K1RUN  (1 << 16)       /* SDCLK1 Run Control/Status */
++#define MDREFR_E1PIN  (1 << 15)       /* SDCKE1 Level Control/Status */
++#define MDREFR_K0DB2  (1 << 14)       /* SDCLK0 Divide by 2 Control/Status */
++#define MDREFR_K0RUN  (1 << 13)       /* SDCLK0 Run Control/Status */
++#define MDREFR_E0PIN  (1 << 12)       /* SDCKE0 Level Control/Status */
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/serial.h
+@@ -0,0 +1,51 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/serial.h
++ * 
++ * 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.
++ */
++
++
++#define BAUD_BASE     921600
++
++/* Standard COM flags */
++#define STD_COM_FLAGS (ASYNC_SKIP_TEST)
++
++#define STD_SERIAL_PORT_DEFNS \
++      {       \
++              type:                   PORT_PXA,       \
++              xmit_fifo_size:         32,             \
++              baud_base:              BAUD_BASE,      \
++              iomem_base:             (void *)&FFUART,\
++              iomem_reg_shift:        2,              \
++              io_type:                SERIAL_IO_MEM32,\
++              irq:                    IRQ_FFUART,     \
++              flags:                  STD_COM_FLAGS,  \
++      }, {    \
++              type:                   PORT_PXA,       \
++              xmit_fifo_size:         32,             \
++              baud_base:              BAUD_BASE,      \
++              iomem_base:             (void *)&BTUART,\
++              iomem_reg_shift:        2,              \
++              io_type:                SERIAL_IO_MEM32,\
++              irq:                    IRQ_BTUART,     \
++              flags:                  STD_COM_FLAGS,  \
++      }, {    \
++              type:                   PORT_PXA,       \
++              xmit_fifo_size:         32,             \
++              baud_base:              BAUD_BASE,      \
++              iomem_base:             (void *)&STUART,\
++              iomem_reg_shift:        2,              \
++              io_type:                SERIAL_IO_MEM32,\
++              irq:                    IRQ_STUART,     \
++              flags:                  STD_COM_FLAGS,  \
++      }
++
++#define RS_TABLE_SIZE 8
++
++#define EXTRA_SERIAL_PORT_DEFNS
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/system.h
+@@ -0,0 +1,32 @@
++/*
++ * linux/include/asm-arm/arch-pxa/system.h
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Jun 15, 2001
++ * Copyright: 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 "hardware.h"
++
++static inline void arch_idle(void)
++{
++      cpu_do_idle();
++}
++
++static inline void arch_reset(char mode)
++{
++      if (mode == 's') {
++              /* Jump into ROM at address 0 */
++              cpu_reset(0);
++      } else {
++              /* Initialize the watchdog and let it fire */
++              OWER = OWER_WME;
++              OSSR = OSSR_M3;
++              OSMR3 = OSCR + 368640;  /* ... in 100 ms */
++      }
++}
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/time.h
+@@ -0,0 +1,86 @@
++/*
++ * linux/include/asm-arm/arch-pxa/time.h
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Jun 15, 2001
++ * Copyright: 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.
++ */
++
++
++static inline unsigned long pxa_get_rtc_time(void)
++{
++      return RCNR;
++}
++
++static int pxa_set_rtc(void)
++{
++      unsigned long current_time = xtime.tv_sec;
++
++      if (RTSR & RTSR_ALE) {
++              /* make sure not to forward the clock over an alarm */
++              unsigned long alarm = RTAR;
++              if (current_time >= alarm && alarm >= RCNR)
++                      return -ERESTARTSYS;
++      }
++      RCNR = current_time;
++      return 0;
++}
++
++/* IRQs are disabled before entering here from do_gettimeofday() */
++static unsigned long pxa_gettimeoffset (void)
++{
++      unsigned long ticks_to_match, elapsed, usec;
++
++      /* Get ticks before next timer match */
++      ticks_to_match = OSMR0 - OSCR;
++
++      /* We need elapsed ticks since last match */
++      elapsed = LATCH - ticks_to_match;
++
++      /* Now convert them to usec */
++      usec = (unsigned long)(elapsed*tick)/LATCH;
++
++      return usec;
++}
++
++static void pxa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      long flags;
++      int next_match;
++
++      do_profile(regs);
++
++      /* Loop until we get ahead of the free running timer.
++       * This ensures an exact clock tick count and time acuracy.
++       * 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.
++       */
++      do {
++              do_leds();
++              do_set_rtc();
++              save_flags_cli( flags );
++              do_timer(regs);
++              OSSR = OSSR_M0;  /* Clear match on timer 0 */
++              next_match = (OSMR0 += LATCH);
++              restore_flags( flags );
++      } while( (signed long)(next_match - OSCR) <= 0 );
++}
++
++extern inline void setup_timer (void)
++{
++      gettimeoffset = pxa_gettimeoffset;
++      set_rtc = pxa_set_rtc;
++      xtime.tv_sec = pxa_get_rtc_time();
++      timer_irq.handler = pxa_timer_interrupt;
++      OSMR0 = 0;              /* set initial match at 0 */
++      OSSR = 0xf;             /* clear status on all timers */
++      setup_arm_irq(IRQ_OST0, &timer_irq);
++      OIER |= OIER_E0;        /* enable match on timer 0 to cause interrupts */
++      OSCR = 0;               /* initialize free-running timer, force first match */
++}
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/timex.h
+@@ -0,0 +1,17 @@
++/*
++ * linux/include/asm-arm/arch-pxa/timex.h
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Jun 15, 2001
++ * Copyright: 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.
++ */
++
++/*
++ * PXA250/210 timer
++ */
++#define CLOCK_TICK_RATE               3686400
++#define CLOCK_TICK_FACTOR     80
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/trizeps2.h
+@@ -0,0 +1,206 @@
++/*
++ *  linux/include/asm-arm/arch-pxa/trizeps2.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 Luc De Cock, Teradyne DS, Ltd.
++ *
++ * 2002-10-10: Initial code started from idp.h
++ */
++
++
++/*
++ * Note: this file must be safe to include in assembly files
++ */
++
++/* comment out following if you have a board  with 32MB RAM */
++//#define PXA_TRIZEPS2_64MB   1
++#undef PXA_TRIZEPS2_64MB
++
++#define TRIZEPS2_FLASH_PHYS           (PXA_CS0_PHYS)
++#define TRIZEPS2_ALT_FLASH_PHYS               (PXA_CS1_PHYS)
++#define TRIZEPS2_MEDIAQ_PHYS          (PXA_CS3_PHYS)
++#define TRIZEPS2_IDE_PHYS             (PXA_CS5_PHYS + 0x03000000)
++#define TRIZEPS2_ETH_PHYS             (0x0C800000)
++#define TRIZEPS2_COREVOLT_PHYS                (PXA_CS5_PHYS + 0x03800000)
++#define TRIZEPS2_BCR_PHYS             (0x0E000000)
++#define TRIZEPS2_CPLD_PHYS            (0x0C000000)
++
++/*
++ * virtual memory map
++ */
++
++#define TRIZEPS2_IDE_BASE             (0xf0000000)
++#define TRIZEPS2_IDE_SIZE             (1*1024*1024)
++
++#define TRIZEPS2_ETH_BASE             (0xf1000000)
++#define TRIZEPS2_ETH_SIZE             (1*1024*1024)
++#define ETH_BASE              TRIZEPS2_ETH_BASE //smc9194 driver compatibility issue
++
++#define TRIZEPS2_COREVOLT_BASE        (TRIZEPS2_ETH_BASE + TRIZEPS2_ETH_SIZE)
++#define TRIZEPS2_COREVOLT_SIZE        (1*1024*1024)
++
++#define TRIZEPS2_BCR_BASE     (0xf0000000)
++#define TRIZEPS2_BCR_SIZE     (1*1024*1024)
++
++#define BCR_P2V(x)            ((x) - TRIZEPS2_BCR_PHYS + TRIZEPS2_BCR_BASE)
++#define BCR_V2P(x)            ((x) - TRIZEPS2_BCR_BASE + TRIZEPS2_BCR_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __BCR_REG(x)                (*((volatile unsigned short *)BCR_P2V(x)))
++#else
++#  define __BCR_REG(x)                BCR_P2V(x)
++#endif
++
++/* board level registers  */
++#define TRIZEPS2_CPLD_BASE    (0xf0100000)
++#define CPLD_P2V(x)             ((x) - TRIZEPS2_CPLD_PHYS + TRIZEPS2_CPLD_BASE)
++#define CPLD_V2P(x)             ((x) - TRIZEPS2_CPLD_BASE + TRIZEPS2_CPLD_PHYS)
++
++#ifndef __ASSEMBLY__
++#  define __CPLD_REG(x)         (*((volatile unsigned short *)CPLD_P2V(x)))
++#else
++#  define __CPLD_REG(x)         CPLD_P2V(x)
++#endif
++
++#define _TRIZEPS2_PCCARD_STATUS       (0x0c000000)
++#define TRIZEPS2_PCCARD_STATUS         __CPLD_REG(_TRIZEPS2_PCCARD_STATUS)
++
++/*
++ * CS memory timing via Static Memory Control Register (MSC0-2)
++ */
++
++#define MSC_CS(cs,val) ((val)<<((cs&1)<<4))
++
++#define MSC_RBUFF_SHIFT               15 
++#define MSC_RBUFF(x)          ((x)<<MSC_RBUFF_SHIFT)
++#define MSC_RBUFF_SLOW                MSC_RBUFF(0)
++#define MSC_RBUFF_FAST                MSC_RBUFF(1)
++
++#define MSC_RRR_SHIFT         12
++#define MSC_RRR(x)            ((x)<<MSC_RRR_SHIFT)
++
++#define MSC_RDN_SHIFT         8
++#define MSC_RDN(x)            ((x)<<MSC_RDN_SHIFT)
++
++#define MSC_RDF_SHIFT         4
++#define MSC_RDF(x)            ((x)<<MSC_RDF_SHIFT)
++
++#define MSC_RBW_SHIFT         3
++#define MSC_RBW(x)            ((x)<<MSC_RBW_SHIFT)
++#define MSC_RBW_16            MSC_RBW(1)
++#define MSC_RBW_32            MSC_RBW(0)
++
++#define MSC_RT_SHIFT          0
++#define MSC_RT(x)             ((x)<<MSC_RT_SHIFT)
++
++
++/*
++ * Bit masks for various registers
++ */
++// TRIZEPS2_BCR_PCCARD_PWR
++#define PCC_3V                (1 << 0)
++#define PCC_5V                (1 << 1)
++#define PCC_EN1               (1 << 2)
++#define PCC_EN0               (1 << 3)
++
++// TRIZEPS2_BCR_PCCARD_EN
++#define PCC_RESET     (1 << 6)
++#define PCC_ENABLE    (1 << 0)
++
++// TRIZEPS2_BSR_PCCARDx_STATUS
++#define _PCC_WRPROT   (1 << 7) // 7-4 read as low true
++#define _PCC_RESET    (1 << 6)
++#define _PCC_IRQ      (1 << 5)
++#define _PCC_INPACK   (1 << 4)
++#define PCC_BVD1      (1 << 0)
++#define PCC_BVD2      (1 << 1)
++#define PCC_VS1               (1 << 2)
++#define PCC_VS2               (1 << 3)
++
++// TRIZEPS2_BCR_CONTROL bits
++#define BCR_LCD_ON    (1 << 4)
++#define BCR_LCD_OFF   (0)
++#define BCR_LCD_MASK  (1 << 4)
++#define BCR_PCMCIA_RESET (1 << 7)
++#define BCR_PCMCIA_NORMAL (0)
++
++#define PCC_DETECT    (GPLR(24) & GPIO_bit(24))
++#define PCC_READY     (GPLR(1) & GPIO_bit(1))
++
++// Board Control Register
++#define _TRIZEPS2_BCR_CONTROL (TRIZEPS2_BCR_PHYS)
++#define TRIZEPS2_BCR_CONTROL  __BCR_REG(_TRIZEPS2_BCR_CONTROL)
++
++// Board TTL-IO register
++#define TRIZEPS2_TTLIO_PHYS   (0x0d800000)
++#define TRIZEPS2_TTLIO_BASE   (0xf2000000)
++// various ioctl cmds
++#define TTLIO_RESET           0
++#define TTLIO_GET             1
++#define TTLIO_SET             2
++#define TTLIO_UNSET           3
++
++/*
++ * Macros for LCD Driver
++ */
++
++#ifdef CONFIG_FB_PXA
++
++#define FB_BACKLIGHT_ON()
++#define FB_BACKLIGHT_OFF()
++
++#define FB_PWR_ON()
++#define FB_PWR_OFF()
++
++#define FB_VLCD_ON()          WRITE_TRIZEPS2_BCR(BCR_LCD_ON,BCR_LCD_MASK);
++#define FB_VLCD_OFF()         WRITE_TRIZEPS2_BCR(BCR_LCD_OFF,BCR_LCD_MASK);
++
++#endif
++
++/* A listing of interrupts used by external hardware devices */
++
++#define GPIO_TOUCH_PANEL_IRQ          2
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(GPIO_TOUCH_PANEL_IRQ)
++#define GPIO_ETHERNET_IRQ             19
++#define ETHERNET_IRQ                  IRQ_GPIO(GPIO_ETHERNET_IRQ)
++#define GPIO_TTLIO_IRQ                        23
++#define TTLIO_IRQ                     IRQ_GPIO(GPIO_TTLIO_IRQ)
++
++#define TOUCH_PANEL_IRQ_EDGE          GPIO_FALLING_EDGE
++#define IDE_IRQ_EDGE                  GPIO_RISING_EDGE
++#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
++
++#define PCMCIA_S_CD_VALID             IRQ_GPIO(24)
++#define PCMCIA_S_CD_VALID_EDGE                GPIO_BOTH_EDGES
++
++#define PCMCIA_S_RDYINT                       IRQ_GPIO(1)
++#define PCMCIA_S_RDYINT_EDGE          GPIO_FALLING_EDGE
++
++/*
++ * macros for MTD driver
++ */
++
++#define FLASH_WRITE_PROTECT_DISABLE() // ((TRIZEPS2_CPLD_FLASH_WE) &= ~(0x1))
++#define FLASH_WRITE_PROTECT_ENABLE()  // ((TRIZEPS2_CPLD_FLASH_WE) |= (0x1))
++
++/* shadow registers for write only registers */
++#ifndef __ASSEMBLY__
++extern unsigned short trizeps2_bcr_shadow;
++#endif
++
++/* 
++ * macros to write to write only register
++ *
++ * none of these macros are protected from 
++ * multiple drivers using them in interrupt context.
++ */
++
++#define WRITE_TRIZEPS2_BCR(value, mask) \
++{\
++      trizeps2_bcr_shadow = ((value & mask) | (trizeps2_bcr_shadow & ~mask));\
++      TRIZEPS2_BCR_CONTROL = trizeps2_bcr_shadow;\
++}
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/uncompress.h
+@@ -0,0 +1,42 @@
++/*
++ * linux/include/asm-arm/arch-pxa/uncompress.h
++ *  
++ * 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.
++ */
++
++#define FFUART                ((volatile unsigned long *)0x40100000)
++#define BTUART                ((volatile unsigned long *)0x40200000)
++#define STUART                ((volatile unsigned long *)0x40700000)
++
++#define UART          FFUART
++
++
++static __inline__ void putc(char c)
++{
++      while (!(UART[5] & 0x20));
++      UART[0] = c;
++}
++
++/*
++ * This does not append a newline
++ */
++static void puts(const char *s)
++{
++      while (*s) {
++              putc(*s);
++              if (*s == '\n')
++                      putc('\r');
++              s++;
++      }
++}
++
++/*
++ * nothing to do
++ */
++#define arch_decomp_setup()
++#define arch_decomp_wdog()
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-pxa/vmalloc.h
+@@ -0,0 +1,23 @@
++/*
++ * linux/include/asm-arm/arch-pxa/vmalloc.h
++ * 
++ * 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.
++ */
++
++/*
++ * Just any arbitrary offset to the start of the vmalloc VM area: the
++ * current 8MB value just means that there will be a 8MB "hole" after the
++ * physical memory until the kernel virtual memory starts.  That means that
++ * any out-of-bounds memory accesses will hopefully be caught.
++ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
++ * area for the same reason. ;)
++ */
++#define VMALLOC_OFFSET          (8*1024*1024)
++#define VMALLOC_START   (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
++#define VMALLOC_VMADDR(x) ((unsigned long)(x))
++#define VMALLOC_END       (0xe8000000)
+--- linux-2.4.27/include/asm-arm/assembler.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/assembler.h
+@@ -13,3 +13,26 @@
+ #include <asm/proc/ptrace.h>
+ #include <asm/proc/assembler.h>
++
++/*
++ * Endian independent macros for shifting bytes within registers.
++ */
++#ifndef __ARMEB__
++#define pull            lsr
++#define push            lsl
++#define byte(x)         (x*8)
++#else
++#define pull            lsl
++#define push            lsr
++#define byte(x)         ((3-x)*8)
++#endif
++
++/*
++ * Data preload for architectures that support it
++ */
++#if __LINUX_ARM_ARCH__ >= 5
++#define PLD(code...)  code
++#else
++#define PLD(code...)
++#endif
++
+--- linux-2.4.27/include/asm-arm/bitops.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/bitops.h
+@@ -91,6 +91,8 @@
+     return (((unsigned char *) addr)[nr >> 3] >> (nr & 7)) & 1;
+ }     
++#if __LINUX_ARM_ARCH__ < 5
++
+ /*
+  * ffz = Find First Zero in word. Undefined if no zero exists,
+  * so code should check against ~0UL first..
+@@ -117,6 +119,23 @@
+ #define ffs(x) generic_ffs(x)
++#else
++
++/*
++ * On ARMv5 and above those functions can be implemented around
++ * the clz instruction for much better code efficiency.
++ */
++
++extern __inline__ int generic_fls(int x);
++#define fls(x) \
++      ( __builtin_constant_p(x) ? generic_fls(x) : \
++        ({ int __r; asm("clz%?\t%0, %1" : "=r"(__r) : "r"(x)); 32-__r; }) )
++#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); })
++#define __ffs(x) (ffs(x) - 1)
++#define ffz(x) __ffs( ~(x) )
++
++#endif
++
+ /*
+  * hweightN: returns the hamming weight (i.e. the number
+  * of bits set) of a N-bit word
+--- linux-2.4.27/include/asm-arm/io.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/io.h
+@@ -168,7 +168,7 @@
+  * devices.  This is the "generic" version.  The PCI specific version
+  * is in pci.h
+  */
+-extern void *consistent_alloc(int gfp, size_t size, dma_addr_t *handle);
++extern void *consistent_alloc(int gfp, size_t size, dma_addr_t *handle, unsigned long flags);
+ extern void consistent_free(void *vaddr, size_t size, dma_addr_t handle);
+ extern void consistent_sync(void *vaddr, size_t size, int rw);
+--- linux-2.4.27/include/asm-arm/memory.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/memory.h
+@@ -123,6 +123,9 @@
+      ((unsigned)((page) - NODE_MEM_MAP(node)) < NODE_DATA(node)->node_size)); \
+ })
++/* We want large page mapping possible */
++#define VMALLOC_ALIGN         0x10000
++
+ #endif
+ /*
+--- linux-2.4.27/include/asm-arm/proc-armv/pgtable.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/proc-armv/pgtable.h
+@@ -15,9 +15,6 @@
+ #ifndef __ASM_PROC_PGTABLE_H
+ #define __ASM_PROC_PGTABLE_H
+-#include <asm/proc/domain.h>
+-#include <asm/arch/vmalloc.h>
+-
+ /*
+  * entries per page directory level: they are two-level, so
+  * we don't really have any PMD directory.
+@@ -26,27 +23,92 @@
+ #define PTRS_PER_PMD          1
+ #define PTRS_PER_PGD          4096
+-/****************
+-* PMD functions *
+-****************/
+-
+-/* PMD types (actually level 1 descriptor) */
+-#define PMD_TYPE_MASK         0x0003
+-#define PMD_TYPE_FAULT                0x0000
+-#define PMD_TYPE_TABLE                0x0001
+-#define PMD_TYPE_SECT         0x0002
+-#define PMD_UPDATABLE         0x0010
+-#define PMD_SECT_CACHEABLE    0x0008
+-#define PMD_SECT_BUFFERABLE   0x0004
+-#define PMD_SECT_AP_WRITE     0x0400
+-#define PMD_SECT_AP_READ      0x0800
++/*
++ * Hardware page table definitions.
++ *
++ * + Level 1 descriptor (PMD)
++ *   - common
++ */
++#define PMD_TYPE_MASK         (3 << 0)
++#define PMD_TYPE_FAULT                (0 << 0)
++#define PMD_TYPE_TABLE                (1 << 0)
++#define PMD_TYPE_SECT         (2 << 0)
++#define PMD_UPDATABLE         (1 << 4)
+ #define PMD_DOMAIN(x)         ((x) << 5)
++#define PMD_PROTECTION                (1 << 9)        /* v5 */
++/*
++ *   - section
++ */
++#define PMD_SECT_BUFFERABLE   (1 << 2)
++#define PMD_SECT_CACHEABLE    (1 << 3)
++#define PMD_SECT_AP_WRITE     (1 << 10)
++#define PMD_SECT_AP_READ      (1 << 11)
++#define PMD_SECT_TEX(x)               ((x) << 12)     /* v5 */
++/*
++ *   - coarse table
++ */
++
++/*
++ * + Level 2 descriptor (PTE)
++ *   - common
++ */
++#define PTE_TYPE_MASK         (3 << 0)
++#define PTE_TYPE_FAULT                (0 << 0)
++#define PTE_TYPE_LARGE                (1 << 0)
++#define PTE_TYPE_SMALL                (2 << 0)
++#define PTE_TYPE_EXT          (3 << 0)        /* v5 */
++#define PTE_BUFFERABLE                (1 << 2)
++#define PTE_CACHEABLE         (1 << 3)
++
++/*
++ *   - extended small page/tiny page
++ */
++#define PTE_EXT_AP_UNO_SRO    (0 << 4)
++#define PTE_EXT_AP_UNO_SRW    (1 << 4)
++#define PTE_EXT_AP_URO_SRW    (2 << 4)
++#define PTE_EXT_AP_URW_SRW    (3 << 4)
++#define PTE_EXT_TEX(x)                ((x) << 6)      /* v5 */
++
++/*
++ *   - small page
++ */
++#define PTE_SMALL_AP_UNO_SRO  (0x00 << 4)
++#define PTE_SMALL_AP_UNO_SRW  (0x55 << 4)
++#define PTE_SMALL_AP_URO_SRW  (0xaa << 4)
++#define PTE_SMALL_AP_URW_SRW  (0xff << 4)
++#define PTE_AP_READ           PTE_SMALL_AP_URO_SRW
++#define PTE_AP_WRITE          PTE_SMALL_AP_UNO_SRW
++
++/*
++ * "Linux" PTE definitions.
++ *
++ * We keep two sets of PTEs - the hardware and the linux version.
++ * This allows greater flexibility in the way we map the Linux bits
++ * onto the hardware tables, and allows us to have YOUNG and DIRTY
++ * bits.
++ *
++ * The PTE table pointer refers to the hardware entries; the "Linux"
++ * entries are stored 1024 bytes below.
++ */
++#define L_PTE_PRESENT         (1 << 0)
++#define L_PTE_YOUNG           (1 << 1)
++#define L_PTE_BUFFERABLE      (1 << 2)        /* matches PTE */
++#define L_PTE_CACHEABLE               (1 << 3)        /* matches PTE */
++#define L_PTE_USER            (1 << 4)
++#define L_PTE_WRITE           (1 << 5)
++#define L_PTE_EXEC            (1 << 6)
++#define L_PTE_DIRTY           (1 << 7)
++
++#ifndef __ASSEMBLY__
++
++#include <asm/proc/domain.h>
++#include <asm/arch/vmalloc.h>
+ #define _PAGE_USER_TABLE      (PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_USER))
+ #define _PAGE_KERNEL_TABLE    (PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_KERNEL))
+ #define pmd_bad(pmd)          (pmd_val(pmd) & 2)
+-#define set_pmd(pmdp,pmd)     cpu_set_pmd(pmdp,pmd)
++#define set_pmd(pmdp,pmd)     cpu_set_pmd(pmdp, pmd)
+ static inline pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
+ {
+@@ -75,49 +137,8 @@
+       return __phys_to_virt(ptr);
+ }
+-/****************
+-* PTE functions *
+-****************/
+-
+-/* PTE types (actually level 2 descriptor) */
+-#define PTE_TYPE_MASK         0x0003
+-#define PTE_TYPE_FAULT                0x0000
+-#define PTE_TYPE_LARGE                0x0001
+-#define PTE_TYPE_SMALL                0x0002
+-#define PTE_AP_READ           0x0aa0
+-#define PTE_AP_WRITE          0x0550
+-#define PTE_CACHEABLE         0x0008
+-#define PTE_BUFFERABLE                0x0004
+-
+ #define set_pte(ptep, pte)    cpu_set_pte(ptep,pte)
+-/* We now keep two sets of ptes - the physical and the linux version.
+- * This gives us many advantages, and allows us greater flexibility.
+- *
+- * The Linux pte's contain:
+- *  bit   meaning
+- *   0    page present
+- *   1    young
+- *   2    bufferable  - matches physical pte
+- *   3    cacheable   - matches physical pte
+- *   4    user
+- *   5    write
+- *   6    execute
+- *   7    dirty
+- *  8-11  unused
+- *  12-31 virtual page address
+- *
+- * These are stored at the pte pointer; the physical PTE is at -1024bytes
+- */
+-#define L_PTE_PRESENT         (1 << 0)
+-#define L_PTE_YOUNG           (1 << 1)
+-#define L_PTE_BUFFERABLE      (1 << 2)
+-#define L_PTE_CACHEABLE               (1 << 3)
+-#define L_PTE_USER            (1 << 4)
+-#define L_PTE_WRITE           (1 << 5)
+-#define L_PTE_EXEC            (1 << 6)
+-#define L_PTE_DIRTY           (1 << 7)
+-
+ /*
+  * The following macros handle the cache and bufferable bits...
+  */
+@@ -162,5 +183,8 @@
+  * Mark the prot value as uncacheable and unbufferable.
+  */
+ #define pgprot_noncached(prot)        __pgprot(pgprot_val(prot) & ~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE))
++#define pgprot_writecombine(prot) __pgprot(pgprot_val(prot) & ~L_PTE_CACHEABLE)
++
++#endif /* __ASSEMBLY__ */
+ #endif /* __ASM_PROC_PGTABLE_H */
+--- linux-2.4.27/include/asm-arm/proc-armv/processor.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/proc-armv/processor.h
+@@ -23,6 +23,9 @@
+ #define KERNEL_STACK_SIZE     PAGE_SIZE
+ struct context_save_struct {
++#ifdef CONFIG_CPU_XSCALE
++      long long acc0;
++#endif
+       unsigned long cpsr;
+       unsigned long r4;
+       unsigned long r5;
+@@ -35,7 +38,11 @@
+       unsigned long pc;
+ };
++#ifdef CONFIG_CPU_XSCALE
++#define INIT_CSS (struct context_save_struct){ 0, SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
++#else
+ #define INIT_CSS (struct context_save_struct){ SVC_MODE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
++#endif
+ #define EXTRA_THREAD_STRUCT                                           \
+       unsigned int    domain;
+--- linux-2.4.27/include/asm-arm/proc-fns.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/proc-fns.h
+@@ -124,6 +124,14 @@
+ #   define CPU_NAME sa1100
+ #  endif
+ # endif
++# ifdef CONFIG_CPU_XSCALE
++#  ifdef CPU_NAME
++#   undef  MULTI_CPU
++#   define MULTI_CPU
++#  else
++#   define CPU_NAME xscale
++#  endif
++# endif
+ #endif
+ #ifndef MULTI_CPU
+--- linux-2.4.27/include/asm-arm/procinfo.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/procinfo.h
+@@ -55,7 +55,8 @@
+ #define HWCAP_FAST_MULT       16
+ #define HWCAP_FPA     32
+ #define HWCAP_VFP     64
+-#define HWCAP_EDSP    128
++#define HWCAP_EDSP    128     /* El Segundo */
+ #define HWCAP_JAVA    256
++#define HWCAP_XSCALE  512     /* XScale DSP co-processor */
+ #endif
+--- linux-2.4.27/include/asm-arm/uaccess.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/asm-arm/uaccess.h
+@@ -86,7 +86,7 @@
+                       __get_user_x(__r1, __p, __e, 1, "lr");          \
+                       break;                                          \
+               case 2:                                                 \
+-                      __get_user_x(__r1, __p, __e, 2, "r2", "lr");    \
++                      __get_user_x(__r1, __p, __e, 2, "ip", "lr");    \
+                       break;                                          \
+               case 4:                                                 \
+                       __get_user_x(__r1, __p, __e, 4, "lr");          \
+--- linux-2.4.27/include/linux/cramfs_fs_sb.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/linux/cramfs_fs_sb.h
+@@ -10,6 +10,10 @@
+                       unsigned long blocks;
+                       unsigned long files;
+                       unsigned long flags;
++#ifdef CONFIG_CRAMFS_LINEAR
++                      unsigned long linear_phys_addr;
++                      char *        linear_virt_addr;
++#endif
+ };
+ #endif
+--- linux-2.4.27/include/linux/i2c-id.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/linux/i2c-id.h
+@@ -100,6 +100,10 @@
+ #define I2C_DRIVERID_SAA7191  57     /* video decoder                 */
+ #define I2C_DRIVERID_INDYCAM  58     /* SGI IndyCam                   */
++#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
+ #define I2C_DRIVERID_EXP2     0xF2
+@@ -172,6 +176,8 @@
+ #define I2C_ALGO_OCP    0x120000      /* IBM or otherwise On-chip I2C algorithm */
++#define I2C_ALGO_PXA  0x400000        /* Intel PXA I2C algorithm  */
++
+ #define I2C_ALGO_EXP  0x800000        /* experimental                 */
+ #define I2C_ALGO_MASK 0xff0000        /* Mask for algorithms          */
+--- linux-2.4.27/include/linux/serial.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/linux/serial.h
+@@ -75,11 +75,13 @@
+ #define PORT_16654    11
+ #define PORT_16850    12
+ #define PORT_RSA      13      /* RSA-DV II/S card */
+-#define PORT_MAX      13
++#define PORT_PXA      14
++#define PORT_MAX      14
+ #define SERIAL_IO_PORT        0
+ #define SERIAL_IO_HUB6        1
+ #define SERIAL_IO_MEM 2
++#define SERIAL_IO_MEM32       3
+ struct serial_uart_config {
+       char    *name;
+--- linux-2.4.27/include/linux/serial_reg.h~2.4.27-vrs1-pxa1
++++ linux-2.4.27/include/linux/serial_reg.h
+@@ -119,6 +119,14 @@
+ #define UART_IERX_SLEEP  0x10 /* Enable sleep mode */
+ /*
++ * The Intel PXA250/210 chip defines those bits
++ */
++#define UART_IER_DMAE 0x80    /* DMA Requests Enable */
++#define UART_IER_UUE  0x40    /* UART Unit Enable */
++#define UART_IER_NRZE 0x20    /* NRZ coding Enable */
++#define UART_IER_RTOIE        0x10    /* Receiver Time Out Interrupt Enable */
++
++/*
+  * These are the definitions for the Modem Control Register
+  */
+ #define UART_MCR_AFE  0x20    /* Enable auto-RTS/CTS (TI16C750) */
+--- /dev/null
++++ linux-2.4.27/include/mmc/ioctl.h
+@@ -0,0 +1,25 @@
++/*
++ *  linux/include/linux/mmc/ioctl.h
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: ioctl.h,v 0.2 2002/07/11 16:28:21 ted Exp ted $
++ *
++ *  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 __MMC_IOCTL_H__
++#define __MMC_IOCTL_H__
++
++#include <asm/ioctl.h>
++
++/* IOCTL commands provided by MMC subsystem */
++#define IOCMMCSTRNSMODE _IOW('I',0x0f01,int)
++#define IOCMMCGTRNSMODE _IOR('I',0x0f02,int)
++#define IOCMMCGCARDESC  _IOR('I',0x0f03,int) /* FIXME */
++#define IOCMMCGBLKSZMAX _IOR('I',0x0f04,ssize_t)
++#define IOCMMCGNOBMAX _IOR('I',0x0f05,ssize_t)
++
++#endif /* __MMC_IOCTL_H__ */
+--- /dev/null
++++ linux-2.4.27/include/mmc/mmc.h
+@@ -0,0 +1,143 @@
++/*
++ *  linux/include/linux/mmc/mmc.h 
++ *
++ *  Author: Vladimir Shebordaev 
++ *  Copyright:  MontaVista Software Inc.
++ *
++ *  $Id: mmc.h,v 0.2.1.2 2002/07/25 16:29:47 ted Exp ted $
++ *
++ *  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 __MMC_H__
++#define __MMC_H__
++
++#include <linux/types.h>
++#include <mmc/types.h>
++
++/*
++ * MMC card type
++ */
++enum _mmc_type {
++    MMC_CARD_TYPE_RO = 1,
++    MMC_CARD_TYPE_RW,
++    MMC_CARD_TYPE_IO
++};
++
++/*
++ * MMC card state
++ */
++enum _mmc_state {
++    MMC_CARD_STATE_IDLE = 1,
++    MMC_CARD_STATE_READY,
++    MMC_CARD_STATE_IDENT,
++    MMC_CARD_STATE_STNBY,
++    MMC_CARD_STATE_TRAN,
++    MMC_CARD_STATE_DATA,
++    MMC_CARD_STATE_RCV,
++    MMC_CARD_STATE_DIS,
++    MMC_CARD_STATE_UNPLUGGED=0xff
++};
++
++/*
++ * Data transfer mode
++ */
++enum _mmc_transfer_mode {
++    MMC_TRANSFER_MODE_STREAM = 1,
++    MMC_TRANSFER_MODE_BLOCK_SINGLE,
++    MMC_TRANSFER_MODE_BLOCK_MULTIPLE,
++    MMC_TRANSFER_MODE_UNDEFINED = -1
++};
++
++struct _mmc_card_csd_rec { /* CSD register contents */
++/* FIXME: BYTE_ORDER */
++      u8      ecc:2,
++              file_format:2,
++              tmp_write_protect:1,
++              perm_write_protect:1,
++              copy:1,
++              file_format_grp:1;
++      u64     content_prot_app:1,
++              rsvd3:4,
++              write_bl_partial:1,
++              write_bl_len:4,
++              r2w_factor:3,
++              default_ecc:2,
++              wp_grp_enable:1,
++              wp_grp_size:5,
++              erase_grp_mult:5,
++              erase_grp_size:5,
++              c_size_mult:3,
++              vdd_w_curr_max:3,
++              vdd_w_curr_min:3,
++              vdd_r_curr_max:3,
++              vdd_r_curr_min:3,
++              c_size:12,
++              rsvd2:2,
++              dsr_imp:1,
++              read_blk_misalign:1,
++              write_blk_misalign:1,
++              read_bl_partial:1;
++
++      u16     read_bl_len:4,
++              ccc:12;
++      u8      tran_speed;
++      u8      nsac;
++      u8      taac;
++      u8      rsvd1:2,
++              spec_vers:4,
++              csd_structure:2;
++};
++
++struct _mmc_card_cid_rec { /* CID register contents */
++/* FIXME: BYTE_ORDER */
++      u8      mdt_year:4,
++              mdt_mon:4;
++      u32     psn;
++      u8      prv_minor:4,
++              prv_major:4;
++      u8      pnm[6];
++      u16     oid;
++      u8      mid;
++};
++
++/* 
++ * Public card description
++ */
++struct _mmc_card_info_rec {
++    mmc_type_t type;
++    mmc_transfer_mode_t transfer_mode; /* current data transfer mode */
++    __u16 rca;        /* card's RCA assigned during initialization */
++    struct _mmc_card_csd_rec csd;
++    struct _mmc_card_cid_rec cid;
++    __u32 tran_speed; /* kbits */
++    __u16 read_bl_len;
++    __u16 write_bl_len;
++    size_t capacity;    /* card's capacity in bytes */
++};
++
++/* 
++ * Micsellaneous defines
++ */
++#ifndef SEEK_SET
++#define SEEK_SET (0)
++#endif
++
++#ifndef SEEK_CUR
++#define SEEK_CUR (1)
++#endif
++
++#ifndef SEEK_END
++#define SEEK_END (2)
++#endif
++
++#ifndef TRUE
++#define TRUE (1)
++#endif
++
++#ifndef FALSE
++#define FALSE (0)
++#endif
++
++#endif /* __MMC_H__ */
+--- /dev/null
++++ linux-2.4.27/include/mmc/types.h
+@@ -0,0 +1,29 @@
++/*
++ *  linux/include/linux/mmc/types.h
++ *
++ *  Author:   Vladimir Shebordaev     
++ *  Copyright:        MontaVista Software Inc.
++ *
++ *    $Id: types.h,v 0.2 2002/07/11 16:28:21 ted Exp ted $
++ *
++ *  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 __MMC_TYPES_H__
++#define __MMC_TYPES_H__
++
++/* MMC card */
++typedef enum _mmc_type mmc_type_t;
++typedef enum _mmc_state mmc_state_t;
++typedef enum _mmc_transfer_mode mmc_transfer_mode_t;
++
++typedef struct _mmc_card_csd_rec mmc_card_csd_rec_t;
++typedef struct _mmc_card_cid_rec mmc_card_cid_rec_t;
++
++typedef struct _mmc_card_info_rec mmc_card_info_rec_t;
++typedef struct _mmc_card_info_rec *mmc_card_info_t;
++
++typedef enum _mmc_error mmc_error_t;
++
++#endif /* __MMC_TYPES_H__ */
+--- /dev/null
++++ linux-2.4.27/include/video/lcdctrl.h
+@@ -0,0 +1,61 @@
++/*
++ *  lcdctrl.h
++ *
++ *  Generic LCD control for brightness, contrast, etc.
++ *  Device specific drivers implement a lcdctrl_device and
++ *  provides access to it via lcdctrl_device_get_ops().
++ *
++ *  Copyright (C) 2002 Intrinsyc 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.
++ *
++ *  History:
++ *    Mar 2002: Initial version [FB]
++ * 
++ */
++#ifndef __LCD_CONTROL_H
++#define __LCD_CONTROL_H
++
++#define _LCDCTRL_IOCTL_ON             1
++#define _LCDCTRL_IOCTL_OFF            2
++#define _LCDCTRL_IOCTL_INTENSITY      3
++#define _LCDCTRL_IOCTL_BRIGHTNESS     4
++#define _LCDCTRL_IOCTL_CONTRAST               5
++#define _LCDCTRL_IOCTL_GET_BRIGHTNESS 6
++#define _LCDCTRL_IOCTL_GET_CONTRAST   7
++#define _LCDCTRL_IOCTL_GET_INTENSITY  8
++
++#define _LCD_CONTROL_NAME "lcdctrl"
++
++#define LCD_NO_SYNC     0
++#define LCD_SYNC_NEEDED 1
++
++int lcdctrl_enable( void);
++int lcdctrl_disable( void);
++
++/* intensity, contrast, and brightness take values
++ * between 0..100.
++ */
++int lcdctrl_set_intensity( int intensity);
++int lcdctrl_set_contrast( int contrast, int sync);
++int lcdctrl_set_brightness( int brightness);
++
++int lcdctrl_get_intensity( void);
++int lcdctrl_get_contrast( void);
++int lcdctrl_get_brightness( void);
++
++struct lcdctrl_device
++{
++      int (*init)( int*, int*, int*);
++      int (*enable)(void);
++      int (*disable)(void);
++      int (*set_intensity)( int i);
++      int (*set_brightness)( int b);
++      int (*set_contrast)( int c, int sync);
++};
++
++int lcdctrl_init( void);
++
++#endif
+--- linux-2.4.27/init/do_mounts.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/init/do_mounts.c
+@@ -394,6 +394,16 @@
+ }
+ #endif
++#ifdef CONFIG_ROOT_CRAMFS_LINEAR
++static int __init mount_linear_cramfs_root(void)
++{
++      void *data = root_mount_data;
++      if (sys_mount("/dev/root","/root","cramfs",root_mountflags,data) == 0)
++              return 1;
++      return 0;
++}
++#endif
++
+ static int __init create_dev(char *name, kdev_t dev, char *devfs_name)
+ {
+       void *handle;
+@@ -759,6 +769,16 @@
+ static void __init mount_root(void)
+ {
++#ifdef CONFIG_ROOT_CRAMFS_LINEAR
++      if (ROOT_DEV == MKDEV(0, 0)) {
++              if (mount_linear_cramfs_root()) {
++                      sys_chdir("/root");
++                      ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
++                      printk("VFS: Mounted root (linear cramfs filesystem).\n");
++                      return;
++              }
++      }
++#endif
+ #ifdef CONFIG_ROOT_NFS
+        if (MAJOR(ROOT_DEV) == NFS_MAJOR
+            && MINOR(ROOT_DEV) == NFS_MINOR) {
+--- linux-2.4.27/mm/memory.c~2.4.27-vrs1-pxa1
++++ linux-2.4.27/mm/memory.c
+@@ -1018,6 +1018,41 @@
+       return 1;       /* Minor fault */
+ bad_wp_page:
++      if (pte_present(pte) && pte_read(pte)) {
++              /*
++               * Handle COW of XIP memory.
++               * Note that the source memory actually isn't a ram page so
++               * no struct page is associated to the source pte.
++               */
++              char *dst;
++              int ret;
++
++              spin_unlock(&mm->page_table_lock);
++              new_page = alloc_page(GFP_HIGHUSER);
++              if (!new_page)
++                      return -1;
++
++              /* copy XIP data to memory */
++              dst = kmap_atomic(new_page, KM_USER0);
++              ret = copy_from_user(dst, (void*)address, PAGE_SIZE);
++              kunmap_atomic(dst, KM_USER0);
++
++              /* make sure pte didn't change while we dropped the lock */
++              spin_lock(&mm->page_table_lock);
++              if (!ret && pte_same(*page_table, pte)) {
++                      ++mm->rss;
++                      break_cow(vma, new_page, address, page_table);
++                      lru_cache_add(new_page);
++                      spin_unlock(&mm->page_table_lock);
++                      return 1;       /* Minor fault */
++              }
++
++              /* pte changed: back off */
++              spin_unlock(&mm->page_table_lock);
++              page_cache_release(new_page);
++              return ret ? -1 : 1;
++      }
++
+       spin_unlock(&mm->page_table_lock);
+       printk("do_wp_page: bogus page at address %08lx\n", address);
+       return -1;
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/2.4.27-vrs1.patch
new file mode 100644 (file)
index 0000000..f4118e8
--- /dev/null
@@ -0,0 +1,90901 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/Documentation/Configure.help~2.4.27-vrs1
++++ linux-2.4.27/Documentation/Configure.help
+@@ -4941,6 +4941,13 @@
+   Say Y to enable support for Permedia2 AGP frame buffer card from
+   3Dlabs (aka `Graphic Blaster Exxtreme') on the PCI bus.
++Permedia3 support (EXPERIMENTAL)
++CONFIG_FB_PM3
++  This is the frame buffer device driver for the 3DLabs Permedia3
++  chipset, used in Formac ProFormance III, 3DLabs Oxygen VX1 &
++  similar boards, 3DLabs Permedia3 Create!, Appian Jeronimo 2000
++  and maybe other boards.
++
+ Phase5 CVisionPPC/BVisionPPC support
+ CONFIG_FB_PM2_CVPPC
+   Say Y to enable support for the Amiga Phase 5 CVisionPPC BVisionPPC
+@@ -13338,6 +13345,17 @@
+   The module will be called tmspci.o. If you want to compile it
+   as a module, say M here and read <file:Documentation/modules.txt>.
++Altera ether00 support
++CONFIG_ETHER00
++  This is the driver for Altera's ether00 ethernet mac IP core. Say
++  Y here if you want to build support for this into the kernel. It
++  is also available as a module (say M here) that can be inserted/
++  removed from the kernel at the same time as the PLD is configured. 
++  If this driver is running on an epxa10 development board then it 
++  will generate a suitable hw address based on the board serial 
++  number (MTD support is required for this). Otherwise you will 
++  need to set a suitable hw address using ifconfig.
++
+ Generic TMS380 ISA support
+ CONFIG_TMSISA
+   This tms380 module supports generic TMS380-based ISA cards.
+@@ -15292,6 +15310,16 @@
+   support" be compiled as a module for this driver to be used
+   properly.
++Altera's uart00 serial driver
++CONFIG_SERIAL_UART00
++  Say Y here if you want to use the hard logic uart on Excalibur. This 
++  driver also supports soft logic implentations of this uart core.
++
++Serial console on uart00 
++CONFIG_SERIAL_UART00_CONSOLE
++  Say Y here if you want to support a serial console on an Excalibur
++  hard logic uart or uart00 IP core.
++
+ USB ConnectTech WhiteHEAT Serial Driver
+ CONFIG_USB_SERIAL_WHITEHEAT
+   Say Y here if you want to use a ConnectTech WhiteHEAT 4 port
+@@ -19326,6 +19354,20 @@
+   <file:Documentation/modules.txt>.
+   The module will be called i2c-velleman.o.
++Guide GPIO adapter
++CONFIG_I2C_GUIDE
++  This supports the Iders GUIDE I2C bit-bashing adapter.  If you have
++  selected the GUIDE A07 as your ARM system type, you cannot deselect
++  this option, as it is required for proper operation of the GUIDE.
++
++  This interface uses /dev/i2c-0 (major 89, minor 0).
++
++  Say Y if you own such an adapter.
++
++  This driver is also available as a module. If you want to compile
++  it as a module, say M here and read Documentation/modules.txt. The
++  module will be called i2c-guide.o.
++
+ I2C PCF 8584 interfaces
+ CONFIG_I2C_ALGOPCF
+   This allows you to use a range of I2C adapters called PCF adapters.
+@@ -20463,6 +20505,17 @@
+   <file:Documentation/modules.txt>. The module will be called
+    softdog.o.
++SA1100 Internal Watchdog
++CONFIG_SA1100_WATCHDOG
++  Watchdog timer embedded into SA11x0 chips. This will reboot your
++  system when timeout is reached.
++  NOTE, that once enabled, this timer cannot be disabled.
++
++  This driver is also available as a module ( = code which can be
++  inserted in and removed from the running kernel whenever you want).
++  If you want to compile it as a module, say M here and read
++  Documentation/modules.txt. The module will be called sa1100_wdt.o.
++
+ Berkshire Products PC Watchdog
+ CONFIG_PCWATCHDOG
+   This is the driver for the Berkshire Products PC Watchdog card.
+@@ -22124,6 +22177,30 @@
+   from RME. If you want to acess advanced features of the card, read
+   Documentation/sound/rme96xx.
++Assabet audio (UDA1341) support
++CONFIG_SOUND_ASSABET_UDA1341
++  Say Y or M if you have an Intel Assabet evaluation board and want to
++  use the Philips UDA 1341 audio chip (the one that drives the stereo
++  audio output) on the SA1100 SSP port.
++
++Compaq iPAQ audio support
++CONFIG_SOUND_H3600_UDA1341
++  Say Y or M if you have a Compaq iPaq handheld computer and want to
++  use its Philips UDA 1341 audio chip.
++
++Audio support for SA1111/UDA1341
++CONFIG_SOUND_SA1111_UDA1341
++  Say Y or M if you have an SA11x0 system with a Philips UDA 1341
++  connected to the SA11x1. An example of such a system is the Intel
++  Assabet evaluation board connected to a Neponset expansion board.
++
++Generic DAC on the SA11x0 SSP port
++CONFIG_SOUND_SA1100SSP
++  Say Y or M if you have an SA-11x0 system with a DAC on the SSP port.
++  The LART has an Burr-Brown PCM 1710 digital to analog convertor on
++  the SSP port, so you want to say Y or M for the LART. It might work
++  on other SA-1100 platforms, too, but this is not tested.
++
+ Are you using a crosscompiler
+ CONFIG_CROSSCOMPILE
+   Say Y here if you are compiling the kernel on a different
+@@ -25818,6 +25895,20 @@
+   Say Y if configuring for a Pangolin.
+   Say N otherwise.
++Shannon
++CONFIG_SA1100_SHANNON
++  The Shannon (also known as a Tuxscreen, and also as a IS2630) was a
++  limited edition webphone produced by Philips. The Shannon is a SA1100
++  platform with a 640x480 LCD, touchscreen, CIR keyboard, PCMCIA slots,
++  and a telco interface.
++
++Simputer
++CONFIG_SA1100_SIMPUTER
++  Say Y here if you are using an Intel(R) StrongARM(R) SA-1110
++  based Simputer.  See http://www.simputer.org/ for information
++  on the Simputer. The Simputer software is actively maintained
++  by PicoPeta Simputers Pvt. Ltd. (http://www.picopeta.com)
++
+ Victor
+ CONFIG_SA1100_VICTOR
+   Say Y here if you are using a Visu Aide Intel(R) StrongARM(R)
+@@ -25825,6 +25916,14 @@
+   <http://www.visuaide.com/pagevictor.en.html> for information on
+   this system.
++Radisys Corp. Tulsa
++CONFIG_SA1100_PFS168
++  The Radisys Corp. PFS-168 (aka Tulsa) is an Intel® StrongArm® SA-1110 based
++  computer which includes the SA-1111 Microprocessor Companion Chip and other
++  custom I/O designed to add connectivity and multimedia features for vending
++  and business machine applications. Say Y here if you require support for
++  this target.
++
+ # Choice: cerf_ram
+ Cerf on-board RAM size
+ CONFIG_SA1100_CERF_8MB
+@@ -25892,37 +25991,65 @@
+   Say Y if you want support for the ARM920T processor.
+   Otherwise, say N.
+-Support ARM1020 processor
+-CONFIG_CPU_ARM1020
+-  The ARM1020 is the cached version of the ARM10 processor,
+-  with an addition of a floating-point unit.
++Support ARM922T processor
++CONFIG_CPU_ARM922T
++  The ARM922T is a version of the ARM920T, but with smaller
++  instruction and data caches. It is used in Altera's 
++  Excalibur XA device family.
+-  Say Y if you want support for the ARM1020 processor.
++  Say Y if you want support for the ARM922T processor.
+   Otherwise, say N.
+-Disable I-Cache
++Disable instruction cache
+ CONFIG_CPU_ICACHE_DISABLE
+-  Say Y here to disable the processor instruction cache. Unless
+-  you have a reason not to or are unsure, say N.
++  Say Y here to disable the processor instruction cache. Unless 
++  you have a reason to do this, say N.
+-Disable D-Cache
++Disable data cache
+ CONFIG_CPU_DCACHE_DISABLE
+-  Say Y here to disable the processor data cache. Unless
+-  you have a reason not to or are unsure, say N.
++  Say Y here to disable the processor data cache. Unless 
++  you have a reason to do this, say N.
+-Force write through D-cache
++Use data cache in writethrough mode
+ CONFIG_CPU_DCACHE_WRITETHROUGH
+-  Say Y here to use the data cache in write-through mode. Unless you
+-  specifically require this or are unsure, say N.
++  Say Y here to use the data cache in writethough mode. Unless you 
++  specifically require this, say N.
+-Round robin I and D cache replacement algorithm
++Support ARM1020 processor
++CONFIG_CPU_ARM1020
++  The ARM1020 is the 32K cached version of the ARM10 processor,
++  with an addition of a floating-point unit.
++
++  Say Y if you want support for the ARM1020 processor.
++  Otherwise, say N.
++
++Support ARM1022 processor
++CONFIG_CPU_ARM1022
++  The ARM1022E is the 16K cached version of the ARM10 processor,
++  with an addition of a floating-point unit.
++
++  Say Y if you want support for the ARM1022 processor.
++  Otherwise, say N.
++
++Force round-robin cache line replacement
+ CONFIG_CPU_CACHE_ROUND_ROBIN
+-  Say Y here to use the predictable round-robin cache replacement
+-  policy.  Unless you specifically require this or are unsure, say N.
++  Say Y here to force the caches to use a round-robin
++  algorithm when picking a cache line to evict. Unless you
++  specifically require this, say N.
++
++Disable the write buffer
++CONFIG_CPU_WB_DISABLE
++  Say Y here to turn off the write buffer (if possible)
++  Unless you specifically require this, say N. Note that
++  not all ARM processors allow the write buffer to be
++  disabled.
+ Disable branch prediction
+ CONFIG_CPU_BPREDICT_DISABLE
+-  Say Y here to disable branch prediction.  If unsure, say N.
++  The ARM10 family of processors support branch prediction,
++  which can significantly speed up execution of loops.
++  Say Y here to disable branch prediction. Unless you
++  specifically require this, say N.
+ Compressed boot loader in ROM/flash
+ CONFIG_ZBOOT_ROM
+@@ -25969,6 +26096,11 @@
+   Say Y here if you are using the inhand electronics OmniMeter.  See
+   <http://www.inhandelectronics.com/html/omni1.html> for details.
++HP Laboratories BadgePAD 4
++CONFIG_SA1100_BADGE4
++  Say Y here if you want to build a kernel for the HP Laboratories
++  BadgePAD 4.
++
+ Load kernel using Angel Debug Monitor
+ CONFIG_ANGELBOOT
+   Say Y if you plan to load the kernel using Angel, ARM Ltd's target
+@@ -25981,6 +26113,15 @@
+   board includes 2 serial ports, Ethernet, IRDA, and expansion headers.
+   It comes with 16 MB SDRAM and 8 MB flash ROM.
++GUIDEA07
++CONFIG_ARCH_GUIDEA07
++  Say Y if you are using a GUIDE (A07) board.
++
++  This board is based on the cs89712 processor and shares much common
++  hardware with the CDB89712 configuration.  When you select this
++  option and the CDB89712 becomes enabled also, don't worry.  It's
++  supposed to be that way.
++
+ CLPS-711X internal ROM bootstrap
+ CONFIG_EP72XX_ROM_BOOT
+   If you say Y here, your CLPS711x-based kernel will use the bootstrap
+@@ -26009,24 +26150,44 @@
+   You may say N here if you are going to load the Acorn FPEmulator
+   early in the bootup.
++Math emulation 80-bit support
++CONFIG_FPE_NWFPE_XP
++  Say Y to include 80-bit support in the kernel floating-point
++  emulator.  Otherwise, only 32 and 64-bit support is compiled in.
++  Note that gcc does not generate 80-bit operations by default,
++  so in most cases this option only enlarges the size of the
++  floating point emulator without any good reason.
++
++  You almost surely want to say N here.
++
+ FastFPE math emulation
+ CONFIG_FPE_FASTFPE
+   Say Y here to include the FAST floating point emulator in the kernel.
+-  This is an experimental much faster emulator which has only 32 bit
+-  precision for the mantissa.  It does not support any exceptions.
+-  This makes it very simple, it is approximately 4-8 times faster than
+-  NWFPE.
++  This is an experimental much faster emulator which is written
++  completely in ARM assembly. All instructions that are not marked as
++  deprecated in the ARM7500FE data sheet are implemented. It supports
++  single, double and double extended precision. It does support
++  exception flags, but not raising exceptions. It gives an average
++  5 times speed increase over NWFPE for FP only code.
++  
++  FastFPE does not require long multiply instruction anymore and is
++  now suitable for all ARM cpus. The presence of the long multiply
++  instruction is detected during initialisation and used to speedup
++  multiply and divide.
+-  It should be sufficient for most programs.  It is definitely not
+-  suitable if you do scientific calculations that need double
+-  precision for iteration formulas that sum up lots of very small
+-  numbers.  If you do not feel you need a faster FP emulation you
+-  should better choose NWFPE.
++  Compliance to IEEE Std 754-1985 was verified using the testfloat
++  program of the SoftFloat package version 2a by John Hauser. All
++  operations except square root were reported to be compliant. However,
++  this is not a proof, and especially does not verify the instruction
++  parser.
++
++  You can compile both emulators into the kernel and choose one
++  of them at boot time by passing "fpe=fastfpe" or "fpe=nwfpe" as
++  kernel parameter.
+   It is also possible to say M to build the emulator as a module
+-  (fastfpe.o).  But keep in mind that you should only load the FP
+-  emulator early in the bootup.  You should never change from NWFPE to
+-  FASTFPE or vice versa in an active system!
++  (fastfpe.o). This was never tested. Only do it if you know what
++  you are doing!
+ DS1620 thermometer support
+ CONFIG_DS1620
+--- /dev/null
++++ linux-2.4.27/Documentation/arm/Porting
+@@ -0,0 +1,135 @@
++Taken from list archive at http://lists.arm.linux.org.uk/pipermail/linux-arm-kernel/2001-July/004064.html
++
++Initial definitions
++-------------------
++
++The following symbol definitions rely on you knowing the translation that
++__virt_to_phys() does for your machine.  This macro converts the passed
++virtual address to a physical address.  Normally, it is simply:
++
++              phys = virt - PAGE_OFFSET + PHYS_OFFSET
++
++
++Decompressor Symbols
++--------------------
++
++ZTEXTADDR
++      Start address of decompressor.  There's no point in talking about
++      virtual or physical addresses here, since the MMU will be off at
++      the time when you call the decompressor code.  You normally call
++      the kernel at this address to start it booting.  This doesn't have
++      to be located in RAM, it can be in flash or other read-only or
++      read-write addressable medium.
++
++ZBSSADDR
++      Start address of zero-initialised work area for the decompressor.
++      This must be pointing at RAM.  The decompressor will zero initialise
++      this for you.  Again, the MMU will be off.
++
++ZRELADDR
++      This is the address where the decompressed kernel will be written,
++      and eventually executed.  The following constraint must be valid:
++
++              __virt_to_phys(TEXTADDR) == ZRELADDR
++
++      The initial part of the kernel is carefully coded to be position
++      independent.
++
++INITRD_PHYS
++      Physical address to place the initial RAM disk.  Only relevant if
++      you are using the bootpImage stuff (which only works on the old
++      struct param_struct).
++
++INITRD_VIRT
++      Virtual address of the initial RAM disk.  The following  constraint
++      must be valid:
++
++              __virt_to_phys(INITRD_VIRT) == INITRD_PHYS
++
++PARAMS_PHYS
++      Physical address of the struct param_struct or tag list, giving the
++      kernel various parameters about its execution environment.
++
++
++Kernel Symbols
++--------------
++
++PHYS_OFFSET
++      Physical start address of the first bank of RAM.
++
++PAGE_OFFSET
++      Virtual start address of the first bank of RAM.  During the kernel
++      boot phase, virtual address PAGE_OFFSET will be mapped to physical
++      address PHYS_OFFSET, along with any other mappings you supply.
++      This should be the same value as TASK_SIZE.
++
++TASK_SIZE
++      The maximum size of a user process in bytes.  Since user space
++      always starts at zero, this is the maximum address that a user
++      process can access+1.  The user space stack grows down from this
++      address.
++
++      Any virtual address below TASK_SIZE is deemed to be user process
++      area, and therefore managed dynamically on a process by process
++      basis by the kernel.  I'll call this the user segment.
++
++      Anything above TASK_SIZE is common to all processes.  I'll call
++      this the kernel segment.
++
++      (In other words, you can't put IO mappings below TASK_SIZE, and
++      hence PAGE_OFFSET).
++
++TEXTADDR
++      Virtual start address of kernel, normally PAGE_OFFSET + 0x8000.
++      This is where the kernel image ends up.  With the latest kernels,
++      it must be located at 32768 bytes into a 128MB region.  Previous
++      kernels placed a restriction of 256MB here.
++
++DATAADDR
++      Virtual address for the kernel data segment.  Must not be defined
++      when using the decompressor.
++
++VMALLOC_START
++VMALLOC_END
++      Virtual addresses bounding the vmalloc() area.  There must not be
++      any static mappings in this area; vmalloc will overwrite them.
++      The addresses must also be in the kernel segment (see above).
++      Normally, the vmalloc() area starts VMALLOC_OFFSET bytes above the
++      last virtual RAM address (found using variable high_memory).
++
++VMALLOC_OFFSET
++      Offset normally incorporated into VMALLOC_START to provide a hole
++      between virtual RAM and the vmalloc area.  We do this to allow
++      out of bounds memory accesses (eg, something writing off the end
++      of the mapped memory map) to be caught.  Normally set to 8MB.
++
++Architecture Specific Macros
++----------------------------
++
++BOOT_MEM(pram,pio,vio)
++      `pram' specifies the physical start address of RAM.  Must always
++      be present, and should be the same as PHYS_OFFSET.
++
++      `pio' is the physical address of an 8MB region containing IO for
++      use with the debugging macros in arch/arm/kernel/debug-armv.S.
++
++      `vio' is the virtual address of the 8MB debugging region.
++
++      It is expected that the debugging region will be re-initialised
++      by the architecture specific code later in the code (via the
++      MAPIO function).
++
++BOOT_PARAMS
++      Same as, and see PARAMS_PHYS.
++
++FIXUP(func)
++      Machine specific fixups, run before memory subsystems have been
++      initialised.
++
++MAPIO(func)
++      Machine specific function to map IO areas (including the debug
++      region above).
++
++INITIRQ(func)
++      Machine specific function to initialise interrupts.
++
+--- /dev/null
++++ linux-2.4.27/Documentation/arm/mem_alignment
+@@ -0,0 +1,58 @@
++Too many problems poped up because of unnoticed misaligned memory access in
++kernel code lately.  Therefore the alignment fixup is now unconditionally
++configured in for SA11x0 based targets.  According to Alan Cox, this is a
++bad idea to configure it out, but Russell King has some good reasons for
++doing so on some f***ed up ARM architectures like the EBSA110.  However
++this is not the case on many design I'm aware of, like all SA11x0 based
++ones.
++
++Of course this is a bad idea to rely on the alignment trap to perform
++unaligned memory access in general.  If those access are predictable, you
++are better to use the macros provided by include/asm/unaligned.h.  The
++alignment trap can fixup misaligned access for the exception cases, but at
++a high performance cost.  It better be rare.
++
++Now for user space applications, it is possible to configure the alignment
++trap to SIGBUS any code performing unaligned access (good for debugging bad
++code), or even fixup the access by software like for kernel code.  The later
++mode isn't recommended for performance reasons (just think about the
++floating point emulation that works about the same way).  Fix your code
++instead!
++
++Please note that randomly changing the behaviour without good thought is
++real bad - it changes the behaviour of all unaligned instructions in user
++space, and might cause programs to fail unexpectedly.
++
++To change the alignment trap behavior, simply echo a number into
++/proc/cpu/alignment.  The number is made up from various bits:
++
++bit           behavior when set
++---           -----------------
++
++0             A user process performing an unaligned memory access
++              will cause the kernel to print a message indicating
++              process name, pid, pc, instruction, address, and the
++              fault code.
++
++1             The kernel will attempt to fix up the user process
++              performing the unaligned access.  This is of course
++              slow (think about the floating point emulator) and
++              not recommended for production use.
++
++2             The kernel will send a SIGBUS signal to the user process
++              performing the unaligned access.
++
++Note that not all combinations are supported - only values 0 through 5.
++(6 and 7 don't make sense).
++
++For example, the following will turn on the warnings, but without
++fixing up or sending SIGBUS signals:
++
++      echo 1 > /proc/cpu/alignment
++
++You can also read the content of the same file to get statistical
++information on unaligned access occurrences plus the current mode of
++operation for user space code.
++
++
++Nicolas Pitre, Mar 13, 2001.  Modified Russell King, Nov 30, 2001.
+--- /dev/null
++++ linux-2.4.27/Documentation/arm/memory.txt
+@@ -0,0 +1,74 @@
++              Kernel Memory Layout on ARM Linux
++
++              Russell King <rmk@arm.linux.org.uk>
++                      April 27, 2003 (2.5.68)
++
++This document describes the virtual memory layout which the Linux
++kernel uses for ARM processors.  It indicates which regions are
++free for platforms to use, and which are used by generic code.
++
++The ARM CPU is capable of addressing a maximum of 4GB virtual memory
++space, and this must be shared between user space processes, the
++kernel, and hardware devices.
++
++As the ARM architecture matures, it becomes necessary to reserve
++certain regions of VM space for use for new facilities; therefore
++this document may reserve more VM space over time.
++
++Start         End             Use
++--------------------------------------------------------------------------
++ffff8000      ffffffff        copy_user_page / clear_user_page use.
++                              For SA11xx and Xscale, this is used to
++                              setup a minicache mapping.
++
++ffff1000      ffff7fff        Reserved.
++                              Platforms must not use this address range.
++
++ffff0000      ffff0fff        CPU vector page.
++                              The CPU vectors are mapped here if the
++                              CPU supports vector relocation (control
++                              register V bit.)
++
++ffe00000      fffeffff        Free for platform use, not recommended.
++
++ffc00000      ffdfffff        2MB consistent memory mapping.
++                              Memory returned by the consistent_alloc
++                              low level function will be dynamically
++                              mapped here.
++
++ff000000      ffbfffff        Free for platform use, not recommended.
++
++VMALLOC_END   ff000000        Free for platform use, recommended.
++
++VMALLOC_START VMALLOC_END     vmalloc() / ioremap() space.
++                              Memory returned by vmalloc/ioremap will
++                              be dynamically placed in this region.
++                              VMALLOC_START may be based upon the value
++                              of the high_memory variable.
++
++PAGE_OFFSET   high_memory     Kernel direct-mapped RAM region.
++                              This maps the platforms RAM, and typically
++                              maps all platform RAM in a 1:1 relationship.
++
++TASK_SIZE     PAGE_OFFSET     Kernel module space
++                              Kernel modules inserted via insmod are
++                              placed here using dynamic mappings.
++
++00001000      TASK_SIZE       User space mappings
++                              Per-thread mappings are placed here via
++                              the mmap() system call.
++
++00000000      00000fff        CPU vector page / null pointer trap
++                              CPUs which do not support vector remapping
++                              place their vector page here.  NULL pointer
++                              dereferences by both the kernel and user
++                              space are also caught via this mapping.
++
++Please note that mappings which collide with the above areas may result
++in a non-bootable kernel, or may cause the kernel to (eventually) panic
++at run time.
++
++Since future CPUs may impact the kernel mapping layout, user programs
++must not access any memory which is not mapped inside their 0x0001000
++to TASK_SIZE address range.  If they wish to access these areas, they
++must set up their own mappings using open() and mmap().
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq/core.txt
+@@ -0,0 +1,94 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++                        C P U F r e q    C o r e
++
++
++                  Dominik Brodowski  <linux@brodo.de>
++                   David Kimdon <dwhedon@debian.org>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++Contents:
++---------
++1.  CPUFreq core and interfaces
++2.  CPUFreq notifiers
++
++1. General Information
++=======================
++
++The CPUFreq core code is located in linux/kernel/cpufreq.c. This
++cpufreq code offers a standardized interface for the CPUFreq
++architecture drivers (those pieces of code that do actual
++frequency transitions), as well as to "notifiers". These are device
++drivers or other part of the kernel that need to be informed of
++policy changes (ex. thermal modules like ACPI) or of all
++frequency changes (ex. timing code) or even need to force certain
++speed limits (like LCD drivers on ARM architecture). Additionally, the
++kernel "constant" loops_per_jiffy is updated on frequency changes
++here.
++
++Reference counting is done by cpufreq_get_cpu and cpufreq_put_cpu,
++which make sure that the cpufreq processor driver is correctly
++registered with the core, and will not be unloaded until
++cpufreq_put_cpu is called.
++
++2. CPUFreq notifiers
++====================
++
++CPUFreq notifiers conform to the standard kernel notifier interface.
++See linux/include/linux/notifier.h for details on notifiers.
++
++There are two different CPUFreq notifiers - policy notifiers and
++transition notifiers.
++
++
++2.1 CPUFreq policy notifiers
++----------------------------
++
++These are notified when a new policy is intended to be set. Each
++CPUFreq policy notifier is called three times for a policy transition:
++
++1.) During CPUFREQ_ADJUST all CPUFreq notifiers may change the limit if
++    they see a need for this - may it be thermal considerations or
++    hardware limitations.
++
++2.) During CPUFREQ_INCOMPATIBLE only changes may be done in order to avoid
++    hardware failure.
++
++3.) And during CPUFREQ_NOTIFY all notifiers are informed of the new policy
++   - if two hardware drivers failed to agree on a new policy before this
++   stage, the incompatible hardware shall be shut down, and the user
++   informed of this.
++
++The phase is specified in the second argument to the notifier.
++
++The third argument, a void *pointer, points to a struct cpufreq_policy
++consisting of five values: cpu, min, max, policy and max_cpu_freq. min 
++and max are the lower and upper frequencies (in kHz) of the new
++policy, policy the new policy, cpu the number of the affected CPU or
++CPUFREQ_ALL_CPUS for all CPUs; and max_cpu_freq the maximum supported
++CPU frequency. This value is given for informational purposes only.
++
++
++2.2 CPUFreq transition notifiers
++--------------------------------
++
++These are notified twice when the CPUfreq driver switches the CPU core
++frequency and this change has any external implications.
++
++The second argument specifies the phase - CPUFREQ_PRECHANGE or
++CPUFREQ_POSTCHANGE.
++
++The third argument is a struct cpufreq_freqs with the following
++values:
++cpu   - number of the affected CPU or CPUFREQ_ALL_CPUS
++old   - old frequency
++new   - new frequency
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq/cpu-drivers.txt
+@@ -0,0 +1,210 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++                         C P U   D r i v e r s 
++
++                     - information for developers -
++
++
++                  Dominik Brodowski  <linux@brodo.de>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++Contents:
++---------
++1.   What To Do?
++1.1  Initialization
++1.2  Per-CPU Initialization
++1.3  verify
++1.4  target or setpolicy?
++1.5  target
++1.6  setpolicy
++2.   Frequency Table Helpers
++
++
++
++1. What To Do?
++==============
++
++So, you just got a brand-new CPU / chipset with datasheets and want to
++add cpufreq support for this CPU / chipset? Great. Here are some hints
++on what is neccessary:
++
++
++1.1 Initialization
++------------------
++
++First of all, in an __initcall level 7 or later (preferrably
++module_init() so that your driver is modularized) function check
++whether this kernel runs on the right CPU and the right chipset. If
++so, register a struct cpufreq_driver with the CPUfreq core using
++cpufreq_register_driver()
++
++What shall this struct cpufreq_driver contain? 
++
++cpufreq_driver.name -         The name of this driver.
++
++cpufreq_driver.init -         A pointer to the per-CPU initialization 
++                              function.
++
++cpufreq_driver.verify -               A pointer to a "verfication" funciton.
++
++cpufreq_driver.setpolicy _or_ 
++cpufreq_driver.target -               See below on the differences.
++
++And optionally
++
++cpufreq_driver.exit -         A pointer to a per-CPU cleanup function.
++
++cpufreq_driver.attr -         A pointer to a NULL-terminated list of
++                              "struct freq_attr" which allow to
++                              export values to sysfs.
++
++
++1.2 Per-CPU Initialization
++--------------------------
++
++Whenever a new CPU is registered with the device model, or after the
++cpufreq driver registers itself, the per-CPU initialization fucntion 
++cpufreq_driver.init is called. It takes a struct cpufreq_policy
++*policy as argument. What to do now?
++
++If necessary, activate the CPUfreq support on your CPU (unlock that
++register etc.).
++
++Then, the driver must fill in the following values:
++
++policy->cpuinfo.min_freq _and_
++policy->cpuinfo.max_freq -    the minimum and maximum frequency 
++                              (in kHz) which is supported by 
++                              this CPU
++policy->cpuinfo.transition_latency   the time it takes on this CPU to
++                              switch between two frequencies (if
++                              appropriate, else specify
++                              CPUFREQ_ETERNAL)
++
++policy->cur                   The current operating frequency of
++                              this CPU (if appropriate)
++policy->min, 
++policy->max, 
++policy->policy and, if neccessary,
++policy->governor              must contain the "default policy" for
++                              this CPU. A few moments later,
++                              cpufreq_driver.verify and either
++                              cpufreq_driver.setpolicy or
++                              cpufreq_driver.target is called with
++                              these values.
++
++For setting some of these values, the frequency table helpers might be
++helpful. See the section 2 for more information on them.
++
++
++1.3 verify
++------------
++
++When the user decides a new policy (consisting of
++"policy,governor,min,max") shall be set, this policy must be validated
++so that incompatible values can be corrected. For verifying these
++values, a frequency table helper and/or the
++cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned
++int min_freq, unsigned int max_freq) function might be helpful. See
++section 2 for details on frequency table helpers.
++
++You need to make sure that at least one valid frequency (or operating
++range) is within policy->min and policy->max. If necessary, increase
++policy->max fist, and only if this is no solution, decreas policy->min.
++
++
++1.4 target or setpolicy?
++----------------------------
++
++Most cpufreq drivers or even most cpu frequency scaling algorithms 
++only allow the CPU to be set to one frequency. For these, you use the
++->target call.
++
++Some cpufreq-capable processors switch the frequency between certain
++limits on their own. These shall use the ->setpolicy call
++
++
++1.4. target
++-------------
++
++The target call has three arguments: struct cpufreq_policy *policy,
++unsigned int target_frequency, unsigned int relation.
++
++The CPUfreq driver must set the new frequency when called here. The
++actual frequency must be determined using the following rules:
++
++- keep close to "target_freq"
++- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!)
++- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal
++  target_freq. ("L for lowest, but no lower than")
++- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal
++  target_freq. ("H for highest, but no higher than")
++
++Here again the frequency table helper might assist you - see section 3
++for details.
++
++
++1.5 setpolicy
++---------------
++
++The setpolicy call only takes a struct cpufreq_policy *policy as
++argument. You need to set the lower limit of the in-processor or
++in-chipset dynamic frequency switching to policy->min, the upper limit
++to policy->max, and -if supported- select a performance-oriented
++setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
++powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
++the reference implementation in arch/i386/kernel/cpu/cpufreq/longrun.c
++
++
++
++2. Frequency Table Helpers
++==========================
++
++As most cpufreq processors only allow for being set to a few specific
++frequencies, a "frequency table" with some functions might assist in
++some work of the processor driver. Such a "frequency table" consists
++of an array of struct cpufreq_freq_table entries, with any value in
++"index" you want to use, and the corresponding frequency in
++"frequency". At the end of the table, you need to add a
++cpufreq_freq_table entry with frequency set to CPUFREQ_TABLE_END. And
++if you want to skip one entry in the table, set the frequency to 
++CPUFREQ_ENTRY_INVALID. The entries don't need to be in ascending
++order.
++
++By calling cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
++                                      struct cpufreq_frequency_table *table);
++the cpuinfo.min_freq and cpuinfo.max_freq values are detected, and
++policy->min and policy->max are set to the same values. This is
++helpful for the per-CPU initialization stage.
++
++int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
++                                   struct cpufreq_frequency_table *table);
++assures that at least one valid frequency is within policy->min and
++policy->max, and all other criteria are met. This is helpful for the
++->verify call.
++
++int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
++                                   struct cpufreq_frequency_table *table,
++                                   unsigned int target_freq,
++                                   unsigned int relation,
++                                   unsigned int *index);
++
++is the corresponding frequency table helper for the ->target
++stage. Just pass the values to this function, and the unsigned int
++index returns the number of the frequency table entry which contains
++the frequency the CPU shall be set to. PLEASE NOTE: This is not the
++"index" which is in this cpufreq_table_entry.index, but instead
++cpufreq_table[index]. So, the new frequency is
++cpufreq_table[index].frequency, and the value you stored into the
++frequency table "index" field is
++cpufreq_table[index].index.
++
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq/governors.txt
+@@ -0,0 +1,155 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++                    C P U F r e q   G o v e r n o r s
++
++                 - information for users and developers -
++
++
++                  Dominik Brodowski  <linux@brodo.de>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++Contents:
++---------
++1.   What is a CPUFreq Governor?
++
++2.   Governors In the Linux Kernel
++2.1  Performance
++2.2  Powersave
++2.3  Userspace
++
++3.   The Governor Interface in the CPUfreq Core
++
++
++
++1. What Is A CPUFreq Governor?
++==============================
++
++Most cpufreq drivers (in fact, all except one, longrun) or even most
++cpu frequency scaling algorithms only offer the CPU to be set to one
++frequency. In order to offer dynamic frequency scaling, the cpufreq
++core must be able to tell these drivers of a "target frequency". So
++these specific drivers will be transformed to offer a "->target"
++call instead of the existing "->setpolicy" call. For "longrun", all
++stays the same, though.
++
++How to decide what frequency within the CPUfreq policy should be used?
++That's done using "cpufreq governors". Two are already in this patch
++-- they're
++set the frequency statically to the lowest or highest frequency,
++respectively. At least two more such governors will be ready for
++addition in the near future, but likely many more as there are various
++different theories and models about dynamic frequency scaling
++around. Using such a generic interface as cpufreq offers to scaling
++governors, these can be tested extensively, and the best one can be
++selected for each specific use.
++
++Basically, it's the following flow graph:
++
++CPU can be set to switch independetly  |         CPU can only be set
++      within specific "limits"                 |       to specific frequencies
++
++                                 "CPUfreq policy"
++              consists of frequency limits (policy->{min,max})
++                   and CPUfreq governor to be used
++                       /                    \
++                      /                      \
++                     /                       the cpufreq governor decides
++                    /                        (dynamically or statically)
++                   /                         what target_freq to set within
++                  /                          the limits of policy->{min,max}
++                 /                                \
++                /                                  \
++      Using the ->setpolicy call,              Using the ->target call,
++          the limits and the                    the frequency closest
++           "policy" is set.                     to target_freq is set.
++                                                It is assured that it
++                                                is within policy->{min,max}
++
++
++2. Governors In the Linux Kernel
++================================
++
++2.1 Performance
++---------------
++
++The CPUfreq governor "performance" sets the CPU statically to the
++highest frequency within the borders of scaling_min_freq and
++scaling_max_freq.
++
++
++2.1 Powersave
++-------------
++
++The CPUfreq governor "powersave" sets the CPU statically to the
++lowest frequency within the borders of scaling_min_freq and
++scaling_max_freq.
++
++
++2.2 Userspace
++-------------
++
++The CPUfreq governor "userspace" allows the user, or any userspace
++program running with UID "root", to set the CPU to a specifc frequency
++by making a sysfs file "scaling_setspeed" available in the CPU-device
++directory.
++
++
++
++3. The Governor Interface in the CPUfreq Core
++=============================================
++
++A new governor must register itself with the CPUfreq core using
++"cpufreq_register_governor". The struct cpufreq_governor, which has to
++be passed to that function, must contain the following values:
++
++governor->name -          A unique name for this governor
++governor->governor -      The governor callback function
++governor->owner       -           .THIS_MODULE for the governor module (if 
++                          appropriate)
++
++The governor->governor callback is called with the current (or to-be-set)
++cpufreq_policy struct for that CPU, and an unsigned int event. The
++following events are currently defined:
++
++CPUFREQ_GOV_START:   This governor shall start its duty for the CPU
++                   policy->cpu
++CPUFREQ_GOV_STOP:    This governor shall end its duty for the CPU
++                   policy->cpu
++CPUFREQ_GOV_LIMITS:  The limits for CPU policy->cpu have changed to
++                   policy->min and policy->max.
++
++If you need other "events" externally of your driver, _only_ use the
++cpufreq_governor_l(unsigned int cpu, unsigned int event) call to the
++CPUfreq core to ensure proper locking.
++
++
++The CPUfreq governor may call the CPU processor driver using one of
++these two functions:
++
++inline int cpufreq_driver_target(struct cpufreq_policy *policy,
++                                 unsigned int target_freq,
++                                 unsigned int relation);
++
++inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
++                                   unsigned int target_freq,
++                                   unsigned int relation);
++
++target_freq must be within policy->min and policy->max, of course.
++What's the difference between these two functions? When your governor
++still is in a direct code path of a call to governor->governor, the
++cpufreq_driver_sem lock is still held in the cpufreq core, and there's
++no need to lock it again (in fact, this would cause a deadlock). So
++use cpufreq_driver_target only in these cases. In all other cases (for
++example, when there's a "daemonized" function that wakes up every
++second), use cpufreq_driver_target_l to lock the cpufreq_driver_sem
++before the command is passed to the cpufreq processor driver.
++
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq/index.txt
+@@ -0,0 +1,56 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++
++
++
++                  Dominik Brodowski  <linux@brodo.de>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++
++Documents in this directory:
++----------------------------
++core.txt      -       General description of the CPUFreq core and
++                      of CPUFreq notifiers
++
++cpu-drivers.txt -     How to implement a new cpufreq processor driver
++
++governors.txt -       What are cpufreq governors and how to
++                      implement them?
++
++index.txt     -       File index, Mailing list and Links (this document)
++
++user-guide.txt        -       User Guide to CPUFreq
++
++
++Mailing List
++------------
++There is a CPU frequency changing CVS commit and general list where
++you can report bugs, problems or submit patches. To post a message,
++send an email to cpufreq@www.linux.org.uk, to subscribe go to
++http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
++mailing list are available to subscribers at
++http://www.linux.org.uk/mailman/private/cpufreq/.
++
++
++Links
++-----
++the FTP archives:
++* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
++
++how to access the CVS repository:
++* http://cvs.arm.linux.org.uk/
++
++the CPUFreq Mailing list:
++* http://www.linux.org.uk/mailman/listinfo/cpufreq
++
++Clock and voltage scaling for the SA-1100:
++* http://www.lart.tudelft.nl/projects/scaling
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq/user-guide.txt
+@@ -0,0 +1,166 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++                           U S E R   G U I D E
++
++
++                  Dominik Brodowski  <linux@brodo.de>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++Contents:
++---------
++1. Supported Architectures and Processors
++1.1 ARM
++1.2 x86
++1.3 sparc64
++
++2. "Policy" / "Governor"?
++2.1 Policy
++2.2 Governor
++
++3. How to change the CPU cpufreq policy and/or speed
++3.1 Preferred interface: sysfs
++3.2 Deprecated interfaces
++
++
++
++1. Supported Architectures and Processors
++=========================================
++
++1.1 ARM
++-------
++
++The following ARM processors are supported by cpufreq:
++
++ARM Integrator
++ARM-SA1100
++ARM-SA1110
++
++
++1.2 x86
++-------
++
++The following processors for the x86 architecture are supported by cpufreq:
++
++AMD Elan - SC400, SC410
++AMD mobile K6-2+
++AMD mobile K6-3+
++Cyrix Media GXm
++Intel mobile PIII [*] and Intel mobile PIII-M on certain chipsets
++Intel Pentium 4, Intel Xeon
++National Semiconductors Geode GX
++Transmeta Crusoe
++varios processors on some ACPI 2.0-compatible systems [**]
++
++[*] only certain Intel mobile PIII processors are supported. If you
++know that you own a speedstep-capable processor, pass the option
++"speedstep_coppermine=1" to the module speedstep.o
++
++[**] Only if "ACPI Processor Performance States" are available
++to the ACPI<->BIOS interface.
++
++
++1.3 sparc64
++-----------
++
++The following processors for the sparc64 architecture are supported by
++cpufreq:
++
++UltraSPARC-III
++
++
++
++2. "Policy" / "Governor" ?
++==========================
++
++Some CPU frequency scaling-capable processor switch between varios
++frequencies and operating voltages "on the fly" without any kernel or
++user involvement. This guarantuees very fast switching to a frequency
++which is high enough to serve the user's needs, but low enough to save
++power.
++
++
++2.1 Policy
++----------
++
++On these systems, all you can do is select the lower and upper
++frequency limit as well as whether you want more aggressive
++power-saving or more instantly avaialble processing power.
++
++
++2.2 Governor
++------------
++
++On all other cpufreq implementations, these boundaries still need to
++be set. Then, a "governor" must be selected. Such a "governor" decides
++what speed the processor shall run within the boundaries. One such
++"governor" is the "userspace" governor. This one allows the user - or
++a yet-to-implement userspace program - to decide what specific speed
++the processor shall run at.
++
++
++3. How to change the CPU cpufreq policy and/or speed
++====================================================
++
++3.1 Preferred Interface: sysfs
++------------------------------
++
++The preferred interface is located in the sysfs filesystem. If you
++mounted it at /sys, the cpufreq interface is located in a subdirectory
++"cpufreq" within the cpu-device directory
++(e.g. /sys/devices/sys/cpu0/cpufreq/ for the first CPU).
++
++cpuinfo_min_freq :            this file shows the minimum operating
++                              frequency the processor can run at(in kHz) 
++cpuinfo_max_freq :            this file shows the maximum operating
++                              frequency the processor can run at(in kHz) 
++scaling_driver :              this file shows what cpufreq driver is
++                              used to set the frequency on this CPU
++
++scaling_available_governors : this file shows the CPUfreq governors
++                              available in this kernel. You can see the
++                              currently activated governor in
++
++scaling_governor,             and by "echoing" the name of another
++                              governor you can change it. Please note
++                              that some governors won't load - they only
++                              work on some specific architectures or
++                              processors.
++scaling_min_freq and 
++scaling_max_freq              show the current "policy limits" (in
++                              kHz). By echoing new values into these
++                              files, you can change these limits.
++
++
++If you have selected the "userspace" governor which allows you to
++set the CPU operating frequency to a specific value, you can read out
++the current frequency in
++
++scaling_setspeed.             By "echoing" a new frequency into this
++                              you can change the speed of the CPU,
++                              but only within the limits of
++                              scaling_min_freq and scaling_max_freq.
++                              
++
++3.2 Deprecated Interfaces
++-------------------------
++
++Depending on your kernel configuration, you might find the following 
++cpufreq-related files:
++/proc/cpufreq
++/proc/sys/cpu/*/speed
++/proc/sys/cpu/*/speed-min
++/proc/sys/cpu/*/speed-max
++
++These are files for deprecated interfaces to cpufreq, which offer far
++less functionality. Because of this, these interfaces aren't described
++here.
++
+--- /dev/null
++++ linux-2.4.27/Documentation/cpufreq-old
+@@ -0,0 +1,332 @@
++     CPU frequency and voltage scaling code in the Linux(TM) kernel
++
++
++                       L i n u x    C P U F r e q
++
++
++
++
++                   Dominik Brodowski <devel@brodo.de>
++
++
++
++   Clock scaling allows you to change the clock speed of the CPUs on the
++    fly. This is a nice method to save battery power, because the lower
++            the clock speed, the less power the CPU consumes.
++
++
++
++Contents:
++---------
++1.  Supported architectures
++2.  User interface
++2.1   Sample script for command line interface
++3.  CPUFreq core and interfaces
++3.1   General information
++3.2   CPUFreq notifiers
++3.3   CPUFreq architecture drivers
++4.  Mailing list and Links
++
++
++
++1. Supported architectures
++==========================
++
++Some architectures detect the lowest and highest possible speed
++settings, while others rely on user information on this. For the
++latter, a boot parameter is required, for the former, you can specify
++one to set the limits between speed settings may occur. 
++The boot parameter has the following syntax:
++
++     cpufreq=minspeed-maxspeed
++
++with both minspeed and maxspeed being given in kHz. To set the lower
++limit to 59 MHz and the upper limit to 221 MHz, specify:
++
++      cpufreq=59000-221000
++
++Check the "Speed Limits Detection" information below on whether
++the driver detects the lowest and highest allowed speed setting
++automatically.
++
++
++ARM Integrator:
++    SA 1100, SA1110
++--------------------------------
++    Speed Limits Detection: On Integrators, the minimum speed is set
++    and the maximum speed has to be specified using the boot
++    parameter. On SA11x0s, the frequencies are fixed (59 - 287 MHz)
++
++
++AMD Elan:
++    SC400, SC410
++--------------------------------
++    Speed Limits Detection: Not implemented. You need to specify the
++    minimum and maximum frequency in the boot parameter (see above).
++
++
++VIA Cyrix Longhaul:
++    VIA Samuel/CyrixIII, VIA Cyrix Samuel/C3, 
++    VIA Cyrix Ezra, VIA Cyrix Ezra-T
++--------------------------------
++    Speed Limits Detection: working. No need for boot parameters.
++    NOTE: Support for certain processors is currently disabled,
++    waiting on updated docs from VIA.
++
++
++Intel SpeedStep:
++    certain mobile Intel Pentium III (Coppermine), and all mobile
++    Intel Pentium III-M (Tulatin) and mobile Intel Pentium 4 P4-Ms.
++--------------------------------
++    Speed Limits Detection: working. No need for boot parameters.
++    NOTE: 
++    1.) mobile Intel Pentium III (Coppermine):
++        The SpeedStep interface may only be used on SpeedStep
++        capable processors. Unforunately, due to lack of documentation,
++        such detection is not yet possible on mobile Intel PIII
++        (Coppermine) processors. In order to activate SpeedStep on such a
++        processor, you have to remove one line manually in
++        linux/drivers/arch/i386/speedstep.c
++
++
++P4 CPU Clock Modulation:
++    Intel Pentium 4 Xeon processors
++--------------------------------
++    Speed Limits Detection: Not implemented. You need to specify the
++    minimum and maximum frequency in the boot parameter (see above).
++
++
++
++2. User Interface
++=================
++
++CPUFreq uses a "sysctl" interface which is located in 
++      /proc/sys/cpu/0/          on UP (uniprocessor) kernels, or 
++      /proc/sys/cpu/any/        on SMP (symmetric multiprocessoring) kernels.
++
++
++In this directory, you will find three files of importance for
++CPUFreq: speed-max, speed-min, and speed: 
++
++speed             shows the current CPU frequency in kHz, 
++speed-min         the minimal supported CPU frequency, and
++speed-max         the maximal supported CPU frequency. 
++
++Please note that you might have to specify these limits as a boot
++parameter depending on the architecture (see above).
++
++
++To change the CPU frequency, "echo" the desired CPU frequency (in kHz)
++to speed. For example, to set the CPU speed to the lowest/highest
++allowed frequency do:
++
++root@notebook:# cat /proc/sys/cpu/0/speed-min > /proc/sys/cpu/0/speed
++root@notebook:# cat /proc/sys/cpu/0/speed-max > /proc/sys/cpu/0/speed
++
++
++2.1   Sample script for command line interface
++**********************************************
++
++
++Michael Ossmann <mike@ossmann.com> has written a small command line
++interface for the infinitely lazy.
++
++#!/bin/bash
++#
++# /usr/local/bin/freq
++#   simple command line interface to cpufreq
++
++[ -n "$1" ] && case "$1" in
++  "min" )
++    # set frequency to minimum
++    cat /proc/sys/cpu/0/speed-min >/proc/sys/cpu/0/speed
++    ;;
++  "max" )
++    # set frequency to maximum
++    cat /proc/sys/cpu/0/speed-max >/proc/sys/cpu/0/speed
++    ;;
++  * )
++    echo "Usage: $0 [min|max]"
++    echo "  min: set frequency to minimum and display new frequency"
++    echo "  max: set frequency to maximum and display new frequency"
++    echo "  no options: display current frequency"
++    exit 1
++    ;;
++esac
++
++# display current frequency
++cat /proc/sys/cpu/0/speed
++exit 0
++
++
++
++3.  CPUFreq core and interfaces
++===============================
++
++3.1   General information
++*************************
++
++The CPUFreq core code is located in linux/kernel/cpufreq.c. This
++cpufreq code offers a standardized interface for the CPUFreq
++architecture drivers (those pieces of code that do the actual
++frequency transition), as well as to "notifiers". These are device
++drivers or other part of the kernel that need to be informed of
++frequency changes (like timing code) or even need to force certain
++speed limits (like LCD drivers on ARM architecture). Aditionally, the
++kernel "constant" loops_per_jiffy is updated on frequency changes
++here.
++
++
++3.2   CPUFreq notifiers
++***********************
++
++CPUFreq notifiers are kernel code that need to be called to either
++a) define certain minimum or maximum speed settings,
++b) be informed of frequency changes in advance of the transition, or
++c) be informed of frequency changes directly after the transition.
++
++A standard kernel notifier interface is offered for this. See
++linux/include/linux/notifier.h for details on notifiers.
++
++
++Data and value passed to CPUFreq notifiers
++------------------------------------------
++The second argument passed to any notifier is an unsigned int stating
++the phase of the transition: 
++CPUFREQ_MINMAX during the process of determing a valid new CPU
++             frequency,
++CPUFREQ_PRECHANGE right before the transition, and 
++CPUFREQ_POSTCHANGE right after the transition.
++
++The third argument, a void *pointer, points to a struct
++cpufreq_freqs. This consists of four values: min, max, cur and new.
++
++min and max are the current speed limits. Please note: Never update
++these values directly, use cpufreq_updateminmax(struct cpufreq_freqs
++*freqs, unsigned int min, unsigned int max) instead. cur is the
++current/old speed, and new is the new speed, but might only be valid
++on CPUFREQ_PRECHANGE or CPUFREQ_POSTCHANGE.
++
++Each notifier gets called all three times on any transition:
++
++CPUFREQ_MINMAX
++Here the notifier is supposed to update the min and max values to the
++limits the protected device / kernel code needs. As stated above,
++always use cpufreq_updateminmax for this.
++
++CPUFREQ_PRECHANGE
++CPUFREQ_POSTCHANGE
++Here the notifier is supposed to update all internal (e.g. device
++driver) code which is dependend on the CPU frequency.
++
++
++3.3   CPUFreq architecture drivers
++**********************************
++
++CPUFreq architecture drivers are the pieces of kernel code that
++actually perform CPU frequency transitions. These need to be
++initialised seperately (seperate initcalls), and may be
++modularized. They interact with the CPUFreq core in the following way:
++
++
++cpufreq_register()
++------------------
++cpufreq_register registers an arch driver to the CPUFreq core. Please
++note that only one arch driver may be registered at any time, -EBUSY
++is returned when an arch driver is already registered. The argument to
++cpufreq_register, cpufreq_driver_t driver, is described later.
++
++
++cpufreq_unregister()
++--------------------
++cpufreq_unregister unregisters an arch driver, e.g. on module
++unloading. Please note that there is no check done that this is called
++from the driver which actually registered itself to the core, so
++please only call this function when you are sure the arch driver got
++registered correctly before.
++
++
++struct cpufreq_driver
++----------------
++On initialisation, the arch driver is supposed to pass the following
++entries in struct cpufreq_driver cpufreq_driver:
++
++cpufreq_verify_t validate: This is a pointer to a function with the
++following definition: 
++     unsigned int validating_function (unsigned int kHz). 
++It is called right before a transition occurs. The proposed new
++speed setting is passed as an argument in kHz; the validating code
++should verify this is a valid speed setting which is currently
++supported by the CPU. It shall return the closest valid CPU frequency
++in kHz.
++
++cpufreq_setspeed_t setspeed: This is a pointer to a function with the
++following definition: 
++     void setspeed_function (unsigned int kHz). 
++This function shall perform the transition to the new CPU frequency 
++given as argument in kHz. Note that this argument is exactly the same
++as the one returned by cpufreq_verify_t validate.
++
++
++unsigned int freq.cur: The current CPU core frequency. Note that this
++is a requirement while the next two entries are optional.
++
++
++unsigned int freq.min (optional): The minimal CPU core frequency this
++CPU supports. This value may be limited further by the
++cpufreq_verify_t validate function, and so this value should be the
++minimal core frequency allowed "theoretically" on this system in this
++configuration.
++
++
++unsigned int freq.max (optional): The maximum CPU core frequency this
++CPU supports. This value may be limited further by the
++cpufreq_verify_t validate function, and so this value should be the
++maximum core frequency allowed "theoretically" on this system in this
++configuration.
++
++
++Some Requirements to CPUFreq architecture drivers
++-------------------------------------------------
++* Only call cpufreq_register() when the ability to switch CPU
++  frequencies is _verified_ or can't be missing
++* cpufreq_unregister() may only be called if cpufreq_register() has
++  been successfully(!) called before
++* All CPUs have to be set to the same speed whenever setspeed() is
++  called
++* Be aware that there is currently no error management in the
++  setspeed() code in the CPUFreq core. So only call yourself a
++  cpufreq_driver if you are really a working cpufreq_driver!
++
++
++
++4.  Mailing list and Links
++**************************
++
++
++Mailing List
++------------
++There is a CPU frequency changing CVS commit and general list where
++you can report bugs, problems or submit patches. To post a message,
++send an email to cpufreq@www.linux.org.uk, to subscribe go to
++http://www.linux.org.uk/mailman/listinfo/cpufreq. Previous post to the
++mailing list are available to subscribers at
++http://www.linux.org.uk/mailman/private/cpufreq/.
++
++
++Links
++-----
++the FTP archives:
++* ftp://ftp.linux.org.uk/pub/linux/cpufreq/
++
++how to access the CVS repository:
++* http://www.arm.linux.org.uk/cvs/
++
++the CPUFreq Mailing list:
++* http://www.linux.org.uk/mailman/listinfo/cpufreq
++
++Clock and voltage scaling for the SA-1100:
++* http://www.lart.tudelft.nl/projects/scaling
++
++CPUFreq project homepage
++* http://www.brodo.de/cpufreq/
+--- /dev/null
++++ linux-2.4.27/Documentation/l3/structure
+@@ -0,0 +1,36 @@
++L3 Bus Driver
++-------------
++
++The structure of the driver is as follows:
++
++      +----------+    +----------+    +----------+
++      | client 1 |    | client 2 |    | client 3 |
++      +-----^----+    +----^-----+    +----^-----+
++            |              |               |
++      +-----v--------------v---------------v-----+
++      |                                          |
++      +-----^-------+              +-------^-----+
++            |       |     core     |       |
++      +-----v----+  |              |  +----v-----+
++      | device   |  |              |  | device   |
++      | driver 1 |  |              |  | driver 2 |
++      +-----^----+  |              |  +----^-----+
++            |       |   services   |       |
++      +-----v-------+              +-------v-----+
++      |                                          |
++      +-----------------^----^-------------------+
++                        |    |
++                        |  +-v---------+
++                        |  | algorithm |
++                        |  |  driver   |
++                        |  +-v---------+
++                        |    |
++                      +-v----v-+
++                      |  bus   |
++                      | driver |
++                      +--------+
++
++Clients talk to the core to attach device drivers and bus adapters, and
++to instruct device drivers to perform actions.  Device drivers then talk
++to the core to perform L3 bus transactions via the algorithm driver and
++ultimately bus driver.
+--- /dev/null
++++ linux-2.4.27/Documentation/serial/driver
+@@ -0,0 +1,208 @@
++
++                      Low Level Serial API
++                      --------------------
++
++
++   $Id: driver,v 1.3 2001/11/24 23:24:47 rmk Exp $
++
++
++This document is meant as a brief overview of some aspects of the new serial
++driver.  It is not complete, any questions you have should be directed to
++<rmk@arm.linux.org.uk>
++
++The reference implementation is contained within serial_amba.c.
++
++
++
++Low Level Serial Hardware Driver
++--------------------------------
++
++The low level serial hardware driver is responsible for supplying port
++information (defined by uart_port) and a set of control methods (defined
++by uart_ops) to the core serial driver.  The low level driver is also
++responsible for handling interrupts for the port, and providing any
++console support.
++
++
++Console Support
++---------------
++
++The serial core provides a few helper functions.  This includes identifing
++the correct port structure (via uart_get_console) and decoding command line
++arguments (uart_parse_options).
++
++
++Locking
++-------
++
++Generally, all locking is done by the core driver, except for the interrupt
++functions.  It is the responsibility of the low level hardware driver to
++perform the necessary locking there using info->lock. (since it is running
++in an interrupt, you only need to use spin_lock() and spin_unlock() from
++the interrupt handler).
++
++
++uart_ops
++--------
++
++The uart_ops structure is the main interface between serial_core and the
++hardware specific driver.  It contains all the methods to control the
++hardware.
++
++  tx_empty(port)
++      This function tests whether the transmitter fifo and shifter
++      for the port described by 'port' is empty.  If it is empty,
++      this function should return TIOCSER_TEMT, otherwise return 0.
++      If the port does not support this operation, then it should
++      return TIOCSER_TEMT.
++
++  set_mctrl(port, mctrl)
++      This function sets the modem control lines for port described
++      by 'port' to the state described by mctrl.  The relevant bits
++      of mctrl are:
++              - TIOCM_RTS     RTS signal.
++              - TIOCM_DTR     DTR signal.
++              - TIOCM_OUT1    OUT1 signal.
++              - TIOCM_OUT2    OUT2 signal.
++      If the appropriate bit is set, the signal should be driven
++      active.  If the bit is clear, the signal should be driven
++      inactive.
++
++  get_mctrl(port)
++      Returns the current state of modem control inputs.  The state
++      of the outputs should not be returned, since the core keeps
++      track of their state.  The state information should include:
++              - TIOCM_DCD     state of DCD signal
++              - TIOCM_CTS     state of CTS signal
++              - TIOCM_DSR     state of DSR signal
++              - TIOCM_RI      state of RI signal
++      The bit is set if the signal is currently driven active.  If
++      the port does not support CTS, DCD or DSR, the driver should
++      indicate that the signal is permanently active.  If RI is
++      not available, the signal should not be indicated as active.
++
++  stop_tx(port,from_tty)
++      Stop transmitting characters.  This might be due to the CTS
++      line becoming inactive or the tty layer indicating we want
++      to stop transmission.
++
++  start_tx(port,nonempty,from_tty)
++      start transmitting characters.  (incidentally, nonempty will
++      always be nonzero, and shouldn't be used - it will be dropped).
++
++  stop_rx(port)
++      Stop receiving characters; the port is in the process of
++      being closed.
++
++  enable_ms(port)
++      Enable the modem status interrupts.
++
++  break_ctl(port,ctl)
++      Control the transmission of a break signal.  If ctl is
++      nonzero, the break signal should be transmitted.  The signal
++      should be terminated when another call is made with a zero
++      ctl.
++
++  startup(port,info)
++      Grab any interrupt resources and initialise any low level driver
++      state.  Enable the port for reception.  It should not activate
++      RTS nor DTR; this will be done via a separate call to set_mctrl.
++
++  shutdown(port,info)
++      Disable the port, disable any break condition that may be in
++      effect, and free any interrupt resources.  It should not disable
++      RTS nor DTR; this will have already been done via a separate
++      call to set_mctrl.
++
++  change_speed(port,cflag,iflag,quot)
++      Change the port parameters, including word length, parity, stop
++      bits.  Update read_status_mask and ignore_status_mask to indicate
++      the types of events we are interested in receiving.  Relevant
++      cflag bits are:
++              CSIZE   - word size
++              CSTOPB  - 2 stop bits
++              PARENB  - parity enable
++              PARODD  - odd parity (when PARENB is in force)
++              CREAD   - enable reception of characters (if not set,
++                        still receive characters from the port, but
++                        throw them away.
++              CRTSCTS - if set, enable CTS status change reporting
++              CLOCAL  - if not set, enable modem status change
++                        reporting.
++      Relevant iflag bits are:
++              INPCK   - enable frame and parity error events to be
++                        passed to the TTY layer.
++              BRKINT
++              PARMRK  - both of these enable break events to be
++                        passed to the TTY layer.
++
++              IGNPAR  - ignore parity and framing errors
++              IGNBRK  - ignore break errors,  If IGNPAR is also
++                        set, ignore overrun errors as well.
++      The interaction of the iflag bits is as follows (parity error
++      given as an example):
++      Parity error    INPCK   IGNPAR
++      None            n/a     n/a     character received
++      Yes             n/a     0       character discarded
++      Yes             0       1       character received, marked as
++                                      TTY_NORMAL
++      Yes             1       1       character received, marked as
++                                      TTY_PARITY
++
++  pm(port,state,oldstate)
++      perform any power management related activities on the specified
++      port.  state indicates the new state (defined by ACPI D0-D3),
++      oldstate indicates the previous state.  Essentially, D0 means
++      fully on, D3 means powered down.
++
++      This function should not be used to grab any resources.
++
++  type(port)
++      Return a pointer to a string constant describing the specified
++      port, or return NULL, in which case the string 'unknown' is
++      substituted.
++
++  release_port(port)
++      Release any memory and IO region resources currently in use by
++      the port.
++
++  request_port(port)
++      Request any memory and IO region resources required by the port.
++      If any fail, no resources should be registered when this function
++      returns, and it should return -EBUSY on failure.
++
++  config_port(port,type)
++      Perform any autoconfiguration steps required for the port.  `type`
++      contains a bit mask of the required configuration.  UART_CONFIG_TYPE
++      indicates that the port requires detection and identification.
++      port->type should be set to the type found, or PORT_UNKNOWN if
++      no port was detected.
++
++      UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal,
++      which should be probed using standard kernel autoprobing techniques.
++      This is not necessary on platforms where ports have interrupts
++      internally hard wired (eg, system on a chip implementations).
++
++  verify_port(port,serinfo)
++      Verify the new serial port information contained within serinfo is
++      suitable for this port type.
++
++  ioctl(port,cmd,arg)
++      Perform any port specific IOCTLs.  IOCTL commands must be defined
++      using the standard numbering system found in <asm/ioctl.h>
++
++
++Other notes
++-----------
++
++It is intended some day to drop the 'unused' entries from uart_port, and
++allow low level drivers to register their own individual uart_port's with
++the core.  This will allow drivers to use uart_port as a pointer to a
++structure containing both the uart_port entry with their own extensions,
++thus:
++
++      struct my_port {
++              struct uart_port        port;
++              int                     my_stuff;
++      };
++
+--- linux-2.4.27/MAINTAINERS~2.4.27-vrs1
++++ linux-2.4.27/MAINTAINERS
+@@ -262,6 +262,8 @@
+ ARM/STRONGARM110 PORT
+ P:    Russell King
+ M:    rmk@arm.linux.org.uk
++P:    Vincent Sanders
++M:    vince@arm.linux.org.uk
+ L:    linux-arm-kernel@lists.arm.linux.org.uk
+ W:    http://www.arm.linux.org.uk/
+ S:    Maintained
+--- linux-2.4.27/Makefile~2.4.27-vrs1
++++ linux-2.4.27/Makefile
+@@ -1,7 +1,7 @@
+ VERSION = 2
+ PATCHLEVEL = 4
+ SUBLEVEL = 27
+-EXTRAVERSION =
++EXTRAVERSION = -vrs1
+ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+@@ -137,7 +137,9 @@
+ DRIVERS-$(CONFIG_ACPI_BOOT) += drivers/acpi/acpi.o
+ DRIVERS-$(CONFIG_PARPORT) += drivers/parport/driver.o
+-DRIVERS-y += drivers/char/char.o \
++DRIVERS-$(CONFIG_L3) += drivers/l3/l3.o
++DRIVERS-y += drivers/serial/serial.o \
++      drivers/char/char.o \
+       drivers/block/block.o \
+       drivers/misc/misc.o \
+       drivers/net/net.o
+@@ -161,6 +163,7 @@
+ DRIVERS-y += drivers/cdrom/driver.o
+ endif
++DRIVERS-$(CONFIG_SSI) += drivers/ssi/ssi.o
+ DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+ DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
+ DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtdlink.o
+@@ -194,6 +197,8 @@
+ DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o
+ DRIVERS-$(CONFIG_ISDN_BOOL) += drivers/isdn/vmlinux-obj.o
+ DRIVERS-$(CONFIG_CRYPTO) += crypto/crypto.o
++DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o
++DRIVERS-$(CONFIG_ARCH_AT91RM9200) += drivers/at91/at91drv.o
+ DRIVERS := $(DRIVERS-y)
+@@ -274,11 +279,6 @@
+ export        NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
+-.S.s:
+-      $(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $<
+-.S.o:
+-      $(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $<
+-
+ Version: dummy
+       @rm -f include/linux/compile.h
+--- linux-2.4.27/Rules.make~2.4.27-vrs1
++++ linux-2.4.27/Rules.make
+@@ -51,15 +51,15 @@
+ #
+ %.s: %.c
+-      $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -S $< -o $@
++      $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@
+ %.i: %.c
+-      $(CPP) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) $< > $@
++      $(CPP) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) $< > $@
+ %.o: %.c
+-      $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $<
++      $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$(*F)) $(CFLAGS_$@) -c -o $@ $<
+       @ ( \
+-          echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$@))))' ; \
++          echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$(*F)) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$(*F)) $$(CFLAGS_$@))))' ; \
+           echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+           echo 'endif' \
+       ) > $(dir $@)/.$(notdir $@).flags
+@@ -272,7 +272,8 @@
+ endif # CONFIG_MODVERSIONS
+ ifneq "$(strip $(export-objs))" ""
+-$(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h
++$(export-objs): $(TOPDIR)/include/linux/modversions.h
++$(export-objs): %.o: %.c
+       $(CC) $(CFLAGS) $(EXTRA_CFLAGS_nostdinc) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+       @ ( \
+           echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS_nostdinc) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS_nostdinc) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
+--- linux-2.4.27/arch/alpha/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/alpha/config.in
+@@ -7,6 +7,7 @@
+ define_bool CONFIG_UID16 n
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_name "Kernel configuration of Linux for Alpha machines"
+--- linux-2.4.27/arch/arm/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/Makefile
+@@ -52,7 +52,7 @@
+ 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
+-AFLAGS                +=$(apcs-y) $(arch-y) -mno-fpu -msoft-float
++AFLAGS                +=$(apcs-y) $(arch-y) -msoft-float
+ ifeq ($(CONFIG_CPU_26),y)
+ PROCESSOR     := armo
+--- linux-2.4.27/arch/arm/boot/compressed/head.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/boot/compressed/head.S
+@@ -40,6 +40,14 @@
+               .macro  writeb, rb
+               strb    \rb, [r3, #0x3f8 << 2]
+               .endm
++#elif defined(CONFIG_ARCH_RISCSTATION)
++              .macro  loadsp, rb
++              mov     \rb, #0x03000000
++              orr     \rb, \rb, #0x00010000
++              .endm
++              .macro  writeb, rb
++              strb    \rb, [r3, #0x3f8 << 2]
++              .endm
+ #elif defined(CONFIG_ARCH_INTEGRATOR)
+               .macro  loadsp, rb
+               mov     \rb, #0x16000000
+@@ -396,6 +404,20 @@
+               mcr     p15, 0, r0, c1, c0, 0   @ load control register
+               mov     pc, r12
++__arm7_cache_on:
++              mov     r12, lr
++              bl      __setup_mmu
++              mov     r0, #0
++              mcr     p15, 0, r0, c7, c0, 0   @ invalidate whole cache v3
++              mcr     p15, 0, r0, c5, c0, 0   @ invalidate whole TLB v3
++              mcr     p15, 0, r3, c2, c0, 0   @ load page table pointer
++              mov     r0, #-1
++              mcr     p15, 0, r0, c3, c0, 0   @ load domain access control
++              mov     r0, #0x7d
++              mcr     p15, 0, r0, c1, c0, 0   @ load control register
++              mov     pc, r12
++
++
+ /*
+  * All code following this line is relocatable.  It is relocated by
+  * the above code to the end of the decompressed kernel image and
+@@ -480,9 +502,9 @@
+               .word   0x41007000              @ ARM7/710
+               .word   0xfff8fe00
++              b       __arm7_cache_on
+               b       __arm7_cache_off
+-              b       __arm7_cache_off
+-              mov     pc, lr
++              b       __armv3_cache_flush
+               .word   0x41807200              @ ARM720T (writethrough)
+               .word   0xffffff00
+@@ -490,14 +512,14 @@
+               b       __armv4_cache_off
+               mov     pc, lr
+-              .word   0x41129200              @ ARM920T
+-              .word   0xff00fff0
++              .word   0x41009200              @ ARM920T, ARM922T, ARM926TEJ-S
++              .word   0xff00ff90
+               b       __armv4_cache_on
+               b       __armv4_cache_off
+               b       __armv4_cache_flush
+-              .word   0x41029220              @ ARM922T
+-              .word   0xff00fff0
++              .word   0x4100a200              @ ARM1020T/E, ARM1022E, ARM1026TEJ-S
++              .word   0xff00ff90
+               b       __armv4_cache_on
+               b       __armv4_cache_off
+               b       __armv4_cache_flush
+--- linux-2.4.27/arch/arm/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/config.in
+@@ -144,6 +144,7 @@
+ mainmenu_option next_comment
+ comment 'AT91RM9200 Implementations'
+ dep_bool '  Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200
++dep_bool '  Cogent CSB337' CONFIG_MACH_CSB337 $CONFIG_ARCH_AT91RM9200
+ endmenu
+ mainmenu_option next_comment
+@@ -189,6 +190,12 @@
+    define_bool CONFIG_ARCH_ACORN n
+ fi
++if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then
++   define_bool CONFIG_PLD y
++else
++   define_bool CONFIG_PLD n
++fi
++
+ #####################################################################
+ # Footbridge support
+ if [ "$CONFIG_ARCH_CO285" = "y" -o \
+@@ -315,26 +322,42 @@
+ # ARM922T
+ if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then
+    define_bool CONFIG_CPU_ARM922T y
+-   define_bool CONFIG_PLD y
+ else
+-   define_bool CONFIG_CPU_ARM922T n
+-   define_bool CONFIG_PLD n
++   if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
++      bool 'Support ARM922T(Excalibur) processor' CONFIG_ARM922T
++   else
++      define_bool CONFIG_CPU_ARM922T n
++   fi
+ fi
+ # ARM926T
+ if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+-   bool 'Support ARM926T processor' CONFIG_CPU_ARM926T
++   bool 'Support ARM926TEJ-S processor' CONFIG_CPU_ARM926T
+ else
+    define_bool CONFIG_CPU_ARM926T n
+ fi
+ # ARM1020
+ if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+-   bool 'Support ARM1020 processor' CONFIG_CPU_ARM1020
++   bool 'Support ARM1020T (Rev0) processor' CONFIG_CPU_ARM1020
+ else
+     define_bool CONFIG_CPU_ARM1020 n
+ fi
++# ARM1020E
++if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
++   bool 'Support ARM1020E (Rev1) processor' CONFIG_CPU_ARM1020E
++else
++    define_bool CONFIG_CPU_ARM1020E n
++fi
++
++# ARM1022
++if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
++   bool 'Support ARM1022 processor' CONFIG_CPU_ARM1020E
++else
++    define_bool CONFIG_CPU_ARM1022 n
++fi
++
+ # ARM1026EJ-S
+ if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
+    bool 'Support ARM1026EJ-S processor' CONFIG_CPU_ARM1026
+@@ -388,25 +411,29 @@
+ if [ "$CONFIG_CPU_ARM720T" = "y" -o "$CONFIG_CPU_ARM920T" = "y" -o \
+      "$CONFIG_CPU_ARM922T" = "y" -o "$CONFIG_CPU_ARM926T" = "y" -o \
+-     "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then
++     "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \
++     "$CONFIG_CPU_ARM1022" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then
+    dep_bool 'Support Thumb instructions (EXPERIMENTAL)' CONFIG_ARM_THUMB $CONFIG_EXPERIMENTAL
+ fi
+ if [ "$CONFIG_CPU_ARM920T" = "y" -o "$CONFIG_CPU_ARM922T" = "y" -o \
+      "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \
++     "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \
+      "$CONFIG_CPU_ARM1026" = "y" ]; then
+    bool 'Disable I-Cache' CONFIG_CPU_ICACHE_DISABLE
+    bool 'Disable D-Cache' CONFIG_CPU_DCACHE_DISABLE
+-   if [ "$CONFIG_CPU_DISABLE_DCACHE" = "n" ]; then
++   if [ "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then
+       bool 'Force write through D-cache' CONFIG_CPU_DCACHE_WRITETHROUGH
+    fi
+ fi
+ if [ "$CONFIG_CPU_ARM926T" = "y" -o "$CONFIG_CPU_ARM1020" = "y" -o \
++     "$CONFIG_CPU_ARM1020E" = "y" -o "$CONFIG_CPU_ARM1022" = "y" -o \
+      "$CONFIG_CPU_ARM1026" = "y" ]; then
+    if [ "$CONFIG_CPU_ICACHE_DISABLE" = "n" -o "$CONFIG_CPU_DCACHE_DISABLE" = "n" ]; then
+       bool 'Round robin I and D cache replacement algorithm' CONFIG_CPU_CACHE_ROUND_ROBIN
+    fi
+ fi
+-if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1026" = "y" ]; then
++if [ "$CONFIG_CPU_ARM1020" = "y" -o "$CONFIG_CPU_ARM1020E" = "y" -o \
++    "$CONFIG_CPU_ARM1026" = "y" -o "$CONFIG_CPU_ARM1022" = "y" ]; then
+    bool 'Disable branch prediction' CONFIG_CPU_BPREDICT_DISABLE
+ fi
+@@ -491,7 +518,7 @@
+ if [ "$CONFIG_FPE_NWFPE" != "n" ]; then
+   bool '  Support extended precision' CONFIG_FPE_NWFPE_XP
+ fi
+-if [ "$CONFIG_CPU_26" = "n" -a "$CONFIG_CPU_32v3" = "n" ]; then
++if [ "$CONFIG_CPU_26" = "n" ]; then
+    dep_tristate 'FastFPE math emulation (EXPERIMENTAL)' CONFIG_FPE_FASTFPE $CONFIG_EXPERIMENTAL
+ fi
+ choice 'Kernel core (/proc/kcore) format' \
+@@ -514,7 +541,8 @@
+      "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+      "$CONFIG_ARCH_CDB89712" = "y" -o   \
+      "$CONFIG_ARCH_P720T" = "y" -o    \
+-     "$CONFIG_ARCH_OMAHA" = "y" ]; then
++     "$CONFIG_ARCH_OMAHA" = "y" -o    \
++     "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
+    bool 'Timer and CPU usage LEDs' CONFIG_LEDS
+    if [ "$CONFIG_LEDS" = "y" ]; then
+       if [ "$CONFIG_ARCH_NETWINDER" = "y" -o  \
+@@ -524,7 +552,8 @@
+          "$CONFIG_ARCH_SA1100" = "y" -o     \
+            "$CONFIG_ARCH_INTEGRATOR" = "y" -o \
+          "$CONFIG_ARCH_P720T" = "y" -o      \
+-         "$CONFIG_ARCH_OMAHA" = "y" ]; then
++         "$CONFIG_ARCH_OMAHA" = "y" -o      \
++         "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
+          bool '  Timer LED' CONFIG_LEDS_TIMER
+          bool '  CPU usage LED' CONFIG_LEDS_CPU
+       fi
+@@ -729,10 +758,7 @@
+ dep_bool '  Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_DEBUG_KERNEL
+ dep_bool '    Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE
+ dep_bool '    Kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X
+-
+-int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0
+-
+ endmenu
+-source crypto/Config.in
+ source lib/Config.in
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/config.in.rej
+@@ -0,0 +1,16 @@
++***************
++*** 144,149 ****
++  mainmenu_option next_comment
++  comment 'AT91RM9200 Implementations'
++  dep_bool '  Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200
++  endmenu
++  
++  mainmenu_option next_comment
++--- 144,150 ----
++  mainmenu_option next_comment
++  comment 'AT91RM9200 Implementations'
++  dep_bool '  Atmel AT91RM9200 Development Board' CONFIG_ARCH_AT91RM9200DK $CONFIG_ARCH_AT91RM9200
+++ dep_bool '  Cogent CSB337' CONFIG_MACH_CSB337 $CONFIG_ARCH_AT91RM9200
++  endmenu
++  
++  mainmenu_option next_comment
+--- linux-2.4.27/arch/arm/def-configs/at91rm9200dk~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/def-configs/at91rm9200dk
+@@ -111,6 +111,7 @@
+ # AT91RM9200 Implementations
+ #
+ CONFIG_ARCH_AT91RM9200DK=y
++# CONFIG_MACH_CSB337 is not set
+ #
+ # CLPS711X/EP721X Implementations
+@@ -125,6 +126,7 @@
+ # 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
+@@ -135,9 +137,10 @@
+ # CONFIG_CPU_ARM720T is not set
+ CONFIG_CPU_ARM920T=y
+ # CONFIG_CPU_ARM922T is not set
+-# CONFIG_PLD 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
+@@ -146,6 +149,7 @@
+ # CONFIG_ARM_THUMB is not set
+ # CONFIG_CPU_ICACHE_DISABLE is not set
+ # CONFIG_CPU_DCACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+ # CONFIG_DISCONTIGMEM is not set
+ #
+@@ -164,6 +168,7 @@
+ # CONFIG_BSD_PROCESS_ACCT is not set
+ CONFIG_SYSCTL=y
+ 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
+@@ -173,6 +178,9 @@
+ # CONFIG_PM is not set
+ # CONFIG_ARTHUR is not set
+ CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x20210000,3145728 root=/dev/ram rw"
++CONFIG_LEDS=y
++CONFIG_LEDS_TIMER=y
++# CONFIG_LEDS_CPU is not set
+ CONFIG_ALIGNMENT_TRAP=y
+ #
+@@ -204,6 +212,7 @@
+ # CONFIG_MTD_CFI_ADV_OPTIONS is not set
+ # CONFIG_MTD_CFI_INTELEXT is not set
+ CONFIG_MTD_CFI_AMDSTD=y
++# CONFIG_MTD_CFI_STAA is not set
+ # CONFIG_MTD_RAM is not set
+ # CONFIG_MTD_ROM is not set
+ # CONFIG_MTD_ABSENT is not set
+@@ -230,7 +239,9 @@
+ # CONFIG_MTD_AUTCPU12 is not set
+ # CONFIG_MTD_EDB7312 is not set
+ # CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_CEIVA is not set
+ # CONFIG_MTD_PCI is not set
++# CONFIG_MTD_PCMCIA is not set
+ #
+ # Self-contained MTD device drivers
+@@ -250,9 +261,9 @@
+ # NAND Flash Device Drivers
+ #
+ CONFIG_MTD_NAND=y
+-CONFIG_MTD_NAND_ECC=y
+ # CONFIG_MTD_NAND_VERIFY_WRITE is not set
+-CONFIG_MTD_AT91_SMARTMEDIA=y
++CONFIG_MTD_NAND_IDS=y
++# CONFIG_MTD_AT91_SMARTMEDIA is not set
+ #
+ # Plug and Play configuration
+@@ -269,6 +280,7 @@
+ # CONFIG_BLK_CPQ_DA is not set
+ # CONFIG_BLK_CPQ_CISS_DA is not set
+ # CONFIG_CISS_SCSI_TAPE is not set
++# CONFIG_CISS_MONITOR_THREAD is not set
+ # CONFIG_BLK_DEV_DAC960 is not set
+ # CONFIG_BLK_DEV_UMEM is not set
+ # CONFIG_BLK_DEV_LOOP is not set
+@@ -276,6 +288,7 @@
+ CONFIG_BLK_DEV_RAM=y
+ CONFIG_BLK_DEV_RAM_SIZE=8192
+ CONFIG_BLK_DEV_INITRD=y
++# CONFIG_BLK_STATS is not set
+ #
+ # Multi-device support (RAID and LVM)
+@@ -312,6 +325,12 @@
+ # CONFIG_SYN_COOKIES is not set
+ # CONFIG_IPV6 is not set
+ # CONFIG_KHTTPD is not set
++
++#
++#    SCTP Configuration (EXPERIMENTAL)
++#
++CONFIG_IPV6_SCTP__=y
++# CONFIG_IP_SCTP is not set
+ # CONFIG_ATM is not set
+ # CONFIG_VLAN_8021Q is not set
+ # CONFIG_IPX is not set
+@@ -382,10 +401,12 @@
+ #
+ # 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
+@@ -455,6 +476,8 @@
+ # CONFIG_INPUT_MOUSEDEV is not set
+ # CONFIG_INPUT_JOYDEV is not set
+ # CONFIG_INPUT_EVDEV is not set
++# CONFIG_INPUT_UINPUT is not set
++# CONFIG_INPUT_MX1TS is not set
+ #
+ # Character devices
+@@ -502,6 +525,7 @@
+ #
+ CONFIG_I2C=y
+ # CONFIG_I2C_ALGOBIT is not set
++# CONFIG_SCx200_ACB is not set
+ # CONFIG_I2C_ALGOPCF is not set
+ CONFIG_I2C_AT91=y
+ CONFIG_I2C_CHARDEV=y
+@@ -528,6 +552,11 @@
+ #
+ # CONFIG_INPUT_GAMEPORT 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
+@@ -536,12 +565,14 @@
+ CONFIG_WATCHDOG_NOWAYOUT=y
+ # CONFIG_ACQUIRE_WDT is not set
+ # CONFIG_ADVANTECH_WDT is not set
++# CONFIG_ALIM1535_WDT is not set
+ # CONFIG_ALIM7101_WDT is not set
+ # CONFIG_SC520_WDT is not set
+ # CONFIG_PCWATCHDOG is not set
+ # CONFIG_21285_WATCHDOG is not set
+ # CONFIG_977_WATCHDOG is not set
+ # CONFIG_SA1100_WATCHDOG is not set
++# CONFIG_EPXA_WATCHDOG is not set
+ # CONFIG_OMAHA_WATCHDOG is not set
+ CONFIG_AT91_WATCHDOG=y
+ # CONFIG_EUROTECH_WDT is not set
+@@ -551,11 +582,16 @@
+ # CONFIG_MIXCOMWD is not set
+ # CONFIG_60XX_WDT is not set
+ # CONFIG_SC1200_WDT is not set
++# CONFIG_SCx200_WDT is not set
+ # CONFIG_SOFT_WATCHDOG is not set
+ # CONFIG_W83877F_WDT is not set
+ # CONFIG_WDT is not set
+ # CONFIG_WDTPCI is not set
+ # CONFIG_MACHZ_WDT is not set
++# CONFIG_AMD7XX_TCO is not set
++# CONFIG_SCx200 is not set
++# CONFIG_SCx200_GPIO is not set
++# CONFIG_AMD_PM768 is not set
+ # CONFIG_NVRAM is not set
+ # CONFIG_RTC is not set
+ CONFIG_AT91_RTC=y
+@@ -568,6 +604,10 @@
+ #
+ # CONFIG_FTAPE is not set
+ # CONFIG_AGP is not set
++
++#
++# Direct Rendering Manager (XFree86 DRI support)
++#
+ # CONFIG_DRM is not set
+ #
+@@ -579,6 +619,7 @@
+ # File systems
+ #
+ # CONFIG_QUOTA is not set
++# CONFIG_QFMT_V2 is not set
+ # CONFIG_AUTOFS_FS is not set
+ # CONFIG_AUTOFS4_FS is not set
+ # CONFIG_REISERFS_FS is not set
+@@ -588,6 +629,9 @@
+ # CONFIG_ADFS_FS_RW is not set
+ # CONFIG_AFFS_FS is not set
+ # CONFIG_HFS_FS is not set
++# CONFIG_HFSPLUS_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
+@@ -605,6 +649,9 @@
+ # 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
+@@ -624,6 +671,11 @@
+ # CONFIG_UDF_RW is not set
+ # CONFIG_UFS_FS is not set
+ # CONFIG_UFS_FS_WRITE is not set
++# CONFIG_XFS_FS is not set
++# CONFIG_XFS_QUOTA is not set
++# CONFIG_XFS_RT is not set
++# CONFIG_XFS_TRACE is not set
++# CONFIG_XFS_DEBUG is not set
+ #
+ # Network File Systems
+@@ -632,9 +684,11 @@
+ # CONFIG_INTERMEZZO_FS is not set
+ # CONFIG_NFS_FS is not set
+ # CONFIG_NFS_V3 is not set
++# CONFIG_NFS_DIRECTIO is not set
+ # CONFIG_ROOT_NFS is not set
+ # CONFIG_NFSD is not set
+ # CONFIG_NFSD_V3 is not set
++# CONFIG_NFSD_TCP is not set
+ # CONFIG_SUNRPC is not set
+ # CONFIG_LOCKD is not set
+ # CONFIG_SMB_FS is not set
+@@ -648,7 +702,6 @@
+ # CONFIG_NCPFS_NLS is not set
+ # CONFIG_NCPFS_EXTRAS is not set
+ # CONFIG_ZISOFS_FS is not set
+-# CONFIG_ZLIB_FS_INFLATE is not set
+ #
+ # Partition Types
+@@ -674,16 +727,18 @@
+ # CONFIG_USB_DEBUG is not set
+ # CONFIG_USB_DEVICEFS is not set
+ # CONFIG_USB_BANDWIDTH is not set
+-# CONFIG_USB_LONG_TIMEOUT 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 is not set
++# CONFIG_USB_SL811HS is not set
+ CONFIG_USB_OHCI_AT91=y
+ # CONFIG_USB_AUDIO is not set
+ # CONFIG_USB_EMI26 is not set
+ # CONFIG_USB_BLUETOOTH is not set
++# CONFIG_USB_MIDI is not set
+ # CONFIG_USB_STORAGE is not set
+ # CONFIG_USB_STORAGE_DEBUG is not set
+ # CONFIG_USB_STORAGE_DATAFAB is not set
+@@ -692,6 +747,7 @@
+ # CONFIG_USB_STORAGE_DPCM is not set
+ # CONFIG_USB_STORAGE_HP8200e is not set
+ # CONFIG_USB_STORAGE_SDDR09 is not set
++# CONFIG_USB_STORAGE_SDDR55 is not set
+ # CONFIG_USB_STORAGE_JUMPSHOT is not set
+ # CONFIG_USB_ACM is not set
+ # CONFIG_USB_PRINTER is not set
+@@ -700,7 +756,10 @@
+ # CONFIG_USB_HIDDEV is not set
+ # CONFIG_USB_KBD is not set
+ # 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 is not set
+ # CONFIG_USB_MDC800 is not set
+ # CONFIG_USB_SCANNER is not set
+@@ -718,35 +777,16 @@
+ # USB Serial Converter support
+ #
+ # CONFIG_USB_SERIAL is not set
+-# CONFIG_USB_SERIAL_GENERIC is not set
+-# CONFIG_USB_SERIAL_BELKIN is not set
+-# CONFIG_USB_SERIAL_WHITEHEAT is not set
+-# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+-# CONFIG_USB_SERIAL_EMPEG is not set
+-# 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_KEYSPAN_PDA is not set
+-# CONFIG_USB_SERIAL_KEYSPAN is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+-# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+-# CONFIG_USB_SERIAL_MCT_U232 is not set
+-# CONFIG_USB_SERIAL_KLSI 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
++
++#
++# Support for USB gadgets
++#
++# CONFIG_USB_GADGET is not set
+ #
+ # Bluetooth support
+@@ -770,3 +810,10 @@
+ CONFIG_DEBUG_LL=y
+ # CONFIG_DEBUG_DC21285_PORT is not set
+ # CONFIG_DEBUG_CLPS711X_UART2 is not set
++
++#
++# Library routines
++#
++CONFIG_CRC32=y
++# CONFIG_ZLIB_INFLATE is not set
++# CONFIG_ZLIB_DEFLATE is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/def-configs/csb337
+@@ -0,0 +1,760 @@
++#
++# 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_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=y
++
++#
++# 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
++CONFIG_MACH_CSB337=y
++
++#
++# 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=y
++# 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_32v3 is not set
++CONFIG_CPU_32v4=y
++# CONFIG_ARM_THUMB is not set
++# CONFIG_CPU_ICACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_WRITETHROUGH 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 is not set
++CONFIG_ZBOOT_ROM_TEXT=0
++CONFIG_ZBOOT_ROM_BSS=0
++# CONFIG_HOTPLUG is not set
++# CONFIG_PCMCIA is not set
++CONFIG_NET=y
++CONFIG_SYSVIPC=y
++# CONFIG_BSD_PROCESS_ACCT is not set
++CONFIG_SYSCTL=y
++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 is not set
++# CONFIG_ARTHUR is not set
++CONFIG_CMDLINE="mem=32M console=ttyS0,38400 initrd=0x20210000,3145728 root=/dev/ram rw"
++# CONFIG_LEDS is not set
++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 is not set
++# 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
++
++#
++# RAM/ROM/Flash chip drivers
++#
++CONFIG_MTD_CFI=y
++CONFIG_MTD_JEDECPROBE=y
++CONFIG_MTD_GEN_PROBE=y
++# CONFIG_MTD_CFI_ADV_OPTIONS is not set
++CONFIG_MTD_CFI_INTELEXT=y
++# CONFIG_MTD_CFI_AMDSTD is not set
++# CONFIG_MTD_CFI_STAA is not set
++# CONFIG_MTD_RAM is not set
++CONFIG_MTD_ROM=y
++# 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_PHYSMAP=y
++CONFIG_MTD_PHYSMAP_START=10000000
++CONFIG_MTD_PHYSMAP_LEN=200000
++CONFIG_MTD_PHYSMAP_BUSWIDTH=2
++# CONFIG_MTD_NORA 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_FORTUNET is not set
++# CONFIG_MTD_EPXA is not set
++# CONFIG_MTD_AUTCPU12 is not set
++# CONFIG_MTD_EDB7312 is not set
++# CONFIG_MTD_IMPA7 is not set
++# CONFIG_MTD_CEIVA 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_AT91_DATAFLASH=y
++# CONFIG_MTD_AT91_DATAFLASH_CARD is not set
++# CONFIG_MTD_MTDRAM is not set
++# CONFIG_MTD_BLKMTD is not set
++# CONFIG_MTD_DOC1000 is not set
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOCPROBE is not set
++
++#
++# NAND Flash Device Drivers
++#
++CONFIG_MTD_NAND=y
++# CONFIG_MTD_NAND_VERIFY_WRITE is not set
++CONFIG_MTD_NAND_IDS=y
++# CONFIG_MTD_AT91_SMARTMEDIA 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_CISS_MONITOR_THREAD is not set
++# CONFIG_BLK_DEV_DAC960 is not set
++# CONFIG_BLK_DEV_UMEM is not set
++# CONFIG_BLK_DEV_LOOP is not set
++# CONFIG_BLK_DEV_NBD is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_BLK_DEV_INITRD is not set
++# CONFIG_BLK_STATS is not set
++
++#
++# 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 is not set
++# 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=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# 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
++
++#
++#    SCTP Configuration (EXPERIMENTAL)
++#
++CONFIG_IPV6_SCTP__=y
++# CONFIG_IP_SCTP is not set
++# CONFIG_ATM is not set
++# CONFIG_VLAN_8021Q is not set
++# 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 is not set
++# 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_AT91_ETHER=y
++# CONFIG_AT91_ETHER_RMII 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 is not set
++# 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 is not set
++# CONFIG_SLIP is not set
++
++#
++# Wireless LAN (non-hamradio)
++#
++# CONFIG_NET_RADIO is not set
++
++#
++# 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
++
++#
++# Amateur Radio support
++#
++# CONFIG_HAMRADIO is not set
++
++#
++# IrDA (infrared) support
++#
++# CONFIG_IRDA is not set
++
++#
++# ATA/ATAPI/MFM/RLL support
++#
++# CONFIG_IDE is not set
++# CONFIG_BLK_DEV_HD is not set
++
++#
++# SCSI support
++#
++# CONFIG_SCSI 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 is not set
++# CONFIG_INPUT_KEYBDEV is not set
++# CONFIG_INPUT_MOUSEDEV is not set
++# CONFIG_INPUT_JOYDEV is not set
++# CONFIG_INPUT_EVDEV is not set
++# CONFIG_INPUT_UINPUT is not set
++# CONFIG_INPUT_MX1TS is not set
++
++#
++# Character devices
++#
++# CONFIG_VT is not set
++# CONFIG_SERIAL is not set
++# CONFIG_SERIAL_EXTENDED is not set
++# CONFIG_SERIAL_NONSTANDARD is not set
++CONFIG_AT91_SPIDEV=y
++
++#
++# 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=y
++CONFIG_SERIAL_AT91_CONSOLE=y
++# 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_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++# CONFIG_UNIX98_PTYS is not set
++
++#
++# I2C support
++#
++CONFIG_I2C=y
++# CONFIG_I2C_ALGOBIT is not set
++# CONFIG_SCx200_ACB is not set
++# CONFIG_I2C_ALGOPCF is not set
++CONFIG_I2C_AT91=y
++CONFIG_I2C_CHARDEV=y
++CONFIG_I2C_PROC=y
++CONFIG_I2C_DS1307=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_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=y
++CONFIG_WATCHDOG_NOWAYOUT=y
++# CONFIG_ACQUIRE_WDT is not set
++# CONFIG_ADVANTECH_WDT is not set
++# CONFIG_ALIM1535_WDT is not set
++# CONFIG_ALIM7101_WDT is not set
++# CONFIG_SC520_WDT is not set
++# CONFIG_PCWATCHDOG is not set
++# CONFIG_21285_WATCHDOG is not set
++# CONFIG_977_WATCHDOG is not set
++# CONFIG_SA1100_WATCHDOG is not set
++# CONFIG_EPXA_WATCHDOG is not set
++# CONFIG_OMAHA_WATCHDOG is not set
++CONFIG_AT91_WATCHDOG=y
++# CONFIG_EUROTECH_WDT is not set
++# CONFIG_IB700_WDT is not set
++# CONFIG_WAFER_WDT is not set
++# CONFIG_I810_TCO is not set
++# CONFIG_MIXCOMWD is not set
++# CONFIG_60XX_WDT is not set
++# CONFIG_SC1200_WDT is not set
++# CONFIG_SCx200_WDT is not set
++# CONFIG_SOFT_WATCHDOG is not set
++# CONFIG_W83877F_WDT is not set
++# CONFIG_WDT is not set
++# CONFIG_WDTPCI is not set
++# CONFIG_MACHZ_WDT is not set
++# CONFIG_AMD7XX_TCO is not set
++# CONFIG_SCx200 is not set
++# CONFIG_SCx200_GPIO is not set
++# CONFIG_AMD_PM768 is not set
++# CONFIG_NVRAM is not set
++# CONFIG_RTC is not set
++CONFIG_AT91_RTC=y
++# 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
++
++#
++# Direct Rendering Manager (XFree86 DRI support)
++#
++# CONFIG_DRM is not set
++
++#
++# Multimedia devices
++#
++# CONFIG_VIDEO_DEV is not set
++
++#
++# File systems
++#
++# CONFIG_QUOTA is not set
++# CONFIG_QFMT_V2 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_HFSPLUS_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 is not set
++# CONFIG_MSDOS_FS is not set
++# CONFIG_UMSDOS_FS is not set
++# CONFIG_VFAT_FS is not set
++# CONFIG_EFS_FS is not set
++# CONFIG_JFFS_FS is not set
++# CONFIG_JFFS2_FS is not set
++# CONFIG_CRAMFS is not set
++# CONFIG_TMPFS is not set
++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=y
++# 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
++# CONFIG_XFS_FS is not set
++# CONFIG_XFS_QUOTA is not set
++# CONFIG_XFS_RT is not set
++# CONFIG_XFS_TRACE is not set
++# CONFIG_XFS_DEBUG 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=y
++# CONFIG_NFS_DIRECTIO 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_LOCKD_V4=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 is not set
++
++#
++# Multimedia Capabilities Port drivers
++#
++# CONFIG_MCP is not set
++# 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
++
++#
++# USB support
++#
++# CONFIG_USB is not set
++
++#
++# Support for USB gadgets
++#
++# CONFIG_USB_GADGET is not set
++
++#
++# Bluetooth support
++#
++# CONFIG_BLUEZ is not set
++
++#
++# Kernel hacking
++#
++CONFIG_FRAME_POINTER=y
++CONFIG_DEBUG_USER=y
++# CONFIG_DEBUG_INFO is not set
++# CONFIG_NO_PGT_CACHE is not set
++CONFIG_DEBUG_KERNEL=y
++# 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=y
++# CONFIG_DEBUG_DC21285_PORT is not set
++# CONFIG_DEBUG_CLPS711X_UART2 is not set
++
++#
++# Library routines
++#
++CONFIG_CRC32=y
++# CONFIG_ZLIB_INFLATE is not set
++# CONFIG_ZLIB_DEFLATE is not set
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/CPDO.S
+@@ -0,0 +1,1786 @@
++/*
++The FP structure has 4 words reserved for each register, the first is used just
++for the sign in bit 31, the second and third are for the mantissa (unsigned
++integer, high 32 bit first) and the fourth is the exponent (signed integer).
++The mantissa is always normalized.
++ 
++If the exponent is 0x80000000, that is the most negative value, the number
++represented is 0 and both mantissa words are also 0.
++ 
++If the exponent is 0x7fffffff, that is the biggest positive value, the number
++represented is infinity if the mantissa is 0, otherwise it is a NaN.
++ 
++Decimal and packed decimal numbers are not supported yet.
++
++The parameters to these functions are r0=destination pointer, r1 and r2
++source pointers. r4 is the instruction. They may use r0-r8, r11. They return
++to r14, which contains the address of a rounding function. The rounding
++function expects r0=address, r1-r4=sign, mantissa high, mantissa low,
++exponent, r5=additional lower mantissa bits.
++
++CPDO_rnf_core expects the return address in r14.
++*/
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_adf
++CPDO_adf:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++      cmp     r11,#0x7fffffff
++      cmpne   r11,#0x80000000
++      cmpne   r4,#0x7fffffff
++      cmpne   r4,#0x80000000
++      beq     CPDO_adf_extra
++      
++      cmp     r1,r6
++      bne     CPDO_suf_s
++
++CPDO_adf_s:
++      subs    r6,r4,r11
++      bmi     CPDO_adf_normalize1st
++
++CPDO_adf_normalize2nd:
++      cmp     r6,#32
++      ble     CPDO_adf_normalize2nd_1
++      cmp     r6,#64
++      bgt     CPDO_adf_normalize2nd_3
++
++CPDO_adf_normalize2nd_2:
++      sub     r6,r6,#32
++      rsb     r11,r6,#32
++      mov     r5,r8,lsr r6
++      add     r5,r5,r7,lsl r11
++      movs    r11,r8,lsl r11
++      orrne   r5,r5,#1
++      mov     r8,r7,lsr r6
++      mov     r7,#0
++      b       CPDO_adf_add
++
++CPDO_adf_normalize2nd_1:
++      rsb     r11,r6,#32
++      mov     r5,r8,lsl r11
++      mov     r8,r8,lsr r6
++      add     r8,r8,r7,lsl r11
++      mov     r7,r7,lsr r6
++      b       CPDO_adf_add
++
++CPDO_adf_normalize2nd_3:
++      mov     r5,#0x40000000
++      mov     pc,r14
++
++CPDO_adf_normalize1st:
++      mov     r4,r11
++      rsb     r6,r6,#0
++      cmp     r6,#32
++      ble     CPDO_adf_normalize1st_1
++      cmp     r6,#64
++      bgt     CPDO_adf_normalize1st_3
++
++CPDO_adf_normalize1st_2:
++      sub     r6,r6,#32
++      rsb     r11,r6,#32
++      mov     r5,r3,lsr r6
++      add     r5,r5,r2,lsl r11
++      movs    r11,r3,lsl r11
++      orrne   r5,r5,#1
++      mov     r3,r2,lsr r6
++      mov     r2,#0
++      b       CPDO_adf_add
++
++CPDO_adf_normalize1st_1:
++      rsb     r11,r6,#32
++      mov     r5,r3,lsl r11
++      mov     r3,r3,lsr r6
++      add     r3,r3,r2,lsl r11
++      mov     r2,r2,lsr r6
++      b       CPDO_adf_add
++
++CPDO_adf_normalize1st_3:
++      mov     r5,#0x40000000
++      mov     r2,r7
++      mov     r3,r8
++      mov     pc,r14
++
++CPDO_adf_add:
++      adds    r3,r3,r8
++      adcs    r2,r2,r7
++      bcc     CPDO_adf_add_no_overflow
++
++      movs    r2,r2,rrx
++      movs    r3,r3,rrx
++      movs    r5,r5,rrx
++      orrcs   r5,r5,#1
++      add     r4,r4,#1
++
++CPDO_adf_add_no_overflow:
++      mov     pc,r14
++
++CPDO_adf_extra:
++      cmp     r4,#0x7fffffff
++      beq     CPDO_adf_1st_infnan
++      cmp     r11,#0x7fffffff
++      beq     CPDO_adf_2nd_infnan
++      cmp     r11,#0x80000000
++      beq     CPDO_adf_2nd_0
++
++CPDO_adf_1st_0:
++      mov     r1,r6
++      mov     r2,r7
++      mov     r3,r8
++      mov     r4,r11
++      mov     r5,#0
++      mov     pc,r14
++
++CPDO_adf_2nd_0:
++      cmp     r4,#0x80000000
++      beq     CPDO_adf_both_0
++      mov     r5,#0
++      mov     pc,r14
++
++CPDO_adf_both_0:
++      cmp     r1,r6
++      beq     CPDO_adf_both_0_equal_sign
++      and     r5,r5,#0x00000060
++      cmp     r5,#0x00000040  // rounding mode M?
++      moveq   r1,#0x80000000
++      movne   r1,#0
++CPDO_adf_both_0_equal_sign:
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14  
++
++CPDO_adf_1st_infnan:
++      cmp     r11,#0x7fffffff
++      beq     CPDO_adf_both_infnan
++CPDO_adf_1st_infnan_entry:
++      orrs    r5,r3,r2,lsl#1  // ignore MSB
++      moveq   pc,r14          // Inf
++      tst     r2,#0x40000000
++      movne   pc,r14          // QNaN
++CPDO_adf_generate_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,#0xffffffff
++      mov     r4,#0x7fffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1        // set invalid operation flag
++      str     r5,[r10,#128]
++      mov     pc,r14
++
++CPDO_adf_2nd_infnan:
++      mov     r1,r6
++      mov     r2,r7
++      mov     r3,r8
++      mov     r4,r11
++      b CPDO_adf_1st_infnan_entry
++
++CPDO_adf_both_infnan:
++      orrs    r5,r3,r2,lsl#1          // ignore MSB
++      beq     CPDO_adf_1st_inf
++      orrs    r5,r8,r7,lsl#1          // ignore MSB
++      beq     CPDO_adf_2nd_inf
++      tst     r2,#0x40000000
++      tstne   r7,#0x40000000
++      beq     CPDO_adf_generate_qnan  // at least one is SNaN
++      orrs    r5,r3,r2,lsl#1          // ignore MSB, FIXME! what is going on here?
++      moveq   r1,r6                   // if first is not NaN
++      moveq   r2,r7                   // give second as result
++      moveq   r3,r8
++      mov     pc,r14
++
++CPDO_adf_1st_inf:
++      orrs    r5,r8,r7,lsl#1          // ignore MSB
++      beq     CPDO_adf_both_inf
++      tst     r7,#0x40000000
++      beq     CPDO_adf_generate_qnan
++      mov     r1,r6                   //if 2nd no SNaN return 2nd
++      mov     r2,r7
++      mov     r3,r8
++      mov     pc,r14
++
++CPDO_adf_2nd_inf:
++      tst     r2,#0x40000000
++      beq     CPDO_adf_generate_qnan
++      mov     pc,r14                  // if 1st no SNaN just return it
++
++CPDO_adf_both_inf:
++      cmp     r1,r6
++      bne     CPDO_adf_generate_qnan  // signs of both inf are different
++      mov     pc,r14
++
++/*--------------------------------------------------------------------------*/
++
++      .globl  CPDO_suf
++CPDO_suf:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++CPDO_suf_l:
++      cmp     r11,#0x7fffffff
++      cmpne   r11,#0x80000000
++      cmpne   r4,#0x7fffffff
++      cmpne   r4,#0x80000000
++      beq     CPDO_suf_extra
++      
++      cmp     r1,r6
++      bne     CPDO_adf_s
++
++CPDO_suf_s:
++      subs    r6,r4,r11
++      blt     CPDO_suf_normalize1st
++      bgt     CPDO_suf_normalize2nd
++      cmp     r2,r7
++      cmpeq   r3,r8
++      beq     CPDO_suf_zero
++      mov     r5,#0
++      bcs     CPDO_suf_sub_1stbigger
++      eor     r1,r1,#0x80000000
++      b       CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize2nd:
++      cmp     r6,#32
++      ble     CPDO_suf_normalize2nd_1
++      cmp     r6,#64
++      bgt     CPDO_suf_normalize2nd_3
++
++CPDO_suf_normalize2nd_2:
++      sub     r6,r6,#32
++      rsb     r11,r6,#32
++      mov     r5,r8,lsr r6
++      add     r5,r5,r7,lsl r11
++      movs    r11,r8,lsl r11
++      orrne   r5,r5,#1
++      mov     r8,r7,lsr r6
++      mov     r7,#0
++      b       CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_1:
++      rsb     r11,r6,#32
++      mov     r5,r8,lsl r11
++      mov     r8,r8,lsr r6
++      add     r8,r8,r7,lsl r11
++      mov     r7,r7,lsr r6
++      b       CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_3:
++      sub     r6,r6,#64
++      cmp     r6,#32
++      bge     CPDO_suf_normalize2nd_4
++      rsb     r11,r6,#32
++      mov     r5,r7,lsr r6
++      orrs    r11,r8,r7,lsl r11
++      orrne   r5,r5,#1
++      mov     r7,#0
++      mov     r8,#0
++      b       CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize2nd_4:
++      mov     r5,#1
++      mov     r7,#0
++      mov     r8,#0
++      b       CPDO_suf_sub_1stbigger
++
++CPDO_suf_normalize1st:
++      eor     r1,r1,#0x80000000
++      mov     r4,r11
++      rsb     r6,r6,#0
++      cmp     r6,#32
++      ble     CPDO_suf_normalize1st_1
++      cmp     r6,#64
++      bgt     CPDO_suf_normalize1st_3
++
++CPDO_suf_normalize1st_2:
++      sub     r6,r6,#32
++      rsb     r11,r6,#32
++      mov     r5,r3,lsr r6
++      add     r5,r5,r2,lsl r11
++      movs    r11,r3,lsl r11
++      orrne   r5,r5,#1
++      mov     r3,r2,lsr r6
++      mov     r2,#0
++      b       CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_1:
++      rsb     r11,r6,#32
++      mov     r5,r3,lsl r11
++      mov     r3,r3,lsr r6
++      add     r3,r3,r2,lsl r11
++      mov     r2,r2,lsr r6
++      b       CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_3:
++      sub     r6,r6,#64
++      cmp     r6,#32
++      bge     CPDO_suf_normalize1st_4
++      rsb     r11,r6,#32
++      mov     r5,r2,lsr r6
++      orrs    r11,r3,r2,lsl r11
++      orrne   r5,r5,#1
++      mov     r2,#0
++      mov     r3,#0
++      b       CPDO_suf_sub_2ndbigger
++
++CPDO_suf_normalize1st_4:
++      mov     r5,#1
++      mov     r2,#0
++      mov     r3,#0
++      b       CPDO_suf_sub_2ndbigger
++
++CPDO_suf_sub_1stbigger:
++      rsbs    r5,r5,#0
++      sbcs    r3,r3,r8
++      sbcs    r2,r2,r7
++      movmi   pc,r14
++      b       CPDO_suf_norm
++
++CPDO_suf_sub_2ndbigger:
++      rsbs    r5,r5,#0
++      sbcs    r3,r8,r3
++      sbcs    r2,r7,r2
++      movmi   pc,r14
++
++CPDO_suf_norm:
++      teq     r2,#0           // normalize 32 bit
++      bne     CPDO_suf_norm16
++      teq     r3,#0           // normalize 64 bit
++      bne     CPDO_suf_norm32
++      mov     r2,r5
++      mov     r3,#0
++      mov     r5,#0
++      sub     r4,r4,#64
++      mov     pc,r14
++CPDO_suf_norm32:
++      mov     r2,r3
++      mov     r3,r5
++      mov     r5,#0
++      sub     r4,r4,#32
++CPDO_suf_norm16:
++      cmp     r2,#0x00010000  // normalize 16 bit
++      bcs     CPDO_suf_norm8
++      mov     r2,r2,lsl#16
++      orr     r2,r2,r3,lsr#16
++      mov     r3,r3,lsl#16
++      orr     r3,r3,r5,lsr#16
++      mov     r5,r5,lsl#16
++      sub     r4,r4,#16
++CPDO_suf_norm8:
++      cmp     r2,#0x01000000  // normalize 8 bit
++      bcs     CPDO_suf_norm4
++      mov     r2,r2,lsl#8
++      orr     r2,r2,r3,lsr#24
++      mov     r3,r3,lsl#8
++      orr     r3,r3,r5,lsr#24
++      mov     r5,r5,lsl#8
++      sub     r4,r4,#8
++CPDO_suf_norm4:
++      cmp     r2,#0x10000000  // normalize 4 bit
++      bcs     CPDO_suf_norm2
++      mov     r2,r2,lsl#4
++      orr     r2,r2,r3,lsr#28
++      mov     r3,r3,lsl#4
++      orr     r3,r3,r5,lsr#28
++      mov     r5,r5,lsl#4
++      sub     r4,r4,#4
++CPDO_suf_norm2:
++      cmp     r2,#0x40000000  // normalize 2 bit
++      bcs     CPDO_suf_norm1
++      mov     r2,r2,lsl#2
++      orr     r2,r2,r3,lsr#30
++      mov     r3,r3,lsl#2
++      orr     r3,r3,r5,lsr#30
++      mov     r5,r5,lsl#2
++      sub     r4,r4,#2
++CPDO_suf_norm1:
++      cmp     r2,#0x80000000  // normalize 1 bit
++      bcs     CPDO_suf_norme
++      mov     r2,r2,lsl#1
++      orr     r2,r2,r3,lsr#31
++      mov     r3,r3,lsl#1
++      orr     r3,r3,r5,lsr#31
++      mov     r5,r5,lsl#1
++      sub     r4,r4,#1
++CPDO_suf_norme:
++      mov     pc,r14
++
++CPDO_suf_zero:
++      and     r5,r5,#0x00000060
++      cmp     r5,#0x00000040  // rounding mode M?
++      moveq   r1,#0x80000000
++      movne   r1,#0
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14  
++
++CPDO_suf_extra:                               // nearly the same as with adf
++      cmp     r11,#0x7fffffff         // the only thing we need to do is
++      bne     CPDO_suf_extra_sign     // to invert the second sign if
++      orrnes  r5,r8,r7,lsl#1          // it is not a NaN, ignore MSB
++      bne     CPDO_adf_extra
++CPDO_suf_extra_sign:
++      eor     r6,r6,#0x80000000
++      b       CPDO_adf_extra
++
++/*---------------------------------------------------------------------------*/
++
++      .globl CPDO_rsf
++CPDO_rsf:
++      ldmia   r1,{r6,r7,r8,r11}
++      ldmia   r2,{r1,r2,r3,r4}
++      b       CPDO_suf_l
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_muf
++CPDO_muf:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++      cmp     r11,#0x7fffffff
++      cmpne   r4,#0x7fffffff
++      beq     CPDO_muf_extra
++      eor     r1,r1,r6                // sign
++      cmp     r11,#0x80000000
++      cmpne   r4,#0x80000000
++      beq     CPDO_muf_zero
++      
++      add     r4,r4,r11               // exponent
++#if 0
++
++#define       x4      r0
++#define       x3      r1
++#define       x2      r4
++#define x1    r6
++#define       y4      r9
++#define       y3      r7
++#define       y2      r10
++#define       y1      r8
++#define z4    r2
++#define z3    r3
++#define       z2      r5
++#define       z1      r11
++#define tmp   r12
++
++      stmdb   r13!, {r0, r1, r4, r9, r10, r12}
++      mov     x4,r2,lsr#16
++      bic     x3,r2,x4,lsl#16
++      mov     x2,r3,lsr#16
++      bic     x1,r3,x2,lsl#16
++      mov     y4,r7,lsr#16
++      bic     y3,r7,y4,lsl#16
++      mov     y2,r8,lsr#16
++      bic     y1,r8,y2,lsl#16
++      mul     z1,x1,y1
++      mul     tmp,x1,y2
++      mov     z2,#0
++      adds    z1,z1,tmp,lsl#16
++      adc     z2,z2,tmp,lsr#16
++      mul     tmp,x2,y1
++      adds    z1,z1,tmp,lsl#16
++      adcs    z2,z2,tmp,lsr#16
++      mul     tmp,x1,y4
++      mov     z3,#0
++      adds    z2,z2,tmp,lsl#16
++      adc     z3,z3,tmp,lsr#16
++      mul     tmp,x2,y3
++      adds    z2,z2,tmp,lsl#16
++      adc     z3,z3,tmp,lsr#16
++      mul     tmp,x3,y2
++      adds    z2,z2,tmp,lsl#16
++      adc     z3,z3,tmp,lsr#16
++      mul     tmp,x4,y1
++      adds    z2,z2,tmp,lsl#16
++      adc     z3,z3,tmp,lsr#16
++      mul     tmp,x3,y4
++      mul     z4,x4,y4
++      adds    z3,z3,tmp,lsl#16
++      adc     z4,z4,tmp,lsr#16
++      mul     tmp,x4,y3
++      adds    z3,z3,tmp,lsl#16
++      adc     z4,z4,tmp,lsr#16
++      mul     tmp,x1,y3
++      adds    z2,z2,tmp
++      mul     tmp,x2,y4
++      adcs    z3,z3,tmp
++      mul     tmp,x2,y2
++      adc     z4,z4,#0
++      adds    z2,z2,tmp
++      mul     tmp,x3,y3
++      adcs    z3,z3,tmp
++      mul     tmp,x3,y1
++      adc     z4,z4,#0
++      adds    z2,z2,tmp
++      teq     z1,#0
++      orrne   z2,z2,#1        // z1 must not be lost for rounding
++      mul     tmp,x4,y2
++      adcs    z3,z3,tmp
++      adcs    z4,z4,#0
++      ldmia   r13!, {r0, r1, r4, r9, r10, r12}
++#else
++
++#define x32     r2
++#define x10     r3
++#define y32     r7
++#define y10     r8
++#define z3      r0
++#define z2      r1
++#define z1      r4
++#define z0      r6
++#define v1      r9
++#define v0      r11
++#define tmp     r5
++
++      stmdb   r13!,{r0,r1,r4,r9}
++
++        mov     z3,x32,lsr#16
++        bic     z2,x32,z3,lsl#16
++        movs    v1,y32,lsr#16
++        bic     v0,y32,v1,lsl#16
++
++        mul     tmp,z3,v0
++        mul     z3,v1,z3
++        mulne   v1,z2,v1
++        mul     z2,v0,z2
++        adds    z2,z2,tmp,lsl#16
++        adc     z3,z3,tmp,lsr#16
++        adds    z2,z2,v1,lsl#16
++        adc     z3,z3,v1,lsr#16
++
++        mov     z1,x10,lsr#16
++        bic     z0,x10,z1,lsl#16
++        movs    v1,y10,lsr#16
++        bic     v0,y10,v1,lsl#16
++
++        mul     tmp,z1,v0
++        mul     z1,v1,z1
++        mulne   v1,z0,v1
++        mul     z0,v0,z0
++        adds    z0,z0,tmp,lsl#16
++        adc     z1,z1,tmp,lsr#16
++        adds    z0,z0,v1,lsl#16
++        adc     z1,z1,v1,lsr#16
++
++        adds    z2,z2,z1        // z3 is max. 0xfffffffe
++        adc     z3,z3,#0        // so this trick is possible
++        adds    z1,z2,z0        // to save one addition
++        adcs    z2,z2,z3
++        adc     z3,z3,#0
++
++        subs    x10,x32,x10
++        mov     v0,#0
++        mov     v1,v0,rrx
++
++        sublo   v0,y32,y10
++        subnes  y10,y10,y32
++
++        orreq   v1,v1,#1<<31
++        eorcs   v1,v1,#1<<31
++        subcc   v0,v0,x10
++
++        movs    x32,x10,lsr#16
++        bic     x10,x10,x32,lsl#16
++        mov     y32,y10,lsr#16
++        bic     y10,y10,y32,lsl#16
++
++        mul     tmp,x10,y10
++        mla     v0,x32,y32,v0
++        mulne   x32,y10,x32
++        adds    tmp,tmp,x32,lsl#16
++        adc     v0,v0,x32,lsr#16
++        mul     y32,x10,y32
++        adds    tmp,tmp,y32,lsl#16
++        adc     v0,v0,y32,lsr#16
++        adds    r5,z1,tmp
++        adcs    r3,z2,v0
++        adc     r2,z3,v1,asr#31
++
++      teq     z0,#0
++      orrne   r5,r5,#1        // z0 must not be lost for rounding
++      cmp     r2,#0
++      
++      ldmia   r13!,{r0,r1,r4,r9}
++#endif
++
++      bpl     CPDO_muf_norm
++      add     r4,r4,#1
++      mov     pc,r14
++
++CPDO_muf_norm:
++      adds    r5,r5,r5
++      adcs    r3,r3,r3
++      adc     r2,r2,r2
++      mov     pc,r14
++
++CPDO_muf_extra:
++      cmp     r4,#0x7fffffff
++      beq     CPDO_muf_1st_infnan
++CPDO_muf_2nd_infnan:
++      orrs    r5,r8,r7,lsl#1          // ignore MSB
++      bne     CPDO_muf_2nd_nan
++      cmp     r4,#0x80000000
++      beq     CPDO_muf_generate_qnan
++      mov     r2,r7                   // copy MSB
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++      eor     r1,r1,r6
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14  
++
++CPDO_muf_1st_infnan:
++      cmp     r11,#0x7fffffff
++      beq     CPDO_muf_both_infnan
++      orrs    r5,r3,r2,lsl#1          // ignore MSB
++      bne     CPDO_muf_1st_nan
++      cmp     r11,#0x80000000
++      beq     CPDO_muf_generate_qnan
++//    mov     r4,#0x7fffffff
++      eor     r1,r1,r6
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_muf_both_infnan:
++      orrs    r5,r3,r2,lsl#1                  // ignore MSB
++      beq     CPDO_muf_both_infnan_1st_inf
++      orrs    r5,r8,r7,lsl#1                  // ignore MSB
++      beq     CPDO_muf_both_infnan_2nd_inf
++      tst     r2,#0x40000000
++      tstne   r7,#0x40000000
++      beq     CPDO_muf_generate_qnan
++      mov     pc,r14
++
++CPDO_muf_both_infnan_1st_inf:
++      orrs    r5,r8,r7,lsl#1          // ignore MSB
++      beq     CPDO_muf_both_inf
++      b       CPDO_muf_2nd_nan        
++
++CPDO_muf_both_infnan_2nd_inf:
++      b       CPDO_muf_1st_nan
++
++CPDO_muf_both_inf:
++      eor     r1,r1,r6
++      orr     r2,r2,r7                // copy both MSB
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_muf_zero:
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_muf_1st_nan:
++      tst     r2,#0x40000000
++      beq     CPDO_muf_generate_qnan
++      mov     pc,r14
++
++CPDO_muf_2nd_nan:
++      tst     r7,#0x40000000
++      beq     CPDO_muf_generate_qnan
++      mov     r1,r6
++      mov     r2,r7
++      mov     r3,r8
++      mov     r4,r11
++      mov     pc,r14
++
++CPDO_muf_generate_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,#0xffffffff
++      mov     r4,#0x7fffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1
++      str     r5,[r10,#128]
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_muf_M
++CPDO_muf_M:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++      cmp     r11,#0x7fffffff
++      cmpne   r4,#0x7fffffff
++      beq     CPDO_muf_extra
++      eor     r1,r1,r6                // sign
++      cmp     r11,#0x80000000
++      cmpne   r4,#0x80000000
++      beq     CPDO_muf_zero
++      
++      add     r4,r4,r11               // exponent
++#if 0
++      umull   r11,r5,r3,r8            // r5|r11 = lo1*lo2
++      teq     r11,#0
++      orrne   r5,r5,#0x00000001       // r11 must not be lost for rounding
++      umull   r11,r6,r8,r2            // r6|r11 = lo2*hi1
++      umull   r8,r2,r7,r2             // r2|r8 = hi1*hi2
++      umull   r12,r3,r7,r3            // r3|r12 = lo1*hi2
++      adds    r5,r5,r12
++      adcs    r3,r3,r6
++      adc     r2,r2,#0
++      adds    r5,r5,r11
++      adcs    r3,r3,r8
++      adcs    r2,r2,#0
++#else
++      umull   r12,r11,r2,r7
++      umull   r2,r6,r8,r2
++      umull   r8,r5,r3,r8
++      adds    r5,r5,r2
++      adcs    r12,r12,r6
++      adc     r11,r11,#0
++      umull   r7,r6,r3,r7
++      adds    r5,r5,r7
++      adcs    r3,r12,r6
++      adc     r2,r11,#0
++      teq     r8,#0
++      orrne   r5,r5,#1        // r8 must not be lost for rounding
++      cmp     r2,#0   
++#endif
++      bpl     CPDO_muf_norm
++      add     r4,r4,#1
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++CPDO_infnan_1:
++      stmia   r0,{r1,r3,r5,r7}
++      b       fastfpe_next
++
++CPDO_infnan_2:
++      stmia   r0,{r2,r4,r6,r8}
++      b       fastfpe_next
++      
++CPDO_nan_12:
++      orr     r2,r3,r4
++      b       CPDO_inf_1
++
++CPDO_nan:
++      mov     r2,#0x40000000          @ create non signalling NaN
++      b       CPDO_inf_1
++      
++CPDO_inf:
++      mov     r2,#0
++CPDO_inf_1:
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++CPDO_store_1234:
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++      
++CPDO_zero:
++      mov     r1,#0
++CPDO_zero_1:
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++
++CPDO_muf_end:
++      cmp     r8,#0x20000000
++      bge     CPDO_inf
++      cmp     r8,#0xe0000000
++      ble     CPDO_zero_1
++      stmia   r0,{r1,r2,r7,r8}
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_dvf
++CPDO_dvf:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++CPDO_dvf_l:
++      cmp     r11,#0x7fffffff
++      cmpne   r4,#0x7fffffff
++      beq     CPDO_dvf_infnan
++      eor     r1,r1,r6
++      cmp     r11,#0x80000000
++      cmpne   r4,#0x80000000
++      beq     CPDO_dvf_zero
++
++      sub     r4,r4,r11
++
++#define x4      r11
++#define x3      r7
++#define x2      r12
++#define x1      r8
++#define y2      r14
++#define y1      r9
++#define z3      r4
++#define z2      r5
++#define z1      r6
++#define tmp     r10
++
++      cmp     r2,r7
++      cmpeq   r3,r8
++      bcs     CPDO_dvf_no_normalize
++
++      sub     r4,r4,#1
++      stmdb   r13!,{r1,r4,r9,r10,r11,r14}
++      mov     r4,r2,lsr#31
++      mov     r5,r2,lsl#1
++      orr     r5,r5,r3,lsr#31
++      mov     r6,r3,lsl#1     // dividend
++      b       CPDO_dvf_normalize_back
++
++CPDO_dvf_no_normalize:
++      stmdb   r13!,{r1,r4,r9,r10,r11,r14}
++      mov     r4,#0
++      mov     r5,r2
++      mov     r6,r3           // dividend
++
++CPDO_dvf_normalize_back:
++      mov     r1,#0
++      sub     r10,r1,r7,lsr#1
++      mov     r11,#0x40000000
++
++      .macro  inv_step
++      adds    r11,r10,r11,lsl#1
++      subcc   r11,r11,r10
++      adc     r1,r1,r1
++      .endm
++
++      .rept   17
++      inv_step
++      .endr
++
++      mov     r1,r1,lsl#15
++      adds    r1,r1,#1<<15
++      movcs   r1,#0xffffffff  // inverse
++      mov     r1,r1,lsr#16
++
++      mov     r2,#0
++      mov     r3,#0           // clear result space
++
++        mov     x4,r7,lsr#16
++        bic     x3,r7,x4,lsl#16
++        mov     x2,r8,lsr#16
++        bic     x1,r8,x2,lsl#16       // split divisor for 16x16=32bit mul
++
++CPDO_dvf_loop_entry:
++      mov     r4,r4,lsl#16
++      orrs    r4,r4,r5,lsr#16
++      mov     r5,r5,lsl#16
++      orr     r5,r5,r6,lsr#16
++      mov     r6,r6,lsl#16    // shift dividend left by 16
++
++      bmi     CPDO_dvf_loop_negative
++        mov     r10,r4,lsr#16
++        mul     r9,r10,r1
++        bic     r10,r4,r10,lsl#16
++        mul     r10,r1,r10
++        add     r9,r9,r10,lsr#16      //estimate 16 bits of result in r9
++
++      mov     r2,r2,lsl#16
++      orr     r2,r2,r3,lsr#16
++      adds    r3,r9,r3,lsl#16         // shift result left by 16 and
++      adc     r2,r2,#0                // add in new result bits
++
++        mov     r9,r9,lsl#1
++        mov     y2,r9,lsr#16
++        bic     y1,r9,y2,lsl#16
++        mul     tmp,x1,y1
++        subs    z1,z1,tmp
++        mul     tmp,x3,y1
++        sbcs    z2,z2,tmp
++        mul     tmp,x4,y2
++        sbc     z3,z3,tmp
++        mul     tmp,x2,y2
++        subs    z2,z2,tmp
++        sbc     z3,z3,#0
++        mul     tmp,x2,y1
++        subs    z1,z1,tmp,lsl#16
++        sbcs    z2,z2,tmp,lsr#16
++        sbc     z3,z3,#0
++        mul     tmp,x1,y2
++        subs    z1,z1,tmp,lsl#16
++        sbcs    z2,z2,tmp,lsr#16
++        sbc     z3,z3,#0
++        mul     tmp,x4,y1
++        subs    z2,z2,tmp,lsl#16
++        sbc     z3,z3,tmp,lsr#16
++        mul     tmp,x3,y2
++        subs    z2,z2,tmp,lsl#16
++        sbc     z3,z3,tmp,lsr#16      // subtract divisor * estimated result
++
++      tst     r2,#0xff000000
++      beq     CPDO_dvf_loop_entry
++
++      b       CPDO_dvf_end_entry
++
++CPDO_dvf_loop_negative:
++        rsb     r14,r4,#0
++        mov     r10,r14,lsr#16
++        mul     r9,r10,r1
++        bic     r10,r14,r10,lsl#16
++        mul     r10,r1,r10
++        add     r9,r9,r10,lsr#16      // estimate 16 bits of result in r9
++
++      mov     r2,r2,lsl#16
++      orr     r2,r2,r3,lsr#16
++      rsbs    r3,r9,r3,lsl#16         // shift result left by 16 and
++      sbc     r2,r2,#0                // add in new result bits
++
++        mov     r9,r9,lsl#1
++        mov     y2,r9,lsr#16
++        bic     y1,r9,y2,lsl#16
++        mul     tmp,x1,y1
++        adds    z1,z1,tmp
++        mul     tmp,x3,y1
++        adcs    z2,z2,tmp
++        mul     tmp,x4,y2
++        adc     z3,z3,tmp
++        mul     tmp,x2,y2
++        adds    z2,z2,tmp
++        adc     z3,z3,#0
++        mul     tmp,x2,y1
++        adds    z1,z1,tmp,lsl#16
++        adcs    z2,z2,tmp,lsr#16
++        adc     z3,z3,#0
++        mul     tmp,x1,y2
++        adds    z1,z1,tmp,lsl#16
++        adcs    z2,z2,tmp,lsr#16
++        adc     z3,z3,#0
++        mul     tmp,x4,y1
++        adds    z2,z2,tmp,lsl#16
++        adc     z3,z3,tmp,lsr#16
++        mul     tmp,x3,y2
++        adds    z2,z2,tmp,lsl#16
++        adc     z3,z3,tmp,lsr#16      // subtract divisor * estimated result
++
++      tst     r2,#0xff000000
++      beq     CPDO_dvf_loop_entry
++
++CPDO_dvf_end_entry:
++      movs    r4,r4,asr#1
++      movs    r5,r5,rrx       // remainder was shifted left by 1
++      movs    r6,r6,rrx       // relative to divisor
++
++        orr     r7,x3,x4,lsl#16
++        orr     r8,x1,x2,lsl#16       // put the split divisor together again
++
++      cmp     r4,#0
++      blt     CPDO_dvf_end_negative
++      cmpeq   r5,r7
++      cmpeq   r6,r8
++      bcc     CPDO_dvf_end
++
++CPDO_dvf_end_positive:
++      adds    r3,r3,#1
++      adc     r2,r2,#0
++
++      subs    r6,r6,r8
++      sbcs    r5,r5,r7
++      sbcs    r4,r4,#0
++      bne     CPDO_dvf_end_positive
++
++      cmp     r5,r7
++      cmpeq   r6,r8
++      bcs     CPDO_dvf_end_positive
++      b       CPDO_dvf_end
++
++CPDO_dvf_end_negative:
++      subs    r3,r3,#1
++      sbc     r2,r2,#0
++
++      adds    r6,r6,r8
++      adcs    r5,r5,r7
++      adcs    r4,r4,#0
++      bmi     CPDO_dvf_end_negative
++
++CPDO_dvf_end:
++      orrs    r9,r5,r6
++      ldmia   r13!,{r1,r4,r9,r10,r11,r14}
++      moveq   pc,r14
++
++      adds    r6,r6,r6
++      adcs    r5,r5,r5
++      movcs   r5,#0xc0000000
++      movcs   pc,r14
++
++      cmp     r5,r7
++      cmpeq   r6,r8
++      movcc   r5,#0x40000000
++      moveq   r5,#0x80000000
++      movhi   r5,#0xc0000000
++      mov     pc,r14
++
++CPDO_dvf_zero:
++      cmp     r11,#0x80000000
++      beq     CPDO_dvf_by_zero
++
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next            // 0 already there
++@     mov     pc,r14
++
++CPDO_dvf_by_zero:
++      cmp     r4,#0x80000000
++      beq     CPDO_dvf_generate_qnan  // first 0 too
++
++      mov     r2,#0x80000000  // set MSB
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#2        // division by zero
++      str     r5,[r10,#128]
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_dvf_infnan:
++      cmp     r4,#0x7fffffff
++      beq     CPDO_dvf_1st_infnan
++
++      orrs    r5,r8,r7,lsl#1          // ignore MSB
++      beq     CPDO_dvf_2nd_inf
++      mov     r1,r6
++      mov     r2,r7
++      mov     r3,r8
++      mov     r4,r11
++      b       CPDO_dvf_1st_or_2nd_nan
++
++CPDO_dvf_2nd_inf:
++      eor     r1,r1,r6
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next    // zero created
++@     mov     pc,r14
++
++CPDO_dvf_1st_infnan:
++      cmp     r11,#0x7fffffff
++      beq     CPDO_dvf_both_infnan
++
++      orrs    r5,r3,r2,lsl#1  // 1st inf? ignore MSB
++      bne     CPDO_dvf_1st_or_2nd_nan
++
++      eor     r1,r1,r6        // sign for inf
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next    // inf already there
++@     mov     pc,r14
++
++CPDO_dvf_1st_or_2nd_nan:
++      tst     r2,#0x40000000
++      beq     CPDO_dvf_generate_qnan
++      mov     pc,r14          // qnan1/2 already/copied there 
++
++CPDO_dvf_both_infnan:
++      orrs    r5,r3,r2,lsl#1  // ignore MSB
++      beq     CPDO_dvf_both_infnan_1st_inf
++      orrs    r5,r8,r7,lsl#1  // ignore MSB
++      beq     CPDO_dvf_both_infnan_2nd_inf
++      tst     r2,#0x40000000
++      tstne   r7,#0x40000000
++      beq     CPDO_dvf_generate_qnan
++      mov     pc,r14
++
++CPDO_dvf_both_infnan_1st_inf:
++      tst     r7,#0x40000000          // 2nd inf or SNaN ?
++      beq     CPDO_dvf_generate_qnan
++      mov     r1,r6
++      mov     r2,r7
++      mov     r3,r8
++      mov     r4,r11                  // copy 2nd QNaN
++      mov     pc,r14
++
++CPDO_dvf_both_infnan_2nd_inf:
++      tst     r2,#0x40000000          // 1st SNaN ?
++      beq     CPDO_dvf_generate_qnan
++      mov     pc,r14
++
++CPDO_dvf_generate_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,#0xffffffff
++      mov     r4,#0x7fffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1
++      str     r5,[r10,#128]
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_dvf_M
++CPDO_dvf_M:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++CPDO_dvf_M_l:
++      cmp     r11,#0x7fffffff
++      cmpne   r4,#0x7fffffff
++      beq     CPDO_dvf_infnan
++      eor     r1,r1,r6
++      cmp     r11,#0x80000000
++      cmpne   r4,#0x80000000
++      beq     CPDO_dvf_zero
++
++      sub     r4,r4,r11
++
++      cmp     r2,r7
++      cmpeq   r3,r8
++      bcs     CPDO_dvf_M_no_normalize
++
++      sub     r4,r4,#1
++      stmdb   r13!,{r1,r4,r9,r10}
++      mov     r4,r2,lsr#31
++      mov     r5,r2,lsl#1
++      orr     r5,r5,r3,lsr#31
++      mov     r6,r3,lsl#1     // dividend
++      b       CPDO_dvf_M_normalize_back
++
++CPDO_dvf_M_no_normalize:
++      stmdb   r13!,{r1,r4,r9,r10}
++      mov     r4,#0
++      mov     r5,r2
++      mov     r6,r3           // dividend
++
++CPDO_dvf_M_normalize_back:
++      mov     r1,#0
++      sub     r10,r1,r7,lsr#1
++      mov     r11,#0x40000000
++
++      .macro  inv_step
++      adds    r11,r10,r11,lsl#1
++      subcc   r11,r11,r10
++      adc     r1,r1,r1
++      .endm
++
++      .rept   18
++      inv_step
++      .endr
++
++      mov     r1,r1,lsl#14
++      adds    r1,r1,#1<<15
++      movcs   r1,#0xffffffff  // inverse
++
++      mov     r2,#0
++      mov     r3,#0           // clear result space
++
++CPDO_dvf_M_loop_entry:
++      mov     r4,r4,lsl#16
++      orrs    r4,r4,r5,lsr#16
++      mov     r5,r5,lsl#16
++      orr     r5,r5,r6,lsr#16
++      mov     r6,r6,lsl#16    // shift dividend left by 16
++
++      bmi     CPDO_dvf_M_loop_negative
++      umull   r10,r9,r4,r1    // estimate 16 bits of result in r9
++
++      mov     r2,r2,lsl#16
++      orr     r2,r2,r3,lsr#16
++      adds    r3,r9,r3,lsl#16 // shift result left by 16 and
++      adc     r2,r2,#0        // add in new result bits
++
++      mov     r9,r9,lsl#1
++      umull   r11,r10,r8,r9   // divisor lo * estimated result
++      subs    r6,r6,r11
++      sbcs    r5,r5,r10
++      sbc     r4,r4,#0
++
++      umull   r11,r10,r7,r9   // divisor hi * estimated result
++      subs    r5,r5,r11
++      sbc     r4,r4,r10
++
++      tst     r2,#0xff000000
++      beq     CPDO_dvf_M_loop_entry
++
++      b       CPDO_dvf_M_end_entry
++
++CPDO_dvf_M_loop_negative:
++      rsb     r11,r4,#0
++      umull   r10,r9,r11,r1   // estimate 16 bits of result in r9
++
++      mov     r2,r2,lsl#16
++      orr     r2,r2,r3,lsr#16
++      rsbs    r3,r9,r3,lsl#16 // shift result left by 16 and
++      sbc     r2,r2,#0        // add in new result bits
++
++      mov     r9,r9,lsl#1
++      umull   r11,r10,r8,r9   // divisor lo * estimated result
++      adds    r6,r6,r11
++      adcs    r5,r5,r10
++      adc     r4,r4,#0
++
++      umlal   r5,r4,r7,r9     // divisor hi * estimated result
++
++      tst     r2,#0xff000000
++      beq     CPDO_dvf_M_loop_entry
++
++CPDO_dvf_M_end_entry:
++      movs    r4,r4,asr#1
++      movs    r5,r5,rrx       // remainder was shifted left by 1
++      movs    r6,r6,rrx       // relative to divisor
++
++      cmp     r4,#0
++      blt     CPDO_dvf_M_end_negative
++      cmpeq   r5,r7
++      cmpeq   r6,r8
++      bcc     CPDO_dvf_M_end
++
++CPDO_dvf_M_end_positive:
++      adds    r3,r3,#1
++      adc     r2,r2,#0
++
++      subs    r6,r6,r8
++      sbcs    r5,r5,r7
++      sbcs    r4,r4,#0
++
++      cmp     r5,r7
++      cmpeq   r6,r8
++      bcs     CPDO_dvf_M_end_positive
++      b       CPDO_dvf_M_end
++
++CPDO_dvf_M_end_negative:
++      subs    r3,r3,#1
++      sbc     r2,r2,#0
++
++      adds    r6,r6,r8
++      adcs    r5,r5,r7
++      adcs    r4,r4,#0
++      bmi     CPDO_dvf_M_end_negative
++
++CPDO_dvf_M_end:
++      orrs    r9,r5,r6
++      ldmia   r13!,{r1,r4,r9,r10}
++      moveq   pc,r14
++
++      adds    r6,r6,r6
++      adcs    r5,r5,r5
++      movcs   r5,#0xc0000000
++      movcs   pc,r14
++
++      cmp     r5,r7
++      cmpeq   r6,r8
++      movcc   r5,#0x40000000
++      moveq   r5,#0x80000000
++      movhi   r5,#0xc0000000
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_rdf
++CPDO_rdf:
++      ldmia   r1,{r6,r7,r8,r11}
++      ldmia   r2,{r1,r2,r3,r4}
++      b       CPDO_dvf_l
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_rdf_M
++CPDO_rdf_M:
++      ldmia   r1,{r6,r7,r8,r11}
++      ldmia   r2,{r1,r2,r3,r4}
++      b       CPDO_dvf_M_l
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_rmf
++CPDO_rmf:
++      ldmia   r2,{r6,r7,r8,r11}
++      ldmia   r1,{r1,r2,r3,r4}
++
++        cmp     r11,#0x7fffffff
++        cmpne r4,#0x7fffffff
++        beq     CPDO_rmf_infnan
++        cmp     r11,#0x80000000
++      cmpne   r4,#0x80000000
++        beq     CPDO_rmf_zero
++
++      cmp     r4,r11
++        bge   CPDO_rmf_loop_entry
++      b       CPDO_rmf_smaller
++
++CPDO_rmf_loop_0:
++      mov     r5,#0
++CPDO_rmf_loop:
++      cmp     r4,r11
++      ble     CPDO_rmf_loop_end
++
++      sub     r4,r4,#1
++
++      adds    r3,r3,r3
++      adcs    r2,r2,r2
++      bcs     CPDO_rmf_loop_anyway
++
++CPDO_rmf_loop_entry:
++      cmp     r2,r7
++      cmpeq   r3,r8
++      bcc     CPDO_rmf_loop_0
++
++CPDO_rmf_loop_anyway:
++      subs    r3,r3,r8
++      sbc     r2,r2,r7
++      mov     r5,#1
++      b       CPDO_rmf_loop
++
++CPDO_rmf_loop_end:
++      teq     r2,#0
++      teqeq   r3,#0
++      beq     CPDO_rmf_created_zero
++
++      //eor   r1,r1,r6        // only if result not zero
++
++      mov     r6,r2,lsr#31
++      mov     r11,r2,lsl#1
++      orr     r11,r11,r3,lsr#31
++
++      cmp     r6,#0
++      cmpeq   r11,r7
++      rsbeqs  r6,r8,r3,lsl#1
++      cmpeq   r5,#1           // for nearest-even
++      bcc     CPDO_rmf_norm
++
++      eor     r1,r1,#0x80000000
++      subs    r3,r8,r3
++      sbc     r2,r7,r2
++      
++CPDO_rmf_norm:
++        teq     r2,#0           // normalize 32 bit
++        moveq   r2,r3
++        moveq   r3,#0
++        subeq   r4,r4,#32
++
++        cmp     r2,#0x00010000  // normalize 16 bit
++        movcc   r2,r2,lsl#16
++        orrcc   r2,r2,r3,lsr#16
++        movcc   r3,r3,lsl#16
++        subcc   r4,r4,#16
++
++        cmp     r2,#0x01000000  // normalize 8 bit
++        movcc   r2,r2,lsl#8
++        orrcc   r2,r2,r3,lsr#24
++        movcc   r3,r3,lsl#8
++        subcc   r4,r4,#8
++
++        cmp     r2,#0x10000000  // normalize 4 bit
++        movcc   r2,r2,lsl#4
++        orrcc   r2,r2,r3,lsr#28
++        movcc   r3,r3,lsl#4
++        subcc   r4,r4,#4
++
++        cmp     r2,#0x40000000  // normalize 2 bit
++        movcc   r2,r2,lsl#2
++        orrcc   r2,r2,r3,lsr#30
++        movcc   r3,r3,lsl#2
++        subcc   r4,r4,#2
++
++        cmp     r2,#0x80000000  // normalize 1 bit
++        movcc   r2,r2,lsl#1
++        orrcc   r2,r2,r3,lsr#31
++        movcc   r3,r3,lsl#1
++        subcc   r4,r4,#1
++
++      mov     r5,#0
++      mov     pc,r14
++
++CPDO_rmf_created_zero:
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_rmf_smaller:
++      add     r5,r4,#1
++      cmp     r5,r11
++      blt     CPDO_rmf_norm
++      cmp     r2,r7
++      cmpeq   r3,r8
++      bls     CPDO_rmf_norm
++
++      eor     r1,r1,#0x80000000
++      adds    r8,r8,r8
++      adc     r7,r7,r7
++      subs    r3,r8,r3
++      sbc     r2,r7,r2
++      b       CPDO_rmf_norm
++
++CPDO_rmf_zero:
++      cmp     r11,#0x80000000
++      beq     CPDO_rmf_generate_qnan
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++@     mov     pc,r14
++
++CPDO_rmf_infnan:
++        cmp     r4,#0x7fffffff
++        beq     CPDO_rmf_1st_infnan
++
++        orrs    r5,r8,r7,lsl#1        // ignore MSB
++        beq     CPDO_rmf_2nd_inf
++        mov     r1,r6
++        mov     r2,r7
++        mov     r3,r8
++        mov     r4,r11
++        b       CPDO_rmf_1st_or_2nd_nan
++
++CPDO_rmf_2nd_inf:
++      mov     pc,r14          // result = 1st operand
++
++CPDO_rmf_1st_infnan:
++        cmp     r11,#0x7fffffff
++        beq     CPDO_rmf_both_infnan
++
++        orrs    r5,r3,r2,lsl#1        // 1st inf?
++        bne     CPDO_rmf_1st_or_2nd_nan
++
++      b       CPDO_rmf_generate_qnan
++
++CPDO_rmf_1st_or_2nd_nan:
++        tst     r2,#0x40000000
++        beq     CPDO_rmf_generate_qnan
++        mov     pc,r14          // qnan1/2 already/copied there
++
++CPDO_rmf_both_infnan:
++        orrs    r5,r3,r2,lsl#1        // ignore MSB
++        beq     CPDO_rmf_both_infnan_1st_inf
++        orrs    r5,r8,r7,lsl#1        // ignore MSB
++        beq     CPDO_rmf_both_infnan_2nd_inf
++        tst     r2,#0x40000000
++        tstne   r7,#0x40000000
++        beq     CPDO_rmf_generate_qnan
++        mov     pc,r14
++
++CPDO_rmf_both_infnan_1st_inf:
++        tst     r7,#0x40000000          // 2nd inf or SNaN ?
++        beq     CPDO_rmf_generate_qnan
++        mov     r1,r6
++        mov     r2,r7
++        mov     r3,r8
++        mov     r4,r11                  // copy 2nd QNaN
++        mov     pc,r14
++
++CPDO_rmf_both_infnan_2nd_inf:
++        tst     r2,#0x40000000          // 1st SNaN ?
++        beq     CPDO_rmf_generate_qnan
++        mov     pc,r14
++
++CPDO_rmf_generate_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,#0xffffffff
++      mov     r4,#0x7fffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1
++      str     r5,[r10,#128]
++      mov     pc,r14
++      
++/*---------------------------------------------------------------------------*/
++
++
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_mvf
++CPDO_mvf:
++      ldmia   r2,{r1,r2,r3,r4}
++      mov     r5,#0
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_mnf
++CPDO_mnf:
++      ldmia   r2,{r1,r2,r3,r4}
++      eor     r1,r1,#0x80000000
++      mov     r5,#0
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_abs
++CPDO_abs:
++      ldmia   r2,{r1,r2,r3,r4}
++      bic     r1,r1,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++      
++      .globl  CPDO_sqt
++CPDO_sqt:
++      ldmia   r2,{r1,r2,r3,r4}
++      cmp     r1,#0
++      bne     CPDO_nan
++      cmp     r4,#0x7fffffff
++      beq     CPDO_store_1234
++
++      tst     r4,r4,lsr#1             @carry=exponent bit 0
++      bcc     CPDO_sqt_exponenteven
++      adds    r3,r3,r3
++      adc     r2,r2,r2
++      cmp     r2,#0x20000000          @set carry for loop
++CPDO_sqt_exponenteven:
++      mov     r4,r4,asr #1
++      str     r4,[r0,#12]
++
++      mov     r4,#0x80000000
++      mov     r5,#0
++      sub     r2,r2,#0x80000000
++
++      mov     r8,#0x40000000
++      mov     r14,#0x80000000
++
++      mov     r1,#1
++      b       CPDO_sqt_loop1_first
++CPDO_sqt_loop1:
++      adds    r3,r3,r3
++      adcs    r2,r2,r2
++CPDO_sqt_loop1_first:
++      add     r6,r4,r8,lsr r1         @r7 const = r5
++      bcs     CPDO_sqt_loop1_1
++      cmp     r2,r6
++      cmpeq   r3,r5                   @r5 for r7
++      bcc     CPDO_sqt_loop1_0
++CPDO_sqt_loop1_1:
++      orr     r4,r4,r14,lsr r1
++      subs    r3,r3,r5                @r5 for r7
++      sbc     r2,r2,r6
++CPDO_sqt_loop1_0:
++      add     r1,r1,#1
++      cmp     r1,#30
++      ble     CPDO_sqt_loop1
++
++      adds    r3,r3,r3
++      adcs    r2,r2,r2
++      bcs     CPDO_sqt_between_1
++      adds    r7,r5,#0x80000000
++      adc     r6,r4,#0
++      cmp     r2,r6
++      cmpeq   r3,r7
++      bcc     CPDO_sqt_between_0
++CPDO_sqt_between_1:
++      orr     r4,r4,#0x00000001
++      subs    r3,r3,r5
++      sbc     r2,r2,r4
++      subs    r3,r3,#0x80000000
++      sbc     r2,r2,#0
++CPDO_sqt_between_0:
++      mov     r1,#0
++
++CPDO_sqt_loop2:
++      adds    r3,r3,r3
++      adcs    r2,r2,r2
++      bcs     CPDO_sqt_loop2_1
++      adds    r7,r5,r8,lsr r1
++      adc     r6,r4,#0
++      cmp     r2,r6
++      cmpeq   r3,r7
++      bcc     CPDO_sqt_loop2_0
++CPDO_sqt_loop2_1:
++      orr     r5,r5,r14,lsr r1
++      subs    r3,r3,r5
++      sbc     r2,r2,r4
++      subs    r3,r3,r8,lsr r1
++      sbc     r2,r2,#0
++CPDO_sqt_loop2_0:
++      add     r1,r1,#1
++      cmp     r1,#30
++      ble     CPDO_sqt_loop2
++
++      adds    r3,r3,r3
++      adcs    r2,r2,r2
++      bcs     CPDO_sqt_after_1
++      cmp     r2,r6
++      cmpeq   r3,r7
++      bcc     CPDO_sqt_after_0
++CPDO_sqt_after_1:
++      orr     r5,r5,#0x00000001
++CPDO_sqt_after_0:
++
++      mov     r1,#0
++      stmia   r0,{r1,r4,r5}
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++      
++      .globl  CPDO_rnd
++CPDO_rnd:
++      ldmia   r2,{r1,r2,r3,r5}
++        bl      CPDO_rnd_core
++      ldr     r6,[r10,#128]
++      stmia   r0,{r1,r2,r3,r5}
++      orr     r6,r6,r4
++      str     r6,[r10,#128]
++      b       fastfpe_next
++      
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDO_rnd_core
++CPDO_rnd_core:
++      and     r6,r4,#0x00000060
++      mov     r4,#0           // for return of exception flags
++      cmp     r5,#63
++      bge     CPDO_rnd_big
++      add     pc,pc,r6,lsr#3
++      mov     r0,r0
++      b       CPDO_rnd_NE
++      b       CPDO_rnd_P
++      b       CPDO_rnd_M
++      b       CPDO_rnd_Z
++
++CPDO_rnd_NE:
++      cmp     r5,#0
++      blt     CPDO_rnd_NE_01
++
++      subs    r6,r5,#31
++      bpl     CPDO_rnd_NE_2
++      mov     r7,#0x40000000
++      mov     r8,#0x7fffffff
++      mov     r7,r7,lsr r5
++      mov     r8,r8,lsr r5
++      teq     r3,#0
++      tsteq   r2,r8
++      orrne   r4,r4,#16       // set inexact flag
++      adds    r2,r2,r7
++      bcs     CPDO_rnd_overflow
++      teq     r3,#0
++      tsteq   r2,r8
++      beq     CPDO_rnd_NE_equal
++      mov     r3,#0
++      bic     r2,r2,r8
++      mov     pc,r14
++
++CPDO_rnd_NE_2:
++      mov     r7,#0x80000000
++      mov     r8,#0xffffffff
++      mov     r7,r7,lsr r6
++      mov     r8,r8,lsr r6
++      tst     r3,r8
++      orrne   r4,r4,#16       // set inexact flag
++      adds    r3,r3,r7
++      adcs    r2,r2,#0
++      bcs     CPDO_rnd_overflow
++      tst     r3,r8
++      beq     CPDO_rnd_NE_equal
++      bic     r3,r3,r8
++      mov     pc,r14
++
++CPDO_rnd_NE_equal:
++      mov     r7,#0x80000000
++      subs    r6,r5,#32
++      bicpl   r3,r3,r7,lsr r6
++      bicmi   r2,r2,r7,lsr r5
++      mov     pc,r14
++
++CPDO_rnd_NE_01:
++      cmp     r5,#-1
++      bne     CPDO_rnd_0
++      cmp     r2,#0x80000000
++      cmpeq   r3,#0
++      beq     CPDO_rnd_0
++
++      mov     r2,#0x80000000
++      mov     r3,#0
++      mov     r5,#0
++      orr     r4,r4,#16       // set inexact flag
++      mov     pc,r14
++
++CPDO_rnd_P:
++      teq     r1,#0
++      beq     CPDO_rnd_NZ
++      b       CPDO_rnd_Z
++
++CPDO_rnd_M:
++      teq     r1,#0
++      beq     CPDO_rnd_Z
++      b       CPDO_rnd_NZ
++      
++CPDO_rnd_Z:
++      cmp     r5,#0           // smaller than 1 will be 0
++      blt     CPDO_rnd_0
++
++      rsbs    r6,r5,#31
++      bmi     CPDO_rnd_Z_2
++      cmp     r3,#0
++      mov     r3,#0
++      mov     r7,r2,lsr r6
++      teqeq   r2,r7,lsl r6
++      mov     r2,r7,lsl r6
++      orrne   r4,r4,#16       // set inexact flag
++      mov     pc,r14
++
++CPDO_rnd_Z_2:
++      rsb     r6,r5,#63
++      mov     r7,r3,lsr r6
++      teq     r3,r7,lsl r6
++      mov     r3,r7,lsl r6
++      orrne   r4,r4,#16       // set inexact flag
++      mov     pc,r14
++
++CPDO_rnd_0:
++      cmp     r5,#0x80000000
++      moveq   pc,r14          // already 0 -> ok
++
++      mov     r2,#0
++      mov     r3,#0
++      mov     r5,#0x80000000
++      orr     r4,r4,#16       // set inexact flag
++      mov     pc,r14
++
++CPDO_rnd_NZ:
++      cmp     r5,#0           // smaller than 1 will be stay 0 or become 1
++      blt     CPDO_rnd_NZ_01
++
++      mov     r7,#0x7fffffff
++      subs    r6,r5,#32
++      bpl     CPDO_rnd_NZ_2
++      mov     r7,r7,lsr r5
++      teq     r3,#0
++      tsteq   r2,r7
++      orrne   r4,r4,#16       // set inexact flag
++      adds    r3,r3,#0xffffffff
++      adcs    r2,r2,r7
++      bcs     CPDO_rnd_overflow
++      mov     r3,#0
++      bic     r2,r2,r7
++      mov     pc,r14
++
++CPDO_rnd_NZ_2:
++      mov     r7,r7,lsr r6
++      tst     r3,r7
++      orrne   r4,r4,#16       // set inexact flag
++      adds    r3,r3,r7
++      adcs    r2,r2,#0
++      bcs     CPDO_rnd_overflow
++      bic     r3,r3,r7
++      mov     pc,r14
++
++CPDO_rnd_NZ_01:
++      cmp     r5,#0x80000000
++      moveq   pc,r14          // already 0 -> ok
++
++      mov     r2,#0x80000000
++      mov     r3,#0
++      mov     r5,#0
++      orr     r4,r4,#16       // set inexact flag
++      mov     pc,r14
++
++CPDO_rnd_overflow:
++      mov     r2,#0x80000000
++      mov     r3,#0
++      add     r5,r5,#1
++      mov     pc,r14
++
++CPDO_rnd_big:
++      cmp     r5,#0x7fffffff
++      movne   pc,r14                  // just big
++      orrs    r6,r3,r2,lsl#1          // ignore MSB
++      moveq   pc,r14                  // infinity
++      tst     r2,#0x40000000          // signalling NaN ?
++      orreq   r4,r4,#1                // set invalid operation flag
++      orreq   r2,r2,#0x40000000       // make quiet NaN
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/CPDT.S
+@@ -0,0 +1,396 @@
++/*
++The FP structure has 4 words reserved for each register, the first is used just
++for the sign in bit 31, the second and third are for the mantissa (unsigned
++integer, high 32 bit first) and the fourth is the exponent (signed integer).
++The mantissa is always normalized.
++
++If the exponent is 0x80000000, that is the most negative value, the number
++represented is 0 and both mantissa words are also 0.
++
++If the exponent is 0x7fffffff, that is the biggest positive value, the number
++represented is infinity if the mantissa is 0, otherwise it is a NaN.
++
++Decimal and packed decimal numbers are not supported yet.
++*/
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_load_single
++CPDT_load_single:
++      ldr     r1,[r6]
++      
++      and     r2,r1,#0x80000000       @ r2 = sign
++      
++      mov     r5,r1,lsr#23
++      bics    r5,r5,#0x100
++      beq     CPDT_ls_e0              @ exponent = 0; zero/denormalized
++      teq     r5,#255
++      beq     CPDT_ls_e255            @ exponent = 255; infinity/NaN
++      
++      sub     r5,r5,#127              @ r5 = exponent, remove normalized bias
++      
++      mov     r3,r1,lsl#8
++      orr     r3,r3,#0x80000000
++      mov     r4,#0                   @ r3,r4 = mantissa
++
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++      
++CPDT_ls_e0:
++      movs    r3,r1,lsl#9
++      beq     CPDT_load_zero
++      
++      mov     r5,#-127
++
++CPDT_ls_e0_norm:
++      tst     r3,#0x80000000
++      subeq   r5,r5,#1
++      moveq   r3,r3,lsl#1
++      beq     CPDT_ls_e0_norm
++      
++      mov     r4,#0
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++      
++CPDT_ls_e255:
++      mov     r3,r1,lsl#8
++      bics    r3,r3,#0x80000000
++      orreq   r3,r3,#0x80000000       // set MSB for inf
++      mov     r4,#0
++      mov     r5,#0x7fffffff
++      stmia   r0,{r2-r5}
++      b       fastfpe_next    
++
++CPDT_load_zero:
++      mov     r3,#0
++      mov     r4,#0
++      mov     r5,#0x80000000
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_load_double
++CPDT_load_double:
++      ldr     r1,[r6]
++      ldr     r6,[r6,#4]
++      
++      and     r2,r1,#0x80000000       @ r2 = sign
++      
++      mov     r5,r1,lsr#20
++      bics    r5,r5,#0x800
++      beq     CPDT_ld_e0              @ exponent = 0; zero/denormalized
++      add     r4,r5,#1
++      teq     r4,#2048
++      beq     CPDT_ld_e2047           @ exponent = 2047; infinity/NaN
++      
++      add     r5,r5,#1
++      sub     r5,r5,#1024             @ r5 = exponent, remove normalized bias
++      
++      mov     r3,r1,lsl#11
++      orr     r3,r3,#0x80000000
++      orr     r3,r3,r6,lsr #21
++      mov     r4,r6,lsl#11            @ r3,r4 = mantissa
++
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++      
++CPDT_ld_e0:
++      mov     r3,r1,lsl#12
++      orr     r3,r3,r6,lsr#20
++      movs    r4,r6,lsl#12
++      teqeq   r3,#0
++      beq     CPDT_load_zero
++      
++      mov     r5,#1
++      sub     r5,r5,#1024
++      
++CPDT_ld_e0_norm:
++      tst     r3,#0x80000000
++      bne     CPDT_ld_e0_norm_end
++      sub     r5,r5,#1
++      movs    r4,r4,lsl#1
++      adc     r3,r3,r3
++      b       CPDT_ld_e0_norm
++CPDT_ld_e0_norm_end:  
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++
++CPDT_ld_e2047:
++      mov     r3,r1,lsl#11
++      orr     r3,r3,r6,lsr #21
++      bic     r3,r3,#0x80000000
++      mov     r4,r6,lsl#11            @ r3,r4 = mantissa
++      orrs    r5,r3,r4
++      orreq   r3,r3,#0x80000000       // set MSB fo inf
++      mov     r5,#0x7fffffff
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++      
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_load_extended
++CPDT_load_extended:
++      ldr     r1,[r6]
++      ldr     r3,[r6,#4]
++      ldr     r4,[r6,#8]
++      
++      and     r2,r1,#0x8000
++      mov     r2,r2,lsl#16
++      mov     r5,r1,lsl#17
++      movs    r5,r5,lsr#17
++      beq     CPDT_le_e0
++      add     r1,r5,#1
++      teq     r1,#32768
++      beq     CPDT_le_e32767
++      
++      add     r5,r5,#1
++      sub     r5,r5,#16384
++      
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++      
++CPDT_le_e0:
++      teq     r3,#0
++      teqeq   r4,#0
++      beq     CPDT_load_zero
++      
++      mov     r5,#2
++      sub     r5,r5,#16384
++      b       CPDT_ld_e0_norm
++      
++CPDT_le_e32767:
++      mov     r5,#0x7fffffff
++      stmia   r0,{r2-r5}
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_load_decimal
++CPDT_load_decimal:
++      
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_store_single
++CPDT_store_single:
++      ldmia   r0,{r1-r4}
++      
++      cmp     r4,#-127
++      ble     CPDT_ss_e0
++      cmp     r4,#128
++      bge     CPDT_ss_e255
++
++      add     r4,r4,#127
++      orr     r1,r1,r4,lsl#23
++      
++      bic     r2,r2,#0x80000000
++      orr     r1,r1,r2,lsr#8
++
++      str     r1,[r6]
++      b       fastfpe_next
++
++CPDT_ss_e0:
++      cmp     r4,#-150
++      ble     CPDT_ss_zero
++      
++      add     r4,r4,#126
++      rsb     r4,r4,#0
++      mov     r2,r2,lsr r4
++      
++      orr     r1,r1,r2,lsr#8
++      
++CPDT_ss_zero:
++      str     r1,[r6]
++      b       fastfpe_next
++
++CPDT_ss_e255:
++      orr     r1,r1,#0x7f000000
++      orr     r1,r1,#0x00800000
++      cmp     r4,#0x7fffffff
++      movne   r2,#0
++      movne   r3,#0
++      bic     r2,r2,#0x80000000
++      orrs    r4,r3,r2,lsl#24         // only bits not stored in single
++      bne     CPDT_ss_nan_special     // NaN must not become Inf
++CPDT_ss_nan_back:
++      orr     r1,r1,r2,lsr#8
++      str     r1,[r6]
++      b       fastfpe_next
++
++CPDT_ss_nan_special:
++      cmp     r2,#1<<8
++      movlt   r2,#1<<8
++      b       CPDT_ss_nan_back
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_store_double
++CPDT_store_double:
++      ldmia   r0,{r1-r4}
++      
++      cmp     r4,#1024                @ this check has to be first, or
++      bge     CPDT_sd_e2047           @ overflow can occur on second !
++      add     r0,r4,#3
++      cmp     r0,#-1023+3             @ cmp with -1023
++      ble     CPDT_sd_e0
++
++      sub     r4,r4,#1
++      add     r4,r4,#1024
++      orr     r1,r1,r4,lsl#20
++      
++      bic     r2,r2,#0x80000000
++      orr     r1,r1,r2,lsr#11
++      
++      mov     r2,r2,lsl#21
++      orr     r2,r2,r3,lsr#11
++      
++      stmia   r6,{r1,r2}
++      b       fastfpe_next
++
++CPDT_sd_e0:
++      add     r0,r4,#1075-1024
++      cmp     r0,#-1024
++      ble     CPDT_sd_zero
++      
++      add     r4,r4,#1024
++      sub     r4,r4,#2
++CPDT_sd_unnormalize:
++      movs    r2,r2,lsr#1
++      mov     r3,r3,rrx
++      adds    r4,r4,#1
++      bne     CPDT_sd_unnormalize
++      
++      orr     r1,r1,r2,lsr#11
++      mov     r2,r2,lsl#21
++      orr     r2,r2,r3,lsr#11
++      
++      stmia   r6,{r1,r2}
++      b       fastfpe_next
++
++CPDT_sd_zero:
++      mov     r2,#0
++      stmia   r6,{r1,r2}
++      b       fastfpe_next
++
++CPDT_sd_e2047:
++      orr     r1,r1,#0x7f000000
++      orr     r1,r1,#0x00f00000
++      cmp     r4,#0x7fffffff
++      movne   r2,#0
++      movne   r3,#0
++      movs    r5,r3,lsl#21            // only bits not stored in double !
++      bne     CPDT_sd_nan_special
++CPDT_sd_nan_back:
++      orr     r1,r1,r2,lsr#11
++      mov     r2,r2,lsl#21
++      orr     r2,r2,r3,lsr#11
++      stmia   r6,{r1,r2}
++      b       fastfpe_next
++
++CPDT_sd_nan_special:
++      bics    r2,r2,#0x80000000
++      bne     CPDT_sd_nan_back
++      cmp     r3,#1<<11
++      movlt   r3,#1<<11
++      b       CPDT_sd_nan_back
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_store_extended
++CPDT_store_extended:
++      ldmia   r0,{r1-r4}
++      
++      cmp     r4,#16384               @ this check has to be first, or
++      bge     CPDT_se_e32767          @ overflow can occur with second !
++      add     r0,r4,#63
++      cmp     r0,#-16383+63
++      ble     CPDT_se_e0
++      
++      sub     r4,r4,#1
++      add     r4,r4,#16384
++      orr     r1,r4,r1,lsr#16
++      
++      stmia   r6,{r1-r3}
++      b       fastfpe_next
++
++CPDT_se_e0:
++      add     r0,r4,#16446-16384
++      cmp     r0,#-16384
++      ble     CPDT_se_zero
++      
++      add     r4,r4,#16384
++      sub     r4,r4,#2
++CPDT_se_unnormalize:
++      movs    r2,r2,lsr#1
++      mov     r3,r3,rrx
++      adds    r4,r4,#1
++      bne     CPDT_se_unnormalize
++
++      mov     r1,r1,lsr#16
++      stmia   r6,{r1-r3}
++      b       fastfpe_next
++      
++CPDT_se_zero:
++      mov     r1,r1,lsr#16
++      mov     r2,#0
++      mov     r3,#0
++      stmia   r6,{r1-r3}
++      b       fastfpe_next
++
++CPDT_se_e32767:
++      cmp     r4,#0x7fffffff
++      movne   r2,#0
++      movne   r3,#0
++      mov     r1,r1,lsr#16
++      orr     r1,r1,#0x00007f00
++      orr     r1,r1,#0x000000ff
++      stmia   r6,{r1-r3}
++      b       fastfpe_next
++              
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_store_decimal
++CPDT_store_decimal:
++
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_sfm
++CPDT_sfm_loop:
++      add     r0,r0,#1<<12
++      and     r0,r0,#7<<12
++CPDT_sfm:
++      add     r7,r10,r0,lsr#8
++      ldmia   r7,{r2-r5}
++      bic     r3,r3,#0x80000000
++      orr     r3,r3,r2
++      stmia   r6!,{r3-r5}
++
++      subs    r1,r1,#1
++      bne     CPDT_sfm_loop
++      b       fastfpe_next
++      
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPDT_lfm
++CPDT_lfm_loop:
++      add     r0,r0,#1<<12
++      and     r0,r0,#7<<12
++CPDT_lfm:
++      add     r7,r10,r0,lsr#8
++      ldmia   r6!,{r3-r5}
++      and     r2,r3,#0x80000000
++      cmp     r5,#0x80000000          // check if the number was 0
++      cmpne   r5,#0x7fffffff          // or inf/NaN
++      biceq   r3,r3,#0x80000000       // yes -> clear mantissa MSB
++      orrne   r3,r3,#0x80000000       // no -> set mantissa MSB
++      stmia   r7,{r2-r5}
++
++      subs    r1,r1,#1
++      bne     CPDT_lfm_loop
++      b       fastfpe_next
++      
++/*---------------------------------------------------------------------------*/
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/CPRT.S
+@@ -0,0 +1,220 @@
++/*
++The FP structure has 4 words reserved for each register, the first is used just
++for the sign in bit 31, the second and third are for the mantissa (unsigned
++integer, high 32 bit first) and the fourth is the exponent (signed integer).
++The mantissa is always normalized.
++
++If the exponent is 0x80000000, that is the most negative value, the number
++represented is 0 and both mantissa words are also 0.
++
++If the exponent is 0x7fffffff, that is the biggest positive value, the number
++represented is infinity if the mantissa is 0, otherwise it is a NaN.
++
++Decimal and packed decimal numbers are not supported yet.
++*/
++
++/*---------------------------------------------------------------------------*/
++
++      .text
++      .globl  CPRT_flt
++CPRT_flt:
++      add     r0,r13,r0,lsr#10
++      ldr     r2,[r0]
++      mov     r0,r1
++      mov     r3,#0
++      cmp     r2,#0
++      beq     CPRT_flt_zero
++
++      ldr     r6,=round_table
++      and     r5,r4,#0x000000e0
++      and     r4,r4,#0x00080000
++      orr     r5,r5,r4,lsr#11
++      ldr     r6,[r6,r5,lsr#3]        // address of rounding function
++      
++      ands    r1,r2,#0x80000000
++      rsbne   r2,r2,#0
++      mov     r4,#31
++      
++      cmp     r2,#0x00010000
++      movcc   r2,r2,lsl#16
++      subcc   r4,r4,#16
++      
++      cmp     r2,#0x01000000
++      movcc   r2,r2,lsl#8
++      subcc   r4,r4,#8
++      
++      cmp     r2,#0x10000000
++      movcc   r2,r2,lsl#4
++      subcc   r4,r4,#4
++      
++      cmp     r2,#0x40000000
++      movcc   r2,r2,lsl#2
++      subcc   r4,r4,#2
++      
++      cmp     r2,#0x80000000
++      movcc   r2,r2,lsl#1
++      subcc   r4,r4,#1
++
++      mov     r5,#0
++      ldr     r14,=fastfpe_next
++      mov     pc,r6
++
++CPRT_flt_zero:
++      mov     r1,#0
++      mov     r4,#0x80000000
++      stmia   r0,{r1,r2,r3,r4}
++      b       fastfpe_next
++      
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPRT_fix
++CPRT_fix:
++      ldmia   r2,{r1,r2,r3,r5}
++      bl      CPDO_rnd_core
++      
++      add     r0,r13,r0,lsr#10
++      cmp     r5,#0
++      blt     CPRT_fix_zero
++      cmp     r5,#30
++      bgt     CPRT_fix_overflow
++
++CPRT_fix_no_overflow: 
++      rsb     r5,r5,#31
++      mov     r2,r2,lsr r5
++      tst     r1,#0x80000000
++      rsbne   r2,r2,#0
++CPRT_fix_zero_back:
++      str     r2,[r0]
++      ldr     r1,[r10,#128]
++      orr     r1,r1,r4        // set flags possibly caused by rounding
++      str     r1,[r10,#128]
++      b       fastfpe_next
++
++CPRT_fix_zero:
++      mov     r2,#0
++      b       CPRT_fix_zero_back
++
++CPRT_fix_overflow:
++      cmp     r1,#0x80000000  // -2^31 is not exactly an overflow ...
++      cmpeq   r2,#0x80000000
++      cmpeq   r5,#31
++      beq     CPRT_fix_no_overflow
++
++      mov     r2,#0x80000000
++      tst     r1,#0x80000000
++      subeq   r2,r2,#1
++      str     r2,[r0]
++
++      ldr     r1,[r10,#128]
++      orr     r1,r1,#1        // set invalid operation flag
++      str     r1,[r10,#128]
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPRT_wfs
++CPRT_wfs:
++        ldr     r0,[r13,r0,lsr#10]
++        str     r0,[r10,#128]
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPRT_rfs
++CPRT_rfs:
++        ldr     r1,[r10,#128]
++        bic     r1,r1,#0xff000000
++        orr     r1,r1,#0x02000000       @ Software Emulation, not Acorn FPE
++        str     r1,[r13,r0,lsr#10]
++      b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPRT_cmf
++CPRT_cmf:
++      ldmia   r1,{r1,r3,r5,r7}
++      ldmia   r2,{r2,r4,r6,r8}
++
++CPRT_cmf_e:
++      ldr     r0,[r13,#16*4]
++      bic     r0,r0,#0xf0000000
++      
++      cmp     r7,#0x7fffffff
++      beq     CPRT_cmf_nan1
++CPRT_cmf_nixnan1:
++      cmp     r8,#0x7fffffff
++      beq     CPRT_cmf_nan2
++CPRT_cmf_nixnan2:
++
++      cmp     r1,r2
++      beq     CPRT_cmf_equalsign
++      b       CPRT_cmf_signx
++
++CPRT_cmf_nan1:
++      orrs    r11,r5,r3,lsl#1         // ignore MSB
++      beq     CPRT_cmf_nixnan1
++      b       CPRT_cmf_unordered
++
++CPRT_cmf_nan2:
++      orrs    r11,r6,r4,lsl#1         // ignore MSB
++      beq     CPRT_cmf_nixnan2
++      b       CPRT_cmf_unordered
++
++CPRT_cmf_equalsign:
++      cmp     r7,r8
++      beq     CPRT_cmf_equalexponent
++      bgt     CPRT_cmf_sign
++      b       CPRT_cmf_signb  
++
++CPRT_cmf_equalexponent:
++      cmp     r3,r4
++      cmpeq   r5,r6
++      beq     CPRT_cmf_equal
++      bhi     CPRT_cmf_sign
++      b       CPRT_cmf_signb
++
++CPRT_cmf_signx:
++      teq     r7,#0x80000000
++      teqeq   r8,#0x80000000
++      beq     CPRT_cmf_equal
++CPRT_cmf_sign:
++      tst     r1,#0x80000000
++      orreq   r0,r0,#0x20000000       // PSR carry
++      orrne   r0,r0,#0x80000000       // PSR negative
++      str     r0,[r13,#16*4]
++      b       fastfpe_next
++
++CPRT_cmf_signb:
++      tst     r1,#0x80000000
++      orrne   r0,r0,#0x20000000       // PSR carry
++      orreq   r0,r0,#0x80000000       // PSR negative
++      str     r0,[r13,#16*4] 
++      b       fastfpe_next
++
++CPRT_cmf_equal:
++      orr     r0,r0,#0x60000000       // PSR carry, zero
++      str     r0,[r13,#16*4]
++      b       fastfpe_next
++
++CPRT_cmf_unordered:
++      ldr     r1,[r10,#128]
++      orr     r1,r1,#1                // set invalid operation flag
++      str     r1,[r10,#128]
++
++      tst     r0,#1<<12               // FPSR AC bit set ?
++      orrne   r0,r0,#0x20000000       // PSR carry
++      orr     r0,r0,#0x10000000       // PSR overflow
++        str     r0,[r13,#16*4]
++
++        b       fastfpe_next
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  CPRT_cnf
++CPRT_cnf:
++      ldmia   r1,{r1,r3,r5,r7}
++      ldmia   r2,{r2,r4,r6,r8}
++      eor     r2,r2,#0x80000000
++      b       CPRT_cmf_e
++
++/*---------------------------------------------------------------------------*/
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/Makefile
+@@ -0,0 +1,27 @@
++#
++# linux/arch/arm/fastfpe/Makefile
++#
++# Copyright (C) Peter Teichmann
++#
++
++O_TARGET := fast-math-emu.o
++
++AFLAGS_CPDO.o := -march=armv3m
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++fastfpe-objs := module.o round.o CPDT.o CPRT.o CPDO.o entry.o
++
++list-multi := fastfpe.o
++
++obj-$(CONFIG_FPE_FASTFPE) += fastfpe.o
++
++USE_STANDARD_AS_RULE := true
++
++include $(TOPDIR)/Rules.make
++
++fastfpe.o: $(fastfpe-objs)
++      $(LD) -r -o $@ $(fastfpe-objs)
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/entry.S
+@@ -0,0 +1,339 @@
++/*
++At entry the registers contain the following information:
++
++r14   return address for undefined exception return
++r9    return address for return from exception
++r13   user registers on stack, offset 0 up to offset 4*15 contains
++      registers r0..15, then the psr
++r10   FP workspace 35 words (init, reg[8][4], fpsr, fpcr)
++ 
++*/
++
++#include <asm/procinfo.h>
++
++/*---------------------------------------------------------------------------*/
++
++      .data
++fp_const:
++      .word   0, 0x00000000, 0, 0x80000000    @ 0
++      .word   0, 0x80000000, 0,          0    @ 1
++      .word   0, 0x80000000, 0,          1    @ 2
++      .word   0, 0xc0000000, 0,          1    @ 3
++      .word   0, 0x80000000, 0,          2    @ 4
++      .word   0, 0xa0000000, 0,          2    @ 5
++      .word   0, 0x80000000, 0,         -1    @ 0.5
++      .word   0, 0xa0000000, 0,          3    @ 10
++fp_undef:
++      .word   0
++fp_cond:
++      .word   0xf0f0  @ eq
++      .word   0x0f0f  @ ne
++      .word   0xcccc  @ cs
++      .word   0x3333  @ cc
++      .word   0xff00  @ mi
++      .word   0x00ff  @ pl
++      .word   0xaaaa  @ vs
++      .word   0x5555  @ vc
++      .word   0x0c0c  @ hi
++      .word   0xf3f3  @ ls
++      .word   0xaa55  @ ge
++      .word   0x55aa  @ lt
++      .word   0x0a05  @ gt
++      .word   0xf5fa  @ le
++      .word   0xffff  @ al
++      .word   0x0000  @ nv
++      
++/*---------------------------------------------------------------------------*/
++      
++      .text
++      .globl  fastfpe_enter
++fastfpe_enter:
++      ldr     r4,=fp_undef
++      str     r14,[r4]                @ to free one register
++      add     r10,r10,#4              @ to make the code simpler
++      ldr     r4,[r13,#60]            @ r4=saved PC
++      ldr     r4,[r4,#-4]             @ r4=trapped instruction
++      and     r1,r4,#0x00000f00       @ r1=coprocessor << 8
++next_enter:
++      cmp     r1,#1<<8                @ copro 1 ?
++      beq     copro_1
++      cmp     r1,#2<<8
++      movne   pc,r14
++
++copro_2:
++      and     r1,r4,#0x0f000000
++      cmp     r1,#0x0c000000          @ CPDT with post indexing
++        cmpne   r1,#0x0d000000          @ CPDT with pre indexing
++        beq     CPDT_M_enter
++      mov     pc,r14
++
++copro_1:
++      and     r1,r4,#0x0f000000
++      cmp     r1,#0x0e000000          @ CPDO
++      beq     CPDO_CPRT_enter
++      cmp     r1,#0x0c000000          @ CPDT with post indexing
++      cmpne   r1,#0x0d000000          @ CPDT with pre indexing
++      beq     CPDT_1_enter
++      mov     pc,r14
++
++/*---------------------------------------------------------------------------*/
++
++      .globl  fastfpe_next
++fastfpe_next:
++      ldr     r5,[r13,#60]
++next_after_cond_false:
++__x1:
++      ldrt    r4,[r5],#4
++      
++      ldr     r0,=fp_cond             @ check condition of next instruction
++      mov     r2,r4,lsr#28
++      cmp     r2,#0xe                 @ "always" condition code
++      bne     next_check_cond
++
++next_check_copro:
++      and     r1,r4,#0x0f000000       @ Test for copro instruction
++      cmp     r1,#0x0c000000
++      rsbgts  r0,r1,#0x0e000000       @ cmpgt #0x0e000000,r1
++      movlt   pc,r9                   @ next is no copro instruction, return
++      
++      ands    r1,r4,#0x00000f00       @ r1 = coprocessor << 8
++      cmpne   r1,#3<<8
++      movge   pc,r9                   @ copro = 0 or >=3, return
++              
++      str     r5,[r13,#60]            @ save updated pc
++      cmp     r1,#1<<8                @ which copro ?
++      beq     copro_1
++      b       copro_2
++
++next_check_cond:
++      ldr     r1,[r13,#64]            @ psr containing flags
++      ldr     r0,[r0,r2,lsl#2]
++      mov     r1,r1,lsr#28
++      mov     r0,r0,lsr r1
++      tst     r0,#1
++      bne     next_check_copro
++      b       next_after_cond_false   @ must not necessarily have been an
++                                      @ FP instruction !
++
++/*---------------------------------------------------------------------------*/
++
++undefined:
++      ldr     r4,=fp_undef
++      ldr     pc,[r4] 
++
++/*---------------------------------------------------------------------------*/
++
++CPDT_1_enter:
++      and     r5,r4,#0x000f0000       @ r5=base register number << 16
++      ldr     r6,[r13,r5,lsr#14]      @ r6=base address
++      cmp     r5,#0x000f0000          @ base register = pc ?
++      addeq   r6,r6,#4
++      and     r7,r4,#0x000000ff       @ r7=offset value
++
++      tst     r4,#0x00800000          @ up or down?
++      addne   r7,r6,r7,lsl#2
++      subeq   r7,r6,r7,lsl#2          @ r6=base address +/- offset
++      tst     r4,#0x01000000          @ preindexing ?
++      movne   r6,r7
++      tst     r4,#0x00200000          @ write back ?
++      cmpne   r5,#0x000f0000          @ base register = pc ?
++      strne   r7,[r13,r5,lsr#14]
++
++      and     r0,r4,#0x00007000       @ r0=fp register number << 12
++      add     r0,r10,r0,lsr#8         @ r0=address of fp register
++
++      and     r1,r4,#0x00008000       @ T0
++      tst     r4,#0x00400000
++      orrne   r1,r1,#0x00010000       @ T1
++      tst     r4,#0x00100000
++      orrne   r1,r1,#0x00020000       @ L/S   
++
++      ldr     pc,[pc,r1,lsr#13]
++      .word   0
++      .word   CPDT_store_single       @ these functions get 
++      .word   CPDT_store_double       @ r0=address of fp register
++      .word   CPDT_store_extended     @ r6=address of data
++      .word   undefined               @ CPDT_store_decimal
++        .word CPDT_load_single
++        .word CPDT_load_double
++        .word CPDT_load_extended
++        .word undefined               @ CPDT_load_decimal
++
++/*---------------------------------------------------------------------------*/
++
++CPDT_M_enter:
++      and     r5,r4,#0x000f0000       @ r5=base register number << 16
++      ldr     r6,[r13,r5,lsr#14]      @ r6=base address
++      cmp     r5,#0x000f0000          @ base register = pc ?
++      addeq   r6,r6,#4
++      and     r7,r4,#0x000000ff       @ r7=offset value
++
++      tst     r4,#0x00800000          @ up or down?
++      addne   r7,r6,r7,lsl#2
++      subeq   r7,r6,r7,lsl#2          @ r7=base address +/- offset
++      tst     r4,#0x01000000          @ preindexing ?
++      movne   r6,r7
++      tst     r4,#0x00200000          @ write back ?
++      cmpne   r5,#0x000f0000          @ base register = pc ?
++      strne   r7,[r13,r5,lsr#14]
++
++      and     r0,r4,#0x00007000       @ r0=fp register number << 12
++      and     r1,r4,#0x00008000
++      mov     r1,r1,lsr#15            @ N0
++      and     r2,r4,#0x00400000
++      orrs    r1,r1,r2,lsr#21         @ N1
++      addeq   r1,r1,#4                @ r1=register count
++
++      tst     r4,#0x00100000          @ load/store
++      beq     CPDT_sfm
++      b       CPDT_lfm
++
++/*---------------------------------------------------------------------------*/
++
++CPDO_CPRT_enter:
++      tst     r4,#0x00000010
++      bne     CPRT_enter
++      
++      and     r0,r4,#0x00007000
++      add     r0,r10,r0,lsr#8         @ r0=address of Fd
++      and     r1,r4,#0x00070000
++      add     r1,r10,r1,lsr#12        @ r1=address of Fn
++      tst     r4,#0x00000008
++      bne     CPDO_const
++      and     r2,r4,#0x00000007
++      add     r2,r10,r2,lsl#4         @ r2=address of Fm
++      
++CPDO_constback:
++      ldr     r3,=round_table
++      and     r5,r4,#0x000000e0
++      and     r6,r4,#0x00080000
++      orr     r5,r5,r6,lsr#11         @ r5=containing rounding mode/precision
++      ldr     r14,[r3,r5,lsr#3]       @ r14=address of rounding function
++      and     r3,r4,#0x00f00000
++      tst     r4,#0x00008000
++      orrne   r3,r3,#0x01000000       @ r3=operation code
++
++      ldr     pc,[pc,r3,lsr#18]
++      .word   0
++CPDO_table:
++      .word   CPDO_adf
++      .word   CPDO_muf
++      .word   CPDO_suf
++      .word   CPDO_rsf
++      .word   CPDO_dvf
++      .word   CPDO_rdf
++      .word   undefined
++      .word   undefined
++      .word   CPDO_rmf
++      .word   CPDO_muf
++      .word   CPDO_dvf
++      .word   CPDO_rdf
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   CPDO_mvf
++      .word   CPDO_mnf
++      .word   CPDO_abs
++      .word   CPDO_rnd
++      .word   CPDO_sqt
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   CPDO_rnd
++      .word   fastfpe_next
++
++CPDO_const:
++      ldr     r2,=fp_const
++      and     r3,r4,#0x00000007
++      add     r2,r2,r3,lsl#4  
++      b       CPDO_constback
++      
++/*---------------------------------------------------------------------------*/
++
++CPRT_enter:
++      and     r0,r4,#0x0000f000       @ r0=Rd<<12
++      and     r1,r4,#0x00070000
++      add     r1,r10,r1,lsr#12        @ r1=address of Fn
++      tst     r4,#0x00000008
++      bne     CPRT_const
++      and     r2,r4,#0x00000007
++      add     r2,r10,r2,lsl#4         @ r2=address of Fm
++      
++CPRT_constback:
++      and     r3,r4,#0x00f00000
++              
++      ldr     pc,[pc,r3,lsr#18]
++      .word   0
++      .word   CPRT_flt
++      .word   CPRT_fix
++      .word   CPRT_wfs
++      .word   CPRT_rfs
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   undefined
++      .word   CPRT_cmf
++      .word   undefined
++      .word   CPRT_cnf
++      .word   undefined
++      .word   CPRT_cmf
++      .word   undefined
++      .word   CPRT_cnf
++
++CPRT_const:
++      ldr     r2,=fp_const
++      and     r3,r4,#0x00000007
++      add     r2,r2,r3,lsl#4  
++      b       CPRT_constback
++
++/*---------------------------------------------------------------------------*/
++
++      @ Test if long multiply instructions are available
++
++      .globl  fastfpe_test
++fastfpe_test:
++      .globl  elf_hwcap
++        ldr     r0,=elf_hwcap
++        ldr     r0,[r0]
++        tst     r0,#HWCAP_FAST_MULT
++        bne     fastfpe_has_long_multiply
++      mov     r0,#0
++      mov     pc,r14
++
++fastfpe_has_long_multiply:
++      adr     r0,CPDO_table
++      ldr     r1,=CPDO_muf_M
++      str     r1,[r0,#1*4]    @ muf
++      str     r1,[r0,#9*4]    @ fml
++      ldr     r1,=CPDO_dvf_M
++      str     r1,[r0,#4*4]    @ dvf
++      str     r1,[r0,#10*4]   @ fdv
++      ldr     r1,=CPDO_rdf_M
++      str     r1,[r0,#5*4]    @ rdf
++      str     r1,[r0,#11*4]   @ frd
++      mov     r0,#1
++      mov     pc,r14
++      
++/*---------------------------------------------------------------------------*/
++
++      @ The fetch of the next instruction to emulate could fault
++
++      .section .fixup,"ax"
++      .align
++__f1:
++      mov     pc,r9
++      .previous
++      .section __ex_table,"a"
++      .align 3
++      .long   __x1,__f1
++      .previous
++      
++/*---------------------------------------------------------------------------*/
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/module.c
+@@ -0,0 +1,64 @@
++/*
++    Fast Floating Point Emulator
++    (c) Peter Teichmann <mail@peter-teichmann.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., 675 Mass Ave, Cambridge, MA 02139, USA.
++*/
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/init.h>
++
++#ifndef MODULE
++#define kern_fp_enter fp_enter
++
++extern char fpe_type[];
++#endif
++
++static void (*orig_fp_enter)(void);   /* old kern_fp_enter value */
++extern void (*kern_fp_enter)(void);   /* current FP handler */
++extern void fastfpe_enter(void);      /* forward declarations */
++extern int fastfpe_test(void);                /* long multiply available ? */
++
++static int __init fpe_init(void)
++{
++  if (fpe_type[0] && strcmp(fpe_type, "fastfpe"))
++    return 0;
++
++  printk("Fast Floating Point Emulator V0.94");
++  if (fastfpe_test() == 1) printk("M");
++  printk(" by Peter Teichmann.\n");
++
++  /* Save pointer to the old FP handler and then patch ourselves in */
++  orig_fp_enter = kern_fp_enter;
++  kern_fp_enter = fastfpe_enter;
++
++  return 0;
++}
++
++static void __exit fpe_exit(void)
++{
++  /* Restore the values we saved earlier. */
++  kern_fp_enter = orig_fp_enter;
++}
++
++module_init(fpe_init);
++module_exit(fpe_exit);
++
++MODULE_AUTHOR("Peter Teichmann <mail@peter-teichmann.de>");
++MODULE_DESCRIPTION("Fast floating point emulator with full precision");
+--- /dev/null
++++ linux-2.4.27/arch/arm/fastfpe/round.S
+@@ -0,0 +1,912 @@
++
++/*
++Rounds fp register r1-r4, additional mantissa bits in r5 and stores result
++at address r0. Returns to fastfpe_next.
++*/
++
++/*------------------------------------------------------------------------*/
++
++      .data
++      .globl  round_table
++round_table:
++      .word   round_single_ne
++      .word   round_single_p
++      .word   round_single_m
++      .word   round_single_z
++      .word   round_double_ne
++      .word   round_double_p
++      .word   round_double_m
++      .word   round_double_z
++      .word   round_extended_ne
++      .word   round_extended_p
++      .word   round_extended_m
++      .word   round_extended_z
++      .word   round_undef
++      .word   round_undef
++      .word   round_undef
++      .word   round_undef
++
++/*------------------------------------------------------------------------*/
++
++      .text
++round_single_ne:
++      cmp     r4,#127
++      bgt     round_single_nz_ne_overflow
++      cmp     r4,#-126-23-1
++      blt     round_single_z_ne_underflow
++      cmp     r4,#-126
++      blt     round_single_ne_denormalized
++
++      adds    r6,r2,#0x80             // add 0x80.00000000.00000000 to
++      bcs     round_single_add_ov     // mantissa and additional bits
++      
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,#0xff        // test for inexact
++
++      ldrne   r7,[r10,#128]
++      orrne   r7,r7,#16       // set inexact flag
++      strne   r7,[r10,#128]
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r6,#0xff
++      biceq   r6,r6,#0x100    // the even thingy
++
++      mov     r3,#0           // remove bits not existing in single
++      bic     r2,r6,#0xff     // remove bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_ne_denormalized:
++      add     r7,r4,#150
++      mov     r6,#0xffffffff
++      mov     r6,r6,lsr r7
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,r6
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8     // set inexact, underflow flag
++      strne   r8,[r10,#128]
++
++      mov     r8,#0x80000000
++      mov     r8,r8,lsr r7
++      adds    r2,r2,r8
++      bcs     round_single_ne_denormalized_ov
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,r6
++      biceq   r2,r2,r8,lsl #1 // the even thingy
++
++      mov     r3,#0
++      bic     r2,r2,r6        // removing bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_ne_denormalized_ov:
++      cmp     r4,#-150
++      cmpeq   r3,#0
++      cmpeq   r2,#0
++      beq     round_single_z_ne_underflow     // 1.0*2^-150 to zero!
++      add     r4,r4,#1
++      cmp     r4,#-126        // left denormalized range ?
++      cmpge   r2,#0x80        // yes -> overflow also without denormalisation ?
++      ldrge   r5,[r10,#128]
++      bicge   r5,r5,#8        // yes -> clear underflow flag
++      strge   r5,[r10,#128]
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_single_p:
++      teq     r1,#0
++      beq     round_single_nz
++      b       round_single_z
++
++/*------------------------------------------------------------------------*/
++
++round_single_m:
++      teq     r1,#0
++      beq     round_single_z
++      b       round_single_nz
++
++/*------------------------------------------------------------------------*/
++
++round_single_z:
++      cmp     r4,#127
++      bgt     round_single_z_overflow
++      cmp     r4,#-126-23
++      blt     round_single_z_ne_underflow
++      cmp     r4,#-126
++      blt     round_single_z_denormalized
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,#0xff        // testing for inexact
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16       // set inexact flag
++      strne   r5,[r10,#128]
++
++      mov     r3,#0
++      bic     r2,r2,#0xff     // removing bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_z_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_single_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0xffffff00
++      mov     r3,#0
++      mov     r4,#127         // biggest non-infinity single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_infnan:
++      orrs    r5,r3,r2,lsl#1          // is it Inf? ignore MSB
++      beq     round_single_infnan_store
++      tst     r2,#0x40000000          // is it a SNaN?
++      beq     round_single_infnan_create_qnan
++      mov     r3,#0                   // these bits can not be stored
++      bic     r2,r2,#0xff             // in single precision
++round_single_infnan_store:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_infnan_create_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0xffffff00
++      bic     r2,r2,#0x80000000       // r2 = 0x7fffff00
++      mov     r3,#0
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1                // set invalid operation flag
++      str     r5,[r10,#128]
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_z_ne_underflow:
++      cmp     r4,#0x80000000
++      beq     round_single_z_zero
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, underflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000  // was by ERROR -127
++round_single_z_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_z_denormalized:
++      mov     r6,#0xffffffff
++      add     r7,r4,#150
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,r6,lsr r7    // testing for tinyness
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, undeflow flag
++      strne   r5,[r10,#128]
++
++      mov     r3,#0
++      bic     r2,r2,r6,lsr r7 // removing bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++      
++/*------------------------------------------------------------------------*/
++
++round_single_nz:
++      cmp     r4,#127
++      bgt     round_single_nz_ne_overflow
++      cmp     r4,#-126-23
++      blt     round_single_nz_underflow
++      cmp     r4,#-126
++      blt     round_single_nz_denormalized
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,#0xffffffff       // add 0xff.ffffffff.ffffffff to
++      adcs    r2,r2,#0xff             // mantissa and additional bits
++      bcs     round_single_add_ov
++      
++      cmp     r5,#0xffffffff
++      cmpeq   r3,#0xffffffff
++      andeq   r5,r2,#0xff
++      cmpeq   r5,#0xff        // test for inexact
++
++      bic     r2,r2,#0xff     // remove bits not existing in single
++
++round_single_add_ov_back:
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16       // set inexact flag
++      strne   r5,[r10,#128]
++
++      mov     r3,#0           // remove bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_add_ov:
++      add     r4,r4,#1
++      cmp     r4,#127
++      bgt     round_single_nz_ne_overflow
++      movs    r2,#0x80000000  // so that inexact flag gets set !!!
++      b       round_single_add_ov_back
++
++round_single_nz_ne_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_single_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000  // set MSB
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_nz_underflow:
++      cmp     r4,#0x80000000
++      beq     round_single_nz_zero
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, underflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000
++      mov     r3,#0
++      mov     r4,#-149        // smallest non-zero single
++round_single_nz_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_nz_denormalized:
++      mov     r6,#0xffffffff
++      add     r7,r4,#150
++      mov     r6,r6,lsr r7
++
++      teq     r5,#0
++      teqeq   r3,#0
++      tsteq   r2,r6
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8     // set inexact, underflow flag
++      strne   r8,[r10,#128]
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,#0xffffffff
++      adcs    r2,r2,r6
++      bcs     round_single_nz_denormalized_ov
++
++      mov     r3,#0
++      bic     r2,r2,r6        // removing bits not existing in single
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_single_nz_denormalized_ov:
++      add     r4,r4,#1
++      cmp     r4,#-126        // left denormalized range ?
++      cmpge   r2,#0x100       // yes -> overflow also without denormalisation ?
++      ldrge   r5,[r10,#128]
++      bicge   r5,r5,#8        // yes -> clear underflow flag
++      strge   r5,[r10,#128]
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++      
++/*------------------------------------------------------------------------*/
++
++round_double_ne:
++      mov     r7,#0xffffffff          // to generate e.g. 0x7ff
++
++      cmp     r4,#1024
++      bge     round_double_nz_ne_overflow
++      add     r6,r4,#1024
++      cmp     r6,#-1022+1024
++      blt     round_double_ne_denormalized
++
++      teq     r5,#0
++      tsteq   r3,r7,lsr#32-11         // testing for inexact
++      ldrne   r6,[r10,#128]
++      orrne   r6,r6,#16               // set inexact flag
++      strne   r6,[r10,#128]
++
++      adds    r3,r3,#0x400            // add 0x0.00000400.00000000 to
++      adcs    r2,r2,#0                // mantissa and additional bits
++      bcs     round_double_add_ov
++
++      teq     r5,#0
++      tsteq   r3,r7,lsr#32-11
++      biceq   r3,r3,#0x800            // the even thingy
++      
++      bic     r3,r3,r7,lsr#32-11      // remove bits not existing in double
++
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_ne_denormalized:
++      cmp     r6,#-1022-52-1+1024
++      blt     round_double_z_ne_underflow
++
++      adds    r6,r6,#1022+53-32-1024
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6                   // testing for tinyness
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8             // set inexact, undeflow flag
++      strne   r8,[r10,#128]
++
++      bics    r8,r6,r6,lsr#1          // generate ...0001000...
++      movne   r11,#0                  // from     ...0001111...
++      biceq   r11,r7,r7,lsr#1         // 64bit
++      
++      adds    r3,r3,r11
++      adcs    r2,r2,r8
++      bcs     round_double_ne_denormalized_ov
++
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6
++      bne     round_double_ne_denormalized_noeventhingy
++      adds    r11,r11,r11
++      adc     r8,r8,r8
++      bic     r3,r3,r11
++      bic     r2,r2,r8                // the even thingy
++
++round_double_ne_denormalized_noeventhingy:
++      bic     r3,r3,r7                // removing bits not existing in
++      bic     r2,r2,r6                // denormalized double
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_ne_denormalized_ov:
++      add     r6,r4,#1024
++      cmp     r6,#-1023-52+1024
++      cmpeq   r3,#0
++      cmpeq   r2,#0
++      beq     round_single_z_ne_underflow     // 1.0*2^(-1023-52) to zero!
++      add     r4,r4,#1
++      cmp     r6,#-1022-1+1024        // left denormalized range ?
++      cmpge   r3,#0x400               // yes -> overflow also without denormalisation ?
++      ldrge   r5,[r10,#128]
++      bicge   r5,r5,#8                // yes -> clear underflow flag
++      strge   r5,[r10,#128]
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_double_p:
++      teq     r1,#0
++      beq     round_double_nz
++      b       round_double_z
++
++/*------------------------------------------------------------------------*/
++
++round_double_m:
++      teq     r1,#0
++      beq     round_double_z
++      b       round_double_nz
++
++/*------------------------------------------------------------------------*/
++
++round_double_z:
++      mov     r7,#0xffffffff
++
++      cmp     r4,#1024
++      bge     round_double_z_overflow
++      add     r6,r4,#1024
++      cmp     r6,#-1022+1024
++      blt     round_double_z_denormalized
++
++      teq     r5,#0
++      tsteq   r3,r7,lsr#32-11         // testing for inexact
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16               // set inexact flag
++      strne   r5,[r10,#128]
++
++      bic     r3,r3,r7,lsr#32-11      // removing bits not existing in double
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_z_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_double_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0xffffffff
++      mov     r3,r2,lsl#11    // 0xfffff800
++      mov     r4,#1024
++      sub     r4,r4,#1        // 1023; biggest non-infinity double
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_infnan:
++      orrs    r5,r3,r2,lsl#1          // is it Inf? ignore MSB
++      beq     round_double_infnan_store
++      tst     r2,#0x40000000          // is it a SNaN?
++      beq     round_double_infnan_create_qnan
++      bic     r3,r3,r7,lsr#32-11      // clear bits not in double
++round_double_infnan_store:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_infnan_create_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,r2,lsl#11            // 0xfffff800
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1                // set invalid operation flag
++      str     r5,[r10,#128]
++      b       round_double_infnan_store
++
++round_double_z_ne_underflow:
++      cmp     r4,#0x80000000
++      beq     round_double_z_zero
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#16+8             // set inexact, underflow flag
++      str     r5,[r10,#128]
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++round_double_z_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_z_denormalized:
++      cmp     r6,#-1022-52+1024
++      blt     round_double_z_ne_underflow
++
++      adds    r6,r6,#1022+53-32-1024
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6           // testing for tinyness
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, undeflow flag
++      strne   r5,[r10,#128]
++
++      bic     r3,r3,r7        // rmoving bits not existing in
++      bic     r2,r2,r6        // denormalized double
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++      
++/*------------------------------------------------------------------------*/
++
++round_double_nz:
++      mov     r7,#0xffffffff          // to generate e.g. 0x7ff
++
++      cmp     r4,#1024
++      bge     round_double_nz_ne_overflow
++      add     r6,r4,#1024
++      cmp     r6,#-1022+1024
++      blt     round_double_nz_denormalized
++
++      teq     r5,#0
++      tsteq   r3,r7,lsr#32-11 // testing for inexact
++      ldrne   r6,[r10,#128]
++      orrne   r6,r6,#16       // set inexact flag
++      strne   r6,[r10,#128]
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,r7,lsr#32-11      // add 0x0.000007ff.ffffffff to
++      adcs    r2,r2,#0                // mantissa and additional bits
++      bcs     round_double_add_ov
++      
++      bic     r3,r3,r7,lsr#32-11      // remove bits not existing in double
++
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_add_ov:
++      add     r4,r4,#1
++      cmp     r4,#1024
++      bge     round_double_nz_ne_overflow
++
++//    ldrne   r6,[r10,#128]
++//    orrne   r6,r6,#16       // set inexact flag
++//    strne   r6,[r10,#128]
++      mov     r2,#0x80000000
++      mov     r3,#0
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_nz_ne_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_double_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000  // set MSB
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_nz_underflow:
++      cmp     r4,#0x80000000
++      beq     round_double_nz_zero
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, underflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000
++      mov     r3,#0
++      mov     r4,#-1074+1024
++      sub     r4,r4,#1024     // smallest non-zero double
++round_double_nz_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_nz_denormalized:
++      cmp     r6,#-1022-52+1024
++      blt     round_double_nz_underflow
++
++      adds    r6,r6,#1022+53-32-1024
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6           // testing for tinyness
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8     // set inexact, undeflow flag
++      strne   r8,[r10,#128]
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,r7
++      adcs    r2,r2,r6
++      bcs     round_double_nz_denormalized_ov
++
++      bic     r3,r3,r7        // rmoving bits not existing in
++      bic     r2,r2,r6        // denormalized double
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_double_nz_denormalized_ov:
++      add     r4,r4,#1
++      add     r6,r4,#1024
++      cmp     r6,#-1022+1024  // left denormalized range ?
++      cmpge   r3,#0x800       // yes -> overflow also without denormalisation ?
++      ldrge   r5,[r10,#128]
++      bicge   r5,r5,#8        // yes -> clear underflow flag
++      strge   r5,[r10,#128]
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_extended_ne:
++      mov     r7,#0xffffffff          // to generate e.g. 0x7ff
++
++      cmp     r4,#16384
++      bge     round_extended_nz_ne_overflow
++      add     r6,r4,#16384
++      cmp     r6,#-16382+16384
++      blt     round_extended_ne_denormalized
++
++      teq     r5,#0                   // testing for inexact
++      ldrne   r6,[r10,#128]
++      orrne   r6,r6,#16               // set inexact flag
++      strne   r6,[r10,#128]
++
++      adds    r5,r5,#0x80000000       // add 0x0.00000400.00000000 to
++      adcs    r3,r3,#0                // mantissa and additional bits
++      adcs    r2,r2,#0
++      bcs     round_extended_add_ov
++
++      teq     r5,#0
++      biceq   r3,r3,#1                // the even thingy
++      
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_ne_denormalized:
++      cmp     r6,#-16382-63-1+16384
++      blt     round_extended_z_ne_underflow
++
++      adds    r6,r6,#16382+64-32-16384
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6                   // testing for tinyness
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8             // set inexact, undeflow flag
++      strne   r8,[r10,#128]
++
++      bics    r8,r6,r6,lsr#1          // generate ...0001000...
++      movne   r11,#0                  // from     ...0001111...
++      biceq   r11,r7,r7,lsr#1         // 64bit
++      
++      adds    r3,r3,r11
++      adcs    r2,r2,r8
++      bcs     round_extended_ne_denormalized_ov
++
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6
++      bne     round_extended_ne_denormalized_noeventhingy
++      adds    r11,r11,r11
++      adc     r8,r8,r8
++      bic     r3,r3,r11
++      bic     r2,r2,r8                // the even thingy
++
++round_extended_ne_denormalized_noeventhingy:
++      bic     r3,r3,r7                // removing bits not existing in
++      bic     r2,r2,r6                // denormalized extended
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_ne_denormalized_ov:
++      add     r6,r4,#16384
++      cmp     r6,#-16383-63+16384
++      cmpeq   r5,#0
++      cmpeq   r3,#0
++      cmpeq   r2,#0
++      beq     round_single_z_ne_underflow     // 1.0*2^(-16383-63) to zero!
++      add     r4,r4,#1
++      cmp     r6,#-16382-1+16384      // left denormalized range ?
++      blt     round_extended_ne_still_denormalized
++      cmp     r5,#0x80000000          // FIXME yes -> overflow also without denormalisation ?
++      ldrcs   r5,[r10,#128]
++      biccs   r5,r5,#8                // yes -> clear underflow flag
++      strcs   r5,[r10,#128]
++round_extended_ne_still_denormalized:
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_extended_p:
++      teq     r1,#0
++      beq     round_extended_nz
++      b       round_extended_z
++
++/*------------------------------------------------------------------------*/
++
++round_extended_m:
++      teq     r1,#0
++      beq     round_extended_z
++      b       round_extended_nz
++
++/*------------------------------------------------------------------------*/
++
++round_extended_z:
++      mov     r7,#0xffffffff
++
++      cmp     r4,#16384
++      bge     round_extended_z_overflow
++      add     r6,r4,#16384
++      cmp     r6,#-16382+16384
++      blt     round_extended_z_denormalized
++
++      teq     r5,#0                   // testing for inexact
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16               // set inexact flag
++      strne   r5,[r10,#128]
++
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_z_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_extended_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0xffffffff
++      mov     r3,#0xffffffff
++      mov     r4,#16384
++      sub     r4,r4,#1        // 16383; biggest non-infinity extended
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_infnan:
++      orrs    r5,r3,r2,lsl#1          // is it Inf? ignore MSB
++      beq     round_extended_infnan_store
++      tst     r2,#0x40000000          // is it a SNaN?
++      beq     round_extended_infnan_create_qnan
++      bic     r3,r3,r7,lsr#32-11      // clear bits not in extended
++round_extended_infnan_store:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_infnan_create_qnan:
++      mov     r1,#0x80000000
++      mov     r2,#0x7fffffff
++      mov     r3,#0xffffffff
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#1                // set invalid operation flag
++      str     r5,[r10,#128]
++      b       round_extended_infnan_store
++
++round_extended_z_ne_underflow:
++      cmp     r4,#0x80000000
++      beq     round_extended_z_zero
++      ldr     r5,[r10,#128]
++      orr     r5,r5,#16+8             // set inexact, underflow flag
++      str     r5,[r10,#128]
++      mov     r2,#0
++      mov     r3,#0
++      mov     r4,#0x80000000
++round_extended_z_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_z_denormalized:
++      cmp     r6,#-16382-63+16384
++      blt     round_extended_z_ne_underflow
++
++      adds    r6,r6,#16382+64-32-16384
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6           // testing for tinyness
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, undeflow flag
++      strne   r5,[r10,#128]
++
++      bic     r3,r3,r7        // removing bits not existing in
++      bic     r2,r2,r6        // denormalized extended
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++      
++/*------------------------------------------------------------------------*/
++
++round_extended_nz:
++      mov     r7,#0xffffffff          // to generate e.g. 0x7ff
++
++      cmp     r4,#16384
++      bge     round_extended_nz_ne_overflow
++      add     r6,r4,#16384
++      cmp     r6,#-16382+16384
++      blt     round_extended_nz_denormalized
++
++      teq     r5,#0           // testing for inexact
++      ldrne   r6,[r10,#128]
++      orrne   r6,r6,#16       // set inexact flag
++      strne   r6,[r10,#128]
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,#0                // add 0x0.0.ffffffff to
++      adcs    r2,r2,#0                // mantissa and additional bits
++      bcs     round_extended_add_ov
++      
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_add_ov:
++      add     r4,r4,#1
++      cmp     r4,#16384
++      bge     round_extended_nz_ne_overflow
++
++//    ldrne   r6,[r10,#128]
++//    orrne   r6,r6,#16       // set inexact flag
++//    strne   r6,[r10,#128]
++      mov     r2,#0x80000000
++      mov     r3,#0
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_nz_ne_overflow:
++      cmp     r4,#0x7fffffff
++      beq     round_extended_infnan
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+4     // set inexact,overflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000  // set MSB
++      mov     r3,#0
++      mov     r4,#0x7fffffff
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_nz_underflow:
++      cmp     r4,#0x80000000
++      beq     round_extended_nz_zero
++
++      ldrne   r5,[r10,#128]
++      orrne   r5,r5,#16+8     // set inexact, underflow flag
++      strne   r5,[r10,#128]
++      mov     r2,#0x80000000
++      mov     r3,#0
++      mov     r4,#-16445+16384
++      sub     r4,r4,#16384    // smallest non-zero extended
++round_extended_nz_zero:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_nz_denormalized:
++      cmp     r6,#-16382-63+16384
++      blt     round_extended_nz_underflow
++
++      adds    r6,r6,#16382+64-32-16384
++
++      addmi   r6,r6,#32
++      movmi   r6,r7,lsr r6
++
++      movpl   r7,r7,lsr r6
++      movpl   r6,#0
++      
++      teq     r5,#0
++      tsteq   r3,r7
++      tsteq   r2,r6           // testing for tinyness
++      ldrne   r8,[r10,#128]
++      orrne   r8,r8,#16+8     // set inexact, undeflow flag
++      strne   r8,[r10,#128]
++
++      adds    r5,r5,#0xffffffff
++      adcs    r3,r3,r7
++      adcs    r2,r2,r6
++      bcs     round_extended_nz_denormalized_ov
++
++      bic     r3,r3,r7        // removing bits not existing in
++      bic     r2,r2,r6        // denormalized extended
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++round_extended_nz_denormalized_ov:
++      add     r4,r4,#1
++      add     r6,r4,#16384
++      cmp     r6,#-16382+16384        // left denormalized range ?
++      cmpge   r3,#1                   // yes -> overflow also without denormalisation ?
++      ldrge   r5,[r10,#128]
++      bicge   r5,r5,#8                // yes -> clear underflow flag
++      strge   r5,[r10,#128]
++      mov     r3,#0
++      mov     r2,#0x80000000
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
++
++round_undef:
++      stmia   r0,{r1-r4}
++      b       fastfpe_next
++
++/*------------------------------------------------------------------------*/
+--- linux-2.4.27/arch/arm/kernel/calls.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/calls.S
+@@ -115,7 +115,7 @@
+               .long   SYMBOL_NAME(sys_ni_syscall)             /* was sys_profil */
+               .long   SYMBOL_NAME(sys_statfs)
+ /* 100 */     .long   SYMBOL_NAME(sys_fstatfs)
+-              .long   SYMBOL_NAME(sys_ni_syscall)
++              .long   SYMBOL_NAME(sys_ni_syscall)             /* 101 was sys_ioperm */
+               .long   SYMBOL_NAME(sys_socketcall)
+               .long   SYMBOL_NAME(sys_syslog)
+               .long   SYMBOL_NAME(sys_setitimer)
+@@ -126,7 +126,7 @@
+               .long   SYMBOL_NAME(sys_ni_syscall)             /* was sys_uname */
+ /* 110 */     .long   SYMBOL_NAME(sys_ni_syscall)             /* was sys_iopl */
+               .long   SYMBOL_NAME(sys_vhangup)
+-              .long   SYMBOL_NAME(sys_ni_syscall)
++              .long   SYMBOL_NAME(sys_ni_syscall)             /* 112 was sys_idle */
+               .long   SYMBOL_NAME(sys_syscall)                /* call a syscall */
+               .long   SYMBOL_NAME(sys_wait4)
+ /* 115 */     .long   SYMBOL_NAME(sys_swapoff)
+@@ -137,7 +137,7 @@
+ /* 120 */     .long   SYMBOL_NAME(sys_clone_wapper)
+               .long   SYMBOL_NAME(sys_setdomainname)
+               .long   SYMBOL_NAME(sys_newuname)
+-              .long   SYMBOL_NAME(sys_ni_syscall)
++              .long   SYMBOL_NAME(sys_ni_syscall)             /* 123 was sys_modify_ldt */
+               .long   SYMBOL_NAME(sys_adjtimex)
+ /* 125 */     .long   SYMBOL_NAME(sys_mprotect)
+               .long   SYMBOL_NAME(sys_sigprocmask)
+@@ -180,7 +180,7 @@
+               .long   SYMBOL_NAME(sys_arm_mremap)
+               .long   SYMBOL_NAME(sys_setresuid16)
+ /* 165 */     .long   SYMBOL_NAME(sys_getresuid16)
+-              .long   SYMBOL_NAME(sys_ni_syscall)
++              .long   SYMBOL_NAME(sys_ni_syscall)             /* 166 was sys_vm86 */
+               .long   SYMBOL_NAME(sys_query_module)
+               .long   SYMBOL_NAME(sys_poll)
+               .long   SYMBOL_NAME(sys_nfsservctl)
+--- linux-2.4.27/arch/arm/kernel/dma-rpc.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/dma-rpc.c
+@@ -26,19 +26,6 @@
+ #include <asm/mach/dma.h>
+ #include <asm/hardware/iomd.h>
+-#if 0
+-typedef enum {
+-      dma_size_8      = 1,
+-      dma_size_16     = 2,
+-      dma_size_32     = 4,
+-      dma_size_128    = 16
+-} dma_size_t;
+-
+-typedef struct {
+-      dma_size_t      transfersize;
+-} dma_t;
+-#endif
+-
+ #define TRANSFER_SIZE 2
+ #define CURA  (0)
+@@ -48,10 +35,6 @@
+ #define CR    (IOMD_IO0CR - IOMD_IO0CURA)
+ #define ST    (IOMD_IO0ST - IOMD_IO0CURA)
+-#define state_prog_a  0
+-#define state_wait_a  1
+-#define state_wait_b  2
+-
+ static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma)
+ {
+       unsigned long end, offset, flags = 0;
+@@ -65,7 +48,7 @@
+               if (end > PAGE_SIZE)
+                       end = PAGE_SIZE;
+-              if (offset + (int) TRANSFER_SIZE > end)
++              if (offset + TRANSFER_SIZE >= end)
+                       flags |= DMA_END_L;
+               sg->length = end - TRANSFER_SIZE;
+@@ -103,27 +86,31 @@
+               if (!(status & DMA_ST_INT))
+                       return;
+-              if (status & DMA_ST_OFL && !dma->sg)
+-                      break;
+-
+-              iomd_get_next_sg(&dma->cur_sg, dma);
++              if ((dma->state ^ status) & DMA_ST_AB)
++                      iomd_get_next_sg(&dma->cur_sg, dma);
+               switch (status & (DMA_ST_OFL | DMA_ST_AB)) {
+               case DMA_ST_OFL:                        /* OIA */
+               case DMA_ST_AB:                         /* .IB */
+                       iomd_writel(dma->cur_sg.dma_address, base + CURA);
+                       iomd_writel(dma->cur_sg.length, base + ENDA);
++                      dma->state = DMA_ST_AB;
+                       break;
+               case DMA_ST_OFL | DMA_ST_AB:            /* OIB */
+               case 0:                                 /* .IA */
+                       iomd_writel(dma->cur_sg.dma_address, base + CURB);
+                       iomd_writel(dma->cur_sg.length, base + ENDB);
++                      dma->state = 0;
+                       break;
+               }
++
++              if (status & DMA_ST_OFL &&
++                  dma->cur_sg.length == (DMA_END_S|DMA_END_L))
++                      break;
+       } while (1);
+-      iomd_writeb(0, base + CR);
++      dma->state = ~DMA_ST_AB;
+       disable_irq(irq);
+ }
+@@ -158,6 +145,7 @@
+               }
+               iomd_writeb(DMA_CR_C, dma_base + CR);
++              dma->state = DMA_ST_AB;
+       }
+               
+       if (dma->dma_mode == DMA_MODE_READ)
+@@ -171,13 +159,11 @@
+ {
+       unsigned long dma_base = dma->dma_base;
+       unsigned long flags;
+-      unsigned int ctrl;
+       local_irq_save(flags);
+-      ctrl = iomd_readb(dma_base + CR);
+-      if (ctrl & DMA_CR_E)
++      if (dma->state != ~DMA_ST_AB)
+               disable_irq(dma->dma_irq);
+-      iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR);
++      iomd_writeb(0, dma_base + CR);
+       local_irq_restore(flags);
+ }
+--- linux-2.4.27/arch/arm/kernel/entry-armv.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/entry-armv.S
+@@ -677,12 +677,11 @@
+               mrs     r9, cpsr                        @ Enable interrupts if they were
+               tst     r3, #I_BIT
+               biceq   r9, r9, #I_BIT                  @ previously
+-              mov     r0, r2                          @ *** remove once everyones in sync
+ /*
+  * This routine must not corrupt r9
+  */
+ #ifdef MULTI_CPU
+-              ldr     r4, .LCprocfns                  @ pass r0, r3 to
++              ldr     r4, .LCprocfns                  @ pass r2, r3 to
+               mov     lr, pc                          @ processor code
+               ldr     pc, [r4]                        @ call processor specific code
+ #else
+@@ -788,9 +787,8 @@
+               stmdb   r5, {sp, lr}^
+               alignment_trap r7, r7, __temp_abt
+               zero_fp
+-              mov     r0, r2                          @ remove once everyones in sync
+ #ifdef MULTI_CPU
+-              ldr     r4, .LCprocfns                  @ pass r0, r3 to
++              ldr     r4, .LCprocfns                  @ pass r2, r3 to
+               mov     lr, pc                          @ processor code
+               ldr     pc, [r4]                        @ call processor specific code
+ #else
+@@ -840,7 +838,8 @@
+               adrsvc  al, r9, ret_from_exception      @ r9  = normal FP return
+               adrsvc  al, lr, fpundefinstr            @ lr  = undefined instr return
+-call_fpe:     get_current_task r10
++call_fpe:     enable_irq r10
++              get_current_task r10
+               mov     r8, #1
+               strb    r8, [r10, #TSK_USED_MATH]       @ set current->used_math
+               ldr     r4, .LCfp
+--- linux-2.4.27/arch/arm/kernel/fiq.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/fiq.c
+@@ -122,23 +122,23 @@
+       register unsigned long tmp, tmp2;
+       __asm__ volatile (
+ #ifdef CONFIG_CPU_26
+-      "mov    %0, pc
+-      bic     %1, %0, #0x3
+-      orr     %1, %1, %3
+-      teqp    %1, #0          @ select FIQ mode
+-      mov     r0, r0
+-      ldmia   %2, {r8 - r14}
+-      teqp    %0, #0          @ return to SVC mode
+-      mov     r0, r0"
++      "mov    %0, pc                                  \n"
++      "bic    %1, %0, #0x3                            \n"
++      "orr    %1, %1, %3                              \n"
++      "teqp   %1, #0          @ select FIQ mode       \n"
++      "mov    r0, r0                                  \n"
++      "ldmia  %2, {r8 - r14}                          \n"
++      "teqp   %0, #0          @ return to SVC mode    \n"
++      "mov    r0, r0                                  \n"
+ #endif
+ #ifdef CONFIG_CPU_32
+-      "mrs    %0, cpsr
+-      mov     %1, %3
+-      msr     cpsr_c, %1      @ select FIQ mode
+-      mov     r0, r0
+-      ldmia   %2, {r8 - r14}
+-      msr     cpsr_c, %0      @ return to SVC mode
+-      mov     r0, r0"
++      "mrs    %0, cpsr                                \n"
++      "mov    %1, %3                                  \n"
++      "msr    cpsr_c, %1      @ select FIQ mode       \n"
++      "mov    r0, r0                                  \n"
++      "ldmia  %2, {r8 - r14}                          \n"
++      "msr    cpsr_c, %0      @ return to SVC mode    \n"
++      "mov    r0, r0                                  \n"
+ #endif
+       : "=&r" (tmp), "=&r" (tmp2)
+       : "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
+@@ -154,23 +154,23 @@
+       register unsigned long tmp, tmp2;
+       __asm__ volatile (
+ #ifdef CONFIG_CPU_26
+-      "mov    %0, pc
+-      bic     %1, %0, #0x3
+-      orr     %1, %1, %3
+-      teqp    %1, #0          @ select FIQ mode
+-      mov     r0, r0
+-      stmia   %2, {r8 - r14}
+-      teqp    %0, #0          @ return to SVC mode
+-      mov     r0, r0"
++      "mov    %0, pc                               \n"
++      "bic    %1, %0, #0x3                         \n"
++      "orr    %1, %1, %3                           \n"
++      "teqp   %1, #0          @ select FIQ mode    \n"
++      "mov    r0, r0                               \n"
++      "stmia  %2, {r8 - r14}                       \n"
++      "teqp   %0, #0          @ return to SVC mode \n"
++      "mov    r0, r0                               \n"
+ #endif
+ #ifdef CONFIG_CPU_32
+-      "mrs    %0, cpsr
+-      mov     %1, %3
+-      msr     cpsr_c, %1      @ select FIQ mode
+-      mov     r0, r0
+-      stmia   %2, {r8 - r14}
+-      msr     cpsr_c, %0      @ return to SVC mode
+-      mov     r0, r0"
++      "mrs    %0, cpsr                             \n"
++      "mov    %1, %3                               \n"
++      "msr    cpsr_c, %1      @ select FIQ mode    \n"
++      "mov    r0, r0                               \n"
++      "stmia  %2, {r8 - r14}                       \n"
++      "msr    cpsr_c, %0      @ return to SVC mode \n"
++      "mov    r0, r0                               \n"
+ #endif
+       : "=&r" (tmp), "=&r" (tmp2)
+       : "r" (&regs->ARM_r8), "I" (I_BIT | F_BIT | FIQ_MODE)
+--- linux-2.4.27/arch/arm/kernel/head-armv.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/head-armv.S
+@@ -1,7 +1,7 @@
+ /*
+  *  linux/arch/arm/kernel/head-armv.S
+  *
+- *  Copyright (C) 1994-1999 Russell King
++ *  Copyright (C) 1994-2003 Russell King
+  *
+  * 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
+@@ -163,10 +163,10 @@
+  */
+               .type   __ret, %function
+ __ret:                ldr     lr, __switch_data
+-              mcr     p15, 0, r0, c1, c0
+-              mrc     p15, 0, r0, c1, c0, 0           @ read it back.
+-              mov     r0, r0
+-              mov     r0, r0
++              mcr     p15, 0, r0, c1, c0, 0
++              mrc     p15, 0, r3, c0, c0, 0
++              mov     r3, r3
++              mov     r3, r3
+               mov     pc, lr
+ /*
+@@ -214,6 +214,11 @@
+  */
+ __create_page_tables:
+               pgtbl   r4, r5                          @ page table address
++#if defined(CONFIG_CPU_DCACHE_DISABLE)
++              bic     r8, r8, #0x00c                  @ clear B, C
++#elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH)
++              bic     r8, r8, #0x004                  @ clear B
++#endif
+               /*
+                * Clear the 16K level 1 swapper page table
+--- linux-2.4.27/arch/arm/kernel/irq.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/irq.c
+@@ -549,7 +549,7 @@
+               kfree(action);
+               goto out;
+       }
+-      printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
++      printk(KERN_ERR "Trying to free IRQ%d\n",irq);
+ #ifdef CONFIG_DEBUG_ERRORS
+       __backtrace();
+ #endif
+--- linux-2.4.27/arch/arm/kernel/ptrace.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/ptrace.c
+@@ -725,11 +725,8 @@
+               goto out_tsk;
+       }
+       ret = -ESRCH;
+-      if (!(child->ptrace & PT_PTRACED))
+-              goto out_tsk;
+-      if (child->state != TASK_STOPPED && request != PTRACE_KILL)
+-              goto out_tsk;
+-      if (child->p_pptr != current)
++      ret = ptrace_check_attach(child, request == PTRACE_KILL);
++      if (ret)
+               goto out_tsk;
+       ret = do_ptrace(request, child, addr, data);
+--- linux-2.4.27/arch/arm/kernel/semaphore.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/semaphore.c
+@@ -193,7 +193,7 @@
+       bl      __down_interruptible            \n\
+       mov     ip, r0                          \n\
+       ldmfd   sp!, {r0 - r3, pc}^             \n\
+-
++                                              \n\
+       .align  5                               \n\
+       .globl  __down_trylock_failed           \n\
+ __down_trylock_failed:                                \n\
+--- linux-2.4.27/arch/arm/kernel/signal.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/kernel/signal.c
+@@ -641,10 +641,7 @@
+                               /* FALLTHRU */
+                       default:
+-                              sigaddset(&current->pending.signal, signr);
+-                              recalc_sigpending(current);
+-                              current->flags |= PF_SIGNALED;
+-                              do_exit(exit_code);
++                              sig_exit(signr, exit_code, &info);
+                               /* NOTREACHED */
+                       }
+               }
+--- linux-2.4.27/arch/arm/lib/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/lib/Makefile
+@@ -15,7 +15,7 @@
+                  strnlen_user.o strchr.o strrchr.o testchangebit.o  \
+                  testclearbit.o testsetbit.o uaccess.o getuser.o    \
+                  putuser.o ashldi3.o ashrdi3.o lshrdi3.o muldi3.o   \
+-                 ucmpdi2.o udivdi3.o lib1funcs.o
++                 ucmpdi2.o udivdi3.o lib1funcs.o div64.o
+ obj-m         :=
+ obj-n         :=
+--- /dev/null
++++ linux-2.4.27/arch/arm/lib/div64.S
+@@ -0,0 +1,59 @@
++#include <linux/linkage.h>
++
++#ifndef __ARMEB__
++ql    .req    r0                      @ quotient low
++qh    .req    r1                      @ quotient high
++onl   .req    r0                      @ original dividend low
++onh   .req    r1                      @ original dividend high
++nl    .req    r4                      @ dividend low
++nh    .req    r5                      @ dividend high
++res   .req    r4                      @ result
++#else
++ql    .req    r1
++qh    .req    r0
++onl   .req    r1
++onh   .req    r0
++nl    .req    r5
++nh    .req    r4
++res   .req    r5
++#endif
++
++dl    .req    r3                      @ divisor low
++dh    .req    r2                      @ divsor high
++
++
++ENTRY(do_div64)
++      stmfd   sp!, {r4, r5, lr}
++      mov     nl, onl
++      movs    nh, onh                 @ if high bits are zero
++      movne   lr, #33
++      moveq   lr, #1                  @ only divide low bits
++      moveq   nh, onl
++
++      tst     dh, #0x80000000
++      bne     2f
++1:    cmp     nh, dh
++      bls     2f
++      add     lr, lr, #1
++      movs    dh, dh, lsl #1          @ left justify disor
++      bpl     1b
++
++2:    movs    nh, onh
++      moveq   dl, dh
++      moveq   dh, #0
++      movne   dl, #0
++      mov     ql, #0
++      mov     qh, #0
++3:    subs    ip, nl, dl              @ trial subtraction
++      sbcs    ip, nh, dh
++      movcs   nh, ip                  @ only update if successful
++      subcs   nl, nl, dl              @ (repeat the subtraction)
++      adcs    ql, ql, ql              @ C=1 if successful, shift into
++      adc     qh, qh, qh              @ quotient
++      movs    dh, dh, lsr #1          @ shift base high part right
++      mov     dl, dl, rrx             @ shift base low part right
++      subs    lr, lr, #1
++      bne     3b
++
++      mov     r2, res
++      ldmfd   sp!, {r4, r5, pc}
+--- linux-2.4.27/arch/arm/lib/putuser.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/lib/putuser.S
+@@ -30,11 +30,11 @@
+       .global __put_user_1
+ __put_user_1:
+-      bic     r2, sp, #0x1f00
+-      bic     r2, r2, #0x00ff
+-      ldr     r2, [r2, #TSK_ADDR_LIMIT]
+-      sub     r2, r2, #1
+-      cmp     r0, r2
++      bic     ip, sp, #0x1f00
++      bic     ip, ip, #0x00ff
++      ldr     ip, [ip, #TSK_ADDR_LIMIT]
++      sub     ip, ip, #1
++      cmp     r0, ip
+ 1:    strlsbt r1, [r0]
+       movls   r0, #0
+       movls   pc, lr
+@@ -42,11 +42,11 @@
+       .global __put_user_2
+ __put_user_2:
+-      bic     r2, sp, #0x1f00
+-      bic     r2, r2, #0x00ff
+-      ldr     r2, [r2, #TSK_ADDR_LIMIT]
+-      sub     r2, r2, #2
+-      cmp     r0, r2
++      bic     ip, sp, #0x1f00
++      bic     ip, ip, #0x00ff
++      ldr     ip, [ip, #TSK_ADDR_LIMIT]
++      sub     ip, ip, #2
++      cmp     r0, ip
+ 2:    strlsbt r1, [r0], #1
+       movls   r1, r1, lsr #8
+ 3:    strlsbt r1, [r0]
+@@ -56,11 +56,11 @@
+       .global __put_user_4
+ __put_user_4:
+-      bic     r2, sp, #0x1f00
+-      bic     r2, r2, #0x00ff
+-      ldr     r2, [r2, #TSK_ADDR_LIMIT]
+-      sub     r2, r2, #4
+-      cmp     r0, r2
++      bic     ip, sp, #0x1f00
++      bic     ip, ip, #0x00ff
++      ldr     ip, [ip, #TSK_ADDR_LIMIT]
++      sub     ip, ip, #4
++      cmp     r0, ip
+ 4:    strlst  r1, [r0]
+       movls   r0, #0
+       movls   pc, lr
+--- linux-2.4.27/arch/arm/mach-at91rm9200/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mach-at91rm9200/Makefile
+@@ -18,4 +18,8 @@
+ export-objs           :=
++# LEDs support
++leds-$(CONFIG_ARCH_AT91RM9200DK) += dk-leds.o
++obj-$(CONFIG_LEDS) += $(leds-y)
++
+ include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-at91rm9200/dk-leds.c
+@@ -0,0 +1,97 @@
++/*
++ * LED driver for the Atmel AT91RM9200 Development Kit.
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++*/
++
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <asm/mach-types.h>
++#include <asm/leds.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pio.h>
++
++
++static inline void at91_led_on(void)
++{
++      AT91_SYS->PIOB_CODR = AT91C_PIO_PB2;
++}
++
++static inline void at91_led_off(void)
++{
++      AT91_SYS->PIOB_SODR = AT91C_PIO_PB2;
++}
++
++static inline void at91_led_toggle(void)
++{
++      unsigned long curr = AT91_SYS->PIOB_ODSR;
++      if (curr & AT91C_PIO_PB2)
++              AT91_SYS->PIOB_CODR = AT91C_PIO_PB2;
++      else
++              AT91_SYS->PIOB_SODR = AT91C_PIO_PB2;
++}
++
++
++/*
++ * Handle LED events.
++ */
++static void at91rm9200dk_leds_event(led_event_t evt)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++      
++      switch(evt) {
++      case led_start:         /* System startup */
++              at91_led_on();
++              break;
++
++      case led_stop:          /* System stop / suspend */
++              at91_led_off();
++              break;
++
++#ifdef CONFIG_LEDS_TIMER
++      case led_timer:         /* Every 50 timer ticks */
++              at91_led_toggle();
++              break;
++#endif
++
++#ifdef CONFIG_LEDS_CPU
++      case led_idle_start:    /* Entering idle state */
++              at91_led_off();
++              break;
++
++      case led_idle_end:      /* Exit idle state */
++              at91_led_on();
++              break;
++#endif
++
++      default:
++              break;
++      }
++      
++      local_irq_restore(flags);
++}
++
++
++static int __init leds_init(void)
++{
++      /* Enable PIO to access the LEDs */
++      AT91_SYS->PIOB_PER = AT91C_PIO_PB2;
++      AT91_SYS->PIOB_OER = AT91C_PIO_PB2;
++
++      leds_event = at91rm9200dk_leds_event;
++
++      leds_event(led_start);
++      return 0;
++}
++
++__initcall(leds_init);
+--- linux-2.4.27/arch/arm/mach-integrator/pci_v3.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mach-integrator/pci_v3.c
+@@ -21,7 +21,6 @@
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
+ #include <linux/config.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/pci.h>
+ #include <linux/ptrace.h>
+@@ -32,6 +31,7 @@
+ #include <linux/init.h>
+ #include <asm/hardware.h>
++#include <asm/io.h>
+ #include <asm/irq.h>
+ #include <asm/system.h>
+ #include <asm/mach/pci.h>
+@@ -447,15 +447,16 @@
+ #define SC_LBFADDR (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x20)
+ #define SC_LBFCODE (IO_ADDRESS(INTEGRATOR_SC_BASE) + 0x24)
+-static int v3_fault(unsigned long addr, struct pt_regs *regs)
++static int
++v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       unsigned long pc = instruction_pointer(regs);
+       unsigned long instr = *(unsigned long *)pc;
+ #if 0
+       char buf[128];
+-      sprintf(buf, "V3 fault: address=0x%08lx, pc=0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
+-              addr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
++      sprintf(buf, "V3 fault: addr 0x%08lx, FSR 0x%03x, PC 0x%08lx [%08lx] LBFADDR=%08x LBFCODE=%02x ISTAT=%02x\n",
++              addr, fsr, pc, instr, __raw_readl(SC_LBFADDR), __raw_readl(SC_LBFCODE) & 255,
+               v3_readb(V3_LB_ISTAT));
+       printk(KERN_DEBUG "%s", buf);
+       printascii(buf);
+@@ -523,8 +524,6 @@
+ #endif
+ }
+-extern int (*external_fault)(unsigned long addr, struct pt_regs *regs);
+-
+ /*
+  * V3_LB_BASE? - local bus address
+  * V3_LB_MAP?  - pci bus address
+@@ -539,7 +538,10 @@
+       /*
+        * Hook in our fault handler for PCI errors
+        */
+-      external_fault = v3_fault;
++      hook_fault_code(4, v3_pci_fault, SIGBUS, "external abort on linefetch");
++      hook_fault_code(6, v3_pci_fault, SIGBUS, "external abort on linefetch");
++      hook_fault_code(8, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
++      hook_fault_code(10, v3_pci_fault, SIGBUS, "external abort on non-linefetch");
+       spin_lock_irqsave(&v3_lock, flags);
+@@ -629,7 +631,7 @@
+ #if 0
+       ret = request_irq(IRQ_LBUSTIMEOUT, lb_timeout, 0, "bus timeout", NULL);
+       if (ret)
+-              printk(KERN_ERR "PCI: unable to grab local bus timeout ".
++              printk(KERN_ERR "PCI: unable to grab local bus timeout "
+                      "interrupt: %d\n", ret);
+ #endif
+ }
+--- linux-2.4.27/arch/arm/mach-sa1100/pm.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mach-sa1100/pm.c
+@@ -21,6 +21,8 @@
+  *
+  * 2002-05-27:        Nicolas Pitre   Killed sleep.h and the kmalloced save array.
+  *                            Storage is local on the stack now.
++ * 2003-06-25:  Jeff Corrall <jcorrall@mac.com>
++ *                    Saved the GPIO levels for resume after sleep.
+  */
+ #include <linux/config.h>
+ #include <linux/init.h>
+@@ -70,13 +72,20 @@
+ int pm_do_suspend(void)
+ {
+       unsigned long sleep_save[SLEEP_SAVE_SIZE];
++      unsigned long sleep_save_gpsr;
++      unsigned long sleep_save_gpcr;
++      unsigned long delta;
+       cli();
+       leds_event(led_stop);
+       /* preserve current time */
+-      RCNR = xtime.tv_sec;
++      delta = xtime.tv_sec - RCNR;
++
++      /* save the current state of the GPIO output pins */
++      sleep_save_gpsr = GPDR & GPLR;
++      sleep_save_gpcr = GPDR & ~GPLR;
+       /* save vital registers */
+       SAVE(OSCR);
+@@ -121,6 +130,10 @@
+       printk(KERN_DEBUG "*** made it back from resume\n");
+ #endif
++      /* restore GPIO output state before enabling the pins */
++      GPSR = sleep_save_gpsr;
++      GPCR = sleep_save_gpcr;
++
+       /* restore registers */
+       RESTORE(GPDR);
+       RESTORE(GRER);
+@@ -151,7 +164,7 @@
+       RESTORE(ICMR);
+       /* restore current time */
+-      xtime.tv_sec = RCNR;
++      xtime.tv_sec = RCNR + delta;
+       leds_event(led_start);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/sa1100_usb.h
+@@ -0,0 +1,193 @@
++/*
++ * sa1100_usb.h
++ *
++ * Public interface to the sa1100 USB core. For use by client modules
++ * like usb-eth and usb-char.
++ *
++ */
++
++#ifndef _SA1100_USB_H
++#define _SA1100_USB_H
++#include <asm/byteorder.h>
++
++typedef void (*usb_callback_t)(int flag, int size);
++
++/* in usb_ctl.c (see also descriptor methods at bottom of file) */
++
++// Open the USB client for client and initialize data structures
++// to default values, but _do not_ start UDC.
++int sa1100_usb_open( const char * client_name );
++
++// Start UDC running
++int sa1100_usb_start( void );
++
++// Immediately stop udc, fire off completion routines w/-EINTR
++int sa1100_usb_stop( void ) ;
++
++// Disconnect client from usb core
++int sa1100_usb_close( void ) ;
++
++// set notify callback for when core reaches configured state
++// return previous pointer (if any)
++typedef void (*usb_notify_t)(void);
++usb_notify_t sa1100_set_configured_callback( usb_notify_t callback );
++
++/* in usb_send.c */
++int sa1100_usb_xmitter_avail( void );
++int sa1100_usb_send(char *buf, int len, usb_callback_t callback);
++void sa1100_usb_send_reset(void);
++
++/* in usb_recev.c */
++int sa1100_usb_recv(char *buf, int len, usb_callback_t callback);
++void sa1100_usb_recv_reset(void);
++
++//////////////////////////////////////////////////////////////////////////////
++// Descriptor Management
++//////////////////////////////////////////////////////////////////////////////
++
++#define DescriptorHeader \
++      __u8 bLength;        \
++      __u8 bDescriptorType
++
++
++// --- Device Descriptor -------------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 bcdUSB;                  /* USB specification revision number in BCD */
++       __u8  bDeviceClass;    /* USB class for entire device */
++       __u8  bDeviceSubClass; /* USB subclass information for entire device */
++       __u8  bDeviceProtocol; /* USB protocol information for entire device */
++       __u8  bMaxPacketSize0; /* Max packet size for endpoint zero */
++       __u16 idVendor;        /* USB vendor ID */
++       __u16 idProduct;       /* USB product ID */
++       __u16 bcdDevice;       /* vendor assigned device release number */
++       __u8  iManufacturer;   /* index of manufacturer string */
++       __u8  iProduct;        /* index of string that describes product */
++       __u8  iSerialNumber;   /* index of string containing device serial number */
++       __u8  bNumConfigurations; /* number fo configurations */
++} __attribute__ ((packed)) device_desc_t;
++
++// --- Configuration Descriptor ------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 wTotalLength;        /* total # of bytes returned in the cfg buf 4 this cfg */
++       __u8  bNumInterfaces;      /* number of interfaces in this cfg */
++       __u8  bConfigurationValue; /* used to uniquely ID this cfg */
++       __u8  iConfiguration;      /* index of string describing configuration */
++       __u8  bmAttributes;        /* bitmap of attributes for ths cfg */
++       __u8  MaxPower;                    /* power draw in 2ma units */
++} __attribute__ ((packed)) config_desc_t;
++
++// bmAttributes:
++enum { USB_CONFIG_REMOTEWAKE=0x20, USB_CONFIG_SELFPOWERED=0x40,
++         USB_CONFIG_BUSPOWERED=0x80 };
++// MaxPower:
++#define USB_POWER( x)  ((x)>>1) /* convert mA to descriptor units of A for MaxPower */
++
++// --- Interface Descriptor ---------------
++
++typedef struct {
++       DescriptorHeader;
++       __u8  bInterfaceNumber;   /* Index uniquely identfying this interface */
++       __u8  bAlternateSetting;  /* ids an alternate setting for this interface */
++       __u8  bNumEndpoints;      /* number of endpoints in this interface */
++       __u8  bInterfaceClass;    /* USB class info applying to this interface */
++       __u8  bInterfaceSubClass; /* USB subclass info applying to this interface */
++       __u8  bInterfaceProtocol; /* USB protocol info applying to this interface */
++       __u8  iInterface;         /* index of string describing interface */
++} __attribute__ ((packed)) intf_desc_t;
++
++// --- Endpoint  Descriptor ---------------
++
++typedef struct {
++       DescriptorHeader;
++       __u8  bEndpointAddress;  /* 0..3 ep num, bit 7: 0 = 0ut 1= in */
++       __u8  bmAttributes;      /* 0..1 = 0: ctrl, 1: isoc, 2: bulk 3: intr */
++       __u16 wMaxPacketSize;    /* data payload size for this ep in this cfg */
++       __u8  bInterval;         /* polling interval for this ep in this cfg */
++} __attribute__ ((packed)) ep_desc_t;
++
++// bEndpointAddress:
++enum { USB_OUT= 0, USB_IN=1 };
++#define USB_EP_ADDRESS(a,d) (((a)&0xf) | ((d) << 7))
++// bmAttributes:
++enum { USB_EP_CNTRL=0, USB_EP_BULK=2, USB_EP_INT=3 };
++
++// --- String Descriptor -------------------
++
++typedef struct {
++       DescriptorHeader;
++       __u16 bString[1];                /* unicode string .. actaully 'n' __u16s */
++} __attribute__ ((packed)) string_desc_t;
++
++/*=======================================================
++ * Handy helpers when working with above
++ *
++ */
++// these are x86-style 16 bit "words" ...
++#define make_word_c( w ) __constant_cpu_to_le16(w)
++#define make_word( w )   __cpu_to_le16(w)
++
++// descriptor types
++enum { USB_DESC_DEVICE=1, USB_DESC_CONFIG=2, USB_DESC_STRING=3,
++         USB_DESC_INTERFACE=4, USB_DESC_ENDPOINT=5 };
++
++
++/*=======================================================
++ * Default descriptor layout for SA-1100 and SA-1110 UDC
++ */
++
++/* "config descriptor buffer" - that is, one config,
++   ..one interface and 2 endpoints */
++struct cdb {
++       config_desc_t cfg;
++       intf_desc_t   intf;
++       ep_desc_t     ep1;
++       ep_desc_t     ep2;
++} __attribute__ ((packed));
++
++
++/* all SA device descriptors */
++typedef struct {
++       device_desc_t dev;   /* device descriptor */
++       struct cdb b;        /* bundle of descriptors for this cfg */
++} __attribute__ ((packed)) desc_t;
++
++
++/*=======================================================
++ * Descriptor API
++ */
++
++/* Get the address of the statically allocated desc_t structure
++   in the usb core driver. Clients can modify this between
++   the time they call sa1100_usb_open() and sa1100_usb_start()
++*/
++desc_t *
++sa1100_usb_get_descriptor_ptr( void );
++
++
++/* Set a pointer to the string descriptor at "index". The driver
++ ..has room for 8 string indicies internally. Index zero holds
++ ..a LANGID code and is set to US English by default. Inidices
++ ..1-7 are available for use in the config descriptors as client's
++ ..see fit. This pointer is assumed to be good as long as the
++ ..SA usb core is open (so statically allocate them). Returnes -EINVAL
++ ..if index out of range */
++int sa1100_usb_set_string_descriptor( int index, string_desc_t * p );
++
++/* reverse of above */
++string_desc_t *
++sa1100_usb_get_string_descriptor( int index );
++
++/* kmalloc() a string descriptor and convert "p" to unicode in it */
++string_desc_t *
++sa1100_usb_kmalloc_string_descriptor( const char * p );
++
++
++
++
++
++
++#endif /* _SA1100_USB_H */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/sa1111-ohci.c
+@@ -0,0 +1,140 @@
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/pci.h>
++#include <linux/mm.h>
++
++#ifdef CONFIG_USB_OHCI
++
++/*
++ * The SA-1111 errata says that the DMA hardware needs to be exercised
++ * before the clocks are turned on to work properly.  This code does
++ * a tiny dma transfer to prime to hardware.
++ *
++ *  What DMA errata?  I've checked October 1999 and February 2001, both
++ *  of which do not mention such a bug, let alone any details of this
++ *  work-around.
++ */
++static void __init sa1111_dma_setup(void)
++{
++      dma_addr_t dma_buf;
++      void * vbuf;
++
++      /* DMA init & setup */
++
++      /* WARNING: The SA-1111 L3 function is used as part of this
++       * SA-1111 DMA errata workaround.
++       *
++       * N.B., When the L3 function is enabled, it uses GPIO_B<4:5>
++       * and takes precedence over the PS/2 mouse and GPIO_B
++       * functions. Refer to "Intel StrongARM SA-1111 Microprocessor
++       * Companion Chip, Sect 10.2" for details.  So this "fix" may
++       * "break" support of either PS/2 mouse or GPIO_B if
++       * precautions are not taken to avoid collisions in
++       * configuration and use of these pins. AFAIK, no precautions
++       * are taken at this time. So it is likely that the action
++       * taken here may cause problems in PS/2 mouse and/or GPIO_B
++       * pin use elsewhere.
++       *
++       * But wait, there's more... What we're doing here is
++       * obviously altogether a bad idea. We're indiscrimanately bit
++       * flipping config for a few different functions here which
++       * are "owned" by other drivers. This needs to be handled
++       * better than it is being done here at this time.  */
++
++      /* prime the dma engine with a tiny dma */
++      SKPCR |= SKPCR_I2SCLKEN;
++      SKAUD |= SKPCR_L3CLKEN | SKPCR_SCLKEN;
++
++      SACR0 |= 0x00003305;
++      SACR1 = 0x00000000;
++
++      /*
++       * We need memory below 1MB.
++       *  NOTE: consistent_alloc gives you some random virtual
++       *  address as its return value, and the DMA address via
++       *  the dma_addr_t pointer.
++       */
++      vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf);
++
++      SADTSA = (unsigned long)dma_buf;
++      SADTCA = 4;
++
++      SADTCS |= 0x00000011;
++      SKPCR |= SKPCR_DCLKEN;
++
++      /* wait */
++      udelay(100);
++
++      /* clear reserved but, then disable SAC */
++      SACR0 &= ~(0x00000002);
++      SACR0 &= ~(0x00000001);
++
++      /* toggle bit clock direction */
++      SACR0 |= 0x00000004;
++      SACR0 &= ~(0x00000004);
++
++      SKAUD &= ~(SKPCR_L3CLKEN | SKPCR_SCLKEN);
++
++      SKPCR &= ~SKPCR_I2SCLKEN;
++
++      consistent_free(vbuf, 4, dma_buf);
++}
++
++/*
++ * reset the SA-1111 usb controller and turn on it's clocks
++ */
++int __init sa1111_ohci_hcd_init(void)
++{
++      unsigned int usb_reset = 0;
++
++      if (machine_is_xp860() ||
++          machine_has_neponset() ||
++          machine_is_pfs168() ||
++          machine_is_badge4())
++              usb_reset = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
++
++      /*
++       * turn on USB clocks
++       */
++      SKPCR |= SKPCR_UCLKEN;
++      udelay(100);
++
++      /*
++       * Force USB reset
++       */
++      USB_RESET = USB_RESET_FORCEIFRESET;
++      USB_RESET |= USB_RESET_FORCEHCRESET;
++      udelay(100);
++
++      /*
++       * Take out of reset
++       */
++      USB_RESET = 0;
++
++      /*
++       * set power sense and control lines (this from the diags code)
++       */
++        USB_RESET = usb_reset;
++
++        /*
++         * Huh?  This is a _read only_ register --rmk
++         */
++      USB_STATUS = 0;
++
++      udelay(10);
++
++      /*
++       * compensate for dma bug
++       */
++      sa1111_dma_setup();
++
++      return 0;
++}
++
++void sa1111_ohci_hcd_cleanup(void)
++{
++      /* turn the USB clock off */
++      SKPCR &= ~SKPCR_UCLKEN;
++}
++
++#endif /* CONFIG_USB_OHCI */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb-char.c
+@@ -0,0 +1,723 @@
++/*
++ * (C) Copyright 2000-2001 Extenex Corporation
++ *
++ *    This program is free software; you can redistribute it and/or modify
++ *    it under the terms of the GNU General Public License as published by
++ *    the Free Software Foundation; either version 2 of the License, or
++ *    (at your option) any later version.
++ *
++ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *  usb-char.c
++ *
++ *  Miscellaneous character device interface for SA1100 USB function
++ *    driver.
++ *
++ *  Background:
++ *  The SA1100 function driver ported from the Compaq Itsy project
++ *  has an interface, usb-eth.c, to feed network packets over the
++ *  usb wire and into the Linux TCP/IP stack.
++ *
++ *  This file replaces that one with a simple character device
++ *  interface that allows unstructured "byte pipe" style reads and
++ *  writes over the USB bulk endpoints by userspace programs.
++ *
++ *  A new define, CONFIG_SA1100_USB_NETLINK, has been created that,
++ *  when set, (the default) causes the ethernet interface to be used.
++ *  When not set, this more pedestrian character interface is linked
++ *  in instead.
++ *
++ *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
++ *
++ *  ward.willats@extenex.com
++ *
++ *  To do:
++ *  - Can't dma into ring buffer directly with pci_map/unmap usb_recv
++ *    uses and get bytes out at the same time DMA is going on. Investigate:
++ *    a) changing usb_recv to use alloc_consistent() at client request; or
++ *    b) non-ring-buffer based data structures. In the meantime, I am using
++ *    a bounce buffer. Simple, but wasteful.
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/cache.h>
++#include <linux/poll.h>
++#include <linux/circ_buf.h>
++#include <linux/timer.h>
++
++#include <asm/io.h>
++#include <asm/semaphore.h>
++#include <asm/proc/page.h>
++#include <asm/mach-types.h>
++
++#include "usb-char.h"
++#include "sa1100_usb.h"
++
++
++
++//////////////////////////////////////////////////////////////////////////////
++// Driver  Options
++//////////////////////////////////////////////////////////////////////////////
++
++#define VERSION       "0.4"
++
++
++#define VERBOSITY 1
++
++#if VERBOSITY
++# define      PRINTK(x, a...) printk (x, ## a)
++#else
++# define      PRINTK(x, a...) /**/
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Globals - Macros - Enums - Structures
++//////////////////////////////////////////////////////////////////////////////
++#ifndef MIN
++#define MIN( a, b ) ((a)<(b)?(a):(b))
++#endif
++
++typedef int bool; enum { false = 0, true = 1 };
++
++static const char pszMe[] = "usbchr: ";
++
++static wait_queue_head_t wq_read;
++static wait_queue_head_t wq_write;
++static wait_queue_head_t wq_poll;
++
++/* Serialze multiple writers onto the transmit hardware
++.. since we sleep the writer during transmit to stay in
++.. sync. (Multiple writers don't make much sense, but..) */
++static DECLARE_MUTEX( xmit_sem );
++
++// size of usb DATA0/1 packets. 64 is standard maximum
++// for bulk transport, though most hosts seem to be able
++// to handle larger.
++#define TX_PACKET_SIZE 64
++#define RX_PACKET_SIZE 64
++#define RBUF_SIZE  (4*PAGE_SIZE)
++
++static struct wcirc_buf {
++  char *buf;
++  int in;
++  int out;
++} rx_ring = { NULL, 0, 0 };
++
++static struct {
++       unsigned long  cnt_rx_complete;
++       unsigned long  cnt_rx_errors;
++       unsigned long  bytes_rx;
++       unsigned long  cnt_tx_timeouts;
++       unsigned long  cnt_tx_errors;
++       unsigned long  bytes_tx;
++} charstats;
++
++
++static char * tx_buf = NULL;
++static char * packet_buffer = NULL;
++static int sending = 0;
++static int usb_ref_count = 0;
++static int last_tx_result = 0;
++static int last_rx_result = 0;
++static int last_tx_size = 0;
++static struct timer_list tx_timer;
++
++//////////////////////////////////////////////////////////////////////////////
++// Prototypes
++//////////////////////////////////////////////////////////////////////////////
++static char *         what_the_f( int e );
++static void   free_txrx_buffers( void );
++static void     twiddle_descriptors( void );
++static void     free_string_descriptors( void ) ;
++static int      usbc_open( struct inode *pInode, struct file *pFile );
++static void     rx_done_callback_packet_buffer( int flag, int size );
++
++static void     tx_timeout( unsigned long );
++static void     tx_done_callback( int flag, int size );
++
++static ssize_t  usbc_read( struct file *, char *, size_t, loff_t * );
++static ssize_t  usbc_write( struct file *, const char *, size_t, loff_t * );
++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait );
++static int usbc_ioctl( struct inode *pInode, struct file *pFile,
++                       unsigned int nCmd, unsigned long argument );
++static int      usbc_close( struct inode *pInode, struct file *pFile );
++
++#ifdef CONFIG_SA1100_EXTENEX1
++static void     extenex_configured_notify_proc( void );
++#endif
++//////////////////////////////////////////////////////////////////////////////
++// Private Helpers
++//////////////////////////////////////////////////////////////////////////////
++
++static char * what_the_f( int e )
++{
++       char * p;
++       switch( e ) {
++       case 0:
++                p = "noErr";
++                break;
++       case -ENODEV:
++                p = "ENODEV - usb not in config state";
++                break;
++       case -EBUSY:
++                p = "EBUSY - another request on the hardware";
++                break;
++       case -EAGAIN:
++                p = "EAGAIN";
++                break;
++       case -EINTR:
++                p = "EINTR - interrupted\n";
++                break;
++       case -EPIPE:
++                p = "EPIPE - zero length xfer\n";
++                break;
++       default:
++                p = "????";
++                break;
++       }
++       return p;
++}
++
++static void free_txrx_buffers( void )
++{
++       if ( rx_ring.buf != NULL ) {
++                kfree( rx_ring.buf );
++                rx_ring.buf = NULL;
++       }
++       if ( packet_buffer != NULL ) {
++                kfree( packet_buffer );
++                packet_buffer = NULL;
++       }
++       if ( tx_buf != NULL ) {
++                kfree( tx_buf );
++                tx_buf = NULL;
++       }
++}
++
++/* twiddle_descriptors()
++ * It is between open() and start(). Setup descriptors.
++ */
++static void twiddle_descriptors( void )
++{
++       desc_t * pDesc = sa1100_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;
++       pDesc->b.ep2.wMaxPacketSize = make_word_c( TX_PACKET_SIZE );
++       pDesc->b.ep2.bmAttributes   = USB_EP_BULK;
++
++       if ( machine_is_extenex1() ) {
++#ifdef CONFIG_SA1100_EXTENEX1
++                pDesc->dev.idVendor = make_word_c( 0xC9F );
++                pDesc->dev.idProduct = 1;
++                pDesc->dev.bcdDevice = make_word_c( 0x0001 );
++                pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED;
++                pDesc->b.cfg.MaxPower = 0;
++
++                pString = sa1100_usb_kmalloc_string_descriptor( "Extenex" );
++                if ( pString ) {
++                         sa1100_usb_set_string_descriptor( 1, pString );
++                         pDesc->dev.iManufacturer = 1;
++                }
++
++                pString = sa1100_usb_kmalloc_string_descriptor( "Handheld Theater" );
++                if ( pString ) {
++                         sa1100_usb_set_string_descriptor( 2, pString );
++                         pDesc->dev.iProduct = 2;
++                }
++
++                pString = sa1100_usb_kmalloc_string_descriptor( "00000000" );
++                if ( pString ) {
++                         sa1100_usb_set_string_descriptor( 3, pString );
++                         pDesc->dev.iSerialNumber = 3;
++                }
++
++                pString = sa1100_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" );
++                if ( pString ) {
++                         sa1100_usb_set_string_descriptor( 4, pString );
++                         pDesc->b.intf.iInterface = 4;
++                }
++                sa1100_set_configured_callback( extenex_configured_notify_proc );
++#endif
++       }
++}
++
++static void free_string_descriptors( void )
++{
++       if ( machine_is_extenex1() ) {
++                string_desc_t * pString;
++                int i;
++                for( i = 1 ; i <= 4 ; i++ ) {
++                         pString = sa1100_usb_get_string_descriptor( i );
++                         if ( pString )
++                                      kfree( pString );
++                }
++       }
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// ASYNCHRONOUS
++//////////////////////////////////////////////////////////////////////////////
++static  void kick_start_rx( void )
++{
++       if ( usb_ref_count ) {
++                int total_space  = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                if ( total_space >= RX_PACKET_SIZE ) {
++                         sa1100_usb_recv( packet_buffer,
++                                                              RX_PACKET_SIZE,
++                                                              rx_done_callback_packet_buffer
++                                                    );
++                }
++       }
++}
++/*
++ * rx_done_callback_packet_buffer()
++ * We have completed a DMA xfer into the temp packet buffer.
++ * Move to ring.
++ *
++ * flag values:
++ * on init,  -EAGAIN
++ * on reset, -EINTR
++ * on RPE, -EIO
++ * on short packet -EPIPE
++ */
++static void
++rx_done_callback_packet_buffer( int flag, int size )
++{
++       charstats.cnt_rx_complete++;
++
++       if ( flag == 0 || flag == -EPIPE ) {
++                size_t n;
++
++                charstats.bytes_rx += size;
++
++                n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                n = MIN( n, size );
++                size -= n;
++
++                memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n );
++                rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1);
++                memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size );
++                rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1);
++
++                wake_up_interruptible( &wq_read );
++                wake_up_interruptible( &wq_poll );
++
++                last_rx_result = 0;
++
++                kick_start_rx();
++
++       } else if ( flag != -EAGAIN ) {
++                charstats.cnt_rx_errors++;
++                last_rx_result = flag;
++                wake_up_interruptible( &wq_read );
++                wake_up_interruptible( &wq_poll );
++       }
++       else  /* init, start a read */
++                kick_start_rx();
++}
++
++
++static void tx_timeout( unsigned long unused )
++{
++      printk( "%stx timeout\n", pszMe );
++      sa1100_usb_send_reset();
++      charstats.cnt_tx_timeouts++;
++}
++
++
++// on init, -EAGAIN
++// on reset, -EINTR
++// on TPE, -EIO
++static void tx_done_callback( int flags, int size )
++{
++       if ( flags == 0 )
++                charstats.bytes_tx += size;
++       else
++                charstats.cnt_tx_errors++;
++       last_tx_size = size;
++       last_tx_result = flags;
++       sending = 0;
++       wake_up_interruptible( &wq_write );
++       wake_up_interruptible( &wq_poll );
++}
++
++
++//////////////////////////////////////////////////////////////////////////////
++// Workers
++//////////////////////////////////////////////////////////////////////////////
++
++static int usbc_open( struct inode *pInode, struct file *pFile )
++{
++       int retval = 0;
++
++       PRINTK( KERN_DEBUG "%sopen()\n", pszMe );
++
++       /* start usb core */
++       retval = sa1100_usb_open( "usb-char" );
++       if ( retval ) return retval;
++
++       /* allocate memory */
++       if ( usb_ref_count == 0 ) {
++                tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA );
++                if ( tx_buf == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++                rx_ring.buf =
++                      (char*) kmalloc( RBUF_SIZE, GFP_KERNEL );
++
++                if ( rx_ring.buf == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++
++                packet_buffer =
++                      (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA  );
++
++                if ( packet_buffer == NULL ) {
++                         printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe );
++                         goto malloc_fail;
++                }
++                rx_ring.in = rx_ring.out = 0;
++                memset( &charstats, 0, sizeof( charstats ) );
++                sending = 0;
++                last_tx_result = 0;
++                last_tx_size = 0;
++       }
++
++       /* modify default descriptors */
++       twiddle_descriptors();
++
++       retval = sa1100_usb_start();
++       if ( retval ) {
++                printk( "%sAGHH! Could not USB core\n", pszMe );
++                free_txrx_buffers();
++                return retval;
++       }
++       usb_ref_count++;   /* must do _before_ kick_start() */
++       MOD_INC_USE_COUNT;
++       kick_start_rx();
++       return 0;
++
++ malloc_fail:
++       free_txrx_buffers();
++       return -ENOMEM;
++}
++
++/*
++ * Read endpoint. Note that you can issue a read to an
++ * unconfigured endpoint. Eventually, the host may come along
++ * and configure underneath this module and data will appear.
++ */
++static ssize_t usbc_read( struct file *pFile, char *pUserBuffer,
++                        size_t stCount, loff_t *pPos )
++{
++       ssize_t retval;
++       int flags;
++       DECLARE_WAITQUEUE( wait, current );
++
++       PRINTK( KERN_DEBUG "%sread()\n", pszMe );
++
++       local_irq_save( flags );
++       if ( last_rx_result == 0 ) {
++                local_irq_restore( flags );
++       } else {  /* an error happended and receiver is paused */
++                local_irq_restore( flags );
++                last_rx_result = 0;
++                kick_start_rx();
++       }
++
++       add_wait_queue( &wq_read, &wait );
++       while( 1 ) {
++                ssize_t bytes_avail;
++                ssize_t bytes_to_end;
++
++                set_current_state( TASK_INTERRUPTIBLE );
++
++                /* snap ring buf state */
++                local_irq_save( flags );
++                bytes_avail  = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
++                local_irq_restore( flags );
++
++                if ( bytes_avail != 0 ) {
++                         ssize_t bytes_to_move = MIN( stCount, bytes_avail );
++                         retval = 0;          // will be bytes transfered
++                         if ( bytes_to_move != 0 ) {
++                                      size_t n = MIN( bytes_to_end, bytes_to_move );
++                                      if ( copy_to_user( pUserBuffer,
++                                                                         &rx_ring.buf[ rx_ring.out ],
++                                                                         n ) ) {
++                                               retval = -EFAULT;
++                                               break;
++                                      }
++                                      bytes_to_move -= n;
++                                      retval += n;
++                                      // might go 1 char off end, so wrap
++                                      rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1);
++                                      if ( copy_to_user( pUserBuffer + n,
++                                                                         &rx_ring.buf[ rx_ring.out ],
++                                                                         bytes_to_move )
++                                               ) {
++                                               retval = -EFAULT;
++                                               break;
++                                      }
++                                      rx_ring.out += bytes_to_move;           // cannot wrap
++                                      retval += bytes_to_move;
++                                      kick_start_rx();
++                         }
++                         break;
++                }
++                else if ( last_rx_result ) {
++                         retval = last_rx_result;
++                         break;
++                }
++                else if ( pFile->f_flags & O_NONBLOCK ) {  // no data, can't sleep
++                         retval = -EAGAIN;
++                         break;
++                }
++                else if ( signal_pending( current ) ) {   // no data, can sleep, but signal
++                         retval = -ERESTARTSYS;
++                         break;
++                }
++                schedule();                                                           // no data, can sleep
++       }
++       set_current_state( TASK_RUNNING );
++       remove_wait_queue( &wq_read, &wait );
++
++       if ( retval < 0 )
++                printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) );
++       return retval;
++}
++
++/*
++ * Write endpoint. This routine attempts to break the passed in buffer
++ * into usb DATA0/1 packet size chunks and send them to the host.
++ * (The lower-level driver tries to do this too, but easier for us
++ * to manage things here.)
++ *
++ * We are at the mercy of the host here, in that it must send an IN
++ * token to us to pull this data back, so hopefully some higher level
++ * protocol is expecting traffic to flow in that direction so the host
++ * is actually polling us. To guard against hangs, a 5 second timeout
++ * is used.
++ *
++ * This routine takes some care to only report bytes sent that have
++ * actually made it across the wire. Thus we try to stay in lockstep
++ * with the completion routine and only have one packet on the xmit
++ * hardware at a time. Multiple simultaneous writers will get
++ * "undefined" results.
++ *
++  */
++static ssize_t  usbc_write( struct file *pFile, const char * pUserBuffer,
++                                                       size_t stCount, loff_t *pPos )
++{
++       ssize_t retval = 0;
++       ssize_t stSent = 0;
++
++       DECLARE_WAITQUEUE( wait, current );
++
++       PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount );
++
++       down( &xmit_sem );  // only one thread onto the hardware at a time
++
++       while( stCount != 0 && retval == 0 ) {
++                int nThisTime  = MIN( TX_PACKET_SIZE, stCount );
++                copy_from_user( tx_buf, pUserBuffer, nThisTime );
++                sending = nThisTime;
++                retval = sa1100_usb_send( tx_buf, nThisTime, tx_done_callback );
++                if ( retval < 0 ) {
++                         char * p = what_the_f( retval );
++                         printk( "%sCould not queue xmission. rc=%d - %s\n",
++                                         pszMe, retval, p );
++                         sending = 0;
++                         break;
++                }
++                /* now have something on the diving board */
++                add_wait_queue( &wq_write, &wait );
++                tx_timer.expires = jiffies + ( HZ * 5 );
++                add_timer( &tx_timer );
++                while( 1 ) {
++                         set_current_state( TASK_INTERRUPTIBLE );
++                         if ( sending == 0 ) {  /* it jumped into the pool */
++                                      del_timer( &tx_timer );
++                                      retval = last_tx_result;
++                                      if ( retval == 0 ) {
++                                               stSent          += last_tx_size;
++                                               pUserBuffer += last_tx_size;
++                                               stCount     -= last_tx_size;
++                                      }
++                                      else
++                                               printk( "%sxmission error rc=%d - %s\n",
++                                                               pszMe, retval, what_the_f(retval) );
++                                      break;
++                         }
++                         else if ( signal_pending( current ) ) {
++                                      del_timer( &tx_timer );
++                                      printk( "%ssignal\n", pszMe  );
++                                      retval = -ERESTARTSYS;
++                                      break;
++                         }
++                         schedule();
++                }
++                set_current_state( TASK_RUNNING );
++                remove_wait_queue( &wq_write, &wait );
++       }
++
++       up( &xmit_sem );
++
++       if ( 0 == retval )
++                retval = stSent;
++       return retval;
++}
++
++static unsigned int usbc_poll( struct file *pFile, poll_table * pWait )
++{
++       unsigned int retval = 0;
++
++       PRINTK( KERN_DEBUG "%poll()\n", pszMe );
++
++       poll_wait( pFile, &wq_poll, pWait );
++
++       if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) )
++                retval |= POLLIN | POLLRDNORM;
++       if ( sa1100_usb_xmitter_avail() )
++                retval |= POLLOUT | POLLWRNORM;
++       return retval;
++}
++
++static int usbc_ioctl( struct inode *pInode, struct file *pFile,
++                       unsigned int nCmd, unsigned long argument )
++{
++       int retval = 0;
++
++       switch( nCmd ) {
++
++       case USBC_IOC_FLUSH_RECEIVER:
++                sa1100_usb_recv_reset();
++                rx_ring.in = rx_ring.out = 0;
++                break;
++
++       case USBC_IOC_FLUSH_TRANSMITTER:
++                sa1100_usb_send_reset();
++                break;
++
++       case USBC_IOC_FLUSH_ALL:
++                sa1100_usb_recv_reset();
++                rx_ring.in = rx_ring.out = 0;
++                sa1100_usb_send_reset();
++                break;
++
++       default:
++                retval = -ENOIOCTLCMD;
++                break;
++
++       }
++       return retval;
++}
++
++
++static int usbc_close( struct inode *pInode, struct file * pFile )
++{
++      PRINTK( KERN_DEBUG "%sclose()\n", pszMe );
++      if ( --usb_ref_count == 0 ) {
++               down( &xmit_sem );
++               sa1100_usb_stop();
++               free_txrx_buffers();
++               free_string_descriptors();
++               del_timer( &tx_timer );
++               sa1100_usb_close();
++               up( &xmit_sem );
++      }
++    MOD_DEC_USE_COUNT;
++    return 0;
++}
++
++#ifdef CONFIG_SA1100_EXTENEX1
++#include "../../../drivers/char/ex_gpio.h"
++void extenex_configured_notify_proc( void )
++{
++       if ( exgpio_play_string( "440,1:698,1" ) == -EAGAIN )
++                printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe );
++}
++#endif
++//////////////////////////////////////////////////////////////////////////////
++// Initialization
++//////////////////////////////////////////////////////////////////////////////
++
++static struct file_operations usbc_fops = {
++              owner:      THIS_MODULE,
++              open:           usbc_open,
++              read:           usbc_read,
++              write:          usbc_write,
++              poll:           usbc_poll,
++              ioctl:          usbc_ioctl,
++              release:        usbc_close,
++};
++
++static struct miscdevice usbc_misc_device = {
++    USBC_MINOR, "usb_char", &usbc_fops
++};
++
++/*
++ * usbc_init()
++ */
++
++int __init usbc_init( void )
++{
++       int rc;
++
++#if !defined( CONFIG_ARCH_SA1100 )
++       return -ENODEV;
++#endif
++
++       if ( (rc = misc_register( &usbc_misc_device )) != 0 ) {
++                printk( KERN_WARNING "%sCould not register device 10, "
++                                "%d. (%d)\n", pszMe, USBC_MINOR, rc );
++                return -EBUSY;
++       }
++
++       // initialize wait queues
++       init_waitqueue_head( &wq_read );
++       init_waitqueue_head( &wq_write );
++       init_waitqueue_head( &wq_poll );
++
++       // initialize tx timeout timer
++       init_timer( &tx_timer );
++       tx_timer.function = tx_timeout;
++
++        printk( KERN_INFO "USB Function Character Driver Interface"
++                                " - %s, (C) 2001, Extenex Corp.\n", VERSION
++                 );
++
++       return rc;
++}
++
++void __exit usbc_exit( void )
++{
++}
++
++EXPORT_NO_SYMBOLS;
++
++module_init(usbc_init);
++module_exit(usbc_exit);
++
++
++
++// end: usb-char.c
++
++
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb-char.h
+@@ -0,0 +1,34 @@
++/*
++ * Copyright (C) 2001 Extenex Corporation
++ *
++ * usb-char.h
++ *
++ * Character device emulation client for SA-1100 client usb core.
++ *
++ *
++ *
++ */
++#ifndef _USB_CHAR_H
++#define _USB_CHAR_H
++
++#define USBC_MAJOR 10      /* miscellaneous character device */
++#define USBC_MINOR 240     /* in the "reserved for local use" range */
++
++#define USBC_MAGIC 0x8E
++
++/* zap everything in receive ring buffer */
++#define USBC_IOC_FLUSH_RECEIVER    _IO( USBC_MAGIC, 0x01 )
++
++/* reset transmitter */
++#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 )
++
++/* do both of above */
++#define USBC_IOC_FLUSH_ALL         _IO( USBC_MAGIC, 0x03 )
++
++
++
++
++
++
++#endif /* _USB_CHAR_H */
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb-eth.c
+@@ -0,0 +1,447 @@
++ /*
++ * Ethernet driver for the SA1100 USB client function
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * This code was loosely inspired by the original initial ethernet test driver
++ * Copyright (c) Compaq Computer Corporation, 1999
++ *
++ * 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.
++ *
++ * This is still work in progress...
++ *
++ * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com
++ * 09/03/2001 - Dropped 'framing' scheme, as it seems to cause a lot of problems with little benefit.
++ *            Now, since we do not know what size of packet we are receiving
++ *            last usb packet in sequence will always be less than max packet
++ *            receive endpoint can accept.
++ *            Now the only way to check correct start of frame is to compare
++ *            MAC address. Also now we are stalling on each receive error.
++ *
++ * 15/03/2001 - Using buffer to get data from UDC. DMA needs to have 8 byte
++ *            aligned buffer, but this breaks IP code (unaligned access).
++ *
++ * 01/04/2001 - stall endpoint operations appeared to be very unstable, so
++ *              they are disabled now.
++ *
++ * 03/06/2001 - Readded "zerocopy" receive path (tunable).
++ *
++ */
++
++// Define DMA_NO_COPY if you want data to arrive directly into the
++// receive network buffers, instead of arriving into bounce buffer
++// and then get copied to network buffer.
++// This does not work correctly right now.
++#undef DMA_NO_COPY
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/timer.h>
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <linux/random.h>
++
++#include "sa1100_usb.h"
++
++
++#define ETHERNET_VENDOR_ID 0x49f
++#define ETHERNET_PRODUCT_ID 0x505A
++#define MAX_PACKET 32768
++#define MIN(a, b) (((a) < (b)) ? (a) : (b))
++
++// Should be global, so that insmod can change these
++int usb_rsize=64;
++int usb_wsize=64;
++
++static struct usbe_info_t {
++  struct net_device *dev;
++  u16 packet_id;
++  struct net_device_stats stats;
++} usbe_info;
++
++static char usb_eth_name[16] = "usbf";
++static struct net_device usb_eth_device;
++static struct sk_buff *cur_tx_skb, *next_tx_skb;
++static struct sk_buff *cur_rx_skb, *next_rx_skb;
++static volatile int terminating;
++#ifndef DMA_NO_COPY
++static char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary
++#endif
++
++static int usb_change_mtu (struct net_device *net, int new_mtu)
++{
++      if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET)
++              return -EINVAL;
++      // no second zero-length packet read wanted after mtu-sized packets
++      if (((new_mtu + sizeof (struct ethhdr)) % usb_rsize) == 0)
++              return -EDOM;
++
++      net->mtu = new_mtu;
++      return 0;
++}
++
++static struct sk_buff *
++usb_new_recv_skb(void)
++{
++      struct sk_buff *skb = alloc_skb( 2 + sizeof (struct ethhdr) + usb_eth_device.mtu,GFP_ATOMIC);
++
++      if (skb) {
++              skb_reserve(skb, 2);
++      }
++      return skb;
++}
++
++static u8 bcast_hwaddr[ETH_ALEN]={0xff,0xff,0xff,0xff,0xff,0xff};
++static void
++usb_recv_callback(int flag, int size)
++{
++      struct sk_buff *skb;
++
++      if (terminating)
++              return;
++
++      skb = cur_rx_skb;
++
++      /* flag validation */
++      if (flag == 0) {
++              if ( skb_tailroom (skb) < size ) { // hey! we are overloaded!!!
++                      usbe_info.stats.rx_over_errors++;
++                      goto error;
++              }
++#ifndef DMA_NO_COPY
++              memcpy(skb->tail,dmabuf,size);
++#endif
++              skb_put(skb, size);
++      } else {
++              if (flag == -EIO) {
++                      usbe_info.stats.rx_errors++;
++              }
++              goto error;
++      }
++
++      /* validate packet length */
++      if (size == usb_rsize ) {
++              /* packet not complete yet */
++              skb = NULL;
++      }
++
++      /*
++       * At this point skb is non null if we have a complete packet.
++       * If so take a fresh skb right away and restart USB receive without
++       * further delays, then process the packet.  Otherwise resume USB
++       * receive on the current skb and exit.
++       */
++
++      if (skb)
++              cur_rx_skb = next_rx_skb;
++#ifndef DMA_NO_COPY
++      sa1100_usb_recv(dmabuf, usb_rsize,
++                      usb_recv_callback);
++#else
++      sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
++                      usb_recv_callback);
++#endif
++      if (!skb)
++              return;
++
++      next_rx_skb = usb_new_recv_skb();
++      if (!next_rx_skb) {
++              /*
++               * We can't aford loosing buffer space...
++               * So we drop the current packet and recycle its skb.
++               */
++              printk("%s: can't allocate new skb\n", __FUNCTION__);
++              usbe_info.stats.rx_dropped++;
++              skb_trim(skb, 0);
++              next_rx_skb = skb;
++              return;
++      }
++      if ( skb->len >= sizeof(struct ethhdr)) {
++              if (memcmp(skb->data,usb_eth_device.dev_addr,ETH_ALEN) && memcmp(skb->data,bcast_hwaddr,ETH_ALEN) ) {
++                      // This frame is not for us. nor it is broadcast
++                      usbe_info.stats.rx_frame_errors++;
++                      kfree_skb(skb);
++                      goto error;
++              }
++      }
++
++      if (skb->len) {
++              int     status;
++// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
++
++              skb->dev = &usb_eth_device;
++              skb->protocol = eth_type_trans (skb, &usb_eth_device);
++              usbe_info.stats.rx_packets++;
++              usbe_info.stats.rx_bytes += skb->len;
++              skb->ip_summed = CHECKSUM_NONE;
++              status = netif_rx (skb);
++              if (status != NET_RX_SUCCESS)
++                      printk("netif_rx failed with code %d\n",status);
++      } else {
++error:
++              /*
++               * Error due to HW addr mismatch, or IO error.
++               * Recycle the current skb and reset USB reception.
++               */
++              skb_trim(cur_rx_skb, 0);
++//            if ( flag == -EINTR || flag == -EAGAIN ) // only if we are coming out of stall
++#ifndef DMA_NO_COPY
++                      sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
++#else
++                      sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback);
++#endif
++      }
++}
++
++
++static void
++usb_send_callback(int flag, int size)
++{
++      struct net_device *dev = usbe_info.dev;
++      struct net_device_stats *stats;
++      struct sk_buff *skb=cur_tx_skb;
++      int ret;
++
++      if (terminating)
++              return;
++
++      stats = &usbe_info.stats;
++      switch (flag) {
++          case 0:
++              stats->tx_packets++;
++              stats->tx_bytes += size;
++              break;
++          case -EIO:
++              stats->tx_errors++;
++              break;
++          default:
++              stats->tx_dropped++;
++              break;
++      }
++
++      cur_tx_skb = next_tx_skb;
++      next_tx_skb = NULL;
++      dev_kfree_skb_irq(skb);
++      if (!cur_tx_skb)
++              return;
++
++      dev->trans_start = jiffies;
++      ret = sa1100_usb_send(cur_tx_skb->data, cur_tx_skb->len, usb_send_callback);
++      if (ret) {
++              /* If the USB core can't accept the packet, we drop it. */
++              dev_kfree_skb_irq(cur_tx_skb);
++              cur_tx_skb = NULL;
++              usbe_info.stats.tx_carrier_errors++;
++      }
++      netif_wake_queue(dev);
++}
++
++static int
++usb_eth_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      int ret;
++      unsigned long flags;
++
++      if (next_tx_skb) {
++              printk("%s: called with next_tx_skb != NULL\n", __FUNCTION__);
++              return 1;
++      }
++
++      if (skb_shared (skb)) {
++              struct sk_buff  *skb2 = skb_unshare(skb, GFP_ATOMIC);
++              if (!skb2) {
++                      usbe_info.stats.tx_dropped++;
++                      dev_kfree_skb(skb);
++                      return 1;
++              }
++              skb = skb2;
++      }
++
++      if ((skb->len % usb_wsize) == 0) {
++              skb->len++; // other side will ignore this one, anyway.
++      }
++
++      local_irq_save(flags);
++      if (cur_tx_skb) {
++              next_tx_skb = skb;
++              netif_stop_queue(dev);
++      } else {
++              cur_tx_skb = skb;
++              dev->trans_start = jiffies;
++              ret = sa1100_usb_send(skb->data, skb->len, usb_send_callback);
++              if (ret) {
++                      /* If the USB core can't accept the packet, we drop it. */
++                      dev_kfree_skb(skb);
++                      cur_tx_skb = NULL;
++                      usbe_info.stats.tx_carrier_errors++;
++              }
++      }
++      local_irq_restore(flags);
++      return 0;
++}
++
++static void
++usb_xmit_timeout(struct net_device *dev )
++{
++      sa1100_usb_send_reset();
++      dev->trans_start = jiffies;
++      netif_wake_queue(dev);
++}
++
++
++static int
++usb_eth_open(struct net_device *dev)
++{
++      terminating = 0;
++      cur_tx_skb = next_tx_skb = NULL;
++      cur_rx_skb = usb_new_recv_skb();
++      next_rx_skb = usb_new_recv_skb();
++      if (!cur_rx_skb || !next_rx_skb) {
++              printk("%s: can't allocate new skb\n", __FUNCTION__);
++              if (cur_rx_skb)
++                      kfree_skb(cur_rx_skb);
++              if (next_rx_skb)
++                      kfree_skb(next_rx_skb);
++              return -ENOMEM;;
++      }
++
++      MOD_INC_USE_COUNT;
++#ifndef DMA_NO_COPY
++      sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
++#else
++      sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
++                      usb_recv_callback);
++#endif
++      return 0;
++}
++
++static int
++usb_eth_release(struct net_device *dev)
++{
++      terminating = 1;
++      sa1100_usb_send_reset();
++      sa1100_usb_recv_reset();
++      if (cur_tx_skb)
++              kfree_skb(cur_tx_skb);
++      if (next_tx_skb)
++              kfree_skb(next_tx_skb);
++      if (cur_rx_skb)
++              kfree_skb(cur_rx_skb);
++      if (next_rx_skb)
++              kfree_skb(next_rx_skb);
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static struct net_device_stats *
++usb_eth_stats(struct net_device *dev)
++{
++      struct usbe_info_t *priv =  (struct usbe_info_t*) dev->priv;
++      struct net_device_stats *stats=NULL;
++
++      if (priv)
++              stats = &priv->stats;
++      return stats;
++}
++
++static int
++usb_eth_probe(struct net_device *dev)
++{
++      u8 node_id [ETH_ALEN];
++
++      get_random_bytes (node_id, sizeof node_id);
++      node_id [0] &= 0xfe;    // clear multicast bit
++
++      /*
++       * Assign the hardware address of the board:
++       * generate it randomly, as there can be many such
++       * devices on the bus.
++       */
++      memcpy (dev->dev_addr, node_id, sizeof node_id);
++
++      dev->open = usb_eth_open;
++      dev->change_mtu = usb_change_mtu;
++      dev->stop = usb_eth_release;
++      dev->hard_start_xmit = usb_eth_xmit;
++      dev->get_stats = usb_eth_stats;
++      dev->watchdog_timeo = 1*HZ;
++      dev->tx_timeout = usb_xmit_timeout;
++      dev->priv = &usbe_info;
++
++      usbe_info.dev = dev;
++
++      /* clear the statistics */
++      memset(&usbe_info.stats, 0, sizeof(struct net_device_stats));
++
++      ether_setup(dev);
++      dev->flags &= ~IFF_MULTICAST;
++      dev->flags &= ~IFF_BROADCAST;
++      //dev->flags |= IFF_NOARP;
++
++      return 0;
++}
++
++#ifdef MODULE
++MODULE_PARM(usb_rsize, "1i");
++MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa1100");
++MODULE_PARM(usb_wsize, "1i");
++MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa1100 to host");
++#endif
++
++static int __init
++usb_eth_init(void)
++{
++      int rc;
++
++#ifndef DMA_NO_COPY
++      dmabuf = kmalloc( usb_rsize, GFP_KERNEL | GFP_DMA );
++      if (!dmabuf)
++              return -ENOMEM;
++#endif
++      strncpy(usb_eth_device.name, usb_eth_name, IFNAMSIZ);
++      usb_eth_device.init = usb_eth_probe;
++      if (register_netdev(&usb_eth_device) != 0)
++              return -EIO;
++
++      rc = sa1100_usb_open( "usb-eth" );
++      if ( rc == 0 ) {
++               string_desc_t * pstr;
++               desc_t * pd = sa1100_usb_get_descriptor_ptr();
++
++               pd->b.ep1.wMaxPacketSize = make_word( usb_rsize );
++               pd->b.ep2.wMaxPacketSize = make_word( usb_wsize );
++               pd->dev.idVendor         = ETHERNET_VENDOR_ID;
++               pd->dev.idProduct     = ETHERNET_PRODUCT_ID;
++               pstr = sa1100_usb_kmalloc_string_descriptor( "SA1100 USB NIC" );
++               if ( pstr ) {
++                        sa1100_usb_set_string_descriptor( 1, pstr );
++                        pd->dev.iProduct = 1;
++               }
++               rc = sa1100_usb_start();
++      }
++      return rc;
++}
++
++module_init(usb_eth_init);
++
++static void __exit
++usb_eth_cleanup(void)
++{
++      string_desc_t * pstr;
++      sa1100_usb_stop();
++      sa1100_usb_close();
++      if ( (pstr = sa1100_usb_get_string_descriptor(1)) != NULL )
++              kfree( pstr );
++#ifndef DMA_NO_COPY
++      kfree(dmabuf);
++#endif
++      unregister_netdev(&usb_eth_device);
++}
++
++module_exit(usb_eth_cleanup);
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.c
+@@ -0,0 +1,774 @@
++ /*
++ *    Copyright (C) Compaq Computer Corporation, 1998, 1999
++ *  Copyright (C) Extenex Corporation, 2001
++ *
++ *  usb_ctl.c
++ *
++ *  SA1100 USB controller core driver.
++ *
++ *  This file provides interrupt routing and overall coordination
++ *  of the three endpoints in usb_ep0, usb_receive (1),  and usb_send (2).
++ *
++ *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/tqueue.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include "sa1100_usb.h"
++#include "usb_ctl.h"
++
++//////////////////////////////////////////////////////////////////////////////
++// Prototypes
++//////////////////////////////////////////////////////////////////////////////
++
++int usbctl_next_state_on_event( int event );
++static void udc_int_hndlr(int, void *, struct pt_regs *);
++static void initialize_descriptors( void );
++static void soft_connect_hook( int enable );
++static void udc_disable(void);
++static void udc_enable(void);
++
++#if CONFIG_PROC_FS
++#define PROC_NODE_NAME "sausb"
++static int usbctl_read_proc(char *page, char **start, off_t off,
++                                                      int count, int *eof, void *data);
++#endif
++
++//////////////////////////////////////////////////////////////////////////////
++// Globals
++//////////////////////////////////////////////////////////////////////////////
++static const char pszMe[] = "usbctl: ";
++struct usb_info_t usbd_info;  /* global to ep0, usb_recv, usb_send */
++
++/* device descriptors */
++static desc_t desc;
++
++#define MAX_STRING_DESC 8
++static string_desc_t * string_desc_array[ MAX_STRING_DESC ];
++static string_desc_t sd_zero;  /* special sd_zero holds language codes */
++
++// called when configured
++static usb_notify_t configured_callback = NULL;
++
++enum {        kStateZombie  = 0,  kStateZombieSuspend  = 1,
++              kStateDefault = 2,  kStateDefaultSuspend = 3,
++              kStateAddr    = 4,  kStateAddrSuspend    = 5,
++              kStateConfig  = 6,  kStateConfigSuspend  = 7
++};
++
++static int device_state_machine[8][6] = {
++//                suspend               reset          resume     adddr config deconfig
++/* zombie */  {  kStateZombieSuspend,  kStateDefault, kError,    kError, kError, kError },
++/* zom sus */ {  kError, kStateDefault, kStateZombie, kError, kError, kError },
++/* default */ {  kStateDefaultSuspend, kError, kStateDefault, kStateAddr, kError, kError },
++/* def sus */ {  kError, kStateDefault, kStateDefault, kError, kError, kError },
++/* addr */    {  kStateAddrSuspend, kStateDefault, kError, kError, kStateConfig, kError },
++/* addr sus */{  kError, kStateDefault, kStateAddr, kError, kError, kError },
++/* config */  {  kStateConfigSuspend, kStateDefault, kError, kError, kError, kStateAddr },
++/* cfg sus */ {  kError, kStateDefault, kStateConfig, kError, kError, kError }
++};
++
++/* "device state" is the usb device framework state, as opposed to the
++   "state machine state" which is whatever the driver needs and is much
++   more fine grained
++*/
++static int sm_state_to_device_state[8] =
++//  zombie           zom suspend          default            default sus
++{ USB_STATE_POWERED, USB_STATE_SUSPENDED, USB_STATE_DEFAULT, USB_STATE_SUSPENDED,
++// addr              addr sus             config                config sus
++  USB_STATE_ADDRESS, USB_STATE_SUSPENDED, USB_STATE_CONFIGURED, USB_STATE_SUSPENDED
++};
++
++static char * state_names[8] =
++{ "zombie", "zombie suspended", "default", "default suspended",
++  "address", "address suspended", "configured", "config suspended"
++};
++
++static char * event_names[6] =
++{ "suspend", "reset", "resume",
++  "address assigned", "configure", "de-configure"
++};
++
++static char * device_state_names[] =
++{ "not attached", "attached", "powered", "default",
++  "address", "configured", "suspended" };
++
++static int sm_state = kStateZombie;
++
++//////////////////////////////////////////////////////////////////////////////
++// Async
++//////////////////////////////////////////////////////////////////////////////
++static void core_kicker(void);
++
++static inline void enable_resume_mask_suspend( void );
++static inline void enable_suspend_mask_resume(void);
++
++static void
++udc_int_hndlr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      __u32 status = Ser0UDCSR;
++
++      /* ReSeT Interrupt Request - UDC has been reset */
++      if ( status & UDCSR_RSTIR )
++      {
++              if ( usbctl_next_state_on_event( kEvReset ) != kError )
++              {
++                      /* starting 20ms or so reset sequence now... */
++                      printk("%sResetting\n", pszMe);
++                      ep0_reset();  // just set state to idle
++                      ep1_reset();  // flush dma, clear false stall
++                      ep2_reset();  // flush dma, clear false stall
++              }
++              // mask reset ints, they flood during sequence, enable
++              // suspend and resume
++              Ser0UDCCR |= UDCCR_REM;    // mask reset
++              Ser0UDCCR &= ~(UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume
++              UDC_flip(  Ser0UDCSR, status ); // clear all pending sources
++              return;         // <-- no reason to continue if resetting
++      }
++      // else we have done something other than reset, so be sure reset enabled
++      UDC_clear( Ser0UDCCR, UDCCR_REM );
++
++      /* RESume Interrupt Request */
++      if ( status & UDCSR_RESIR )
++      {
++              usbctl_next_state_on_event( kEvResume );
++              core_kicker();
++              enable_suspend_mask_resume();
++      }
++
++      /* SUSpend Interrupt Request */
++      if ( status & UDCSR_SUSIR )
++      {
++              usbctl_next_state_on_event( kEvSuspend );
++              enable_resume_mask_suspend();
++      }
++
++      UDC_flip(Ser0UDCSR, status); // clear all pending sources
++
++      if (status & UDCSR_EIR)
++               ep0_int_hndlr();
++
++      if (status & UDCSR_RIR)
++              ep1_int_hndlr(status);
++
++      if (status & UDCSR_TIR)
++              ep2_int_hndlr(status);
++}
++
++static inline void enable_resume_mask_suspend( void )
++{
++       int i = 0;
++
++       while( 1 ) {
++                Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events
++                udelay( i );
++                if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR) )
++                         break;
++                if ( ++i == 50 ) {
++                         printk( "%senable_resume(): Could not set SUSIM %8.8X\n",
++                                         pszMe, Ser0UDCCR );
++                         break;
++                }
++       }
++
++       i = 0;
++       while( 1 ) {
++                Ser0UDCCR &= ~UDCCR_RESIM;
++                udelay( i );
++                if ( ( Ser0UDCCR & UDCCR_RESIM ) == 0
++                         ||
++                         (Ser0UDCSR & UDCSR_RSTIR)
++                       )
++                         break;
++                if ( ++i == 50 ) {
++                         printk( "%senable_resume(): Could not clear RESIM %8.8X\n",
++                                         pszMe, Ser0UDCCR );
++                         break;
++                }
++       }
++}
++
++static inline void enable_suspend_mask_resume(void)
++{
++       int i = 0;
++       while( 1 ) {
++                Ser0UDCCR |= UDCCR_RESIM; // mask future resume events
++                udelay( i );
++                if ( Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR) )
++                         break;
++                if ( ++i == 50 ) {
++                         printk( "%senable_suspend(): Could not set RESIM %8.8X\n",
++                                         pszMe, Ser0UDCCR );
++                         break;
++                }
++       }
++       i = 0;
++       while( 1 ) {
++                Ser0UDCCR &= ~UDCCR_SUSIM;
++                udelay( i );
++                if ( ( Ser0UDCCR & UDCCR_SUSIM ) == 0
++                         ||
++                         (Ser0UDCSR & UDCSR_RSTIR)
++                       )
++                         break;
++                if ( ++i == 50 ) {
++                         printk( "%senable_suspend(): Could not clear SUSIM %8.8X\n",
++                                         pszMe, Ser0UDCCR );
++                         break;
++                }
++       }
++}
++
++
++//////////////////////////////////////////////////////////////////////////////
++// Public Interface
++//////////////////////////////////////////////////////////////////////////////
++
++/* Open SA usb core on behalf of a client, but don't start running */
++
++int
++sa1100_usb_open( const char * client )
++{
++       if ( usbd_info.client_name != NULL )
++                return -EBUSY;
++
++       usbd_info.client_name = (char*) client;
++       memset(&usbd_info.stats, 0, sizeof(struct usb_stats_t));
++       memset(string_desc_array, 0, sizeof(string_desc_array));
++
++       /* hack to start in zombie suspended state */
++       sm_state = kStateZombieSuspend;
++       usbd_info.state = USB_STATE_SUSPENDED;
++
++       /* create descriptors for enumeration */
++       initialize_descriptors();
++
++       printk( "%sOpened for %s\n", pszMe, client );
++       return 0;
++}
++
++/* Start running. Must have called usb_open (above) first */
++int
++sa1100_usb_start( void )
++{
++       if ( usbd_info.client_name == NULL ) {
++                printk( "%s%s - no client registered\n",
++                                pszMe, __FUNCTION__ );
++                return -EPERM;
++       }
++
++       /* start UDC internal machinery running */
++       udc_enable();
++       udelay( 100 );
++
++       /* clear stall - receiver seems to start stalled? 19Jan01ww */
++       /* also clear other stuff just to be thurough 22Feb01ww */
++       UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC );
++       UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC );
++
++       /* mask everything */
++       Ser0UDCCR = 0xFC;
++
++       /* flush DMA and fire through some -EAGAINs */
++       ep1_init( usbd_info.dmach_rx );
++       ep2_init( usbd_info.dmach_tx );
++
++       /* give endpoint notification we are starting */
++       ep1_state_change_notify( USB_STATE_SUSPENDED );
++       ep2_state_change_notify( USB_STATE_SUSPENDED );
++
++       /* enable any platform specific hardware */
++       soft_connect_hook( 1 );
++
++      /* clear all top-level sources */
++       Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR   |
++                       UDCSR_RIR   | UDCSR_TIR   | UDCSR_SUSIR ;
++
++       /* EXERIMENT - a short line in the spec says toggling this
++        ..bit diddles the internal state machine in the udc to
++        ..expect a suspend */
++       Ser0UDCCR  |= UDCCR_RESIM;
++       /* END EXPERIMENT 10Feb01ww */
++
++       /* enable any platform specific hardware */
++       soft_connect_hook( 1 );
++
++       /* enable interrupts. If you are unplugged you will
++          immediately get a suspend interrupt. If you are plugged
++          and have a soft connect-circuit, you will get a reset
++        If you are plugged without a soft-connect, I think you
++          also get suspend. In short, start with suspend masked
++          and everything else enabled */
++       UDC_write( Ser0UDCCR, UDCCR_SUSIM );
++
++       printk( "%sStarted for %s\n", pszMe, usbd_info.client_name );
++       return 0;
++}
++
++/* Stop USB core from running */
++int
++sa1100_usb_stop( void )
++{
++       if ( usbd_info.client_name == NULL ) {
++                printk( "%s%s - no client registered\n",
++                                pszMe, __FUNCTION__ );
++                return -EPERM;
++       }
++       /* mask everything */
++       Ser0UDCCR = 0xFC;
++       ep1_reset();
++       ep2_reset();
++       udc_disable();
++       printk( "%sStopped\n", pszMe );
++       return 0;
++}
++
++/* Tell SA core client is through using it */
++int
++sa1100_usb_close( void )
++{
++       if ( usbd_info.client_name == NULL ) {
++                printk( "%s%s - no client registered\n",
++                                pszMe, __FUNCTION__ );
++                return -EPERM;
++       }
++       usbd_info.client_name = NULL;
++       printk( "%sClosed\n", pszMe );
++       return 0;
++}
++
++/* set a proc to be called when device is configured */
++usb_notify_t sa1100_set_configured_callback( usb_notify_t func )
++{
++       usb_notify_t retval = configured_callback;
++       configured_callback = func;
++       return retval;
++}
++
++/*====================================================
++ * Descriptor Manipulation.
++ * Use these between open() and start() above to setup
++ * the descriptors for your device.
++ *
++ */
++
++/* get pointer to static default descriptor */
++desc_t *
++sa1100_usb_get_descriptor_ptr( void ) { return &desc; }
++
++/* optional: set a string descriptor */
++int
++sa1100_usb_set_string_descriptor( int i, string_desc_t * p )
++{
++       int retval;
++       if ( i < MAX_STRING_DESC ) {
++                string_desc_array[i] = p;
++                retval = 0;
++       } else {
++                retval = -EINVAL;
++       }
++       return retval;
++}
++
++/* optional: get a previously set string descriptor */
++string_desc_t *
++sa1100_usb_get_string_descriptor( int i )
++{
++       return ( i < MAX_STRING_DESC )
++                  ? string_desc_array[i]
++                  : NULL;
++}
++
++
++/* optional: kmalloc and unicode up a string descriptor */
++string_desc_t *
++sa1100_usb_kmalloc_string_descriptor( const char * p )
++{
++       string_desc_t * pResult = NULL;
++
++       if ( p ) {
++                int len = strlen( p );
++                int uni_len = len * sizeof( __u16 );
++                pResult = (string_desc_t*) kmalloc( uni_len + 2, GFP_KERNEL ); /* ugh! */
++                if ( pResult != NULL ) {
++                         int i;
++                         pResult->bLength = uni_len + 2;
++                         pResult->bDescriptorType = USB_DESC_STRING;
++                         for( i = 0; i < len ; i++ ) {
++                                      pResult->bString[i] = make_word( (__u16) p[i] );
++                         }
++                }
++       }
++       return pResult;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Exports to rest of driver
++//////////////////////////////////////////////////////////////////////////////
++
++/* called by the int handler here and the two endpoint files when interesting
++   .."events" happen */
++
++int
++usbctl_next_state_on_event( int event )
++{
++      int next_state = device_state_machine[ sm_state ][ event ];
++      if ( next_state != kError )
++      {
++              int next_device_state = sm_state_to_device_state[ next_state ];
++              printk( "%s%s --> [%s] --> %s. Device in %s state.\n",
++                              pszMe, state_names[ sm_state ], event_names[ event ],
++                              state_names[ next_state ], device_state_names[ next_device_state ] );
++
++              sm_state = next_state;
++              if ( usbd_info.state != next_device_state )
++              {
++                      if ( configured_callback != NULL
++                               &&
++                               next_device_state == USB_STATE_CONFIGURED
++                               &&
++                               usbd_info.state != USB_STATE_SUSPENDED
++                         ) {
++                        configured_callback();
++                      }
++                      usbd_info.state = next_device_state;
++                      ep1_state_change_notify( next_device_state );
++                      ep2_state_change_notify( next_device_state );
++              }
++      }
++#if 0
++      else
++              printk( "%s%s --> [%s] --> ??? is an error.\n",
++                              pszMe, state_names[ sm_state ], event_names[ event ] );
++#endif
++      return next_state;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Private Helpers
++//////////////////////////////////////////////////////////////////////////////
++
++/* setup default descriptors */
++
++static void
++initialize_descriptors(void)
++{
++      desc.dev.bLength               = sizeof( device_desc_t );
++      desc.dev.bDescriptorType       = USB_DESC_DEVICE;
++      desc.dev.bcdUSB                = 0x100; /* 1.0 */
++      desc.dev.bDeviceClass          = 0xFF;  /* vendor specific */
++      desc.dev.bDeviceSubClass       = 0;
++      desc.dev.bDeviceProtocol       = 0;
++      desc.dev.bMaxPacketSize0       = 8;     /* ep0 max fifo size */
++      desc.dev.idVendor              = 0;     /* vendor ID undefined */
++      desc.dev.idProduct             = 0; /* product */
++      desc.dev.bcdDevice             = 0; /* vendor assigned device release num */
++      desc.dev.iManufacturer         = 0;     /* index of manufacturer string */
++      desc.dev.iProduct              = 0; /* index of product description string */
++      desc.dev.iSerialNumber         = 0;     /* index of string holding product s/n */
++      desc.dev.bNumConfigurations    = 1;
++
++      desc.b.cfg.bLength             = sizeof( config_desc_t );
++      desc.b.cfg.bDescriptorType     = USB_DESC_CONFIG;
++      desc.b.cfg.wTotalLength        = make_word_c( sizeof(struct cdb) );
++      desc.b.cfg.bNumInterfaces      = 1;
++      desc.b.cfg.bConfigurationValue = 1;
++      desc.b.cfg.iConfiguration      = 0;
++      desc.b.cfg.bmAttributes        = USB_CONFIG_BUSPOWERED;
++      desc.b.cfg.MaxPower            = USB_POWER( 500 );
++
++      desc.b.intf.bLength            = sizeof( intf_desc_t );
++      desc.b.intf.bDescriptorType    = USB_DESC_INTERFACE;
++      desc.b.intf.bInterfaceNumber   = 0; /* unique intf index*/
++      desc.b.intf.bAlternateSetting  = 0;
++      desc.b.intf.bNumEndpoints      = 2;
++      desc.b.intf.bInterfaceClass    = 0xFF; /* vendor specific */
++      desc.b.intf.bInterfaceSubClass = 0;
++      desc.b.intf.bInterfaceProtocol = 0;
++      desc.b.intf.iInterface         = 0;
++
++      desc.b.ep1.bLength             = sizeof( ep_desc_t );
++      desc.b.ep1.bDescriptorType     = USB_DESC_ENDPOINT;
++      desc.b.ep1.bEndpointAddress    = USB_EP_ADDRESS( 1, USB_OUT );
++      desc.b.ep1.bmAttributes        = USB_EP_BULK;
++      desc.b.ep1.wMaxPacketSize      = make_word_c( 64 );
++      desc.b.ep1.bInterval           = 0;
++
++      desc.b.ep2.bLength             = sizeof( ep_desc_t );
++      desc.b.ep2.bDescriptorType     = USB_DESC_ENDPOINT;
++      desc.b.ep2.bEndpointAddress    = USB_EP_ADDRESS( 2, USB_IN );
++      desc.b.ep2.bmAttributes        = USB_EP_BULK;
++      desc.b.ep2.wMaxPacketSize      = make_word_c( 64 );
++      desc.b.ep2.bInterval           = 0;
++
++      /* set language */
++      /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */
++      sd_zero.bDescriptorType = USB_DESC_STRING;
++      sd_zero.bLength         = sizeof( string_desc_t );
++      sd_zero.bString[0]      = make_word_c( 0x409 ); /* American English */
++      sa1100_usb_set_string_descriptor( 0, &sd_zero );
++}
++
++/* soft_connect_hook()
++ * Some devices have platform-specific circuitry to make USB
++ * not seem to be plugged in, even when it is. This allows
++ * software to control when a device 'appears' on the USB bus
++ * (after Linux has booted and this driver has loaded, for
++ * example). If you have such a circuit, control it here.
++ */
++static void
++soft_connect_hook( int enable )
++{
++#ifdef CONFIG_SA1100_EXTENEX1
++       if (machine_is_extenex1() ) {
++                if ( enable ) {
++                         PPDR |= PPC_USB_SOFT_CON;
++                         PPSR |= PPC_USB_SOFT_CON;
++                } else {
++                         PPSR &= ~PPC_USB_SOFT_CON;
++                         PPDR &= ~PPC_USB_SOFT_CON;
++                }
++       }
++#endif
++}
++
++/* disable the UDC at the source */
++static void
++udc_disable(void)
++{
++      soft_connect_hook( 0 );
++      UDC_set( Ser0UDCCR, UDCCR_UDD );
++}
++
++
++/*  enable the udc at the source */
++static void
++udc_enable(void)
++{
++      UDC_clear(Ser0UDCCR, UDCCR_UDD);
++}
++
++// HACK DEBUG  3Mar01ww
++// Well, maybe not, it really seems to help!  08Mar01ww
++static void
++core_kicker( void )
++{
++       __u32 car = Ser0UDCAR;
++       __u32 imp = Ser0UDCIMP;
++       __u32 omp = Ser0UDCOMP;
++
++       UDC_set( Ser0UDCCR, UDCCR_UDD );
++       udelay( 300 );
++       UDC_clear(Ser0UDCCR, UDCCR_UDD);
++
++       Ser0UDCAR = car;
++       Ser0UDCIMP = imp;
++       Ser0UDCOMP = omp;
++}
++
++//////////////////////////////////////////////////////////////////////////////
++// Proc Filesystem Support
++//////////////////////////////////////////////////////////////////////////////
++
++#if CONFIG_PROC_FS
++
++#define SAY( fmt, args... )  p += sprintf(p, fmt, ## args )
++#define SAYV(  num )         p += sprintf(p, num_fmt, "Value", num )
++#define SAYC( label, yn )    p += sprintf(p, yn_fmt, label, yn )
++#define SAYS( label, v )     p += sprintf(p, cnt_fmt, label, v )
++
++static int usbctl_read_proc(char *page, char **start, off_t off,
++                          int count, int *eof, void *data)
++{
++       const char * num_fmt   = "%25.25s: %8.8lX\n";
++       const char * cnt_fmt   = "%25.25s: %lu\n";
++       const char * yn_fmt    = "%25.25s: %s\n";
++       const char * yes       = "YES";
++       const char * no        = "NO";
++       unsigned long v;
++       char * p = page;
++       int len;
++
++       SAY( "SA1100 USB Controller Core\n" );
++       SAY( "USB state: %s (%s) %d\n",
++                device_state_names[ sm_state_to_device_state[ sm_state ] ],
++                state_names[ sm_state ],
++                sm_state );
++
++       SAYS( "ep0 bytes read", usbd_info.stats.ep0_bytes_read );
++       SAYS( "ep0 bytes written", usbd_info.stats.ep0_bytes_written );
++       SAYS( "ep0 FIFO read failures", usbd_info.stats.ep0_fifo_write_failures );
++       SAYS( "ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures );
++
++       SAY( "\n" );
++
++       v = Ser0UDCAR;
++       SAY( "%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v );
++       v = Ser0UDCIMP;
++       SAY( "%25.25s: %ld (%8.8lX)\n", "IN  max packet size", v+1, v );
++       v = Ser0UDCOMP;
++       SAY( "%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v );
++
++       v = Ser0UDCCR;
++       SAY( "\nUDC Mask Register\n" );
++       SAYV( v );
++       SAYC( "UDC Active",                ( v & UDCCR_UDA ) ? yes : no );
++       SAYC( "Suspend interrupts masked", ( v & UDCCR_SUSIM ) ? yes : no );
++       SAYC( "Resume interrupts masked",  ( v & UDCCR_RESIM ) ? yes : no );
++       SAYC( "Reset interrupts masked",   ( v & UDCCR_REM ) ? yes : no );
++
++       v = Ser0UDCSR;
++       SAY( "\nUDC Interrupt Request Register\n" );
++       SAYV( v );
++       SAYC( "Reset pending",      ( v & UDCSR_RSTIR ) ? yes : no );
++       SAYC( "Suspend pending",    ( v & UDCSR_SUSIR ) ? yes : no );
++       SAYC( "Resume pending",     ( v & UDCSR_RESIR ) ? yes : no );
++       SAYC( "ep0 pending",        ( v & UDCSR_EIR )   ? yes : no );
++       SAYC( "receiver pending",   ( v & UDCSR_RIR )   ? yes : no );
++       SAYC( "tramsitter pending", ( v & UDCSR_TIR )   ? yes : no );
++
++#ifdef CONFIG_SA1100_EXTENEX1
++       SAYC( "\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden" );
++#endif
++
++#if 0
++       v = Ser0UDCCS0;
++       SAY( "\nUDC Endpoint Zero Status Register\n" );
++       SAYV( v );
++       SAYC( "Out Packet Ready", ( v & UDCCS0_OPR ) ? yes : no );
++       SAYC( "In Packet Ready",  ( v & UDCCS0_IPR ) ? yes : no );
++       SAYC( "Sent Stall",       ( v & UDCCS0_SST ) ? yes : no );
++       SAYC( "Force Stall",      ( v & UDCCS0_FST ) ? yes : no );
++       SAYC( "Data End",         ( v & UDCCS0_DE )  ? yes : no );
++       SAYC( "Data Setup End",   ( v & UDCCS0_SE )  ? yes : no );
++       SAYC( "Serviced (SO)",    ( v & UDCCS0_SO )  ? yes : no );
++
++       v = Ser0UDCCS1;
++       SAY( "\nUDC Receiver Status Register\n" );
++       SAYV( v );
++       SAYC( "Receive Packet Complete", ( v & UDCCS1_RPC ) ? yes : no );
++       SAYC( "Sent Stall",              ( v & UDCCS1_SST ) ? yes : no );
++       SAYC( "Force Stall",             ( v & UDCCS1_FST ) ? yes : no );
++       SAYC( "Receive Packet Error",    ( v & UDCCS1_RPE ) ? yes : no );
++       SAYC( "Receive FIFO not empty",  ( v & UDCCS1_RNE ) ? yes : no );
++
++       v = Ser0UDCCS2;
++       SAY( "\nUDC Transmitter Status Register\n" );
++       SAYV( v );
++       SAYC( "FIFO has < 8 of 16 chars", ( v & UDCCS2_TFS ) ? yes : no );
++       SAYC( "Transmit Packet Complete", ( v & UDCCS2_TPC ) ? yes : no );
++       SAYC( "Transmit FIFO underrun",   ( v & UDCCS2_TUR ) ? yes : no );
++       SAYC( "Transmit Packet Error",    ( v & UDCCS2_TPE ) ? yes : no );
++       SAYC( "Sent Stall",               ( v & UDCCS2_SST ) ? yes : no );
++       SAYC( "Force Stall",              ( v & UDCCS2_FST ) ? yes : no );
++#endif
++
++       len = ( p - page ) - off;
++       if ( len < 0 )
++                len = 0;
++       *eof = ( len <=count ) ? 1 : 0;
++       *start = page + off;
++       return len;
++}
++
++#endif  /* CONFIG_PROC_FS */
++
++//////////////////////////////////////////////////////////////////////////////
++// Module Initialization and Shutdown
++//////////////////////////////////////////////////////////////////////////////
++/*
++ * usbctl_init()
++ * Module load time. Allocate dma and interrupt resources. Setup /proc fs
++ * entry. Leave UDC disabled.
++ */
++int __init usbctl_init( void )
++{
++      int retval = 0;
++
++      udc_disable();
++
++      memset( &usbd_info, 0, sizeof( usbd_info ) );
++
++#if CONFIG_PROC_FS
++      create_proc_read_entry ( PROC_NODE_NAME, 0, NULL, usbctl_read_proc, NULL);
++#endif
++
++      /* setup rx dma */
++      retval = sa1100_request_dma(&usbd_info.dmach_rx, "USB receive", DMA_Ser0UDCRd);
++      if (retval) {
++              printk("%sunable to register for rx dma rc=%d\n", pszMe, retval );
++              goto err_rx_dma;
++      }
++
++      /* setup tx dma */
++      retval = sa1100_request_dma(&usbd_info.dmach_tx, "USB transmit", DMA_Ser0UDCWr);
++      if (retval) {
++              printk("%sunable to register for tx dma rc=%d\n",pszMe,retval);
++              goto err_tx_dma;
++      }
++
++      /* now allocate the IRQ. */
++      retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, SA_INTERRUPT,
++                        "SA USB core", NULL);
++      if (retval) {
++              printk("%sCouldn't request USB irq rc=%d\n",pszMe, retval);
++              goto err_irq;
++      }
++
++      printk( "SA1100 USB Controller Core Initialized\n");
++      return 0;
++
++err_irq:
++      sa1100_free_dma(usbd_info.dmach_tx);
++      usbd_info.dmach_tx = 0;
++err_tx_dma:
++      sa1100_free_dma(usbd_info.dmach_rx);
++      usbd_info.dmach_rx = 0;
++err_rx_dma:
++      return retval;
++}
++/*
++ * usbctl_exit()
++ * Release DMA and interrupt resources
++ */
++void __exit usbctl_exit( void )
++{
++    printk("Unloading SA1100 USB Controller\n");
++
++      udc_disable();
++
++#if CONFIG_PROC_FS
++    remove_proc_entry ( PROC_NODE_NAME, NULL);
++#endif
++
++    sa1100_free_dma(usbd_info.dmach_rx);
++    sa1100_free_dma(usbd_info.dmach_tx);
++    free_irq(IRQ_Ser0UDC, NULL);
++}
++
++EXPORT_SYMBOL( sa1100_usb_open );
++EXPORT_SYMBOL( sa1100_usb_start );
++EXPORT_SYMBOL( sa1100_usb_stop );
++EXPORT_SYMBOL( sa1100_usb_close );
++
++
++EXPORT_SYMBOL( sa1100_usb_get_descriptor_ptr );
++EXPORT_SYMBOL( sa1100_usb_set_string_descriptor );
++EXPORT_SYMBOL( sa1100_usb_get_string_descriptor );
++EXPORT_SYMBOL( sa1100_usb_kmalloc_string_descriptor );
++
++
++module_init( usbctl_init );
++module_exit( usbctl_exit );
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.h
+@@ -0,0 +1,123 @@
++/*
++ *    Copyright (C)  Compaq Computer Corporation, 1998, 1999
++ *    Copyright (C)  Extenex Corporation 2001
++ *
++ *  usb_ctl.h
++ *
++ *  PRIVATE interface used to share info among components of the SA-1100 USB
++ *  core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core
++ *  should use sa1100_usb.h.
++ *
++ */
++
++#ifndef _USB_CTL_H
++#define _USB_CTL_H
++
++#include <asm/dma.h>  /* dmach_t */
++
++
++/*
++ * These states correspond to those in the USB specification v1.0
++ * in chapter 8, Device Framework.
++ */
++enum { USB_STATE_NOTATTACHED=0, USB_STATE_ATTACHED=1,USB_STATE_POWERED=2,
++         USB_STATE_DEFAULT=3, USB_STATE_ADDRESS=4, USB_STATE_CONFIGURED=5,
++         USB_STATE_SUSPENDED=6};
++
++struct usb_stats_t {
++       unsigned long ep0_fifo_write_failures;
++       unsigned long ep0_bytes_written;
++       unsigned long ep0_fifo_read_failures;
++       unsigned long ep0_bytes_read;
++};
++
++struct usb_info_t
++{
++       char * client_name;
++       dmach_t dmach_tx, dmach_rx;
++       int state;
++       unsigned char address;
++       struct usb_stats_t stats;
++};
++
++/* in usb_ctl.c */
++extern struct usb_info_t usbd_info;
++
++/*
++ * Function Prototypes
++ */
++enum { kError=-1, kEvSuspend=0, kEvReset=1,
++         kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 };
++int usbctl_next_state_on_event( int event );
++
++/* endpoint zero */
++void ep0_reset(void);
++void ep0_int_hndlr(void);
++
++/* receiver */
++void ep1_state_change_notify( int new_state );
++int  ep1_recv(void);
++int  ep1_init(int chn);
++void ep1_int_hndlr(int status);
++void ep1_reset(void);
++void ep1_stall(void);
++
++/* xmitter */
++void ep2_state_change_notify( int new_state );
++void ep2_reset(void);
++int  ep2_init(int chn);
++void ep2_int_hndlr(int status);
++void ep2_stall(void);
++
++#define UDC_write(reg, val) { \
++      int i = 10000; \
++      do { \
++              (reg) = (val); \
++              if (i-- <= 0) { \
++                      printk( "%s [%d]: write %#x to %p (%#x) failed\n", \
++                              __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++                      break; \
++              } \
++      } while((reg) != (val)); \
++}
++
++#define UDC_set(reg, val) { \
++      int i = 10000; \
++      do { \
++              (reg) |= (val); \
++              if (i-- <= 0) { \
++                      printk( "%s [%d]: set %#x of %p (%#x) failed\n", \
++                              __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++                      break; \
++              } \
++      } while(!((reg) & (val))); \
++}
++
++#define UDC_clear(reg, val) { \
++      int i = 10000; \
++      do { \
++              (reg) &= ~(val); \
++              if (i-- <= 0) { \
++                      printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \
++                              __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++                      break; \
++              } \
++      } while((reg) & (val)); \
++}
++
++#define UDC_flip(reg, val) { \
++      int i = 10000; \
++      (reg) = (val); \
++      do { \
++              (reg) = (val); \
++              if (i-- <= 0) { \
++                      printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \
++                              __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++                      break; \
++              } \
++      } while(((reg) & (val))); \
++}
++
++
++#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}}
++#endif /* _USB_CTL_H */
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_ep0.c
+@@ -0,0 +1,911 @@
++/*
++ * Copyright (C) Extenex Corporation 2001
++ * Much folklore gleaned from original code:
++ *    Copyright (C) Compaq Computer Corporation, 1998, 1999
++ *
++ *  usb_ep0.c - SA1100 USB controller driver.
++ *              Endpoint zero management
++ *
++ *  Please see:
++ *    linux/Documentation/arm/SA1100/SA1100_USB
++ *  for details. (Especially since Intel docs are full of
++ *  errors about ep0 operation.) ward.willats@extenex.com.
++ *
++ * Intel also has a "Universal Serial Bus Client Device
++ * Validation for the StrongARM SA-1100 Microprocessor"
++ * document, which has flow charts and assembler test driver,
++ * but be careful, since it is just for validation and not
++ * a "real world" solution.
++ *
++ * A summary of three types of data-returning setups:
++ *
++ * 1. Setup request <= 8 bytes. That is, requests that can
++ *    be fullfilled in one write to the FIFO. DE is set
++ *    with IPR in queue_and_start_write(). (I don't know
++ *    if there really are any of these!)
++ *
++ * 2. Setup requests > 8 bytes (requiring more than one
++ *    IN to get back to the host), and we have at least
++ *    as much or more data than the host requested. In
++ *    this case we pump out everything we've got, and
++ *    when the final interrupt comes in due to the UDC
++ *    clearing the last IPR, we just set DE.
++ *
++ * 3. Setup requests > 8 bytes, but we don't have enough
++ *    data to satisfy the request. In this case, we send
++ *    everything we've got, and when the final interrupt
++ *    comes in due to the UDC clearing the last IPR
++ *    we write nothing to the FIFO and set both IPR and DE
++ *    so the UDC sends an empty packet and forces the host
++ *    to perform short packet retirement instead of stalling
++ *    out.
++ *
++ */
++
++#include <linux/delay.h>
++#include "sa1100_usb.h"  /* public interface */
++#include "usb_ctl.h"     /* private stuff */
++
++
++// 1 == lots of trace noise,  0 = only "important' stuff
++#define VERBOSITY 0
++
++enum { true = 1, false = 0 };
++typedef int bool;
++#ifndef MIN
++#define MIN( a, b ) ((a)<(b)?(a):(b))
++#endif
++
++#if 1 && !defined( ASSERT )
++#  define ASSERT(expr) \
++          if(!(expr)) { \
++          printk( "Assertion failed! %s,%s,%s,line=%d\n",\
++          #expr,__FILE__,__FUNCTION__,__LINE__); \
++          }
++#else
++#  define ASSERT(expr)
++#endif
++
++#if VERBOSITY
++#define PRINTKD(fmt, args...) printk( fmt , ## args)
++#else
++#define PRINTKD(fmt, args...)
++#endif
++
++/*================================================
++ * USB Protocol Stuff
++ */
++
++/* Request Codes   */
++enum { GET_STATUS=0,         CLEAR_FEATURE=1,     SET_FEATURE=3,
++         SET_ADDRESS=5,        GET_DESCRIPTOR=6,        SET_DESCRIPTOR=7,
++         GET_CONFIGURATION=8,  SET_CONFIGURATION=9, GET_INTERFACE=10,
++         SET_INTERFACE=11 };
++
++
++/* USB Device Requests */
++typedef struct
++{
++    __u8 bmRequestType;
++    __u8 bRequest;
++    __u16 wValue;
++    __u16 wIndex;
++    __u16 wLength;
++} usb_dev_request_t  __attribute__ ((packed));
++
++/***************************************************************************
++Prototypes
++***************************************************************************/
++/* "setup handlers" -- the main functions dispatched to by the
++   .. isr. These represent the major "modes" of endpoint 0 operaton */
++static void sh_setup_begin(void);                             /* setup begin (idle) */
++static void sh_write( void );                                 /* writing data */
++static void sh_write_with_empty_packet( void ); /* empty packet at end of xfer*/
++/* called before both sh_write routines above */
++static void common_write_preamble( void );
++
++/* other subroutines */
++static __u32  queue_and_start_write( void * p, int req, int act );
++static void write_fifo( void );
++static int read_fifo( usb_dev_request_t * p );
++static void get_descriptor( usb_dev_request_t * pReq );
++
++/* some voodo helpers  01Mar01ww */
++static void set_cs_bits( __u32 set_bits );
++static void set_de( void );
++static void set_ipr( void );
++static void set_ipr_and_de( void );
++static bool clear_opr( void );
++
++/***************************************************************************
++Inline Helpers
++***************************************************************************/
++
++/* Data extraction from usb_request_t fields */
++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };
++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }
++
++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }
++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }
++
++/* following is hook for self-powered flag in GET_STATUS. Some devices
++   .. might like to override and return real info */
++static inline bool self_powered_hook( void ) { return true; }
++
++/* print string descriptor */
++static inline void psdesc( string_desc_t * p )
++{
++       int i;
++       int nchars = ( p->bLength - 2 ) / sizeof( __u16 );
++       printk( "'" );
++       for( i = 0 ; i < nchars ; i++ ) {
++                printk( "%c", (char) p->bString[i] );
++       }
++       printk( "'\n" );
++}
++
++
++#if VERBOSITY
++/* "pcs" == "print control status" */
++static inline void pcs( void )
++{
++       __u32 foo = Ser0UDCCS0;
++       printk( "%8.8X: %s %s %s %s\n",
++                       foo,
++                       foo & UDCCS0_SE ? "SE" : "",
++                       foo & UDCCS0_OPR ? "OPR" : "",
++                       foo & UDCCS0_IPR ? "IPR" : "",
++                       foo & UDCCS0_SST ? "SST" : ""
++       );
++}
++static inline void preq( usb_dev_request_t * pReq )
++{
++       static char * tnames[] = { "dev", "intf", "ep", "oth" };
++       static char * rnames[] = { "std", "class", "vendor", "???" };
++       char * psz;
++       switch( pReq->bRequest ) {
++       case GET_STATUS: psz = "get stat"; break;
++       case CLEAR_FEATURE: psz = "clr feat"; break;
++       case SET_FEATURE: psz = "set feat"; break;
++       case SET_ADDRESS: psz = "set addr"; break;
++       case GET_DESCRIPTOR: psz = "get desc"; break;
++       case SET_DESCRIPTOR: psz = "set desc"; break;
++       case GET_CONFIGURATION: psz = "get cfg"; break;
++       case SET_CONFIGURATION: psz = "set cfg"; break;
++       case GET_INTERFACE: psz = "get intf"; break;
++       case SET_INTERFACE: psz = "set intf"; break;
++       default: psz = "unknown"; break;
++       }
++       printk( "- [%s: %s req to %s. dir=%s]\n", psz,
++                       rnames[ (pReq->bmRequestType >> 5) & 3 ],
++                       tnames[ pReq->bmRequestType & 3 ],
++                       ( pReq->bmRequestType & 0x80 ) ? "in" : "out" );
++}
++
++#else
++static inline void pcs( void ){}
++static inline void preq( void ){}
++#endif
++
++/***************************************************************************
++Globals
++***************************************************************************/
++static const char pszMe[] = "usbep0: ";
++
++/* pointer to current setup handler */
++static void (*current_handler)(void) = sh_setup_begin;
++
++/* global write struct to keep write
++   ..state around across interrupts */
++static struct {
++              unsigned char *p;
++              int bytes_left;
++} wr;
++
++/***************************************************************************
++Public Interface
++***************************************************************************/
++
++/* reset received from HUB (or controller just went nuts and reset by itself!)
++  so udc core has been reset, track this state here  */
++void
++ep0_reset(void)
++{
++       /* reset state machine */
++       current_handler = sh_setup_begin;
++       wr.p = NULL;
++       wr.bytes_left = 0;
++       usbd_info.address=0;
++}
++
++/* handle interrupt for endpoint zero */
++void
++ep0_int_hndlr( void )
++{
++       PRINTKD( "/\\(%d)\n", Ser0UDCAR );
++       pcs();
++
++       /* if not in setup begin, we are returning data.
++              execute a common preamble to both write handlers
++       */
++       if ( current_handler != sh_setup_begin )
++                common_write_preamble();
++
++       (*current_handler)();
++
++       PRINTKD( "---\n" );
++       pcs();
++       PRINTKD( "\\/\n" );
++}
++
++/***************************************************************************
++Setup Handlers
++***************************************************************************/
++/*
++ * sh_setup_begin()
++ * This setup handler is the "idle" state of endpoint zero. It looks for OPR
++ * (OUT packet ready) to see if a setup request has been been received from the
++ * host. Requests without a return data phase are immediately handled. Otherwise,
++ * in the case of GET_XXXX the handler may be set to one of the sh_write_xxxx
++ * data pumpers if more than 8 bytes need to get back to the host.
++ *
++ */
++static void
++sh_setup_begin( void )
++{
++       unsigned char status_buf[2];  /* returned in GET_STATUS */
++       usb_dev_request_t req;
++       int request_type;
++       int n;
++       __u32 cs_bits;
++       __u32 address;
++       __u32 cs_reg_in = Ser0UDCCS0;
++
++       if (cs_reg_in & UDCCS0_SST) {
++                PRINTKD( "%ssetup begin: sent stall. Continuing\n", pszMe );
++                set_cs_bits( UDCCS0_SST );
++      }
++
++       if ( cs_reg_in & UDCCS0_SE ) {
++                PRINTKD( "%ssetup begin: Early term of setup. Continuing\n", pszMe );
++                set_cs_bits( UDCCS0_SSE );             /* clear setup end */
++       }
++
++       /* Be sure out packet ready, otherwise something is wrong */
++       if ( (cs_reg_in & UDCCS0_OPR) == 0 ) {
++                /* we can get here early...if so, we'll int again in a moment  */
++                PRINTKD( "%ssetup begin: no OUT packet available. Exiting\n", pszMe );
++                goto sh_sb_end;
++       }
++
++       /* read the setup request */
++       n = read_fifo( &req );
++       if ( n != sizeof( req ) ) {
++                printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
++                                " Stalling out...\n",
++                                pszMe, sizeof( req ), n );
++                /* force stall, serviced out */
++                set_cs_bits( UDCCS0_FST | UDCCS0_SO  );
++                goto sh_sb_end;
++       }
++
++       /* Is it a standard request? (not vendor or class request) */
++       request_type = type_code_from_request( req.bmRequestType );
++       if ( request_type != 0 ) {
++                printk( "%ssetup begin: unsupported bmRequestType: %d ignored\n",
++                                pszMe, request_type );
++                set_cs_bits( UDCCS0_DE | UDCCS0_SO );
++                goto sh_sb_end;
++       }
++
++#if VERBOSITY
++       {
++       unsigned char * pdb = (unsigned char *) &req;
++       PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
++                       pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
++                );
++       preq( &req );
++       }
++#endif
++
++       /* Handle it */
++       switch( req.bRequest ) {
++
++                /* This first bunch have no data phase */
++
++       case SET_ADDRESS:
++                address = (__u32) (req.wValue & 0x7F);
++                /* when SO and DE sent, UDC will enter status phase and ack,
++                       ..propagating new address to udc core. Next control transfer
++                       ..will be on the new address. You can't see the change in a
++                       ..read back of CAR until then. (about 250us later, on my box).
++                       ..The original Intel driver sets S0 and DE and code to check
++                       ..that address has propagated here. I tried this, but it
++                       ..would only work sometimes! The rest of the time it would
++                       ..never propagate and we'd spin forever. So now I just set
++                       ..it and pray...
++                */
++                Ser0UDCAR = address;
++                usbd_info.address = address;
++                usbctl_next_state_on_event( kEvAddress );
++                set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */
++                printk( "%sI have been assigned address: %d\n", pszMe, address );
++                break;
++
++
++       case SET_CONFIGURATION:
++                if ( req.wValue == 1 ) {
++                         /* configured */
++                         if (usbctl_next_state_on_event( kEvConfig ) != kError){
++                                                              /* (re)set the out and in max packet sizes */
++                                      desc_t * pDesc = sa1100_usb_get_descriptor_ptr();
++                                      __u32 out = __le16_to_cpu( pDesc->b.ep1.wMaxPacketSize );
++                                      __u32 in  = __le16_to_cpu( pDesc->b.ep2.wMaxPacketSize );
++                                      Ser0UDCOMP = ( out - 1 );
++                                      Ser0UDCIMP = ( in - 1 );
++                                      printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in );
++                         }
++                } else if ( req.wValue == 0 ) {
++                         /* de-configured */
++                         if (usbctl_next_state_on_event( kEvDeConfig ) != kError )
++                                      printk( "%sDe-Configured\n", pszMe );
++                } else {
++                         printk( "%ssetup phase: Unknown "
++                                         "\"set configuration\" data %d\n",
++                                         pszMe, req.wValue );
++                }
++                set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */
++                break;
++
++       case CLEAR_FEATURE:
++                /* could check data length, direction...26Jan01ww */
++                if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */
++                         int ep = windex_to_ep_num( req.wIndex );
++                         if ( ep == 1 ) {
++                                      printk( "%sclear feature \"endpoint halt\" "
++                                                      " on receiver\n", pszMe );
++                                      ep1_reset();
++                         }
++                         else if ( ep == 2 ) {
++                                      printk( "%sclear feature \"endpoint halt\" "
++                                                      "on xmitter\n", pszMe );
++                                      ep2_reset();
++                         } else {
++                                      printk( "%sclear feature \"endpoint halt\" "
++                                                      "on unsupported ep # %d\n",
++                                                      pszMe, ep );
++                         }
++                } else {
++                         printk( "%sUnsupported feature selector (%d) "
++                                         "in clear feature. Ignored.\n" ,
++                                         pszMe, req.wValue );
++                }
++                set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */
++                break;
++
++       case SET_FEATURE:
++                if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */
++                         int ep = windex_to_ep_num( req.wValue );
++                         if ( ep == 1 ) {
++                                      printk( "%set feature \"endpoint halt\" "
++                                                      "on receiver\n", pszMe );
++                                      ep1_stall();
++                         }
++                         else if ( ep == 2 ) {
++                                      printk( "%sset feature \"endpoint halt\" "
++                                                      " on xmitter\n", pszMe );
++                                      ep2_stall();
++                         } else {
++                                      printk( "%sset feature \"endpoint halt\" "
++                                                      "on unsupported ep # %d\n",
++                                                      pszMe, ep );
++                         }
++                }
++                else {
++                         printk( "%sUnsupported feature selector "
++                                         "(%d) in set feature\n",
++                                         pszMe, req.wValue );
++                }
++                set_cs_bits( UDCCS0_SO | UDCCS0_DE );  /* no data phase */
++                break;
++
++
++                /* The rest have a data phase that writes back to the host */
++       case GET_STATUS:
++                /* return status bit flags */
++                status_buf[0] = status_buf[1] = 0;
++                n = request_target(req.bmRequestType);
++                switch( n ) {
++                case kTargetDevice:
++                         if ( self_powered_hook() )
++                                      status_buf[0] |= 1;
++                         break;
++                case kTargetInterface:
++                         break;
++                case kTargetEndpoint:
++                         /* return stalled bit */
++                         n = windex_to_ep_num( req.wIndex );
++                         if ( n == 1 )
++                                      status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4;
++                         else if ( n == 2 )
++                                      status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5;
++                         else {
++                                      printk( "%sUnknown endpoint (%d) "
++                                                      "in GET_STATUS\n", pszMe, n );
++                         }
++                         break;
++                default:
++                         printk( "%sUnknown target (%d) in GET_STATUS\n",
++                                         pszMe, n );
++                         /* fall thru */
++                         break;
++                }
++                cs_bits  = queue_and_start_write( status_buf,
++                                                                                      req.wLength,
++                                                                                      sizeof( status_buf ) );
++                set_cs_bits( cs_bits );
++                break;
++       case GET_DESCRIPTOR:
++                get_descriptor( &req );
++                break;
++
++       case GET_CONFIGURATION:
++                status_buf[0] = (usbd_info.state ==  USB_STATE_CONFIGURED)
++                         ? 1
++                         : 0;
++                cs_bits = queue_and_start_write( status_buf, req.wLength, 1 );
++                set_cs_bits( cs_bits );
++                break;
++       case GET_INTERFACE:
++                printk( "%sfixme: get interface not supported\n", pszMe );
++                cs_bits = queue_and_start_write( NULL, req.wLength, 0 );
++                set_cs_bits( cs_bits );
++                break;
++       case SET_INTERFACE:
++                printk( "%sfixme: set interface not supported\n", pszMe );
++                set_cs_bits( UDCCS0_DE | UDCCS0_SO );
++                break;
++       default :
++                printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
++                break;
++       } /* switch( bRequest ) */
++
++sh_sb_end:
++       return;
++
++}
++/*
++ * common_wrtie_preamble()
++ * Called before execution of sh_write() or sh_write_with_empty_packet()
++ * Handles common abort conditions.
++ *
++ */
++static void common_write_preamble( void )
++{
++       /* If "setup end" has been set, the usb controller has
++              ..terminated a setup transaction before we set DE. This
++              ..happens during enumeration with some hosts. For example,
++              ..the host will ask for our device descriptor and specify
++              ..a return of 64 bytes. When we hand back the first 8, the
++              ..host will know our max packet size and turn around and
++              ..issue a new setup immediately. This causes the UDC to auto-ack
++              ..the new setup and set SE. We must then "unload" (process)
++              ..the new setup, which is what will happen after this preamble
++              ..is finished executing.
++       */
++       __u32 cs_reg_in = Ser0UDCCS0;
++
++       if ( cs_reg_in & UDCCS0_SE ) {
++                PRINTKD( "%swrite_preamble(): Early termination of setup\n", pszMe );
++                Ser0UDCCS0 =  UDCCS0_SSE;              /* clear setup end */
++                current_handler = sh_setup_begin;
++       }
++
++       if ( cs_reg_in & UDCCS0_SST ) {
++                PRINTKD( "%swrite_preamble(): UDC sent stall\n", pszMe );
++                Ser0UDCCS0 = UDCCS0_SST;               /* clear setup end */
++                current_handler = sh_setup_begin;
++       }
++
++       if ( cs_reg_in & UDCCS0_OPR ) {
++                PRINTKD( "%swrite_preamble(): see OPR. Stopping write to "
++                                 "handle new SETUP\n", pszMe );
++                /* very rarely, you can get OPR and leftover IPR. Try to clear */
++                UDC_clear( Ser0UDCCS0, UDCCS0_IPR );
++                current_handler = sh_setup_begin;
++       }
++}
++
++/*
++ * sh_write()
++ * This is the setup handler when we are in the data return phase of
++ * a setup request and have as much (or more) data than the host
++ * requested. If we enter this routine and bytes left is zero, the
++ * last data packet has gone (int is because IPR was just cleared)
++ * so we just set DE and reset. Otheriwse, we write another packet
++ * and set IPR.
++ */
++static void sh_write()
++{
++       PRINTKD( "W\n" );
++
++       if ( Ser0UDCCS0 & UDCCS0_IPR ) {
++                PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe );
++                return;
++       }
++
++       /* If bytes left is zero, we are coming in on the
++              ..interrupt after the last packet went out. And
++              ..we know we don't have to empty packet this transfer
++              ..so just set DE and we are done */
++
++       if ( 0 == wr.bytes_left ) {
++                /* that's it, so data end  */
++                set_de();
++                wr.p = NULL;                                  /* be anal */
++                current_handler = sh_setup_begin;
++       } else {
++                /* Otherwise, more data to go */
++                write_fifo();
++                set_ipr();
++       }
++}
++/*
++ * sh_write_with_empty_packet()
++ * This is the setup handler when we don't have enough data to
++ * satisfy the host's request. After we send everything we've got
++ * we must send an empty packet (by setting IPR and DE) so the
++ * host can perform "short packet retirement" and not stall.
++ *
++ */
++static void sh_write_with_empty_packet( void )
++{
++       __u32 cs_reg_out = 0;
++       PRINTKD( "WE\n" );
++
++       if ( Ser0UDCCS0 & UDCCS0_IPR ) {
++                PRINTKD( "%ssh_write(): IPR set, exiting\n", pszMe );
++                return;
++       }
++
++       /* If bytes left is zero, we are coming in on the
++              ..interrupt after the last packet went out.
++              ..we must do short packet suff, so set DE and IPR
++       */
++
++       if ( 0 == wr.bytes_left ) {
++                set_ipr_and_de();
++                wr.p = NULL;
++                current_handler = sh_setup_begin;
++                PRINTKD( "%ssh_write empty() Sent empty packet \n", pszMe );
++       }  else {
++                write_fifo();                         /* send data */
++                set_ipr();                            /* flag a packet is ready */
++       }
++       Ser0UDCCS0 = cs_reg_out;
++}
++
++/***************************************************************************
++Other Private Subroutines
++***************************************************************************/
++/*
++ * queue_and_start_write()
++ * p == data to send
++ * req == bytes host requested
++ * act == bytes we actually have
++ * Returns: bits to be flipped in ep0 control/status register
++ *
++ * Called from sh_setup_begin() to begin a data return phase. Sets up the
++ * global "wr"-ite structure and load the outbound FIFO with data.
++ * If can't send all the data, set appropriate handler for next interrupt.
++ *
++ */
++static __u32  queue_and_start_write( void * in, int req, int act )
++{
++       __u32 cs_reg_bits = UDCCS0_IPR;
++       unsigned char * p = (unsigned char*) in;
++
++       PRINTKD( "Qr=%d a=%d\n",req,act );
++
++      /* thou shalt not enter data phase until the serviced OUT is clear */
++       if ( ! clear_opr() ) {
++                printk( "%sSO did not clear OPR\n", pszMe );
++                return ( UDCCS0_DE | UDCCS0_SO ) ;
++       }
++       wr.p = p;
++       wr.bytes_left = MIN( act, req );
++
++       write_fifo();
++
++       if ( 0 == wr.bytes_left ) {
++                cs_reg_bits |= UDCCS0_DE;     /* out in 1 so data end */
++                wr.p = NULL;                                  /* be anal */
++       }
++       else if ( act < req )   /* we are going to short-change host */
++                current_handler = sh_write_with_empty_packet; /* so need nul to not stall */
++       else /* we have as much or more than requested */
++                current_handler = sh_write;
++
++       return cs_reg_bits; /* note: IPR was set uncondtionally at start of routine */
++}
++/*
++ * write_fifo()
++ * Stick bytes in the 8 bytes endpoint zero FIFO.
++ * This version uses a variety of tricks to make sure the bytes
++ * are written correctly. 1. The count register is checked to
++ * see if the byte went in, and the write is attempted again
++ * if not. 2. An overall counter is used to break out so we
++ * don't hang in those (rare) cases where the UDC reverses
++ * direction of the FIFO underneath us without notification
++ * (in response to host aborting a setup transaction early).
++ *
++ */
++static void write_fifo( void )
++{
++      int bytes_this_time = MIN( wr.bytes_left, 8 );
++      int bytes_written = 0;
++      int i=0;
++
++      PRINTKD( "WF=%d: ", bytes_this_time );
++
++      while( bytes_this_time-- ) {
++               PRINTKD( "%2.2X ", *wr.p );
++               i = 0;
++               do {
++                        Ser0UDCD0 = *wr.p;
++                        udelay( 20 );  /* voodo 28Feb01ww */
++                        i++;
++               } while( Ser0UDCWC == bytes_written && i < 10 );
++               if ( i == 50 ) {
++                        printk( "%swrite_fifo: write failure\n", pszMe );
++                        usbd_info.stats.ep0_fifo_write_failures++;
++               }
++
++               wr.p++;
++               bytes_written++;
++      }
++      wr.bytes_left -= bytes_written;
++
++      /* following propagation voodo so maybe caller writing IPR in
++         ..a moment might actually get it to stick 28Feb01ww */
++      udelay( 300 );
++
++      usbd_info.stats.ep0_bytes_written += bytes_written;
++      PRINTKD( "L=%d WCR=%8.8X\n", wr.bytes_left, Ser0UDCWC );
++}
++/*
++ * read_fifo()
++ * Read 1-8 bytes out of FIFO and put in request.
++ * Called to do the initial read of setup requests
++ * from the host. Return number of bytes read.
++ *
++ * Like write fifo above, this driver uses multiple
++ * reads checked agains the count register with an
++ * overall timeout.
++ *
++ */
++static int
++read_fifo( usb_dev_request_t * request )
++{
++      int bytes_read = 0;
++      int fifo_count;
++      int i;
++
++      unsigned char * pOut = (unsigned char*) request;
++
++      fifo_count = ( Ser0UDCWC & 0xFF );
++
++      ASSERT( fifo_count <= 8 );
++      PRINTKD( "RF=%d ", fifo_count );
++
++      while( fifo_count-- ) {
++               i = 0;
++               do {
++                        *pOut = (unsigned char) Ser0UDCD0;
++                        udelay( 10 );
++               } while( ( Ser0UDCWC & 0xFF ) != fifo_count && i < 10 );
++               if ( i == 10 ) {
++                        printk( "%sread_fifo(): read failure\n", pszMe );
++                        usbd_info.stats.ep0_fifo_read_failures++;
++               }
++               pOut++;
++               bytes_read++;
++      }
++
++      PRINTKD( "fc=%d\n", bytes_read );
++      usbd_info.stats.ep0_bytes_read++;
++      return bytes_read;
++}
++
++/*
++ * get_descriptor()
++ * Called from sh_setup_begin to handle data return
++ * for a GET_DESCRIPTOR setup request.
++ */
++static void get_descriptor( usb_dev_request_t * pReq )
++{
++       __u32 cs_bits = 0;
++       string_desc_t * pString;
++       ep_desc_t * pEndpoint;
++
++       desc_t * pDesc = sa1100_usb_get_descriptor_ptr();
++       int type = pReq->wValue >> 8;
++       int idx  = pReq->wValue & 0xFF;
++
++       switch( type ) {
++       case USB_DESC_DEVICE:
++                cs_bits =
++                         queue_and_start_write( &pDesc->dev,
++                                                                        pReq->wLength,
++                                                                        pDesc->dev.bLength );
++                break;
++
++                // return config descriptor buffer, cfg, intf, 2 ep
++       case USB_DESC_CONFIG:
++                cs_bits =
++                         queue_and_start_write( &pDesc->b,
++                                                                        pReq->wLength,
++                                                                        sizeof( struct cdb ) );
++                break;
++
++                // not quite right, since doesn't do language code checking
++       case USB_DESC_STRING:
++                pString = sa1100_usb_get_string_descriptor( idx );
++                if ( pString ) {
++                         if ( idx != 0 ) {  // if not language index
++                                      printk( "%sReturn string %d: ", pszMe, idx );
++                                      psdesc( pString );
++                         }
++                         cs_bits =
++                                      queue_and_start_write( pString,
++                                                                                 pReq->wLength,
++                                                                                 pString->bLength );
++                }
++                else {
++                         printk("%sunkown string index %d Stall.\n", pszMe, idx );
++                         cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST );
++                }
++                break;
++
++       case USB_DESC_INTERFACE:
++                if ( idx == pDesc->b.intf.bInterfaceNumber ) {
++                         cs_bits =
++                                      queue_and_start_write( &pDesc->b.intf,
++                                                                                 pReq->wLength,
++                                                                                 pDesc->b.intf.bLength );
++                }
++                break;
++
++       case USB_DESC_ENDPOINT: /* correct? 21Feb01ww */
++                if ( idx == 1 )
++                         pEndpoint = &pDesc->b.ep1;
++                else if ( idx == 2 )
++                         pEndpoint = &pDesc->b.ep2;
++                else
++                         pEndpoint = NULL;
++                if ( pEndpoint ) {
++                         cs_bits =
++                                      queue_and_start_write( pEndpoint,
++                                                                                 pReq->wLength,
++                                                                                 pEndpoint->bLength );
++                } else {
++                         printk("%sunkown endpoint index %d Stall.\n", pszMe, idx );
++                         cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST );
++                }
++                break;
++
++
++       default :
++                printk("%sunknown descriptor type %d. Stall.\n", pszMe, type );
++                cs_bits = ( UDCCS0_DE | UDCCS0_SO | UDCCS0_FST );
++                break;
++
++       }
++       set_cs_bits( cs_bits );
++}
++
++
++/* some voodo I am adding, since the vanilla macros just aren't doing it  1Mar01ww */
++
++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE )
++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS ))
++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE)
++
++static void set_cs_bits( __u32 bits )
++{
++       if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST ) )
++                Ser0UDCCS0 = bits;
++       else if ( (bits & BOTH_BITS) == BOTH_BITS )
++                set_ipr_and_de();
++       else if ( bits & UDCCS0_IPR )
++                set_ipr();
++       else if ( bits & UDCCS0_DE )
++                set_de();
++}
++
++static void set_de( void )
++{
++       int i = 1;
++       while( 1 ) {
++                if ( OK_TO_WRITE ) {
++                              Ser0UDCCS0 |= UDCCS0_DE;
++                } else {
++                         PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe );
++                         break;
++                }
++                if ( Ser0UDCCS0 & UDCCS0_DE )
++                         break;
++                udelay( i );
++                if ( ++i == 50  ) {
++                         printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n",
++                                         pszMe, UDCCS0_DE, Ser0UDCCS0 );
++                         break;
++                }
++       }
++}
++
++static void set_ipr( void )
++{
++       int i = 1;
++       while( 1 ) {
++                if ( OK_TO_WRITE ) {
++                              Ser0UDCCS0 |= UDCCS0_IPR;
++                } else {
++                         PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe );
++                         break;
++                }
++                if ( Ser0UDCCS0 & UDCCS0_IPR )
++                         break;
++                udelay( i );
++                if ( ++i == 50  ) {
++                         printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n",
++                                         pszMe, UDCCS0_IPR, Ser0UDCCS0 );
++                         break;
++                }
++       }
++}
++
++
++
++static void set_ipr_and_de( void )
++{
++       int i = 1;
++       while( 1 ) {
++                if ( OK_TO_WRITE ) {
++                         Ser0UDCCS0 |= BOTH_BITS;
++                } else {
++                         PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe );
++                         break;
++                }
++                if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS)
++                         break;
++                udelay( i );
++                if ( ++i == 50  ) {
++                         printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n",
++                                         pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 );
++                         break;
++                }
++       }
++}
++
++static bool clear_opr( void )
++{
++       int i = 10000;
++       bool is_clear;
++       do {
++                Ser0UDCCS0 = UDCCS0_SO;
++                is_clear  = ! ( Ser0UDCCS0 & UDCCS0_OPR );
++                if ( i-- <= 0 ) {
++                         printk( "%sclear_opr(): failed\n", pszMe );
++                         break;
++                }
++       } while( ! is_clear );
++       return is_clear;
++}
++
++
++
++
++
++/* end usb_ep0.c */
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_recv.c
+@@ -0,0 +1,205 @@
++/*
++ * Generic receive layer for the SA1100 USB client function
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * This code was loosely inspired by the original version which was
++ * Copyright (c) Compaq Computer Corporation, 1998-1999
++ *
++ * 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.
++ *
++ * This is still work in progress...
++ *
++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
++ */
++
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++
++#include "sa1100_usb.h"
++#include "usb_ctl.h"
++
++
++static char *ep1_buf;
++static int ep1_len;
++static usb_callback_t ep1_callback;
++static char *ep1_curdmabuf;
++static dma_addr_t ep1_curdmapos;
++static int ep1_curdmalen;
++static int ep1_remain;
++static int dmachn_rx;
++static int rx_pktsize;
++
++static int naking;
++
++static void
++ep1_start(void)
++{
++      sa1100_dma_flush_all(dmachn_rx);
++      if (!ep1_curdmalen) {
++              ep1_curdmalen = rx_pktsize;
++              if (ep1_curdmalen > ep1_remain)
++                      ep1_curdmalen = ep1_remain;
++              ep1_curdmapos = pci_map_single(NULL, ep1_curdmabuf, ep1_curdmalen,
++                                             PCI_DMA_FROMDEVICE);
++      }
++      sa1100_dma_queue_buffer(dmachn_rx, NULL, ep1_curdmapos, ep1_curdmalen);
++      if ( naking ) {
++              /* turn off NAK of OUT packets, if set */
++              UDC_flip( Ser0UDCCS1, UDCCS1_RPC );
++              naking = 0;
++      }
++}
++
++static void
++ep1_done(int flag)
++{
++      int size = ep1_len - ep1_remain;
++
++      if (!ep1_len)
++              return;
++      if (ep1_curdmalen)
++              pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++                               PCI_DMA_FROMDEVICE);
++      ep1_len = ep1_curdmalen = 0;
++      if (ep1_callback) {
++              ep1_callback(flag, size);
++      }
++}
++
++void
++ep1_state_change_notify( int new_state )
++{
++
++}
++
++void
++ep1_stall( void )
++{
++      /* SET_FEATURE force stall at UDC */
++      UDC_set( Ser0UDCCS1, UDCCS1_FST );
++}
++
++int
++ep1_init(int chn)
++{
++      desc_t * pd = sa1100_usb_get_descriptor_ptr();
++      rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
++      dmachn_rx = chn;
++      sa1100_dma_flush_all(dmachn_rx);
++      ep1_done(-EAGAIN);
++      return 0;
++}
++
++void
++ep1_reset(void)
++{
++      desc_t * pd = sa1100_usb_get_descriptor_ptr();
++      rx_pktsize = __le16_to_cpu( pd->b.ep1.wMaxPacketSize );
++      sa1100_dma_flush_all(dmachn_rx);
++      UDC_clear(Ser0UDCCS1, UDCCS1_FST);
++      ep1_done(-EINTR);
++}
++
++void
++ep1_int_hndlr(int udcsr)
++{
++      dma_addr_t dma_addr;
++      unsigned int len;
++      int status = Ser0UDCCS1;
++
++      if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking );
++
++      if (status & UDCCS1_RPC) {
++
++              if (!ep1_curdmalen) {
++                      printk("usb_recv: RPC for non-existent buffer\n");
++                      naking=1;
++                      return;
++              }
++
++              sa1100_dma_stop(dmachn_rx);
++
++              if (status & UDCCS1_SST) {
++                      printk("usb_recv: stall sent OMP=%d\n",Ser0UDCOMP);
++                      UDC_flip(Ser0UDCCS1, UDCCS1_SST);
++                      ep1_done(-EIO); // UDC aborted current transfer, so we do
++                      return;
++              }
++
++              if (status & UDCCS1_RPE) {
++                  printk("usb_recv: RPError %x\n", status);
++                      UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
++                      ep1_done(-EIO);
++                      return;
++              }
++
++              sa1100_dma_get_current(dmachn_rx, NULL, &dma_addr);
++              pci_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++                               PCI_DMA_FROMDEVICE);
++              len = dma_addr - ep1_curdmapos;
++              if (len < ep1_curdmalen) {
++                      char *buf = ep1_curdmabuf + len;
++                      while (Ser0UDCCS1 & UDCCS1_RNE) {
++                              if (len >= ep1_curdmalen) {
++                                      printk("usb_recv: too much data in fifo\n");
++                                      break;
++                              }
++                              *buf++ = Ser0UDCDR;
++                              len++;
++                      }
++              } else if (Ser0UDCCS1 & UDCCS1_RNE) {
++                      printk("usb_recv: fifo screwed, shouldn't contain data\n");
++                      len = 0;
++              }
++              ep1_curdmalen = 0;  /* dma unmap already done */
++              ep1_remain -= len;
++              naking = 1;
++              ep1_done((len) ? 0 : -EPIPE);
++      }
++      /* else, you can get here if we are holding NAK */
++}
++
++int
++sa1100_usb_recv(char *buf, int len, usb_callback_t callback)
++{
++      int flags;
++
++      if (ep1_len)
++              return -EBUSY;
++
++      local_irq_save(flags);
++      ep1_buf = buf;
++      ep1_len = len;
++      ep1_callback = callback;
++      ep1_remain = len;
++      ep1_curdmabuf = buf;
++      ep1_curdmalen = 0;
++      ep1_start();
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++EXPORT_SYMBOL(sa1100_usb_recv);
++
++void
++sa1100_usb_recv_reset(void)
++{
++      ep1_reset();
++}
++
++EXPORT_SYMBOL(sa1100_usb_recv_reset);
++
++void
++sa1100_usb_recv_stall(void)
++{
++      ep1_stall();
++}
++
++EXPORT_SYMBOL(sa1100_usb_recv_stall);
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_send.c
+@@ -0,0 +1,205 @@
++/*
++ * Generic xmit layer for the SA1100 USB client function
++ * Copyright (c) 2001 by Nicolas Pitre
++ *
++ * This code was loosely inspired by the original version which was
++ * Copyright (c) Compaq Computer Corporation, 1998-1999
++ *
++ * 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.
++ *
++ * This is still work in progress...
++ *
++ * Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
++ * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware
++ *            bug, I think. green@iXcelerator.com
++ */
++
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/errno.h>
++#include <linux/delay.h> // for the massive_attack hack 28Feb01ww
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++#include <asm/byteorder.h>
++
++#include "sa1100_usb.h"
++#include "usb_ctl.h"
++
++
++static char *ep2_buf;
++static int ep2_len;
++static usb_callback_t ep2_callback;
++static dma_addr_t ep2_dma;
++static dma_addr_t ep2_curdmapos;
++static int ep2_curdmalen;
++static int ep2_remain;
++static int dmachn_tx;
++static int tx_pktsize;
++
++/* device state is changing, async */
++void
++ep2_state_change_notify( int new_state )
++{
++}
++
++/* set feature stall executing, async */
++void
++ep2_stall( void )
++{
++      UDC_set( Ser0UDCCS2, UDCCS2_FST );  /* force stall at UDC */
++}
++
++static void
++ep2_start(void)
++{
++      if (!ep2_len)
++              return;
++
++      ep2_curdmalen = tx_pktsize;
++      if (ep2_curdmalen > ep2_remain)
++              ep2_curdmalen = ep2_remain;
++
++      /* must do this _before_ queue buffer.. */
++      UDC_flip( Ser0UDCCS2,UDCCS2_TPC );  /* stop NAKing IN tokens */
++      UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++
++      /* Remove if never seen...8Mar01ww */
++      {
++               int massive_attack = 20;
++               while ( Ser0UDCIMP != ep2_curdmalen-1 && massive_attack-- ) {
++                        printk( "usbsnd: Oh no you don't! Let me spin..." );
++                        udelay( 500 );
++                        printk( "and try again...\n" );
++                        UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++               }
++               if ( massive_attack != 20 ) {
++                        if ( Ser0UDCIMP != ep2_curdmalen-1 )
++                                 printk( "usbsnd: Massive attack FAILED :-( %d\n",
++                                                 20 - massive_attack );
++                        else
++                                 printk( "usbsnd: Massive attack WORKED :-) %d\n",
++                                                 20 - massive_attack );
++               }
++      }
++      /* End remove if never seen... 8Mar01ww */
++
++      Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug
++      sa1100_dma_queue_buffer(dmachn_tx, NULL, ep2_curdmapos, ep2_curdmalen);
++}
++
++static void
++ep2_done(int flag)
++{
++      int size = ep2_len - ep2_remain;
++      if (ep2_len) {
++              pci_unmap_single(NULL, ep2_dma, ep2_len, PCI_DMA_TODEVICE);
++              ep2_len = 0;
++              if (ep2_callback)
++                      ep2_callback(flag, size);
++      }
++}
++
++int
++ep2_init(int chn)
++{
++      desc_t * pd = sa1100_usb_get_descriptor_ptr();
++      tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize );
++      dmachn_tx = chn;
++      sa1100_dma_flush_all(dmachn_tx);
++      ep2_done(-EAGAIN);
++      return 0;
++}
++
++void
++ep2_reset(void)
++{
++      desc_t * pd = sa1100_usb_get_descriptor_ptr();
++      tx_pktsize = __le16_to_cpu( pd->b.ep2.wMaxPacketSize );
++      UDC_clear(Ser0UDCCS2, UDCCS2_FST);
++      sa1100_dma_flush_all(dmachn_tx);
++      ep2_done(-EINTR);
++}
++
++void
++ep2_int_hndlr(int udcsr)
++{
++      int status = Ser0UDCCS2;
++
++      if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug.
++              Ser0UDCAR = usbd_info.address;
++
++      UDC_flip(Ser0UDCCS2, UDCCS2_SST);
++
++      if (status & UDCCS2_TPC) {
++              sa1100_dma_flush_all(dmachn_tx);
++
++              if (status & (UDCCS2_TPE | UDCCS2_TUR)) {
++                      printk("usb_send: transmit error %x\n", status);
++                      ep2_done(-EIO);
++              } else {
++#if 1 // 22Feb01ww/Oleg
++                      ep2_curdmapos += ep2_curdmalen;
++                      ep2_remain -= ep2_curdmalen;
++#else
++                      ep2_curdmapos += Ser0UDCIMP + 1; // this is workaround
++                      ep2_remain -= Ser0UDCIMP + 1;    // for case when setting of Ser0UDCIMP was failed
++#endif
++
++                      if (ep2_remain != 0) {
++                              ep2_start();
++                      } else {
++                              ep2_done(0);
++                      }
++              }
++      } else {
++              printk("usb_send: Not TPC: UDCCS2 = %x\n", status);
++      }
++}
++
++int
++sa1100_usb_send(char *buf, int len, usb_callback_t callback)
++{
++      int flags;
++
++      if (usbd_info.state != USB_STATE_CONFIGURED)
++              return -ENODEV;
++
++      if (ep2_len)
++              return -EBUSY;
++
++      local_irq_save(flags);
++      ep2_buf = buf;
++      ep2_len = len;
++      ep2_dma = pci_map_single(NULL, buf, len, PCI_DMA_TODEVICE);
++      ep2_callback = callback;
++      ep2_remain = len;
++      ep2_curdmapos = ep2_dma;
++      ep2_start();
++      local_irq_restore(flags);
++
++      return 0;
++}
++
++
++void
++sa1100_usb_send_reset(void)
++{
++      ep2_reset();
++}
++
++int sa1100_usb_xmitter_avail( void )
++{
++      if (usbd_info.state != USB_STATE_CONFIGURED)
++              return -ENODEV;
++      if (ep2_len)
++              return -EBUSY;
++      return 0;
++}
++
++
++EXPORT_SYMBOL(sa1100_usb_xmitter_avail);
++EXPORT_SYMBOL(sa1100_usb_send);
++EXPORT_SYMBOL(sa1100_usb_send_reset);
+--- linux-2.4.27/arch/arm/mm/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/Makefile
+@@ -39,6 +39,8 @@
+ p-$(CONFIG_CPU_ARM925T)       += proc-arm925.o
+ p-$(CONFIG_CPU_ARM926T)       += proc-arm926.o
+ p-$(CONFIG_CPU_ARM1020)       += proc-arm1020.o
++p-$(CONFIG_CPU_ARM1020E) += proc-arm1020E.o
++p-$(CONFIG_CPU_ARM1022)       += proc-arm1022.o
+ p-$(CONFIG_CPU_ARM1026)       += proc-arm1026.o
+ p-$(CONFIG_CPU_SA110) += proc-sa110.o
+ p-$(CONFIG_CPU_SA1100)        += proc-sa110.o
+--- linux-2.4.27/arch/arm/mm/alignment.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/alignment.c
+@@ -11,7 +11,6 @@
+ #include <linux/config.h>
+ #include <linux/compiler.h>
+ #include <linux/signal.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -19,7 +18,6 @@
+ #include <linux/ptrace.h>
+ #include <linux/mman.h>
+ #include <linux/mm.h>
+-#include <linux/interrupt.h>
+ #include <linux/proc_fs.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+@@ -30,13 +28,11 @@
+ #include <asm/pgtable.h>
+ #include <asm/unaligned.h>
+-extern void
+-do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
+-          int error_code, struct pt_regs *regs);
++#include "fault.h"
+ /*
+  * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998
+- * /proc/sys/debug/alignment, modified and integrated into
++ * /proc/cpu/alignment, modified and integrated into
+  * Linux 2.1 by Russell King
+  *
+  * Speed optimisations and better fault handling by Russell King.
+@@ -130,31 +126,6 @@
+       return count;
+ }
+-/*
+- * This needs to be done after sysctl_init, otherwise sys/ will be
+- * overwritten.  Actually, this shouldn't be in sys/ at all since
+- * it isn't a sysctl, and it doesn't contain sysctl information.
+- * We now locate it in /proc/cpu/alignment instead.
+- */
+-static int __init alignment_init(void)
+-{
+-      struct proc_dir_entry *res;
+-
+-      res = proc_mkdir("cpu", NULL);
+-      if (!res)
+-              return -ENOMEM;
+-
+-      res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
+-      if (!res)
+-              return -ENOMEM;
+-
+-      res->read_proc = proc_alignment_read;
+-      res->write_proc = proc_alignment_write;
+-
+-      return 0;
+-}
+-
+-__initcall(alignment_init);
+ #endif /* CONFIG_PROC_FS */
+ union offset_union {
+@@ -429,7 +400,7 @@
+        * For alignment faults on the ARM922T/ARM920T the MMU  makes
+        * the FSR (and hence addr) equal to the updated base address
+        * of the multiple access rather than the restored value.
+-       * Switch this messsage off if we've got a ARM92[02], otherwise
++       * Switch this message off if we've got a ARM92[02], otherwise
+        * [ls]dm alignment faults are noisy!
+        */
+ #if !(defined CONFIG_CPU_ARM922T)  && !(defined CONFIG_CPU_ARM920T)
+@@ -486,7 +457,8 @@
+       return TYPE_ERROR;
+ }
+-int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs)
++static int
++do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       union offset_union offset;
+       unsigned long instr, instrptr;
+@@ -541,7 +513,7 @@
+                       case SHIFT_RORRRX:
+                               if (shiftval == 0) {
+                                       offset.un >>= 1;
+-                                      if (regs->ARM_cpsr & CC_C_BIT)
++                                      if (regs->ARM_cpsr & PSR_C_BIT)
+                                               offset.un |= 1 << 31;
+                               } else
+                                       offset.un = offset.un >> shiftval |
+@@ -577,7 +549,7 @@
+       /*
+        * We got a fault - fix it up, or die.
+        */
+-      do_bad_area(current, current->mm, addr, error_code, regs);
++      do_bad_area(current, current->mm, addr, fsr, regs);
+       return 0;
+  bad:
+@@ -594,8 +566,8 @@
+       if (ai_usermode & 1)
+               printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%08lx "
+-                     "Address=0x%08lx Code 0x%02x\n", current->comm,
+-                      current->pid, instrptr, instr, addr, error_code);
++                     "Address=0x%08lx FSR 0x%03x\n", current->comm,
++                      current->pid, instrptr, instr, addr, fsr);
+       if (ai_usermode & 2)
+               goto fixup;
+@@ -607,3 +579,34 @@
+       return 0;
+ }
++
++/*
++ * This needs to be done after sysctl_init, otherwise sys/ will be
++ * overwritten.  Actually, this shouldn't be in sys/ at all since
++ * it isn't a sysctl, and it doesn't contain sysctl information.
++ * We now locate it in /proc/cpu/alignment instead.
++ */
++static int __init alignment_init(void)
++{
++#ifdef CONFIG_PROC_FS
++      struct proc_dir_entry *res;
++
++      res = proc_mkdir("cpu", NULL);
++      if (!res)
++              return -ENOMEM;
++
++      res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res);
++      if (!res)
++              return -ENOMEM;
++
++      res->read_proc = proc_alignment_read;
++      res->write_proc = proc_alignment_write;
++#endif
++
++      hook_fault_code(1, do_alignment, SIGILL, "alignment exception");
++      hook_fault_code(3, do_alignment, SIGILL, "alignment exception");
++
++      return 0;
++}
++
++__initcall(alignment_init);
+--- linux-2.4.27/arch/arm/mm/fault-armv.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/fault-armv.c
+@@ -2,116 +2,90 @@
+  *  linux/arch/arm/mm/fault-armv.c
+  *
+  *  Copyright (C) 1995  Linus Torvalds
+- *  Modifications for ARM processor (c) 1995-2001 Russell King
++ *  Modifications for ARM processor (c) 1995-2003 Russell King
+  *
+  * 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/signal.h>
+ #include <linux/sched.h>
+ #include <linux/kernel.h>
+-#include <linux/errno.h>
+-#include <linux/string.h>
+ #include <linux/types.h>
+ #include <linux/ptrace.h>
+-#include <linux/mman.h>
+ #include <linux/mm.h>
+-#include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+ #include <linux/bitops.h>
+ #include <linux/init.h>
+-#include <asm/system.h>
+-#include <asm/uaccess.h>
+ #include <asm/pgalloc.h>
+ #include <asm/pgtable.h>
++#include <asm/io.h>
+-extern void show_pte(struct mm_struct *mm, unsigned long addr);
+-extern int do_page_fault(unsigned long addr, int error_code,
+-                       struct pt_regs *regs);
+-extern int do_translation_fault(unsigned long addr, int error_code,
+-                              struct pt_regs *regs);
+-extern void do_bad_area(struct task_struct *tsk, struct mm_struct *mm,
+-                      unsigned long addr, int error_code,
+-                      struct pt_regs *regs);
+-
+-#ifdef CONFIG_ALIGNMENT_TRAP
+-extern int do_alignment(unsigned long addr, int error_code, struct pt_regs *regs);
+-#else
+-#define do_alignment do_bad
+-#endif
+-
++#include "fault.h"
+ /*
+  * Some section permission faults need to be handled gracefully.
+  * They can happen due to a __{get,put}_user during an oops.
+  */
+ static int
+-do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs)
++do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       struct task_struct *tsk = current;
+-      do_bad_area(tsk, tsk->active_mm, addr, error_code, regs);
++      do_bad_area(tsk, tsk->active_mm, addr, fsr, regs);
+       return 0;
+ }
+ /*
+- * Hook for things that need to trap external faults.  Note that
+- * we don't guarantee that this will be the final version of the
+- * interface.
+- */
+-int (*external_fault)(unsigned long addr, struct pt_regs *regs);
+-
+-static int
+-do_external_fault(unsigned long addr, int error_code, struct pt_regs *regs)
+-{
+-      if (external_fault)
+-              return external_fault(addr, regs);
+-      return 1;
+-}
+-
+-/*
+  * This abort handler always returns "fault".
+  */
+ static int
+-do_bad(unsigned long addr, int error_code, struct pt_regs *regs)
++do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       return 1;
+ }
+-static const struct fsr_info {
+-      int     (*fn)(unsigned long addr, int error_code, struct pt_regs *regs);
++static struct fsr_info {
++      int     (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
+       int     sig;
+       const char *name;
+ } fsr_info[] = {
+       { do_bad,               SIGSEGV, "vector exception"                },
+-      { do_alignment,         SIGILL,  "alignment exception"             },
++      { do_bad,               SIGILL,  "alignment exception"             },
+       { do_bad,               SIGKILL, "terminal exception"              },
+-      { do_alignment,         SIGILL,  "alignment exception"             },
+-      { do_external_fault,    SIGBUS,  "external abort on linefetch"     },
++      { do_bad,               SIGILL,  "alignment exception"             },
++      { do_bad,               SIGBUS,  "external abort on linefetch"     },
+       { do_translation_fault, SIGSEGV, "section translation fault"       },
+-      { do_external_fault,    SIGBUS,  "external abort on linefetch"     },
++      { do_bad,               SIGBUS,  "external abort on linefetch"     },
+       { do_page_fault,        SIGSEGV, "page translation fault"          },
+-      { do_external_fault,    SIGBUS,  "external abort on non-linefetch" },
++      { do_bad,               SIGBUS,  "external abort on non-linefetch" },
+       { do_bad,               SIGSEGV, "section domain fault"            },
+-      { do_external_fault,    SIGBUS,  "external abort on non-linefetch" },
++      { do_bad,               SIGBUS,  "external abort on non-linefetch" },
+       { do_bad,               SIGSEGV, "page domain fault"               },
+       { do_bad,               SIGBUS,  "external abort on translation"   },
+       { do_sect_fault,        SIGSEGV, "section permission fault"        },
+       { do_bad,               SIGBUS,  "external abort on translation"   },
+-      { do_page_fault,        SIGSEGV, "page permission fault"           }
++      { do_page_fault,        SIGSEGV, "page permission fault"           },
+ };
++void __init
++hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
++              int sig, const char *name)
++{
++      if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) {
++              fsr_info[nr].fn   = fn;
++              fsr_info[nr].sig  = sig;
++              fsr_info[nr].name = name;
++      }
++}
++
+ /*
+  * Dispatch a data abort to the relevant handler.
+  */
+ asmlinkage void
+-do_DataAbort(unsigned long addr, int error_code, struct pt_regs *regs, int fsr)
++do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       const struct fsr_info *inf = fsr_info + (fsr & 15);
+-      if (!inf->fn(addr, error_code, regs))
++      if (!inf->fn(addr, fsr, regs))
+               return;
+       printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
+@@ -127,25 +101,28 @@
+       do_translation_fault(addr, 0, regs);
+ }
++static unsigned long shared_pte_mask = L_PTE_CACHEABLE;
++
+ /*
+  * We take the easy way out of this problem - we make the
+  * PTE uncacheable.  However, we leave the write buffer on.
+  */
+-static void adjust_pte(struct vm_area_struct *vma, unsigned long address)
++static int adjust_pte(struct vm_area_struct *vma, unsigned long address)
+ {
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *pte, entry;
++      int ret = 0;
+       pgd = pgd_offset(vma->vm_mm, address);
+       if (pgd_none(*pgd))
+-              return;
++              goto no_pgd;
+       if (pgd_bad(*pgd))
+               goto bad_pgd;
+       pmd = pmd_offset(pgd, address);
+       if (pmd_none(*pmd))
+-              return;
++              goto no_pmd;
+       if (pmd_bad(*pmd))
+               goto bad_pmd;
+@@ -156,33 +133,38 @@
+        * If this page isn't present, or is already setup to
+        * fault (ie, is old), we can safely ignore any issues.
+        */
+-      if (pte_present(entry) && pte_val(entry) & L_PTE_CACHEABLE) {
++      if (pte_present(entry) && pte_val(entry) & shared_pte_mask) {
+               flush_cache_page(vma, address);
+-              pte_val(entry) &= ~L_PTE_CACHEABLE;
++              pte_val(entry) &= ~shared_pte_mask;
+               set_pte(pte, entry);
+               flush_tlb_page(vma, address);
++              ret = 1;
+       }
+-      return;
++      return ret;
+ bad_pgd:
+       pgd_ERROR(*pgd);
+       pgd_clear(pgd);
+-      return;
++no_pgd:
++      return 0;
+ bad_pmd:
+       pmd_ERROR(*pmd);
+       pmd_clear(pmd);
+-      return;
++no_pmd:
++      return 0;
+ }
+ static void
+-make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page)
++make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page, int dirty)
+ {
+       struct vm_area_struct *mpnt;
+       struct mm_struct *mm = vma->vm_mm;
+-      unsigned long pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
++      unsigned long pgoff;
+       int aliases = 0;
++      pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT);
++
+       /*
+        * If we have any shared mappings that are in the same mm
+        * space, then we need to handle them specially to maintain
+@@ -194,7 +176,7 @@
+               /*
+                * If this VMA is not in our MM, we can ignore it.
+-               * Note that we intentionally don't mask out the VMA
++               * Note that we intentionally mask out the VMA
+                * that we are fixing up.
+                */
+               if (mpnt->vm_mm != mm || mpnt == vma)
+@@ -210,14 +192,17 @@
+               if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT)
+                       continue;
++              off = mpnt->vm_start + (off << PAGE_SHIFT);
++
+               /*
+                * Ok, it is within mpnt.  Fix it up.
+                */
+-              adjust_pte(mpnt, mpnt->vm_start + (off << PAGE_SHIFT));
+-              aliases ++;
++              aliases += adjust_pte(mpnt, off);
+       }
+       if (aliases)
+               adjust_pte(vma, addr);
++      else if (dirty)
++              flush_cache_page(vma, addr);
+ }
+ /*
+@@ -242,11 +227,85 @@
+               return;
+       page = pfn_to_page(pfn);
+       if (page->mapping) {
+-              if (test_and_clear_bit(PG_dcache_dirty, &page->flags)) {
++              int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags);
++
++              if (dirty) {
+                       unsigned long kvirt = (unsigned long)page_address(page);
+                       cpu_cache_clean_invalidate_range(kvirt, kvirt + PAGE_SIZE, 0);
+               }
+-              make_coherent(vma, addr, page);
++              make_coherent(vma, addr, page, dirty);
++      }
++}
++
++/*
++ * Check whether the write buffer has physical address aliasing
++ * issues.  If it has, we need to avoid them for the case where
++ * we have several shared mappings of the same object in user
++ * space.
++ */
++static int __init check_writebuffer(unsigned long *p1, unsigned long *p2)
++{
++      register unsigned long zero = 0, one = 1, val;
++
++      mb();
++      *p1 = one;
++      mb();
++      *p2 = zero;
++      mb();
++      val = *p1;
++      mb();
++      return val != zero;
++}
++
++static inline void *map_page(struct page *page)
++{
++      void *map;
++
++      map = __ioremap(page_to_phys(page), PAGE_SIZE, L_PTE_BUFFERABLE);
++      if (map)
++              get_page(page);
++      return map;
++}
++
++static inline void unmap_page(void *map)
++{
++      iounmap(map);
++}
++
++void __init check_writebuffer_bugs(void)
++{
++      struct page *page;
++      const char *reason;
++      unsigned long v = 1;
++
++      printk(KERN_INFO "CPU: Testing write buffer: ");
++
++      page = alloc_page(GFP_KERNEL);
++      if (page) {
++              unsigned long *p1, *p2;
++
++              p1 = map_page(page);
++              p2 = map_page(page);
++
++              if (p1 && p2) {
++                      v = check_writebuffer(p1, p2);
++                      reason = "enabling work-around";
++              } else {
++                      reason = "unable to map memory\n";
++              }
++
++              unmap_page(p1);
++              unmap_page(p2);
++              put_page(page);
++      } else {
++              reason = "unable to grab page\n";
++      }
++
++      if (v) {
++              printk("FAIL - %s\n", reason);
++              shared_pte_mask |= L_PTE_BUFFERABLE;
++      } else {
++              printk("pass\n");
+       }
+ }
+--- linux-2.4.27/arch/arm/mm/fault-common.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/fault-common.c
+@@ -11,21 +11,17 @@
+ #include <linux/config.h>
+ #include <linux/signal.h>
+ #include <linux/sched.h>
+-#include <linux/kernel.h>
+-#include <linux/errno.h>
+ #include <linux/string.h>
+-#include <linux/types.h>
+ #include <linux/ptrace.h>
+-#include <linux/mman.h>
+ #include <linux/mm.h>
+ #include <linux/interrupt.h>
+-#include <linux/proc_fs.h>
+ #include <linux/init.h>
+ #include <asm/system.h>
+-#include <asm/uaccess.h>
+ #include <asm/pgtable.h>
+-#include <asm/unaligned.h>
++#include <asm/uaccess.h>
++
++#include "fault.h"
+ #ifdef CONFIG_CPU_26
+ #define FAULT_CODE_WRITE      0x02
+@@ -34,13 +30,11 @@
+ #define READ_FAULT(m)         (!((m) & FAULT_CODE_WRITE))
+ #else
+ /*
+- * On 32-bit processors, we define "mode" to be zero when reading,
+- * non-zero when writing.  This now ties up nicely with the polarity
+- * of the 26-bit machines, and also means that we avoid the horrible
+- * gcc code for "int val = !other_val;".
++ * "code" is actually the FSR register.  Bit 11 set means the
++ * instruction was performing a write.
+  */
+-#define DO_COW(m)             (m)
+-#define READ_FAULT(m)         (!(m))
++#define DO_COW(code)          ((code) & (1 << 11))
++#define READ_FAULT(code)      (!DO_COW(code))
+ #endif
+ /*
+@@ -54,16 +48,17 @@
+       if (!mm)
+               mm = &init_mm;
+-      printk(KERN_ALERT "mm = %p pgd = %p\n", mm, mm->pgd);
+-
+       fs = get_fs();
+       set_fs(get_ds());
++
+       do {
+-              pgd_t pg, *pgd = pgd_offset(mm, addr);
++              pgd_t pg, *pgd;
+               pmd_t pm, *pmd;
+               pte_t pt, *pte;
+-              printk(KERN_ALERT "*pgd = ");
++              printk(KERN_ALERT "pgd = %p\n", mm->pgd);
++              pgd = pgd_offset(mm, addr);
++              printk(KERN_ALERT "[%08lx] *pgd=", addr);
+               if (__get_user(pgd_val(pg), (unsigned long *)pgd)) {
+                       printk("(faulted)");
+@@ -122,7 +117,7 @@
+  * Oops.  The kernel tried to access some page that wasn't present.
+  */
+ static void
+-__do_kernel_fault(struct mm_struct *mm, unsigned long addr, int error_code,
++__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
+                 struct pt_regs *regs)
+ {
+       unsigned long fixup;
+@@ -148,7 +143,7 @@
+               "paging request", addr);
+       show_pte(mm, addr);
+-      die("Oops", regs, error_code);
++      die("Oops", regs, fsr);
+       do_exit(SIGKILL);
+ }
+@@ -157,20 +152,20 @@
+  * User mode accesses just cause a SIGSEGV
+  */
+ static void
+-__do_user_fault(struct task_struct *tsk, unsigned long addr, int error_code,
+-              int code, struct pt_regs *regs)
++__do_user_fault(struct task_struct *tsk, unsigned long addr,
++              unsigned int fsr, int code, struct pt_regs *regs)
+ {
+       struct siginfo si;
+ #ifdef CONFIG_DEBUG_USER
+       printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, "
+              "lr=0x%08lx (bad address=0x%08lx, code %d)\n",
+-             tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, error_code);
++             tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, fsr);
+       show_regs(regs);
+ #endif
+       tsk->thread.address = addr;
+-      tsk->thread.error_code = error_code;
++      tsk->thread.error_code = fsr;
+       tsk->thread.trap_no = 14;
+       si.si_signo = SIGSEGV;
+       si.si_errno = 0;
+@@ -181,20 +176,20 @@
+ void
+ do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
+-          int error_code, struct pt_regs *regs)
++          unsigned int fsr, struct pt_regs *regs)
+ {
+       /*
+        * If we are in kernel mode at this point, we
+        * have no context to handle this fault with.
+        */
+       if (user_mode(regs))
+-              __do_user_fault(tsk, addr, error_code, SEGV_MAPERR, regs);
++              __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs);
+       else
+-              __do_kernel_fault(mm, addr, error_code, regs);
++              __do_kernel_fault(mm, addr, fsr, regs);
+ }
+ static int
+-__do_page_fault(struct mm_struct *mm, unsigned long addr, int error_code,
++__do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
+               struct task_struct *tsk)
+ {
+       struct vm_area_struct *vma;
+@@ -212,7 +207,7 @@
+        * memory access, so we can handle it.
+        */
+ good_area:
+-      if (READ_FAULT(error_code)) /* read? */
++      if (READ_FAULT(fsr)) /* read? */
+               mask = VM_READ|VM_EXEC;
+       else
+               mask = VM_WRITE;
+@@ -227,7 +222,7 @@
+        * than endlessly redo the fault.
+        */
+ survive:
+-      fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(error_code));
++      fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(fsr));
+       /*
+        * Handle the "normal" cases first - successful and sigbus
+@@ -260,7 +255,7 @@
+       return fault;
+ }
+-int do_page_fault(unsigned long addr, int error_code, struct pt_regs *regs)
++int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+ {
+       struct task_struct *tsk;
+       struct mm_struct *mm;
+@@ -277,7 +272,7 @@
+               goto no_context;
+       down_read(&mm->mmap_sem);
+-      fault = __do_page_fault(mm, addr, error_code, tsk);
++      fault = __do_page_fault(mm, addr, fsr, tsk);
+       up_read(&mm->mmap_sem);
+       /*
+@@ -308,7 +303,7 @@
+               printk("VM: killing process %s\n", tsk->comm);
+               do_exit(SIGKILL);
+       } else
+-              __do_user_fault(tsk, addr, error_code, fault == -1 ?
++              __do_user_fault(tsk, addr, fsr, fault == -1 ?
+                               SEGV_ACCERR : SEGV_MAPERR, regs);
+       return 0;
+@@ -323,7 +318,7 @@
+        * or user mode.
+        */
+       tsk->thread.address = addr;
+-      tsk->thread.error_code = error_code;
++      tsk->thread.error_code = fsr;
+       tsk->thread.trap_no = 14;
+       force_sig(SIGBUS, tsk);
+ #ifdef CONFIG_DEBUG_USER
+@@ -336,7 +331,7 @@
+               return 0;
+ no_context:
+-      __do_kernel_fault(mm, addr, error_code, regs);
++      __do_kernel_fault(mm, addr, fsr, regs);
+       return 0;
+ }
+@@ -357,21 +352,23 @@
+  * interrupt or a critical region, and should only copy the information
+  * from the master page table, nothing more.
+  */
+-int do_translation_fault(unsigned long addr, int error_code, struct pt_regs *regs)
++int do_translation_fault(unsigned long addr, unsigned int fsr,
++                       struct pt_regs *regs)
+ {
+       struct task_struct *tsk;
+-      struct mm_struct *mm;
+       int offset;
+       pgd_t *pgd, *pgd_k;
+       pmd_t *pmd, *pmd_k;
+       if (addr < TASK_SIZE)
+-              return do_page_fault(addr, error_code, regs);
++              return do_page_fault(addr, fsr, regs);
+       offset = __pgd_offset(addr);
+       /*
+        * FIXME: CP15 C1 is write only on ARMv3 architectures.
++       * You really need to read the value in the page table
++       * register, not a copy.
+        */
+       pgd = cpu_get_pgd() + offset;
+       pgd_k = init_mm.pgd + offset;
+@@ -395,8 +392,7 @@
+ bad_area:
+       tsk = current;
+-      mm  = tsk->active_mm;
+-      do_bad_area(tsk, mm, addr, error_code, regs);
++      do_bad_area(tsk, tsk->active_mm, addr, fsr, regs);
+       return 0;
+ }
+--- /dev/null
++++ linux-2.4.27/arch/arm/mm/fault.h
+@@ -0,0 +1,9 @@
++void do_bad_area(struct task_struct *tsk, struct mm_struct *mm,
++               unsigned long addr, unsigned int fsr, struct pt_regs *regs);
++
++void show_pte(struct mm_struct *mm, unsigned long addr);
++
++int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
++
++int do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
++
+--- linux-2.4.27/arch/arm/mm/init.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/init.c
+@@ -9,7 +9,6 @@
+  */
+ #include <linux/config.h>
+ #include <linux/signal.h>
+-#include <linux/sched.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -18,7 +17,6 @@
+ #include <linux/mman.h>
+ #include <linux/mm.h>
+ #include <linux/swap.h>
+-#include <linux/swapctl.h>
+ #include <linux/smp.h>
+ #include <linux/init.h>
+ #include <linux/bootmem.h>
+--- linux-2.4.27/arch/arm/mm/ioremap.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/ioremap.c
+@@ -144,7 +144,7 @@
+        */
+       offset = phys_addr & ~PAGE_MASK;
+       phys_addr &= PAGE_MASK;
+-      size = PAGE_ALIGN(last_addr) - phys_addr;
++      size = PAGE_ALIGN(last_addr + 1) - phys_addr;
+       /*
+        * Ok, go for it..
+--- linux-2.4.27/arch/arm/mm/mm-armv.c~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/mm-armv.c
+@@ -9,7 +9,6 @@
+  *
+  *  Page table sludge for ARM v3 and v4 processor architectures.
+  */
+-#include <linux/sched.h>
+ #include <linux/mm.h>
+ #include <linux/init.h>
+ #include <linux/bootmem.h>
+@@ -390,6 +389,9 @@
+       init_maps->bufferable = 0;
+       create_mapping(init_maps);
++
++      flush_cache_all();
++      flush_tlb_all();
+ }
+ /*
+--- linux-2.4.27/arch/arm/mm/proc-arm1020.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm1020.S
+@@ -65,18 +65,21 @@
+  *
+  * Returns:
+  *  r0 = address of abort
+- *  r1 != 0 if writing
+- *  r3 = FSR
++ *  r1 = FSR
++ *  r3 = corrupted
+  *  r4 = corrupted
+  */
+       .align  5
+ ENTRY(cpu_arm1020_data_abort)
+-      mrc     p15, 0, r3, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      ldr     r1, [r2]                        @ read aborted instruction
+-      and     r3, r3, #255
+-      tst     r1, r1, lsr #21                 @ C = bit 20
+-      sbc     r1, r1, r1                      @ r1 = C - 1
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
+       mov     pc, lr
+ /*
+@@ -170,10 +173,10 @@
+ #endif
+       subs    r3, r3, #1
+       cmp     r3, #0
+-      bge     2b                              @ entries 3F to 0
++      bhs     2b                              @ entries 3F to 0
+       subs    r1, r1, #1
+       cmp     r1, #0
+-      bge     1b                              @ segments 7 to 0
++      bhs     1b                              @ segments 7 to 0
+ #endif
+         
+ #ifndef CONFIG_CPU_ICACHE_DISABLE
+@@ -201,7 +204,7 @@
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+-      bgt     cpu_arm1020_cache_clean_invalidate_all_r2
++      bhi     cpu_arm1020_cache_clean_invalidate_all_r2
+       mcr     p15, 0, r3, c7, c10, 4
+ #ifndef CONFIG_CPU_DCACHE_DISABLE
+ 1:    mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
+@@ -214,7 +217,7 @@
+ #endif
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+ #endif
+ #ifndef CONFIG_CPU_ICACHE_DISABLE
+@@ -302,7 +305,7 @@
+ #endif
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+ #else
+       /* D cache off, but still drain the write buffer */
+       mcr     p15, 0, r0, c7, c10, 4          @ Drain write buffer
+@@ -328,7 +331,7 @@
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+-      bgt     cpu_arm1020_cache_clean_invalidate_all_r2
++      bhi     cpu_arm1020_cache_clean_invalidate_all_r2
+       mcr     p15, 0, r3, c7, c10, 4
+ #ifndef CONFIG_CPU_DCACHE_DISABLE
+ 1:    mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
+@@ -341,7 +344,7 @@
+ #endif
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+ #endif
+ #ifndef CONFIG_CPU_BPREDICT_DISABLE
+@@ -488,7 +491,7 @@
+       mov     r0, r0
+ #endif
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -541,10 +544,10 @@
+ #endif
+       subs    r3, r3, #1
+       cmp     r3, #0
+-      bge     2b                              @ entries 3F to 0
++      bhs     2b                              @ entries 3F to 0
+       subs    r1, r1, #1
+       cmp     r1, #0
+-      bge     1b                              @ segments 15 to 0
++      bhs     1b                              @ segments 15 to 0
+ #endif
+       mov     r1, #0
+@@ -603,7 +606,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+@@ -740,12 +743,12 @@
+       .type   cpu_arch_name, #object
+ cpu_arch_name:
+-      .asciz  "armv4"
++      .asciz  "armv5t"
+       .size   cpu_arch_name, . - cpu_arch_name
+       .type   cpu_elf_name, #object
+ cpu_elf_name:
+-      .asciz  "v4"
++      .asciz  "v5"
+       .size   cpu_elf_name, . - cpu_elf_name
+       .align
+@@ -753,9 +756,9 @@
+       .type   __arm1020_proc_info,#object
+ __arm1020_proc_info:
+-      .long   0x4100a200
+-      .long   0xff00fff0
+-      .long   0x00000c02                      @ mmuflags
++      .long   0x4104a200                      @ ARM 1020T (Architecture v5T)
++      .long   0xff0ffff0
++      .long   0x00000c0e                      @ mmuflags
+       b       __arm1020_setup
+       .long   cpu_arch_name
+       .long   cpu_elf_name
+--- /dev/null
++++ linux-2.4.27/arch/arm/mm/proc-arm1020E.S
+@@ -0,0 +1,718 @@
++/*
++ *  linux/arch/arm/mm/proc-arm1020E.S: MMU functions for ARM1020E
++ *
++ *  Copyright (C) 2000 ARM Limited
++ *  Copyright (C) 2000 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
++ * 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
++ *
++ *
++ * These are the low level assembler for performing cache and TLB
++ * functions on the arm1020E.
++ */
++#include <linux/linkage.h>
++#include <linux/config.h>
++#include <asm/assembler.h>
++#include <asm/constants.h>
++#include <asm/procinfo.h>
++#include <asm/hardware.h>
++
++/*
++ * This is the maximum size of an area which will be invalidated
++ * using the single invalidate entry instructions.  Anything larger
++ * than this, and we go for the whole cache.
++ *
++ * This value should be chosen such that we choose the cheapest
++ * alternative.
++ */
++#define MAX_AREA_SIZE 32768
++
++/*
++ * the cache line size of the I and D cache
++ */
++#define DCACHELINESIZE        32
++#define ICACHELINESIZE        32
++
++/*
++ * and the page size
++ */
++#define LOG2PAGESIZE  12      /* == 4096 Bytes */
++#define PAGESIZE      (1 << LOG2PAGESIZE)
++
++/*
++ * create some useful conditional macro definitions
++ * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback)
++ */
++#ifdef  CONFIG_CPU_DCACHE_DISABLE
++    #undef  CONFIG_CPU_DCACHE_WRITETHROUGH
++    #undef  CONFIG_CPU_DCACHE_WRITEBACK
++    #undef  CONFIG_CPU_DCACHE_ENABLE
++#else
++  #ifdef  CONFIG_CPU_DCACHE_WRITETHROUGH
++    #undef  CONFIG_CPU_DCACHE_WRITEBACK
++  #else
++    #define CONFIG_CPU_DCACHE_WRITEBACK
++  #endif
++    #define CONFIG_CPU_DCACHE_ENABLE
++#endif
++
++#ifdef  CONFIG_CPU_ICACHE_DISABLE
++    #undef  CONFIG_CPU_ICACHE_ENABLE
++#else
++    #define CONFIG_CPU_ICACHE_ENABLE
++#endif
++
++      .text
++
++/*
++ * cpu_arm1020E_data_abort()
++ *
++ * obtain information about current aborted instruction
++ * Note: we read user space.  This means we might cause a data
++ * abort here if the I-TLB and D-TLB aren't seeing the same
++ * picture.  Unfortunately, this does happen.  We live with it.
++ *
++ *  r2 = address of aborted instruction
++ *  r3 = cpsr
++ *
++ * Returns:
++ *  r0 = address of abort
++ *  r1 = FSR
++ *  r3 = corrupted
++ *  r4 = corrupted
++ */
++      .align  5
++ENTRY(cpu_arm1020E_data_abort)
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r0, c6, c0, 0           @ get FAR
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_check_bugs()
++ */
++ENTRY(cpu_arm1020E_check_bugs)
++      mrs     ip, cpsr
++      bic     ip, ip, #F_BIT
++      msr     cpsr, ip
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_proc_init()
++ */
++ENTRY(cpu_arm1020E_proc_init)
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_proc_fin()
++ */
++ENTRY(cpu_arm1020E_proc_fin)
++      stmfd   sp!, {lr}
++      mov     ip, #F_BIT | I_BIT | SVC_MODE
++      msr     cpsr_c, ip
++      bl      cpu_arm1020E_cache_clean_invalidate_all
++      mrc     p15, 0, r0, c1, c0, 0           @ ctrl register
++      bic     r0, r0, #0x1000                 @ ...i............
++      bic     r0, r0, #0x000e                 @ ............wca.
++      mcr     p15, 0, r0, c1, c0, 0           @ disable caches
++      ldmfd   sp!, {pc}
++
++/*
++ * cpu_arm1020E_reset(loc)
++ *
++ * Perform a soft reset of the system.        Put the CPU into the
++ * same state as it would be if it had been reset, and branch
++ * to what would be the reset vector.
++ *
++ * loc: location to jump to for soft reset
++ */
++      .align  5
++ENTRY(cpu_arm1020E_reset)
++      mov     ip, #0
++      mcr     p15, 0, ip, c7, c7, 0           @ invalidate I,D caches
++      mcr     p15, 0, ip, c7, c10, 4          @ drain WB
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I & D TLBs
++      mrc     p15, 0, ip, c1, c0, 0           @ ctrl register
++      bic     ip, ip, #0x000f                 @ ............wcam
++      bic     ip, ip, #0x1100                 @ ...i...s........
++      mcr     p15, 0, ip, c1, c0, 0           @ ctrl register
++      mov     pc, r0
++
++/*
++ * cpu_arm1020E_do_idle()
++ */
++      .align  5
++ENTRY(cpu_arm1020E_do_idle)
++      mcr     p15, 0, r0, c7, c0, 4           @ Wait for interrupt
++      mov     pc, lr
++
++/* ================================= CACHE ================================ */
++
++
++/*
++ * cpu_arm1020E_cache_clean_invalidate_all()
++ *
++ * clean and invalidate all cache lines
++ *
++ * Note:
++ *  1. we should preserve r0 and ip at all times
++ */
++      .align  5
++ENTRY(cpu_arm1020E_cache_clean_invalidate_all)
++      mov     r2, #1
++cpu_arm1020E_cache_clean_invalidate_all_r2:
++
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mov     r1, #0x0F << 5                  @ 16 segments
++1:    orr     r3, r1, #63 << 26               @ 64 entries
++2:    mcr     p15, 0, r3, c7, c14, 2          @ clean and invalidate D index
++      subs    r3, r3, #1 << 26
++      bcs     2b
++      subs    r1, r1, #1 << 5
++      bcs     1b                              @ segments 15 to 0
++#endif
++
++      mov     r1, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r1, c7, c6, 0           @ invalidate D cache
++#endif
++
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      teq     r2, #0
++      mcrne   p15, 0, r1, c7, c5, 0           @ invalidate I cache
++#endif
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_cache_clean_invalidate_range(start, end, flags)
++ *
++ * clean and invalidate all cache lines associated with this area of memory
++ *
++ * start: Area start address
++ * end:   Area end address
++ * flags: nonzero for I cache as well
++ */
++      .align  5
++ENTRY(cpu_arm1020E_cache_clean_invalidate_range)
++      bic     r0, r0, #DCACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      bhs     cpu_arm1020E_cache_clean_invalidate_all_r2
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      teq     r2, #0
++      mcrne   p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b                              @ unsigned lower or same - must include end point (r1)!
++
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_flush_ram_page(page)
++ *
++ * clean and invalidate all cache lines associated with this area of memory
++ *
++ * page: page to clean and invalidate
++ */
++      .align  5
++ENTRY(cpu_arm1020E_flush_ram_page)
++      mov     r1, #PAGESIZE
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      subs    r1, r1, #DCACHELINESIZE
++      bne     1b
++
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/* ================================ D-CACHE =============================== */
++
++/*
++ * cpu_arm1020E_dcache_invalidate_range(start, end)
++ *
++ * throw away all D-cached data in specified region without an obligation
++ * to write them back.        Note however that we must clean the D-cached entries
++ * around the boundaries if the start and/or end address are not cache
++ * aligned.
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_arm1020E_dcache_invalidate_range)
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      bic     r0, r0, #DCACHELINESIZE - 1
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      tst     r0, #DCACHELINESIZE - 1
++      bic     r0, r0, #DCACHELINESIZE - 1
++      mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry at start
++      tst     r1, #DCACHELINESIZE - 1
++      mcrne   p15, 0, r1, c7, c10, 1          @ clean D entry at end
++#endif
++
++1:
++#ifdef CONFIG_CPU_DCACHE_ENABLE
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b
++
++      /* Even if the D cache is off still drain the write buffer */
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ Drain write buffer
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_dcache_clean_range(start, end)
++ *
++ * For the specified virtual address range, ensure that all caches contain
++ * clean data, such that peripheral accesses to the physical RAM fetch
++ * correct data.
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_arm1020E_dcache_clean_range)
++
++      mov     r2, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      bic     r0, r0, #DCACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      bhs     cpu_arm1020E_cache_clean_invalidate_all_r2
++
++1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b
++#endif
++
++      mcr     p15, 0, r2, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_dcache_clean_page(page)
++ *
++ * Cleans a single page of dcache so that if we have any future aliased
++ * mappings, they will be consistent at the time that they are created.
++ *
++ * page: virtual address of page to clean from dcache
++ *
++ * Note:
++ *     we don't invalidate the entries since when we write the page
++ *     out to disk, the entries may get reloaded into the cache.
++ */
++      .align  5
++ENTRY(cpu_arm1020E_dcache_clean_page)
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      mov     r1, #PAGESIZE
++1:
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++      add     r0, r0, #DCACHELINESIZE
++      subs    r1, r1, #DCACHELINESIZE
++      bne     1b
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_dcache_clean_entry(addr)
++ *
++ * Clean the specified entry of any caches such that the MMU
++ * translation fetches will obtain correct data.
++ *
++ * addr: cache-unaligned virtual address
++ */
++      .align  5
++ENTRY(cpu_arm1020E_dcache_clean_entry)
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      bic     r0, r0, #DCACHELINESIZE - 1
++      mcr     p15, 0, r0, c7, c10, 1          @ clean single D entry
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/* ================================ I-CACHE =============================== */
++
++/*
++ * cpu_arm1020E_icache_invalidate_range(start, end)
++ *
++ * invalidate a range of virtual addresses from the Icache
++ *
++ * This is a little misleading, it is not intended to clean out
++ * the i-cache but to make sure that any data written to the
++ * range is made consistent.  This means that when we execute code
++ * in that region, everything works as we expect.
++ *
++ * This generally means writing back data in the Dcache and
++ * write buffer and invalidating the Icache over that region
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ *
++ * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to
++ * loop twice, once for i-cache, once for d-cache)
++ */
++      .align  5
++ENTRY(cpu_arm1020E_icache_invalidate_range)
++      bic     r0, r0, #ICACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      movhs   r2, #1
++      bhs     cpu_arm1020E_cache_clean_invalidate_all_r2
++
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b                              @ unsigned lower or same - includes r1 entry
++
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++ENTRY(cpu_arm1020E_icache_invalidate_page)
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      add     r1, r0, #PAGESIZE
++      b       cpu_arm1020E_icache_invalidate_range
++
++/* ================================== TLB ================================= */
++
++/*
++ * cpu_arm1020E_tlb_invalidate_all()
++ *
++ * Invalidate all TLB entries
++ */
++      .align  5
++ENTRY(cpu_arm1020E_tlb_invalidate_all)
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mcr     p15, 0, r0, c8, c7, 0           @ invalidate I & D tlbs
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_tlb_invalidate_range(start, end)
++ *
++ * invalidate TLB entries covering the specified range
++ *
++ * start: range start address
++ * end:   range end address
++ */
++      .align  5
++ENTRY(cpu_arm1020E_tlb_invalidate_range)
++      sub     r3, r1, r0
++      cmp     r3, #256 * PAGESIZE
++      bhs     cpu_arm1020E_tlb_invalidate_all
++      mov     r3, #0
++      mcr     p15, 0, r3, c7, c10, 4          @ drain WB
++      mov     r3, #PAGESIZE
++      sub     r3, r3, #1
++      bic     r0, r0, r3
++1:    mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
++      add     r0, r0, #PAGESIZE
++      cmp     r0, r1
++      bls     1b
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_tlb_invalidate_page(page, flags)
++ *
++ * invalidate the TLB entries for the specified page.
++ *
++ * page:  page to invalidate
++ * flags: non-zero if we include the I TLB
++ */
++      .align  5
++ENTRY(cpu_arm1020E_tlb_invalidate_page)
++      mov     r3, #0
++      mcr     p15, 0, r3, c7, c10, 4          @ drain WB
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      teq     r1, #0
++      mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcrne   p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
++      mov     pc, lr
++
++/* =============================== PageTable ============================== */
++
++/*
++ * cpu_arm1020E_set_pgd(pgd)
++ *
++ * Set the translation base pointer to be as described by pgd.
++ *
++ * pgd: new page tables
++ */
++      .align  5
++ENTRY(cpu_arm1020E_set_pgd)
++      stmfd   sp!, {lr}
++      bl      cpu_arm1020E_cache_clean_invalidate_all @ preserves r0
++      mov     r1, #0
++      mcr     p15, 0, r0, c2, c0, 0           @ load page table pointer
++      mcr     p15, 0, r1, c8, c7, 0           @ invalidate I & D TLBs
++      ldmfd   sp!, {pc}
++
++/*
++ * cpu_arm1020E_set_pmd(pmdp, pmd)
++ *
++ * Set a level 1 translation table entry, and clean it out of
++ * any caches such that the MMUs can load it correctly.
++ *
++ * pmdp: pointer to PMD entry
++ * pmd:  PMD value to store
++ */
++      .align  5
++ENTRY(cpu_arm1020E_set_pmd)
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      eor     r2, r1, #0x0a                   @ C & Section
++      tst     r2, #0x0b
++      biceq   r1, r1, #4                      @ clear bufferable bit
++#endif
++      str     r1, [r0]
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++#endif
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1020E_set_pte(ptep, pte)
++ *
++ * Set a PTE and flush it out
++ */
++      .align  5
++ENTRY(cpu_arm1020E_set_pte)
++      str     r1, [r0], #-1024                @ linux version
++
++      eor     r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY
++
++      bic     r2, r1, #0xff0
++      bic     r2, r2, #3
++      orr     r2, r2, #HPTE_TYPE_SMALL
++
++      tst     r1, #LPTE_USER                  @ User?
++      orrne   r2, r2, #HPTE_AP_READ
++
++      tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
++      orreq   r2, r2, #HPTE_AP_WRITE
++
++      tst     r1, #LPTE_PRESENT | LPTE_YOUNG  @ Present and Young?
++      movne   r2, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      eor     r3, r2, #0x0a   @ C and Small Page?
++      tst     r3, #0x0b                       @ if so..
++      biceq   r2, r2, #0x04   @ clear the bufferable bit
++#endif
++      str     r2, [r0]                        @ hardware version
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++
++cpu_manu_name:
++      .asciz  "ARM"
++ENTRY(cpu_arm1020E_name)
++      .ascii  "Arm1020E"
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      .ascii  "i"
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      .ascii  "d"
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      .ascii  "(wt)"
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      .ascii  "(wb)"
++#endif
++#endif
++#ifndef CONFIG_CPU_BPREDICT_DISABLE
++      .ascii  "B"
++#endif
++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
++      .ascii  "RR"
++#endif
++      .ascii  "\0"
++      .align
++
++      .section ".text.init", #alloc, #execinstr
++
++__arm1020E_setup:
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c7, 0           @ invalidate I,D caches on v4
++      mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v4
++      mcr     p15, 0, r0, c8, c7, 0           @ invalidate I,D TLBs on v4
++      mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
++      mov     r0, #0x1f                       @ Domains 0, 1 = client
++      mcr     p15, 0, r0, c3, c0, 0           @ load domain access register
++
++      mrc     p15, 0, r0, c1, c0, 0           @ Read current control register
++/*
++ * The only thing worth keeping from the initial control register is the endian bit
++ */
++
++      and     r0, r0, #0x0080                 @ ........B....... Preserve  endian bit, zero all others.
++      orr     r0, r0, #0x0070                 @ .........111.... Set the SBO (Should Be One) bits
++/*
++ * Turn on what we want.
++ */
++      orr     r0, r0, #0x0001                 @ ...............M Enable MMU (Alignment is special cased elsewhere)
++      orr     r0, r0, #0x0100                 @ .......S........ Enable system MMU protection
++      orr     r0, r0, #0x2000                 @ ..V............. Enable high vectors
++
++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
++      orr     r0, r0, #0x4000                 @ .R.............. Force round-robin replacement
++#endif
++
++#ifndef CONFIG_CPU_BPREDICT_DISABLE
++      orr     r0, r0, #0x0800                 @ ....Z........... Enable branch prediction
++#endif
++
++#ifdef CONFIG_CPU_DCACHE_ENABLE
++      orr     r0, r0, #0x0004                 @ .............C.. Enable D cache
++#endif
++#ifndef CONFIG_CPU_WB_DISABLE
++      orr     r0, r0, #0x0008                 @ ............W... Write Buffer enabled
++#endif
++
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      orr     r0, r0, #0x1000                 @ ...I............ Enable I Cache
++#endif
++
++      mov     pc, lr
++
++      .text
++
++/*
++ * Purpose : Function pointers used to access above functions - all calls
++ *         come through these
++ */
++      .type   arm1020E_processor_functions, #object
++arm1020E_processor_functions:
++      .word   cpu_arm1020E_data_abort
++      .word   cpu_arm1020E_check_bugs
++      .word   cpu_arm1020E_proc_init
++      .word   cpu_arm1020E_proc_fin
++      .word   cpu_arm1020E_reset
++      .word   cpu_arm1020E_do_idle
++
++      /* cache */
++      .word   cpu_arm1020E_cache_clean_invalidate_all
++      .word   cpu_arm1020E_cache_clean_invalidate_range
++      .word   cpu_arm1020E_flush_ram_page
++
++      /* dcache */
++      .word   cpu_arm1020E_dcache_invalidate_range
++      .word   cpu_arm1020E_dcache_clean_range
++      .word   cpu_arm1020E_dcache_clean_page
++      .word   cpu_arm1020E_dcache_clean_entry
++
++      /* icache */
++      .word   cpu_arm1020E_icache_invalidate_range
++      .word   cpu_arm1020E_icache_invalidate_page
++
++      /* tlb */
++      .word   cpu_arm1020E_tlb_invalidate_all
++      .word   cpu_arm1020E_tlb_invalidate_range
++      .word   cpu_arm1020E_tlb_invalidate_page
++
++      /* pgtable */
++      .word   cpu_arm1020E_set_pgd
++      .word   cpu_arm1020E_set_pmd
++      .word   cpu_arm1020E_set_pte
++      .size   arm1020E_processor_functions, . - arm1020E_processor_functions
++
++      .type   cpu_arm1020E_info, #object
++cpu_arm1020E_info:
++      .long   cpu_manu_name
++      .long   cpu_arm1020E_name
++      .size   cpu_arm1020E_info, . - cpu_arm1020E_info
++
++      .type   cpu_arch_name, #object
++cpu_arch_name:
++      .asciz  "armv5te"
++      .size   cpu_arch_name, . - cpu_arch_name
++
++      .type   cpu_elf_name, #object
++cpu_elf_name:
++      .asciz  "v5"
++      .size   cpu_elf_name, . - cpu_elf_name
++      .align
++
++      .section ".proc.info", #alloc, #execinstr
++
++      .type   __arm1020E_proc_info,#object
++__arm1020E_proc_info:
++      .long   0x4105a200                      @ ARM 1020E (Architecture v5TE)
++      .long   0xff0ffff0
++      .long   0x00000c1e                      @ mmuflags
++      b       __arm1020E_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
++      .long   cpu_arm1020E_info
++      .long   arm1020E_processor_functions
++      .size   __arm1020E_proc_info, . - __arm1020E_proc_info
++
+--- /dev/null
++++ linux-2.4.27/arch/arm/mm/proc-arm1022.S
+@@ -0,0 +1,716 @@
++/*
++ *  linux/arch/arm/mm/proc-arm1022.S: MMU functions for ARM1022E
++ *
++ *  Copyright (C) 2000 ARM Limited
++ *  Copyright (C) 2000 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
++ * 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
++ *
++ *
++ * These are the low level assembler for performing cache and TLB
++ * functions on the arm1022E.
++ */
++#include <linux/linkage.h>
++#include <linux/config.h>
++#include <asm/assembler.h>
++#include <asm/constants.h>
++#include <asm/procinfo.h>
++#include <asm/hardware.h>
++
++/*
++ * This is the maximum size of an area which will be invalidated
++ * using the single invalidate entry instructions.  Anything larger
++ * than this, and we go for the whole cache.
++ *
++ * This value should be chosen such that we choose the cheapest
++ * alternative.
++ */
++#define MAX_AREA_SIZE 16384
++
++/*
++ * the cache line size of the I and D cache
++ */
++#define DCACHELINESIZE        32
++#define ICACHELINESIZE        32
++
++/*
++ * and the page size
++ */
++#define LOG2PAGESIZE  12      /* == 4096 Bytes */
++#define PAGESIZE      (1 << LOG2PAGESIZE)
++
++/*
++ * create some useful conditional macro definitions
++ * we often need to know if we are ((not dcache disable) and writethrough) or ((not dcache disable) and writeback)
++ */
++#ifdef  CONFIG_CPU_DCACHE_DISABLE
++    #undef  CONFIG_CPU_DCACHE_WRITETHROUGH
++    #undef  CONFIG_CPU_DCACHE_WRITEBACK
++    #undef  CONFIG_CPU_DCACHE_ENABLE
++#else
++  #ifdef  CONFIG_CPU_DCACHE_WRITETHROUGH
++    #undef  CONFIG_CPU_DCACHE_WRITEBACK
++  #else
++    #define CONFIG_CPU_DCACHE_WRITEBACK
++  #endif
++    #define CONFIG_CPU_DCACHE_ENABLE
++#endif
++
++#ifdef  CONFIG_CPU_ICACHE_DISABLE
++    #undef  CONFIG_CPU_ICACHE_ENABLE
++#else
++    #define CONFIG_CPU_ICACHE_ENABLE
++#endif
++
++      .text
++
++/*
++ * cpu_arm1022_data_abort()
++ *
++ * obtain information about current aborted instruction
++ * Note: we read user space.  This means we might cause a data
++ * abort here if the I-TLB and D-TLB aren't seeing the same
++ * picture.  Unfortunately, this does happen.  We live with it.
++ *
++ *  r2 = address of aborted instruction
++ *  r3 = cpsr
++ *
++ * Returns:
++ *  r0 = address of abort
++ *  r1 = FSR
++ *  r3 = corrupted
++ *  r4 = corrupted
++ */
++      .align  5
++ENTRY(cpu_arm1022_data_abort)
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r0, c6, c0, 0           @ get FAR
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_check_bugs()
++ */
++ENTRY(cpu_arm1022_check_bugs)
++      mrs     ip, cpsr
++      bic     ip, ip, #F_BIT
++      msr     cpsr, ip
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_proc_init()
++ */
++ENTRY(cpu_arm1022_proc_init)
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_proc_fin()
++ */
++ENTRY(cpu_arm1022_proc_fin)
++      stmfd   sp!, {lr}
++      mov     ip, #F_BIT | I_BIT | SVC_MODE
++      msr     cpsr_c, ip
++      bl      cpu_arm1022_cache_clean_invalidate_all
++      mrc     p15, 0, r0, c1, c0, 0           @ ctrl register
++      bic     r0, r0, #0x1000                 @ ...i............
++      bic     r0, r0, #0x000e                 @ ............wca.
++      mcr     p15, 0, r0, c1, c0, 0           @ disable caches
++      ldmfd   sp!, {pc}
++
++/*
++ * cpu_arm1022_reset(loc)
++ *
++ * Perform a soft reset of the system.        Put the CPU into the
++ * same state as it would be if it had been reset, and branch
++ * to what would be the reset vector.
++ *
++ * loc: location to jump to for soft reset
++ */
++      .align  5
++ENTRY(cpu_arm1022_reset)
++      mov     ip, #0
++      mcr     p15, 0, ip, c7, c7, 0           @ invalidate I,D caches
++      mcr     p15, 0, ip, c7, c10, 4          @ drain WB
++      mcr     p15, 0, ip, c8, c7, 0           @ invalidate I & D TLBs
++      mrc     p15, 0, ip, c1, c0, 0           @ ctrl register
++      bic     ip, ip, #0x000f                 @ ............wcam
++      bic     ip, ip, #0x1100                 @ ...i...s........
++      mcr     p15, 0, ip, c1, c0, 0           @ ctrl register
++      mov     pc, r0
++
++/*
++ * cpu_arm1022_do_idle()
++ */
++      .align  5
++ENTRY(cpu_arm1022_do_idle)
++      mcr     p15, 0, r0, c7, c0, 4           @ Wait for interrupt
++      mov     pc, lr
++
++/* ================================= CACHE ================================ */
++
++
++/*
++ * cpu_arm1022_cache_clean_invalidate_all()
++ *
++ * clean and invalidate all cache lines
++ *
++ * Note:
++ *  1. we should preserve r0 and ip at all times
++ */
++      .align  5
++ENTRY(cpu_arm1022_cache_clean_invalidate_all)
++      mov     r2, #1
++cpu_arm1022_cache_clean_invalidate_all_r2:
++
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mov     r1, #7 << 5                     @ 8 segments
++1:    orr     r3, r1, #63 << 26               @ 64 entries
++2:    mcr     p15, 0, r3, c7, c14, 2          @ clean and invalidate D index
++      subs    r3, r3, #1 << 26
++      bcs     2b
++      subs    r1, r1, #1 << 5
++      bcs     1b                              @ segments 7 to 0
++#endif
++
++      mov     r1, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r1, c7, c6, 0           @ invalidate D cache
++#endif
++
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      teq     r2, #0
++      mcrne   p15, 0, r1, c7, c5, 0           @ invalidate I cache
++#endif
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_cache_clean_invalidate_range(start, end, flags)
++ *
++ * clean and invalidate all cache lines associated with this area of memory
++ *
++ * start: Area start address
++ * end:   Area end address
++ * flags: nonzero for I cache as well
++ */
++      .align  5
++ENTRY(cpu_arm1022_cache_clean_invalidate_range)
++      bic     r0, r0, #DCACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      bhs     cpu_arm1022_cache_clean_invalidate_all_r2
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      teq     r2, #0
++      mcrne   p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b                              @ unsigned lower or same - must include end point (r1)!
++
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_flush_ram_page(page)
++ *
++ * clean and invalidate all cache lines associated with this area of memory
++ *
++ * page: page to clean and invalidate
++ */
++      .align  5
++ENTRY(cpu_arm1022_flush_ram_page)
++      mov     r1, #PAGESIZE
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c14, 1          @ clean and invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      subs    r1, r1, #DCACHELINESIZE
++      bne     1b
++
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/* ================================ D-CACHE =============================== */
++
++/*
++ * cpu_arm1022_dcache_invalidate_range(start, end)
++ *
++ * throw away all D-cached data in specified region without an obligation
++ * to write them back.        Note however that we must clean the D-cached entries
++ * around the boundaries if the start and/or end address are not cache
++ * aligned.
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_arm1022_dcache_invalidate_range)
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      bic     r0, r0, #DCACHELINESIZE - 1
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      tst     r0, #DCACHELINESIZE - 1
++      bic     r0, r0, #DCACHELINESIZE - 1
++      mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry at start
++      tst     r1, #DCACHELINESIZE - 1
++      mcrne   p15, 0, r1, c7, c10, 1          @ clean D entry at end
++#endif
++
++1:
++#ifdef CONFIG_CPU_DCACHE_ENABLE
++      mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b
++
++      /* Even if the D cache is off still drain the write buffer */
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ Drain write buffer
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_dcache_clean_range(start, end)
++ *
++ * For the specified virtual address range, ensure that all caches contain
++ * clean data, such that peripheral accesses to the physical RAM fetch
++ * correct data.
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ */
++      .align  5
++ENTRY(cpu_arm1022_dcache_clean_range)
++
++      mov     r2, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      bic     r0, r0, #DCACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      bhs     cpu_arm1022_cache_clean_invalidate_all_r2
++
++1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b
++#endif
++
++      mcr     p15, 0, r2, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_dcache_clean_page(page)
++ *
++ * Cleans a single page of dcache so that if we have any future aliased
++ * mappings, they will be consistent at the time that they are created.
++ *
++ * page: virtual address of page to clean from dcache
++ *
++ * Note:
++ *     we don't invalidate the entries since when we write the page
++ *     out to disk, the entries may get reloaded into the cache.
++ */
++      .align  5
++ENTRY(cpu_arm1022_dcache_clean_page)
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      mov     r1, #PAGESIZE
++1:
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++      add     r0, r0, #DCACHELINESIZE
++      subs    r1, r1, #DCACHELINESIZE
++      bne     1b
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_dcache_clean_entry(addr)
++ *
++ * Clean the specified entry of any caches such that the MMU
++ * translation fetches will obtain correct data.
++ *
++ * addr: cache-unaligned virtual address
++ */
++      .align  5
++ENTRY(cpu_arm1022_dcache_clean_entry)
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      bic     r0, r0, #DCACHELINESIZE - 1
++      mcr     p15, 0, r0, c7, c10, 1          @ clean single D entry
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/* ================================ I-CACHE =============================== */
++
++/*
++ * cpu_arm1022_icache_invalidate_range(start, end)
++ *
++ * invalidate a range of virtual addresses from the Icache
++ *
++ * This is a little misleading, it is not intended to clean out
++ * the i-cache but to make sure that any data written to the
++ * range is made consistent.  This means that when we execute code
++ * in that region, everything works as we expect.
++ *
++ * This generally means writing back data in the Dcache and
++ * write buffer and invalidating the Icache over that region
++ *
++ * start: virtual start address
++ * end:   virtual end address
++ *
++ * NOTE: ICACHELINESIZE == DCACHELINESIZE (so we don't need to
++ * loop twice, once for i-cache, once for d-cache)
++ */
++      .align  5
++ENTRY(cpu_arm1022_icache_invalidate_range)
++      bic     r0, r0, #ICACHELINESIZE - 1
++      sub     r3, r1, r0
++      cmp     r3, #MAX_AREA_SIZE
++      movhs   r2, #1
++      bhs     cpu_arm1022_cache_clean_invalidate_all_r2
++1:
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ Clean D entry
++#endif
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      mcr     p15, 0, r0, c7, c5, 1           @ Invalidate I entry
++#endif
++      add     r0, r0, #DCACHELINESIZE
++      cmp     r0, r1
++      bls     1b                              @ unsigned lower or same - includes r1 entry
++
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++ENTRY(cpu_arm1022_icache_invalidate_page)
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      add     r1, r0, #PAGESIZE
++      b       cpu_arm1022_icache_invalidate_range
++
++/* ================================== TLB ================================= */
++
++/*
++ * cpu_arm1022_tlb_invalidate_all()
++ *
++ * Invalidate all TLB entries
++ */
++      .align  5
++ENTRY(cpu_arm1022_tlb_invalidate_all)
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mcr     p15, 0, r0, c8, c7, 0           @ invalidate I & D tlbs
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_tlb_invalidate_range(start, end)
++ *
++ * invalidate TLB entries covering the specified range
++ *
++ * start: range start address
++ * end:   range end address
++ */
++      .align  5
++ENTRY(cpu_arm1022_tlb_invalidate_range)
++      sub     r3, r1, r0
++      cmp     r3, #256 * PAGESIZE
++      bhs     cpu_arm1022_tlb_invalidate_all
++      mov     r3, #0
++      mcr     p15, 0, r3, c7, c10, 4          @ drain WB
++      mov     r3, #PAGESIZE
++      sub     r3, r3, #1
++      bic     r0, r0, r3
++1:    mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
++      add     r0, r0, #PAGESIZE
++      cmp     r0, r1
++      bls     1b
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_tlb_invalidate_page(page, flags)
++ *
++ * invalidate the TLB entries for the specified page.
++ *
++ * page:  page to invalidate
++ * flags: non-zero if we include the I TLB
++ */
++      .align  5
++ENTRY(cpu_arm1022_tlb_invalidate_page)
++      mov     r3, #0
++      mcr     p15, 0, r3, c7, c10, 4          @ drain WB
++      mov     r0, r0, LSR #LOG2PAGESIZE       @ round down to nearest page
++      mov     r0, r0, LSL #LOG2PAGESIZE
++      teq     r1, #0
++      mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
++      mcrne   p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
++      mov     pc, lr
++
++/* =============================== PageTable ============================== */
++
++/*
++ * cpu_arm1022_set_pgd(pgd)
++ *
++ * Set the translation base pointer to be as described by pgd.
++ *
++ * pgd: new page tables
++ */
++      .align  5
++ENTRY(cpu_arm1022_set_pgd)
++      stmfd   sp!, {lr}
++      bl      cpu_arm1022_cache_clean_invalidate_all  @ preserves r0
++      mov     r1, #0
++      mcr     p15, 0, r0, c2, c0, 0           @ load page table pointer
++      mcr     p15, 0, r1, c8, c7, 0           @ invalidate I & D TLBs
++      ldmfd   sp!, {pc}
++
++/*
++ * cpu_arm1022_set_pmd(pmdp, pmd)
++ *
++ * Set a level 1 translation table entry, and clean it out of
++ * any caches such that the MMUs can load it correctly.
++ *
++ * pmdp: pointer to PMD entry
++ * pmd:  PMD value to store
++ */
++      .align  5
++ENTRY(cpu_arm1022_set_pmd)
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      eor     r2, r1, #0x0a                   @ C & Section
++      tst     r2, #0x0b
++      biceq   r1, r1, #4                      @ clear bufferable bit
++#endif
++      str     r1, [r0]
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++#endif
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++/*
++ * cpu_arm1022_set_pte(ptep, pte)
++ *
++ * Set a PTE and flush it out
++ */
++      .align  5
++ENTRY(cpu_arm1022_set_pte)
++      str     r1, [r0], #-1024                @ linux version
++
++      eor     r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY
++
++      bic     r2, r1, #0xff0
++      bic     r2, r2, #3
++      orr     r2, r2, #HPTE_TYPE_SMALL
++
++      tst     r1, #LPTE_USER                  @ User?
++      orrne   r2, r2, #HPTE_AP_READ
++
++      tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
++      orreq   r2, r2, #HPTE_AP_WRITE
++
++      tst     r1, #LPTE_PRESENT | LPTE_YOUNG  @ Present and Young?
++      movne   r2, #0
++
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      eor     r3, r2, #0x0a   @ C and Small Page?
++      tst     r3, #0x0b                       @ if so..
++      biceq   r2, r2, #0x04   @ clear the bufferable bit
++#endif
++      str     r2, [r0]                        @ hardware version
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
++#endif
++      mov     r1, #0
++      mcr     p15, 0, r1, c7, c10, 4          @ drain WB
++      mov     pc, lr
++
++
++cpu_manu_name:
++      .asciz  "ARM"
++ENTRY(cpu_arm1022_name)
++      .ascii  "Arm1022E"
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      .ascii  "i"
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      .ascii  "d"
++#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
++      .ascii  "(wt)"
++#endif
++#ifdef CONFIG_CPU_DCACHE_WRITEBACK
++      .ascii  "(wb)"
++#endif
++#endif
++#ifndef CONFIG_CPU_BPREDICT_DISABLE
++      .ascii  "B"
++#endif
++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
++      .ascii  "RR"
++#endif
++      .ascii  "\0"
++      .align
++
++      .section ".text.init", #alloc, #execinstr
++
++__arm1022_setup:
++      mov     r0, #0
++      mcr     p15, 0, r0, c7, c7, 0           @ invalidate I,D caches on v4
++      mcr     p15, 0, r0, c7, c10, 4          @ drain write buffer on v4
++      mcr     p15, 0, r0, c8, c7, 0           @ invalidate I,D TLBs on v4
++      mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
++      mov     r0, #0x1f                       @ Domains 0, 1 = client
++      mcr     p15, 0, r0, c3, c0, 0           @ load domain access register
++
++      mrc     p15, 0, r0, c1, c0, 0           @ Read current control register
++/*
++ * The only thing worth keeping from the initial control register is the endian bit
++ */
++
++      and     r0, r0, #0x0080                 @ ........B....... Preserve  endian bit, zero all others.
++      orr     r0, r0, #0x0070                 @ .........111.... Set the SBO (Should Be One) bits
++/*
++ * Turn on what we want.
++ */
++      orr     r0, r0, #0x0001                 @ ...............M Enable MMU (Alignment is special cased elsewhere)
++      orr     r0, r0, #0x0100                 @ .......S........ Enable system MMU protection
++      orr     r0, r0, #0x2000                 @ ..V............. Enable high vectors
++
++#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN
++      orr     r0, r0, #0x4000                 @ .R.............. Force round-robin replacement
++#endif
++
++#ifndef CONFIG_CPU_BPREDICT_DISABLE
++      orr     r0, r0, #0x0800                 @ ....Z........... Enable branch prediction
++#endif
++
++#ifdef CONFIG_CPU_DCACHE_ENABLE
++      orr     r0, r0, #0x0004                 @ .............C.. Enable D cache
++#endif
++#ifndef CONFIG_CPU_WB_DISABLE
++      orr     r0, r0, #0x0008                 @ ............W... Write Buffer enabled
++#endif
++
++#ifdef CONFIG_CPU_ICACHE_ENABLE
++      orr     r0, r0, #0x1000                 @ ...I............ Enable I Cache
++#endif
++
++      mov     pc, lr
++
++      .text
++
++/*
++ * Purpose : Function pointers used to access above functions - all calls
++ *         come through these
++ */
++      .type   arm1022_processor_functions, #object
++arm1022_processor_functions:
++      .word   cpu_arm1022_data_abort
++      .word   cpu_arm1022_check_bugs
++      .word   cpu_arm1022_proc_init
++      .word   cpu_arm1022_proc_fin
++      .word   cpu_arm1022_reset
++      .word   cpu_arm1022_do_idle
++
++      /* cache */
++      .word   cpu_arm1022_cache_clean_invalidate_all
++      .word   cpu_arm1022_cache_clean_invalidate_range
++      .word   cpu_arm1022_flush_ram_page
++
++      /* dcache */
++      .word   cpu_arm1022_dcache_invalidate_range
++      .word   cpu_arm1022_dcache_clean_range
++      .word   cpu_arm1022_dcache_clean_page
++      .word   cpu_arm1022_dcache_clean_entry
++
++      /* icache */
++      .word   cpu_arm1022_icache_invalidate_range
++      .word   cpu_arm1022_icache_invalidate_page
++
++      /* tlb */
++      .word   cpu_arm1022_tlb_invalidate_all
++      .word   cpu_arm1022_tlb_invalidate_range
++      .word   cpu_arm1022_tlb_invalidate_page
++
++      /* pgtable */
++      .word   cpu_arm1022_set_pgd
++      .word   cpu_arm1022_set_pmd
++      .word   cpu_arm1022_set_pte
++      .size   arm1022_processor_functions, . - arm1022_processor_functions
++
++      .type   cpu_arm1022_info, #object
++cpu_arm1022_info:
++      .long   cpu_manu_name
++      .long   cpu_arm1022_name
++      .size   cpu_arm1022_info, . - cpu_arm1022_info
++
++      .type   cpu_arch_name, #object
++cpu_arch_name:
++      .asciz  "armv5t"
++      .size   cpu_arch_name, . - cpu_arch_name
++
++      .type   cpu_elf_name, #object
++cpu_elf_name:
++      .asciz  "v5"
++      .size   cpu_elf_name, . - cpu_elf_name
++      .align
++
++      .section ".proc.info", #alloc, #execinstr
++
++      .type   __arm1022_proc_info,#object
++__arm1022_proc_info:
++      .long   0x4100a220                      @ ARM 1022
++      .long   0xff00fff0
++      .long   0x00000c1e                      @ mmuflags
++      b       __arm1022_setup
++      .long   cpu_arch_name
++      .long   cpu_elf_name
++      .long   HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
++      .long   cpu_arm1022_info
++      .long   arm1022_processor_functions
++      .size   __arm1022_proc_info, . - __arm1022_proc_info
+--- linux-2.4.27/arch/arm/mm/proc-arm1026.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm1026.S
+@@ -66,19 +66,24 @@
+  *
+  * Returns:
+  *  r0 = address of abort
+- *  r1 != 0 if writing
+- *  r3 = FSR
++ *  r1 = FSR, bit 11 set if writing
++ *  r3 = corrupted
+  *  r4 = corrupted
+  */
+       .align  5
+ ENTRY(cpu_arm1026_data_abort)
+-      mrc     p15, 0, r3, c5, c0, 0           @ get FSR
+-      and     r2, r3, #0b1101                 @ Check for translation error
+-      sub     r1, r2, #0b0101
+-
+-      and     r3, r3, #255
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      tst     r3, #PSR_J_BIT                  @ Java?
++      orrne   r1, r1, #1 << 11                @ always assume write
++      movne   pc, lr
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
+       mov     pc, lr
+ /*
+@@ -254,7 +259,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm1026_dcache_invalidate_range)
+-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       tst     r0, #DCACHELINESIZE - 1
+       mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry
+       tst     r1, #DCACHELINESIZE - 1
+@@ -279,7 +284,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm1026_dcache_clean_range)
+-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+@@ -309,7 +314,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm1026_dcache_clean_page)
+-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mov     r1, #PAGESIZE
+ 1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+       add     r0, r0, #DCACHELINESIZE
+@@ -330,7 +335,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm1026_dcache_clean_entry)
+-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -473,7 +478,7 @@
+       biceq   r1, r1, #4                      @ clear bufferable bit
+ #endif
+       str     r1, [r0]
+-#ifndef CONFIG_CPU_ARM1026_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -494,7 +499,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+@@ -634,12 +639,12 @@
+       .type   cpu_arch_name, #object
+ cpu_arch_name:
+-      .asciz  "armv5EJ"
++      .asciz  "armv5tej"
+       .size   cpu_arch_name, . - cpu_arch_name
+       .type   cpu_elf_name, #object
+ cpu_elf_name:
+-      .asciz  "v5EJ"
++      .asciz  "v5"
+       .size   cpu_elf_name, . - cpu_elf_name
+       .align
+--- linux-2.4.27/arch/arm/mm/proc-arm6,7.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm6,7.S
+@@ -72,7 +72,7 @@
+ 1:            mcr     p15, 0, r0, c6, c0, 0           @ purge TLB
+               add     r0, r0, #4096
+               cmp     r0, r1
+-              blt     1b
++              blo     1b
+               mov     pc, lr
+ ENTRY(cpu_arm7_tlb_invalidate_range)
+@@ -85,7 +85,7 @@
+ 1:            mcr     p15, 0, r0, c6, c0, 0           @ purge TLB
+               add     r0, r0, #0x4000
+               cmp     r0, r1
+-              blt     1b
++              blo     1b
+               mov     pc, lr
+ #endif
+@@ -110,15 +110,13 @@
+  * Purpose : obtain information about current aborted instruction
+  *
+  * Returns : r0 = address of abort
+- *       : r1 != 0 if writing
+- *       : r3 = FSR
++ *       : r1 = FSR, bit 11 set if writing
++ *       : r3 = corrupted
+  *       : sp = pointer to registers
+  */
+ ENTRY(cpu_arm6_data_abort)
+               ldr     r4, [r0]                        @ read instruction causing problem
+-              tst     r4, r4, lsr #21                 @ C = bit 20
+-              sbc     r1, r1, r1                      @ r1 = C - 1
+               and     r2, r4, #14 << 24
+               teq     r2, #8 << 24                    @ was it ldm/stm
+               bne     Ldata_simple
+@@ -144,14 +142,14 @@
+               addeq   r7, r0, r7, lsl #2              @ Do correction (signed)
+ Ldata_saver7: str     r7, [sp, r5, lsr #14]           @ Put register
+ Ldata_simple: mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-              mrc     p15, 0, r3, c5, c0, 0           @ get FSR
+-              and     r3, r3, #255
++              mrc     p15, 0, r1, c5, c0, 0           @ get FSR
++              bic     r1, r1, #1 << 11 | 1 << 10
++              tst     r4, #1 << 20
++              orreq   r1, r1, #1 << 11
+               mov     pc, lr
+ ENTRY(cpu_arm7_data_abort)
+               ldr     r4, [r0]                        @ read instruction causing problem
+-              tst     r4, r4, lsr #21                 @ C = bit 20
+-              sbc     r1, r1, r1                      @ r1 = C - 1
+               and     r2, r4, #15 << 24
+               add     pc, pc, r2, lsr #22             @ Now branch to the relevent processing routine
+               movs    pc, lr
+@@ -336,7 +334,7 @@
+               bic     r2, r2, #3
+               orr     r2, r2, #HPTE_TYPE_SMALL
+-              tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++              tst     r1, #LPTE_USER                  @ User?
+               orrne   r2, r2, #HPTE_AP_READ
+               tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-arm720.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm720.S
+@@ -97,7 +97,7 @@
+ 1:            mcr     p15, 0, r0, c8, c7, 1           @ flush TLB (v4)
+               add     r0, r0, #PAGESIZE
+               cmp     r0, r1
+-              blt     1b
++              blo     1b
+               mov     pc, lr
+ /*
+@@ -124,8 +124,8 @@
+  * picture.  Unfortunately, this does happen.  We live with it.
+  *
+  * Returns : r0 = address of abort
+- *       : r1 != 0 if writing
+- *       : r3 = FSR
++ *       : r1 = FSR, bit 11 set if writing
++ *       : r3 = corrupted
+  *       : sp = pointer to registers
+  */
+@@ -150,16 +150,16 @@
+               addeq   r7, r0, r7, lsl #2              @ Do correction (signed)
+ Ldata_saver7: str     r7, [sp, r5, lsr #14]           @ Put register
+ Ldata_simple: mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-              mrc     p15, 0, r3, c5, c0, 0           @ get FSR
+-              and     r3, r3, #255
++              mrc     p15, 0, r1, c5, c0, 0           @ get FSR
++              bic     r1, r1, #1 << 11 | 1 << 10
++              tst     r4, #1 << 20
++              orreq   r1, r1, #1 << 11
+               mov     pc, lr
+ ENTRY(cpu_arm720_data_abort)
+-              tst     r3, #T_BIT
++              tst     r3, #PSR_T_BIT
+               bne     .data_thumb_abort
+-              ldr     r4, [r0]                        @ read instruction causing problem
+-              tst     r4, r4, lsr #21                 @ C = bit 20
+-              sbc     r1, r1, r1                      @ r1 = C - 1
++              ldr     r4, [r2]                        @ read instruction causing problem
+               and     r2, r4, #15 << 24
+               add     pc, pc, r2, lsr #22             @ Now branch to the relevent processing routine
+               movs    pc, lr
+@@ -270,9 +270,9 @@
+               b       Ldata_saver7
+ .data_thumb_abort:
+-              ldrh    r4, [r0]                        @ read instruction
+-              tst     r4, r4, lsr #12                 @ C = bit 11
+-              sbc     r1, r1, r1                      @ r1 = C - 1
++              ldrh    r4, [r2]                        @ read instruction
++              tst     r4, #1 << 11
++              orrne   r4, r4, #1 << 20
+               and     r2, r4, #15 << 12
+               add     pc, pc, r2, lsr #10             @ lookup in table
+               nop
+@@ -318,8 +318,8 @@
+               and     r0, r0, #15                     @ number of regs to transfer
+               ldr     r7, [sp, #13 << 2]
+               tst     r4, #1 << 11
+-              addne   r7, r7, r0, lsl #2              @ increment SP if PUSH
+-              subeq   r7, r7, r0, lsr #2              @ decrement SP if POP
++              addeq   r7, r7, r0, lsl #2              @ increment SP if PUSH
++              subne   r7, r7, r0, lsl #2              @ decrement SP if POP
+               str     r7, [sp, #13 << 2]
+               b       Ldata_simple
+@@ -336,7 +336,7 @@
+               and     r0, r0, #15                     @ number of regs to transfer
+               and     r5, r4, #7 << 8
+               ldr     r7, [sp, r5, lsr #6]
+-              sub     r7, r7, r0, lsr #2              @ always decrement
++              sub     r7, r7, r0, lsl #2              @ always decrement
+               str     r7, [sp, r5, lsr #6]
+               b       Ldata_simple
+@@ -418,7 +418,7 @@
+               bic     r2, r2, #3
+               orr     r2, r2, #HPTE_TYPE_SMALL
+-              tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++              tst     r1, #LPTE_USER                  @ User?
+               orrne   r2, r2, #HPTE_AP_READ
+               tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-arm920.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm920.S
+@@ -71,12 +71,16 @@
+  */
+       .align  5
+ ENTRY(cpu_arm920_data_abort)
+-      mrc     p15, 0, r3, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      ldr     r1, [r2]                        @ read aborted instruction
+-      and     r3, r3, #255
+-      tst     r1, r1, lsr #21                 @ C = bit 20
+-      sbc     r1, r1, r1                      @ r1 = C - 1
++
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
+       mov     pc, lr
+       
+ /*
+@@ -186,10 +190,9 @@
+               .align  5
+ ENTRY(cpu_arm920_cache_clean_invalidate_range)
+       bic     r0, r0, #DCACHELINESIZE - 1     @ && added by PGM
+-      bic     r1, r1, #DCACHELINESIZE - 1     @ && added by DHM
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+-      bgt     cpu_arm920_cache_clean_invalidate_all_r2
++      bhi     cpu_arm920_cache_clean_invalidate_all_r2
+ 1:    teq     r2, #0
+ #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+@@ -207,7 +210,7 @@
+       add     r0, r0, #DCACHELINESIZE
+ #endif
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mcr     p15, 0, r1, c7, c10, 4          @ drain WB
+       mov     pc, lr
+@@ -253,18 +256,17 @@
+  */
+       .align  5
+ ENTRY(cpu_arm920_dcache_invalidate_range)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       tst     r0, #DCACHELINESIZE - 1
+       mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry
+       tst     r1, #DCACHELINESIZE - 1
+       mcrne   p15, 0, r1, c7, c10, 1          @ clean D entry
+ #endif                @ clean D entry
+       bic     r0, r0, #DCACHELINESIZE - 1
+-      bic     r1, r1, #DCACHELINESIZE - 1
+ 1:    mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -279,20 +281,17 @@
+  */
+       .align  5
+ ENTRY(cpu_arm920_dcache_clean_range)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+       mov     r2, #0
+-      bgt     cpu_arm920_cache_clean_invalidate_all_r2
+-
+-      bic     r1, r1, #DCACHELINESIZE -1
+-      add     r1, r1, #DCACHELINESIZE
++      bhi     cpu_arm920_cache_clean_invalidate_all_r2
+ 1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+       add     r0, r0, #DCACHELINESIZE
+       subs    r1, r1, #DCACHELINESIZE
+-      bpl     1b
++      bcs     1b
+ #endif
+       mcr     p15, 0, r2, c7, c10, 4          @ drain WB
+       mov     pc, lr
+@@ -312,7 +311,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm920_dcache_clean_page)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mov     r1, #PAGESIZE
+ 1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+       add     r0, r0, #DCACHELINESIZE
+@@ -333,7 +332,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm920_dcache_clean_entry)
+-#ifndef CONFIG_CPU_ARM920_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -365,16 +364,13 @@
+       bic     r0, r0, #ICACHELINESIZE - 1     @ Safety check
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+-      bgt     cpu_arm920_cache_clean_invalidate_all_r2
+-
+-      bic     r1, r1, #ICACHELINESIZE - 1
+-      add     r1, r1, #ICACHELINESIZE
++      bhi     cpu_arm920_cache_clean_invalidate_all_r2
+ 1:    mcr     p15, 0, r0, c7, c5, 1           @ Clean I entry
+       mcr     p15, 0, r0, c7, c10, 1          @ Clean D entry
+       add     r0, r0, #ICACHELINESIZE
+       subs    r1, r1, #ICACHELINESIZE
+-      bne     1b
++      bcs     1b
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -418,13 +414,12 @@
+       mov     r3, #PAGESIZE
+       sub     r3, r3, #1
+       bic     r0, r0, r3
+-      bic     r1, r1, r3
+ 1:    mcr     p15, 0, r0, c8, c6, 1           @ invalidate D TLB entry
+       mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
+       add     r0, r0, #PAGESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -457,7 +452,6 @@
+ ENTRY(cpu_arm920_set_pgd)
+       mov     ip, #0
+ #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
+-      /* Any reason why we don't use mcr p15, 0, r0, c7, c7, 0 here? --rmk */
+       mcr     p15, 0, ip, c7, c6, 0           @ invalidate D cache
+ #else
+ @ && 'Clean & Invalidate whole DCache'
+@@ -514,7 +508,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-arm922.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm922.S
+@@ -62,17 +62,20 @@
+  *
+  * Returns:
+  *  r0 = address of abort
+- *  r1 != 0 if writing
+- *  r3 = FSR
++ *  r1 = FSR, bit 11 set if writing
++ *  r3 = corrupted
+  */
+       .align  5
+ ENTRY(cpu_arm922_data_abort)
+-      ldr     r1, [r0]                        @ read aborted instruction
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      tst     r1, r1, lsr #21                 @ C = bit 20
+-      mrc     p15, 0, r3, c5, c0, 0           @ get FSR
+-      sbc     r1, r1, r1                      @ r1 = C - 1
+-      and     r3, r3, #255
++      tst     r3, #PSR_T_BIT
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
+       mov     pc, lr
+       
+ /*
+@@ -185,7 +188,7 @@
+       bic     r1, r1, #DCACHELINESIZE - 1     @ && added by DHM
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+-      bgt     cpu_arm922_cache_clean_invalidate_all_r2
++      bhi     cpu_arm922_cache_clean_invalidate_all_r2
+ 1:    teq     r2, #0
+ #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+@@ -203,7 +206,7 @@
+       add     r0, r0, #DCACHELINESIZE
+ #endif
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mcr     p15, 0, r1, c7, c10, 4          @ drain WB
+       mov     pc, lr
+@@ -249,7 +252,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm922_dcache_invalidate_range)
+-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       tst     r0, #DCACHELINESIZE - 1
+       mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry
+       tst     r1, #DCACHELINESIZE - 1
+@@ -260,7 +263,7 @@
+ 1:    mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -275,12 +278,12 @@
+  */
+       .align  5
+ ENTRY(cpu_arm922_dcache_clean_range)
+-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+       mov     r2, #0
+-      bgt     cpu_arm922_cache_clean_invalidate_all_r2
++      bhi     cpu_arm922_cache_clean_invalidate_all_r2
+       bic     r1, r1, #DCACHELINESIZE -1
+       add     r1, r1, #DCACHELINESIZE
+@@ -308,7 +311,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm922_dcache_clean_page)
+-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mov     r1, #PAGESIZE
+ 1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+       add     r0, r0, #DCACHELINESIZE
+@@ -329,7 +332,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm922_dcache_clean_entry)
+-#ifndef CONFIG_CPU_ARM922_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -361,7 +364,7 @@
+       bic     r0, r0, #ICACHELINESIZE - 1     @ Safety check
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+-      bgt     cpu_arm922_cache_clean_invalidate_all_r2
++      bhi     cpu_arm922_cache_clean_invalidate_all_r2
+       bic     r1, r1, #ICACHELINESIZE - 1
+       add     r1, r1, #ICACHELINESIZE
+@@ -420,7 +423,7 @@
+       mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
+       add     r0, r0, #PAGESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -510,7 +513,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-arm925.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm925.S
+@@ -69,24 +69,24 @@
+  *
+  * Returns:
+  *  r0 = address of abort
+- *  r1 != 0 if writing
+- *  r3 = FSR
++ *  r1 = FSR, bit 11 set if writing
++ *  r3 = corrupted
+  *  r4 = corrupted
+  */
+       .align  5
+ ENTRY(cpu_arm925_data_abort)
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      mrc     p15, 0, r4, c5, c0, 0           @ get FSR
+-
+-      tst     r3, #1<<5                       @ Check for Thumb-bit (NE -> found)
+-      ldrneh  r1, [r2]                        @ Read aborted Thumb instruction
+-      tstne   r1, r1, lsr #12                 @ C = bit 11
+-
+-      ldreq   r1, [r2]                        @ Read aborted ARM instruction
+-      tsteq   r1, r1, lsr #21                 @ C = bit 20
+-
+-      sbc     r1, r1, r1                      @ r1 = C - 1
+-      and     r3, r4, #255
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      tst     r3, #PSR_J_BIT                  @ Java?
++      orrne   r1, r1, #1 << 11                @ always assume write
++      movne   pc, lr
++      tst     r3, #PSR_T_BIT                  @ Thumb?
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ L = 0 -> write
++      orreq   r1, r1, #1 << 11                @ yes.
+       mov     pc, lr
+ /*
+@@ -207,7 +207,7 @@
+       bic     r1, r1, #DCACHELINESIZE - 1     @ && added by DHM
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+-      bgt     cpu_arm925_cache_clean_invalidate_all_r2
++      bhi     cpu_arm925_cache_clean_invalidate_all_r2
+ 1:    teq     r2, #0
+ #ifdef CONFIG_CPU_ARM925_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+@@ -225,7 +225,7 @@
+       add     r0, r0, #DCACHELINESIZE
+ #endif
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mcr     p15, 0, r1, c7, c10, 4          @ drain WB
+       mov     pc, lr
+@@ -282,7 +282,7 @@
+ 1:    mcr     p15, 0, r0, c7, c6, 1           @ invalidate D entry
+       add     r0, r0, #DCACHELINESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -302,7 +302,7 @@
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+       mov     r2, #0
+-      bgt     cpu_arm925_cache_clean_invalidate_all_r2
++      bhi     cpu_arm925_cache_clean_invalidate_all_r2
+       bic     r1, r1, #DCACHELINESIZE -1
+       add     r1, r1, #DCACHELINESIZE
+@@ -383,7 +383,7 @@
+       bic     r0, r0, #ICACHELINESIZE - 1     @ Safety check
+       sub     r1, r1, r0
+       cmp     r1, #MAX_AREA_SIZE
+-      bgt     cpu_arm925_cache_clean_invalidate_all_r2
++      bhi     cpu_arm925_cache_clean_invalidate_all_r2
+       bic     r1, r1, #ICACHELINESIZE - 1
+       add     r1, r1, #ICACHELINESIZE
+@@ -443,7 +443,7 @@
+       mcr     p15, 0, r0, c8, c5, 1           @ invalidate I TLB entry
+       add     r0, r0, #PAGESIZE
+       cmp     r0, r1
+-      blt     1b
++      blo     1b
+       mov     pc, lr
+ /*
+@@ -532,7 +532,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-arm926.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-arm926.S
+@@ -66,28 +66,24 @@
+  *
+  * Returns:
+  *  r0 = address of abort
+- *  r1 != 0 if writing
+- *  r3 = FSR
++ *  r1 = FSR, bit 11 set if writing
++ *  r3 = corrupted
+  *  r4 = corrupted
+  */
+       .align  5
+ ENTRY(cpu_arm926_data_abort)
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      mrc     p15, 0, r4, c5, c0, 0           @ get FSR
+-
+-      tst     r3, #1<<24                      @ Check for Jbit (NE -> found)
+-      movne   r3, #-1                         @ Mark as writing
+-      bne     2f
+-
+-      tst     r3, #1<<5                       @ Check for Thumb-bit (NE -> found)
+-      ldrneh  r1, [r2]                        @ Read aborted Thumb instruction        
+-      ldreq   r1, [r2]                        @ Read aborted ARM instruction
+-      movne   r1, r1, lsl #(20-12)            @ shift thumb bit 10 to ARM bit 20
+-      tsteq   r1, r1, lsr #21                 @ C = bit 20
+-
+-      sbc     r1, r1, r1                      @ r1 = C - 1
+-2:
+-      and     r3, r4, #255
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      tst     r3, #PSR_J_BIT                  @ Java?
++      orrne   r1, r1, #1 << 11                @ always assume write
++      movne   pc, lr
++      tst     r3, #PSR_T_BIT                  @ Thumb?
++      ldrneh  r3, [r2]                        @ read aborted thumb instruction
++      ldreq   r3, [r2]                        @ read aborted ARM instruction
++      movne   r3, r3, lsl #(21 - 12)          @ move thumb bit 11 to ARM bit 20
++      tst     r3, #1 << 20                    @ L = 0 -> write
++      orreq   r1, r1, #1 << 11                @ yes.
+       mov     pc, lr
+ /*
+@@ -263,7 +259,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm926_dcache_invalidate_range)
+-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       tst     r0, #DCACHELINESIZE - 1
+       mcrne   p15, 0, r0, c7, c10, 1          @ clean D entry
+       tst     r1, #DCACHELINESIZE - 1
+@@ -288,7 +284,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm926_dcache_clean_range)
+-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       bic     r0, r0, #DCACHELINESIZE - 1
+       sub     r3, r1, r0
+       cmp     r3, #MAX_AREA_SIZE
+@@ -318,7 +314,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm926_dcache_clean_page)
+-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mov     r1, #PAGESIZE
+ 1:    mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+       add     r0, r0, #DCACHELINESIZE
+@@ -339,7 +335,7 @@
+  */
+       .align  5
+ ENTRY(cpu_arm926_dcache_clean_entry)
+-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -482,7 +478,7 @@
+       biceq   r1, r1, #4                      @ clear bufferable bit
+ #endif
+       str     r1, [r0]
+-#ifndef CONFIG_CPU_ARM926_WRITETHROUGH
++#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
+       mcr     p15, 0, r0, c7, c10, 1          @ clean D entry
+ #endif
+       mcr     p15, 0, r0, c7, c10, 4          @ drain WB
+@@ -503,7 +499,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/mm/proc-sa110.S~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/mm/proc-sa110.S
+@@ -86,12 +86,12 @@
+       .align  5
+ ENTRY(cpu_sa110_data_abort)
+ ENTRY(cpu_sa1100_data_abort)
+-      mrc     p15, 0, r3, c5, c0, 0           @ get FSR
++      mrc     p15, 0, r1, c5, c0, 0           @ get FSR
+       mrc     p15, 0, r0, c6, c0, 0           @ get FAR
+-      ldr     r1, [r2]                        @ read aborted instruction
+-      and     r3, r3, #255
+-      tst     r1, r1, lsr #21                 @ C = bit 20
+-      sbc     r1, r1, r1                      @ r1 = C - 1
++      ldr     r3, [r2]                        @ read aborted instruction
++      bic     r1, r1, #1 << 11 | 1 << 10      @ clear bits 11 and 10 of FSR
++      tst     r3, #1 << 20                    @ check write
++      orreq   r1, r1, #1 << 11
+       mov     pc, lr
+ /*
+@@ -551,7 +551,7 @@
+       bic     r2, r2, #3
+       orr     r2, r2, #HPTE_TYPE_SMALL
+-      tst     r1, #LPTE_USER | LPTE_EXEC      @ User or Exec?
++      tst     r1, #LPTE_USER                  @ User?
+       orrne   r2, r2, #HPTE_AP_READ
+       tst     r1, #LPTE_WRITE | LPTE_DIRTY    @ Write and Dirty?
+--- linux-2.4.27/arch/arm/tools/mach-types~2.4.27-vrs1
++++ linux-2.4.27/arch/arm/tools/mach-types
+@@ -6,7 +6,7 @@
+ # To add an entry into this database, please see Documentation/arm/README,
+ # or contact rmk@arm.linux.org.uk
+ #
+-# Last update: Sat Jun 28 12:10:54 2003
++# Last update: Mon Apr 19 21:11:35 2004
+ #
+ # machine_is_xxx      CONFIG_xxxx             MACH_TYPE_xxx           number
+ #
+@@ -202,7 +202,7 @@
+ fester                        SA1100_FESTER           FESTER                  191
+ gpi                   ARCH_GPI                GPI                     192
+ smdk2410              ARCH_SMDK2410           SMDK2410                193
+-premium                       ARCH_PREMIUM            PREMIUM                 194
++i519                  ARCH_I519               I519                    194
+ nexio                 SA1100_NEXIO            NEXIO                   195
+ bitbox                        SA1100_BITBOX           BITBOX                  196
+ g200                  SA1100_G200             G200                    197
+@@ -228,7 +228,7 @@
+ arnold                        SA1100_ARNOLD           ARNOLD                  217
+ psiboard              SA1100_PSIBOARD         PSIBOARD                218
+ jz8028                        ARCH_JZ8028             JZ8028                  219
+-h5400                 ARCH_IPAQ3              IPAQ3                   220
++h5400                 ARCH_H5400              H5400                   220
+ forte                 SA1100_FORTE            FORTE                   221
+ acam                  SA1100_ACAM             ACAM                    222
+ abox                  SA1100_ABOX             ABOX                    223
+@@ -259,7 +259,7 @@
+ stork_egg             ARCH_STORK_EGG          STORK_EGG               248
+ wismo                 SA1100_WISMO            WISMO                   249
+ ezlinx                        ARCH_EZLINX             EZLINX                  250
+-at91rm9200            ARCH_AT91               AT91                    251
++at91rm9200            ARCH_AT91RM9200         AT91RM9200              251
+ orion                 ARCH_ORION              ORION                   252
+ neptune                       ARCH_NEPTUNE            NEPTUNE                 253
+ hackkit                       SA1100_HACKKIT          HACKKIT                 254
+@@ -295,12 +295,12 @@
+ adsbitsyplus          SA1100_ADSBITSYPLUS     ADSBITSYPLUS            284
+ adsagc                        SA1100_ADSAGC           ADSAGC                  285
+ stp7312                       ARCH_STP7312            STP7312                 286
+-nx_phnx                       ARCH_PXA255             PXA255                  287
++nx_phnx                       MACH_NX_PHNX            NX_PHNX                 287
+ wep_ep250             ARCH_WEP_EP250          WEP_EP250               288
+ inhandelf3            ARCH_INHANDELF3         INHANDELF3              289
+ adi_coyote            ARCH_ADI_COYOTE         ADI_COYOTE              290
+ iyonix                        ARCH_IYONIX             IYONIX                  291
+-damicam_sa1110                ARCH_DAMICAM_SA1110     DAMICAM_SA1110          292
++damicam1              ARCH_DAMICAM_SA1110     DAMICAM_SA1110          292
+ meg03                 ARCH_MEG03              MEG03                   293
+ pxa_whitechapel               ARCH_PXA_WHITECHAPEL    PXA_WHITECHAPEL         294
+ nwsc                  ARCH_NWSC               NWSC                    295
+@@ -356,3 +356,172 @@
+ seedpxa_c2            ARCH_SEEDPXA_C2         SEEDPXA_C2              345
+ ixp4xx_mguardpci      ARCH_IXP4XX_MGUARD_PCI  IXP4XX_MGUARD_PCI       346
+ h1940                 ARCH_H1940              H1940                   347
++scorpio                       ARCH_SCORPIO            SCORPIO                 348
++viva                  ARCH_VIVA               VIVA                    349
++pxa_xcard             ARCH_PXA_XCARD          PXA_XCARD               350
++csb335                        ARCH_CSB335             CSB335                  351
++ixrd425                       ARCH_IXRD425            IXRD425                 352
++iq80315                       ARCH_IQ80315            IQ80315                 353
++nmp7312                       ARCH_NMP7312            NMP7312                 354
++cx861xx                       ARCH_CX861XX            CX861XX                 355
++enp2611                       ARCH_ENP2611            ENP2611                 356
++xda                   SA1100_XDA              XDA                     357
++csir_ims              ARCH_CSIR_IMS           CSIR_IMS                358
++ixp421_dnaeeth                ARCH_IXP421_DNAEETH     IXP421_DNAEETH          359
++pocketserv9200                ARCH_POCKETSERV9200     POCKETSERV9200          360
++toto                  ARCH_TOTO               TOTO                    361
++s3c2440                       ARCH_S3C2440            S3C2440                 362
++ks8695p                       ARCH_KS8695P            KS8695P                 363
++se4000                        ARCH_SE4000             SE4000                  364
++quadriceps            ARCH_QUADRICEPS         QUADRICEPS              365
++bronco                        ARCH_BRONCO             BRONCO                  366
++esl_wireless_tab      ARCH_ESL_WIRELESS_TABLETESL_WIRELESS_TABLET     367
++esl_sofcomp           ARCH_ESL_SOFCOMP        ESL_SOFCOMP             368
++s5c7375                       ARCH_S5C7375            S5C7375                 369
++spearhead             ARCH_SPEARHEAD          SPEARHEAD               370
++pantera                       ARCH_PANTERA            PANTERA                 371
++prayoglite            ARCH_PRAYOGLITE         PRAYOGLITE              372
++gumstik                       ARCH_GUMSTIK            GUMSTIK                 373
++rcube                 ARCH_RCUBE              RCUBE                   374
++rea_olv                       ARCH_REA_OLV            REA_OLV                 375
++pxa_iphone            ARCH_PXA_IPHONE         PXA_IPHONE              376
++s3c3410                       ARCH_S3C3410            S3C3410                 377
++espd_4510b            ARCH_ESPD_4510B         ESPD_4510B              378
++mp1x                  ARCH_MP1X               MP1X                    379
++at91rm9200tb          ARCH_AT91RM9200TB       AT91RM9200TB            380
++adsvgx                        ARCH_ADSVGX             ADSVGX                  381
++omap_h2                       ARCH_OMAP_H2            OMAP_H2                 382
++pelee                 ARCH_PELEE              PELEE                   383
++e740                  MACH_E740               E740                    384
++iq80331                       ARCH_IQ80331            IQ80331                 385
++versatile_pb          ARCH_VERSATILE_PB       VERSATILE_PB            387
++kev7a400              MACH_KEV7A400           KEV7A400                388
++lpd7a400              MACH_LPD7A400           LPD7A400                389
++lpd7a404              MACH_LPD7A404           LPD7A404                390
++fujitsu_camelot               ARCH_FUJITSU_CAMELOT    FUJITSU_CAMELOT         391
++janus2m                       ARCH_JANUS2M            JANUS2M                 392
++embtf                 MACH_EMBTF              EMBTF                   393
++hpm                   MACH_HPM                HPM                     394
++smdk2410tk            MACH_SMDK2410TK         SMDK2410TK              395
++smdk2410aj            MACH_SMDK2410AJ         SMDK2410AJ              396
++streetracer           MACH_STREETRACER        STREETRACER             397
++eframe                        MACH_EFRAME             EFRAME                  398
++csb337                        MACH_CSB337             CSB337                  399
++pxa_lark              MACH_PXA_LARK           PXA_LARK                400
++pxa_pnp2110           MACH_PNP2110            PNP2110                 401
++tcc72x                        MACH_TCC72X             TCC72X                  402
++altair                        MACH_ALTAIR             ALTAIR                  403
++kc3                   MACH_KC3                KC3                     404
++sinteftd              MACH_SINTEFTD           SINTEFTD                405
++mainstone             MACH_MAINSTONE          MAINSTONE               406
++aday4x                        MACH_ADAY4X             ADAY4X                  407
++lite300                       MACH_LITE300            LITE300                 408
++s5c7376                       MACH_S5C7376            S5C7376                 409
++mt02                  MACH_MT02               MT02                    410
++mport3s                       MACH_MPORT3S            MPORT3S                 411
++ra_alpha              MACH_RA_ALPHA           RA_ALPHA                412
++xcep                  MACH_XCEP               XCEP                    413
++arcom_mercury         MACH_ARCOM_MERCURY      ARCOM_MERCURY           414
++stargate              MACH_STARGATE           STARGATE                415
++armadilloj            MACH_ARMADILLOJ         ARMADILLOJ              416
++elroy_jack            MACH_ELROY_JACK         ELROY_JACK              417
++backend                       MACH_BACKEND            BACKEND                 418
++s5linbox              MACH_S5LINBOX           S5LINBOX                419
++nomadik                       MACH_NOMADIK            NOMADIK                 420
++ia_cpu_9200           MACH_IA_CPU_9200        IA_CPU_9200             421
++at91_bja1             MACH_AT91_BJA1          AT91_BJA1               422
++corgi                 MACH_CORGI              CORGI                   423
++poodle                        MACH_POODLE             POODLE                  424
++ten                   MACH_TEN                TEN                     425
++roverp5p              MACH_ROVERP5P           ROVERP5P                426
++sc2700                        MACH_SC2700             SC2700                  427
++ex_eagle              MACH_EX_EAGLE           EX_EAGLE                428
++nx_pxa12              MACH_NX_PXA12           NX_PXA12                429
++nx_pxa5                       MACH_NX_PXA5            NX_PXA5                 430
++blackboard2           MACH_BLACKBOARD2        BLACKBOARD2             431
++i819                  MACH_I819               I819                    432
++ixmb995e              MACH_IXMB995E           IXMB995E                433
++skyrider              MACH_SKYRIDER           SKYRIDER                434
++skyhawk                       MACH_SKYHAWK            SKYHAWK                 435
++enterprise            MACH_ENTERPRISE         ENTERPRISE              436
++dep2410                       MACH_DEP2410            DEP2410                 437
++armcore                       MACH_ARMCORE            ARMCORE                 438
++hobbit                        MACH_HOBBIT             HOBBIT                  439
++h7210                 MACH_H7210              H7210                   440
++pxa_netdcu5           MACH_PXA_NETDCU5        PXA_NETDCU5             441
++acc                   MACH_ACC                ACC                     442
++esl_sarva             MACH_ESL_SARVA          ESL_SARVA               443
++xm250                 MACH_XM250              XM250                   444
++t6tc1xb                       MACH_T6TC1XB            T6TC1XB                 445
++ess710                        MACH_ESS710             ESS710                  446
++mx3ads                        MACH_MX3ADS             MX3ADS                  447
++himalaya              MACH_HIMALAYA           HIMALAYA                448
++bolfenk                       MACH_BOLFENK            BOLFENK                 449
++at91rm9200kr          MACH_AT91RM9200KR       AT91RM9200KR            450
++edb9312                       MACH_EDB9312            EDB9312                 451
++omap_generic          MACH_OMAP_GENERIC       OMAP_GENERIC            452
++aximx3                        MACH_AXIMX3             AXIMX3                  453
++eb67xdip              MACH_EB67XDIP           EB67XDIP                454
++webtxs                        MACH_WEBTXS             WEBTXS                  455
++hawk                  MACH_HAWK               HAWK                    456
++ccat91sbc001          MACH_CCAT91SBC001       CCAT91SBC001            457
++expresso              MACH_EXPRESSO           EXPRESSO                458
++h4000                 MACH_H4000              H4000                   459
++dino                  MACH_DINO               DINO                    460
++ml675k                        MACH_ML675K             ML675K                  461
++edb9301                       MACH_EDB9301            EDB9301                 462
++edb9315                       MACH_EDB9315            EDB9315                 463
++reciva_tt             MACH_RECIVA_TT          RECIVA_TT               464
++cstcb01                       MACH_CSTCB01            CSTCB01                 465
++cstcb1                        MACH_CSTCB1             CSTCB1                  466
++shadwell              MACH_SHADWELL           SHADWELL                467
++goepel263             MACH_GOEPEL263          GOEPEL263               468
++acq100                        MACH_ACQ100             ACQ100                  469
++mx1fs2                        MACH_MX1FS2             MX1FS2                  470
++hiptop_g1             MACH_HIPTOP_G1          HIPTOP_G1               471
++sparky                        MACH_SPARKY             SPARKY                  472
++ns9750                        MACH_NS9750             NS9750                  473
++phoenix                       MACH_PHOENIX            PHOENIX                 474
++vr1000                        MACH_VR1000             VR1000                  475
++deisterpxa            MACH_DEISTERPXA         DEISTERPXA              476
++bcm1160                       MACH_BCM1160            BCM1160                 477
++pcm022                        MACH_PCM022             PCM022                  478
++adsgcx                        MACH_ADSGCX             ADSGCX                  479
++dreadnaught           MACH_DREADNAUGHT        DREADNAUGHT             480
++dm320                 MACH_DM320              DM320                   481
++markov                        MACH_MARKOV             MARKOV                  482
++cos7a400              MACH_COS7A400           COS7A400                483
++milano                        MACH_MILANO             MILANO                  484
++ue9328                        MACH_UE9328             UE9328                  485
++uex255                        MACH_UEX255             UEX255                  486
++ue2410                        MACH_UE2410             UE2410                  487
++a620                  MACH_A620               A620                    488
++ocelot                        MACH_OCELOT             OCELOT                  489
++cheetah                       MACH_CHEETAH            CHEETAH                 490
++omap_perseus2         MACH_OMAP_PERSEUS2      OMAP_PERSEUS2           491
++zvue                  MACH_ZVUE               ZVUE                    492
++roverp1                       MACH_ROVERP1            ROVERP1                 493
++asidial2              MACH_ASIDIAL2           ASIDIAL2                494
++s3c24a0                       MACH_S3C24A0            S3C24A0                 495
++e800                  MACH_E800               E800                    496
++e750                  MACH_E750               E750                    497
++s3c5500                       MACH_S3C5500            S3C5500                 498
++smdk5500              MACH_SMDK5500           SMDK5500                499
++signalsync            MACH_SIGNALSYNC         SIGNALSYNC              500
++nbc                   MACH_NBC                NBC                     501
++er4525                        MACH_ER4525             ER4525                  502
++netbookpro            MACH_NETBOOKPRO         NETBOOKPRO              503
++hw90200                       MACH_HW90200            HW90200                 504
++condor                        MACH_CONDOR             CONDOR                  505
++cup                   MACH_CUP                CUP                     506
++kite                  MACH_KITE               KITE                    507
++scb9328                       MACH_SCB9328            SCB9328                 508
++omap_h3                       MACH_OMAP_H3            OMAP_H3                 509
++omap_h4                       MACH_OMAP_H4            OMAP_H4                 510
++n10                   MACH_N10                N10                     511
++montajade             MACH_MONTAJADE          MONTAJADE               512
++sg560                 MACH_SG560              SG560                   513
++dp1000                        MACH_DP1000             DP1000                  514
++omap_osk              MACH_OMAP_OSK           OMAP_OSK                515
++rg100v3                       MACH_RG100V3            RG100V3                 516
++mx2ads                        MACH_MX2ADS             MX2ADS                  517
+--- linux-2.4.27/arch/i386/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/i386/config.in
+@@ -9,6 +9,7 @@
+ define_bool CONFIG_UID16 y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_option next_comment
+ comment 'Code maturity level options'
+ bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
+--- linux-2.4.27/arch/i386/kernel/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/i386/kernel/Makefile
+@@ -7,8 +7,8 @@
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+-.S.o:
+-      $(CC) $(AFLAGS) -traditional -c $< -o $*.o
++USE_STANDARD_AS_RULE := true
++EXTRA_AFLAGS := -traditional
+ all: kernel.o head.o init_task.o
+--- linux-2.4.27/arch/i386/kernel/apm.c~2.4.27-vrs1
++++ linux-2.4.27/arch/i386/kernel/apm.c
+@@ -1267,6 +1267,7 @@
+               as->suspend_wait = 0;
+               as->suspend_result = err;
+       }
++      ignore_normal_resume = 1;
+       wake_up_interruptible(&apm_suspend_waitqueue);
+       return err;
+ }
+@@ -1319,6 +1320,8 @@
+               if (ignore_bounce
+                   && ((jiffies - last_resume) > bounce_interval))
+                       ignore_bounce = 0;
++              if (ignore_normal_resume && (event != APM_NORMAL_RESUME))
++                      ignore_normal_resume = 0;
+               switch (event) {
+               case APM_SYS_STANDBY:
+--- linux-2.4.27/arch/i386/lib/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/i386/lib/Makefile
+@@ -2,8 +2,7 @@
+ # Makefile for i386-specific library files..
+ #
+-.S.o:
+-      $(CC) $(AFLAGS) -c $< -o $*.o
++USE_STANDARD_AS_RULE := true
+ L_TARGET = lib.a
+--- linux-2.4.27/arch/i386/math-emu/Makefile~2.4.27-vrs1
++++ linux-2.4.27/arch/i386/math-emu/Makefile
+@@ -2,15 +2,15 @@
+ #               Makefile for wm-FPU-emu
+ #
++USE_STANDARD_AS_RULE := true
++
+ O_TARGET := math.o
+ #DEBUG        = -DDEBUGGING
+ DEBUG =
+ PARANOID = -DPARANOID
+ CFLAGS        := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin $(MATH_EMULATION)
+-
+-.S.o:
+-      $(CC) $(AFLAGS) $(PARANOID) -c $<
++EXTRA_AFLAGS := $(PARANOID)
+ # From 'C' language sources:
+ C_OBJS =fpu_entry.o errors.o \
+--- linux-2.4.27/arch/ia64/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/ia64/config.in
+@@ -25,6 +25,7 @@
+ define_bool CONFIG_SBUS n
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ choice 'IA-64 processor type' \
+       "Itanium                CONFIG_ITANIUM \
+--- linux-2.4.27/arch/m68k/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/m68k/config.in
+@@ -6,6 +6,7 @@
+ define_bool CONFIG_UID16 y
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_name "Linux/68k Kernel Configuration"
+--- linux-2.4.27/arch/mips/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/mips/config.in
+@@ -5,5 +5,6 @@
+ define_bool CONFIG_MIPS y
+ define_bool CONFIG_MIPS32 y
+ define_bool CONFIG_MIPS64 n
++define_bool CONFIG_GENERIC_ISA_DMA y
+ source arch/mips/config-shared.in
+--- linux-2.4.27/arch/parisc/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/parisc/config.in
+@@ -9,6 +9,7 @@
+ define_bool CONFIG_UID16 n
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_option next_comment
+ comment 'Code maturity level options'
+--- linux-2.4.27/arch/ppc/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/ppc/config.in
+@@ -6,6 +6,7 @@
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
+ define_bool CONFIG_HAVE_DEC_LOCK y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_name "Linux/PowerPC Kernel Configuration"
+--- linux-2.4.27/arch/sh/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/sh/config.in
+@@ -9,6 +9,7 @@
+ define_bool CONFIG_UID16 y
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_option next_comment
+ comment 'Code maturity level options'
+--- linux-2.4.27/arch/sparc/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/sparc/config.in
+@@ -6,6 +6,7 @@
+ define_bool CONFIG_UID16 y
+ define_bool CONFIG_HIGHMEM y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ mainmenu_option next_comment
+ comment 'Code maturity level options'
+--- linux-2.4.27/arch/sparc64/config.in~2.4.27-vrs1
++++ linux-2.4.27/arch/sparc64/config.in
+@@ -43,6 +43,7 @@
+ define_bool CONFIG_HAVE_DEC_LOCK y
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
++define_bool CONFIG_GENERIC_ISA_DMA y
+ define_bool CONFIG_ISA n
+ define_bool CONFIG_ISAPNP n
+ define_bool CONFIG_EISA n
+--- linux-2.4.27/drivers/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/Makefile
+@@ -8,9 +8,9 @@
+ mod-subdirs :=        dio hil mtd sbus video macintosh usb input telephony ide \
+               message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
+-              fc4 net/hamradio i2c acpi bluetooth usb/gadget
++              fc4 net/hamradio i2c l3 acpi bluetooth serial usb/gadget
+-subdir-y :=   parport char block net sound misc media cdrom hotplug
++subdir-y :=   parport serial char block net sound misc media cdrom hotplug pld
+ subdir-m :=   $(subdir-y)
+@@ -45,8 +45,12 @@
+ # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set  -- ch
+ subdir-$(CONFIG_HAMRADIO)     += net/hamradio
+ subdir-$(CONFIG_I2C)          += i2c
++subdir-$(CONFIG_L3)           += l3
+ subdir-$(CONFIG_ACPI_BOOT)    += acpi
+ subdir-$(CONFIG_BLUEZ)                += bluetooth
++subdir-$(CONFIG_SSI)          += ssi
++
++subdir-$(CONFIG_ARCH_AT91RM9200)+= at91
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.27/drivers/acorn/char/i2c.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/char/i2c.c
+@@ -33,9 +33,13 @@
+ static struct i2c_client *rtc_client;
+ static const unsigned char days_in_mon[] = 
+       { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+-static unsigned int rtc_epoch = 1900;
+ #define CMOS_CHECKSUM (63)
++
++/*
++ * Acorn machines store the year in the static RAM at
++ * location 128.
++ */
+ #define CMOS_YEAR     (64 + 128)
+ static inline int rtc_command(int cmd, void *data)
+@@ -49,51 +53,91 @@
+ }
+ /*
++ * Update the century + year bytes in the CMOS RAM, ensuring
++ * that the check byte is correctly adjusted for the change.
++ */
++static int rtc_update_year(unsigned int new_year)
++{
++      unsigned char yr[2], chk;
++      struct mem cmos_year  = { CMOS_YEAR, sizeof(yr), yr };
++      struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
++      int ret;
++
++      ret = rtc_command(MEM_READ, &cmos_check);
++      if (ret)
++              goto out;
++      ret = rtc_command(MEM_READ, &cmos_year);
++      if (ret)
++              goto out;
++
++      chk -= yr[1] + yr[0];
++
++      yr[1] = new_year / 100;
++      yr[0] = new_year % 100;
++
++      chk += yr[1] + yr[0];
++
++      ret = rtc_command(MEM_WRITE, &cmos_year);
++      if (ret == 0)
++              ret = rtc_command(MEM_WRITE, &cmos_check);
++ out:
++      return ret;
++}
++
++
++/*
+  * Read the current RTC time and date, and update xtime.
+  */
+ static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year)
+ {
+       unsigned char ctrl, yr[2];
+       struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr };
++      int real_year, year_offset;
+       /*
+        * Ensure that the RTC is running.
+        */
+       rtc_command(RTC_GETCTRL, &ctrl);
+       if (ctrl & 0xc0) {
+-              unsigned char new_ctrl;
+-
+-              new_ctrl = ctrl & ~0xc0;
++              unsigned char new_ctrl = ctrl & ~0xc0;
+-              printk("RTC: resetting control %02X -> %02X\n",
+-                     ctrl, new_ctrl);
++              printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n",
++                       ctrl, new_ctrl);
+               rtc_command(RTC_SETCTRL, &new_ctrl);
+       }
++      if (rtc_command(RTC_GETDATETIME, rtctm) ||
++          rtc_command(MEM_READ, &rtcmem))
++              return;
++
++      real_year = yr[0];
++
+       /*
+-       * Acorn machines store the year in
+-       * the static RAM at location 192.
++       * The RTC year holds the LSB two bits of the current
++       * year, which should reflect the LSB two bits of the
++       * CMOS copy of the year.  Any difference indicates
++       * that we have to correct the CMOS version.
+        */
+-      if (rtc_command(MEM_READ, &rtcmem))
+-              return;
++      year_offset = rtctm->year_off - (real_year & 3);
++      if (year_offset < 0)
++              /*
++               * RTC year wrapped.  Adjust it appropriately.
++               */
++              year_offset += 4;
+-      if (rtc_command(RTC_GETDATETIME, rtctm))
+-              return;
++      *year = real_year + year_offset + yr[1] * 100;
+-      *year = yr[1] * 100 + yr[0];
+ }
+ static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year)
+ {
+-      unsigned char yr[2], leap, chk;
+-      struct mem cmos_year  = { CMOS_YEAR, sizeof(yr), yr };
+-      struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
++      unsigned char leap;
+       int ret;
+       leap = (!(year % 4) && (year % 100)) || !(year % 400);
+-      if (rtctm->mon > 12 || rtctm->mday == 0)
++      if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0)
+               return -EINVAL;
+       if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap)))
+@@ -102,21 +146,16 @@
+       if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60)
+               return -EINVAL;
+-      ret = rtc_command(RTC_SETDATETIME, rtctm);
+-      if (ret == 0) {
+-              rtc_command(MEM_READ, &cmos_check);
+-              rtc_command(MEM_READ, &cmos_year);
+-
+-              chk -= yr[1] + yr[0];
+-
+-              yr[1] = year / 100;
+-              yr[0] = year % 100;
++      /*
++       * The RTC's own 2-bit year must reflect the least
++       * significant two bits of the CMOS year.
++       */
++      rtctm->year_off = (year % 100) & 3;
+-              chk += yr[1] + yr[0];
++      ret = rtc_command(RTC_SETDATETIME, rtctm);
++      if (ret == 0)
++              ret = rtc_update_year(year);
+-              rtc_command(MEM_WRITE, &cmos_year);
+-              rtc_command(MEM_WRITE, &cmos_check);
+-      }
+       return ret;
+ }
+@@ -166,7 +205,6 @@
+               break;
+       case RTC_RD_TIME:
+-              memset(&rtctm, 0, sizeof(struct rtc_time));
+               get_rtc_time(&rtc_raw, &year);
+               rtctm.tm_sec  = rtc_raw.secs;
+               rtctm.tm_min  = rtc_raw.mins;
+@@ -188,13 +226,12 @@
+               rtc_raw.hours    = rtctm.tm_hour;
+               rtc_raw.mday     = rtctm.tm_mday;
+               rtc_raw.mon      = rtctm.tm_mon + 1;
+-              rtc_raw.year_off = 2;
+               year             = rtctm.tm_year + 1900;
+               return set_rtc_time(&rtc_raw, year);
+               break;
+       case RTC_EPOCH_READ:
+-              return put_user(rtc_epoch, (unsigned long *)arg);
++              return put_user(1900, (unsigned long *)arg);
+       }
+       return -EINVAL;
+--- linux-2.4.27/drivers/acorn/net/ether1.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/net/ether1.c
+@@ -80,7 +80,7 @@
+ #define BUS_16 16
+ #define BUS_8  8
+-static const card_ids __init ether1_cids[] = {
++static card_ids __initdata ether1_cids[] = {
+       { MANU_ACORN, PROD_ACORN_ETHER1 },
+       { 0xffff, 0xffff }
+ };
+@@ -153,35 +153,35 @@
+               length -= thislen;
+               __asm__ __volatile__(
+-      "subs   %3, %3, #2
+-      bmi     2f
+-1:    ldr     %0, [%1], #2
+-      mov     %0, %0, lsl #16
+-      orr     %0, %0, %0, lsr #16
+-      str     %0, [%2], #4
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%1], #2
+-      mov     %0, %0, lsl #16
+-      orr     %0, %0, %0, lsr #16
+-      str     %0, [%2], #4
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%1], #2
+-      mov     %0, %0, lsl #16
+-      orr     %0, %0, %0, lsr #16
+-      str     %0, [%2], #4
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%1], #2
+-      mov     %0, %0, lsl #16
+-      orr     %0, %0, %0, lsr #16
+-      str     %0, [%2], #4
+-      subs    %3, %3, #2
+-      bpl     1b
+-2:    adds    %3, %3, #1
+-      ldreqb  %0, [%1]
+-      streqb  %0, [%2]"
++"     subs    %3, %3, #2           \n"
++"     bmi     2f                   \n"
++"1:   ldr     %0, [%1], #2         \n"
++"     mov     %0, %0, lsl #16      \n"
++"     orr     %0, %0, %0, lsr #16  \n"
++"     str     %0, [%2], #4         \n"
++"     subs    %3, %3, #2           \n"
++"     bmi     2f                   \n"
++"     ldr     %0, [%1], #2         \n"
++"     mov     %0, %0, lsl #16      \n"
++"     orr     %0, %0, %0, lsr #16  \n"
++"     str     %0, [%2], #4         \n"
++"     subs    %3, %3, #2           \n"
++"     bmi     2f                   \n"
++"     ldr     %0, [%1], #2         \n"
++"     mov     %0, %0, lsl #16      \n"
++"     orr     %0, %0, %0, lsr #16  \n"
++"     str     %0, [%2], #4         \n"
++"     subs    %3, %3, #2           \n"
++"     bmi     2f                   \n"
++"     ldr     %0, [%1], #2         \n"
++"     mov     %0, %0, lsl #16      \n"
++"     orr     %0, %0, %0, lsr #16  \n"
++"     str     %0, [%2], #4         \n"
++"     subs    %3, %3, #2           \n"
++"     bpl     1b                   \n"
++"2:   adds    %3, %3, #1           \n"
++"     ldreqb  %0, [%1]             \n"
++"     streqb  %0, [%2]             \n"
+               : "=&r" (used), "=&r" (data)
+               : "r"  (addr), "r" (thislen), "1" (data));
+@@ -215,35 +215,35 @@
+               length -= thislen;
+               __asm__ __volatile__(
+-      "subs   %3, %3, #2
+-      bmi     2f
+-1:    ldr     %0, [%2], #4
+-      strb    %0, [%1], #1
+-      mov     %0, %0, lsr #8
+-      strb    %0, [%1], #1
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%2], #4
+-      strb    %0, [%1], #1
+-      mov     %0, %0, lsr #8
+-      strb    %0, [%1], #1
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%2], #4
+-      strb    %0, [%1], #1
+-      mov     %0, %0, lsr #8
+-      strb    %0, [%1], #1
+-      subs    %3, %3, #2
+-      bmi     2f
+-      ldr     %0, [%2], #4
+-      strb    %0, [%1], #1
+-      mov     %0, %0, lsr #8
+-      strb    %0, [%1], #1
+-      subs    %3, %3, #2
+-      bpl     1b
+-2:    adds    %3, %3, #1
+-      ldreqb  %0, [%2]
+-      streqb  %0, [%1]"
++"     subs    %3, %3, #2      \n"
++"     bmi     2f              \n"
++"1:   ldr     %0, [%2], #4    \n"
++"     strb    %0, [%1], #1    \n"
++"     mov     %0, %0, lsr #8  \n"
++"     strb    %0, [%1], #1    \n"
++"     subs    %3, %3, #2      \n"
++"     bmi     2f              \n"
++"     ldr     %0, [%2], #4    \n"
++"     strb    %0, [%1], #1    \n"
++"     mov     %0, %0, lsr #8  \n"
++"     strb    %0, [%1], #1    \n"
++"     subs    %3, %3, #2      \n"
++"     bmi     2f              \n"
++"     ldr     %0, [%2], #4    \n"
++"     strb    %0, [%1], #1    \n"
++"     mov     %0, %0, lsr #8  \n"
++"     strb    %0, [%1], #1    \n"
++"     subs    %3, %3, #2      \n"
++"     bmi     2f              \n"
++"     ldr     %0, [%2], #4    \n"
++"     strb    %0, [%1], #1    \n"
++"     mov     %0, %0, lsr #8  \n"
++"     strb    %0, [%1], #1    \n"
++"     subs    %3, %3, #2      \n"
++"     bpl     1b              \n"
++"2:   adds    %3, %3, #1      \n"
++"     ldreqb  %0, [%2]        \n"
++"     streqb  %0, [%1]        \n"
+               : "=&r" (used), "=&r" (data)
+               : "r"  (addr), "r" (thislen), "1" (data));
+--- linux-2.4.27/drivers/acorn/net/ether3.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/net/ether3.c
+@@ -75,7 +75,7 @@
+ #include "ether3.h"
+ static unsigned int net_debug = NET_DEBUG;
+-static const card_ids __init ether3_cids[] = {
++static card_ids __initdata ether3_cids[] = {
+       { MANU_ANT2, PROD_ANT_ETHER3 },
+       { MANU_ANT,  PROD_ANT_ETHER3 },
+       { MANU_ANT,  PROD_ANT_ETHERB },
+--- linux-2.4.27/drivers/acorn/net/etherh.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/net/etherh.c
+@@ -57,7 +57,7 @@
+ static unsigned int net_debug = NET_DEBUG;
+-static const card_ids __init etherh_cids[] = {
++static card_ids __initdata etherh_cids[] = {
+       { MANU_ANT, PROD_ANT_ETHERM      },
+       { MANU_I3,  PROD_I3_ETHERLAN500  },
+       { MANU_I3,  PROD_I3_ETHERLAN600  },
+--- linux-2.4.27/drivers/acorn/scsi/cumana_1.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/scsi/cumana_1.c
+@@ -153,20 +153,20 @@
+         ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
+         outb(0x00, instance->io_port - 577);
+-      if (instance->irq != IRQ_NONE)
++      if (instance->irq != SCSI_IRQ_NONE)
+           if (request_irq(instance->irq, do_cumanascsi_intr, SA_INTERRUPT, "CumanaSCSI-1", NULL)) {
+               printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+                   instance->host_no, instance->irq);
+-              instance->irq = IRQ_NONE;
++              instance->irq = SCSI_IRQ_NONE;
+           }
+-      if (instance->irq == IRQ_NONE) {
++      if (instance->irq == SCSI_IRQ_NONE) {
+           printk("scsi%d: interrupts not enabled. for better interactive performance,\n", instance->host_no);
+           printk("scsi%d: please jumper the board for a free IRQ.\n", instance->host_no);
+       }
+       printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port);
+-      if (instance->irq == IRQ_NONE)
++      if (instance->irq == SCSI_IRQ_NONE)
+           printk ("s disabled");
+       else
+           printk (" %d", instance->irq);
+@@ -185,7 +185,7 @@
+ {
+       int i;
+-      if (shpnt->irq != IRQ_NONE)
++      if (shpnt->irq != SCSI_IRQ_NONE)
+               free_irq (shpnt->irq, NULL);
+       if (shpnt->io_port)
+               release_region (shpnt->io_port, shpnt->n_io_port);
+--- linux-2.4.27/drivers/acorn/scsi/ecoscsi.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/scsi/ecoscsi.c
+@@ -106,7 +106,7 @@
+     instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+     instance->io_port = 0x80ce8000;
+     instance->n_io_port = 144;
+-    instance->irq = IRQ_NONE;
++    instance->irq = SCSI_IRQ_NONE;
+     if (check_region (instance->io_port, instance->n_io_port)) {
+       scsi_unregister (instance);
+@@ -130,20 +130,20 @@
+       return 0;
+     }
+-    if (instance->irq != IRQ_NONE)
++    if (instance->irq != SCSI_IRQ_NONE)
+       if (request_irq(instance->irq, do_ecoscsi_intr, SA_INTERRUPT, "ecoscsi", NULL)) {
+           printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+           instance->host_no, instance->irq);
+-          instance->irq = IRQ_NONE;
++          instance->irq = SCSI_IRQ_NONE;
+       }
+-    if (instance->irq != IRQ_NONE) {
++    if (instance->irq != SCSI_IRQ_NONE) {
+       printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+       printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+     }
+     printk("scsi%d: at port %X irq", instance->host_no, instance->io_port);
+-    if (instance->irq == IRQ_NONE)
++    if (instance->irq == SCSI_IRQ_NONE)
+       printk ("s disabled");
+     else
+         printk (" %d", instance->irq);
+@@ -157,7 +157,7 @@
+ int ecoscsi_release (struct Scsi_Host *shpnt)
+ {
+-      if (shpnt->irq != IRQ_NONE)
++      if (shpnt->irq != SCSI_IRQ_NONE)
+               free_irq (shpnt->irq, NULL);
+       if (shpnt->io_port)
+               release_region (shpnt->io_port, shpnt->n_io_port);
+--- linux-2.4.27/drivers/acorn/scsi/oak.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/acorn/scsi/oak.c
+@@ -97,7 +97,7 @@
+ };
+ #define OAK_ADDRESS(card) (ecard_address((card), ECARD_MEMC, 0))
+-#define OAK_IRQ(card)   (IRQ_NONE)
++#define OAK_IRQ(card)   (SCSI_IRQ_NONE)
+ /*
+  * Function : int oakscsi_detect(Scsi_Host_Template * tpnt)
+  *
+@@ -136,20 +136,20 @@
+       instance->n_io_port = 255;
+       request_region (instance->io_port, instance->n_io_port, "Oak SCSI");
+-      if (instance->irq != IRQ_NONE)
++      if (instance->irq != SCSI_IRQ_NONE)
+           if (request_irq(instance->irq, do_oakscsi_intr, SA_INTERRUPT, "Oak SCSI", NULL)) {
+               printk("scsi%d: IRQ%d not free, interrupts disabled\n",
+                   instance->host_no, instance->irq);
+-              instance->irq = IRQ_NONE;
++              instance->irq = SCSI_IRQ_NONE;
+           }
+-      if (instance->irq != IRQ_NONE) {
++      if (instance->irq != SCSI_IRQ_NONE) {
+           printk("scsi%d: eek! Interrupts enabled, but I don't think\n", instance->host_no);
+           printk("scsi%d: that the board had an interrupt!\n", instance->host_no);
+       }
+       printk("scsi%d: at port %lX irq", instance->host_no, instance->io_port);
+-      if (instance->irq == IRQ_NONE)
++      if (instance->irq == SCSI_IRQ_NONE)
+           printk ("s disabled");
+       else
+           printk (" %d", instance->irq);
+@@ -172,7 +172,7 @@
+ {
+       int i;
+-      if (shpnt->irq != IRQ_NONE)
++      if (shpnt->irq != SCSI_IRQ_NONE)
+               free_irq (shpnt->irq, NULL);
+       if (shpnt->io_port)
+               release_region (shpnt->io_port, shpnt->n_io_port);
+--- /dev/null
++++ linux-2.4.27/drivers/at91/Makefile
+@@ -0,0 +1,23 @@
++#
++# Makefile for the AT91RM9200-specific Linux kernel 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 (not a .c file).
++
++O_TARGET := at91drv.o
++
++subdir-y :=   serial net watchdog rtc usb i2c spi mtd
++subdir-m :=   $(subdir-y)
++
++obj-$(CONFIG_SERIAL_AT91)             += serial/at91serial.o
++obj-$(CONFIG_AT91_ETHER)              += net/at91net.o
++obj-$(CONFIG_AT91_WATCHDOG)           += watchdog/at91wdt.o
++obj-$(CONFIG_AT91_RTC)                        += rtc/at91rtc.o
++obj-$(CONFIG_USB)                     += usb/at91usb.o
++obj-$(CONFIG_I2C_AT91)                        += i2c/at91i2c.o
++obj-$(CONFIG_AT91_SPIDEV)             += spi/at91spi.o
++obj-$(CONFIG_MTD_AT91_DATAFLASH)      += spi/at91spi.o mtd/at91mtd.o
++obj-$(CONFIG_MTD_AT91_SMARTMEDIA)     += mtd/at91mtd.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/i2c/Makefile
+@@ -0,0 +1,15 @@
++# File: drivers/at91/i2c/Makefile
++#
++# Makefile for the Atmel AT91RM9200 I2C (TWI) device drivers
++#
++
++O_TARGET := at91i2c.o
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_I2C_AT91) += at91_i2c.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/i2c/at91_i2c.c
+@@ -0,0 +1,257 @@
++/*
++    i2c Support for Atmel's AT91RM9200 Two-Wire Interface
++
++    (c) Rick Bronson
++
++    Borrowed heavily from original work by:
++    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++*/
++
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/init.h>
++
++#include <asm/arch/AT91RM9200_TWI.h>
++#include <asm/arch/pio.h>
++#include "at91_i2c.h"
++
++#define DBG(x...) do {\
++      if (debug > 0) \
++              printk(KERN_DEBUG "i2c:" x); \
++      } while(0)
++
++int debug = 0;
++
++static struct at91_i2c_local *at91_i2c_device;
++
++/*
++ * Poll the i2c status register until the specified bit is set.
++ * Returns 0 if timed out (100 msec)
++ */
++static short at91_poll_status(AT91PS_TWI twi, unsigned long bit) {
++      int loop_cntr = 10000;
++      do {
++              udelay(10);
++      } while (!(twi->TWI_SR & bit) && (--loop_cntr > 0));
++
++      return (loop_cntr > 0);
++}
++
++/*
++ * Generic i2c master transfer entrypoint
++ */
++static int at91_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
++{
++      struct at91_i2c_local *device = (struct at91_i2c_local *) adap->data;
++      AT91PS_TWI twi = (AT91PS_TWI) device->base_addr;
++
++      struct i2c_msg *pmsg;
++      int length;
++      unsigned char *buf;
++
++      /*
++       * i2c_smbus_xfer_emulated() in drivers/i2c/i2c-core.c states:
++       * "... In the case of writing, we need to use only one message;
++       * when reading, we need two..."
++       */
++
++      pmsg = msgs;            /* look at 1st message, it contains the address/command */
++      if (num >= 1 && num <= 2) {
++              DBG("xfer: doing %s %d bytes to 0x%02x - %d messages\n",
++                  pmsg->flags & I2C_M_RD ? "read" : "write",
++                  pmsg->len, pmsg->buf[0], num);
++
++              /* Set the TWI Master Mode Register */
++              twi->TWI_MMR = (pmsg->addr << 16) | (pmsg->len << 8)
++                      | ((pmsg + 1)->flags & I2C_M_RD ? AT91C_TWI_MREAD : 0);
++
++              /* Set TWI Internal Address Register with first messages data field */
++              if (pmsg->len == 1)
++                      twi->TWI_IADR = pmsg->buf[0];
++              else if (pmsg->len == 2)
++                      twi->TWI_IADR = pmsg->buf[0] << 8 | pmsg->buf[1];
++              else                    /* must be 3 */
++                      twi->TWI_IADR =  pmsg->buf[0] << 16 | pmsg->buf[1] << 8 | pmsg->buf[2];
++
++              /* 1st message contains the address/command */
++              if (num > 1)
++                      pmsg++;         /* go to real message */
++
++              length = pmsg->len;
++              buf = pmsg->buf;
++              if (length && buf) {    /* sanity check */
++                      if (pmsg->flags & I2C_M_RD) {
++                              twi->TWI_CR = AT91C_TWI_START;
++                              while (length--) {
++                                      if (!length)
++                                              twi->TWI_CR = AT91C_TWI_STOP;
++                                      /* Wait until transfer is finished */
++                                      if (!at91_poll_status(twi, AT91C_TWI_RXRDY)) {
++                                              printk(KERN_ERR "at91_i2c: timeout 1\n");
++                                              return 0;
++                                      }
++                                      *buf++ = twi->TWI_RHR;
++                              }
++                              if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
++                                      printk(KERN_ERR "at91_i2c: timeout 2\n");
++                                      return 0;
++                              }
++                      } else {
++                              twi->TWI_CR = AT91C_TWI_START;
++                              while (length--) {
++                                      twi->TWI_THR = *buf++;
++                                      if (!length)
++                                              twi->TWI_CR = AT91C_TWI_STOP;
++                                      if (!at91_poll_status(twi, AT91C_TWI_TXRDY)) {
++                                              printk(KERN_ERR "at91_i2c: timeout 3\n");
++                                              return 0;
++                                      }
++                              }
++                              /* Wait until transfer is finished */
++                              if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
++                                      printk(KERN_ERR "at91_i2c: timeout 4\n");
++                                      return 0;
++                              }
++                      }
++              }
++              DBG("transfer complete\n");
++              return num;
++      }
++      else {
++              printk(KERN_ERR "at91_i2c: unexpected number of messages: %d\n", num);
++              return 0;
++      }
++}
++
++/*
++ * Return list of supported functionality
++ */
++static u32 at91_func(struct i2c_adapter *adapter)
++{
++      return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE
++              | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA
++              | I2C_FUNC_SMBUS_BLOCK_DATA;
++}
++
++/*
++ * Open
++ */
++static void at91_inc(struct i2c_adapter *adapter)
++{
++      MOD_INC_USE_COUNT;
++}
++
++/*
++ * Close
++ */
++static void at91_dec(struct i2c_adapter *adapter)
++{
++      MOD_DEC_USE_COUNT;
++}
++
++/* For now, we only handle combined mode (smbus) */
++static struct i2c_algorithm at91_algorithm = {
++      name:"at91 i2c",
++      id:I2C_ALGO_SMBUS,
++      master_xfer:at91_xfer,
++      functionality:at91_func,
++};
++
++/*
++ * Main initialization routine
++ */
++static int __init i2c_at91_init(void)
++{
++      AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI;
++      struct at91_i2c_local *device;
++      int rc;
++
++      AT91_CfgPIO_TWI();
++      AT91_SYS->PMC_PCER = 1 << AT91C_ID_TWI;         /* enable peripheral clock */
++
++      twi->TWI_IDR = 0x3ff;                           /* Disable all interrupts */
++      twi->TWI_CR = AT91C_TWI_SWRST;                  /* Reset peripheral */
++      twi->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS; /* Set Master mode */
++
++      /* Here, CKDIV = 1 and CHDIV=CLDIV  ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */
++      twi->TWI_CWGR = AT91C_TWI_CKDIV1 | AT91C_TWI_CLDIV3 | (AT91C_TWI_CLDIV3 << 8);
++
++      device = (struct at91_i2c_local *) kmalloc(sizeof(struct at91_i2c_local), GFP_KERNEL);
++      if (device == NULL) {
++              printk(KERN_ERR "at91_i2c: can't allocate inteface!\n");
++              return -ENOMEM;
++      }
++      memset(device, 0, sizeof(struct at91_i2c_local));
++      at91_i2c_device = device;
++
++      sprintf(device->adapter.name, "AT91RM9200");
++      device->adapter.data = (void *) device;
++      device->adapter.id = I2C_ALGO_SMBUS;
++      device->adapter.algo = &at91_algorithm;
++      device->adapter.algo_data = NULL;
++      device->adapter.inc_use = at91_inc;
++      device->adapter.dec_use = at91_dec;
++      device->adapter.client_register = NULL;
++      device->adapter.client_unregister = NULL;
++      device->base_addr = AT91C_VA_BASE_TWI;
++
++      rc = i2c_add_adapter(&device->adapter);
++      if (rc) {
++              printk(KERN_ERR "at91_i2c: Adapter %s registration failed\n", device->adapter.name);
++              device->adapter.data = NULL;
++              kfree(device);
++      }
++      else
++              printk(KERN_INFO "Found AT91 i2c\n");
++      return rc;
++}
++
++/*
++ * Clean up routine
++ */
++static void __exit i2c_at91_cleanup(void)
++{
++      struct at91_i2c_local *device = at91_i2c_device;
++      int rc;
++
++      rc = i2c_del_adapter(&device->adapter);
++      device->adapter.data = NULL;
++      kfree(device);
++      
++      AT91_SYS->PMC_PCDR = 1 << AT91C_ID_TWI;         /* disable peripheral clock */
++
++      /* We aren't that prepared to deal with this... */
++      if (rc)
++              printk(KERN_ERR "at91_i2c: i2c_del_adapter failed (%i), that's bad!\n", rc);
++}
++
++module_init(i2c_at91_init);
++module_exit(i2c_at91_cleanup);
++
++MODULE_AUTHOR("Rick Bronson");
++MODULE_DESCRIPTION("I2C driver for Atmel AT91RM9200");
++MODULE_LICENSE("GPL");
++MODULE_PARM(debug, "i");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/at91/i2c/at91_i2c.h
+@@ -0,0 +1,43 @@
++/*
++    i2c Support for Atmel's AT91RM9200 Two-Wire Interface
++
++    (c) Rick Bronson
++
++    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., 675 Mass Ave, Cambridge, MA 02139, USA.
++*/
++
++#ifndef AT91_I2C_H
++#define AT91_I2C_H
++
++#define AT91C_TWI_CLOCK               100000
++#define AT91C_TWI_SCLOCK      (10 * AT91C_MASTER_CLOCK / AT91C_TWI_CLOCK)
++#define AT91C_TWI_CKDIV1      (2 << 16)       /* TWI clock divider.  NOTE: see Errata #22 */
++
++#if (AT91C_TWI_SCLOCK % 10) >= 5
++#define AT91C_TWI_CLDIV2 ((AT91C_TWI_SCLOCK / 10) - 5)
++#else
++#define AT91C_TWI_CLDIV2 ((AT91C_TWI_SCLOCK / 10) - 6)
++#endif
++#define AT91C_TWI_CLDIV3 ((AT91C_TWI_CLDIV2 + (4 - AT91C_TWI_CLDIV2 % 4)) >> 2)
++
++#define AT91C_EEPROM_I2C_ADDRESS        (0x50 << 16)
++
++/* Physical interface */
++struct at91_i2c_local {
++      struct i2c_adapter adapter;
++      unsigned long base_addr;
++};
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/at91/mtd/Makefile
+@@ -0,0 +1,19 @@
++# File: drivers/at91/mtd/Makefile
++#
++# Makefile for the Atmel AT91RM9200 MTD devices.
++#   Includes: NAND flash (SmartMedia) & DataFlash
++#
++
++O_TARGET := at91mtd.o
++
++export-objs :=
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_MTD_AT91_DATAFLASH) += at91_dataflash.o
++obj-$(CONFIG_MTD_AT91_SMARTMEDIA) += at91_nand.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/mtd/at91_dataflash.c
+@@ -0,0 +1,540 @@
++/*
++ * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/arch/AT91RM9200_SPI.h>
++#include <asm/arch/pio.h>
++#include "at91_dataflash.h"
++#include "../spi/at91_spi.h"
++
++#undef DEBUG_DATAFLASH
++
++/* Detected DataFlash devices */
++static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
++static int nr_devices = 0;
++
++/* ......................................................................... */
++
++#ifdef CONFIG_MTD_PARTITIONS
++
++static struct mtd_partition *mtd_parts = 0;
++static int mtd_parts_nr = 0;
++
++#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
++
++static struct mtd_partition static_partitions[] =
++{
++      {
++              name:           "bootloader",
++              offset:         0,
++              size:           64 * 1024,              /* 64 Kb */
++              mask_flags:     MTD_WRITEABLE           /* read-only */
++      },
++      {
++              name:           "kernel",
++              offset:         MTDPART_OFS_NXTBLK,
++              size:           768 *1024,              /* 768 Kb */
++      },
++      {
++              name:           "filesystem",
++              offset:         MTDPART_OFS_NXTBLK,
++              size:           MTDPART_SIZ_FULL,
++      }
++};
++
++int parse_cmdline_partitions(struct mtd_info *master,
++              struct mtd_partition **pparts, const char *mtd_id);
++
++#endif
++
++/* ......................................................................... */
++
++/* Allocate a single SPI transfer descriptor.  We're assuming that if multiple
++   SPI transfers occur at the same time, spi_access_bus() will serialize them.
++   If this is not valid, then either (i) each dataflash 'priv' structure
++   needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
++   another mechanism.   */
++static struct spi_transfer_list* spi_transfer_desc;
++
++/*
++ * Perform a SPI transfer to access the DataFlash device.
++ */
++static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
++              char* txnext, int txnext_len, char* rxnext, int rxnext_len)
++{
++      struct spi_transfer_list* list = spi_transfer_desc;
++
++      list->tx[0] = tx;       list->txlen[0] = tx_len;
++      list->rx[0] = rx;       list->rxlen[0] = rx_len;
++
++      list->tx[1] = txnext;   list->txlen[1] = txnext_len;
++      list->rx[1] = rxnext;   list->rxlen[1] = rxnext_len;
++
++      list->nr_transfers = nr;
++
++      return spi_transfer(list);
++}
++
++/* ......................................................................... */
++
++/*
++ * Poll the DataFlash device until it is READY.
++ */
++static void at91_dataflash_waitready(void)
++{
++      char* command = kmalloc(2, GFP_KERNEL);
++
++      if (!command)
++              return;
++
++      do {
++              command[0] = OP_READ_STATUS;
++              command[1] = 0;
++
++              do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
++      } while ((command[1] & 0x80) == 0);
++
++      kfree(command);
++}
++
++/*
++ * Return the status of the DataFlash device.
++ */
++static unsigned short at91_dataflash_status(void)
++{
++      unsigned short status;
++      char* command = kmalloc(2, GFP_KERNEL);
++
++      if (!command)
++              return 0;
++
++      command[0] = OP_READ_STATUS;
++      command[1] = 0;
++
++      do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
++      status = command[1];
++
++      kfree(command);
++      return status;
++}
++
++/* ......................................................................... */
++
++/*
++ * Erase blocks of flash.
++ */
++static int at91_dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++      struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
++      unsigned int pageaddr;
++      char* command;
++
++#ifdef DEBUG_DATAFLASH
++      printk("dataflash_erase: addr=%i len=%i\n", instr->addr, instr->len);
++#endif
++
++      /* Sanity checks */
++      if (instr->addr + instr->len > mtd->size)
++              return -EINVAL;
++      if ((instr->len % mtd->erasesize != 0) || (instr->len % priv->page_size != 0))
++              return -EINVAL;
++      if ((instr->addr % priv->page_size) != 0)
++              return -EINVAL;
++
++      command = kmalloc(4, GFP_KERNEL);
++      if (!command)
++              return -ENOMEM;
++
++      while (instr->len > 0) {
++              /* Calculate flash page address */
++              pageaddr = (instr->addr / priv->page_size) << priv->page_offset;
++
++              command[0] = OP_ERASE_PAGE;
++              command[1] = (pageaddr & 0x00FF0000) >> 16;
++              command[2] = (pageaddr & 0x0000FF00) >> 8;
++              command[3] = 0;
++#ifdef DEBUG_DATAFLASH
++              printk("ERASE: (%x) %x %x %x [%i]\n", command[0], command[1], command[2], command[3], pageaddr);
++#endif
++
++              /* Send command to SPI device */
++              spi_access_bus(priv->spi);
++              do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0);
++
++              at91_dataflash_waitready();             /* poll status until ready */
++              spi_release_bus(priv->spi);
++
++              instr->addr += priv->page_size;         /* next page */
++              instr->len -= priv->page_size;
++      }
++
++      kfree(command);
++
++      /* Inform MTD subsystem that erase is complete */
++      instr->state = MTD_ERASE_DONE;
++      if (instr->callback)
++              instr->callback(instr);
++
++      return 0;
++}
++
++/*
++ * Read from the DataFlash device.
++ *   from   : Start offset in flash device
++ *   len    : Amount to read
++ *   retlen : About of data actually read
++ *   buf    : Buffer containing the data
++ */
++static int at91_dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
++{
++      struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
++      unsigned int addr;
++      char* command;
++
++#ifdef DEBUG_DATAFLASH
++      printk("dataflash_read: %lli .. %lli\n", from, from+len);
++#endif
++
++      *retlen = 0;
++
++      /* Sanity checks */
++      if (!len)
++              return 0;
++      if (from + len > mtd->size)
++              return -EINVAL;
++
++      /* Calculate flash page/byte address */
++      addr = (((unsigned)from / priv->page_size) << priv->page_offset) + ((unsigned)from % priv->page_size);
++
++      command = kmalloc(8, GFP_KERNEL);
++      if (!command)
++              return -ENOMEM;
++
++      command[0] = OP_READ_CONTINUOUS;
++      command[1] = (addr & 0x00FF0000) >> 16;
++      command[2] = (addr & 0x0000FF00) >> 8;
++      command[3] = (addr & 0x000000FF);
++#ifdef DEBUG_DATAFLASH
++      printk("READ: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]);
++#endif
++
++      /* Send command to SPI device */
++      spi_access_bus(priv->spi);
++      do_spi_transfer(2, command, 8, command, 8, buf, len, buf, len);
++      spi_release_bus(priv->spi);
++
++      *retlen = len;
++      kfree(command);
++      return 0;
++}
++
++/*
++ * Write to the DataFlash device.
++ *   to     : Start offset in flash device
++ *   len    : Amount to write
++ *   retlen : Amount of data actually written
++ *   buf    : Buffer containing the data
++ */
++static int at91_dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
++{
++      struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
++      unsigned int pageaddr, addr, offset, writelen;
++      size_t remaining;
++      u_char *writebuf;
++      unsigned short status;
++      int res = 0;
++      char* command;
++      char* tmpbuf = NULL;
++
++#ifdef DEBUG_DATAFLASH
++      printk("dataflash_write: %lli .. %lli\n", to, to+len);
++#endif
++
++      *retlen = 0;
++
++      /* Sanity checks */
++      if (!len)
++              return 0;
++      if (to + len > mtd->size)
++              return -EINVAL;
++
++      command = kmalloc(4, GFP_KERNEL);
++      if (!command)
++              return -ENOMEM;
++
++      pageaddr = ((unsigned)to / priv->page_size);
++      offset = ((unsigned)to % priv->page_size);
++      if (offset + len > priv->page_size)
++              writelen = priv->page_size - offset;
++      else
++              writelen = len;
++      writebuf = buf;
++      remaining = len;
++
++      /* Allocate temporary buffer */
++      tmpbuf = kmalloc(priv->page_size, GFP_KERNEL);
++      if (!tmpbuf) {
++              kfree(command);
++              return -ENOMEM;
++      }
++
++      /* Gain access to the SPI bus */
++      spi_access_bus(priv->spi);
++
++      while (remaining > 0) {
++#ifdef DEBUG_DATAFLASH
++              printk("write @ %i:%i len=%i\n", pageaddr, offset, writelen);
++#endif
++
++              /* (1) Transfer to Buffer1 */
++              if (writelen != priv->page_size) {
++                      addr = pageaddr << priv->page_offset;
++                      command[0] = OP_TRANSFER_BUF1;
++                      command[1] = (addr & 0x00FF0000) >> 16;
++                      command[2] = (addr & 0x0000FF00) >> 8;
++                      command[3] = 0;
++#ifdef DEBUG_DATAFLASH
++                      printk("TRANSFER: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]);
++#endif
++                      do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0);
++                      at91_dataflash_waitready();
++              }
++
++              /* (2) Program via Buffer1 */
++              addr = (pageaddr << priv->page_offset) + offset;
++              command[0] = OP_PROGRAM_VIA_BUF1;
++              command[1] = (addr & 0x00FF0000) >> 16;
++              command[2] = (addr & 0x0000FF00) >> 8;
++              command[3] = (addr & 0x000000FF);
++#ifdef DEBUG_DATAFLASH
++              printk("PROGRAM: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]);
++#endif
++              do_spi_transfer(2, command, 4, command, 4, writebuf, writelen, tmpbuf, writelen);
++              at91_dataflash_waitready();
++
++              /* (3) Compare to Buffer1 */
++              addr = pageaddr << priv->page_offset;
++              command[0] = OP_COMPARE_BUF1;
++              command[1] = (addr & 0x00FF0000) >> 16;
++              command[2] = (addr & 0x0000FF00) >> 8;
++              command[3] = 0;
++#ifdef DEBUG_DATAFLASH
++              printk("COMPARE: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]);
++#endif
++              do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0);
++              at91_dataflash_waitready();
++
++              /* Get result of the compare operation */
++              status = at91_dataflash_status();
++              if ((status & 0x40) == 1) {
++                      printk("at91_dataflash: Write error on page %i\n", pageaddr);
++                      remaining = 0;
++                      res = -EIO;
++              }
++
++              remaining = remaining - writelen;
++              pageaddr++;
++              offset = 0;
++              writebuf += writelen;
++              *retlen += writelen;
++
++              if (remaining > priv->page_size)
++                      writelen = priv->page_size;
++              else
++                      writelen = remaining;
++      }
++
++      /* Release SPI bus */
++      spi_release_bus(priv->spi);
++
++      kfree(tmpbuf);
++      kfree(command);
++      return res;
++}
++
++/* ......................................................................... */
++
++/*
++ * Initialize and register DataFlash device with MTD subsystem.
++ */
++static int add_dataflash(int channel, char *name, int IDsize, int nr_pages, int pagesize, int pageoffset)
++{
++      struct mtd_info *device;
++      struct dataflash_local *priv;
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++      char mtdID[14];
++#endif
++
++      if (nr_devices >= DATAFLASH_MAX_DEVICES) {
++              printk(KERN_ERR "at91_dataflash: Too many devices detected\n");
++              return 0;
++      }
++
++      device = (struct mtd_info *) kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++      if (!device)
++              return -ENOMEM;
++      memset(device, 0, sizeof(struct mtd_info));
++
++      device->name = name;
++      device->size = nr_pages * pagesize;
++      device->erasesize = pagesize;
++      device->module = THIS_MODULE;
++      device->type = MTD_NORFLASH;
++      device->flags = MTD_CAP_NORFLASH;
++      device->erase = at91_dataflash_erase;
++      device->read = at91_dataflash_read;
++      device->write = at91_dataflash_write;
++
++      priv = (struct dataflash_local *) kmalloc(sizeof(struct dataflash_local), GFP_KERNEL);
++      if (!priv) {
++              kfree(device);
++              return -ENOMEM;
++      }
++      memset(priv, 0, sizeof(struct dataflash_local));
++
++      priv->spi = channel;
++      priv->page_size = pagesize;
++      priv->page_offset = pageoffset;
++      device->priv = priv;
++
++      mtd_devices[nr_devices] = device;
++      nr_devices++;
++      printk("at91_dataflash: %s detected [spi%i] (%i bytes)\n", name, channel, device->size);
++
++#ifdef CONFIG_MTD_PARTITIONS
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++      sprintf(mtdID, "dataflash%i", nr_devices-1);
++      mtd_parts_nr = parse_cmdline_partitions(device, &mtd_parts, mtdID);
++#endif
++      if (mtd_parts_nr <= 0) {
++              mtd_parts = static_partitions;
++              mtd_parts_nr = NB_OF(static_partitions);
++      }
++
++      if (mtd_parts_nr > 0) {
++#ifdef DATAFLASH_ALWAYS_ADD_DEVICE
++              add_mtd_device(device);
++#endif
++              return add_mtd_partitions(device, mtd_parts, mtd_parts_nr);
++      }
++#endif
++      return add_mtd_device(device);          /* add whole device */
++}
++
++/*
++ * Detect and initialize DataFlash device connected to specified SPI channel.
++ *
++ *   Device            Density         ID code                 Nr Pages        Page Size       Page offset
++ *   AT45DB011B        1Mbit   (128K)  xx0011xx (0x0c)         512             264             9
++ *   AT45DB021B        2Mbit   (256K)  xx0101xx (0x14)         1025            264             9
++ *   AT45DB041B        4Mbit   (512K)  xx0111xx (0x1c)         2048            264             9
++ *   AT45DB081B        8Mbit   (1M)    xx1001xx (0x24)         4096            264             9
++ *   AT45DB0161B       16Mbit  (2M)    xx1011xx (0x2c)         4096            528             10
++ *   AT45DB0321B       32Mbit  (4M)    xx1101xx (0x34)         8192            528             10
++ *   AT45DB0642        64Mbit  (8M)    xx1111xx (0x3c)         8192            1056            11
++ *   AT45DB1282        128Mbit (16M)   xx0100xx (0x10)         16384           1056            11
++ */
++static int at91_dataflash_detect(int channel)
++{
++      int res = 0;
++      unsigned short status;
++
++      spi_access_bus(channel);
++      status = at91_dataflash_status();
++      if (status != 0xff) {                   /* no dataflash device there */
++              switch (status & 0x3c) {
++                      case 0x0c:      /* 0 0 1 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB011B", SZ_128K, 512, 264, 9);
++                              break;
++                      case 0x14:      /* 0 1 0 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB021B", SZ_256K, 1025, 264, 9);
++                              break;
++                      case 0x1c:      /* 0 1 1 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB041B", SZ_512K, 2048, 264, 9);
++                              break;
++                      case 0x24:      /* 1 0 0 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB081B", SZ_1M, 4096, 264, 9);
++                              break;
++                      case 0x2c:      /* 1 0 1 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB161B", SZ_2M, 4096, 528, 10);
++                              break;
++                      case 0x34:      /* 1 1 0 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB321B", SZ_4M, 8192, 528, 10);
++                              break;
++                      case 0x3c:      /* 1 1 1 1 */
++                              res = add_dataflash(channel, "Atmel AT45DB642", SZ_8M, 8192, 1056, 11);
++                              break;
++// Currently unsupported since Atmel removed the "Main Memory Program via Buffer" commands.
++//                    case 0x10:      /* 0 1 0 0 */
++//                            res = add_dataflash(channel, "Atmel AT45DB1282", SZ_16M, 16384, 1056, 11);
++//                            break;
++                      default:
++                              printk(KERN_ERR "at91_dataflash: Unknown device (%x)\n", status & 0x3c);
++              }
++      }
++      spi_release_bus(channel);
++
++      return res;
++}
++
++static int __init at91_dataflash_init(void)
++{
++      spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL);
++      if (!spi_transfer_desc)
++              return -ENOMEM;
++
++      /* DataFlash (SPI chip select 0) */
++      at91_dataflash_detect(0);
++
++#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
++      /* DataFlash card (SPI chip select 3) */
++      AT91_CfgPIO_DataFlashCard();
++      at91_dataflash_detect(3);
++#endif
++
++      return 0;
++}
++
++static void __exit at91_dataflash_exit(void)
++{
++      int i;
++
++      for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
++              if (mtd_devices[i]) {
++#ifdef CONFIG_MTD_PARTITIONS
++                      del_mtd_partitions(mtd_devices[i]);
++#else
++                      del_mtd_device(mtd_devices[i]);
++#endif
++                      kfree(mtd_devices[i]->priv);
++                      kfree(mtd_devices[i]);
++              }
++      }
++      nr_devices = 0;
++      kfree(spi_transfer_desc);
++}
++
++
++EXPORT_NO_SYMBOLS;
++
++module_init(at91_dataflash_init);
++module_exit(at91_dataflash_exit);
++
++MODULE_LICENSE("GPL")
++MODULE_AUTHOR("Andrew Victor")
++MODULE_DESCRIPTION("DataFlash driver for Atmel AT91RM9200")
+--- /dev/null
++++ linux-2.4.27/drivers/at91/mtd/at91_dataflash.h
+@@ -0,0 +1,43 @@
++/*
++ * Atmel DataFlash driver for the Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef AT91_DATAFLASH_H
++#define AT91_DATAFLASH_H
++
++#define DATAFLASH_MAX_DEVICES 4       /* max number of dataflash devices */
++#undef        DATAFLASH_ALWAYS_ADD_DEVICE     /* always add whole device when using partitions? */
++
++#define OP_READ_CONTINUOUS    0xE8
++#define OP_READ_PAGE          0xD2
++#define OP_READ_BUFFER1               0xD4
++#define OP_READ_BUFFER2               0xD6
++#define OP_READ_STATUS                0xD7
++
++#define OP_ERASE_PAGE         0x81
++#define OP_ERASE_BLOCK                0x50
++
++#define OP_TRANSFER_BUF1      0x53
++#define OP_TRANSFER_BUF2      0x55
++#define OP_COMPARE_BUF1               0x60
++#define OP_COMPARE_BUF2               0x61
++
++#define OP_PROGRAM_VIA_BUF1   0x82
++#define OP_PROGRAM_VIA_BUF2   0x85
++
++struct dataflash_local
++{
++      int spi;                        /* SPI chip-select number */
++
++      unsigned int page_size;         /* number of bytes per page */
++      unsigned short page_offset;     /* page offset in flash address */
++};
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/at91/mtd/at91_nand.c
+@@ -0,0 +1,328 @@
++/*
++ * drivers/at91/mtd/at91_nand.c
++ *
++ *  Copyright (c) 2003 Rick Bronson
++ *
++ *  Derived from drivers/mtd/nand/autcpu12.c
++ *     Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
++ *
++ *  Derived from drivers/mtd/spia.c
++ *     Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
++ *
++ * 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/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>
++#include <asm/sizes.h>
++
++#include <asm/arch/pio.h>
++#include "at91_nand.h"
++
++/*
++ * MTD structure for AT91 board
++ */
++static struct mtd_info *at91_mtd = NULL;
++static struct nand_chip *my_nand_chip = NULL;
++
++static int at91_fio_base;
++
++#ifdef CONFIG_MTD_PARTITIONS
++
++/*
++ * Define partitions for flash devices
++ */
++
++static struct mtd_partition partition_info32k[] = {
++      { name: "AT91 NAND partition 1, kernel",
++        offset:  0,
++        size:   1 * SZ_1M },
++      { name: "AT91 NAND partition 2, filesystem",
++        offset:  1 * SZ_1M,
++        size:   16 * SZ_1M },
++      { name: "AT91 NAND partition 3a, storage",
++        offset: (1 * SZ_1M) + (16 * SZ_1M),
++        size:   1 * SZ_1M },
++      { name: "AT91 NAND partition 3b, storage",
++        offset: (2 * SZ_1M) + (16 * SZ_1M),
++        size:   1 * SZ_1M },
++      { name: "AT91 NAND partition 3c, storage",
++        offset: (3 * SZ_1M) + (16 * SZ_1M),
++        size:   1 * SZ_1M },
++      { name: "AT91 NAND partition 3d, storage",
++        offset: (4 * SZ_1M) + (16 * SZ_1M),
++        size:   1 * SZ_1M },
++};
++
++static struct mtd_partition partition_info64k[] = {
++      { name: "AT91 NAND partition 1, kernel",
++        offset:  0,
++        size:   1 * SZ_1M },
++      { name: "AT91 NAND partition 2, filesystem",
++        offset:  1 * SZ_1M,
++        size:   16 * SZ_1M },
++      { name: "AT91 NAND partition 3, storage",
++        offset: (1 * SZ_1M) + (16 * SZ_1M),
++        size:   47 * SZ_1M },
++};
++
++#endif
++
++/*
++ * Hardware specific access to control-lines
++ */
++static void at91_hwcontrol(int cmd)
++{
++      struct nand_chip *my_nand = my_nand_chip;
++      switch(cmd)
++      {
++      case NAND_CTL_SETCLE:
++              my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_CLE;
++              break;
++      case NAND_CTL_CLRCLE:
++              my_nand->IO_ADDR_W = at91_fio_base;
++              break;
++      case NAND_CTL_SETALE:
++              my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_ALE;
++              break;
++      case NAND_CTL_CLRALE:
++              my_nand->IO_ADDR_W = at91_fio_base;
++              break;
++      case NAND_CTL_SETNCE:
++              break;
++      case NAND_CTL_CLRNCE:
++              break;
++      }
++}
++
++/*
++ * Send command to NAND device
++ */
++static void at91_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++      register struct nand_chip *my_nand = mtd->priv;
++
++      /* Begin command latch cycle */
++      register unsigned long NAND_IO_ADDR = my_nand->IO_ADDR_W + AT91_SMART_MEDIA_CLE;
++
++      /*
++       * Write out the command to the device.
++       */
++      if (command != NAND_CMD_SEQIN)
++              writeb (command, NAND_IO_ADDR);
++      else {
++              if (mtd->oobblock == 256 && column >= 256) {
++                      column -= 256;
++                      writeb (NAND_CMD_RESET, NAND_IO_ADDR);
++                      writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
++                      writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++              }
++              else
++                      if (mtd->oobblock == 512 && column >= 256) {
++                              if (column < 512) {
++                                      column -= 256;
++                                      writeb (NAND_CMD_READ1, NAND_IO_ADDR);
++                                      writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++                              } else {
++                                      column -= 512;
++                                      writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
++                                      writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++                              }
++                      } else {
++                              writeb (NAND_CMD_READ0, NAND_IO_ADDR);
++                              writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
++                      }
++      }
++
++      /* Set ALE and clear CLE to start address cycle */
++      NAND_IO_ADDR = at91_fio_base;
++
++      if (column != -1 || page_addr != -1)
++              NAND_IO_ADDR += AT91_SMART_MEDIA_ALE;
++
++      /* Serially input address */
++      if (column != -1)
++              writeb (column, NAND_IO_ADDR);
++      if (page_addr != -1) {
++              writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
++              writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
++              /* One more address cycle for higher density devices */
++              if (mtd->size & 0x0c000000) {
++                      writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR);
++              }
++      }
++
++      /* wait until command is processed */
++      while (!my_nand->dev_ready())
++              ;
++}
++
++/*
++ * Read the Device Ready pin.
++ */
++static int at91_device_ready(void)
++{
++      return AT91_PIO_SmartMedia_RDY();
++}
++/*
++ * Main initialization routine
++ */
++static int __init at91_init (void)
++{
++      struct nand_chip *my_nand;
++      int err = 0;
++
++      /* Allocate memory for MTD device structure and private data */
++      at91_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
++      if (!at91_mtd) {
++              printk ("Unable to allocate AT91 NAND MTD device structure.\n");
++              err = -ENOMEM;
++              goto out;
++      }
++
++      /* map physical adress */
++      at91_fio_base = (unsigned long) ioremap(AT91_SMARTMEDIA_BASE, SZ_8M);
++      if(!at91_fio_base) {
++              printk("ioremap AT91 NAND failed\n");
++              err = -EIO;
++              goto out_mtd;
++      }
++
++      /* Get pointer to private data */
++      my_nand_chip = my_nand = (struct nand_chip *) (&at91_mtd[1]);
++
++      /* Initialize structures */
++      memset((char *) at91_mtd, 0, sizeof(struct mtd_info));
++      memset((char *) my_nand, 0, sizeof(struct nand_chip));
++
++      /* Link the private data with the MTD structure */
++      at91_mtd->priv = my_nand;
++
++      /* Set address of NAND IO lines */
++      my_nand->IO_ADDR_R = at91_fio_base;
++      my_nand->IO_ADDR_W = at91_fio_base;
++      my_nand->hwcontrol = at91_hwcontrol;
++      my_nand->dev_ready = at91_device_ready;
++      my_nand->cmdfunc = at91_nand_command;   /* we need our own */
++      my_nand->eccmode = NAND_ECC_SOFT;       /* enable ECC */
++      /* 20 us command delay time */
++      my_nand->chip_delay = 20;
++
++      /* Setup Smart Media, first enable the address range of CS3 */
++      AT91_SYS->EBI_CSA |= AT91C_EBI_CS3A_SMC_SmartMedia;
++      /* set the bus interface characteristics based on
++         tDS Data Set up Time 30 - ns
++         tDH Data Hold Time 20 - ns
++         tALS ALE Set up Time 20 - ns
++         16ns at 60 MHz ~= 3  */
++#define AT91C_SM_ID_RWH       (5 << 28)               /* orig = 5 */
++#define AT91C_SM_RWH  (1 << 28)               /* orig = 1 */
++#define AT91C_SM_RWS  (0 << 24)               /* orig = 0 */
++#define AT91C_SM_TDF  (1 << 8)                /* orig = 1 */
++#define AT91C_SM_NWS  (5)                     /* orig = 3 */
++      AT91_SYS->EBI_SMC2_CSR[3] = ( AT91C_SM_RWH | AT91C_SM_RWS |
++                                       AT91C_SMC2_ACSS_STANDARD |
++                                       AT91C_SMC2_DBW_8 | AT91C_SM_TDF |
++                                       AT91C_SMC2_WSEN | AT91C_SM_NWS);
++
++      AT91_CfgPIO_SmartMedia();
++
++      if (AT91_PIO_SmartMedia_CardDetect())
++              printk ("No ");
++      printk ("SmartMedia card inserted.\n");
++
++      /* Scan to find existance of the device */
++      if (nand_scan (at91_mtd)) {
++              err = -ENXIO;
++              goto out_ior;
++      }
++
++      /* Allocate memory for internal data buffer */
++      my_nand->data_buf = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL);
++      if (!my_nand->data_buf) {
++              printk ("Unable to allocate AT91 NAND data buffer.\n");
++              err = -ENOMEM;
++              goto out_ior;
++      }
++
++      /* Allocate memory for internal data buffer */
++      my_nand->data_cache = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL);
++      if (!my_nand->data_cache) {
++              printk ("Unable to allocate AT91 NAND data cache.\n");
++              err = -ENOMEM;
++              goto out_buf;
++      }
++      my_nand->cache_page = -1;
++
++#ifdef CONFIG_MTD_PARTITIONS
++      /* Register the partitions */
++      switch(at91_mtd->size)
++      {
++      case SZ_32M:
++              err = add_mtd_partitions(at91_mtd, partition_info32k,
++                              ARRAY_SIZE (partition_info32k));
++              break;
++      case SZ_64M:
++              err = add_mtd_partitions(at91_mtd, partition_info64k,
++                              ARRAY_SIZE (partition_info64k));
++              break;
++      default:
++              printk ("Unsupported SmartMedia device\n");
++              err = -ENXIO;
++              goto out_cac;
++      }
++#else
++      err = add_mtd_device(at91_mtd);
++#endif
++      goto out;
++
++ out_cac:
++      kfree (my_nand->data_cache);
++ out_buf:
++      kfree (my_nand->data_buf);
++ out_ior:
++      iounmap((void *)at91_fio_base);
++ out_mtd:
++      kfree (at91_mtd);
++ out:
++      return err;
++}
++
++/*
++ * Clean up routine
++ */
++static void __exit at91_cleanup (void)
++{
++      struct nand_chip *my_nand = (struct nand_chip *) &at91_mtd[1];
++
++      /* Unregister partitions */
++      del_mtd_partitions(at91_mtd);
++
++      /* Unregister the device */
++      del_mtd_device (at91_mtd);
++
++      /* Free internal data buffers */
++      kfree (my_nand->data_buf);
++      kfree (my_nand->data_cache);
++
++      /* unmap physical adress */
++      iounmap((void *)at91_fio_base);
++
++      /* Free the MTD device structure */
++      kfree (at91_mtd);
++}
++
++module_init(at91_init);
++module_exit(at91_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Rick Bronson");
++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on ATMEL AT91RM9200");
+--- /dev/null
++++ linux-2.4.27/drivers/at91/mtd/at91_nand.h
+@@ -0,0 +1,27 @@
++/*
++ * AT91RM9200 specific NAND (SmartMedia) defines
++ *
++ * (c) 2003 Rick Bronson
++ *
++ * 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
++ */
++
++#ifndef __AT91_NAND_H
++#define __AT91_NAND_H
++
++#define AT91_SMART_MEDIA_ALE (1 << 22)  /* our ALE is AD22 */
++#define AT91_SMART_MEDIA_CLE (1 << 21)  /* our CLE is AD21 */
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/at91/net/Makefile
+@@ -0,0 +1,15 @@
++# File: drivers/at91/net/Makefile
++#
++# Makefile for the Atmel AT91RM9200 ethernet device drivers
++#
++
++O_TARGET := at91net.o
++
++obj-y   :=
++obj-m   :=
++obj-n   :=
++obj-  :=
++
++obj-$(CONFIG_AT91_ETHER) += at91_ether.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/net/at91_ether.c
+@@ -0,0 +1,875 @@
++/*
++ * Ethernet driver for the Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc.
++ * Initial version by Rick Bronson 01/11/2003
++ *
++ * Intel LXT971A PHY support by Christopher Bahns & David Knickerbocker
++ *   (Polaroid Corporation)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/config.h>
++#include <linux/mii.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/skbuff.h>
++#include <asm/io.h>
++#include <linux/pci.h>
++#include <linux/crc32.h>
++#include <asm/uaccess.h>
++#include <linux/ethtool.h>
++
++#include <asm/arch/AT91RM9200_EMAC.h>
++#include <asm/arch/pio.h>
++#include "at91_ether.h"
++
++static struct net_device at91_dev;
++
++/* ........................... PHY INTERFACE ........................... */
++
++/*
++ * Enable the MDIO bit in MAC control register
++ * When not called from an interrupt-handler, access to the PHY must be
++ *  protected by a spinlock.
++ */
++static void enable_mdi(AT91PS_EMAC regs)
++{
++      regs->EMAC_CTL |= AT91C_EMAC_MPE;       /* enable management port */
++}
++
++/*
++ * Disable the MDIO bit in the MAC control register
++ */
++static void disable_mdi(AT91PS_EMAC regs)
++{
++      regs->EMAC_CTL &= ~AT91C_EMAC_MPE;      /* disable management port */
++}
++
++/*
++ * Write value to the a PHY register
++ * Note: MDI interface is assumed to already have been enabled.
++ */
++static void write_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int value)
++{
++      regs->EMAC_MAN = (AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_W
++              | ((phy_addr & 0x1f) << 23) | (address << 18)) + (value & 0xffff);
++
++      /* Wait until IDLE bit in Network Status register is cleared */
++      // TODO: Enforce some maximum loop-count?
++      while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); }
++}
++
++/*
++ * Read value stored in a PHY register.
++ * Note: MDI interface is assumed to already have been enabled.
++ */
++static void read_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int *value)
++{
++      regs->EMAC_MAN = AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_R
++              | ((phy_addr & 0x1f) << 23) | (address << 18);
++
++      /* Wait until IDLE bit in Network Status register is cleared */
++      // TODO: Enforce some maximum loop-count?
++      while (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); }
++
++      *value = (regs->EMAC_MAN & 0x0000ffff);
++}
++
++/* ........................... PHY MANAGEMENT .......................... */
++
++/*
++ * Access the PHY to determine the current Link speed and Mode, and update the
++ * MAC accordingly.
++ * If no link or auto-negotiation is busy, then no changes are made.
++ * Returns:  0 : OK
++ *          -1 : No link
++ *          -2 : AutoNegotiation still in progress
++ */
++static int update_linkspeed(struct net_device *dev, AT91PS_EMAC regs) {
++      unsigned int bmsr, bmcr, lpa, mac_cfg;
++      unsigned int speed, duplex;
++
++      /* Link status is latched, so read twice to get current value */
++      read_phy(regs, 0, MII_BMSR, &bmsr);
++      read_phy(regs, 0, MII_BMSR, &bmsr);
++      if (!(bmsr & BMSR_LSTATUS)) return -1;                  /* no link */
++
++      read_phy(regs, 0, MII_BMCR, &bmcr);
++      if (bmcr & BMCR_ANENABLE) {                             /* AutoNegotiation is enabled */
++              if (!(bmsr & BMSR_ANEGCOMPLETE)) return -2;     /* auto-negotitation in progress */
++
++              read_phy(regs, 0, MII_LPA, &lpa);
++              if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) speed = SPEED_100;
++              else speed = SPEED_10;
++              if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) duplex = DUPLEX_FULL;
++              else duplex = DUPLEX_HALF;
++      } else {
++              speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10;
++              duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
++      }
++
++      /* Update the MAC */
++      mac_cfg = regs->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
++      if (speed == SPEED_100) {
++              if (duplex == DUPLEX_FULL)              /* 100 Full Duplex */
++                      regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD | AT91C_EMAC_FD;
++              else                                    /* 100 Half Duplex */
++                      regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD;
++      } else {
++              if (duplex == DUPLEX_FULL)              /* 10 Full Duplex */
++                      regs->EMAC_CFG = mac_cfg | AT91C_EMAC_FD;
++              else                                    /* 10 Half Duplex */
++                      regs->EMAC_CFG = mac_cfg;
++      }
++
++      printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex");
++      return 0;
++}
++
++/*
++ * Handle interrupts from the PHY
++ */
++static void at91ether_phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct net_device *dev = (struct net_device *) dev_id;
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr;
++      int status;
++      unsigned int phy;
++
++      enable_mdi(emac);
++      if (lp->phy_type == MII_DM9161_ID)
++              read_phy(emac, 0, MII_DSINTR_REG, &phy);        /* ack interrupt in Davicom PHY */
++      else if (lp->phy_type == MII_LXT971A_ID)
++              read_phy(emac, 0, MII_ISINTS_REG, &phy);        /* ack interrupt in Intel PHY */
++              
++      status = AT91_SYS->PIOC_ISR;            /* acknowledge interrupt in PIO */
++
++      status = update_linkspeed(dev, emac);
++      if (status == -1) {                     /* link is down */
++              netif_carrier_off(dev);
++              printk(KERN_INFO "%s: Link down.\n", dev->name);
++      } else if (status == -2) {              /* auto-negotiation in progress */
++              /* Do nothing - another interrupt generated when negotiation complete */
++      } else {                                /* link is operational */
++              netif_carrier_on(dev);
++      }
++      disable_mdi(emac);
++}
++
++/*
++ * Initialize and enable the PHY interrupt when link-state changes
++ */
++static void enable_phyirq(struct net_device *dev, AT91PS_EMAC regs)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      unsigned int dsintr, status;
++
++      // TODO: Check error code.  Really need a generic PIO (interrupt)
++      // layer since we're really only interested in the PC4 (DK) or PC2 (CSB337) line.
++      (void) request_irq(AT91C_ID_PIOC, at91ether_phy_interrupt, 0, dev->name, dev);
++
++      status = AT91_SYS->PIOC_ISR;            /* clear any pending PIO interrupts */
++#ifdef CONFIG_MACH_CSB337
++      AT91_SYS->PIOC_IER = AT91C_PIO_PC2;     /* Enable interrupt */
++#else
++      AT91_SYS->PIOC_IER = AT91C_PIO_PC4;     /* Enable interrupt */
++#endif
++
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++              
++      if (lp->phy_type == MII_DM9161_ID) {            /* for Davicom PHY */
++              read_phy(regs, 0, MII_DSINTR_REG, &dsintr);
++              dsintr = dsintr & ~0xf00;               /* clear bits 8..11 */
++              write_phy(regs, 0, MII_DSINTR_REG, dsintr);
++      }
++      else if (lp->phy_type == MII_LXT971A_ID) {      /* for Intel PHY */
++              read_phy(regs, 0, MII_ISINTE_REG, &dsintr);
++              dsintr = dsintr | 0xf2;                 /* set bits 1, 4..7 */
++              write_phy(regs, 0, MII_ISINTE_REG, dsintr);
++      }
++              
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++}
++
++/*
++ * Disable the PHY interrupt
++ */
++static void disable_phyirq(struct net_device *dev, AT91PS_EMAC regs)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      unsigned int dsintr;
++
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++
++      if (lp->phy_type == MII_DM9161_ID) {            /* for Davicom PHY */
++              read_phy(regs, 0, MII_DSINTR_REG, &dsintr);
++              dsintr = dsintr | 0xf00;                        /* set bits 8..11 */
++              write_phy(regs, 0, MII_DSINTR_REG, dsintr);
++      }
++      else if (lp->phy_type == MII_LXT971A_ID) {      /* for Intel PHY */
++              read_phy(regs, 0, MII_ISINTE_REG, &dsintr);
++              dsintr = dsintr & ~0xf2;                        /* clear bits 1, 4..7 */
++              write_phy(regs, 0, MII_ISINTE_REG, dsintr);
++      }
++
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++
++#ifdef CONFIG_MACH_CSB337
++      AT91_SYS->PIOC_IDR = AT91C_PIO_PC2;             /* Disable interrupt */
++#else
++      AT91_SYS->PIOC_IDR = AT91C_PIO_PC4;             /* Disable interrupt */
++#endif
++      free_irq(AT91C_ID_PIOC, dev);                   /* Free interrupt handler */
++}
++
++/*
++ * Perform a software reset of the PHY.
++ */
++static void reset_phy(struct net_device *dev, AT91PS_EMAC regs)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      unsigned int bmcr;
++
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++
++      /* Perform PHY reset */
++      write_phy(regs, 0, MII_BMCR, BMCR_RESET);
++
++      /* Wait until PHY reset is complete */
++      do {
++              read_phy(regs, 0, MII_BMCR, &bmcr);
++      } while (!(bmcr && BMCR_RESET));
++
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++}
++
++
++/* ......................... ADDRESS MANAGEMENT ........................ */
++
++/*
++ * Set the ethernet MAC address in dev->dev_addr
++ */
++static void get_mac_address(struct net_device *dev) {
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      char addr[6];
++      unsigned int hi, lo;
++
++      /* Check if bootloader set address in Specific-Address 1 */
++      hi = regs->EMAC_SA1H;
++      lo = regs->EMAC_SA1L;
++      addr[0] = (lo & 0xff);
++      addr[1] = (lo & 0xff00) >> 8;
++      addr[2] = (lo & 0xff0000) >> 16;
++      addr[3] = (lo & 0xff000000) >> 24;
++      addr[4] = (hi & 0xff);
++      addr[5] = (hi & 0xff00) >> 8;
++
++      if (is_valid_ether_addr(addr)) {
++              memcpy(dev->dev_addr, &addr, 6);
++              return;
++      }
++
++      /* Check if bootloader set address in Specific-Address 2 */
++      hi = regs->EMAC_SA2H;
++      lo = regs->EMAC_SA2L;
++      addr[0] = (lo & 0xff);
++      addr[1] = (lo & 0xff00) >> 8;
++      addr[2] = (lo & 0xff0000) >> 16;
++      addr[3] = (lo & 0xff000000) >> 24;
++      addr[4] = (hi & 0xff);
++      addr[5] = (hi & 0xff00) >> 8;
++
++      if (is_valid_ether_addr(addr)) {
++              memcpy(dev->dev_addr, &addr, 6);
++              return;
++      }
++}
++
++/*
++ * Program the hardware MAC address from dev->dev_addr.
++ */
++static void update_mac_address(struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++
++      regs->EMAC_SA1L = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0]);
++      regs->EMAC_SA1H = (dev->dev_addr[5] << 8) | (dev->dev_addr[4]);
++}
++
++/*
++ * Store the new hardware address in dev->dev_addr, and update the MAC.
++ */
++static int set_mac_address(struct net_device *dev, void* addr)
++{
++      struct sockaddr *address = addr;
++
++      if (!is_valid_ether_addr(address->sa_data))
++              return -EADDRNOTAVAIL;
++
++      memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
++      update_mac_address(dev);
++
++      printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
++              dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
++              dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
++
++      return 0;
++}
++
++/*
++ * Add multicast addresses to the internal multicast-hash table.
++ */
++static void at91ether_sethashtable(struct net_device *dev, AT91PS_EMAC regs)
++{
++      struct dev_mc_list *curr;
++      unsigned char mc_filter[2];
++      unsigned int i, bitnr;
++
++      mc_filter[0] = mc_filter[1] = 0;
++
++      curr = dev->mc_list;
++      for (i = 0; i < dev->mc_count; i++, curr = curr->next) {
++              if (!curr) break;       /* unexpected end of list */
++
++              bitnr = ether_crc(ETH_ALEN, curr->dmi_addr) >> 26;
++              mc_filter[bitnr >> 5] |= 1 << (bitnr & 31);
++      }
++
++      regs->EMAC_HSH = mc_filter[1];
++      regs->EMAC_HSL = mc_filter[0];
++}
++
++/*
++ * Enable/Disable promiscuous and multicast modes.
++ */
++static void at91ether_set_rx_mode(struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++
++      if (dev->flags & IFF_PROMISC) {                 /* Enable promiscuous mode */
++              regs->EMAC_CFG |= AT91C_EMAC_CAF;
++      } else if (dev->flags & (~IFF_PROMISC)) {       /* Disable promiscuous mode */
++              regs->EMAC_CFG &= ~AT91C_EMAC_CAF;
++      }
++
++      if (dev->flags & IFF_ALLMULTI) {                /* Enable all multicast mode */
++              regs->EMAC_HSH = -1;
++              regs->EMAC_HSL = -1;
++              regs->EMAC_CFG |= AT91C_EMAC_MTI;
++      } else if (dev->mc_count > 0) {                 /* Enable specific multicasts */
++              at91ether_sethashtable(dev, regs);
++              regs->EMAC_CFG |= AT91C_EMAC_MTI;
++      } else if (dev->flags & (~IFF_ALLMULTI)) {      /* Disable all multicast mode */
++              regs->EMAC_HSH = 0;
++              regs->EMAC_HSL = 0;
++              regs->EMAC_CFG &= ~AT91C_EMAC_MTI;
++      }
++}
++
++/* ............................... IOCTL ............................... */
++
++static int mdio_read(struct net_device *dev, int phy_id, int location)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      unsigned int value;
++
++      read_phy(regs, phy_id, location, &value);
++      return value;
++}
++
++static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++
++      write_phy(regs, phy_id, location, value);
++}
++
++/*
++ * ethtool support.
++ */
++static int at91ether_ethtool_ioctl (struct net_device *dev, void *useraddr)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      u32 ethcmd;
++      int res = 0;
++
++      if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
++              return -EFAULT;
++
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++
++      switch (ethcmd) {
++      case ETHTOOL_GSET: {
++              struct ethtool_cmd ecmd = { ETHTOOL_GSET };
++              res = mii_ethtool_gset(&lp->mii, &ecmd);
++              if (lp->phy_media == PORT_FIBRE) {              /* override media type since mii.c doesn't know */
++                      ecmd.supported = SUPPORTED_FIBRE;
++                      ecmd.port = PORT_FIBRE;
++              }
++              if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
++                      res = -EFAULT;
++              break;
++      }
++      case ETHTOOL_SSET: {
++              struct ethtool_cmd ecmd;
++              if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
++                      res = -EFAULT;
++              else
++                      res = mii_ethtool_sset(&lp->mii, &ecmd);
++              break;
++      }
++      case ETHTOOL_NWAY_RST: {
++              res = mii_nway_restart(&lp->mii);
++              break;
++      }
++      case ETHTOOL_GLINK: {
++              struct ethtool_value edata = { ETHTOOL_GLINK };
++              edata.data = mii_link_ok(&lp->mii);
++              if (copy_to_user(useraddr, &edata, sizeof(edata)))
++                      res = -EFAULT;
++              break;
++      }
++      default:
++              res = -EOPNOTSUPP;
++      }
++
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++
++      return res;
++}
++
++/*
++ * User-space ioctl interface.
++ */
++static int at91ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
++{
++      switch(cmd) {
++      case SIOCETHTOOL:
++              return at91ether_ethtool_ioctl(dev, (void *) rq->ifr_data);
++      default:
++              return -EOPNOTSUPP;
++      }
++}
++
++/* ................................ MAC ................................ */
++
++/*
++ * Initialize and start the Receiver and Transmit subsystems
++ */
++static void at91ether_start(struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      int i;
++      struct recv_desc_bufs *dlist, *dlist_phys;
++
++      dlist = lp->dlist;
++      dlist_phys = lp->dlist_phys;
++
++      for (i = 0; i < MAX_RX_DESCR; i++) {
++              dlist->descriptors[i].addr = (unsigned int) &dlist_phys->recv_buf[i][0];
++              dlist->descriptors[i].size = 0;
++      }
++
++      /* Set the Wrap bit on the last descriptor */
++      dlist->descriptors[i-1].addr |= EMAC_DESC_WRAP;
++
++      /* Reset buffer index */
++      lp->rxBuffIndex = 0;
++
++      /* Program address of descriptor list in Rx Buffer Queue register */
++      regs->EMAC_RBQP = (AT91_REG) dlist_phys;
++
++      /* Enable Receive and Transmit */
++      regs->EMAC_CTL |= (AT91C_EMAC_RE | AT91C_EMAC_TE);
++}
++
++/*
++ * Open the ethernet interface
++ */
++static int at91ether_open(struct net_device *dev)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++
++        if (!is_valid_ether_addr(dev->dev_addr))
++              return -EADDRNOTAVAIL;
++
++      AT91_SYS->PMC_PCER = 1 << AT91C_ID_EMAC;        /* Re-enable Peripheral clock */
++      regs->EMAC_CTL |= AT91C_EMAC_CSR;               /* Clear internal statistics */
++
++      /* Update the MAC address (incase user has changed it) */
++      update_mac_address(dev);
++
++      /* Enable PHY interrupt */
++      enable_phyirq(dev, regs);
++
++      /* Enable MAC interrupts */
++      regs->EMAC_IER = AT91C_EMAC_RCOM | AT91C_EMAC_RBNA
++                      | AT91C_EMAC_TUND | AT91C_EMAC_RTRY | AT91C_EMAC_TCOM
++                      | AT91C_EMAC_ROVR | AT91C_EMAC_HRESP;
++
++      /* Determine current link speed */
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++      (void) update_linkspeed(dev, regs);
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++
++      at91ether_start(dev);
++      netif_start_queue(dev);
++      return 0;
++}
++
++/*
++ * Close the interface
++ */
++static int at91ether_close(struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++
++      /* Disable Receiver and Transmitter */
++      regs->EMAC_CTL &= ~(AT91C_EMAC_TE | AT91C_EMAC_RE);
++
++      /* Disable PHY interrupt */
++      disable_phyirq(dev, regs);
++
++      /* Disable MAC interrupts */
++      regs->EMAC_IDR = AT91C_EMAC_RCOM | AT91C_EMAC_RBNA
++                      | AT91C_EMAC_TUND | AT91C_EMAC_RTRY | AT91C_EMAC_TCOM
++                      | AT91C_EMAC_ROVR | AT91C_EMAC_HRESP;
++
++      netif_stop_queue(dev);
++
++      AT91_SYS->PMC_PCDR = 1 << AT91C_ID_EMAC;        /* Disable Peripheral clock */
++
++      return 0;
++}
++
++/*
++ * Transmit packet.
++ */
++static int at91ether_tx(struct sk_buff *skb, struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++
++      if (regs->EMAC_TSR & AT91C_EMAC_BNQ) {
++              netif_stop_queue(dev);
++
++              /* Store packet information (to free when Tx completed) */
++              lp->skb = skb;
++              lp->skb_length = skb->len;
++              lp->skb_physaddr = pci_map_single(NULL, skb->data, skb->len, PCI_DMA_TODEVICE);
++              lp->stats.tx_bytes += skb->len;
++
++              /* Set address of the data in the Transmit Address register */
++              regs->EMAC_TAR = lp->skb_physaddr;
++              /* Set length of the packet in the Transmit Control register */
++              regs->EMAC_TCR = skb->len;
++
++              dev->trans_start = jiffies;
++      } else {
++              printk(KERN_ERR "at91_ether.c: at91ether_tx() called, but device is busy!\n");
++              return 1;       /* if we return anything but zero, dev.c:1055 calls kfree_skb(skb)
++                              on this skb, he also reports -ENETDOWN and printk's, so either
++                              we free and return(0) or don't free and return 1 */
++      }
++
++      return 0;
++}
++
++/*
++ * Update the current statistics from the internal statistics registers.
++ */
++static struct net_device_stats *at91ether_stats(struct net_device *dev)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;
++      int ale, lenerr, seqe, lcol, ecol;
++
++      if (netif_running(dev)) {
++              lp->stats.rx_packets += regs->EMAC_OK;                  /* Good frames received */
++              ale = regs->EMAC_ALE;
++              lp->stats.rx_frame_errors += ale;                       /* Alignment errors */
++              lenerr = regs->EMAC_ELR + regs->EMAC_USF;
++              lp->stats.rx_length_errors += lenerr;                   /* Excessive Length or Undersize Frame error */
++              seqe = regs->EMAC_SEQE;
++              lp->stats.rx_crc_errors += seqe;                        /* CRC error */
++              lp->stats.rx_fifo_errors += regs->EMAC_DRFC;            /* Receive buffer not available */
++              lp->stats.rx_errors += (ale + lenerr + seqe + regs->EMAC_CDE + regs->EMAC_RJB);
++
++              lp->stats.tx_packets += regs->EMAC_FRA;                 /* Frames successfully transmitted */
++              lp->stats.tx_fifo_errors += regs->EMAC_TUE;             /* Transmit FIFO underruns */
++              lp->stats.tx_carrier_errors += regs->EMAC_CSE;          /* Carrier Sense errors */
++              lp->stats.tx_heartbeat_errors += regs->EMAC_SQEE;       /* Heartbeat error */
++
++              lcol = regs->EMAC_LCOL;
++              ecol = regs->EMAC_ECOL;
++              lp->stats.tx_window_errors += lcol;                     /* Late collisions */
++              lp->stats.tx_aborted_errors += ecol;                    /* 16 collisions */
++
++              lp->stats.collisions += (regs->EMAC_SCOL + regs->EMAC_MCOL + lcol + ecol);
++      }
++      return &lp->stats;
++}
++
++/*
++ * Extract received frame from buffer descriptors and sent to upper layers.
++ * (Called from interrupt context)
++ */
++static void at91ether_rx(struct net_device *dev)
++{
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      struct recv_desc_bufs *dlist;
++      unsigned char *p_recv;
++      struct sk_buff *skb;
++      unsigned int pktlen;
++
++      dlist = lp->dlist;
++      while (dlist->descriptors[lp->rxBuffIndex].addr & EMAC_DESC_DONE) {
++              p_recv = dlist->recv_buf[lp->rxBuffIndex];
++              pktlen = dlist->descriptors[lp->rxBuffIndex].size & 0x7ff;      /* Length of frame including FCS */
++              skb = alloc_skb(pktlen + 2, GFP_ATOMIC);
++              if (skb != NULL) {
++                      skb_reserve(skb, 2);
++                      memcpy(skb_put(skb, pktlen), p_recv, pktlen);
++
++                      skb->dev = dev;
++                      skb->protocol = eth_type_trans(skb, dev);
++                      skb->len = pktlen;
++                      dev->last_rx = jiffies;
++                      lp->stats.rx_bytes += pktlen;
++                      netif_rx(skb);
++              }
++              else {
++                      lp->stats.rx_dropped += 1;
++                      printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
++              }
++
++              if (dlist->descriptors[lp->rxBuffIndex].size & EMAC_MULTICAST)
++                      lp->stats.multicast++;
++
++              dlist->descriptors[lp->rxBuffIndex].addr &= ~EMAC_DESC_DONE;    /* reset ownership bit */
++              if (lp->rxBuffIndex == MAX_RX_DESCR-1)                          /* wrap after last buffer */
++                      lp->rxBuffIndex = 0;
++              else
++                      lp->rxBuffIndex++;
++      }
++}
++
++/*
++ * MAC interrupt handler
++ */
++static void at91ether_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct net_device *dev = (struct net_device *) dev_id;
++      struct at91_private *lp = (struct at91_private *) dev->priv;
++      AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr;
++      unsigned long intstatus;
++
++      /* MAC Interrupt Status register indicates what interrupts are pending.
++         It is automatically cleared once read. */
++      intstatus = emac->EMAC_ISR;
++
++      if (intstatus & AT91C_EMAC_RCOM)                /* Receive complete */
++              at91ether_rx(dev);
++
++      if (intstatus & AT91C_EMAC_TCOM) {              /* Transmit complete */
++              /* The TCOM bit is set even if the transmission failed. */
++              if (intstatus & (AT91C_EMAC_TUND | AT91C_EMAC_RTRY))
++                      lp->stats.tx_errors += 1;
++
++              dev_kfree_skb_irq(lp->skb);
++              pci_unmap_single(NULL, lp->skb_physaddr, lp->skb_length, PCI_DMA_TODEVICE);
++              netif_wake_queue(dev);
++      }
++
++      if (intstatus & AT91C_EMAC_RBNA)
++              printk("%s: RBNA error\n", dev->name);
++      if (intstatus & AT91C_EMAC_ROVR)
++              printk("%s: ROVR error\n", dev->name);
++}
++
++/*
++ * Initialize the ethernet interface
++ */
++static int at91ether_setup(struct net_device *dev, unsigned long phy_type)
++{
++      struct at91_private *lp;
++      AT91PS_EMAC regs;
++      static int already_initialized = 0;
++      unsigned int val;
++
++      if (already_initialized)
++              return 0;
++
++      dev = init_etherdev(dev, sizeof(struct at91_private));
++      dev->base_addr = AT91C_VA_BASE_EMAC;
++      dev->irq = AT91C_ID_EMAC;
++      SET_MODULE_OWNER(dev);
++
++      /* Install the interrupt handler */
++      if (request_irq(dev->irq, at91ether_interrupt, 0, dev->name, dev))
++              return -EBUSY;
++
++      /* Allocate memory for private data structure */
++      lp = (struct at91_private *) kmalloc(sizeof(struct at91_private), GFP_KERNEL);
++      if (lp == NULL) {
++              free_irq(dev->irq, dev);
++              return -ENOMEM;
++      }
++      memset(lp, 0, sizeof(struct at91_private));
++      dev->priv = lp;
++
++      /* Allocate memory for DMA Receive descriptors */
++      lp->dlist = (struct recv_desc_bufs *) consistent_alloc(GFP_DMA | GFP_KERNEL, sizeof(struct recv_desc_bufs), (dma_addr_t *) &lp->dlist_phys);
++      if (lp->dlist == NULL) {
++              kfree(dev->priv);
++              free_irq(dev->irq, dev);
++              return -ENOMEM;
++      }
++
++      spin_lock_init(&lp->lock);
++
++      ether_setup(dev);
++      dev->open = at91ether_open;
++      dev->stop = at91ether_close;
++      dev->hard_start_xmit = at91ether_tx;
++      dev->get_stats = at91ether_stats;
++      dev->set_multicast_list = at91ether_set_rx_mode;
++      dev->do_ioctl = at91ether_ioctl;
++      dev->set_mac_address = set_mac_address;
++
++      get_mac_address(dev);           /* Get ethernet address and store it in dev->dev_addr */
++      update_mac_address(dev);        /* Program ethernet address into MAC */
++
++      regs = (AT91PS_EMAC) dev->base_addr;
++      regs->EMAC_CTL = 0;
++
++#ifdef CONFIG_AT91_ETHER_RMII
++      regs->EMAC_CFG = AT91C_EMAC_BIG | AT91C_EMAC_RMII;
++#else
++      regs->EMAC_CFG = AT91C_EMAC_BIG;
++#endif
++      if (phy_type == MII_LXT971A_ID)
++              regs->EMAC_CFG |= AT91C_EMAC_CLK_HCLK_64;       /* MDIO clock = system clock/64 */
++
++      if (phy_type == MII_DM9161_ID) {
++              spin_lock_irq(&lp->lock);
++              enable_mdi(regs);
++
++              read_phy(regs, 0, MII_DSCR_REG, &val);
++              if ((val & (1 << 10)) == 0)                     /* DSCR bit 10 is 0 -- fiber mode */
++                      lp->phy_media = PORT_FIBRE;
++
++              disable_mdi(regs);
++              spin_unlock_irq(&lp->lock);
++      }
++
++      lp->mii.dev = dev;              /* Support for ethtool */
++      lp->mii.mdio_read = mdio_read;
++      lp->mii.mdio_write = mdio_write;
++      
++      lp->phy_type = phy_type;        /* Type of PHY connected */
++
++      /* Determine current link speed */
++      spin_lock_irq(&lp->lock);
++      enable_mdi(regs);
++      (void) update_linkspeed(dev, regs);
++      disable_mdi(regs);
++      spin_unlock_irq(&lp->lock);
++
++      /* Display ethernet banner */
++      printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%02x:%02x:%02x:%02x:%02x:%02x)\n",
++              dev->name, (uint) dev->base_addr, dev->irq,
++              regs->EMAC_CFG & AT91C_EMAC_SPD ? "100-" : "10-",
++              regs->EMAC_CFG & AT91C_EMAC_FD ? "FullDuplex" : "HalfDuplex",
++              dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
++              dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
++      if (phy_type == MII_DM9161_ID)
++              printk(KERN_INFO "%s: Davicom 9196 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)");
++      else if (phy_type == MII_LXT971A_ID)
++              printk(KERN_INFO "%s: Intel LXT971A PHY\n", dev->name);
++
++      already_initialized = 1;
++      return 0;
++}
++
++/*
++ * Detect MAC and PHY and perform initialization
++ */
++static int at91ether_probe(struct net_device *dev)
++{
++      AT91PS_EMAC regs = (AT91PS_EMAC) AT91C_VA_BASE_EMAC;
++      unsigned int phyid1, phyid2;
++      int detected = -1;
++
++      /* Configure the hardware - RMII vs MII mode */
++#ifdef CONFIG_AT91_ETHER_RMII
++      AT91_CfgPIO_EMAC_RMII();
++#else
++      AT91_CfgPIO_EMAC_MII();
++#endif
++
++      AT91_CfgPIO_EMAC_PHY();                         /* Configure PHY interrupt */
++      AT91_SYS->PMC_PCER = 1 << AT91C_ID_EMAC;        /* Enable Peripheral clock */
++
++      /* Read the PHY ID registers */
++      enable_mdi(regs);
++      read_phy(regs, 0, MII_PHYSID1, &phyid1);
++      read_phy(regs, 0, MII_PHYSID2, &phyid2);
++      disable_mdi(regs);
++
++      /* Davicom 9161: PHY_ID1 = 0x181  PHY_ID2 = B881 */
++      if (((phyid1 << 16) | (phyid2 & 0xfff0)) == MII_DM9161_ID) {
++              detected = at91ether_setup(dev, MII_DM9161_ID);
++      }
++      /* Intel LXT971A: PHY_ID1 = 0x13  PHY_ID2 = 78E0 */
++      else if (((phyid1 << 16) | (phyid2 & 0xfff0)) == MII_LXT971A_ID) {
++              detected = at91ether_setup(dev, MII_LXT971A_ID);
++      }
++
++      AT91_SYS->PMC_PCDR = 1 << AT91C_ID_EMAC;        /* Disable Peripheral clock */
++
++      return detected;
++}
++
++static int __init at91ether_init(void)
++{
++      if (!at91ether_probe(&at91_dev))
++              return register_netdev(&at91_dev);
++
++      return -1;
++}
++
++static void __exit at91ether_exit(void)
++{
++      unregister_netdev(&at91_dev);
++}
++
++module_init(at91ether_init)
++module_exit(at91ether_exit)
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("AT91RM9200 EMAC Ethernet driver");
++MODULE_AUTHOR("Andrew Victor");
+--- /dev/null
++++ linux-2.4.27/drivers/at91/net/at91_ether.h
+@@ -0,0 +1,79 @@
++/*
++ * Ethernet driver for the Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc.
++ * Initial version by Rick Bronson.
++ *
++ * 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 AT91_ETHERNET
++#define AT91_ETHERNET
++
++
++/* Davicom 9161 PHY */
++#define MII_DM9161_ID   0x0181b880
++
++/* Davicom specific registers */
++#define MII_DSCR_REG  16
++#define MII_DSCSR_REG   17
++#define MII_DSINTR_REG  21
++
++/* Intel LXT971A PHY */
++#define MII_LXT971A_ID        0x001378E0
++
++/* Intel specific registers */
++#define MII_ISINTE_REG        18
++#define MII_ISINTS_REG        19
++
++/* ........................................................................ */
++
++#define MAX_RBUFF_SZ  0x600           /* 1518 rounded up */
++#define MAX_RX_DESCR  9               /* max number of receive buffers */
++
++#define EMAC_DESC_DONE        0x00000001      /* bit for if DMA is done */
++#define EMAC_DESC_WRAP        0x00000002      /* bit for wrap */
++
++#define EMAC_BROADCAST        0x80000000      /* broadcast address */
++#define EMAC_MULTICAST        0x40000000      /* multicast address */
++#define EMAC_UNICAST  0x20000000      /* unicast address */
++
++struct rbf_t
++{
++      unsigned int addr;
++      unsigned long size;
++};
++
++struct recv_desc_bufs
++{
++      struct rbf_t descriptors[MAX_RX_DESCR];         /* must be on sizeof (rbf_t) boundary */
++      char recv_buf[MAX_RX_DESCR][MAX_RBUFF_SZ];      /* must be on long boundary */
++};
++
++struct at91_private
++{
++      struct net_device_stats stats;
++      struct mii_if_info mii;                 /* ethtool support */
++
++      /* PHY */
++      unsigned long phy_type;                 /* type of PHY (PHY_ID) */
++      spinlock_t lock;                        /* lock for MDI interface */
++      short phy_media;                        /* media interface type */
++
++      /* Transmit */
++      struct sk_buff *skb;                    /* holds skb until xmit interrupt completes */
++      dma_addr_t skb_physaddr;                /* phys addr from pci_map_single */
++      int skb_length;                         /* saved skb length for pci_unmap_single */
++
++      /* Receive */
++      int rxBuffIndex;                        /* index into receive descriptor list */
++      struct recv_desc_bufs *dlist;           /* descriptor list address */
++      struct recv_desc_bufs *dlist_phys;      /* descriptor list physical address */
++};
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/at91/rtc/Makefile
+@@ -0,0 +1,15 @@
++# File: drivers/at91/rtc/Makefile
++#
++# Makefile for the Atmel AT91RM9200 real time clock device drivers
++#
++
++O_TARGET := at91rtc.o
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_AT91_RTC) += at91_rtc.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/rtc/at91_rtc.c
+@@ -0,0 +1,441 @@
++/*
++ *    Real Time Clock interface for Linux on Atmel AT91RM9200
++ *
++ *    Copyright (c) 2002 Rick Bronson
++ *
++ *      Based on sa1100-rtc.c by Nils Faerber
++ *    Based on rtc.c by Paul Gortmaker
++ *    Date/time conversion routines taken from arch/arm/kernel/time.c
++ *                    by Linus Torvalds and Russell King
++ *            and the GNU C Library
++ *    ( ... I love the GPL ... just take what you need! ;)
++ *
++ *    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/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++
++#define AT91_RTC_FREQ 1
++#define EPOCH         1970
++
++/* Those are the bits from a classic RTC we want to mimic */
++#define AT91_RTC_IRQF 0x80    /* any of the following 3 is active */
++#define AT91_RTC_PF   0x40
++#define AT91_RTC_AF   0x20
++#define AT91_RTC_UF   0x10
++
++#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10)
++#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10)
++
++static unsigned long rtc_status = 0;
++static unsigned long rtc_irq_data;
++static unsigned int at91_alarm_year = EPOCH;
++
++static struct fasync_struct *at91_rtc_async_queue;
++static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_wait);
++static DECLARE_WAIT_QUEUE_HEAD(at91_rtc_update);
++static spinlock_t at91_rtc_updlock;   /* some spinlocks for saving/restoring interrupt levels */
++extern spinlock_t at91_rtc_lock;
++
++static const unsigned char days_in_mo[] =
++    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
++
++#define is_leap(year) \
++      ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
++
++static const unsigned short int __mon_yday[2][13] =
++{
++      /* Normal years.  */
++      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
++      /* Leap years.  */
++      { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
++};
++
++/*
++ * Returns day since start of the year [0-365]
++ *  (from drivers/char/efirtc.c)
++ */
++static inline int compute_yday(int year, int month, int day)
++{
++      return  __mon_yday[is_leap(year)][month] + day-1;
++}
++
++/*
++ * Set current time and date in RTC
++ */
++static void at91_rtc_settime(struct rtc_time *tval)
++{
++      unsigned long flags;
++
++      /* Stop Time/Calendar from counting */
++      AT91_SYS->RTC_CR |= (AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM);
++
++      spin_lock_irqsave(&at91_rtc_updlock, flags);    /* stop int's else we wakeup b4 we sleep */
++      AT91_SYS->RTC_IER = AT91C_RTC_ACKUPD;
++      interruptible_sleep_on(&at91_rtc_update);       /* wait for ACKUPD interrupt to hit */
++      spin_unlock_irqrestore(&at91_rtc_updlock, flags);
++      AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD;
++
++      AT91_SYS->RTC_TIMR = BIN2BCD(tval->tm_sec) << 0
++                      | BIN2BCD(tval->tm_min) << 8
++                      | BIN2BCD(tval->tm_hour) << 16;
++
++      AT91_SYS->RTC_CALR = BIN2BCD((tval->tm_year + 1900) / 100)      /* century */
++                      | BIN2BCD(tval->tm_year % 100) << 8     /* year */
++                      | BIN2BCD(tval->tm_mon + 1) << 16       /* tm_mon starts at zero */
++                      | BIN2BCD(tval->tm_wday + 1) << 21      /* day of the week [0-6], Sunday=0 */
++                      | BIN2BCD(tval->tm_mday) << 24;
++
++      /* Restart Time/Calendar */
++      AT91_SYS->RTC_CR &= ~(AT91C_RTC_UPDCAL | AT91C_RTC_UPDTIM);
++}
++
++/*
++ * Decode time/date into rtc_time structure
++ */
++static void at91_rtc_decodetime(AT91_REG *timereg, AT91_REG *calreg, struct rtc_time *tval)
++{
++      unsigned int time, date;
++
++      do {                    /* must read twice in case it changes */
++              time = *timereg;
++              date = *calreg;
++      } while ((time != *timereg) || (date != *calreg));
++
++      tval->tm_sec = BCD2BIN((time & AT91C_RTC_SEC) >> 0);
++      tval->tm_min = BCD2BIN((time & AT91C_RTC_MIN) >> 8);
++      tval->tm_hour = BCD2BIN((time & AT91C_RTC_HOUR) >> 16);
++
++      /* The Calendar Alarm register does not have a field for
++         the year - so these will return an invalid value.  When an
++         alarm is set, at91_alarm_year wille store the current year. */
++      tval->tm_year = BCD2BIN(date & AT91C_RTC_CENT) * 100;           /* century */
++      tval->tm_year += BCD2BIN((date & AT91C_RTC_YEAR) >> 8);         /* year */
++
++      tval->tm_wday = BCD2BIN((date & AT91C_RTC_DAY) >> 21) - 1;      /* day of the week [0-6], Sunday=0 */
++      tval->tm_mon = BCD2BIN(((date & AT91C_RTC_MONTH) >> 16) - 1);
++      tval->tm_mday = BCD2BIN((date & AT91C_RTC_DATE) >> 24);
++}
++
++/*
++ * IRQ handler for the RTC
++ */
++static void at91_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int rtsr = AT91_SYS->RTC_SR & AT91_SYS->RTC_IMR;
++
++      /* update irq data & counter */
++      if (rtsr) {             /* this interrupt is shared!  Is it ours? */
++              if (rtsr & AT91C_RTC_ALARM)
++                      rtc_irq_data |= (AT91_RTC_AF | AT91_RTC_IRQF);
++              if (rtsr & AT91C_RTC_SECEV)
++                      rtc_irq_data |= (AT91_RTC_UF | AT91_RTC_IRQF);
++              if (rtsr & AT91C_RTC_ACKUPD)
++                      wake_up_interruptible(&at91_rtc_update);
++              rtc_irq_data += 0x100;
++              AT91_SYS->RTC_SCCR = rtsr;              /* clear status reg */
++
++              /* wake up waiting process */
++              wake_up_interruptible(&at91_rtc_wait);
++              kill_fasync(&at91_rtc_async_queue, SIGIO, POLL_IN);
++      }
++}
++
++static int at91_rtc_open(struct inode *inode, struct file *file)
++{
++      if (test_and_set_bit(1, &rtc_status))
++              return -EBUSY;
++      rtc_irq_data = 0;
++      return 0;
++}
++
++static int at91_rtc_release(struct inode *inode, struct file *file)
++{
++      rtc_status = 0;
++      return 0;
++}
++
++static int at91_rtc_fasync(int fd, struct file *filp, int on)
++{
++      return fasync_helper(fd, filp, on, &at91_rtc_async_queue);
++}
++
++static unsigned int at91_rtc_poll(struct file *file, poll_table * wait)
++{
++      poll_wait(file, &at91_rtc_wait, wait);
++      return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM;
++}
++
++static ssize_t at91_rtc_read(struct file * file, char *buf, size_t count, loff_t * ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      unsigned long data;
++      ssize_t retval;
++
++      if (count < sizeof(unsigned long))
++              return -EINVAL;
++
++      add_wait_queue(&at91_rtc_wait, &wait);
++      set_current_state(TASK_INTERRUPTIBLE);
++      for (;;) {
++              spin_lock_irq(&at91_rtc_lock);
++              data = rtc_irq_data;
++              if (data != 0) {
++                      rtc_irq_data = 0;
++                      break;
++              }
++              spin_unlock_irq(&at91_rtc_lock);
++
++              if (file->f_flags & O_NONBLOCK) {
++                      retval = -EAGAIN;
++                      goto out;
++              }
++
++              if (signal_pending(current)) {
++                      retval = -ERESTARTSYS;
++                      goto out;
++              }
++
++              schedule();
++      }
++      spin_unlock_irq(&at91_rtc_lock);
++
++      data -= 0x100;          /* the first IRQ wasn't actually missed */
++      retval = put_user(data, (unsigned long *) buf);
++      if (!retval)
++              retval = sizeof(unsigned long);
++
++out:
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&at91_rtc_wait, &wait);
++      return retval;
++}
++
++/*
++ * Handle commands from user-space
++ */
++static int at91_rtc_ioctl(struct inode *inode, struct file *file,
++                        unsigned int cmd, unsigned long arg)
++{
++      struct rtc_time tm, tm2;
++      int ret = 0;
++
++      spin_lock_irq(&at91_rtc_lock);
++      switch (cmd) {
++      case RTC_AIE_OFF:       /* alarm off */
++              AT91_SYS->RTC_IDR = AT91C_RTC_ALARM;
++              rtc_irq_data = 0;
++              break;
++      case RTC_AIE_ON:        /* alarm on */
++              AT91_SYS->RTC_IER = AT91C_RTC_ALARM;
++              rtc_irq_data = 0;
++              break;
++      case RTC_UIE_OFF:       /* update off */
++              AT91_SYS->RTC_IDR = AT91C_RTC_SECEV;
++              rtc_irq_data = 0;
++              break;
++      case RTC_UIE_ON:        /* update on */
++              AT91_SYS->RTC_IER = AT91C_RTC_SECEV;
++              rtc_irq_data = 0;
++              break;
++      case RTC_PIE_OFF:       /* periodic off */
++              AT91_SYS->RTC_IDR = AT91C_RTC_SECEV;
++              rtc_irq_data = 0;
++              break;
++      case RTC_PIE_ON:        /* periodic on */
++              AT91_SYS->RTC_IER = AT91C_RTC_SECEV;
++              rtc_irq_data = 0;
++              break;
++      case RTC_ALM_READ:      /* read alarm */
++              memset(&tm, 0, sizeof(struct rtc_time));
++              at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm);
++              tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday);
++              tm.tm_year = at91_alarm_year - 1900;
++              ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0;
++              break;
++      case RTC_ALM_SET:       /* set alarm */
++              if (copy_from_user(&tm2, (struct rtc_time *) arg, sizeof(tm2)))
++                      ret = -EFAULT;
++              else {
++                      at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm);
++                      at91_alarm_year = tm.tm_year;
++                      if ((unsigned) tm2.tm_hour < 24)        /* do some range checking */
++                              tm.tm_hour = tm2.tm_hour;
++                      if ((unsigned) tm2.tm_min < 60)
++                              tm.tm_min = tm2.tm_min;
++                      if ((unsigned) tm2.tm_sec < 60)
++                              tm.tm_sec = tm2.tm_sec;
++                      AT91_SYS->RTC_TIMALR = BIN2BCD(tm.tm_sec) << 0
++                              | BIN2BCD(tm.tm_min) << 8
++                              | BIN2BCD(tm.tm_hour) << 16
++                              | AT91C_RTC_HOUREN | AT91C_RTC_MINEN
++                              | AT91C_RTC_SECEN;
++                      AT91_SYS->RTC_CALALR = BIN2BCD(tm.tm_mon + 1) << 16     /* tm_mon starts at zero */
++                              | BIN2BCD(tm.tm_mday) << 24
++                              | AT91C_RTC_DATEEN | AT91C_RTC_MONTHEN;
++              }
++              break;
++      case RTC_RD_TIME:       /* read time */
++              memset(&tm, 0, sizeof(struct rtc_time));
++              at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm);
++              tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday);
++              tm.tm_year = tm.tm_year - 1900;
++              ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0;
++              break;
++      case RTC_SET_TIME:      /* set time */
++              if (!capable(CAP_SYS_TIME))
++                      ret = -EACCES;
++              else {
++                      if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm)))
++                              ret = -EFAULT;
++                      else {
++                              int tm_year = tm.tm_year + 1900;
++                              if (tm_year < EPOCH
++                                  || (unsigned) tm.tm_mon >= 12
++                                  || tm.tm_mday < 1
++                                  || tm.tm_mday > (days_in_mo[tm.tm_mon] + (tm.tm_mon == 1 && is_leap(tm_year)))
++                                  || (unsigned) tm.tm_hour >= 24
++                                  || (unsigned) tm.tm_min >= 60
++                                  || (unsigned) tm.tm_sec >= 60)
++                                      ret = -EINVAL;
++                              else
++                                      at91_rtc_settime(&tm);
++                      }
++              }
++              break;
++      case RTC_IRQP_READ:     /* read periodic alarm frequency */
++              ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg);
++              break;
++      case RTC_IRQP_SET:      /* set periodic alarm frequency */
++              if (arg != AT91_RTC_FREQ)
++                      ret = -EINVAL;
++              break;
++      case RTC_EPOCH_READ:    /* read epoch */
++              ret = put_user(EPOCH, (unsigned long *) arg);
++              break;
++      default:
++              ret = -EINVAL;
++              break;
++      }
++      spin_unlock_irq(&at91_rtc_lock);
++      return ret;
++}
++
++/*
++ * Provide RTC information in /proc/driver/rtc
++ */
++static int at91_rtc_read_proc(char *page, char **start, off_t off,
++                            int count, int *eof, void *data)
++{
++      char *p = page;
++      int len;
++      struct rtc_time tm;
++
++      at91_rtc_decodetime(&(AT91_SYS->RTC_TIMR), &(AT91_SYS->RTC_CALR), &tm);
++      p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n"
++                      "rtc_date\t: %04d-%02d-%02d\n"
++                      "rtc_epoch\t: %04d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      tm.tm_year, tm.tm_mon + 1, tm.tm_mday, EPOCH);
++      at91_rtc_decodetime(&(AT91_SYS->RTC_TIMALR), &(AT91_SYS->RTC_CALALR), &tm);
++      p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n"
++                      "alrm_date\t: %04d-%02d-%02d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      at91_alarm_year, tm.tm_mon + 1, tm.tm_mday);
++      p += sprintf(p, "alarm_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ALARM) ? "yes" : "no");
++      p += sprintf(p, "update_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_ACKUPD) ? "yes" : "no");
++      p += sprintf(p, "periodic_IRQ\t: %s\n", (AT91_SYS->RTC_IMR & AT91C_RTC_SECEV) ? "yes" : "no");
++      p += sprintf(p, "periodic_freq\t: %ld\n", (unsigned long) AT91_RTC_FREQ);
++
++      len = (p - page) - off;
++      if (len < 0)
++              len = 0;
++
++      *eof = (len <= count) ? 1 : 0;
++      *start = page + off;
++
++      return len;
++}
++
++static struct file_operations at91_rtc_fops = {
++      owner:THIS_MODULE,
++      llseek:no_llseek,
++      read:at91_rtc_read,
++      poll:at91_rtc_poll,
++      ioctl:at91_rtc_ioctl,
++      open:at91_rtc_open,
++      release:at91_rtc_release,
++      fasync:at91_rtc_fasync,
++};
++
++static struct miscdevice at91_rtc_miscdev = {
++      minor:RTC_MINOR,
++      name:"rtc",
++      fops:&at91_rtc_fops,
++};
++
++/*
++ * Initialize and install RTC driver
++ */
++static int __init at91_rtc_init(void)
++{
++      int ret;
++
++      AT91_SYS->RTC_CR = 0;
++      AT91_SYS->RTC_MR = 0;   /* put in 24 hour format */
++      /* Disable all interrupts */
++      AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV;
++
++      spin_lock_init(&at91_rtc_updlock);
++      spin_lock_init(&at91_rtc_lock);
++
++      misc_register(&at91_rtc_miscdev);
++      create_proc_read_entry("driver/rtc", 0, 0, at91_rtc_read_proc, NULL);
++      ret = request_irq(AT91C_ID_SYS, at91_rtc_interrupt, SA_SHIRQ,
++                      "at91_rtc", &rtc_status);
++      if (ret) {
++              printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", AT91C_ID_SYS);
++              remove_proc_entry("driver/rtc", NULL);
++              misc_deregister(&at91_rtc_miscdev);
++              return ret;
++      }
++
++      printk(KERN_INFO "AT91 Real Time Clock driver\n");
++      return 0;
++}
++
++/*
++ * Disable and remove the RTC driver
++ */
++static void __exit at91_rtc_exit(void)
++{
++      /* Disable all interrupts */
++      AT91_SYS->RTC_IDR = AT91C_RTC_ACKUPD | AT91C_RTC_ALARM | AT91C_RTC_SECEV | AT91C_RTC_TIMEV | AT91C_RTC_CALEV;
++      free_irq(AT91C_ID_SYS, &rtc_status);
++
++      rtc_status = 0;
++      remove_proc_entry("driver/rtc", NULL);
++      misc_deregister(&at91_rtc_miscdev);
++}
++
++module_init(at91_rtc_init);
++module_exit(at91_rtc_exit);
++
++MODULE_AUTHOR("Rick Bronson");
++MODULE_DESCRIPTION("AT91 Realtime Clock Driver (AT91_RTC)");
++MODULE_LICENSE("GPL");
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/at91/serial/Makefile
+@@ -0,0 +1,15 @@
++# File: drivers/at91/serial/Makefile
++#
++# Makefile for the Atmel AT91RM9200 serial and console device drivers
++#
++
++O_TARGET := at91serial.o
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_SERIAL_AT91) += at91_serial.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/serial/at91_serial.c
+@@ -0,0 +1,881 @@
++/*
++ *  linux/drivers/char/at91_serial.c
++ *
++ *  Driver for Atmel AT91RM9200 Serial ports
++ *
++ *  Copyright (c) Rick Bronson
++ *
++ *  Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd.
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ * 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/tty.h>
++#include <linux/ioport.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/arch/AT91RM9200_USART.h>
++#include <asm/mach/serial_at91rm9200.h>
++#include <asm/arch/pio.h>
++
++
++#if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#define SERIAL_AT91_MAJOR     TTY_MAJOR
++#define CALLOUT_AT91_MAJOR    TTYAUX_MAJOR
++#define MINOR_START           64
++
++#define AT91C_VA_BASE_DBGU    ((unsigned long) &(AT91_SYS->DBGU_CR))
++#define AT91_ISR_PASS_LIMIT   256
++
++#define UART_PUT_CR(port,v)   ((AT91PS_USART)(port)->membase)->US_CR = v
++#define UART_GET_MR(port)     ((AT91PS_USART)(port)->membase)->US_MR
++#define UART_PUT_MR(port,v)   ((AT91PS_USART)(port)->membase)->US_MR = v
++#define UART_PUT_IER(port,v)  ((AT91PS_USART)(port)->membase)->US_IER = v
++#define UART_PUT_IDR(port,v)  ((AT91PS_USART)(port)->membase)->US_IDR = v
++#define UART_GET_IMR(port)    ((AT91PS_USART)(port)->membase)->US_IMR
++#define UART_GET_CSR(port)    ((AT91PS_USART)(port)->membase)->US_CSR
++#define UART_GET_CHAR(port)   ((AT91PS_USART)(port)->membase)->US_RHR
++#define UART_PUT_CHAR(port,v) ((AT91PS_USART)(port)->membase)->US_THR = v
++#define UART_GET_BRGR(port)   ((AT91PS_USART)(port)->membase)->US_BRGR
++#define UART_PUT_BRGR(port,v) ((AT91PS_USART)(port)->membase)->US_BRGR = v
++#define UART_PUT_RTOR(port,v) ((AT91PS_USART)(port)->membase)->US_RTOR = v
++
++// #define UART_GET_CR(port)  ((AT91PS_USART)(port)->membase)->US_CR          // is write-only
++
++ /* PDC registers */
++#define UART_PUT_PTCR(port,v) ((AT91PS_USART)(port)->membase)->US_PTCR = v
++#define UART_PUT_RPR(port,v)  ((AT91PS_USART)(port)->membase)->US_RPR = v
++#define UART_PUT_RCR(port,v)  ((AT91PS_USART)(port)->membase)->US_RCR = v
++#define UART_GET_RCR(port)    ((AT91PS_USART)(port)->membase)->US_RCR
++#define UART_PUT_RNPR(port,v) ((AT91PS_USART)(port)->membase)->US_RNPR = v
++#define UART_PUT_RNCR(port,v) ((AT91PS_USART)(port)->membase)->US_RNCR = v
++
++static struct tty_driver normal, callout;
++static struct tty_struct *at91_table[AT91C_NR_UART];
++static struct termios *at91_termios[AT91C_NR_UART], *at91_termios_locked[AT91C_NR_UART];
++
++const int at91_serialmap[AT91C_NR_UART] = AT91C_UART_MAP;
++
++static int (*at91_open)(struct uart_port *);
++static void (*at91_close)(struct uart_port *);
++
++#ifdef SUPPORT_SYSRQ
++static struct console at91_console;
++#endif
++
++/*
++ * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
++ */
++static u_int at91_tx_empty(struct uart_port *port)
++{
++      return UART_GET_CSR(port) & AT91C_US_TXEMPTY ? TIOCSER_TEMT : 0;
++}
++
++/*
++ * Set state of the modem control output lines
++ */
++static void at91_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++      unsigned int control = 0;
++
++      /*
++       * Errata #39: RTS0 is not internally connected to PA21.  We need to drive
++       *  the pin manually.
++       */
++      if (port->mapbase == AT91C_VA_BASE_US0) {
++              if (mctrl & TIOCM_RTS)
++                      AT91_SYS->PIOA_CODR = AT91C_PA21_RTS0;
++              else
++                      AT91_SYS->PIOA_SODR = AT91C_PA21_RTS0;
++      }
++
++      if (mctrl & TIOCM_RTS)
++              control |= AT91C_US_RTSEN;
++      else
++              control |= AT91C_US_RTSDIS;
++
++      if (mctrl & TIOCM_DTR)
++              control |= AT91C_US_DTREN;
++      else
++              control |=  AT91C_US_DTRDIS;
++
++      UART_PUT_CR(port,control);
++}
++
++/*
++ * Get state of the modem control input lines
++ */
++static u_int at91_get_mctrl(struct uart_port *port)
++{
++      unsigned int status, ret = 0;
++
++      status = UART_GET_CSR(port);
++      if (status & AT91C_US_DCD)
++              ret |= TIOCM_CD;
++      if (status & AT91C_US_CTS)
++              ret |= TIOCM_CTS;
++      if (status & AT91C_US_DSR)
++              ret |= TIOCM_DSR;
++      if (status & AT91C_US_RI)
++              ret |= TIOCM_RI;
++
++      return ret;
++}
++
++/*
++ * Stop transmitting.
++ */
++static void at91_stop_tx(struct uart_port *port, u_int from_tty)
++{
++      UART_PUT_IDR(port, AT91C_US_TXRDY);
++      port->read_status_mask &= ~AT91C_US_TXRDY;
++}
++
++/*
++ * Start transmitting.
++ */
++static void at91_start_tx(struct uart_port *port, u_int from_tty)
++{
++      unsigned long flags;
++
++      local_irq_save(flags);
++      port->read_status_mask |= AT91C_US_TXRDY;
++      UART_PUT_IER(port, AT91C_US_TXRDY);
++      local_irq_restore(flags);
++}
++
++/*
++ * Stop receiving - port is in process of being closed.
++ */
++static void at91_stop_rx(struct uart_port *port)
++{
++      UART_PUT_IDR(port, AT91C_US_RXRDY);
++}
++
++/*
++ * Enable modem status interrupts
++ */
++static void at91_enable_ms(struct uart_port *port)
++{
++      UART_PUT_IER(port, AT91C_US_RIIC | AT91C_US_DSRIC | AT91C_US_DCDIC | AT91C_US_CTSIC);
++}
++
++/*
++ * Control the transmission of a break signal
++ */
++static void at91_break_ctl(struct uart_port *port, int break_state)
++{
++      if (break_state != 0)
++              UART_PUT_CR(port, AT91C_US_STTBRK);     /* start break */
++      else
++              UART_PUT_CR(port, AT91C_US_STPBRK);     /* stop break */
++}
++
++/*
++ * Characters received (called from interrupt handler)
++ */
++static void at91_rx_chars(struct uart_port *port, struct pt_regs *regs)
++{
++      struct uart_info *info = port->info;
++      struct tty_struct *tty = info->tty;
++      unsigned int status, ch, flg, ignored = 0;
++
++      status = UART_GET_CSR(port);
++      while (status & (AT91C_US_RXRDY)) {
++              ch = UART_GET_CHAR(port);
++
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              port->icount.rx++;
++
++              flg = TTY_NORMAL;
++
++              /*
++               * note that the error handling code is
++               * out of the main execution path
++               */
++              if (status & (AT91C_US_PARE | AT91C_US_FRAME | AT91C_US_OVRE))
++                      goto handle_error;
++
++              if (uart_handle_sysrq_char(port, ch, regs))
++                      goto ignore_char;
++
++      error_return:
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++      ignore_char:
++              status = UART_GET_CSR(port);
++      }
++out:
++      tty_flip_buffer_push(tty);
++      return;
++
++handle_error:
++      if (status & (AT91C_US_PARE | AT91C_US_FRAME | AT91C_US_OVRE))
++              UART_PUT_CR(port, AT91C_US_RSTSTA);  /* clear error */
++      if (status & (AT91C_US_PARE))
++              port->icount.parity++;
++      else if (status & (AT91C_US_FRAME))
++              port->icount.frame++;
++      if (status & (AT91C_US_OVRE))
++              port->icount.overrun++;
++
++      if (status & port->ignore_status_mask) {
++              if (++ignored > 100)
++                      goto out;
++              goto ignore_char;
++      }
++
++      status &= port->read_status_mask;
++
++      UART_PUT_CR(port, AT91C_US_RSTSTA);  /* clear error */
++      if (status & AT91C_US_PARE)
++              flg = TTY_PARITY;
++      else if (status & AT91C_US_FRAME)
++              flg = TTY_FRAME;
++
++      if (status & AT91C_US_OVRE) {
++              /*
++               * overrun does *not* affect the character
++               * we read from the FIFO
++               */
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              ch = 0;
++              flg = TTY_OVERRUN;
++      }
++#ifdef SUPPORT_SYSRQ
++      port->sysrq = 0;
++#endif
++      goto error_return;
++}
++
++/*
++ * Transmit characters (called from interrupt handler)
++ */
++static void at91_tx_chars(struct uart_port *port)
++{
++      struct circ_buf *xmit = &port->info->xmit;
++
++      if (port->x_char) {
++              UART_PUT_CHAR(port, port->x_char);
++              port->icount.tx++;
++              port->x_char = 0;
++              return;
++      }
++      if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
++              at91_stop_tx(port, 0);
++              return;
++      }
++
++      while (UART_GET_CSR(port) & AT91C_US_TXRDY) {
++              UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      }
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(port);
++
++      if (uart_circ_empty(xmit))
++              at91_stop_tx(port, 0);
++}
++
++/*
++ * Interrupt handler
++ */
++static void at91_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      unsigned int status, pending, pass_counter = 0;
++
++      status = UART_GET_CSR(port);
++      pending = status & port->read_status_mask;
++      if (pending) {
++              do {
++                      if (pending & AT91C_US_RXRDY)
++                              at91_rx_chars(port, regs);
++
++                      /* Clear the relevent break bits */
++                      if (pending & AT91C_US_RXBRK) {
++                              UART_PUT_CR(port, AT91C_US_RSTSTA);
++                              port->icount.brk++;
++#ifdef SUPPORT_SYSRQ
++                              if (port->line == at91_console.index && !port->sysrq) {
++                                      port->sysrq = jiffies + HZ*5;
++                              }
++#endif
++                      }
++
++                      // TODO: All reads to CSR will clear these interrupts!
++                      if (pending & AT91C_US_RIIC) port->icount.rng++;
++                      if (pending & AT91C_US_DSRIC) port->icount.dsr++;
++                      if (pending & AT91C_US_DCDIC) {
++                              port->icount.dcd++;
++                              uart_handle_dcd_change(port, status & AT91C_US_DCD);
++                      }
++                      if (pending & AT91C_US_CTSIC) {
++                              port->icount.cts++;
++                              uart_handle_cts_change(port, status & AT91C_US_CTS);
++                      }
++                      if (pending & (AT91C_US_RIIC | AT91C_US_DSRIC | AT91C_US_DCDIC | AT91C_US_CTSIC))
++                              wake_up_interruptible(&port->info->delta_msr_wait);
++
++                      if (pending & AT91C_US_TXRDY)
++                              at91_tx_chars(port);
++                      if (pass_counter++ > AT91_ISR_PASS_LIMIT)
++                              break;
++
++                      status = UART_GET_CSR(port);
++                      pending = status & port->read_status_mask;
++              } while (pending);
++      }
++}
++
++/*
++ * Perform initialization and enable port for reception
++ */
++static int at91_startup(struct uart_port *port)
++{
++      int retval;
++
++      /*
++       * Allocate the IRQ
++       */
++      retval = request_irq(port->irq, at91_interrupt, SA_SHIRQ, "at91_serial", port);
++      if (retval) {
++              printk("at91_serial: at91_startup - Can't get irq\n");
++              return retval;
++      }
++      /*
++       * If there is a specific "open" function (to register
++       * control line interrupts)
++       */
++      if (at91_open) {
++              retval = at91_open(port);
++              if (retval) {
++                      free_irq(port->irq, port);
++                      return retval;
++              }
++      }
++
++      /* Enable peripheral clock if required */
++      if (port->irq != AT91C_ID_SYS)
++              AT91_SYS->PMC_PCER = 1 << port->irq;
++
++      port->read_status_mask = AT91C_US_RXRDY | AT91C_US_TXRDY | AT91C_US_OVRE
++                      | AT91C_US_FRAME | AT91C_US_PARE | AT91C_US_RXBRK;
++      /*
++       * Finally, clear and enable interrupts
++       */
++      UART_PUT_IDR(port, -1);
++      UART_PUT_CR(port, AT91C_US_TXEN | AT91C_US_RXEN);  /* enable xmit & rcvr */
++      UART_PUT_IER(port, AT91C_US_RXRDY);  /* do receive only */
++      return 0;
++}
++
++/*
++ * Disable the port
++ */
++static void at91_shutdown(struct uart_port *port)
++{
++      /*
++       * Free the interrupt
++       */
++      free_irq(port->irq, port);
++
++      /*
++       * If there is a specific "close" function (to unregister
++       * control line interrupts)
++       */
++      if (at91_close)
++              at91_close(port);
++
++      /*
++       * Disable all interrupts, port and break condition.
++       */
++      UART_PUT_CR(port, AT91C_US_RSTSTA);
++      UART_PUT_IDR(port, -1);
++
++      /* Disable peripheral clock if required */
++      if (port->irq != AT91C_ID_SYS)
++              AT91_SYS->PMC_PCDR = 1 << port->irq;
++}
++
++static struct uart_ops at91_pops;             /* forward declaration */
++
++/*
++ * Change the port parameters
++ */
++static void at91_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)
++{
++      unsigned long flags;
++      unsigned int mode, imr;
++
++      /* Get current mode register */
++      mode = UART_GET_MR(port) & ~(AT91C_US_CHRL | AT91C_US_NBSTOP | AT91C_US_PAR);
++
++      /* byte size */
++      switch (cflag & CSIZE) {
++      case CS5:
++              mode |= AT91C_US_CHRL_5_BITS;
++              break;
++      case CS6:
++              mode |= AT91C_US_CHRL_6_BITS;
++              break;
++      case CS7:
++              mode |= AT91C_US_CHRL_7_BITS;
++              break;
++      default:
++              mode |= AT91C_US_CHRL_8_BITS;
++              break;
++      }
++
++      /* stop bits */
++      if (cflag & CSTOPB)
++              mode |= AT91C_US_NBSTOP_2_BIT;
++
++      /* parity */
++      if (cflag & PARENB) {
++              if (cflag & CMSPAR) {                   /* Mark or Space parity */
++                      if (cflag & PARODD)
++                              mode |= AT91C_US_PAR_MARK;
++                      else
++                              mode |= AT91C_US_PAR_SPACE;
++              }
++              else if (cflag & PARODD)
++                      mode |= AT91C_US_PAR_ODD;
++              else
++                      mode |= AT91C_US_PAR_EVEN;
++      }
++      else
++              mode |= AT91C_US_PAR_NONE;
++
++      port->read_status_mask |= AT91C_US_OVRE;
++      if (iflag & INPCK)
++              port->read_status_mask |= AT91C_US_FRAME | AT91C_US_PARE;
++      if (iflag & (BRKINT | PARMRK))
++              port->read_status_mask |= AT91C_US_RXBRK;
++
++      /*
++       * Characters to ignore
++       */
++      port->ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              port->ignore_status_mask |= (AT91C_US_FRAME | AT91C_US_PARE);
++      if (iflag & IGNBRK) {
++              port->ignore_status_mask |= AT91C_US_RXBRK;
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns too (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      port->ignore_status_mask |= AT91C_US_OVRE;
++      }
++
++      // TODO: Ignore all characters if CREAD is set.
++
++      /* first, disable interrupts and drain transmitter */
++      local_irq_save(flags);
++      imr = UART_GET_IMR(port);       /* get interrupt mask */
++      UART_PUT_IDR(port, -1);         /* disable all interrupts */
++      local_irq_restore(flags);
++      while (!(UART_GET_CSR(port) & AT91C_US_TXEMPTY)) { barrier(); }
++
++      /* disable receiver and transmitter */
++      UART_PUT_CR(port, AT91C_US_TXDIS | AT91C_US_RXDIS);
++
++      /* set the parity, stop bits and data size */
++      UART_PUT_MR(port, mode);
++
++      /* set the baud rate */
++      UART_PUT_BRGR(port, quot);
++      UART_PUT_CR(port, AT91C_US_TXEN | AT91C_US_RXEN);
++
++      /* restore interrupts */
++      UART_PUT_IER(port, imr);
++
++      /* CTS flow-control and modem-status interrupts */
++      if (UART_ENABLE_MS(port, cflag))
++              at91_pops.enable_ms(port);
++}
++
++/*
++ * Return string describing the specified port
++ */
++static const char *at91_type(struct uart_port *port)
++{
++      return port->type == PORT_AT91RM9200 ? "AT91_SERIAL" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'.
++ */
++static void at91_release_port(struct uart_port *port)
++{
++      release_mem_region(port->mapbase,
++              port->mapbase == AT91C_VA_BASE_DBGU ? 512 : SZ_16K);
++}
++
++/*
++ * Request the memory region(s) being used by 'port'.
++ */
++static int at91_request_port(struct uart_port *port)
++{
++      return request_mem_region(port->mapbase,
++              port->mapbase == AT91C_VA_BASE_DBGU ? 512 : SZ_16K,
++              "at91_serial") != NULL ? 0 : -EBUSY;
++
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void at91_config_port(struct uart_port *port, int flags)
++{
++      if (flags & UART_CONFIG_TYPE) {
++              port->type = PORT_AT91RM9200;
++              at91_request_port(port);
++      }
++}
++
++/*
++ * Verify the new serial_struct (for TIOCSSERIAL).
++ */
++static int at91_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      int ret = 0;
++      if (ser->type != PORT_UNKNOWN && ser->type != PORT_AT91RM9200)
++              ret = -EINVAL;
++      if (port->irq != ser->irq)
++              ret = -EINVAL;
++      if (ser->io_type != SERIAL_IO_MEM)
++              ret = -EINVAL;
++      if (port->uartclk / 16 != ser->baud_base)
++              ret = -EINVAL;
++      if ((void *)port->mapbase != ser->iomem_base)
++              ret = -EINVAL;
++      if (port->iobase != ser->port)
++              ret = -EINVAL;
++      if (ser->hub6 != 0)
++              ret = -EINVAL;
++      return ret;
++}
++
++static struct uart_ops at91_pops = {
++      tx_empty:       at91_tx_empty,
++      set_mctrl:      at91_set_mctrl,
++      get_mctrl:      at91_get_mctrl,
++      stop_tx:        at91_stop_tx,
++      start_tx:       at91_start_tx,
++      stop_rx:        at91_stop_rx,
++      enable_ms:      at91_enable_ms,
++      break_ctl:      at91_break_ctl,
++      startup:        at91_startup,
++      shutdown:       at91_shutdown,
++      change_speed:   at91_change_speed,
++      type:           at91_type,
++      release_port:   at91_release_port,
++      request_port:   at91_request_port,
++      config_port:    at91_config_port,
++      verify_port:    at91_verify_port,
++};
++
++static struct uart_port at91_ports[AT91C_NR_UART];
++
++void __init at91_init_ports(void)
++{
++      static int first = 1;
++      int i;
++
++      if (!first)
++              return;
++      first = 0;
++
++      for (i = 0; i < AT91C_NR_UART; i++) {
++              at91_ports[i].iotype    = SERIAL_IO_MEM;
++              at91_ports[i].flags     = ASYNC_BOOT_AUTOCONF;
++              at91_ports[i].uartclk   = AT91C_MASTER_CLOCK;
++              at91_ports[i].ops       = &at91_pops;
++              at91_ports[i].fifosize  = 1;
++              at91_ports[i].line      = i;
++      }
++}
++
++void __init at91_register_uart_fns(struct at91rm9200_port_fns *fns)
++{
++      if (fns->enable_ms)
++              at91_pops.enable_ms = fns->enable_ms;
++      if (fns->get_mctrl)
++              at91_pops.get_mctrl = fns->get_mctrl;
++      if (fns->set_mctrl)
++              at91_pops.set_mctrl = fns->set_mctrl;
++      at91_open          = fns->open;
++      at91_close         = fns->close;
++      at91_pops.pm       = fns->pm;
++      at91_pops.set_wake = fns->set_wake;
++}
++
++/*
++ * Setup ports.
++ */
++void __init at91_register_uart(int idx, int port)
++{
++      if ((idx < 0) || (idx >= AT91C_NR_UART)) {
++              printk(KERN_ERR __FUNCTION__ ": bad index number %d\n", idx);
++              return;
++      }
++
++      switch (port) {
++      case 0:
++              at91_ports[idx].membase = (void *) AT91C_VA_BASE_US0;
++              at91_ports[idx].mapbase = AT91C_VA_BASE_US0;
++              at91_ports[idx].irq     = AT91C_ID_US0;
++              AT91_CfgPIO_USART0();
++              break;
++      case 1:
++              at91_ports[idx].membase = (void *) AT91C_VA_BASE_US1;
++              at91_ports[idx].mapbase = AT91C_VA_BASE_US1;
++              at91_ports[idx].irq     = AT91C_ID_US1;
++              AT91_CfgPIO_USART1();
++              break;
++      case 2:
++              at91_ports[idx].membase = (void *) AT91C_VA_BASE_US2;
++              at91_ports[idx].mapbase = AT91C_VA_BASE_US2;
++              at91_ports[idx].irq     = AT91C_ID_US2;
++              AT91_CfgPIO_USART2();
++              break;
++      case 3:
++              at91_ports[idx].membase = (void *) AT91C_VA_BASE_US3;
++              at91_ports[idx].mapbase = AT91C_VA_BASE_US3;
++              at91_ports[idx].irq     = AT91C_ID_US3;
++              AT91_CfgPIO_USART3();
++              break;
++      case 4:
++              at91_ports[idx].membase = (void *) AT91C_VA_BASE_DBGU;
++              at91_ports[idx].mapbase = AT91C_VA_BASE_DBGU;
++              at91_ports[idx].irq     = AT91C_ID_SYS;
++              AT91_CfgPIO_DBGU();
++              break;
++      default:
++              printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port);
++      }
++}
++
++#ifdef CONFIG_SERIAL_AT91_CONSOLE
++
++/*
++ * Interrupts are disabled on entering
++ */
++static void at91_console_write(struct console *co, const char *s, u_int count)
++{
++      struct uart_port *port = at91_ports + co->index;
++      unsigned int status, i, imr;
++
++      /*
++       *      First, save IMR and then disable interrupts
++       */
++      imr = UART_GET_IMR(port);       /* get interrupt mask */
++      UART_PUT_IDR(port, AT91C_US_RXRDY | AT91C_US_TXRDY);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = UART_GET_CSR(port);
++              } while (!(status & AT91C_US_TXRDY));
++              UART_PUT_CHAR(port, s[i]);
++              if (s[i] == '\n') {
++                      do {
++                              status = UART_GET_CSR(port);
++                      } while (!(status & AT91C_US_TXRDY));
++                      UART_PUT_CHAR(port, '\r');
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore IMR
++       */
++      do {
++              status = UART_GET_CSR(port);
++      } while (!(status & AT91C_US_TXRDY));
++      UART_PUT_IER(port, imr);        /* set interrupts back the way they were */
++}
++
++static kdev_t at91_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_AT91_MAJOR, MINOR_START + co->index);
++}
++
++/*
++ * If the port was already initialised (eg, by a boot loader), try to determine
++ * the current setup.
++ */
++static void __init at91_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
++{
++      unsigned int mr, quot;
++
++// TODO: CR is a write-only register
++//    unsigned int cr;
++//
++//    cr = UART_GET_CR(port) & (AT91C_US_RXEN | AT91C_US_TXEN);
++//    if (cr == (AT91C_US_RXEN | AT91C_US_TXEN)) {
++//            /* ok, the port was enabled */
++//
++//            mr = UART_GET_MR(port) & AT91C_US_PAR;
++//
++//            *parity = 'n';
++//            if (mr == AT91C_US_PAR_EVEN)
++//                    *parity = 'e';
++//            else if (mr == AT91C_US_PAR_ODD)
++//                    *parity = 'o';
++//    }
++
++      mr = UART_GET_MR(port) & AT91C_US_CHRL;
++      if (mr == AT91C_US_CHRL_8_BITS)
++              *bits = 8;
++      else
++              *bits = 7;
++
++      quot = UART_GET_BRGR(port);
++      *baud = port->uartclk / (16 * (quot));
++}
++
++static int __init at91_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = AT91C_CONSOLE_DEFAULT_BAUDRATE;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      port = uart_get_console(at91_ports, AT91C_NR_UART, co);
++
++      // TODO: The console port should be initialized, and clock enabled if
++      //  we're not relying on the bootloader to do it.
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              at91_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console at91_console = {
++      name:           "ttyS",
++      write:          at91_console_write,
++      device:         at91_console_device,
++      setup:          at91_console_setup,
++      flags:          CON_PRINTBUFFER,
++      index:          AT91C_CONSOLE,
++};
++
++#define AT91_CONSOLE_DEVICE   &at91_console
++
++void __init at91_console_init(void)
++{
++      at91_init_ports();
++      register_console(&at91_console);
++}
++
++#else
++#define AT91_CONSOLE_DEVICE   NULL
++#endif
++
++static struct uart_driver at91_reg = {
++      owner:                  THIS_MODULE,
++      normal_major:           SERIAL_AT91_MAJOR,
++#ifdef CONFIG_DEVFS_FS
++      normal_name:            "ttyS%d",
++      callout_name:           "cua%d",
++#else
++      normal_name:            "ttyS",
++      callout_name:           "cua",
++#endif
++      normal_driver:          &normal,
++      callout_major:          CALLOUT_AT91_MAJOR,
++      callout_driver:         &callout,
++      table:                  at91_table,
++      termios:                at91_termios,
++      termios_locked:         at91_termios_locked,
++      minor:                  MINOR_START,
++      nr:                     AT91C_NR_UART,
++      cons:                   AT91_CONSOLE_DEVICE,
++};
++
++static int __init at91_serial_init(void)
++{
++      int ret, i;
++
++      at91_init_ports();
++
++      ret = uart_register_driver(&at91_reg);
++      if (ret)
++              return ret;
++
++      for (i = 0; i < AT91C_NR_UART; i++) {
++              if (at91_serialmap[i] >= 0)
++                      uart_add_one_port(&at91_reg, &at91_ports[i]);
++      }
++
++      return 0;
++}
++
++static void __exit at91_serial_exit(void)
++{
++      int i;
++
++      for (i = 0; i < AT91C_NR_UART; i++) {
++              if (at91_serialmap[i] >= 0)
++                      uart_remove_one_port(&at91_reg, &at91_ports[i]);
++      }
++
++      uart_unregister_driver(&at91_reg);
++}
++
++module_init(at91_serial_init);
++module_exit(at91_serial_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("Rick Bronson");
++MODULE_DESCRIPTION("AT91 generic serial port driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/at91/spi/Makefile
+@@ -0,0 +1,17 @@
++# File: drivers/at91/spi/Makefile
++#
++# Makefile for the Atmel AT91RM9200 SPI device drivers
++#
++
++O_TARGET := at91spi.o
++
++export-objs := at91_spi.o
++
++obj-y := at91_spi.o
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_AT91_SPIDEV) += at91_spidev.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/spi/at91_spi.c
+@@ -0,0 +1,275 @@
++/*
++ * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <asm/semaphore.h>
++#include <linux/pci.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++
++#include <asm/arch/AT91RM9200_SPI.h>
++#include <asm/arch/pio.h>
++#include "at91_spi.h"
++
++#undef DEBUG_SPI
++
++static struct spi_local spi_dev[NR_SPI_DEVICES];      /* state of the SPI devices */
++static int spi_enabled = 0;
++static struct semaphore spi_lock;                     /* protect access to SPI bus */
++static int current_device = -1;                               /* currently selected SPI device */
++
++DECLARE_COMPLETION(transfer_complete);
++
++/* SPI controller device */
++static AT91PS_SPI controller = (AT91PS_SPI) AT91C_VA_BASE_SPI;
++
++/* ......................................................................... */
++
++/*
++ * Access and enable the SPI bus.
++ * This MUST be called before any transfers are performed.
++ */
++void spi_access_bus(short device)
++{
++      /* Ensure that requested device is valid */
++      if ((device < 0) || (device >= NR_SPI_DEVICES))
++              panic("at91_spi: spi_access_bus called with invalid device");
++
++      if (spi_enabled == 0) {
++              AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Enable Peripheral clock */
++              controller->SPI_CR = AT91C_SPI_SPIEN;   /* Enable SPI */
++#ifdef DEBUG_SPI
++              printk("SPI on\n");
++#endif
++      }
++      MOD_INC_USE_COUNT;
++      spi_enabled++;
++
++      /* Lock the SPI bus */
++      down(&spi_lock);
++      current_device = device;
++
++      /* Enable PIO */
++      if (!spi_dev[device].pio_enabled) {
++              switch (device) {
++                      case 0: AT91_CfgPIO_SPI_CS0();  break;
++                      case 1: AT91_CfgPIO_SPI_CS1();  break;
++                      case 2: AT91_CfgPIO_SPI_CS2();  break;
++                      case 3: AT91_CfgPIO_SPI_CS3();  break;
++              }
++              spi_dev[device].pio_enabled = 1;
++#ifdef DEBUG_SPI
++              printk("SPI CS%i enabled\n", device);
++#endif
++      }
++
++      /* Configure SPI bus for device */
++      controller->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (spi_dev[device].pcs << 16);
++}
++
++/*
++ * Relinquish control of the SPI bus.
++ */
++void spi_release_bus(short device)
++{
++      if (device != current_device)
++              panic("at91_spi: spi_release called with invalid device");
++
++      /* Release the SPI bus */
++      current_device = -1;
++      up(&spi_lock);
++
++      spi_enabled--;
++      MOD_DEC_USE_COUNT;
++      if (spi_enabled == 0) {
++              controller->SPI_CR = AT91C_SPI_SPIDIS;  /* Disable SPI */
++              AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Disable Peripheral clock */
++#ifdef DEBUG_SPI
++              printk("SPI off\n");
++#endif
++      }
++}
++
++/*
++ * Perform a data transfer over the SPI bus
++ */
++int spi_transfer(struct spi_transfer_list* list)
++{
++      struct spi_local *device = (struct spi_local *) &spi_dev[current_device];
++
++      if (!list)
++              panic("at91_spi: spi_transfer called with NULL transfer list");
++      if (current_device == -1)
++              panic("at91_spi: spi_transfer called without acquiring bus");
++
++#ifdef DEBUG_SPI
++      printk("SPI transfer start [%i]\n", list->nr_transfers);
++#endif
++
++      /* Store transfer list */
++      device->xfers = list;
++      list->curr = 0;
++
++      /* Assume there must be at least one transfer */
++      device->tx = pci_map_single(NULL, list->tx[0], list->txlen[0], PCI_DMA_TODEVICE);
++      device->rx = pci_map_single(NULL, list->rx[0], list->rxlen[0], PCI_DMA_FROMDEVICE);
++
++      /* Program PDC registers */
++      controller->SPI_TPR = device->tx;
++      controller->SPI_RPR = device->rx;
++      controller->SPI_TCR = list->txlen[0];
++      controller->SPI_RCR = list->rxlen[0];
++
++      /* Is there a second transfer? */
++      if (list->nr_transfers > 1) {
++              device->txnext = pci_map_single(NULL, list->tx[1], list->txlen[1], PCI_DMA_TODEVICE);
++              device->rxnext = pci_map_single(NULL, list->rx[1], list->rxlen[1], PCI_DMA_FROMDEVICE);
++
++              /* Program Next PDC registers */
++              controller->SPI_TNPR = device->txnext;
++              controller->SPI_RNPR = device->rxnext;
++              controller->SPI_TNCR = list->txlen[1];
++              controller->SPI_RNCR = list->rxlen[1];
++      }
++      else {
++              device->txnext = 0;
++              device->rxnext = 0;
++              controller->SPI_TNCR = 0;
++              controller->SPI_RNCR = 0;
++      }
++
++      // TODO: If we are doing consecutive transfers (at high speed, or
++      //   small buffers), then it might be worth modifying the 'Delay between
++      //   Consecutive Transfers' in the CSR registers.
++      //   This is an issue if we cannot chain the next buffer fast enough
++      //   in the interrupt handler.
++
++      /* Enable transmitter and receiver */
++      controller->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
++
++      controller->SPI_IER = AT91C_SPI_SPENDRX;        /* enable buffer complete interrupt */
++      wait_for_completion(&transfer_complete);
++
++#ifdef DEBUG_SPI
++      printk("SPI transfer end\n");
++#endif
++
++      return 0;
++}
++
++/* ......................................................................... */
++
++/*
++ * Handle interrupts from the SPI controller.
++ */
++static void spi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int status;
++      struct spi_local *device = (struct spi_local *) &spi_dev[current_device];
++      struct spi_transfer_list *list = device->xfers;
++
++#ifdef DEBUG_SPI
++      printk("SPI interrupt %i\n", current_device);
++#endif
++
++      if (!list)
++              panic("at91_spi: spi_interrupt with a NULL transfer list");
++
++              status = controller->SPI_SR & controller->SPI_IMR;      /* read status */
++
++      pci_unmap_single(NULL, device->tx, list->txlen[list->curr], PCI_DMA_TODEVICE);
++      pci_unmap_single(NULL, device->rx, list->rxlen[list->curr], PCI_DMA_FROMDEVICE);
++
++      device->tx = device->txnext;    /* move next transfer to current transfer */
++      device->rx = device->rxnext;
++
++      list->curr = list->curr + 1;
++      if (list->curr == list->nr_transfers) {         /* all transfers complete */
++              controller->SPI_IDR = AT91C_SPI_SPENDRX;        /* disable interrupt */
++
++              /* Disable transmitter and receiver */
++              controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
++
++              device->xfers = NULL;
++              complete(&transfer_complete);
++      }
++      else if (list->curr+1 == list->nr_transfers) {  /* no more next transfers */
++              device->txnext = 0;
++              device->rxnext = 0;
++              controller->SPI_TNCR = 0;
++              controller->SPI_RNCR = 0;
++      }
++      else {
++              int i = (list->curr)+1;
++
++              device->txnext = pci_map_single(NULL, list->tx[i], list->txlen[i], PCI_DMA_TODEVICE);
++              device->rxnext = pci_map_single(NULL, list->rx[i], list->rxlen[i], PCI_DMA_FROMDEVICE);
++              controller->SPI_TNPR = device->txnext;
++              controller->SPI_RNPR = device->rxnext;
++              controller->SPI_TNCR = list->txlen[i];
++              controller->SPI_RNCR = list->rxlen[i];
++      }
++}
++
++/* ......................................................................... */
++
++/*
++ * Initialize the SPI controller
++ */
++static int __init at91_spi_init(void)
++{
++      init_MUTEX(&spi_lock);
++
++      AT91_CfgPIO_SPI();
++
++      controller->SPI_CR = AT91C_SPI_SWRST;   /* software reset of SPI controller */
++
++      /* Set Chip Select registers to good defaults */
++      controller->SPI_CSR0 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);
++      controller->SPI_CSR1 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);
++      controller->SPI_CSR2 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);
++      controller->SPI_CSR3 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);
++
++      controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
++
++      memset(&spi_dev, 0, sizeof(spi_dev));
++      spi_dev[0].pcs = 0xE;
++      spi_dev[1].pcs = 0xD;
++      spi_dev[2].pcs = 0xB;
++      spi_dev[3].pcs = 0x7;
++
++      if (request_irq(AT91C_ID_SPI, spi_interrupt, 0, "spi", NULL))
++              return -EBUSY;
++
++      controller->SPI_CR = AT91C_SPI_SPIEN;           /* Enable SPI */
++
++      return 0;
++}
++
++static void at91_spi_exit(void)
++{
++      controller->SPI_CR = AT91C_SPI_SPIDIS;          /* Disable SPI */
++
++      free_irq(AT91C_ID_SPI, 0);
++}
++
++
++EXPORT_SYMBOL(spi_access_bus);
++EXPORT_SYMBOL(spi_release_bus);
++EXPORT_SYMBOL(spi_transfer);
++
++module_init(at91_spi_init);
++module_exit(at91_spi_exit);
++
++MODULE_LICENSE("GPL")
++MODULE_AUTHOR("Andrew Victor")
++MODULE_DESCRIPTION("SPI driver for Atmel AT91RM9200")
+--- /dev/null
++++ linux-2.4.27/drivers/at91/spi/at91_spi.h
+@@ -0,0 +1,56 @@
++/*
++ * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#ifndef AT91_SPI_H
++#define AT91_SPI_H
++
++/* Maximum number of buffers in a single SPI transfer.
++ *  DataFlash uses maximum of 2
++ *  spidev interface supports up to 8.
++ */
++#define MAX_SPI_TRANSFERS     8
++
++#define NR_SPI_DEVICES        4       /* number of devices on SPI bus */
++
++#define DATAFLASH_CLK         6000000
++#define DEFAULT_SPI_BAUD      AT91C_MASTER_CLOCK / (2 * DATAFLASH_CLK)
++
++#define SPI_MAJOR             153     /* registered device number */
++
++/*
++ * Describes the buffers for a SPI transfer.
++ * A transmit & receive buffer must be specified for each transfer
++ */
++struct spi_transfer_list {
++      void* tx[MAX_SPI_TRANSFERS];    /* transmit */
++      int txlen[MAX_SPI_TRANSFERS];
++      void* rx[MAX_SPI_TRANSFERS];    /* receive */
++      int rxlen[MAX_SPI_TRANSFERS];
++      int nr_transfers;               /* number of transfers */
++      int curr;                       /* current transfer */
++};
++
++struct spi_local {
++      unsigned int pcs;               /* Peripheral Chip Select value */
++      short pio_enabled;              /* has PIO been enabled? */
++
++      struct spi_transfer_list *xfers;        /* current transfer list */
++      dma_addr_t tx, rx;              /* DMA address for current transfer */
++      dma_addr_t txnext, rxnext;      /* DMA address for next transfer */
++};
++
++
++/* Exported functions */
++extern void spi_access_bus(short device);
++extern void spi_release_bus(short device);
++extern int spi_transfer(struct spi_transfer_list* list);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/at91/spi/at91_spidev.c
+@@ -0,0 +1,226 @@
++/*
++ * User-space interface to the SPI bus on Atmel AT91RM9200
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * Based on SPI driver by Rick Bronson
++ *
++ * 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/config.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/iobuf.h>
++#include <linux/highmem.h>
++
++#ifdef CONFIG_DEVFS_FS
++#include <linux/devfs_fs_kernel.h>
++#endif
++
++#include "at91_spi.h"
++
++#undef DEBUG_SPIDEV
++
++#ifdef CONFIG_DEVFS_FS
++static devfs_handle_t devfs_handle = NULL;
++static devfs_handle_t devfs_spi[NR_SPI_DEVICES];
++#endif
++
++/* ......................................................................... */
++
++/*
++ * Read or Write to SPI bus.
++ */
++static ssize_t spidev_rd_wr(struct file *file, char *buf, size_t count, loff_t *offset)
++{
++      unsigned int spi_device = (unsigned int) file->private_data;
++      struct kiobuf *iobuf;
++      unsigned int ofs, pagelen;
++      int res, i;
++
++      struct spi_transfer_list* list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL);
++      if (!list)
++              return -ENOMEM;
++
++      res = alloc_kiovec(1, &iobuf);
++      if (res) {
++              kfree(list);
++              return res;
++      }
++
++      res = map_user_kiobuf(READ, iobuf, (unsigned long) buf, count);
++      if (res) {
++              free_kiovec(1, &iobuf);
++              kfree(list);
++              return res;
++      }
++
++      /* More pages than transfer slots in spi_transfer_list */
++      if (iobuf->nr_pages >= MAX_SPI_TRANSFERS) {
++              unmap_kiobuf(iobuf);
++              free_kiovec(1, &iobuf);
++              kfree(list);
++              return -EFBIG;
++      }
++
++#ifdef DEBUG_SPIDEV
++      printk("spidev_rd_rw: %i %i\n", count, iobuf->nr_pages);
++#endif
++
++      /* Set default return value = transfer length */
++      res = count;
++
++      /*
++       * At this point, the virtual area buf[0] .. buf[count-1] will have
++       * corresponding pages mapped in the physical memory and locked until
++       * we unmap the kiobuf.  The pages cannot be swapped out or moved
++       * around.
++       */
++      ofs = iobuf->offset;
++      pagelen = PAGE_SIZE - iobuf->offset;
++      if (count < pagelen)
++              pagelen = count;
++
++      for (i = 0; i < iobuf->nr_pages; i++) {
++              list->tx[i] = list->rx[i] = page_address(iobuf->maplist[i]) + ofs;
++              list->txlen[i] = list->rxlen[i] = pagelen;
++
++#ifdef DEBUG_SPIDEV
++              printk("  %i: %x  (%i)\n", i, list->tx[i], list->txlen[i]);
++#endif
++
++              ofs = 0;        /* all subsequent transfers start at beginning of a page */
++              count = count - pagelen;
++              pagelen = (count < PAGE_SIZE) ? count : PAGE_SIZE;
++      }
++      list->nr_transfers = iobuf->nr_pages;
++
++      /* Perform transfer on SPI bus */
++      spi_access_bus(spi_device);
++      spi_transfer(list);
++      spi_release_bus(spi_device);
++
++      unmap_kiobuf(iobuf);
++      free_kiovec(1, &iobuf);
++      kfree(list);
++
++      return res;
++}
++
++static int spidev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++      int spi_device = MINOR(inode->i_rdev);
++
++      if (spi_device >= NR_SPI_DEVICES)
++              return -ENODEV;
++
++      // TODO: This interface can be used to configure the SPI bus.
++      // Configurable options could include: Speed, Clock Polarity, Clock Phase
++
++      switch(cmd) {
++              default:
++                      return -ENOIOCTLCMD;
++      }
++}
++
++/*
++ * Open the SPI device
++ */
++static int spidev_open(struct inode *inode, struct file *file)
++{
++      unsigned int spi_device = MINOR(inode->i_rdev);
++
++      if (spi_device >= NR_SPI_DEVICES)
++              return -ENODEV;
++
++      MOD_INC_USE_COUNT;
++
++      /*
++       * 'private_data' is actually a pointer, but we overload it with the
++       * value we want to store.
++       */
++      (unsigned int) file->private_data = spi_device;
++
++      return 0;
++}
++
++/*
++ * Close the SPI device
++ */
++static int spidev_close(struct inode *inode, struct file *file)
++{
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++/* ......................................................................... */
++
++static struct file_operations spidev_fops = {
++      owner:          THIS_MODULE,
++      llseek:         no_llseek,
++      read:           spidev_rd_wr,
++      write:          spidev_rd_wr,
++      ioctl:          spidev_ioctl,
++      open:           spidev_open,
++      release:        spidev_close,
++};
++
++/*
++ * Install the SPI /dev interface driver
++ */
++static int __init at91_spidev_init(void)
++{
++      int i;
++      char name[3];
++
++#ifdef CONFIG_DEVFS_FS
++      if (devfs_register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) {
++#else
++      if (register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) {
++#endif
++              printk(KERN_ERR "at91_spidev: Unable to get major %d for SPI bus\n", SPI_MAJOR);
++              return -EIO;
++      }
++
++#ifdef CONFIG_DEVFS_FS
++      devfs_handle = devfs_mk_dir(NULL, "spi", NULL);
++
++      for (i = 0; i < NR_SPI_DEVICES; i++) {
++              sprintf (name, "%d", i);
++              devfs_spi[i] = devfs_register (devfs_handle, name,
++                      DEVFS_FL_DEFAULT, SPI_MAJOR, i, S_IFCHR | S_IRUSR | S_IWUSR,
++                      &spidev_fops, NULL);
++      }
++#endif
++      printk(KERN_INFO "AT91 SPI driver loaded\n");
++
++      return 0;
++}
++
++/*
++ * Remove the SPI /dev interface driver
++ */
++static void __exit at91_spidev_exit(void)
++{
++#ifdef CONFIG_DEVFS_FS
++      devfs_unregister(devfs_handle);
++      if (devfs_unregister_chrdev(SPI_MAJOR, "spi")) {
++#else
++      if (unregister_chrdev(SPI_MAJOR,"spi")) {
++#endif
++              printk(KERN_ERR "at91_spidev: Unable to release major %d for SPI bus\n", SPI_MAJOR);
++              return;
++      }
++}
++
++module_init(at91_spidev_init);
++module_exit(at91_spidev_exit);
++
++MODULE_LICENSE("GPL")
++MODULE_AUTHOR("Andrew Victor")
++MODULE_DESCRIPTION("SPI /dev interface for Atmel AT91RM9200")
+--- /dev/null
++++ linux-2.4.27/drivers/at91/usb/Makefile
+@@ -0,0 +1,17 @@
++# File: drivers/at91/usb/Makefile
++#
++# Makefile for the Atmel AT91RM9200 USB device drivers
++#
++
++O_TARGET := at91usb.o
++
++export-objs :=
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_USB_OHCI_AT91) += at91_usb-ohci.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/usb/at91_usb-ohci.c
+@@ -0,0 +1,85 @@
++/*
++ *  linux/drivers/at91/usb/at91_usb_ohci-at91.c
++ *
++ *  (c) Rick Bronson
++ *
++ *  The outline of this code was taken from Brad Parkers <brad@heeltoe.com>
++ *  original OHCI driver modifications, and reworked into a cleaner form
++ *  by Russell King <rmk@arm.linux.org.uk>.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++
++#include <asm/arch/AT91RM9200_UHP.h>
++
++/*
++  NOTE:
++  The following is so that we don't have to include usb-ohci.h or pci.h as the
++  usb-ohci.c driver needs these routines even when the architecture
++  has no PCI bus...
++*/
++
++extern int __devinit hc_add_ohci(struct pci_dev *dev, int irq, void *membase,
++          unsigned long flags, void *ohci, const char *name,
++          const char *slot_name);
++extern void hc_remove_ohci(void *ohci);
++
++static void *at91_ohci;
++AT91PS_UHP ohci_regs;
++
++static int __init at91_ohci_init(void)
++{
++      int ret;
++
++      ohci_regs = ioremap(AT91_UHP_BASE, SZ_4K);
++      if (!ohci_regs) {
++              printk(KERN_ERR "at91_usb-ohci: ioremap failed\n");
++              return -EIO;
++      }
++
++      /* Enable PLLB */
++      AT91_SYS->CKGR_PLLBR = AT91_PLLB_INIT;
++      while ((AT91_SYS->PMC_SR & 4) == 0);
++
++      /* Now, enable the USB clock */
++      AT91_SYS->PMC_SCER = AT91C_PMC_UHP;     /* enable system clock */
++      AT91_SYS->PMC_PCER = 1 << AT91C_ID_UHP; /* enable peripheral clock */
++
++      /* Take Hc out of reset */
++      ohci_regs->UHP_HcControl = 2 << 6;
++
++      /* Initialise the generic OHCI driver. */
++      ret = hc_add_ohci((struct pci_dev *) 1, AT91C_ID_UHP,
++                        (void *)ohci_regs, 0, &at91_ohci,
++                        "usb-ohci", "at91");
++      if (ret)
++              iounmap(ohci_regs);
++
++      return ret;
++}
++
++static void __exit at91_ohci_exit(void)
++{
++      hc_remove_ohci(at91_ohci);
++
++      /* Force UHP_Hc to reset */
++      ohci_regs->UHP_HcControl = 0;
++
++       /* Stop the USB clock. */
++      AT91_SYS->PMC_SCDR = AT91C_PMC_UHP;     /* disable system clock */
++      AT91_SYS->PMC_PCDR = 1 << AT91C_ID_UHP; /* disable peripheral clock */
++
++      iounmap(ohci_regs);
++}
++
++module_init(at91_ohci_init);
++module_exit(at91_ohci_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/at91/watchdog/Makefile
+@@ -0,0 +1,15 @@
++# File: drivers/at91/watchdog/Makefile
++#
++# Makefile for the Atmel AT91RM9200 watchdog device driver
++#
++
++O_TARGET := at91wdt.o
++
++obj-y :=
++obj-m :=
++obj-n :=
++obj-  :=
++
++obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/at91/watchdog/at91_wdt.c
+@@ -0,0 +1,193 @@
++/*
++ * Watchdog driver for Atmel AT91RM9200 (Thunder)
++ *
++ * (c) SAN People (Pty) Ltd
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version
++ * 2 of the License, or (at your option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <asm/uaccess.h>
++#include <linux/init.h>
++
++#define WDT_DEFAULT_TIME 5    /* 5 seconds */
++#define WDT_MAX_TIME 256      /* 256 seconds */
++
++static int at91wdt_time = WDT_DEFAULT_TIME;
++static int at91wdt_busy;
++
++/* ......................................................................... */
++
++/*
++ * Disable the watchdog.
++ */
++static void at91_wdt_stop(void)
++{
++      AT91_SYS->ST_WDMR = AT91C_ST_EXTEN;
++}
++
++/*
++ * Enable and reset the watchdog.
++ */
++static void at91_wdt_start(void)
++{
++      AT91_SYS->ST_WDMR = AT91C_ST_EXTEN | AT91C_ST_RSTEN | (((65536 * at91wdt_time) >> 8) & AT91C_ST_WDV);
++      AT91_SYS->ST_CR = AT91C_ST_WDRST;
++}
++
++/* ......................................................................... */
++
++/*
++ * Watchdog device is opened, and watchdog starts running.
++ */
++static int at91_wdt_open(struct inode *inode, struct file *file)
++{
++      if (test_and_set_bit(1, &at91wdt_busy))
++              return -EBUSY;
++      MOD_INC_USE_COUNT;
++
++      /*
++       * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz
++       *
++       * Since WDV is a 16-bit counter, the maximum period is
++       * 65536 / 0.256 = 256 seconds.
++       */
++
++      at91_wdt_start();
++      return 0;
++}
++
++/*
++ * Close the watchdog device.
++ * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
++ *  disabled.
++ */
++static int at91_wdt_close(struct inode *inode, struct file *file)
++{
++#ifndef CONFIG_WATCHDOG_NOWAYOUT
++      /* Disable the watchdog when file is closed */
++      at91_wdt_stop();
++#endif
++
++      at91wdt_busy = 0;
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++/*
++ * Handle commands from user-space.
++ */
++static int at91_wdt_ioctl(struct inode *inode, struct file *file,
++              unsigned int cmd, unsigned long arg)
++{
++      unsigned int new_value;
++      static struct watchdog_info info = {
++              identity: "at91 watchdog",
++              options:  WDIOF_SETTIMEOUT,
++      };
++
++      switch(cmd) {
++              case WDIOC_KEEPALIVE:
++                      AT91_SYS->ST_CR = AT91C_ST_WDRST;       /* Pat the watchdog */
++                      return 0;
++
++              case WDIOC_GETSUPPORT:
++                      return copy_to_user((struct watchdog_info *)arg, &info, sizeof(info));
++
++              case WDIOC_SETTIMEOUT:
++                      if (get_user(new_value, (int *)arg))
++                              return -EFAULT;
++                      if ((new_value <= 0) || (new_value > WDT_MAX_TIME))
++                              return -EINVAL;
++
++                      /* Restart watchdog with new time */
++                      at91wdt_time = new_value;
++                      at91_wdt_start();
++
++                      /* Return current value */
++                      return put_user(at91wdt_time, (int *)arg);
++
++              case WDIOC_GETTIMEOUT:
++                      return put_user(at91wdt_time, (int *)arg);
++
++              case WDIOC_GETSTATUS:
++                      return put_user(0, (int *)arg);
++
++              case WDIOC_SETOPTIONS:
++                      if (get_user(new_value, (int *)arg))
++                              return -EFAULT;
++                      if (new_value & WDIOS_DISABLECARD)
++                              at91_wdt_stop();
++                      if (new_value & WDIOS_ENABLECARD)
++                              at91_wdt_start();
++                      return 0;
++
++              default:
++                      return -ENOIOCTLCMD;
++      }
++}
++
++/*
++ * Pat the watchdog whenever device is written to.
++ */
++static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
++{
++      /*  Can't seek (pwrite) on this device  */
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++
++      if (len) {
++              AT91_SYS->ST_CR = AT91C_ST_WDRST;       /* Pat the watchdog */
++              return len;
++      }
++
++      return 0;
++}
++
++/* ......................................................................... */
++
++static struct file_operations at91wdt_fops =
++{
++      .owner          = THIS_MODULE,
++      .ioctl          = at91_wdt_ioctl,
++      .open           = at91_wdt_open,
++      .release        = at91_wdt_close,
++      .write          = at91_wdt_write,
++};
++
++static struct miscdevice at91wdt_miscdev =
++{
++      .minor          = WATCHDOG_MINOR,
++      .name           = "watchdog",
++      .fops           = &at91wdt_fops,
++};
++
++static int __init at91_wdt_init(void)
++{
++      int res;
++
++      res = misc_register(&at91wdt_miscdev);
++      if (res)
++              return res;
++
++      printk("AT91 Watchdog Timer enabled (%d seconds)\n", WDT_DEFAULT_TIME);
++      return 0;
++}
++
++static void __exit at91_wdt_exit(void)
++{
++      misc_deregister(&at91wdt_miscdev);
++}
++
++module_init(at91_wdt_init);
++module_exit(at91_wdt_exit);
++
++MODULE_LICENSE("GPL")
++MODULE_AUTHOR("Andrew Victor")
++MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200")
+--- linux-2.4.27/drivers/block/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/block/Makefile
+@@ -27,7 +27,7 @@
+ obj-$(CONFIG_BLK_DEV_PS2)     += ps2esdi.o
+ obj-$(CONFIG_BLK_DEV_XD)      += xd.o
+ obj-$(CONFIG_BLK_CPQ_DA)      += cpqarray.o
+-obj-$(CONFIG_BLK_CPQ_CISS_DA)  += cciss.o
++obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
+ obj-$(CONFIG_BLK_DEV_DAC960)  += DAC960.o
+ obj-$(CONFIG_BLK_DEV_UMEM)    += umem.o
+ obj-$(CONFIG_BLK_DEV_NBD)     += nbd.o
+@@ -35,4 +35,10 @@
+ subdir-$(CONFIG_PARIDE) += paride
++ifeq ($(CONFIG_ARCH_ACORN),y)
++mod-subdirs   += ../acorn/block
++subdir-y      += ../acorn/block
++obj-y         += ../acorn/block/acorn-block.o
++endif
++
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.27/drivers/block/ll_rw_blk.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/block/ll_rw_blk.c
+@@ -32,6 +32,19 @@
+ #include <linux/slab.h>
+ #include <linux/module.h>
++/* Maybe something to cleanup in 2.3?
++ * We shouldn't touch 0x3f2 on machines which don't have a PC floppy controller
++ * - it may contain something else which could cause a system hang.  This is
++ * now selected by a configuration option, but maybe it ought to be in the
++ * floppy code itself? - rmk
++ */
++#if defined(__i386__) || (defined(__arm__) && defined(CONFIG_ARCH_ACORN))
++#define FLOPPY_BOOT_DISABLE
++#endif
++#ifdef CONFIG_BLK_DEV_FD
++#undef FLOPPY_BOOT_DISABLE
++#endif
++
+ /*
+  * MAC Floppy IWM hooks
+  */
+@@ -524,7 +537,7 @@
+       elevator_init(&q->elevator, ELEVATOR_LINUS);
+       blk_init_free_list(q);
+       q->request_fn           = rfn;
+-      q->back_merge_fn        = ll_back_merge_fn;
++      q->back_merge_fn        = ll_back_merge_fn;
+       q->front_merge_fn       = ll_front_merge_fn;
+       q->merge_requests_fn    = ll_merge_requests_fn;
+       q->make_request_fn      = __make_request;
+@@ -1549,7 +1562,7 @@
+       mfm_init();
+ #endif
+ #ifdef CONFIG_PARIDE
+-      { extern void paride_init(void); paride_init(); };
++      { extern void paride_init(void); paride_init(); }
+ #endif
+ #ifdef CONFIG_MAC_FLOPPY
+       swim3_init();
+@@ -1563,12 +1576,14 @@
+ #ifdef CONFIG_ATARI_FLOPPY
+       atari_floppy_init();
+ #endif
++#ifdef CONFIG_BLK_DEV_FD1772
++      fd1772_init();
++#endif
+ #ifdef CONFIG_BLK_DEV_FD
+       floppy_init();
+-#else
+-#if defined(__i386__) /* Do we even need this? */
+-      outb_p(0xc, 0x3f2);
+ #endif
++#ifdef FLOPPY_BOOT_DISABLE
++      outb_p(0xc, 0x3f2);
+ #endif
+ #ifdef CONFIG_CDU31A
+       cdu31a_init();
+@@ -1626,7 +1641,7 @@
+       jsfd_init();
+ #endif
+       return 0;
+-};
++}
+ EXPORT_SYMBOL(io_request_lock);
+ EXPORT_SYMBOL(end_that_request_first);
+--- linux-2.4.27/drivers/cdrom/cdrom.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/cdrom/cdrom.c
+@@ -246,8 +246,8 @@
+ #define CD_DVD                0x80
+ /* Define this to remove _all_ the debugging messages */
+-/* #define ERRLOGMASK CD_NOTHING */
+-#define ERRLOGMASK (CD_WARNING)
++#define ERRLOGMASK CD_NOTHING
++/* #define ERRLOGMASK (CD_WARNING) */
+ /* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
+ /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */
+--- linux-2.4.27/drivers/char/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/Config.in
+@@ -20,10 +20,10 @@
+    if [ "$CONFIG_IA64" = "y" ]; then
+       bool '  Support for serial port described by EFI HCDP table' CONFIG_SERIAL_HCDP
+    fi
+-   if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+-      tristate '   Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
+-      tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL
+-   fi
++fi
++if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
++   dep_tristate '   Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_SERIAL
++   dep_tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL $CONFIG_SERIAL
+ fi
+ dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL
+ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
+@@ -132,18 +132,6 @@
+        bool '  SGI SN2 IOC4 serial port support' CONFIG_SGI_IOC4_SERIAL
+       fi
+    fi
+-fi
+-if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then
+-   tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232
+-fi
+-if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+-   bool 'DC21285 serial port support' CONFIG_SERIAL_21285
+-   if [ "$CONFIG_SERIAL_21285" = "y" ]; then
+-      if [ "$CONFIG_OBSOLETE" = "y" ]; then
+-         bool '  Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD
+-      fi
+-      bool '  Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE
+-   fi
+    if [ "$CONFIG_PARISC" = "y" ]; then
+      bool '  PDC software console support' CONFIG_PDC_CONSOLE
+    fi
+@@ -168,6 +156,16 @@
+ if [ "$CONFIG_CPU_VR41XX" = "y" ]; then
+    bool 'NEC VR4100 series Keyboard Interface Unit Support ' CONFIG_VR41XX_KIU
+ fi
++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
++  tristate 'AT91RM9200 SPI device interface' CONFIG_AT91_SPIDEV
++fi
++
++source drivers/serial/Config.in
++
++if [ "$CONFIG_ARCH_ANAKIN" = "y" ]; then
++   tristate 'Anakin touchscreen support' CONFIG_TOUCHSCREEN_ANAKIN
++fi
++
+ bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
+ if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
+    int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
+@@ -190,6 +188,12 @@
+ source drivers/i2c/Config.in
++if [ "$CONFIG_I2C" != "n" ]; then
++    dep_tristate '  DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
++fi
++
++source drivers/l3/Config.in
++
+ mainmenu_option next_comment
+ comment 'Mice'
+ tristate 'Bus Mouse Support' CONFIG_BUSMOUSE
+@@ -245,11 +249,13 @@
+    tristate '  ALi M7101 PMU Watchdog Timer' CONFIG_ALIM7101_WDT
+    tristate '  AMD "Elan" SC520 Watchdog Timer' CONFIG_SC520_WDT
+    tristate '  Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
+-   if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+-      tristate '  DC21285 watchdog' CONFIG_21285_WATCHDOG
+-      if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then
+-         tristate '  NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG
+-      fi
++   if [ "$CONFIG_ARM" = "y" ]; then
++      dep_tristate '  DC21285 watchdog' CONFIG_21285_WATCHDOG $CONFIG_FOOTBRIDGE
++      dep_tristate '  NetWinder WB83C977 watchdog' CONFIG_977_WATCHDOG $CONFIG_ARCH_NETWINDER
++      dep_tristate '  SA1100 watchdog' CONFIG_SA1100_WATCHDOG $CONFIG_ARCH_SA1100
++      dep_tristate '  EPXA watchdog' CONFIG_EPXA_WATCHDOG $CONFIG_ARCH_CAMELOT
++      dep_tristate '  Omaha watchdog' CONFIG_OMAHA_WATCHDOG $CONFIG_ARCH_OMAHA
++      dep_tristate '  AT91RM9200 watchdog' CONFIG_AT91_WATCHDOG $CONFIG_ARCH_AT91RM9200
+    fi
+    tristate '  Eurotech CPU-1220/1410 Watchdog Timer' CONFIG_EUROTECH_WDT
+    tristate '  IB700 SBC Watchdog Timer' CONFIG_IB700_WDT
+@@ -325,6 +331,15 @@
+ if [ "$CONFIG_TOSHIBA_RBTX4927" = "y" -o "$CONFIG_TOSHIBA_JMR3927" = "y" ]; then
+    tristate 'Dallas DS1742 RTC support' CONFIG_DS1742
+ fi
++if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
++   tristate 'SA1100 Real Time Clock' CONFIG_SA1100_RTC
++fi
++if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then
++   tristate 'Omaha Real Time Clock' CONFIG_OMAHA_RTC
++fi
++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
++   tristate 'AT91RM9200 Real Time Clock' CONFIG_AT91_RTC
++fi
+ tristate 'Double Talk PC internal speech card support' CONFIG_DTLK
+ tristate 'Siemens R3964 line discipline' CONFIG_R3964
+--- linux-2.4.27/drivers/char/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/Makefile
+@@ -29,7 +29,7 @@
+ mod-subdirs   :=      joystick ftape drm drm-4.0 pcmcia
+-list-multi    :=      
++list-multi    :=
+ KEYMAP   =defkeymap.o
+ KEYBD    =pc_keyb.o
+@@ -106,11 +106,39 @@
+ endif
+ ifeq ($(ARCH),arm)
+-  ifneq ($(CONFIG_PC_KEYMAP),y)
+-    KEYMAP   =
++  KEYMAP     :=
++  KEYBD      :=
++  ifeq ($(CONFIG_PC_KEYMAP),y)
++    KEYMAP   := defkeymap.o
+   endif
+-  ifneq ($(CONFIG_PC_KEYB),y)
+-    KEYBD    =
++  ifeq ($(CONFIG_PC_KEYB),y)
++    KEYBD    += pc_keyb.o
++  endif
++  ifeq ($(CONFIG_KMI_KEYB),y)
++    KEYBD    += amba_kmi_keyb.o
++  endif
++  ifeq ($(CONFIG_SA1111),y)
++    KEYBD    += sa1111_keyb.o
++  endif
++  ifeq ($(CONFIG_ARCH_EDB7211),y)
++    KEYBD    += edb7211_keyb.o
++  endif
++  ifeq ($(CONFIG_ARCH_AUTCPU12),y)
++    KEYMAP   := defkeymap.o
++    KEYBD    += clps711x_keyb.o
++  endif
++  ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y)
++    KEYMAP    = gckeymap.o
++    KEYBD    += gc_keyb.o
++  endif
++  ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
++    KEYBD    += cerf_keyb.o
++  endif
++  ifeq ($(CONFIG_ARCH_FORTUNET),y)
++    KEYMAP   := defkeymap.o
++  endif
++  ifeq ($(CONFIG_ARCH_GUIDEA07),y)
++    KEYMAP   := defkeymap.o
+   endif
+ endif
+@@ -173,11 +201,9 @@
+ obj-$(CONFIG_SERIAL) += $(SERIAL)
+ obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o
+ obj-$(CONFIG_SERIAL_HCDP) += hcdp_serial.o
+-obj-$(CONFIG_SERIAL_21285) += serial_21285.o
+-obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
+-obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o
+ obj-$(CONFIG_TS_AU1X00_ADS7846) += au1000_ts.o
+ obj-$(CONFIG_SERIAL_DEC) += decserial.o
++obj-$(CONFIG_TOUCHSCREEN_ANAKIN) += anakin_ts.o
+ ifndef CONFIG_SUN_KEYBOARD
+   obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD)
+@@ -254,6 +280,8 @@
+ obj-$(CONFIG_SGI_DS1286) += ds1286.o
+ obj-$(CONFIG_MIPS_RTC) += mips_rtc.o
+ obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
++obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o
++obj-$(CONFIG_OMAHA_RTC) += omaha-rtc.o
+ ifeq ($(CONFIG_PPC),)
+   obj-$(CONFIG_NVRAM) += nvram.o
+ endif
+@@ -292,6 +320,7 @@
+ obj-$(CONFIG_NWFLASH) += nwflash.o
+ obj-$(CONFIG_SCx200) += scx200.o
+ obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
++obj-$(CONFIG_SA1100_CONSUS) += consusbutton.o
+ # Only one watchdog can succeed. We probe the hardware watchdog
+ # drivers first, then the softdog driver.  This means if your hardware
+@@ -320,15 +349,27 @@
+ obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
+ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
+ obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
++obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
++obj-$(CONFIG_EPXA_WATCHDOG) += epxa_wdt.o
++obj-$(CONFIG_OMAHA_WATCHDOG) += omaha_wdt.o
+ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+ obj-$(CONFIG_INDYDOG) += indydog.o
+ obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o
++# I2C char devices
++obj-$(CONFIG_I2C_DS1307) += ds1307.o
++
+ subdir-$(CONFIG_MWAVE) += mwave
+ ifeq ($(CONFIG_MWAVE),y)
+   obj-y += mwave/mwave.o
+ endif
++ifeq ($(CONFIG_ARCH_ACORN),y)
++mod-subdirs   += ../acorn/char
++subdir-y      += ../acorn/char
++obj-y         += ../acorn/char/acorn-char.o
++endif
++
+ subdir-$(CONFIG_IPMI_HANDLER) += ipmi
+ ifeq ($(CONFIG_IPMI_HANDLER),y)
+   obj-y += ipmi/ipmi.o
+--- /dev/null
++++ linux-2.4.27/drivers/char/amba_kmi_keyb.c
+@@ -0,0 +1,999 @@
++/*
++ *  linux/drivers/char/amba_kmi_keyb.c
++ *
++ *  AMBA Keyboard and Mouse Interface Driver
++ *
++ *  Copyright (C) 2000 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
++ * 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 keyboard driver drives a PS/2 keyboard and mouse connected
++ *  to the KMI interfaces.  The KMI interfaces are nothing more than
++ *  a uart; there is no inteligence in them to do keycode translation.
++ *  We leave all that up to the keyboard itself.
++ *
++ *       FIXES:
++ *             dirk.uffmann@nokia.com:  enabled PS/2 reconnection
++ */
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>  /* for in_interrupt */
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/delay.h>      /* for udelay */
++#include <linux/kbd_kern.h>   /* for keyboard_tasklet */
++#include <linux/kbd_ll.h>
++
++#include <asm/io.h>
++#include <asm/hardware/amba_kmi.h>
++#include <asm/mach/amba_kmi.h>
++#include <asm/keyboard.h>
++
++//#define DEBUG(s) printk s
++#define DEBUG(s) do { } while (0)
++
++#define CONFIG_AMBA_PS2_RECONNECT
++
++#define KMI_BASE      (kmi->base)
++
++#define KMI_RESET             0x00
++#define KMI_RESET_POR         0x01
++#define KMI_RESET_DONE                0x02
++
++#define KMI_NO_ACK            0xffff
++
++#define PS2_O_RESET           0xff
++#define PS2_O_RESEND          0xfe
++#define PS2_O_DISABLE         0xf5
++#define PS2_O_ENABLE          0xf4
++#define PS2_O_ECHO            0xee
++
++/*
++ * Keyboard
++ */
++#define PS2_O_SET_DEFAULT     0xf6
++#define PS2_O_SET_RATE_DELAY  0xf3
++#define PS2_O_SET_SCANSET     0xf0
++#define PS2_O_INDICATORS      0xed
++
++/*
++ * Mouse
++ */
++#define PS2_O_SET_SAMPLE      0xf3
++#define PS2_O_SET_STREAM      0xea
++#define PS2_O_SET_RES         0xe8
++#define PS2_O_SET_SCALE21     0xe7
++#define PS2_O_SET_SCALE11     0xe6
++#define PS2_O_REQ_STATUS      0xe9
++
++/*
++ * Responses
++ */
++#define PS2_I_RESEND          0xfe
++#define PS2_I_DIAGFAIL                0xfc
++#define PS2_I_ACK             0xfa
++#define PS2_I_BREAK           0xf0
++#define PS2_I_ECHO            0xee
++#define PS2_I_BAT_OK          0xaa
++
++static char *kmi_type[] = { "Keyboard", "Mouse" };
++
++static struct kmi_info *kmi_keyb;
++static struct kmi_info *kmi_mouse;
++
++static inline void __kmi_send(struct kmi_info *kmi, u_int val)
++{
++      u_int status;
++
++      do {
++              status = __raw_readb(KMISTAT);
++      } while (!(status & KMISTAT_TXEMPTY));
++
++      kmi->resend_count += 1;
++      __raw_writeb(val, KMIDATA);
++}
++
++static void kmi_send(struct kmi_info *kmi, u_int val)
++{
++      kmi->last_tx = val;
++      kmi->resend_count = -1;
++      __kmi_send(kmi, val);
++}
++
++static u_int kmi_send_and_wait(struct kmi_info *kmi, u_int val, u_int timeo)
++{
++      DECLARE_WAITQUEUE(wait, current);
++
++      if (kmi->present == 0)
++              return KMI_NO_ACK;
++
++      kmi->res = KMI_NO_ACK;
++      kmi->last_tx = val;
++      kmi->resend_count = -1;
++
++      if (current->pid != 0 && !in_interrupt()) {
++              add_wait_queue(&kmi->wait_q, &wait);
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              __kmi_send(kmi, val);
++              schedule_timeout(timeo);
++              current->state = TASK_RUNNING;
++              remove_wait_queue(&kmi->wait_q, &wait);
++      } else {
++              int i;
++
++              __kmi_send(kmi, val);
++              for (i = 0; i < 1000; i++) {
++                      if (kmi->res != KMI_NO_ACK)
++                              break;
++                      udelay(100);
++              }
++      }
++
++      return kmi->res;
++}
++
++/*
++ * This lot should probably be separated into a separate file...
++ */
++#ifdef CONFIG_KMI_MOUSE
++
++#include <linux/fs.h>         /* for struct file_ops */
++#include <linux/poll.h>       /* for poll_table */
++#include <linux/miscdevice.h> /* for struct miscdev */
++#include <linux/random.h>     /* for add_mouse_randomness */
++#include <linux/slab.h>       /* for kmalloc */
++#include <linux/smp_lock.h>   /* for {un,}lock_kernel */
++#include <linux/spinlock.h>
++
++#include <asm/uaccess.h>
++
++#define BUF_SZ        2048
++
++static spinlock_t kmi_mouse_lock;
++static int kmi_mouse_count;
++static struct queue {
++      u_int                   head;
++      u_int                   tail;
++      struct fasync_struct    *fasync;
++      unsigned char           buf[BUF_SZ];
++} *queue;
++
++#define queue_empty() (queue->head == queue->tail)
++
++static u_char get_from_queue(void)
++{
++      unsigned long flags;
++      u_char res;
++
++      spin_lock_irqsave(&kmi_mouse_lock, flags);
++      res = queue->buf[queue->tail];
++      queue->tail = (queue->tail + 1) & (BUF_SZ-1);
++      spin_unlock_irqrestore(&kmi_mouse_lock, flags);
++
++      return res;
++}
++
++static ssize_t
++kmi_mouse_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      ssize_t i = count;
++
++      if (queue_empty()) {
++              int ret;
++
++              if (file->f_flags & O_NONBLOCK)
++                      return -EAGAIN;
++              ret = wait_event_interruptible(kmi_mouse->wait_q, !queue_empty());
++              if (ret)
++                      return ret;
++      }
++      while (i > 0 && !queue_empty()) {
++              u_char c;
++              c = get_from_queue();
++              put_user(c, buf++);
++              i--;
++      }
++      if (count - i)
++              file->f_dentry->d_inode->i_atime = CURRENT_TIME;
++      return count - i;
++}
++
++static ssize_t
++kmi_mouse_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
++{
++      ssize_t retval = 0;
++
++      if (count > 32)
++              count = 32;
++
++      do {
++              char c;
++              get_user(c, buf++);
++              kmi_send_and_wait(kmi_mouse, c, HZ);
++              retval++;
++      } while (--count);
++
++      if (retval)
++              file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
++
++      return retval;
++}
++
++static unsigned int
++kmi_mouse_poll(struct file *file, poll_table *wait)
++{
++      poll_wait(file, &kmi_mouse->wait_q, wait);
++      return (!queue_empty()) ? POLLIN | POLLRDNORM : 0;
++}
++
++static int
++kmi_mouse_release(struct inode *inode, struct file *file)
++{
++      lock_kernel();
++      fasync_helper(-1, file, 0, &queue->fasync);
++      if (--kmi_mouse_count == 0)
++              kmi_send_and_wait(kmi_mouse, PS2_O_DISABLE, HZ);
++      unlock_kernel();
++      return 0;
++}
++
++static int
++kmi_mouse_open(struct inode *inode, struct file *file)
++{
++      if (kmi_mouse_count++)
++              return 0;
++      queue->head = queue->tail = 0;
++      kmi_send_and_wait(kmi_mouse, PS2_O_ENABLE, HZ);
++      return 0;
++}
++
++static int
++kmi_mouse_fasync(int fd, struct file *filp, int on)
++{
++      int retval = fasync_helper(fd, filp, on, &queue->fasync);
++      if (retval > 0)
++              retval = 0;
++      return retval;
++}
++
++static struct file_operations ps_fops = {
++      read:           kmi_mouse_read,
++      write:          kmi_mouse_write,
++      poll:           kmi_mouse_poll,
++      open:           kmi_mouse_open,
++      release:        kmi_mouse_release,
++      fasync:         kmi_mouse_fasync,
++};
++
++static struct miscdevice ps_mouse = {
++      minor:          PSMOUSE_MINOR,
++      name:           "psaux",
++      fops:           &ps_fops,
++};
++
++static u_char kmi_mse_init_string[] = {
++      PS2_O_DISABLE,
++      PS2_O_SET_SAMPLE, 100,
++      PS2_O_SET_RES, 3,
++      PS2_O_SET_SCALE21
++};
++
++/*
++ * The "normal" mouse scancode processing
++ */
++static void kmi_mse_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs)
++{
++      u_int head;
++
++      add_mouse_randomness(val);
++
++#ifdef CONFIG_AMBA_PS2_RECONNECT
++      /* Try to detect a hot-plug event on the PS/2 mouse port */
++      switch (kmi->hotplug_state) {
++      case 0:
++              /* Maybe we lost contact... */
++              if (val == PS2_I_BAT_OK) {
++                      kmi->hotplug_state++;
++                      DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state));
++              }
++              break;
++
++      case 1:
++              /* Again, maybe (but only maybe) we lost contact... */
++              if (val == 0) {
++                      kmi->hotplug_state++;
++                      kmi_send(kmi, PS2_O_REQ_STATUS);
++                      DEBUG(("%s: Got 0xAA 0x00. Sent Status Request\n", kmi->name));
++              } else {
++                      kmi->hotplug_state = 0;
++                      DEBUG(("%s: No 0x00 followed 0xAA. No reconnect.\n", kmi->name));
++              }
++              break;
++
++      case 2:
++              /* Eat up acknowledge */
++              if (val == PS2_I_ACK)
++                      kmi->hotplug_state++;
++              else {
++                      kmi->hotplug_state = 0;
++                      DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val));
++              }
++              break;
++
++      case 3:
++              /* check if data reporting is still enabled, then no POR has happend */
++              kmi->reconnect = !(val & 1<<5);
++              DEBUG(("%s: Data reporting disabled?: (%d)\n", kmi->name, kmi->reconnect));
++              kmi->hotplug_state++;
++              DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state));
++              break;
++
++      case 4:
++              /* Eat up one status byte */
++              kmi->hotplug_state++;
++              DEBUG(("%s: Going to hotplug state %d\n", kmi->name, kmi->hotplug_state));
++              break;
++
++      case 5:
++              /* Eat up another status byte */
++              if (kmi->reconnect) {
++                      kmi->config_num = 0;
++                      kmi_send(kmi, kmi_mse_init_string[kmi->config_num]);
++                      kmi->config_num++;
++                      kmi->hotplug_state++;
++                      DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num));
++              } else {
++                      kmi->hotplug_state = 0;
++                      DEBUG(("%s: False Alarm...\n", kmi->name));
++              }
++              break;
++
++      case 6:
++              if (val == PS2_I_ACK && kmi->config_num < sizeof(kmi_mse_init_string)) {
++                      kmi_send(kmi, kmi_mse_init_string[kmi->config_num]);
++                      kmi->config_num++;
++                      DEBUG(("%s: Sending byte %d of PS/2 init string.\n", kmi->name, kmi->config_num));
++              } else {
++                      if (val == PS2_I_ACK) {
++                              DEBUG(("%s: Now enable the mouse again...\n", kmi->name));
++                              queue->head = queue->tail = 0;
++                              kmi_send(kmi, PS2_O_ENABLE);
++                              kmi->hotplug_state++;
++                      } else {
++                              kmi->hotplug_state = 0;
++                              DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val));
++                      }
++              }
++              break;
++
++      case 7:
++              /* Eat up last acknowledge from enable */
++              if (val == PS2_I_ACK)
++                      printk(KERN_ERR "%s: reconnected\n", kmi->name);
++              else
++                      DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val));
++
++              kmi->hotplug_state = 0;
++              break;
++
++      } /* switch (kmi->hotplug_state) */
++
++      /* while inside hotplug mechanism, don't misinterpret values */
++      if (kmi->hotplug_state > 2)
++              return;
++#endif
++
++      /* We are waiting for the mouse to respond to a kmi_send_and_wait() */
++      if (kmi->res == KMI_NO_ACK) {
++              if (val == PS2_I_RESEND) {
++                      if (kmi->resend_count < 5)
++                              __kmi_send(kmi, kmi->last_tx);
++                      else {
++                              printk(KERN_ERR "%s: too many resends\n", kmi->name);
++                              return;
++                      }
++              }
++
++              if (val == PS2_I_ACK) {
++                      kmi->res = val;
++                      wake_up(&kmi->wait_q);
++              }
++              return;
++      }
++
++      /* The mouse autonomously send new data, so wake up mouse_read() */
++      if (queue) {
++              head = queue->head;
++              queue->buf[head] = val;
++              head = (head + 1) & (BUF_SZ - 1);
++              if (head != queue->tail) {
++                      queue->head = head;
++                      kill_fasync(&queue->fasync, SIGIO, POLL_IN);
++                      wake_up_interruptible(&kmi->wait_q);
++              }
++      }
++}
++
++static int kmi_init_mouse(struct kmi_info *kmi)
++{
++      u_int ret, i;
++
++      if (kmi->present) {
++              kmi->rx = kmi_mse_intr;
++
++              for (i = 0; i < sizeof(kmi_mse_init_string); i++) {
++                      ret = kmi_send_and_wait(kmi, kmi_mse_init_string[i], HZ);
++                      if (ret != PS2_I_ACK)
++                              printk("%s: didn't get ack (0x%2.2x)\n",
++                                      kmi->name, ret);
++              }
++      }
++
++      queue = kmalloc(sizeof(*queue), GFP_KERNEL);
++      if (queue) {
++              memset(queue, 0, sizeof(*queue));
++              misc_register(&ps_mouse);
++              ret = 0;
++      } else
++              ret = -ENOMEM;
++
++      return ret;
++}
++#endif /* CONFIG_KMI_MOUSE */
++
++/*
++ * The "program" we send to the keyboard to set it up how we want it:
++ *  - default typematic delays
++ *  - scancode set 1
++ */
++static u_char kmi_kbd_init_string[] = {
++      PS2_O_DISABLE,
++      PS2_O_SET_DEFAULT,
++      PS2_O_SET_SCANSET, 0x01,
++      PS2_O_ENABLE
++};
++
++static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs);
++
++static int __kmi_init_keyboard(struct kmi_info *kmi)
++{
++      u_int ret, i;
++
++      if (!kmi->present)
++              return 0;
++
++      kmi->rx = kmi_kbd_intr;
++
++      for (i = 0; i < sizeof(kmi_kbd_init_string); i++) {
++              ret = kmi_send_and_wait(kmi, kmi_kbd_init_string[i], HZ);
++              if (ret != PS2_I_ACK)
++                      printk("%s: didn't ack (0x%2.2x)\n",
++                              kmi->name, ret);
++      }
++
++      return 0;
++}
++
++static void kmi_kbd_init_tasklet(unsigned long k)
++{
++      struct kmi_info *kmi = (struct kmi_info *)k;
++      __kmi_init_keyboard(kmi);
++}
++
++static DECLARE_TASKLET_DISABLED(kmikbd_init_tasklet, kmi_kbd_init_tasklet, 0);
++
++/*
++ * The "normal" keyboard scancode processing
++ */
++static void kmi_kbd_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs)
++{
++#ifdef CONFIG_AMBA_PS2_RECONNECT
++      /* Try to detect a hot-plug event on the PS/2 keyboard port */
++      switch (kmi->hotplug_state) {
++      case 0:
++              /* Maybe we lost contact... */
++              if (val == PS2_I_BAT_OK) {
++                      kmi_send(kmi, PS2_O_SET_SCANSET);
++                      kmi->hotplug_state++;
++                      DEBUG(("%s: Saw 0xAA. Going to hotplug state %d\n", kmi->name, kmi->hotplug_state));
++              }
++              break;
++
++      case 1:
++              /* Eat up acknowledge */
++              if (val == PS2_I_ACK) {
++                      /* Request scan code set: '2' if POR has happend, '1' is false alarm */
++                      kmi_send(kmi, 0);
++                      kmi->hotplug_state++;
++              }
++              else {
++                      kmi->hotplug_state = 0;
++                      DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val));
++              }
++              break;
++
++      case 2:
++              /* Eat up acknowledge */
++              if (val == PS2_I_ACK)
++                      kmi->hotplug_state++;
++              else {
++                      kmi->hotplug_state = 0;
++                      DEBUG(("%s: didn't get ack (0x%2.2x)\n", kmi->name, val));
++              }
++              break;
++
++      case 3:
++              kmi->hotplug_state = 0;
++              if (val == 2) {
++                      DEBUG(("%s: POR detected. Scan code is: (%d)\n", kmi->name, val));
++                      kmi->present = 1;
++                      tasklet_schedule(&kmikbd_init_tasklet);
++                      printk(KERN_ERR "%s: reconnected\n", kmi->name);
++                      return;
++              }
++              else
++                      DEBUG(("%s: False Alarm...\n", kmi->name));
++              break;
++
++      } /* switch (kmi->hotplug_state) */
++#endif
++
++      if (val == PS2_I_DIAGFAIL) {
++              printk(KERN_ERR "%s: diagnostic failed\n", kmi->name);
++              return;
++      }
++
++      /* We are waiting for the keyboard to respond to a kmi_send_and_wait() */
++      if (kmi->res == KMI_NO_ACK) {
++              if (val == PS2_I_RESEND) {
++                      if (kmi->resend_count < 5)
++                              __kmi_send(kmi, kmi->last_tx);
++                      else {
++                              printk(KERN_ERR "%s: too many resends\n", kmi->name);
++                              return;
++                      }
++              }
++
++              if (val >= 0xee) {
++                      kmi->res = val;
++                      wake_up(&kmi->wait_q);
++              }
++              return;
++      }
++
++#ifdef CONFIG_VT
++      kbd_pt_regs = regs;
++      handle_scancode(val, !(val & 0x80));
++      tasklet_schedule(&keyboard_tasklet);
++#endif
++}
++
++static void kmi_intr(int nr, void *devid, struct pt_regs *regs)
++{
++      struct kmi_info *kmi = devid;
++      u_int status = __raw_readb(KMIIR);
++
++      if (status & KMIIR_RXINTR) {
++              u_int val = __raw_readb(KMIDATA);
++
++              if (kmi->rx)
++                      kmi->rx(kmi, val, regs);
++      }
++}
++
++static int kmi_init_keyboard(struct kmi_info *kmi)
++{
++      kmikbd_init_tasklet.data = (unsigned long)kmi;
++      tasklet_enable(&kmikbd_init_tasklet);
++
++      return __kmi_init_keyboard(kmi);
++}
++
++/*
++ * Reset interrupt handler
++ */
++static void __init
++kmi_reset_intr(struct kmi_info *kmi, u_int val, struct pt_regs *regs)
++{
++      if (kmi->state == KMI_RESET) {
++              if (val == PS2_I_ACK)
++                      kmi->state = KMI_RESET_POR;
++              else {
++                      val = KMI_NO_ACK;
++                      goto finished;
++              }
++      } else if (kmi->state == KMI_RESET_POR) {
++finished:
++              kmi->res = val;
++              kmi->state = KMI_RESET_DONE;
++              kmi->rx = NULL;
++              wake_up(&kmi->wait_q);
++      }
++}
++
++/*
++ * Reset the device plugged into this interface
++ */
++static int __init kmi_reset(struct kmi_info *kmi)
++{
++      u_int res;
++      int ret = 0;
++
++      kmi->state = KMI_RESET;
++      kmi->rx = kmi_reset_intr;
++      res = kmi_send_and_wait(kmi, PS2_O_RESET, HZ);
++      kmi->rx = NULL;
++
++      if (res != PS2_I_BAT_OK) {
++              printk(KERN_ERR "%s: reset failed; ", kmi->name);
++              if (kmi->res != KMI_NO_ACK)
++                      printk("code 0x%2.2x\n", kmi->res);
++              else
++                      printk("no ack\n");
++              ret = -EINVAL;
++      }
++      return ret;
++}
++
++static int __init kmi_init_one_interface(struct kmi_info *kmi)
++{
++      u_int stat;
++      int ret = -ENODEV;
++
++      init_waitqueue_head(&kmi->wait_q);
++
++      printk(KERN_INFO "%s at 0x%8.8x on irq %d (%s)\n", kmi->name,
++              kmi->base, kmi->irq, kmi_type[kmi->type]);
++
++      /*
++       * Initialise the KMI interface
++       */
++      __raw_writeb(kmi->divisor, KMICLKDIV);
++      __raw_writeb(KMICR_EN, KMICR);
++
++      /*
++       * Check that the data and clock lines are OK.
++       */
++      stat = __raw_readb(KMISTAT);
++      if ((stat & (KMISTAT_IC|KMISTAT_ID)) != (KMISTAT_IC|KMISTAT_ID)) {
++              printk(KERN_ERR "%s: %s%s%sline%s stuck low\n", kmi->name,
++                      (stat & KMISTAT_IC) ? "" : "clock ",
++                      (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "and ",
++                      (stat & KMISTAT_ID) ? "" : "data ",
++                      (stat & (KMISTAT_IC | KMISTAT_ID)) ? "" : "s");
++              goto bad;
++      }
++
++      /*
++       * Claim the appropriate interrupts
++       */
++      ret = request_irq(kmi->irq, kmi_intr, 0, kmi->name, kmi);
++      if (ret)
++              goto bad;
++
++      /*
++       * Enable the receive interrupt, and reset the device.
++       */
++      __raw_writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
++      kmi->present = 1;
++      kmi->present = kmi_reset(kmi) == 0;
++
++      switch (kmi->type) {
++      case KMI_KEYBOARD:
++              ret = kmi_init_keyboard(kmi);
++              break;
++
++#ifdef CONFIG_KMI_MOUSE
++      case KMI_MOUSE:
++              ret = kmi_init_mouse(kmi);
++              break;
++#endif
++      }
++
++      return ret;
++
++bad:
++      /*
++       * Oh dear, the interface was bad, disable it.
++       */
++      __raw_writeb(0, KMICR);
++      return ret;
++}
++
++#ifdef CONFIG_VT
++/*
++ * The fragment between #ifdef above and #endif * CONFIG_VT *
++ * is from the pc_keyb.c driver.  It is not copyrighted under the
++ * above notice.  This code is by various authors; please see
++ * drivers/char/pc_keyb.c for further information.
++ */
++
++/*
++ * 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
++
++/* BTC */
++#define E0_MACRO   112
++/* LK450 */
++#define E0_F13           113
++#define E0_F14           114
++#define E0_HELP    115
++#define E0_DO    116
++#define E0_F17           117
++#define E0_KPMINPLUS 118
++/*
++ * My OmniKey generates e0 4c for  the "OMNI" key and the
++ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
++ */
++#define E0_OK 124
++/*
++ * 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
++#define E0_MSRW 126
++#define E0_MSTM 127
++
++static u_char e0_keys[128] = {
++      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,
++      E0_KPENTER,     E0_RCTRL,       0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              E0_KPSLASH,     0,              E0_PRSCR,
++      E0_RALT,        0,              0,              0,
++      0,              E0_F13,         E0_F14,         E0_HELP,
++      E0_DO,          E0_F17,         0,              0,
++      0,              0,              E0_BREAK,       E0_HOME,
++      E0_UP,          E0_PGUP,        0,              E0_LEFT,
++      E0_OK,          E0_RIGHT,       E0_KPMINPLUS,   E0_END,
++      E0_DOWN,        E0_PGDN,        E0_INS,         E0_DEL,
++      0,              0,              0,              0,
++      0,              0,              0,              E0_MSLW,
++      E0_MSRW,        E0_MSTM,        0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              E0_MACRO,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0,
++      0,              0,              0,              0
++};
++
++#ifdef CONFIG_MAGIC_SYSRQ
++u_char kmi_kbd_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 */
++#endif
++
++int kmi_kbd_setkeycode(u_int scancode, u_int keycode)
++{
++      if (scancode < 128 || scancode > 255 || keycode > 127)
++              return -EINVAL;
++      e0_keys[scancode - 128] = keycode;
++      return 0;
++}
++
++int kmi_kbd_getkeycode(u_int scancode)
++{
++      if (scancode < 128 || scancode > 255)
++              return -EINVAL;
++      return e0_keys[scancode - 128];
++}
++              
++int kmi_kbd_translate(u_char scancode, u_char *keycode, char raw_mode)
++{
++      static int prev_scancode = 0;
++
++      /* special prefix scancodes.. */
++      if (scancode == 0xe0 || scancode == 0xe1) {
++              prev_scancode = scancode;
++              return 0;
++      }
++
++      /* 0xff is sent by a few keyboards, ignore it.  0x00 is error */
++      if (scancode == 0x00 || scancode == 0xff) {
++              prev_scancode = 0;
++              return 0;
++      }
++
++      scancode &= 0x7f;
++
++      if (prev_scancode) {
++              int old_scancode = prev_scancode;
++
++              prev_scancode = 0;
++              switch (old_scancode) {
++              case 0xe0:
++                      /*
++                       * The keyboard maintains its own internal caps lock
++                       * and num lock status.  In caps lock mode, E0 AA
++                       * precedes make code and E0 2A follows break code.
++                       * In numlock mode, E0 2A precedes make code, and
++                       * E0 AA follows break code.  We do our own book-
++                       * keeping, so we will just ignore these.
++                       *
++                       * For my keyboard there is no caps lock mode, but
++                       * there are both Shift-L and Shift-R modes. The
++                       * former mode generates E0 2A / E0 AA pairs, the
++                       * latter E0 B6 / E0 36 pairs.  So, we should also
++                       * ignore the latter. - aeb@cwi.nl
++                       */
++                      if  (scancode == 0x2a || scancode == 0x36)
++                              return 0;
++                      if (e0_keys[scancode])
++                              *keycode = e0_keys[scancode];
++                      else {
++                              if (!raw_mode)
++                                      printk(KERN_INFO "kbd: unknown "
++                                              "scancode e0 %02x\n",
++                                              scancode);
++                              return 0;
++                      }
++                      break;
++
++              case 0xe1:
++                      if (scancode == 0x1d)
++                              prev_scancode = 0x100;
++                      else {
++                              if (!raw_mode)
++                                      printk(KERN_INFO "kbd: unknown "
++                                              "scancode e1 %02x\n",
++                                              scancode);
++                              return 0;
++                      }
++                      break;
++
++              case 0x100:
++                      if (scancode == 0x45)
++                              *keycode = E1_PAUSE;
++                      else {
++                              if (!raw_mode)
++                                      printk(KERN_INFO "kbd: unknown "
++                                              "scan code e1 1d %02x\n",
++                                              scancode);
++                              return 0;
++                      }
++                      break;
++              }
++      } else
++              *keycode = scancode;
++      return 1;
++}
++
++char kmi_kbd_unexpected_up(u_char keycode)
++{
++      return 0x80;
++}
++
++void kmi_kbd_leds(u_char leds)
++{
++      struct kmi_info *kmi = kmi_keyb;
++      u_int ret;
++
++      if (kmi) {
++              ret = kmi_send_and_wait(kmi, PS2_O_INDICATORS, HZ);
++              if (ret != KMI_NO_ACK)
++                      ret = kmi_send_and_wait(kmi, leds, HZ);
++              if (ret == KMI_NO_ACK)
++                      kmi->present = 0;
++      }
++}
++
++int __init kmi_kbd_init(void)
++{
++      int  ret = -ENODEV;
++
++      if (kmi_keyb) {
++              strcpy(kmi_keyb->name, "kmikbd");
++              ret = kmi_init_one_interface(kmi_keyb);
++      }
++
++      if (ret == 0) {
++              k_setkeycode    = kmi_kbd_setkeycode;
++              k_getkeycode    = kmi_kbd_getkeycode;
++              k_translate     = kmi_kbd_translate;
++              k_unexpected_up = kmi_kbd_unexpected_up;
++              k_leds          = kmi_kbd_leds;
++#ifdef CONFIG_MAGIC_SYSRQ
++              k_sysrq_xlate   = kmi_kbd_sysrq_xlate;
++              k_sysrq_key     = 0x54;
++#endif
++      }
++
++      return ret;
++}
++
++#endif /* CONFIG_VT */
++
++int register_kmi(struct kmi_info *kmi)
++{
++      struct kmi_info **kmip = NULL;
++      int ret;
++
++      if (kmi->type == KMI_KEYBOARD)
++              kmip = &kmi_keyb;
++      else if (kmi->type == KMI_MOUSE)
++              kmip = &kmi_mouse;
++
++      ret = -EINVAL;
++      if (kmip) {
++              ret = -EBUSY;
++              if (!*kmip) {
++                      *kmip = kmi;
++                      ret = 0;
++              }
++      }
++
++      return ret;
++}
++
++#ifdef CONFIG_KMI_MOUSE
++static int __init kmi_init(void)
++{
++      int  ret = -ENODEV;
++
++      if (kmi_mouse) {
++              strcpy(kmi_mouse->name, "kmimouse");
++              ret = kmi_init_one_interface(kmi_mouse);
++      }
++
++      return ret;
++}
++
++__initcall(kmi_init);
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/char/anakin_ts.c
+@@ -0,0 +1,208 @@
++/*
++ *  linux/drivers/char/anakin_ts.c
++ *
++ *  Copyright (C) 2001 Aleph One Ltd. for Acunia N.V.
++ *
++ * 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.
++ *
++ *  Changelog:
++ *   18-Apr-2001 TTC  Created
++ *   23-Oct-2001 dwmw2        Cleanup
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/fs.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/compiler.h>
++#include <linux/interrupt.h>
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++
++/*
++ * TSBUF_SIZE must be a power of two
++ */
++#define ANAKIN_TS_MINOR       16
++#define TSBUF_SIZE    256
++#define NEXT(index)   (((index) + 1) & (TSBUF_SIZE - 1))
++
++static unsigned short buffer[TSBUF_SIZE][4];
++static int head, tail;
++static DECLARE_WAIT_QUEUE_HEAD(queue);
++static DECLARE_MUTEX(open_sem);
++static spinlock_t tailptr_lock = SPIN_LOCK_UNLOCKED;
++static struct fasync_struct *fasync;
++
++/*
++ * Interrupt handler and standard file operations
++ */
++static void
++anakin_ts_handler(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int status = __raw_readl(IO_BASE + IO_CONTROLLER + 0x24);
++
++      /*
++       * iPAQ format (u16 pressure, x, y, millisecs)
++       */
++      switch (status >> 20 & 3) {
++      case 0:
++              return;
++      case 2:
++              buffer[head][0] = 0;
++              break;
++      default:
++              buffer[head][0] = 0x7f;
++      }
++
++      if (unlikely((volatile int)tail == NEXT(head))) {
++              /* Run out of space in the buffer. Move the tail pointer */
++              spin_lock(&tailptr_lock);
++
++              if ((volatile int)tail == NEXT(head)) {
++                      tail = NEXT(NEXT(head));
++              }
++              spin_unlock(&tailptr_lock);
++      }
++
++      buffer[head][1] = status >> 2 & 0xff;
++      buffer[head][2] = status >> 12 & 0xff;
++      buffer[head][3] = jiffies;
++      mb();
++      head = NEXT(head);
++
++      wake_up_interruptible(&queue);
++      kill_fasync(&fasync, SIGIO, POLL_IN);
++
++}
++
++static ssize_t
++anakin_ts_read(struct file *filp, char *buf, size_t count, loff_t *l)
++{
++      unsigned short data[4];
++      ssize_t written = 0;
++
++      if (head == tail) {
++              if (filp->f_flags & O_NONBLOCK)
++                      return -EAGAIN;
++              if (wait_event_interruptible(queue, (volatile int)head != (volatile int)tail))
++                      return -ERESTARTSYS;
++      }
++
++      while ((volatile int)head != (volatile int)tail && count >= sizeof data) {
++              /* Copy the data out with the spinlock held, so the 
++                 interrupt can't fill the buffer and move the tail 
++                 pointer while we're doing it */
++              spin_lock_irq(&tailptr_lock);
++
++              memcpy(data, buffer[tail], sizeof data);
++              tail = NEXT(tail);
++
++              spin_unlock_irq(&tailptr_lock);
++
++              if (copy_to_user(buf, data, sizeof data))
++                      return -EFAULT;
++              count -= sizeof data;
++              buf += sizeof data;
++              written += sizeof data;
++      }
++      return written ? written : -EINVAL;
++}
++
++static unsigned int
++anakin_ts_poll(struct file *filp, poll_table *wait)
++{
++      poll_wait(filp, &queue, wait);
++      return head != tail ? POLLIN | POLLRDNORM : 0;
++}
++
++static int
++anakin_ts_ioctl(struct inode *inode, struct file *filp,
++              unsigned int cmd, unsigned long arg)
++{
++      /*
++       * Future ioctl goes here
++       */
++      return 0;
++}
++
++static int
++anakin_ts_open(struct inode *inode, struct file *filp)
++{
++      if (down_trylock(&open_sem))
++              return -EBUSY;
++      return 0;
++}
++
++static int
++anakin_ts_fasync(int fd, struct file *filp, int on)
++{
++      return fasync_helper(fd, filp, on, &fasync);
++}
++
++static int
++anakin_ts_release(struct inode *inode, struct file *filp)
++{
++      anakin_ts_fasync(-1, filp, 0);
++      up(&open_sem);
++      return 0;
++}
++
++static struct file_operations anakin_ts_fops = {
++      owner:          THIS_MODULE,
++      read:           anakin_ts_read,
++      poll:           anakin_ts_poll,
++      ioctl:          anakin_ts_ioctl,
++      open:           anakin_ts_open,
++      release:        anakin_ts_release,
++      fasync:         anakin_ts_fasync,
++};
++
++static struct miscdevice anakin_ts_miscdev = {
++        ANAKIN_TS_MINOR,
++        "anakin_ts",
++        &anakin_ts_fops
++};
++
++/*
++ * Initialization and exit routines
++ */
++int __init
++anakin_ts_init(void)
++{
++      int retval;
++
++      if ((retval = request_irq(IRQ_TOUCHSCREEN, anakin_ts_handler,
++                      SA_INTERRUPT, "anakin_ts", 0))) {
++              printk(KERN_WARNING "anakin_ts: failed to get IRQ\n");
++              return retval;
++      }
++      __raw_writel(1, IO_BASE + IO_CONTROLLER + 8);
++      misc_register(&anakin_ts_miscdev);
++
++      printk(KERN_NOTICE "Anakin touchscreen driver initialised\n");
++
++      return 0;
++}
++
++void __exit
++anakin_ts_exit(void)
++{
++      __raw_writel(0, IO_BASE + IO_CONTROLLER + 8);
++      free_irq(IRQ_TOUCHSCREEN, 0);
++      misc_deregister(&anakin_ts_miscdev);
++}
++
++module_init(anakin_ts_init);
++module_exit(anakin_ts_exit);
++
++MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>");
++MODULE_DESCRIPTION("Anakin touchscreen driver");
++MODULE_SUPPORTED_DEVICE("touchscreen/anakin");
+--- /dev/null
++++ linux-2.4.27/drivers/char/cerf_keyb.c
+@@ -0,0 +1,380 @@
++/*
++   cerf_keyb.c: This is the end. Daniel is writing a device driver!!!
++*/
++#include <linux/config.h>
++
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/mm.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/kbd_kern.h>
++#include <linux/smp_lock.h>
++#include <linux/timer.h>
++
++#include <asm/keyboard.h>
++#include <asm/bitops.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++
++#include <asm/io.h>
++
++#define KBD_REPORT_UNKN
++
++#define KBD_REPORT_ERR                        /* Report keyboard errors */
++#define KBD_REPORT_UNKN                 /* Report unknown scan codes */
++#define KBD_REPORT_TIMEOUTS           /* Report keyboard timeouts */
++#define KBD_NO_DATA         (-1)        /* No data */
++#define KBD_REPEAT_START    (0x20)
++#define KBD_REPEAT_CONTINUE (0x05)
++#define KBD_KEY_DOWN_MAX    (0x10)
++#define UINT_LEN            (20)
++#define SC_LIM              (69)
++#define KBD_ROWS            (5)
++#define KBD_COLUMNS         (8)
++
++#define KBD_KEYUP           (0x80)
++#define KBD_MODESCAN        (0x7f)
++#define KBD_CAPSSCAN        (0x3a)
++#define KBD_SHIFTSCAN       (0x2a)
++#define KBD_NUMCURSCAN      (0x7c)
++#define KBD_CTRLSCAN        (0x1d)
++#define KBD_ALTSCAN         (0x38)
++
++#define KBD_UP_OFF          (0)
++#define KBD_UP_ON           (1)
++#define KBD_DOWN            (2)
++#define KBD_DOWN_HOLD       (3)
++
++
++
++static unsigned char handle_kbd_event(void);
++static unsigned char kbd_read_input(void);
++static void column_set(unsigned int column);
++static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]);
++
++static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
++static struct timer_list kbd_timer;
++
++static short mode_ena = 0;
++static short numcur_ena = 0;
++static short shift_ena = 0;
++
++#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
++#define E0_MACRO   112
++#define E0_F13     113
++#define E0_F14     114
++#define E0_HELP    115
++#define E0_DO      116
++#define E0_F17     117
++#define E0_KPMINPLUS 118
++#define E0_OK      124
++#define E0_MSLW    125
++#define E0_MSRW    126
++#define E0_MSTM    127
++
++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, E0_KPENTER, 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, E0_KPSLASH, 0, E0_PRSCR,             /* 0x30-0x37 */
++  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,       /* 0x38-0x3f */
++  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,       /* 0x40-0x47 */
++  E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
++  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,       /* 0x50-0x57 */
++  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,           /* 0x58-0x5f */
++  0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x60-0x67 */
++  0, 0, 0, 0, 0, 0, 0, E0_MACRO,                      /* 0x68-0x6f */
++  0, 0, 0, 0, 0, 0, 0, 0,                             /* 0x70-0x77 */
++  0, 0, 0, 0, 0, 0, 0, 0                              /* 0x78-0x7f */
++};
++
++static unsigned char cerf_normal_map[KBD_ROWS][KBD_COLUMNS] = {
++   {KBD_ALTSCAN, KBD_MODESCAN, 0x1e, 0x30, 0x2e, 0x20, 0x00, 0x00},
++   {0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x00},
++   {0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x00},
++   {0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x00},
++   {0x2c, KBD_SHIFTSCAN, KBD_CTRLSCAN, 0x39, KBD_NUMCURSCAN, 0x2b, 0x1c, 0x00}
++};
++
++static unsigned char cerf_mode_map[KBD_ROWS][KBD_COLUMNS] = {
++   {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00},
++   {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, //
++   {0x0d, 0x0c, 0x37, 0x35, 0x0d, 0x48, 0x28, 0x00},
++   {0x01, 0x33, 0x34, 0x00, 0x4b, 0x27, 0x4d, 0x00}, //
++   {0x0f, 0x00, KBD_CAPSSCAN, 0x0e, 0x00, 0x50, 0x00, 0x00}
++};
++
++static unsigned char cerf_numcur_map[KBD_ROWS][KBD_COLUMNS] = {
++   {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00},
++   {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00},
++   {0x0d, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00},
++   {0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x4d, 0x00},
++   {0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00}
++};
++
++static void column_set(unsigned int column)
++{
++   if (column < 0)
++   {
++      CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF);
++      CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF);
++   }
++   else
++   {
++      if(column < 4)
++      {
++         CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_A, 1 << (column % 4), 0xFF);
++         CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF);
++      }
++      else
++      {
++         CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF);
++         CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_B, 1 << (column % 4), 0xFF);
++      }
++   }
++}
++
++static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS])
++{
++   int i, j;
++
++   for(i = 0; i < KBD_COLUMNS; i++)
++   {
++      column_set(i);
++      udelay(50);
++      for(j = 0; j < KBD_ROWS; j++)
++      {
++         if(mode_ena)
++            codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_mode_map[j][i]?cerf_mode_map[j][i]:cerf_normal_map[j][i]):0;
++         else if(numcur_ena)
++            codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_numcur_map[j][i]?cerf_numcur_map[j][i]:cerf_normal_map[j][i]):0;
++         else
++            codeval[j][i] = (GPLR & (1 << (20 + j)))?cerf_normal_map[j][i]:0;
++      }
++   }
++   column_set(-1);
++
++   return 0;
++}
++
++static unsigned char kbd_read_input(void)
++{
++   int i, j, k, l;
++   unsigned char prev;
++   static unsigned char count = 0;
++
++   static unsigned char oldcodes[KBD_ROWS][KBD_COLUMNS]={{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}};
++   unsigned char inputcode[KBD_ROWS][KBD_COLUMNS];
++
++   memset(inputcode, 0, sizeof(unsigned char) * (KBD_ROWS * KBD_COLUMNS));
++   scancodes(inputcode);
++
++   for(i = 0; i < KBD_COLUMNS; i++)
++   {
++      for(j = 0; j < KBD_ROWS; j++)
++      {
++//         if(oldcodes[j][i] == 0xe0)
++//            oldcodes[j][i] =
++         if(oldcodes[j][i] != inputcode[j][i])
++         {
++            // Value of the key before entering this function
++            prev = oldcodes[j][i];
++
++            // KEYUP
++            if(inputcode[j][i] == 0 && oldcodes[j][i] != 0 && !(oldcodes[j][i] & KBD_KEYUP))
++            {
++               oldcodes[j][i] |= KBD_KEYUP;
++
++               if(mode_ena == KBD_UP_ON)
++                  mode_ena = KBD_UP_OFF;
++               if(prev == KBD_MODESCAN)
++                  if(mode_ena == KBD_DOWN_HOLD)
++                     mode_ena = KBD_UP_OFF;
++                  else if(mode_ena == KBD_DOWN)
++                     mode_ena = KBD_UP_ON;
++               if(mode_ena == KBD_DOWN)
++                  mode_ena = KBD_DOWN_HOLD;
++            }
++            // RESET KEYUP
++            else if(oldcodes[j][i] & KBD_KEYUP)
++               oldcodes[j][i] = 0;
++            // KEY DOWN
++            else
++            {
++               oldcodes[j][i] = inputcode[j][i];
++
++               // Parse out mode modifiers before the keyboard interpreter can touch them
++             if(inputcode[j][i] == KBD_MODESCAN)
++               {
++                  if(!mode_ena)
++                     mode_ena = KBD_DOWN;
++                  continue;
++               }
++               if(inputcode[j][i] == KBD_NUMCURSCAN)
++               {
++                  numcur_ena = numcur_ena?0:1;
++                  continue;
++               }
++            }
++            //printk("Modified: (%#x,%#x), ipv:%#x, To: (%#.2x), From: (%#.2x), Flags:%d,%d,%d\r\n", j, i, inputcode[j][i], oldcodes[j][i], prev, mode_ena, shift_ena, numcur_ena);
++            return oldcodes[j][i];
++         }
++      }
++   }
++
++   return (unsigned char)(KBD_NO_DATA);
++}
++
++int cerf_kbd_translate(unsigned char scancode, unsigned char *keycode,
++                  char raw_mode)
++{
++      static int prev_scancode;
++
++      if (scancode == 0xe0 || scancode == 0xe1) {
++              prev_scancode = scancode;
++              return 0;
++      }
++
++      if (scancode == 0x00 || scancode == 0xff) {
++              prev_scancode = 0;
++              return 0;
++      }
++
++      scancode &= 0x7f;
++
++      if (prev_scancode) {
++        if (prev_scancode != 0xe0) {
++            if (prev_scancode == 0xe1 && scancode == 0x1d) {
++                prev_scancode = 0x100;
++                return 0;
++            } else if (prev_scancode == 0x100 && scancode == 0x45) {
++                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 (scancode == 0x2a || scancode == 0x36)
++              return 0;
++            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;
++}
++
++static inline void handle_keyboard_event(unsigned char scancode)
++{
++   if(scancode != (unsigned char)(KBD_NO_DATA))
++   {
++#ifdef CONFIG_VT
++      handle_scancode(scancode, !(scancode & KBD_KEYUP));
++#endif
++      tasklet_schedule(&keyboard_tasklet);
++   }
++}
++
++static unsigned char handle_kbd_event(void)
++{
++   unsigned char scancode;
++
++   scancode = kbd_read_input();
++   handle_keyboard_event(scancode);
++
++   return 0;
++}
++
++/* Handle the automatic interrupts handled by the timer */
++static void keyboard_interrupt(unsigned long foo)
++{
++   spin_lock_irq(&kbd_controller_lock);
++   handle_kbd_event();
++   spin_unlock_irq(&kbd_controller_lock);
++
++   kbd_timer.expires = 8 + jiffies;
++   kbd_timer.data = 0x00000000;
++   kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt;
++
++   add_timer(&kbd_timer);
++}
++
++void cerf_leds(unsigned char leds)
++{
++}
++char cerf_unexpected_up(unsigned char keycode)
++{
++return 0;
++}
++int cerf_getkeycode(unsigned int scancode)
++{
++return 0;
++}
++int cerf_setkeycode(unsigned int scancode, unsigned int keycode)
++{
++return 0;
++}
++
++void cerf_kbd_init_hw(void)
++{
++   printk("Starting Cerf PDA Keyboard Driver... ");
++
++   k_setkeycode       = cerf_setkeycode;
++   k_getkeycode    = cerf_getkeycode;
++   k_translate     = cerf_kbd_translate;
++   k_unexpected_up = cerf_unexpected_up;
++   k_leds          = cerf_leds;
++
++   GPDR &= ~(GPIO_GPIO(20) | GPIO_GPIO(21) | GPIO_GPIO(22) | GPIO_GPIO(23) | GPIO_GPIO(24));
++   kbd_timer.expires = 40 + jiffies;
++   kbd_timer.data = 0x00000000;
++   kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt;
++
++   add_timer(&kbd_timer);
++
++   printk("Done\r\n");
++}
+--- /dev/null
++++ linux-2.4.27/drivers/char/clps711x_keyb.c
+@@ -0,0 +1,547 @@
++/*
++ * drivers/char/clps711x_keyb.c
++ *
++ * Copyright (C) 2001 Thomas Gleixner <gleixner@autronix.de>
++ *
++ * based on drivers/edb7211_keyb.c, which is copyright (C) 2000 Bluemug Inc.
++ * 
++ * Keyboard driver for ARM Linux on EP7xxx and CS89712 processors
++ *
++ * 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. See the file COPYING 
++ * in the main directory of this archive for more details.
++ *
++ *
++ * Hardware: 
++ *
++ * matrix scan keyboards based on EP7209,7211,7212,7312 and CS89712 
++ * on chip keyboard scanner.          
++ * Adaption for different machines is done in init function.
++ *
++ * Basic Function:
++ *
++ * Basicly the driver is interrupt driven. It sets all column drivers 
++ * high. If any key is pressed, a interrupt occures. Now a seperate scan of
++ * each column is done. This scan is timer based, because we use a keyboard 
++ * interface with decoupling capacitors (neccecary if you want to survive 
++ * EMC compliance tests). Always one line is set high. When next timer event 
++ * occures the scan data on port A are valid. This makes also sure, that no
++ * spurious keys are scanned. The kbd int on these CPU's is not deglitched!
++ * After scanning all columns, we switch back to int mode, if no key is
++ * pressed. If any is pressed we reschedule the scan within a programmable
++ * delay. If we would switch back to interrupt mode as long as a key is pressed,
++ * we come right back to the interrupt, because the int. is level triggered !
++ * The timer based scan of the seperate columns can also be done in one
++ * timer event (set fastscan to 1).
++ *
++ * Summary:
++ * The design of this keyboard controller chip is stupid at all !
++ *
++ * Matrix translation:
++ * The matrix translation table is based on standard XT scancodes. Maybe
++ * you have to adjust the KEYISPRINTABLE macro if you set other codes.
++ *
++ * HandyKey:
++ *
++ * On small matrix keyboards you don't have enough keys for operation.
++ * The intention was to implement a operation mode as it's used on handys.
++ * You can rotate trough four scancode levels and produce e.g. with a 4x3
++ * matrix 4*3*4 = 48 different keycodes. That's basicly enough for editing
++ * filenames or things like that. The HandyKey function takes care about 
++ * nonprintable keys like cursors, backspace, del ...
++ * If a key is pressed and is a printable keycode, the code is put to the
++ * main keyboard handler and a cursor left is applied. If you press the same
++ * key again, the current character is deleted and the next level character
++ * is applied. (e.g. 1, a, b, c, 1 ....). If you press a different key, the
++ * driver applies cursor right, before processing the new key.
++ * The autocomplete feature moves the cursor right, if you do not press a
++ * key within a programmable time.
++ * If HandyKey is off, the keyboard behaviour is that of a standard keyboard
++ * HandyKey can be en/disabled from userspace with the proc/keyboard entry
++ * 
++ * proc/keyboard:
++ * 
++ * Read access gives back the actual state of the HandyKey function   
++ *    h:0     Disabled
++ *    h:1     Enabled
++ * Write access has two functions. Changing the HandyKey mode and applying
++ * a different scancode translation table.
++ * Syntax is:         h:0     disable Handykey
++ *            h:1     enabled Handykey
++ *            t:array[256] of bytes   Transfer translation table      
++ */
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/ptrace.h>
++#include <linux/signal.h>
++#include <linux/timer.h>
++#include <linux/tqueue.h>
++#include <linux/random.h>
++#include <linux/ctype.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/kbd_kern.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++
++#include <asm/bitops.h>
++#include <asm/keyboard.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/uaccess.h>
++
++#include <asm/io.h>
++#include <asm/system.h>
++
++void clps711x_kbd_init_hw(void);
++
++/*
++ * Values for the keyboard column scan control register.
++ */
++#define KBSC_HI           0x0     /*   All driven high */
++#define KBSC_LO           0x1     /*   All driven low */
++#define KBSC_X            0x2     /*   All high impedance */
++#define KBSC_COL0   0x8           /*   Column 0 high, others high impedance */
++#define KBSC_COL1   0x9           /*   Column 1 high, others high impedance */
++#define KBSC_COL2   0xa           /*   Column 2 high, others high impedance */
++#define KBSC_COL3   0xb           /*   Column 3 high, others high impedance */
++#define KBSC_COL4   0xc           /*   Column 4 high, others high impedance */
++#define KBSC_COL5   0xd           /*   Column 5 high, others high impedance */
++#define KBSC_COL6   0xe           /*   Column 6 high, others high impedance */
++#define KBSC_COL7   0xf           /*   Column 7 high, others high impedance */
++
++/*
++* Keycodes for cursor left/right and delete (used by HandyKey)        
++*/
++#define KEYCODE_CLEFT 0x4b
++#define KEYCODE_CRIGHT  0x4d
++#define KEYCODE_DEL   0x53
++#define KEYISPRINTABLE(code) ( (code > 0x01 && code < 0x37 && code != 0x1c \
++                               && code != 0x0e) || code == 0x39) 
++
++/* Simple translation table for the SysRq keys */
++#ifdef CONFIG_MAGIC_SYSRQ
++unsigned char clps711x_kbd_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 */
++#endif
++
++/* 
++ * This table maps row/column keyboard matrix positions to XT scancodes.
++ * It's a default table, which can be overriden by writing to proc/keyboard 
++ */
++#ifdef CONFIG_ARCH_AUTCPU12
++static unsigned char autcpu12_scancode[256] = 
++{
++/*  Column: 
++  Row       0     1     2     3     4     5     6     7   */
++/* A0 */  0x08, 0x09, 0x0a, 0x0e, 0x05, 0x06, 0x00, 0x00,
++/* A1 */  0x07, 0x53, 0x02, 0x03, 0x04, 0x0f, 0x00, 0x00,
++/* A2 */  0x0c, 0x0b, 0x33, 0x1c, 0xff, 0x4b, 0x00, 0x00,
++/* A3 */  0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00,
++/* A4 */  0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++
++/* A0 */  0x1e, 0x20, 0x22, 0x0e, 0x24, 0x32, 0x00, 0x00,
++/* A1 */  0x19, 0x53, 0x1f, 0x2f, 0x15, 0x0f, 0x00, 0x00,
++/* A2 */  0x0c, 0x39, 0x34, 0x1c, 0xff, 0x4b, 0x00, 0x00,
++/* A3 */  0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00,
++/* A4 */  0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++
++/* A0 */  0x30, 0x12, 0x23, 0x0e, 0x25, 0x31, 0x00, 0x00,
++/* A1 */  0x10, 0x53, 0x14, 0x11, 0x2c, 0x0f, 0x00, 0x00,
++/* A2 */  0x0c, 0x0b, 0x27, 0x1c, 0xff, 0x4b, 0x00, 0x00,
++/* A3 */  0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00,
++/* A4 */  0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++
++/* A0 */  0x2e, 0x21, 0x17, 0x0e, 0x26, 0x18, 0x00, 0x00,
++/* A1 */  0x13, 0x53, 0x16, 0x2D, 0x04, 0x0f, 0x00, 0x00,
++/* A2 */  0x0c, 0x39, 0x35, 0x1c, 0xff, 0x4b, 0x00, 0x00,
++/* A3 */  0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00,
++/* A4 */  0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* A7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++};
++#endif
++
++static int keys[8];
++static int new_keys[8];
++static int previous_keys[8];
++
++static int fastscan;
++static int scan_interval;
++static int scan_delay;
++static int last_column; 
++static int key_is_pressed;
++
++static unsigned char *act_scancode;
++
++static struct kbd_handy_key {
++      int             ena;
++      int             code;
++      int             shift;
++      int             autocomplete;
++      unsigned long   expires;
++      unsigned long   delay;
++      unsigned char   left;
++      unsigned char   right;
++      unsigned char   del;
++} khandy;
++
++static struct tq_struct kbd_process_task;
++static struct timer_list clps711x_kbd_timer;
++static struct timer_list clps711x_kbdhandy_timer;
++static struct proc_dir_entry *clps711x_keyboard_proc_entry = NULL;
++
++/*
++ * Translate a raw keycode to an XT keyboard scancode.
++ */
++static int clps711x_translate(unsigned char scancode, unsigned char *keycode,
++                               char raw_mode)
++{
++      *keycode = act_scancode[scancode];
++      return 1;
++}
++
++/*
++* Initialize handykey structure
++* clear code, clear shift
++* scan scancode for cursor right/left and delete
++*/
++static void clps711x_handykey_init(void) {
++
++      int     i;
++
++      khandy.ena = 0;
++      khandy.code = 0;
++      khandy.shift = 0;
++      khandy.autocomplete = 0;
++      for(i = 0; i < 64; i++) {
++              switch(act_scancode[i]) {
++                      case KEYCODE_CLEFT:     khandy.left = i; break; 
++                      case KEYCODE_CRIGHT:    khandy.right = i; break;        
++                      case KEYCODE_DEL:       khandy.del = i; break;
++              }
++      }       
++} 
++
++/*
++* Check for handy key and process it  
++*/
++void inline clps711x_checkhandy(int col, int row) {
++
++      int scode, down;
++      unsigned char kcode;
++      
++      scode = (row<<3) + col;
++      down  = keys[col]>>row & 0x01;
++      kcode = act_scancode[scode];
++      
++      if (!khandy.ena) {
++              if (khandy.code) {                      
++                      handle_scancode(khandy.right,1);
++                      handle_scancode(khandy.right,0);
++              }
++              khandy.code = 0;
++              khandy.shift = 0;
++              khandy.autocomplete = 0;
++      }
++
++      if(!kcode)
++              return;
++
++      if (!down || !khandy.ena) {
++              if (khandy.ena && KEYISPRINTABLE(act_scancode[scode]))
++                      khandy.autocomplete = 1;
++              else
++                      handle_scancode(scode + khandy.shift, down);
++              return; 
++      }
++      
++      khandy.autocomplete = 0;
++      if (KEYISPRINTABLE(kcode)) {
++              if (khandy.code) {      
++                      if(khandy.code == (scode|0x100)) {
++                              handle_scancode(khandy.del,1);
++                              handle_scancode(khandy.del,0);
++                              khandy.shift = khandy.shift < 3*64 ? khandy.shift + 64 : 0 ;
++                      } else {
++                              handle_scancode(khandy.right,1);
++                              handle_scancode(khandy.right,0);
++                              khandy.shift = 0;
++                      }                       
++              }
++              handle_scancode(scode + khandy.shift, 1);
++              handle_scancode(scode + khandy.shift, 0);
++              khandy.code = scode | 0x100;
++              handle_scancode(khandy.left,1);
++              handle_scancode(khandy.left,0);
++      } else {
++              if (khandy.code) {
++                      khandy.code = 0;
++                      handle_scancode(khandy.right,1);
++                      handle_scancode(khandy.right,0);
++              }
++              khandy.shift = 0;
++              handle_scancode(scode, down);
++      }
++}
++
++
++/*
++ * Process the new key data 
++ */
++static void clps711x_kbd_process(void* data)
++{
++      int col,row,res;
++
++      for (col = 0; col < 8; col++) {
++              if (( res = previous_keys[col] ^ keys[col]) == 0)               
++                      continue;
++              for(row = 0; row < 8; row++) {
++                      if ( ((res >> row) & 0x01) != 0) 
++                              clps711x_checkhandy(col,row);   
++              }                               
++      }
++      /* Update the state variables. */
++      memcpy(previous_keys, keys, 8 * sizeof(int));
++
++      /* reschedule, if autocomplete pending */
++      if (khandy.autocomplete) {
++              khandy.expires = jiffies + khandy.delay;
++              mod_timer(&clps711x_kbdhandy_timer,khandy.expires);
++      }
++
++}
++
++static char clps711x_unexpected_up(unsigned char scancode)
++{
++      return 0200;
++}
++
++/*
++* Handle timer event, for autocomplete function
++* Reschedule keyboard process task
++*/
++static void clps711x_kbdhandy_timeout(unsigned long data) 
++{
++      if(khandy.autocomplete) {
++              khandy.code = 0;
++              khandy.shift = 0;
++              khandy.autocomplete = 0;
++              handle_scancode(khandy.right,1);
++              handle_scancode(khandy.right,0);
++      }
++}
++
++/*
++* Handle timer event, while in pollmode
++*/
++static void clps711x_kbd_timeout(unsigned long data)
++{
++      int i;
++      unsigned long flags;
++      /* 
++      * read bits of actual column or all columns in fastscan-mode
++      */
++      for (i = 0; i < 8; i++) {
++              new_keys[last_column - KBSC_COL0] = clps_readb(PADR) & 0xff;
++              key_is_pressed |= new_keys[last_column - KBSC_COL0];
++              last_column = last_column < KBSC_COL7 ? last_column + 1 : KBSC_COL0;
++              local_irq_save(flags);
++              clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) 
++                              | last_column, SYSCON1);
++              local_irq_restore(flags);
++              /*
++              * For fastscan, apply a short delay to settle scanlines
++              * else break and wait for next timeout
++              */
++              if (fastscan)
++                      udelay(5);      
++              else
++                      break;
++      }
++
++      if (key_is_pressed)
++              khandy.autocomplete = 0;
++
++      /*
++      * switch to interupt mode, if all columns scanned and no key pressed
++      * else reschedule scan
++      */
++      if (last_column == KBSC_COL0) {
++              if (!key_is_pressed) {
++                      local_irq_save(flags);
++                      clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK)
++                                       | KBSC_HI, SYSCON1);
++                      local_irq_restore(flags);
++                      clps_writel(0,KBDEOI);  
++                      enable_irq(IRQ_KBDINT);
++              } else {
++                      clps711x_kbd_timer.expires = jiffies + scan_interval;
++                      add_timer(&clps711x_kbd_timer);
++              }               
++              key_is_pressed = 0;
++              memcpy(keys, new_keys, 8 * sizeof(int));
++              for (i = 0; i < 8; i++) {
++                      if (previous_keys[i] != keys[i]) {
++                              queue_task(&kbd_process_task, &tq_timer);
++                              return;
++                      }
++              }
++      } else {
++              clps711x_kbd_timer.expires = jiffies + scan_delay;
++              add_timer(&clps711x_kbd_timer);
++      }
++}
++
++/*
++* Keyboard interrupt, change to scheduling mode
++*/
++static void clps711x_kbd_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++
++#ifdef CONFIG_VT
++      kbd_pt_regs = regs;
++#endif
++      disable_irq(IRQ_KBDINT);
++      khandy.autocomplete = 0;
++      clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK)
++               | KBSC_COL0, SYSCON1);
++      clps711x_kbd_timer.expires = jiffies + scan_delay;
++      add_timer(&clps711x_kbd_timer);
++}
++
++
++static int clps711x_kbd_proc_keyboard_read(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++      if (count < 2) 
++              return -EINVAL;
++
++      return sprintf(page,"h:%d\n",khandy.ena);
++}
++
++static int clps711x_kbd_proc_keyboard_write(struct file *file, const char *buffer, 
++              unsigned long count, void *data)
++{
++      unsigned char buf[260];
++
++      if (count < 3|| count > 258)
++              return -EINVAL;
++      if (copy_from_user(buf, buffer, count)) 
++              return -EFAULT;
++      if (buf[1] != ':')
++              return -EINVAL;
++                      
++      if (buf[0] == 'h') {
++              switch (buf[2]) {
++                      case '0':
++                      case '1':
++                      case '2': khandy.ena = buf[2]-'0'; return count;        
++              }
++      }       
++              
++      if (buf[0] == 't' && count == 258) {
++              memcpy(act_scancode,buf+2,256);
++              /* rescan cursor left/right and del */ 
++              clps711x_handykey_init();
++              return count;
++      }
++      
++      return -EINVAL;
++}
++
++
++/*
++ * Initialize the keyboard hardware. 
++ * Set all columns high
++ * Install interrupt handler
++ *
++ * Machine dependent parameters:
++ *
++ * fastscan:          0 = timer based scan for each column
++ *                    1 = full scan is done in one timer event
++ * scan_delay:                time between column scans 
++ *                    setup even if you use fastscan (leeds to timer mode)
++ * scan_interval:     time between full scans
++ * handy.delay:               timeout before last entry get's automatically valid
++ * 
++ */
++void __init clps711x_kbd_init_hw(void)
++{
++
++      /*
++      * put here  machine dependent init stuff 
++      */
++      if (machine_is_autcpu12()) {
++              fastscan = 0;
++              scan_interval = 50*HZ/1000;
++              scan_delay = 20*HZ/1000;
++              khandy.delay = 750*HZ/1000;
++              act_scancode = autcpu12_scancode;
++      } else {
++              printk("No initialization, keyboard killed\n");
++              return;
++      }
++
++      last_column = KBSC_COL0;
++      key_is_pressed = 0;
++
++      clps711x_handykey_init();
++
++      /* Register the /proc entry */
++      clps711x_keyboard_proc_entry = create_proc_entry("keyboard", 0444,
++              &proc_root);
++      if (clps711x_keyboard_proc_entry == NULL)
++              printk("Couldn't create the /proc entry for the keyboard\n");
++      else {
++              clps711x_keyboard_proc_entry->read_proc = 
++                      &clps711x_kbd_proc_keyboard_read;
++              clps711x_keyboard_proc_entry->write_proc = 
++                      &clps711x_kbd_proc_keyboard_write;
++      }
++
++      /* Initialize the matrix processing task. */
++      k_translate     = clps711x_translate;
++      k_unexpected_up = clps711x_unexpected_up;
++      kbd_process_task.routine = clps711x_kbd_process;
++      kbd_process_task.data = 0;
++      
++      /* Setup the timer for keyboard polling, after kbd int */
++      init_timer(&clps711x_kbd_timer);
++      clps711x_kbd_timer.function = clps711x_kbd_timeout;
++      clps711x_kbd_timer.data = 0;
++      init_timer(&clps711x_kbdhandy_timer);
++      clps711x_kbdhandy_timer.function = clps711x_kbdhandy_timeout;
++      clps711x_kbdhandy_timer.data = 1;
++
++      /* Initialise scan hardware, request int */
++      clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK)
++               | KBSC_HI, SYSCON1);
++      request_irq(IRQ_KBDINT, clps711x_kbd_int, 0,"keyboard", NULL);
++
++      printk("clps711x keyboard init done\n");
++
++}
+--- linux-2.4.27/drivers/char/console.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/console.c
+@@ -72,8 +72,14 @@
+  *
+  * Removed console_lock, enabled interrupts across all console operations
+  * 13 March 2001, Andrew Morton
++ *
++ * Split out con_write_ctrl_* functions from do_con_write & changed
++ * vc_state to function pointer
++ * by Russell King <rmk@arm.linux.org.uk>, July 1998
+  */
++#define CONSOLE_WIP
++
+ #include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/tty.h>
+@@ -228,7 +234,7 @@
+ static inline unsigned short *screenpos(int currcons, int offset, int viewed)
+ {
+       unsigned short *p;
+-      
++
+       if (!viewed)
+               p = (unsigned short *)(origin + offset);
+       else if (!sw->con_screen_pos)
+@@ -729,7 +735,7 @@
+               else {
+                       unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER);
+                       if (!p) {
+-                              for (i = first; i < currcons; i++)
++                              for (i = first; i< currcons; i++)
+                                       if (newscreens[i])
+                                               kfree(newscreens[i]);
+                               return -ENOMEM;
+@@ -847,7 +853,7 @@
+ /* the default colour table, for VGA+ colour systems */
+ int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
+     0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
+-int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
++int default_grn[] = {0x00,0x00,0xaa,0xaa,0x00,0x00,0xaa,0xaa,
+     0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
+ int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
+     0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
+@@ -1393,6 +1399,19 @@
+       need_wrap = 0;
+ }
++static int con_write_ctrl_ESnormal(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESesc(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESnonstd(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESpalette(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESsquare(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESgetpars(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESgotpars(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESpercent(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESfunckey(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_EShash(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESsetG0(int, struct tty_struct *, unsigned int);
++static int con_write_ctrl_ESsetG1(int, struct tty_struct *, unsigned int);
++
+ enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
+       EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
+       ESpalette };
+@@ -1402,7 +1421,7 @@
+ {
+       top             = 0;
+       bottom          = video_num_lines;
+-      vc_state        = ESnormal;
++      vc_state        = con_write_ctrl_ESnormal;
+       ques            = 0;
+       translate       = set_translate(LAT1_MAP,currcons);
+       G0_charset      = LAT1_MAP;
+@@ -1500,328 +1519,426 @@
+               disp_ctrl = 0;
+               return;
+       case 24: case 26:
+-              vc_state = ESnormal;
++              vc_state = con_write_ctrl_ESnormal;
+               return;
+       case 27:
+-              vc_state = ESesc;
++              vc_state = con_write_ctrl_ESesc;
+               return;
+       case 127:
+               del(currcons);
+               return;
+       case 128+27:
+-              vc_state = ESsquare;
++              vc_state = con_write_ctrl_ESsquare;
+               return;
+       }
+-      switch(vc_state) {
+-      case ESesc:
+-              vc_state = ESnormal;
+-              switch (c) {
+-              case '[':
+-                      vc_state = ESsquare;
+-                      return;
+-              case ']':
+-                      vc_state = ESnonstd;
+-                      return;
+-              case '%':
+-                      vc_state = ESpercent;
+-                      return;
+-              case 'E':
+-                      cr(currcons);
+-                      lf(currcons);
+-                      return;
+-              case 'M':
+-                      ri(currcons);
+-                      return;
+-              case 'D':
+-                      lf(currcons);
+-                      return;
+-              case 'H':
+-                      tab_stop[x >> 5] |= (1 << (x & 31));
+-                      return;
+-              case 'Z':
+-                      respond_ID(tty);
+-                      return;
+-              case '7':
+-                      save_cur(currcons);
+-                      return;
+-              case '8':
+-                      restore_cur(currcons);
+-                      return;
+-              case '(':
+-                      vc_state = ESsetG0;
+-                      return;
+-              case ')':
+-                      vc_state = ESsetG1;
+-                      return;
+-              case '#':
+-                      vc_state = EShash;
+-                      return;
+-              case 'c':
+-                      reset_terminal(currcons,1);
+-                      return;
+-              case '>':  /* Numeric keypad */
+-                      clr_kbd(kbdapplic);
+-                      return;
+-              case '=':  /* Appl. keypad */
+-                      set_kbd(kbdapplic);
+-                      return;
++      vc_state(currcons, tty, c);
++}
++
++static int con_write_utf(int currcons, int c)
++{
++      unsigned int chr;
++
++      /* Combine UTF-8 into Unicode */
++      /* Incomplete characters silently ignored */
++      if (c < 0x80) {
++              utf_count = 0;
++              return c;
++      }
++
++      if (utf_count > 0 && (c & 0xc0) == 0x80) {
++              chr = (utf_char << 6) | (c & 0x3f);
++              utf_count--;
++              if (utf_count == 0)
++                      return chr;
++      } else {
++              unsigned int count;
++              if ((c & 0xe0) == 0xc0) {
++                      count = 1;
++                      chr = (c & 0x1f);
++              } else if ((c & 0xf0) == 0xe0) {
++                      count = 2;
++                      chr = (c & 0x0f);
++              } else if ((c & 0xf8) == 0xf0) {
++                      count = 3;
++                      chr = (c & 0x07);
++              } else if ((c & 0xfc) == 0xf8) {
++                      count = 4;
++                      chr = (c & 0x03);
++              } else if ((c & 0xfe) == 0xfc) {
++                      count = 5;
++                      chr = (c & 0x01);
++              } else {
++                      count = 0;
++                      chr = 0;
+               }
+-              return;
+-      case ESnonstd:
+-              if (c=='P') {   /* palette escape sequence */
+-                      for (npar=0; npar<NPAR; npar++)
+-                              par[npar] = 0 ;
+-                      npar = 0 ;
+-                      vc_state = ESpalette;
+-                      return;
+-              } else if (c=='R') {   /* reset palette */
+-                      reset_palette(currcons);
+-                      vc_state = ESnormal;
+-              } else
+-                      vc_state = ESnormal;
+-              return;
+-      case ESpalette:
+-              if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
+-                      par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
+-                      if (npar==7) {
+-                              int i = par[0]*3, j = 1;
+-                              palette[i] = 16*par[j++];
+-                              palette[i++] += par[j++];
+-                              palette[i] = 16*par[j++];
+-                              palette[i++] += par[j++];
+-                              palette[i] = 16*par[j++];
+-                              palette[i] += par[j];
+-                              set_palette(currcons);
+-                              vc_state = ESnormal;
+-                      }
+-              } else
+-                      vc_state = ESnormal;
+-              return;
+-      case ESsquare:
+-              for(npar = 0 ; npar < NPAR ; npar++)
+-                      par[npar] = 0;
+-              npar = 0;
+-              vc_state = ESgetpars;
+-              if (c == '[') { /* Function key */
+-                      vc_state=ESfunckey;
+-                      return;
++              utf_count = count;
++      }
++
++      utf_char = chr;
++      return -1;
++}
++
++static int con_write_ctrl_ESnormal(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      return 0;
++}
++
++static int con_write_ctrl_ESesc(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      vc_state = con_write_ctrl_ESnormal;
++      switch (c) {
++      case '[':
++              vc_state = con_write_ctrl_ESsquare;
++              break;
++      case ']':
++              vc_state = con_write_ctrl_ESnonstd;
++              break;
++      case '%':
++              vc_state = con_write_ctrl_ESpercent;
++              break;
++      case 'E':
++              cr(currcons);
++              lf(currcons);
++              break;
++      case 'M':
++              ri(currcons);
++              break;
++      case 'D':
++              lf(currcons);
++              break;
++      case 'H':
++              tab_stop[x >> 5] |= (1 << (x & 31));
++              break;
++      case 'Z':
++              respond_ID(tty);
++              break;
++      case '7':
++              save_cur(currcons);
++              break;
++      case '8':
++              restore_cur(currcons);
++              return 1;
++      case '(':
++              vc_state = con_write_ctrl_ESsetG0;
++              break;
++      case ')':
++              vc_state = con_write_ctrl_ESsetG1;
++              break;
++      case '#':
++              vc_state = con_write_ctrl_EShash;
++              break;
++      case 'c':
++              reset_terminal(currcons,1);
++              return 1;
++      case '>':  /* Numeric keypad */
++              clr_kbd(kbdapplic);
++              break;
++      case '=':  /* Appl. keypad */
++              set_kbd(kbdapplic);
++              break;
++      }
++      return 0;
++}
++
++static int con_write_ctrl_ESnonstd(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      switch (c) {
++      case 'P': /* palette escape sequence */
++              for (npar=0; npar<NPAR; npar++)
++                      par[npar] = 0 ;
++              npar = 0 ;
++              vc_state = con_write_ctrl_ESpalette;
++              break;
++      case 'R': /* reset palette */
++              reset_palette (currcons);
++      default:
++              vc_state = con_write_ctrl_ESnormal;
++      }
++      return 0;
++}
++
++static int con_write_ctrl_ESpalette(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
++              par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
++              if (npar==7) {
++                      int i = par[0]*3, j = 1;
++                      palette[i] = 16*par[j++];
++                      palette[i++] += par[j++];
++                      palette[i] = 16*par[j++];
++                      palette[i++] += par[j++];
++                      palette[i] = 16*par[j++];
++                      palette[i] += par[j];
++                      set_palette(currcons);
++                      vc_state = con_write_ctrl_ESnormal;
+               }
+-              ques = (c=='?');
+-              if (ques)
+-                      return;
+-      case ESgetpars:
+-              if (c==';' && npar<NPAR-1) {
+-                      npar++;
+-                      return;
+-              } else if (c>='0' && c<='9') {
+-                      par[npar] *= 10;
+-                      par[npar] += c-'0';
+-                      return;
+-              } else vc_state=ESgotpars;
+-      case ESgotpars:
+-              vc_state = ESnormal;
+-              switch(c) {
+-              case 'h':
+-                      set_mode(currcons,1);
+-                      return;
+-              case 'l':
+-                      set_mode(currcons,0);
+-                      return;
+-              case 'c':
+-                      if (ques) {
+-                              if (par[0])
+-                                      cursor_type = par[0] | (par[1]<<8) | (par[2]<<16);
+-                              else
+-                                      cursor_type = CUR_DEFAULT;
+-                              return;
+-                      }
+-                      break;
+-              case 'm':
+-                      if (ques) {
+-                              clear_selection();
+-                              if (par[0])
+-                                      complement_mask = par[0]<<8 | par[1];
+-                              else
+-                                      complement_mask = s_complement_mask;
+-                              return;
+-                      }
+-                      break;
+-              case 'n':
+-                      if (!ques) {
+-                              if (par[0] == 5)
+-                                      status_report(tty);
+-                              else if (par[0] == 6)
+-                                      cursor_report(currcons,tty);
+-                      }
+-                      return;
++      } else
++              vc_state = con_write_ctrl_ESnormal;
++      return 0;
++}
++
++static int con_write_ctrl_ESsquare(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      for(npar = 0 ; npar < NPAR ; npar++)
++              par[npar] = 0;
++      npar = 0;
++      vc_state = con_write_ctrl_ESgetpars;
++      if (c == '[') { /* Function key */
++              vc_state = con_write_ctrl_ESfunckey;
++              return 0;
++      }
++      ques = (c=='?');
++      if (ques)
++              return 0;
++      return con_write_ctrl_ESgetpars(currcons, tty, c);
++}
++
++static int con_write_ctrl_ESgetpars(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      if (c==';' && npar<NPAR-1) {
++              npar++;
++              return 0;
++      } else if (c>='0' && c<='9') {
++              par[npar] *= 10;
++              par[npar] += c-'0';
++              return 0;
++      } else vc_state = con_write_ctrl_ESgotpars;
++      return con_write_ctrl_ESgotpars(currcons, tty, c);
++}
++
++static int con_write_ctrl_ESgotpars(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      vc_state = con_write_ctrl_ESnormal;
++      switch(c) {
++      case 'h':
++              set_mode(currcons,1);
++              return 0;
++      case 'l':
++              set_mode(currcons,0);
++              return 0;
++      case 'c':
++              if (ques) {
++                      if (par[0])
++                              cursor_type = par[0] | (par[1]<<8) | (par[2]<<16);
++                      else
++                              cursor_type = CUR_DEFAULT;
++                      return 0;
+               }
++              break;
++      case 'm':
+               if (ques) {
+-                      ques = 0;
+-                      return;
++                      clear_selection();
++                      if (par[0])
++                              complement_mask = par[0]<<8 | par[1];
++                      else
++                              complement_mask = s_complement_mask;
++                      return 0;
+               }
+-              switch(c) {
+-              case 'G': case '`':
+-                      if (par[0]) par[0]--;
+-                      gotoxy(currcons,par[0],y);
+-                      return;
+-              case 'A':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,x,y-par[0]);
+-                      return;
+-              case 'B': case 'e':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,x,y+par[0]);
+-                      return;
+-              case 'C': case 'a':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,x+par[0],y);
+-                      return;
+-              case 'D':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,x-par[0],y);
+-                      return;
+-              case 'E':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,0,y+par[0]);
+-                      return;
+-              case 'F':
+-                      if (!par[0]) par[0]++;
+-                      gotoxy(currcons,0,y-par[0]);
+-                      return;
+-              case 'd':
+-                      if (par[0]) par[0]--;
+-                      gotoxay(currcons,x,par[0]);
+-                      return;
+-              case 'H': case 'f':
+-                      if (par[0]) par[0]--;
+-                      if (par[1]) par[1]--;
+-                      gotoxay(currcons,par[1],par[0]);
+-                      return;
+-              case 'J':
+-                      csi_J(currcons,par[0]);
+-                      return;
+-              case 'K':
+-                      csi_K(currcons,par[0]);
+-                      return;
+-              case 'L':
+-                      csi_L(currcons,par[0]);
+-                      return;
+-              case 'M':
+-                      csi_M(currcons,par[0]);
+-                      return;
+-              case 'P':
+-                      csi_P(currcons,par[0]);
+-                      return;
+-              case 'c':
+-                      if (!par[0])
+-                              respond_ID(tty);
+-                      return;
+-              case 'g':
+-                      if (!par[0])
+-                              tab_stop[x >> 5] &= ~(1 << (x & 31));
+-                      else if (par[0] == 3) {
+-                              tab_stop[0] =
+-                                      tab_stop[1] =
+-                                      tab_stop[2] =
+-                                      tab_stop[3] =
+-                                      tab_stop[4] = 0;
+-                      }
+-                      return;
+-              case 'm':
+-                      csi_m(currcons);
+-                      return;
+-              case 'q': /* DECLL - but only 3 leds */
+-                      /* map 0,1,2,3 to 0,1,2,4 */
+-                      if (par[0] < 4)
+-                              setledstate(kbd_table + currcons,
+-                                          (par[0] < 3) ? par[0] : 4);
+-                      return;
+-              case 'r':
+-                      if (!par[0])
+-                              par[0]++;
+-                      if (!par[1])
+-                              par[1] = video_num_lines;
+-                      /* Minimum allowed region is 2 lines */
+-                      if (par[0] < par[1] &&
+-                          par[1] <= video_num_lines) {
+-                              top=par[0]-1;
+-                              bottom=par[1];
+-                              gotoxay(currcons,0,0);
+-                      }
+-                      return;
+-              case 's':
+-                      save_cur(currcons);
+-                      return;
+-              case 'u':
+-                      restore_cur(currcons);
+-                      return;
+-              case 'X':
+-                      csi_X(currcons, par[0]);
+-                      return;
+-              case '@':
+-                      csi_at(currcons,par[0]);
+-                      return;
+-              case ']': /* setterm functions */
+-                      setterm_command(currcons);
+-                      return;
++              break;
++      case 'n':
++              if (!ques) {
++                      if (par[0] == 5)
++                              status_report(tty);
++                      else if (par[0] == 6)
++                              cursor_report(currcons,tty);
+               }
+-              return;
+-      case ESpercent:
+-              vc_state = ESnormal;
+-              switch (c) {
+-              case '@':  /* defined in ISO 2022 */
+-                      utf = 0;
+-                      return;
+-              case 'G':  /* prelim official escape code */
+-              case '8':  /* retained for compatibility */
+-                      utf = 1;
+-                      return;
++              return 0;
++      }
++      if (ques) {
++              ques = 0;
++              return 0;
++      }
++      switch(c) {
++      case 'G': case '`':
++              if (par[0]) par[0]--;
++              gotoxy(currcons,par[0],y);
++              break;
++      case 'A':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,x,y-par[0]);
++              break;
++      case 'B': case 'e':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,x,y+par[0]);
++              break;
++      case 'C': case 'a':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,x+par[0],y);
++              break;
++      case 'D':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,x-par[0],y);
++              break;
++      case 'E':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,0,y+par[0]);
++              break;
++      case 'F':
++              if (!par[0]) par[0]++;
++              gotoxy(currcons,0,y-par[0]);
++              break;
++      case 'd':
++              if (par[0]) par[0]--;
++              gotoxay(currcons,x,par[0]);
++              break;
++      case 'H': case 'f':
++              if (par[0]) par[0]--;
++              if (par[1]) par[1]--;
++              gotoxay(currcons,par[1],par[0]);
++              break;
++      case 'J':
++              csi_J(currcons,par[0]);
++              break;
++      case 'K':
++              csi_K(currcons,par[0]);
++              break;
++      case 'L':
++              csi_L(currcons,par[0]);
++              break;
++      case 'M':
++              csi_M(currcons,par[0]);
++              break;
++      case 'P':
++              csi_P(currcons,par[0]);
++              break;
++      case 'c':
++              if (!par[0])
++                      respond_ID(tty);
++              break;
++      case 'g':
++              if (!par[0])
++                      tab_stop[x >> 5] &= ~(1 << (x & 31));
++              else if (par[0] == 3) {
++                      tab_stop[0] =
++                      tab_stop[1] =
++                      tab_stop[2] =
++                      tab_stop[3] =
++                      tab_stop[4] = 0;
+               }
+-              return;
+-      case ESfunckey:
+-              vc_state = ESnormal;
+-              return;
+-      case EShash:
+-              vc_state = ESnormal;
+-              if (c == '8') {
+-                      /* DEC screen alignment test. kludge :-) */
+-                      video_erase_char =
+-                              (video_erase_char & 0xff00) | 'E';
+-                      csi_J(currcons, 2);
+-                      video_erase_char =
+-                              (video_erase_char & 0xff00) | ' ';
+-                      do_update_region(currcons, origin, screenbuf_size/2);
++              break;
++      case 'm':
++              csi_m(currcons);
++              return 1;
++      case 'q': /* DECLL - but only 3 leds */
++              /* map 0,1,2,3 to 0,1,2,4 */
++              if (par[0] < 4)
++                setledstate(kbd_table + currcons,
++                            (par[0] < 3) ? par[0] : 4);
++              break;
++      case 'r':
++              if (!par[0])
++                      par[0]++;
++              if (!par[1])
++                      par[1] = video_num_lines;
++              /* Minimum allowed region is 2 lines */
++              if (par[0] < par[1] &&
++                  par[1] <= video_num_lines) {
++                      top=par[0]-1;
++                      bottom=par[1];
++                      gotoxay(currcons,0,0);
+               }
+-              return;
+-      case ESsetG0:
+-              if (c == '0')
+-                      G0_charset = GRAF_MAP;
+-              else if (c == 'B')
+-                      G0_charset = LAT1_MAP;
+-              else if (c == 'U')
+-                      G0_charset = IBMPC_MAP;
+-              else if (c == 'K')
+-                      G0_charset = USER_MAP;
+-              if (charset == 0)
+-                      translate = set_translate(G0_charset,currcons);
+-              vc_state = ESnormal;
+-              return;
+-      case ESsetG1:
+-              if (c == '0')
+-                      G1_charset = GRAF_MAP;
+-              else if (c == 'B')
+-                      G1_charset = LAT1_MAP;
+-              else if (c == 'U')
+-                      G1_charset = IBMPC_MAP;
+-              else if (c == 'K')
+-                      G1_charset = USER_MAP;
+-              if (charset == 1)
+-                      translate = set_translate(G1_charset,currcons);
+-              vc_state = ESnormal;
+-              return;
+-      default:
+-              vc_state = ESnormal;
++              break;
++      case 's':
++              save_cur(currcons);
++              break;
++      case 'u':
++              restore_cur(currcons);
++              return 1;
++      case 'X':
++              csi_X(currcons, par[0]);
++              break;
++      case '@':
++              csi_at(currcons,par[0]);
++              break;
++      case ']': /* setterm functions */
++              setterm_command(currcons);
++              break;
++      }
++      return 0;
++}
++
++static int con_write_ctrl_ESpercent(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      vc_state = con_write_ctrl_ESnormal;
++      switch (c) {
++      case '@':  /* defined in ISO 2022 */
++              utf = 0;
++              break;
++      case 'G':  /* prelim official escape code */
++      case '8':  /* retained for compatibility */
++              utf = 1;
++              break;
++      }
++      return 0;
++}
++
++static int con_write_ctrl_ESfunckey(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      vc_state = con_write_ctrl_ESnormal;
++      return 0;
++}
++
++static int con_write_ctrl_EShash(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      vc_state = con_write_ctrl_ESnormal;
++      if (c == '8') {
++              /* DEC screen alignment test. kludge :-) */
++              video_erase_char =
++                      (video_erase_char & 0xff00) | 'E';
++              csi_J(currcons, 2);
++              video_erase_char =
++                      (video_erase_char & 0xff00) | ' ';
++              do_update_region(currcons, origin, screenbuf_size/2);
++      }
++      return 0;
++}
++
++static int con_write_ctrl_ESsetG0(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      switch (c) {
++      case '0':
++              G0_charset = GRAF_MAP;
++              break;
++      case 'B':
++              G0_charset = LAT1_MAP;
++              break;
++      case 'U':
++              G0_charset = IBMPC_MAP;
++              break;
++      case 'K':
++              G0_charset = USER_MAP;
++              break;
++      }
++      if (charset == 0) {
++              translate = set_translate(G0_charset,currcons);
++              return 1;
++      }
++      vc_state = con_write_ctrl_ESnormal;
++      return 0;
++}
++
++static int con_write_ctrl_ESsetG1(int currcons, struct tty_struct *tty, unsigned int c)
++{
++      switch (c) {
++      case '0':
++              G1_charset = GRAF_MAP;
++              break;
++      case 'B':
++              G1_charset = LAT1_MAP;
++              break;
++      case 'U':
++              G1_charset = IBMPC_MAP;
++              break;
++      case 'K':
++              G1_charset = USER_MAP;
++              break;
++      }
++      if (charset == 1) {
++              translate = set_translate(G1_charset,currcons);
++              return 1;
+       }
++      vc_state = con_write_ctrl_ESnormal;
++      return 0;
+ }
+ /* This is a temporary buffer used to prepare a tty console write
+@@ -1855,7 +1972,7 @@
+       unsigned long draw_from = 0, draw_to = 0;
+       struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+       u16 himask, charmask;
+-      const unsigned char *orig_buf = NULL;
++      const unsigned char *orig_buf;
+       int orig_count;
+       if (in_interrupt())
+@@ -1913,42 +2030,12 @@
+               count--;
+               if (utf) {
+-                  /* Combine UTF-8 into Unicode */
+-                  /* Incomplete characters silently ignored */
+-                  if(c > 0x7f) {
+-                      if (utf_count > 0 && (c & 0xc0) == 0x80) {
+-                              utf_char = (utf_char << 6) | (c & 0x3f);
+-                              utf_count--;
+-                              if (utf_count == 0)
+-                                  tc = c = utf_char;
+-                              else continue;
+-                      } else {
+-                              if ((c & 0xe0) == 0xc0) {
+-                                  utf_count = 1;
+-                                  utf_char = (c & 0x1f);
+-                              } else if ((c & 0xf0) == 0xe0) {
+-                                  utf_count = 2;
+-                                  utf_char = (c & 0x0f);
+-                              } else if ((c & 0xf8) == 0xf0) {
+-                                  utf_count = 3;
+-                                  utf_char = (c & 0x07);
+-                              } else if ((c & 0xfc) == 0xf8) {
+-                                  utf_count = 4;
+-                                  utf_char = (c & 0x03);
+-                              } else if ((c & 0xfe) == 0xfc) {
+-                                  utf_count = 5;
+-                                  utf_char = (c & 0x01);
+-                              } else
+-                                  utf_count = 0;
++                      tc = con_write_utf(currcons, c);
++                      if (tc < 0)
+                               continue;
+-                            }
+-                  } else {
+-                    tc = c;
+-                    utf_count = 0;
+-                  }
+-              } else {        /* no utf */
+-                tc = translate[toggle_meta ? (c|0x80) : c];
+-              }
++                      c = tc;
++              } else  /* no utf */
++                      tc = translate[toggle_meta ? (c|0x80) : c];
+                 /* If the original code was a control character we
+                  * only allow a glyph to be displayed if the code is
+@@ -1966,7 +2053,7 @@
+                         && (c != 127 || disp_ctrl)
+                       && (c != 128+27);
+-              if (vc_state == ESnormal && ok) {
++              if (vc_state == con_write_ctrl_ESnormal && ok) {
+                       /* Now try to find out how to display it */
+                       tc = conv_uni_to_pc(vc_cons[currcons].d, tc);
+                       if ( tc == -4 ) {
+@@ -2112,7 +2199,7 @@
+       if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1))
+               currcons = kmsg_redirect - 1;
+-      /* read `x' only after setting currecons properly (otherwise
++      /* read `x' only after setting currcons properly (otherwise
+          the `x' macro will read the x of the foreground console). */
+       myx = x;
+@@ -2475,8 +2562,8 @@
+       console_driver.init_termios = tty_std_termios;
+       console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
+       /* Tell tty_register_driver() to skip consoles because they are
+-       * registered before kmalloc() is ready. We'll patch them in later. 
+-       * See comments at console_init(); see also con_init_devfs(). 
++       * registered before kmalloc() is ready. We'll patch them in later.
++       * See comments at console_init(); see also con_init_devfs().
+        */
+       console_driver.flags |= TTY_DRIVER_NO_DEVFS;
+       console_driver.refcount = &console_refcount;
+@@ -2719,7 +2806,7 @@
+       if (console_blank_hook && console_blank_hook(1))
+               return;
+-      if (vesa_blank_mode)
++      if (vesa_blank_mode)
+               sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
+ }
+@@ -2893,7 +2980,7 @@
+               if (!op->height) {              /* Need to guess font height [compat] */
+                       int h, i;
+                       u8 *charmap = op->data, tmp;
+-                      
++
+                       /* If from KDFONTOP ioctl, don't allow things which can be done in userland,
+                          so that we can get rid of this soon */
+                       if (!(op->flags & KD_FONT_FLAG_OLD))
+@@ -2940,18 +3027,18 @@
+       op->data = old_op.data;
+       if (!rc && !set) {
+               int c = (op->width+7)/8 * 32 * op->charcount;
+-              
++
+               if (op->data && op->charcount > old_op.charcount)
+                       rc = -ENOSPC;
+               if (!(op->flags & KD_FONT_FLAG_OLD)) {
+-                      if (op->width > old_op.width || 
++                      if (op->width > old_op.width ||
+                           op->height > old_op.height)
+                               rc = -ENOSPC;
+               } else {
+                       if (op->width != 8)
+                               rc = -EIO;
+                       else if ((old_op.height && op->height > old_op.height) ||
+-                               op->height > 32)
++                               op->height > 32)
+                               rc = -ENOSPC;
+               }
+               if (!rc && op->data && copy_to_user(op->data, temp, c))
+--- /dev/null
++++ linux-2.4.27/drivers/char/ds1307.c
+@@ -0,0 +1,604 @@
++/*
++ * ds1307.c
++ *
++ * Device driver for Dallas Semiconductor's Real Time Controller DS1307.
++ *
++ * Copyright (C) 2002 Intrinsyc 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/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 "ds1307.h"
++
++#define DEBUG 0
++
++#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 = DS1307_I2C_SLAVE_ADDR;
++
++struct i2c_driver ds1307_driver;
++struct i2c_client *ds1307_i2c_client = 0;
++
++static unsigned short ignore[] = { I2C_CLIENT_END };
++static unsigned short normal_addr[] = { DS1307_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++
++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 int ds1307_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long);
++static int ds1307_rtc_open(struct inode *inode, struct file *file);
++static int ds1307_rtc_release(struct inode *inode, struct file *file);
++
++static struct file_operations rtc_fops = {
++      owner:          THIS_MODULE,
++      ioctl:          ds1307_rtc_ioctl,
++      open:           ds1307_rtc_open,
++      release:        ds1307_rtc_release,
++};
++
++static struct miscdevice ds1307_rtc_miscdev = {
++      RTC_MINOR,
++      "rtc",
++      &rtc_fops
++};
++
++static int ds1307_probe(struct i2c_adapter *adap);
++static int ds1307_detach(struct i2c_client *client);
++static int ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++struct i2c_driver ds1307_driver = {
++      name:           "DS1307",
++      id:             I2C_DRIVERID_DS1307,
++      flags:          I2C_DF_NOTIFY,
++      attach_adapter: ds1307_probe,
++      detach_client:  ds1307_detach,
++      command:        ds1307_command
++};
++
++static spinlock_t ds1307_rtc_lock = SPIN_LOCK_UNLOCKED;
++
++#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */
++
++static int
++ds1307_readram( char *buf, int len)
++{
++      unsigned long   flags;
++      unsigned char ad[1] = { 0 };
++      int ret;
++      struct i2c_msg msgs[2] = {
++              { ds1307_i2c_client->addr  , 0,        1, ad  },
++              { ds1307_i2c_client->addr  , I2C_M_RD, len, buf } };
++
++      spin_lock_irqsave(&ds1307_rtc_lock, flags);
++      ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2);
++      spin_unlock_irqrestore(&ds1307_rtc_lock,flags);
++
++      return ret;
++}
++
++static void
++ds1307_dumpram( void)
++{
++      unsigned char buf[DS1307_RAM_SIZE];
++      int ret;
++
++      ret = ds1307_readram( buf, DS1307_RAM_SIZE);
++
++      if( ret > 0)
++      {
++              int i;
++              for( i=0; i<DS1307_RAM_SIZE; i++)
++              {
++                      printk ("%02X ", buf[i]);
++                      if( (i%8) == 7) printk ("\n");
++              }
++              printk ("\n");
++      }
++}
++
++static void
++ds1307_enable_clock( int enable)
++{
++      unsigned char buf[2], ad[1] = { 0 };
++      struct i2c_msg msgs[2] = {
++              { ds1307_i2c_client->addr       , 0,        1, ad  },
++              { ds1307_i2c_client->addr       , I2C_M_RD, 1, buf }
++      };
++      unsigned char ctrl_info;
++      int ret;
++
++      if( enable)
++              ctrl_info = SQW_ENABLE | RATE_32768HZ;
++      else
++              ctrl_info = SQW_DISABLE;
++      ds1307_command(ds1307_i2c_client, DS1307_SETCTRL, &ctrl_info);
++
++      /* read addr 0 (Clock-Halt bit and second counter */
++      ret = i2c_transfer(ds1307_i2c_client->adapter, msgs, 2);
++
++      if( enable)
++              buf[1] = buf[0] & ~CLOCK_HALT; /* clear Clock-Halt bit */
++      else
++              buf[1] = buf[0] | CLOCK_HALT; /* set Clock-Halt bit */
++      buf[0] = 0;     /* control register address on DS1307 */
++
++      ret = i2c_master_send(ds1307_i2c_client, (char *)buf, 2);
++}
++
++static int
++ds1307_attach(struct i2c_adapter *adap, int addr, unsigned short flags,int kind)
++{
++      struct i2c_client *c;
++      unsigned char buf[1], ad[1] = { 7 };
++      struct i2c_msg msgs[2] = {
++              { addr  , 0,        1, ad  },
++              { addr  , I2C_M_RD, 1, buf }
++      };
++      int ret;
++
++      c = (struct i2c_client *)kmalloc(sizeof(*c), GFP_KERNEL);
++      if (!c)
++              return -ENOMEM;
++
++      strcpy(c->name, "DS1307");
++      c->id           = ds1307_driver.id;
++      c->flags        = 0;
++      c->addr         = addr;
++      c->adapter      = adap;
++      c->driver       = &ds1307_driver;
++      c->data         = NULL;
++
++      ret = i2c_transfer(c->adapter, msgs, 2);
++
++      if ( ret == 2 )
++      {
++              DAT(c) = buf[0];
++      }
++      else
++              printk ("ds1307_attach(): i2c_transfer() returned %d.\n",ret);
++
++      ds1307_i2c_client = c;
++      ds1307_enable_clock( 1);
++
++      return i2c_attach_client(c);
++}
++
++static int
++ds1307_probe(struct i2c_adapter *adap)
++{
++      return i2c_probe(adap, &addr_data, ds1307_attach);
++}
++
++static int
++ds1307_detach(struct i2c_client *client)
++{
++      i2c_detach_client(client);
++      ds1307_enable_clock( 0);
++
++      return 0;
++}
++
++static void
++ds1307_convert_to_time( struct rtc_time *dt, char *buf)
++{
++      dt->tm_sec = BCD_TO_BIN(buf[0]);
++      dt->tm_min = BCD_TO_BIN(buf[1]);
++
++      if ( TWELVE_HOUR_MODE(buf[2]) )
++      {
++              dt->tm_hour = HOURS_12(buf[2]);
++              if (HOURS_AP(buf[2])) /* PM */
++              {
++                      dt->tm_hour += 12;
++              }
++      }
++      else /* 24-hour-mode */
++      {
++              dt->tm_hour = 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("ds1307_get_datetime: year = %d\n", dt->tm_year);
++              printk("ds1307_get_datetime: mon  = %d\n", dt->tm_mon);
++              printk("ds1307_get_datetime: mday = %d\n", dt->tm_mday);
++              printk("ds1307_get_datetime: hour = %d\n", dt->tm_hour);
++              printk("ds1307_get_datetime: min  = %d\n", dt->tm_min);
++              printk("ds1307_get_datetime: sec  = %d\n", dt->tm_sec);
++      }
++}
++
++static int
++ds1307_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;
++
++      memset(buf, 0, sizeof(buf));
++
++      ret = i2c_transfer(client->adapter, msgs, 2);
++
++      if (ret == 2) {
++              ds1307_convert_to_time( dt, buf);
++              ret = 0;
++      }
++      else
++              printk("ds1307_get_datetime(), i2c_transfer() returned %d\n",ret);
++
++      return ret;
++}
++
++static int
++ds1307_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
++{
++      unsigned char buf[8];
++      int ret, len = 4;
++
++      if( rtc_debug > 2)
++      {
++              printk("ds1307_set_datetime: tm_year = %d\n", dt->tm_year);
++              printk("ds1307_set_datetime: tm_mon  = %d\n", dt->tm_mon);
++              printk("ds1307_set_datetime: tm_mday = %d\n", dt->tm_mday);
++              printk("ds1307_set_datetime: tm_hour = %d\n", dt->tm_hour);
++              printk("ds1307_set_datetime: tm_min  = %d\n", dt->tm_min);
++              printk("ds1307_set_datetime: tm_sec  = %d\n", dt->tm_sec);
++      }
++
++      buf[0] = 0;     /* register address on DS1307 */
++      buf[1] = (BIN_TO_BCD(dt->tm_sec));
++      buf[2] = (BIN_TO_BCD(dt->tm_min));
++      buf[3] = (BIN_TO_BCD(dt->tm_hour));
++
++      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("ds1307_set_datetime(), i2c_master_send() returned %d\n",ret);
++
++
++      return ret;
++}
++
++static int
++ds1307_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
++{
++      *ctrl = DAT(client);
++
++      return 0;
++}
++
++static int
++ds1307_set_ctrl(struct i2c_client *client, unsigned char *cinfo)
++{
++      unsigned char buf[2];
++      int ret;
++
++
++      buf[0] = 7;     /* control register address on DS1307 */
++      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;
++}
++
++static int
++ds1307_read_mem(struct i2c_client *client, struct rtc_mem *mem)
++{
++      unsigned char addr[1];
++      struct i2c_msg msgs[2] = {
++              { client->addr, 0,        1, addr },
++              { client->addr, I2C_M_RD, mem->nr, mem->data }
++      };
++
++      if ( (mem->loc < DS1307_RAM_ADDR_START) ||
++           ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) )
++              return -EINVAL;
++
++      addr[0] = mem->loc;
++
++      return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
++}
++
++static int
++ds1307_write_mem(struct i2c_client *client, struct rtc_mem *mem)
++{
++      unsigned char addr[1];
++      struct i2c_msg msgs[2] = {
++              { client->addr, 0, 1, addr },
++              { client->addr, 0, mem->nr, mem->data }
++      };
++
++      if ( (mem->loc < DS1307_RAM_ADDR_START) ||
++           ((mem->loc + mem->nr -1) > DS1307_RAM_ADDR_END) )
++              return -EINVAL;
++
++      addr[0] = mem->loc;
++
++      return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
++}
++
++static int
++ds1307_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++      switch (cmd) {
++      case DS1307_GETDATETIME:
++              return ds1307_get_datetime(client, arg);
++
++      case DS1307_SETTIME:
++              return ds1307_set_datetime(client, arg, 0);
++
++      case DS1307_SETDATETIME:
++              return ds1307_set_datetime(client, arg, 1);
++
++      case DS1307_GETCTRL:
++              return ds1307_get_ctrl(client, arg);
++
++      case DS1307_SETCTRL:
++              return ds1307_set_ctrl(client, arg);
++
++      case DS1307_MEM_READ:
++              return ds1307_read_mem(client, arg);
++
++      case DS1307_MEM_WRITE:
++              return ds1307_write_mem(client, arg);
++
++      default:
++              return -EINVAL;
++      }
++}
++
++static int
++ds1307_rtc_open(struct inode *inode, struct file *file)
++{
++      return 0;
++}
++
++static int
++ds1307_rtc_release(struct inode *inode, struct file *file)
++{
++      return 0;
++}
++
++static int
++ds1307_rtc_ioctl( struct inode *inode, struct file *file,
++              unsigned int cmd, unsigned long arg)
++{
++      unsigned long   flags;
++      struct rtc_time wtime;
++      int status = 0;
++
++      switch (cmd) {
++              default:
++              case RTC_UIE_ON:
++              case RTC_UIE_OFF:
++              case RTC_PIE_ON:
++              case RTC_PIE_OFF:
++              case RTC_AIE_ON:
++              case RTC_AIE_OFF:
++              case RTC_ALM_SET:
++              case RTC_ALM_READ:
++              case RTC_IRQP_READ:
++              case RTC_IRQP_SET:
++              case RTC_EPOCH_READ:
++              case RTC_EPOCH_SET:
++              case RTC_WKALM_SET:
++              case RTC_WKALM_RD:
++                      status = -EINVAL;
++                      break;
++
++              case RTC_RD_TIME:
++                      spin_lock_irqsave(&ds1307_rtc_lock, flags);
++                      ds1307_command( ds1307_i2c_client, DS1307_GETDATETIME, &wtime);
++                      spin_unlock_irqrestore(&ds1307_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(&ds1307_rtc_lock, flags);
++                      ds1307_command( ds1307_i2c_client, DS1307_SETDATETIME, &wtime);
++                      spin_unlock_irqrestore(&ds1307_rtc_lock,flags);
++                      break;
++      }
++
++      return status;
++}
++
++static char *
++ds1307_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 ds1307_rtc_proc_output( char *buf)
++{
++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++      unsigned char ram[DS1307_RAM_SIZE];
++      int ret;
++
++      char *p = buf;
++
++      ret = ds1307_readram( ram, DS1307_RAM_SIZE);
++      if( ret > 0)
++      {
++              int i;
++              struct rtc_time dt;
++              char text[9];
++
++              p += sprintf(p, "DS1307 (64x8 Serial Real Time Clock)\n");
++
++              ds1307_convert_to_time( &dt, ram);
++              p += sprintf(p, "Date/Time           : %02d-%s-%04d %02d:%02d:%02d\n",
++                      dt.tm_mday, ds1307_mon2str(dt.tm_mon), dt.tm_year + 1900,
++                      dt.tm_hour, dt.tm_min, dt.tm_sec);
++
++              p += sprintf(p, "Clock halted        : %s\n", CHECK(ram[0],0x80));
++              p += sprintf(p, "24h mode            : %s\n", CHECK(ram[2],0x40));
++              p += sprintf(p, "Square wave enabled : %s\n", CHECK(ram[7],0x10));
++              p += sprintf(p, "Freq                : ");
++
++              switch( ram[7] & 0x03)
++              {
++                      case RATE_1HZ:
++                              p += sprintf(p, "1Hz\n");
++                              break;
++                      case RATE_4096HZ:
++                              p += sprintf(p, "4.096kHz\n");
++                              break;
++                      case RATE_8192HZ:
++                              p += sprintf(p, "8.192kHz\n");
++                              break;
++                      case RATE_32768HZ:
++                      default:
++                              p += sprintf(p, "32.768kHz\n");
++                              break;
++
++              }
++
++              p += sprintf(p, "RAM dump:\n");
++              text[8]='\0';
++              for( i=0; i<DS1307_RAM_SIZE; 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");
++      }
++      else
++      {
++              p += sprintf(p, "Failed to read RTC memory!\n");
++      }
++
++      return  p - buf;
++}
++
++static int ds1307_rtc_read_proc(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++      int len = ds1307_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 ds1307_init(void)
++{
++      int retval=0;
++
++      if( slave_address != 0xffff)
++      {
++              normal_addr[0] = slave_address;
++      }
++
++      if( normal_addr[0] == 0xffff)
++      {
++              printk(KERN_ERR"I2C: Invalid slave address for DS1307 RTC (%#x)\n",
++                      normal_addr[0]);
++              return -EINVAL;
++      }
++
++      retval = i2c_add_driver(&ds1307_driver);
++
++      if (retval==0)
++      {
++              misc_register (&ds1307_rtc_miscdev);
++              create_proc_read_entry (PROC_DS1307_NAME, 0, 0, ds1307_rtc_read_proc, NULL);
++              printk("I2C: DS1307 RTC driver successfully loaded\n");
++
++              if( rtc_debug) ds1307_dumpram();
++      }
++      return retval;
++}
++
++static __exit void ds1307_exit(void)
++{
++      remove_proc_entry (PROC_DS1307_NAME, NULL);
++      misc_deregister(&ds1307_rtc_miscdev);
++      i2c_del_driver(&ds1307_driver);
++}
++
++module_init(ds1307_init);
++module_exit(ds1307_exit);
++
++MODULE_PARM (slave_address, "i");
++MODULE_PARM_DESC (slave_address, "I2C slave address for DS1307 RTC.");
++
++MODULE_AUTHOR ("Intrinsyc Software Inc.");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/char/ds1307.h
+@@ -0,0 +1,58 @@
++/*
++ * ds1307.h
++ *
++ * Copyright (C) 2002 Intrinsyc 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.
++ *
++ */
++#ifndef DS1307_H
++#define DS1307_H
++
++#if defined(CONFIG_PXA_EMERSON_SBC) || defined(CONFIG_PXA_CERF_BOARD) || defined(CONFIG_MACH_CSB337)
++      #define DS1307_I2C_SLAVE_ADDR   0x68
++#else
++      #define DS1307_I2C_SLAVE_ADDR   0xffff
++#endif
++
++#define DS1307_RAM_ADDR_START 0x08
++#define DS1307_RAM_ADDR_END   0x3F
++#define DS1307_RAM_SIZE 0x40
++
++#define PROC_DS1307_NAME      "driver/ds1307"
++
++struct rtc_mem {
++      unsigned int    loc;
++      unsigned int    nr;
++      unsigned char   *data;
++};
++
++#define DS1307_GETDATETIME    0
++#define DS1307_SETTIME                1
++#define DS1307_SETDATETIME    2
++#define DS1307_GETCTRL                3
++#define DS1307_SETCTRL                4
++#define DS1307_MEM_READ               5
++#define DS1307_MEM_WRITE      6
++
++#define SQW_ENABLE    0x10    /* Square Wave Enable */
++#define SQW_DISABLE   0x00    /* Square Wave disable */
++
++#define RATE_32768HZ  0x03    /* Rate Select 32.768KHz */
++#define RATE_8192HZ   0x02    /* Rate Select 8.192KHz */
++#define RATE_4096HZ   0x01    /* Rate Select 4.096KHz */
++#define RATE_1HZ      0x00    /* Rate Select 1Hz */
++
++#define CLOCK_HALT    0x80    /* Clock Halt */
++
++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
++
++#define TWELVE_HOUR_MODE(n)   (((n)>>6)&1)
++#define HOURS_AP(n)           (((n)>>5)&1)
++#define HOURS_12(n)           BCD_TO_BIN((n)&0x1F)
++#define HOURS_24(n)           BCD_TO_BIN((n)&0x3F)
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/char/ds1307.h.rej
+@@ -0,0 +1,17 @@
++***************
++*** 11,17 ****
++  #ifndef DS1307_H
++  #define DS1307_H
++  
++- #if defined(CONFIG_PXA_EMERSON_SBC) || defined(CONFIG_PXA_CERF_BOARD)
++      #define DS1307_I2C_SLAVE_ADDR   0x68
++  #else
++      #define DS1307_I2C_SLAVE_ADDR   0xffff
++--- 11,17 ----
++  #ifndef DS1307_H
++  #define DS1307_H
++  
+++ #if defined(CONFIG_PXA_EMERSON_SBC) || defined(CONFIG_PXA_CERF_BOARD) || defined(CONFIG_MACH_CSB337)
++      #define DS1307_I2C_SLAVE_ADDR   0x68
++  #else
++      #define DS1307_I2C_SLAVE_ADDR   0xffff
+--- /dev/null
++++ linux-2.4.27/drivers/char/edb7211_keyb.c
+@@ -0,0 +1,335 @@
++/*
++ * drivers/char/edb7211_keyb.c
++ *
++ * Copyright (C) 2000 Blue Mug, Inc.  All Rights Reserved.
++ *
++ * EDB7211 Keyboard driver for ARM Linux.
++ *
++ * The EP7211 keyboard hardware only supports generating interrupts for 64 keys.
++ * The EBD7211's keyboard has 84 keys. Therefore we need to poll for keys,
++ * instead of waiting for interrupts.
++ *
++ * In a real-world hardware situation, this would be a bad thing. It would
++ * kill power management.
++ */
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/ptrace.h>
++#include <linux/signal.h>
++#include <linux/timer.h>
++#include <linux/tqueue.h>
++#include <linux/random.h>
++#include <linux/ctype.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/kbd_kern.h>
++#include <linux/delay.h>
++
++#include <asm/bitops.h>
++#include <asm/keyboard.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++
++#include <asm/io.h>
++#include <asm/system.h>
++
++
++/*
++ * The number of jiffies between keyboard scans.
++ */
++#define KEYBOARD_SCAN_INTERVAL        5
++
++/*
++ * Values for the keyboard column scan control register.
++ */
++#define KBSC_HI           0x0     /*   All driven high */
++#define KBSC_LO           0x1     /*   All driven low */
++#define KBSC_X            0x2     /*   All high impedance */
++#define KBSC_COL0   0x8           /*   Column 0 high, others high impedance */
++#define KBSC_COL1   0x9           /*   Column 1 high, others high impedance */
++#define KBSC_COL2   0xa           /*   Column 2 high, others high impedance */
++#define KBSC_COL3   0xb           /*   Column 3 high, others high impedance */
++#define KBSC_COL4   0xc           /*   Column 4 high, others high impedance */
++#define KBSC_COL5   0xd           /*   Column 5 high, others high impedance */
++#define KBSC_COL6   0xe           /*   Column 6 high, others high impedance */
++#define KBSC_COL7   0xf           /*   Column 7 high, others high impedance */
++
++
++/* XXX: Figure out what these values should be... */
++/* Simple translation table for the SysRq keys */
++#ifdef CONFIG_MAGIC_SYSRQ
++unsigned char edb7211_kbd_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 */
++#endif
++
++/* 
++ * Row/column to scancode mappings.
++ *
++ * This table maps row/column keyboard matrix positions to XT scancodes.
++ * 
++ * The port A rows come first, followed by the extended rows.
++ */
++static unsigned char colrow_2_scancode[128] = 
++{
++/*  Column: 
++  Row       0     1     2     3     4     5     6     7   */
++/* A0 */  0x01, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x40, 0x41,
++/* A1 */  0x02, 0x07, 0x06, 0x05, 0x04, 0x03, 0x08, 0x09,
++/* A2 */  0x0f, 0x14, 0x13, 0x12, 0x11, 0x10, 0x15, 0x16,
++/* A3 */  0x3a, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x23, 0x24,
++/* A4 */  0x29, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x31, 0x32,
++/* A5 */  0x39, 0x35, 0x6F, 0x52, 0x00, 0x6B, 0x34, 0x33,
++/* A6 */  0x6A, 0x27, 0x28, 0x00, 0x1c, 0x6D, 0x26, 0x25,
++/* A7 */  0x67, 0x19, 0x1a, 0x1b, 0x2b, 0x68, 0x18, 0x17,
++/* E0 */  0x6C, 0x0c, 0x0d, 0x0e, 0x00, 0x66, 0x0b, 0x0a,
++/* E1 */  0x69, 0x44, 0x45, 0x37, 0x46, 0x77, 0x43, 0x42,
++/* E2 */  0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E3 */  0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E4 */  0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E5 */  0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E6 */  0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E7 */  0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++};
++
++/*
++ * A bitfield array which contains the state of the keyboard after the last
++ * scan. A bit set in this array corresponds to a key down. Only the lower
++ * 16 bits of each array element are used.
++ */
++static unsigned long previous_keys[8];
++static unsigned long keys[8];
++
++
++/* This will be set to a non-zero value if a key was found to be pressed
++ * in the last scan. */
++static int key_is_pressed;
++
++static struct tq_struct kbd_process_task;
++static struct timer_list edb7211_kbd_timer;
++
++/*
++ * External methods.
++ */
++void edb7211_kbd_init_hw(void);
++
++/* 
++ * Internal methods.
++ */
++static int edb7211_kbd_scan_matrix(u_long* keys);
++static void edb7211_kbd_timeout(unsigned long data);
++static void edb7211_kbd_process(void* data);
++
++/*
++ * Translate a raw keycode to an XT keyboard scancode.
++ */
++static int
++edb7211_translate(unsigned char scancode, unsigned char *keycode,
++                char raw_mode)
++{
++      *keycode = colrow_2_scancode[scancode & 0x7f];
++      return 1;
++}
++
++/*
++ * Scan the keyboard matrix; for each key that is pressed, set the
++ * corresponding bit in the bitfield array.
++ *
++ * The parameter is expected to be an array of 8 32-bit values. Only the lower
++ * 16 bits of each value is used. Each value contains the row bits for the
++ * corresponding column.
++ */
++static int
++edb7211_kbd_scan_matrix(u_long* keys)
++{
++      int column, row, key_pressed;
++      unsigned char port_a_data, ext_port_data;
++
++      key_pressed = 0;
++
++      /* Drive all the columns low. */
++      clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_LO, 
++              SYSCON1);
++
++      for (column = 0; column < 8; column++) {
++
++              /* Drive the column high. */
++              clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | 
++                          (KBSC_COL0 + column), SYSCON1);
++
++              /* Read port A and the extended port. */
++              port_a_data = clps_readb(PADR) & 0xff;
++              ext_port_data = __raw_readb(EP7211_VIRT_EXTKBD) & 0xff;
++
++              /* Drive all columns tri-state. */
++              clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, 
++                      SYSCON1);
++
++              /* Look at each column in port A. */
++              for (row=0; row < 8; row++) {
++                      /* If the row's bit is set, set the bit in the bitfield.
++                       * Otherwise, clear it. 
++                       */
++                      if (port_a_data & (1 << row)) {
++                              keys[column] |= (1 << row); 
++                              key_pressed = 1;
++                      } else {
++                              keys[column] &= ~(1 << row); 
++                      }
++              }
++
++              /* Look at each column in the extended port. */
++              for (row=0; row < 8; row++) {
++                      /* If the row's bit is set, set the bit in the bitfield.
++                       * Otherwise, clear it. 
++                       */
++                      if (ext_port_data & (1 << row)) {
++                              keys[column] |= (1 << (row + 8)); 
++                              key_pressed = 1;
++                      } else {
++                              keys[column] &= ~(1 << (row + 8)); 
++                      }
++              }
++
++              /* 
++               * Short delay: The example code for the EDB7211 runs an empty
++               * loop 256 times. At this rate, there were some spurious keys
++               * generated. I doubled the delay to let the column drives 
++               * settle some. 
++               */
++              for (row=0; row < 512; row++) { }
++      }
++
++      /* If we could use interrupts, we would drive all columns high so 
++       * that interrupts will be generated on key presses. But we can't,
++       * so we leave all columns floating. 
++       */
++      clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, 
++              SYSCON1);
++
++      return key_pressed;
++}
++
++/* 
++ * XXX: This is really ugly; this needs to be reworked to have less levels of
++ *    indentation.
++ */
++static void
++edb7211_kbd_timeout(unsigned long data)
++{
++      /* Schedule the next timer event. */
++      edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL;
++      add_timer(&edb7211_kbd_timer);
++
++      if (edb7211_kbd_scan_matrix(keys) || key_is_pressed) {
++              queue_task(&kbd_process_task, &tq_timer);
++      } else {
++              key_is_pressed = 0;
++      }
++}
++
++/*
++ * Process the keys that have been pressed. 
++ */
++static void
++edb7211_kbd_process(void* data)
++{
++      int i;
++
++      /* First check if any keys have been released. */
++      if (key_is_pressed) {
++              for (i=0; i < 8; i++) {
++                      if (previous_keys[i]) {
++                              int row;
++
++                              for (row=0; row < 16; row++) {
++                                      if ((previous_keys[i] & (1 << row)) &&
++                                              !(keys[i] & (1 << row))) {
++                                              /* Generate the up event. */
++                                              handle_scancode(
++                                                              (row<<3)+i, 0);
++                                      }
++                              }
++                      }
++              }
++      }
++
++      key_is_pressed = 0;
++
++      /* Now scan the keys and send press events. */
++      for (i=0; i < 8; i++) {
++              if (keys[i]) {
++                      int row;
++
++                      for (row=0; row < 16; row++) {
++                              if (keys[i] & (1 << row)) {
++                                      if (previous_keys[i] & (1 << row)) {
++                                              /* Generate the hold event. */
++                                              handle_scancode((row<<3)+i, 1);
++                                      } else {
++                                              /* Generate the down event. */
++                                              handle_scancode((row<<3)+i, 1);
++                                      }
++
++                                      key_is_pressed = 1;
++                              }
++                      }
++              }
++      }
++
++      /* Update the state variables. */
++      memcpy(previous_keys, keys, 8 * sizeof(unsigned long));
++}
++
++static char edb7211_unexpected_up(unsigned char scancode)
++{
++      return 0200;
++}
++
++static void edb7211_leds(unsigned char leds)
++{
++}
++
++/*
++ * Initialize the keyboard hardware. Set the column drives low and
++ * start the timer.
++ */
++void __init
++edb7211_kbd_init_hw(void)
++{
++      k_translate     = edb7211_translate;
++      k_unexpected_up = edb7211_unexpected_up;
++      k_leds          = edb7211_leds;
++
++      /* 
++       * If we had the ability to use interrupts, we would want to drive all
++       * columns high. But we have more keys than can generate interrupts, so
++       * we leave them floating.
++       */
++      clps_writel((clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) | KBSC_X, 
++              SYSCON1);
++
++      /* Initialize the matrix processing task. */
++      kbd_process_task.routine = edb7211_kbd_process;
++      kbd_process_task.data = NULL;
++
++      /* Setup the timer to poll the keyboard. */
++      init_timer(&edb7211_kbd_timer);
++      edb7211_kbd_timer.function = edb7211_kbd_timeout;
++      edb7211_kbd_timer.data = (unsigned long)NULL;
++      edb7211_kbd_timer.expires = jiffies + KEYBOARD_SCAN_INTERVAL;
++      add_timer(&edb7211_kbd_timer); 
++}
++
++
+--- /dev/null
++++ linux-2.4.27/drivers/char/epxa_wdt.c
+@@ -0,0 +1,178 @@
++/*
++ *    Watchdog driver for the Altera Excalibur EPXA1DB
++ *
++ *      (c) Copyright 2003 Krzysztof Marianski <kmarian@konin.lm.pl>
++ *          Based on SA11x0 Watchdog driver by Oleg Drokin <green@crimea.edu>
++ *
++ *    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 material is provided "AS-IS" and at no charge
++ *
++ *    (c) Copyright 2003 Krzysztof Marianski <kmarian@konin.lm.pl>
++ *
++ *      1/08/2003 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++
++#define WATCHDOG00_TYPE (volatile unsigned int*)
++#include <asm/arch/watchdog00.h>
++#include <asm/bitops.h>
++
++#define TIMER_MARGIN  30              /* (secs) Default is 30 seconds */
++
++static int margin = TIMER_MARGIN;     /* in seconds */
++static int epxa1wdt_users;
++static unsigned char last_written_byte;
++
++#ifdef CONFIG_WATCHDOG_NOWAYOUT
++static int nowayout=1;
++#else
++static int nowayout=0;
++#endif
++
++#ifdef MODULE
++MODULE_PARM(margin,"i");
++MODULE_PARM(nowayout, "i");
++#endif
++
++/*
++ *    Allow only one person to hold it open
++ */
++
++static int epxa1dog_open(struct inode *inode, struct file *file)
++{
++      if(test_and_set_bit(1,&epxa1wdt_users))
++              return -EBUSY;
++
++      /* Reset the Watchdog, just to be sure we don't set
++          a value close to actual value of WDOG_COUNT register */
++      *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1;
++      *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2;
++
++      /* Activate EPXA1DB Watchdog timer */
++      *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK;
++
++      last_written_byte = 'V'; //in case user opens it only to ioctl
++      return 0;
++}
++
++static int epxa1dog_release(struct inode *inode, struct file *file)
++{
++      /*
++       *      Shut off the timer and set lock bit when no special
++       *      character 'V' was last written
++       */
++
++      if ((last_written_byte != 'V') && (nowayout)) {
++              *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)) |= WDOG_CR_LK_MSK;
++              printk("No special character 'V' was written to Watchdog just before closing it\n");
++              printk("WATCHDOG LOCKED - Reboot expected!!!\n");
++      } else
++              *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))=0;
++
++      epxa1wdt_users = 0;
++
++      return 0;
++}
++
++static ssize_t epxa1dog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
++{
++      /*  Can't seek (pwrite) on this device  */
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++
++      /* Reset Watchdog timer. */
++      if(len) {
++              *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1;
++              *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2;
++              last_written_byte = *data;
++              return 1;
++      }
++      return 0;
++}
++
++static int epxa1dog_ioctl(struct inode *inode, struct file *file,
++      unsigned int cmd, unsigned long arg)
++{
++      static struct watchdog_info ident = {
++              identity: "EPXA Watchdog",
++      };
++
++      switch(cmd){
++      default:
++              return -ENOIOCTLCMD;
++      case WDIOC_GETSUPPORT:
++              return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
++//    case WDIOC_GETSTATUS: //TODO
++//            return put_user(0,(int *)arg);
++//    case WDIOC_GETBOOTSTATUS: //TODO
++//            return 0;
++      case WDIOC_KEEPALIVE:
++              *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_1;
++              *WDOG_RELOAD(IO_ADDRESS(EXC_WATCHDOG00_BASE))=WDOG_RELOAD_MAGIC_2;
++              return 0;
++      case WDIOC_SETTIMEOUT:
++              *WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE))= (EXC_INPUT_CLK_FREQUENCY * margin) & WDOG_CR_TRIGGER_MSK;
++              return 0;
++      case WDIOC_GETTIMEOUT:
++              return put_user( ((*WDOG_CR(IO_ADDRESS(EXC_WATCHDOG00_BASE)))/EXC_INPUT_CLK_FREQUENCY), (int*)arg);
++      }
++}
++
++static struct file_operations epxa1dog_fops = {
++      .owner          = THIS_MODULE,
++      .write          = epxa1dog_write,
++      .ioctl          = epxa1dog_ioctl,
++      .open           = epxa1dog_open,
++      .release        = epxa1dog_release,
++};
++
++static struct miscdevice epxa1dog_miscdev=
++{
++      .minor  = WATCHDOG_MINOR,
++      .name   = "EPXA watchdog",
++      .fops   = &epxa1dog_fops
++};
++
++static int __init epxa1dog_init(void)
++{
++      int ret;
++
++      ret = misc_register(&epxa1dog_miscdev);
++
++      if (ret)
++              return ret;
++
++      printk("EPXA Watchdog Timer: timer margin %d sec\n", margin);
++      printk("EPXA Watchdog Timer: no way out is %s\n", nowayout ? "enabled" : "disabled");
++
++      return 0;
++}
++
++static void __exit epxa1dog_exit(void)
++{
++      misc_deregister(&epxa1dog_miscdev);
++}
++
++module_init(epxa1dog_init);
++module_exit(epxa1dog_exit);
++
++MODULE_AUTHOR("Krzysztof Marianski <kmarian@konin.lm.pl>");
++MODULE_DESCRIPTION("EPXA Watchdog Timer");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/char/gc_kbmap.h
+@@ -0,0 +1,162 @@
++
++
++#define KK_NONE               0x7f
++#define KK_ESC                0x00
++#define KK_F1         0x01
++#define KK_F2         0x02
++#define KK_F3         0x03
++#define KK_F4         0x04
++#define KK_F5         0x05
++#define KK_F6         0x06
++#define KK_F7         0x07
++#define KK_F8         0x08
++#define KK_F9         0x09
++#define KK_F10                0x0a
++#define KK_F11                0x0b
++#define KK_F12                0x0c
++#define KK_PRNT               0x0d
++#define KK_SCRL               0x0e
++#define KK_BRK                0x0f
++#define KK_AGR                0x10
++#define KK_1          0x11
++#define KK_2          0x12
++#define KK_3          0x13
++#define KK_4          0x14
++#define KK_5          0x15
++#define KK_6          0x16
++#define KK_7          0x17
++#define KK_8          0x18
++#define KK_9          0x19
++#define KK_0          0x1a
++#define KK_MINS               0x1b
++#define KK_EQLS               0x1c
++#define KK_BKSP               0x1e
++#define KK_INS                0x1f
++#define KK_HOME               0x20
++#define KK_PGUP               0x21
++#define KK_NUML               0x22
++#define KP_SLH                0x23
++#define KP_STR                0x24
++#define KP_MNS                0x3a
++#define KK_TAB                0x26
++#define KK_Q          0x27
++#define KK_W          0x28
++#define KK_E          0x29
++#define KK_R          0x2a
++#define KK_T          0x2b
++#define KK_Y          0x2c
++#define KK_U          0x2d
++#define KK_I          0x2e
++#define KK_O          0x2f
++#define KK_P          0x30
++#define KK_LSBK               0x31
++#define KK_RSBK               0x32
++#define KK_ENTR               0x47
++#define KK_DEL                0x34
++#define KK_END                0x35
++#define KK_PGDN               0x36
++#define KP_7          0x37
++#define KP_8          0x38
++#define KP_9          0x39
++#define KP_PLS                0x4b
++#define KK_CAPS               0x5d
++#define KK_A          0x3c
++#define KK_S          0x3d
++#define KK_D          0x3e
++#define KK_F          0x3f
++#define KK_G          0x40
++#define KK_H          0x41
++#define KK_J          0x42
++#define KK_K          0x43
++#define KK_L          0x44
++#define KK_SEMI               0x45
++#define KK_SQOT               0x46
++#define KK_HASH               0x1d
++#define KP_4          0x48
++#define KP_5          0x49
++#define KP_6          0x4a
++#define KK_LSFT               0x4c
++#define KK_BSLH               0x33
++#define KK_Z          0x4e
++#define KK_X          0x4f
++#define KK_C          0x50
++#define KK_V          0x51
++#define KK_B          0x52
++#define KK_N          0x53
++#define KK_M          0x54
++#define KK_COMA               0x55
++#define KK_DOT                0x56
++#define KK_FSLH               0x57
++#define KK_RSFT               0x58
++#define KK_UP         0x59
++#define KP_1          0x5a
++#define KP_2          0x5b
++#define KP_3          0x5c
++#define KP_ENT                0x67
++#define KK_LCTL               0x3b
++#define KK_LALT               0x5e
++#define KK_SPCE               0x5f
++#define KK_RALT               0x60
++#define KK_RCTL               0x61
++#define KK_LEFT               0x62
++#define KK_DOWN               0x63
++#define KK_RGHT               0x64
++#define KP_0          0x65
++#define KP_DOT                0x66
++
++static char kbmap[128] = {
++KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_AGR,  KK_BSLH, KK_TAB,  KK_Z,    KK_A,    KK_X,    KK_NONE,
++KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, 0x21,   KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_ESC,  KK_DEL,  KK_Q,    KK_CAPS, KK_S,    KK_C,    KK_3,
++KK_NONE, KK_1,    KK_NONE, KK_W,    KK_NONE, KK_D,    KK_V,    KK_4,
++KK_NONE, KK_2,    KK_T,    KK_E,    KK_NONE, KK_F,    KK_B,    KK_5,
++KK_NONE, KK_9,    KK_Y,    KK_R,    KK_K,    KK_G,    KK_N,    KK_6,
++KK_NONE, KK_0,    KK_U,    KK_O,    KK_L,    KK_H,    KK_M,    KK_7,
++KK_NONE, KK_MINS, KK_I,    KK_P,    KK_SEMI, KK_J,    KK_COMA, KK_8,
++KK_NONE, KK_EQLS, KK_ENTR, KK_LSBK, KK_BSLH, KK_FSLH, KK_DOT,  KK_NONE,
++KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_BKSP, KK_DOWN, KK_RSBK, KK_UP,   KK_LEFT, KK_SPCE, KK_RGHT,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE};
++
++static char kbmapFN[128] = {
++KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE,   0x21, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F3,
++KK_NONE, KK_F1,   KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F4,
++KK_NONE, KK_F2,   KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F5,
++KK_NONE, KK_F9,   KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F6,
++KK_NONE, KK_F10,  KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F7,
++KK_NONE, KK_NUML, KK_NONE, KK_INS,  KK_PRNT, KK_NONE, KK_NONE, KK_F8,
++KK_NONE, KK_BRK,  KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_PGDN, KK_SCRL, KK_PGUP, KK_HOME, KK_NONE, KK_END,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE};
++
++static char kbmapNL[128] = {
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KP_9,   KK_NONE, KK_NONE, KP_2,   KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KP_STR, KP_4,   KP_6,   KP_3,   KK_NONE, KP_0,   KP_7,
++KK_NONE, KK_NONE, KP_5,   KP_MNS, KP_PLS, KP_1,   KK_NONE, KP_8,
++KK_NONE, KK_NONE, KP_ENT, KK_NONE, KK_NONE, KP_SLH, KP_DOT, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE,
++KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE};
++
++
++
+--- /dev/null
++++ linux-2.4.27/drivers/char/gc_keyb.c
+@@ -0,0 +1,1145 @@
++/*
++ * linux/arch/arm/drivers/char/gc_keyb.c
++ *
++ * Copyright 2000 Applied Data Systems
++ *
++ * Keyboard & Smartio driver for GraphicsClient ARM Linux.
++ * Graphics Client is SA1110 based single board computer by
++ *    Applied Data Systems (http://www.applieddata.net)
++ *
++ * Change log:
++ *    7-10/6/01 Thomas Thaele <tthaele@papenmeier.de>
++ *       - Added Keyboard Sniffer on /dev/sio12 <minor = 12>
++ *       - First implementation of PC- compatible Scancodes (thanks to pc_keyb.c)
++ *       3/23/01 Woojung Huh
++ *          Power Management added
++ *            12/01/00 Woojung Huh
++ *                    Bug fixed
++ *            11/16/00 Woojung Huh [whuh@applieddata.net]
++ *                    Added smartio device driver on it
++ */
++
++/*
++ * Introduced setkeycode, ketkeycode for the GC+ by Thomas Thaele
++ * <tthaele@papenmeier.de> GC+ now performs like a real PC on the keyboard.
++ * Warning: this code is still beta! PrntScrn and Pause keys are not
++ * completely tested and implemented!!! Keyboard driver can be confused
++ * by hacking like crazy on the keyboard. (hardware problem on serial line?)
++ */
++
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/kbd_ll.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/kbd_kern.h>
++
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/keyboard.h>
++#include <linux/tqueue.h>
++#include <linux/proc_fs.h>
++#include <linux/pm.h>
++
++#define ADS_AVR_IRQ   63
++
++#define       SMARTIO_IOCTL_BASES             's'
++#define       SMARTIO_KPD_TIMEOUT             _IOW(SMARTIO_IOCTL_BASES, 0, int)
++#define       SMARTIO_KPD_SETUP               _IOW(SMARTIO_IOCTL_BASES, 1, short)
++#define       SMARTIO_BL_CONTROL              _IOW(SMARTIO_IOCTL_BASES, 2, char)
++#define       SMARTIO_BL_CONTRAST             _IOW(SMARTIO_IOCTL_BASES, 3, char)
++#define SMARTIO_PORT_CONFIG           _IOW(SMARTIO_IOCTL_BASES, 4, char)
++#define SMARTIO_SNIFFER_TIMEOUT               _IOW(SMARTIO_IOCTL_BASES, 5, long)
++
++
++/* Simple translation table for the SysRq keys */
++
++#ifdef CONFIG_MAGIC_SYSRQ
++unsigned char pckbd_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 */
++#endif
++
++/*
++ * 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
++
++/*
++ * The keycodes below are randomly located in 89-95,112-118,120-127.
++ * They could be thrown away (and all occurrences below replaced by 0),
++ * but that would force many users to use the `setkeycodes' utility, where
++ * they needed not before. It does not matter that there are duplicates, as
++ * long as no duplication occurs for any single keyboard.
++ */
++#define SC_LIM 89
++
++#define FOCUS_PF1 85           /* actual code! */
++#define FOCUS_PF2 89
++#define FOCUS_PF3 90
++#define FOCUS_PF4 91
++#define FOCUS_PF5 92
++#define FOCUS_PF6 93
++#define FOCUS_PF7 94
++#define FOCUS_PF8 95
++#define FOCUS_PF9 120
++#define FOCUS_PF10 121
++#define FOCUS_PF11 122
++#define FOCUS_PF12 123
++
++#define JAP_86     124
++/* tfj@olivia.ping.dk:
++ * The four keys are located over the numeric keypad, and are
++ * labelled A1-A4. It's an rc930 keyboard, from
++ * Regnecentralen/RC International, Now ICL.
++ * Scancodes: 59, 5a, 5b, 5c.
++ */
++#define RGN1 124
++#define RGN2 125
++#define RGN3 126
++#define RGN4 127
++
++static unsigned char high_keys[128 - SC_LIM] = {
++  RGN1, RGN2, RGN3, RGN4, 0, 0, 0,                   /* 0x59-0x5f */
++  0, 0, 0, 0, 0, 0, 0, 0,                            /* 0x60-0x67 */
++  0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,          /* 0x68-0x6f */
++  0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3,    /* 0x70-0x77 */
++  FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,        /* 0x78-0x7b */
++  FOCUS_PF8, JAP_86, FOCUS_PF10, 0                   /* 0x7c-0x7f */
++};
++
++/* BTC */
++#define E0_MACRO   112
++/* LK450 */
++#define E0_F13     113
++#define E0_F14     114
++#define E0_HELP    115
++#define E0_DO      116
++#define E0_F17     117
++#define E0_KPMINPLUS 118
++/*
++ * My OmniKey generates e0 4c for  the "OMNI" key and the
++ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
++ */
++#define E0_OK 124
++/*
++ * 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
++#define E0_MSRW       126
++#define E0_MSTM       127
++
++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, E0_KPENTER, 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, E0_KPSLASH, 0, E0_PRSCR,           /* 0x30-0x37 */
++  E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,             /* 0x38-0x3f */
++  E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,             /* 0x40-0x47 */
++  E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
++  E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,             /* 0x50-0x57 */
++  0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,         /* 0x58-0x5f */
++  0, 0, 0, 0, 0, 0, 0, 0,                           /* 0x60-0x67 */
++  0, 0, 0, 0, 0, 0, 0, E0_MACRO,                    /* 0x68-0x6f */
++  0, 0, 0, 0, 0, 0, 0, 0,                           /* 0x70-0x77 */
++  0, 0, 0, 0, 0, 0, 0, 0                            /* 0x78-0x7f */
++};
++
++int gc_kbd_setkeycode(unsigned int scancode, unsigned int keycode)
++{
++      if (scancode < SC_LIM || scancode > 255 || keycode > 127)
++        return -EINVAL;
++      if (scancode < 128)
++        high_keys[scancode - SC_LIM] = keycode;
++      else
++        e0_keys[scancode - 128] = keycode;
++      return 0;
++}
++
++int gc_kbd_getkeycode(unsigned int scancode)
++{
++      return
++        (scancode < SC_LIM || scancode > 255) ? -EINVAL :
++        (scancode < 128) ? high_keys[scancode - SC_LIM] :
++          e0_keys[scancode - 128];
++}
++
++int gc_kbd_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;
++      }
++
++      /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
++      if (scancode == 0x00 || scancode == 0xff) {
++              prev_scancode = 0;
++              return 0;
++      }
++
++      scancode &= 0x7f;
++
++      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;
++            /*
++             *  The keyboard maintains its own internal caps lock and
++             *  num lock statuses. In caps lock mode E0 AA precedes make
++             *  code and E0 2A follows break code. In num lock mode,
++             *  E0 2A precedes make code and E0 AA follows break code.
++             *  We do our own book-keeping, so we will just ignore these.
++             */
++            /*
++             *  For my keyboard there is no caps lock mode, but there are
++             *  both Shift-L and Shift-R modes. The former mode generates
++             *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
++             *  So, we should also ignore the latter. - aeb@cwi.nl
++             */
++            if (scancode == 0x2a || scancode == 0x36)
++              return 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 if (scancode >= SC_LIM) {
++          /* This happens with the FOCUS 9000 keyboard
++             Its keys PF1..PF12 are reported to generate
++             55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
++             Moreover, unless repeated, they do not generate
++             key-down events, so we have to zero up_flag below */
++          /* Also, Japanese 86/106 keyboards are reported to
++             generate 0x73 and 0x7d for \ - and \ | respectively. */
++          /* Also, some Brazilian keyboard is reported to produce
++             0x73 and 0x7e for \ ? and KP-dot, respectively. */
++
++        *keycode = high_keys[scancode - SC_LIM];
++
++        if (!*keycode) {
++            if (!raw_mode) {
++#ifdef KBD_REPORT_UNKN
++                printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"
++                       " - ignored\n", scancode);
++#endif
++            }
++            return 0;
++        }
++      } else
++        *keycode = scancode;
++      return 1;
++}
++
++// this table converts the hardware dependent codes of a MF-2 Keyboard to
++// the codes normally comming out of a i8042. This table is 128 Bytes too
++// big, but for stability reasons it should be kept like it is!
++// There is no range checking in the code!
++static int mf_two_kbdmap[256] = {
++      00, 67, 65, 63, 61, 59, 60, 88, 00, 68, 66, 64, 62, 15, 41, 00,
++      00, 56, 42, 00, 29, 16, 02, 00, 00, 00, 44, 31, 30, 17, 03, 00,
++      00, 46, 45, 32, 18, 05, 04, 00, 00, 57, 47, 33, 20, 19, 06, 00,
++      00, 49, 48, 35, 34, 21,  7, 00, 00, 00, 50, 36, 22,  8,  9, 00,
++      00, 51, 37, 23, 24, 11, 10, 00, 00, 52, 53, 38, 39, 25, 12, 00,
++      00, 00, 40, 00, 26, 13, 00, 00, 58, 54, 28, 27, 00, 43, 00, 00,
++      00, 86, 00, 00, 00, 00, 14, 00, 00, 79, 00, 75, 71, 00, 00, 00,
++      82, 83, 80, 76, 77, 72, 01, 69, 87, 78, 81, 74, 55, 73, 70, 00,
++      00, 00, 00, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
++      00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 };
++
++
++// some texts displayed by the proc_file_system
++static char *kbd_sniff[2] = { "off", "on" };
++static char *kbd_sniff_mode[2] = { "passive", "active" };
++
++#define PASSIVE 0
++#define ACTIVE  1
++
++// is the sniffer active (1) or inactive (0)
++static int  SNIFFER = 0;
++// do we get a copy (SNIFFMODE = PASSIVE) or do we get the original data (SNIFFMODE = ACTIVE)
++// and have to reinsert the data
++static int  SNIFFMODE = PASSIVE;
++
++// we allow only one process to sniff
++static int sniffer_in_use = 0;
++
++// timeout for the keyboard sniffer -1 = blocking, otherwise timeout in msecs
++static long sniffer_timeout = -1;
++
++// the value we sniffed from the keyboard
++static int sniffed_value;
++
++static char *smartio_version = "1.02 MF-II compatibility patch <tthaele@papenmeier.de>";
++static char *smartio_date = "Aug-27-2001";
++
++static int sio_reset_flag;
++static int kbd_press_flag;
++
++static void send_SSP_msg(unchar *pBuf, int num)
++{
++      ushort  tmp;
++      int             i;
++
++      for (i=0;i<num;i++) {
++              while ((Ser4SSSR & SSSR_TNF) == 0);
++              tmp = pBuf[i];
++              Ser4SSDR = (tmp << 8);
++      }
++
++      // Throw away Echo
++      for (i=0;i<num;i++) {
++              while ((Ser4SSSR & SSSR_RNE) == 0);
++              tmp = Ser4SSDR;
++      }
++}
++
++static unchar ReadSSPByte(void)
++{
++      if (Ser4SSSR & SSSR_ROR) {
++              printk("%s() : Overrun\n", __FUNCTION__);
++              return 0;
++      }
++
++      Ser4SSDR = 0x00;
++
++      while ((Ser4SSSR & SSSR_RNE) == 0);
++
++      return ((unchar) Ser4SSDR);
++}
++
++static ulong read_SSP_response(int num)
++{
++      int             i;
++      ulong   ret;
++
++      // discard leading 0x00 and command echo 0 (command group value)
++      while (ReadSSPByte() == 0);
++      // discard command echo 1 (command code value)
++      ReadSSPByte();
++
++      // data from SMARTIO
++      // It assumes LSB first.
++      // NOTE:Some command uses MSB first order
++      ret = 0;
++      for (i=0;i<num;i++) {
++              ret |= ReadSSPByte() << (8*i);
++      }
++
++      return ret;
++}
++
++typedef       struct  t_SMARTIO_CMD {
++      unchar  Group;
++      unchar  Code;
++      unchar  Opt[2];
++}     SMARTIO_CMD;
++
++static        SMARTIO_CMD RD_INT_CMD = { 0x83, 0x01, { 0x00, 0x00 } };
++static        SMARTIO_CMD RD_KBD_CMD = { 0x83, 0x02, { 0x00, 0x00 } };
++static        SMARTIO_CMD RD_ADC_CMD = { 0x83, 0x28, { 0x00, 0x00 } };
++static        SMARTIO_CMD RD_KPD_CMD = { 0x83, 0x04, { 0x00, 0x00 } };
++
++static        volatile ushort adc_value;
++static        volatile unchar kpd_value;
++static        unsigned int    kpd_timeout = 10000;                    // 10000 msec
++
++static  ulong kbd_int, kpd_int, adc_int;
++
++static void smartio_interrupt_task(void *data);
++
++static struct tq_struct tq_smartio = {
++              { NULL, NULL },         // struct list_head
++              0,                      // unsigned long sync
++              smartio_interrupt_task, // void (*routine)(void *)
++              NULL,                   // void *data
++};
++
++DECLARE_WAIT_QUEUE_HEAD(smartio_queue);
++DECLARE_WAIT_QUEUE_HEAD(smartio_adc_queue);
++DECLARE_WAIT_QUEUE_HEAD(smartio_kpd_queue);
++DECLARE_WAIT_QUEUE_HEAD(keyboard_done_queue);
++DECLARE_WAIT_QUEUE_HEAD(sniffer_queue);
++
++static spinlock_t smartio_busy_lock = SPIN_LOCK_UNLOCKED;
++static atomic_t       smartio_busy = ATOMIC_INIT(0);
++
++static int f_five_pressed = 0;
++static int f_seven_pressed = 0;
++//static int e_null_counter = 0;
++//static int f_null_counter = 0;
++//static int keydown = 0;
++static unchar previous_code = 0;
++//static int e0 = 0;
++
++static void smartio_interrupt_task(void *arg)
++{
++      unchar  code;
++      unsigned long flags;
++      unchar  dummy;
++
++      spin_lock_irqsave(&smartio_busy_lock, flags);
++      if (atomic_read(&smartio_busy) == 1) {
++              spin_unlock_irqrestore(&smartio_busy_lock, flags);
++              queue_task(&tq_smartio, &tq_timer);
++      }
++      else {
++              atomic_set(&smartio_busy, 1);
++              spin_unlock_irqrestore(&smartio_busy_lock, flags);
++      }
++
++      /* Read SMARTIO Interrupt Status to check which Interrupt is occurred
++       * and Clear SMARTIO Interrupt */
++      send_SSP_msg((unchar *) &RD_INT_CMD, 2);
++      code = (unchar) (read_SSP_response(1) & 0xFF);
++
++#ifdef CONFIG_VT
++      if (code  & 0x04) {                                     // Keyboard Interrupt
++              kbd_int++;
++              /* Read Scan code */
++              send_SSP_msg((unchar *) &RD_KBD_CMD, 2);
++              code = (unchar) (read_SSP_response(1) & 0xFF);
++              dummy = code & 0x80;
++              if ((code == 0xE0) || (code == 0xE1) || (code == 0xF0)) {       // combined code
++                      if (code == 0xF0) {
++                              if (!previous_code) {
++                                      code = 0xE0;
++                                      previous_code = 0xF0;
++                              } else {
++                                      code = mf_two_kbdmap[code & 0x7F] | dummy;
++                                      previous_code = 0;
++                              }
++                      } else if (code == 0xE0) {
++                              if (previous_code != 0) {
++                                      code = mf_two_kbdmap[code & 0x7F] | dummy;
++                                      previous_code = 0;
++                              } else previous_code = code;
++                      } else {                                                // 0xE1
++                              if (!previous_code) {
++                                      code = mf_two_kbdmap[code &0x7F] | dummy;
++                                      previous_code = 0;
++                              } else {
++                                      previous_code = code;
++                              }
++                      }
++              } else {
++                      if (code == 0x03) {
++                              f_five_pressed = 1;
++                      } else if (code == 0x83) {
++                              if (f_five_pressed != 0) {
++                                      f_five_pressed = 0;
++                                      code = 0x03;
++                              } else if (f_seven_pressed == 0) {
++                                      f_seven_pressed = 1;
++                                      code = 2;
++                                      dummy = 0;
++                              } else {
++                                      f_seven_pressed = 0;
++                                      code = 2;
++                              }
++                      }
++                      previous_code = 0;
++                      code &= 0x7F;
++                      code = mf_two_kbdmap[code] | dummy;
++              }
++              sniffed_value = (ushort)code;
++              if (SNIFFER) wake_up_interruptible(&sniffer_queue);
++              if (SNIFFMODE == PASSIVE) {
++                      handle_scancode( code, (code & 0x80) ? 0 : 1 );
++                      if (code & 0x80) {
++                              wake_up_interruptible(&keyboard_done_queue);
++                              mdelay(10);             // this makes the whole thing a bit more stable
++                                                      // keyboard handling can be corrupted when hitting
++                                                      // thousands of keys like crazy. kbd_translate might catch up
++                                                      // with irq routine? or there is simply a buffer overflow on
++                                                      // the serial device? somehow it looses some key sequences.
++                                                      // if a break code is lost or coruppted the keyboard starts
++                                                      // to autorepeat like crazy and appears to hang.
++                                                      // this needs further investigations! Thomas
++                              kbd_press_flag = 0;
++                      }
++                      else
++                              kbd_press_flag = 1;
++              }
++              code = 0;       // prevent furthermore if ... then to react!
++      }
++#endif
++      // ADC resolution is 10bit (0x000 ~ 0x3FF)
++      if (code & 0x02) {                                      // ADC Complete Interrupt
++              adc_int++;
++              send_SSP_msg((unchar *) &RD_ADC_CMD, 2);
++              adc_value = (ushort) (read_SSP_response(2) & 0x3FF);
++              wake_up_interruptible(&smartio_adc_queue);
++      }
++
++      if (code & 0x08) {                                      // Keypad interrupt
++              kpd_int++;
++              send_SSP_msg((unchar *) &RD_KPD_CMD, 2);
++              kpd_value = (unchar) (read_SSP_response(1) & 0xFF);
++              wake_up_interruptible(&smartio_kpd_queue);
++      }
++
++      spin_lock_irqsave(&smartio_busy_lock, flags);
++      atomic_set(&smartio_busy, 0);
++      spin_unlock_irqrestore(&smartio_busy_lock, flags);
++
++      enable_irq(ADS_AVR_IRQ);
++
++      wake_up_interruptible(&smartio_queue);
++}
++
++static void gc_sio_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++#ifdef CONFIG_VT
++      kbd_pt_regs = regs;
++#endif
++
++      // *NOTE*
++      // ADS SMARTIO interrupt is cleared after reading interrupt status
++      // from smartio.
++      // disable SMARTIO IRQ here and re-enable at samrtio_bh.
++      // 11/13/00 Woojung
++      disable_irq(ADS_AVR_IRQ);
++
++      queue_task(&tq_smartio, &tq_immediate);
++      mark_bh(IMMEDIATE_BH);
++}
++
++char gc_kbd_unexpected_up(unsigned char keycode)
++{
++      return 0;
++}
++
++static inline void gc_sio_init(void)
++{
++      GPDR |= (GPIO_GPIO10 | GPIO_GPIO12 | GPIO_GPIO13);      // Output
++      GPDR &= ~GPIO_GPIO11;
++
++      // Alternative Function
++      GAFR |= (GPIO_GPIO10 | GPIO_GPIO11 | GPIO_GPIO12 | GPIO_GPIO13);
++
++      Ser4SSCR0 = 0xA707;
++      Ser4SSSR = SSSR_ROR;
++      Ser4SSCR1 = 0x0010;
++      Ser4SSCR0 = 0xA787;
++
++      // Reset SMARTIO
++      ADS_AVR_REG &= 0xFE;
++      mdelay(300);                    // 10 mSec
++      ADS_AVR_REG |= 0x01;
++      mdelay(10);                     // 10 mSec
++
++}
++
++void __init gc_kbd_init_hw(void)
++{
++      printk (KERN_INFO "Graphics Client keyboard driver v1.0\n");
++
++      k_setkeycode    = gc_kbd_setkeycode;
++      k_getkeycode    = gc_kbd_getkeycode;
++      k_translate     = gc_kbd_translate;
++      k_unexpected_up = gc_kbd_unexpected_up;
++#ifdef CONFIG_MAGIC_SYSRQ
++      k_sysrq_key     = 0x54;
++      /* sysrq table??? --rmk */
++#endif
++
++      gc_sio_init();
++
++      if (request_irq(ADS_AVR_IRQ,gc_sio_interrupt,0,"smartio", NULL) != 0)
++              printk("Could not allocate SMARTIO IRQ!\n");
++
++      sio_reset_flag = 1;
++}
++
++/* SMARTIO ADC Interface */
++#define SMARTIO_VERSION                               0
++#define SMARTIO_PORT_A                                1
++#define SMARTIO_PORT_B                                2
++#define SMARTIO_PORT_C                                3
++#define SMARTIO_PORT_D                                4
++#define SMARTIO_SELECT_OPTION                 5
++#define SMARTIO_BACKLITE                      6
++#define SMARTIO_KEYPAD                                7
++#define SMARTIO_ADC                           8
++#define       SMARTIO_VEE_PWM                         9
++#define SMARTIO_SLEEP                         11
++#define SMARTIO_KBD_SNIFFER                   12
++
++static        SMARTIO_CMD CONV_ADC_CMD = { 0x80, 0x28, { 0x00, 0x00 } };
++static        SMARTIO_CMD READ_PORT_CMD = { 0x82, 0x00, { 0x00, 0x00 } };
++
++static        SMARTIO_CMD READ_DEVVER_CMD = { 0x82, 0x05, { 0x00, 0x00 } };
++static        SMARTIO_CMD READ_DEVTYPE_CMD = { 0x82, 0x06, { 0x00, 0x00 } };
++static        SMARTIO_CMD READ_FWLEVEL_CMD = { 0x82, 0x07, { 0x00, 0x00 } };
++
++static int lock_smartio(unsigned long *flags)
++{
++      spin_lock_irqsave(&smartio_busy_lock, *flags);
++      if (atomic_read(&smartio_busy) == 1) {
++              spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++              interruptible_sleep_on(&smartio_queue);
++      }
++      else {
++              atomic_set(&smartio_busy, 1);
++              spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++      }
++
++      return 1;
++}
++
++static int unlock_smartio(unsigned long *flags)
++{
++      spin_lock_irqsave(&smartio_busy_lock, *flags);
++      atomic_set(&smartio_busy, 0);
++      spin_unlock_irqrestore(&smartio_busy_lock, *flags);
++
++      return 1;
++}
++
++static ushort read_sio_adc(int channel)
++{
++      unsigned long   flags;
++
++      if ((channel < 0) || (channel > 7))
++              return 0xFFFF;
++
++      CONV_ADC_CMD.Opt[0] = (unchar) channel;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &CONV_ADC_CMD, 3);
++      unlock_smartio(&flags);
++
++      interruptible_sleep_on(&smartio_adc_queue);
++
++      return adc_value & 0x3FF;
++}
++
++static ushort read_sio_port(int port)
++{
++      unsigned long   flags;
++      ushort                  ret;
++
++      if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D))
++              return 0xFFFF;
++
++      READ_PORT_CMD.Code = (unchar) port;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &READ_PORT_CMD, 2);
++      ret = read_SSP_response(1);
++      unlock_smartio(&flags);
++
++      return ret;
++}
++
++static ushort read_sio_kpd(void)
++{
++      long    timeout;
++
++      // kpd_timeout is mSec order
++      // interrupt_sleep_on_timeout is based on 10msec timer tick
++      if (kpd_timeout == -1) {
++              interruptible_sleep_on(&smartio_kpd_queue);
++      }
++      else {
++              timeout = interruptible_sleep_on_timeout(&smartio_kpd_queue,
++                                                              kpd_timeout/10);
++              if (timeout == 0) {
++                      // timeout without keypad input
++                      return 0xFFFF;
++              }
++      }
++      return kpd_value;
++}
++
++static ushort read_sio_sniff(void)
++{
++        long    timeout;
++
++        // kpd_timeout is mSec order
++        // interrupt_sleep_on_timeout is based on 10msec timer tick
++        if (sniffer_timeout == -1) {
++                interruptible_sleep_on(&sniffer_queue);
++        }
++        else {
++                timeout = interruptible_sleep_on_timeout(&sniffer_queue,
++                                                                sniffer_timeout/10);
++                if (timeout == 0) {
++                        // timeout without keypad input
++                        return -1;
++                }
++        }
++        return (ushort)sniffed_value;
++}
++
++static struct sio_ver {
++      uint    DevVer;
++      uint    DevType;
++      uint    FwLevel;
++};
++
++static ushort read_sio_version(struct sio_ver *ptr)
++{
++      unsigned long   flags;
++      ushort          ret;
++
++      // Read Device Version
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &READ_DEVVER_CMD, 2);
++      ret = read_SSP_response(1);
++      unlock_smartio(&flags);
++      ptr->DevVer = (uint)ret;
++      // Read Device Type
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &READ_DEVTYPE_CMD, 2);
++      ret = read_SSP_response(2);
++      unlock_smartio(&flags);
++      // swap MSB & LSB
++      ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8);
++      ptr->DevType = (uint)ret;
++      // Read Firmware Level
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &READ_FWLEVEL_CMD, 2);
++      ret = read_SSP_response(2);
++      unlock_smartio(&flags);
++      // swap MSB & LSB
++      ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8);
++      ptr->FwLevel = (uint)ret;
++
++      return 0;
++}
++
++static ssize_t sio_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      unsigned int minor = MINOR(inode->i_rdev);
++      ushort  *ret = (ushort *)buf;
++
++      switch (minor) {
++      case    SMARTIO_ADC:
++                      if ((*ret = read_sio_adc(buf[0])) != 0xFFFF)
++                              return sizeof(ushort);                                  // 2 bytes
++      case    SMARTIO_PORT_B:
++      case    SMARTIO_PORT_C:
++      case    SMARTIO_PORT_D:
++                      if ((*ret = read_sio_port(minor)) != 0xFFFF)
++                              return sizeof(ushort);
++      case    SMARTIO_VERSION:
++                      if ((read_sio_version((struct sio_ver *)buf)) != 0xFFFF)
++                              return sizeof(struct sio_ver);
++      case    SMARTIO_KEYPAD:
++                      if ((*ret = read_sio_kpd()) != 0xFFFF)
++                              return sizeof(ushort);
++      case    SMARTIO_KBD_SNIFFER:
++                      if ((*ret = read_sio_sniff()) != (ushort)-1)
++                              return 1;
++      default :
++                      return -ENXIO;
++      }
++}
++
++static        SMARTIO_CMD WRITE_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } };
++static        SMARTIO_CMD SELECT_OPT_CMD = { 0x80, 0x00, { 0x00, 0x00 } };
++static        SMARTIO_CMD CONTROL_BL_CMD = { 0x80, 0x00, { 0x00, 0x00 } };
++static        SMARTIO_CMD CONTRAST_BL_CMD = { 0x80, 0x21, { 0x00, 0x00 } };
++static        SMARTIO_CMD CONTROL_KPD_CMD = { 0x80, 0x27, { 0x00, 0x00 } };
++static        SMARTIO_CMD CONTROL_VEE_CMD = { 0x80, 0x22, { 0x00, 0x00 } };
++
++static ushort write_sio_port(int port, unchar value)
++{
++      unsigned long   flags;
++
++      if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D))
++              return 0xFFFF;
++
++      WRITE_PORT_CMD.Code = (unchar) port;
++      WRITE_PORT_CMD.Opt[0] = (unchar) value;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &WRITE_PORT_CMD, 3);
++      unlock_smartio(&flags);
++
++      return 0;
++}
++
++static ushort write_sio_select(unchar select)
++{
++      unsigned long   flags;
++
++      if ((select < 1) || (select > 2))
++              return 0xFFFF;
++
++      SELECT_OPT_CMD.Code = (unchar) (select + 0x28);
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &SELECT_OPT_CMD, 2);
++      unlock_smartio(&flags);
++
++      return 0;
++}
++
++static ushort control_sio_backlite(int cmd, int value)
++{
++      unsigned long   flags;
++
++      if (cmd == SMARTIO_BL_CONTRAST) {
++              value &= 0xFF;
++              CONTRAST_BL_CMD.Opt[0] = (unchar) value;
++
++              lock_smartio(&flags);
++              send_SSP_msg((unchar *) &CONTRAST_BL_CMD, 3);
++              unlock_smartio(&flags);
++      }
++      else if (cmd == SMARTIO_BL_CONTROL) {
++              if (value == 0x00) {
++                      // Backlite OFF
++                      CONTROL_BL_CMD.Code = 0x24;
++              }
++              else {
++                      // Backlite ON
++                      CONTROL_BL_CMD.Code = 0x23;
++              }
++              lock_smartio(&flags);
++              send_SSP_msg((unchar *) &CONTROL_BL_CMD, 2);
++              unlock_smartio(&flags);
++      }
++      else
++              return 0xFFFF;
++
++      return 0;
++}
++
++static ushort control_sio_keypad(int x, int y)
++{
++      unsigned long   flags;
++
++      if ( (x<1) || (x>8) || (y<1) || (y>8)) {
++              return 0xFFFF;
++      }
++
++      CONTROL_KPD_CMD.Opt[0] = (unchar) x;
++      CONTROL_KPD_CMD.Opt[1] = (unchar) y;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &CONTROL_KPD_CMD, 4);
++      unlock_smartio(&flags);
++
++      return 0;
++}
++
++static ushort control_sio_vee(int value)
++{
++      unsigned long   flags;
++
++      value &= 0xFF;
++      CONTROL_VEE_CMD.Opt[0] = (unchar) value;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &CONTROL_VEE_CMD, 3);
++      unlock_smartio(&flags);
++
++      return 0;
++}
++
++static ssize_t sio_write(struct file *file, const char *buf, size_t cont, loff_t *ppos)
++{
++      struct inode *inode = file->f_dentry->d_inode;
++      unsigned int minor = MINOR(inode->i_rdev);
++
++      switch (minor) {
++      case    SMARTIO_PORT_B:
++      case    SMARTIO_PORT_C:
++      case    SMARTIO_PORT_D:
++                      if (write_sio_port(minor, buf[0]) != 0xFFFF)
++                              return 1;
++      case    SMARTIO_SELECT_OPTION:
++                      if (write_sio_select(buf[0]) != 0xFFFF)
++                              return 1;
++      case    SMARTIO_BACKLITE:
++                      if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF)
++                              return 1;
++      case    SMARTIO_KEYPAD:
++                      if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF)
++                              return 2;
++      case    SMARTIO_VEE_PWM:
++                      if (control_sio_vee(buf[0]) != 0xFFFF)
++                              return 1;
++      case    SMARTIO_KBD_SNIFFER:
++                      // here are the scancodes injected
++                      handle_scancode((unchar)buf[0], (buf[0] & 0x80) ? 0 : 1);
++                      wake_up_interruptible(&keyboard_done_queue);
++                      // give some time to process! File IO is a bit faster than manual typing ;-)
++                      udelay(10000);
++                      return 1;
++      default:
++              return -ENXIO;
++      }
++}
++
++static unsigned int sio_poll(struct file *file, struct poll_table_struct *wait)
++{
++      return 0;
++}
++
++static        SMARTIO_CMD IOCTL_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } };
++
++static ushort ioctl_sio_port(int port, unchar value)
++{
++      unsigned long   flags;
++
++      if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D))
++              return 0xFFFF;
++
++      IOCTL_PORT_CMD.Code = (unchar) port + 0x04;             // 0x05 ~ 0x08
++      if (port == SMARTIO_PORT_B) {
++              // Port B has 4 bits only
++              IOCTL_PORT_CMD.Opt[0] = (unchar) value & 0x0F;
++      }
++      else
++              IOCTL_PORT_CMD.Opt[0] = (unchar) value;
++
++      lock_smartio(&flags);
++      send_SSP_msg((unchar *) &IOCTL_PORT_CMD, 3);
++      unlock_smartio(&flags);
++
++      return 0;
++}
++
++static int sio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++      unsigned int minor = MINOR(inode->i_rdev);
++      unchar  *buf = (unchar *)arg;
++
++      switch (minor) {
++      case    SMARTIO_PORT_B:
++      case    SMARTIO_PORT_C:
++      case    SMARTIO_PORT_D:
++                      if (cmd == SMARTIO_PORT_CONFIG) {
++                              if (ioctl_sio_port(minor, buf[0]) != 0xFFFF)
++                                      return 0;
++                      }
++                      return -EINVAL;
++      case    SMARTIO_SELECT_OPTION:
++                      if (write_sio_select(buf[0]) != 0xFFFF) return 0;
++                      return -EINVAL;
++      case    SMARTIO_BACKLITE:
++                      if (cmd == SMARTIO_BL_CONTROL) {
++                              if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) return 0;
++                      }
++                      else if (cmd == SMARTIO_BL_CONTRAST) {
++                              if (control_sio_backlite(SMARTIO_BL_CONTRAST, buf[0]) != 0xFFFF) return 0;
++                      }
++                      else return -EINVAL;
++      case    SMARTIO_KEYPAD:
++                      if (cmd == SMARTIO_KPD_TIMEOUT) {
++                              kpd_timeout = *(long*)buf;
++                              return 0;
++                      }
++                      else if (cmd == SMARTIO_KPD_SETUP) {
++                              if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) return 0;
++                      }
++                      return -EINVAL;
++      case    SMARTIO_VEE_PWM:
++                      if (control_sio_vee(buf[0]) != 0xFFFF) return 0;
++                      return -EINVAL;
++      case    SMARTIO_KBD_SNIFFER:
++                      if (cmd == SMARTIO_SNIFFER_TIMEOUT) {
++                              sniffer_timeout = *(long*)buf;
++                              if (sniffer_timeout < 0) sniffer_timeout = -1;
++                              // the value will be devided by 10 later on
++                              if (!sniffer_timeout) sniffer_timeout = 10;
++                              return 0;
++                      }
++                      return -EINVAL;
++      default:
++              return -ENXIO;
++      }
++}
++
++static int sio_open(struct inode *inode, struct file *file)
++{
++        unsigned int minor = MINOR(inode->i_rdev);
++
++      // we open all by default. we only have a special handler for the kbd sniffer
++      switch (minor) {
++              case SMARTIO_KBD_SNIFFER:
++                      if (sniffer_in_use) return -EBUSY;
++                      sniffer_in_use = 1;
++                      SNIFFER = 1;
++                      // sniff in active or passive mode
++                      if ((file->f_flags & O_RDWR) == O_RDWR) SNIFFMODE = 1; else SNIFFMODE = 0;
++                      // do we have a blocking or non blocking sniffer?
++                      if ((file->f_flags & O_NONBLOCK) == O_NONBLOCK) sniffer_timeout = 100; else sniffer_timeout = -1;
++                      break;
++              default:
++                      break;
++      }
++      return 0;
++}
++
++static int sio_close(struct inode *inode, struct file *file)
++{
++        unsigned int minor = MINOR(inode->i_rdev);
++
++      switch (minor) {
++              case SMARTIO_KBD_SNIFFER:
++                      SNIFFER = 0;
++                      SNIFFMODE = 0;
++                      sniffer_in_use = 0;
++                      break;
++              default:
++                      break;
++      }
++      return 0;
++}
++
++static struct file_operations sio_fops = {
++      read:           sio_read,
++      write:          sio_write,
++      poll:           sio_poll,
++      ioctl:          sio_ioctl,
++      open:           sio_open,
++      release:        sio_close,
++};
++
++static struct proc_dir_entry *sio_dir, *parent_dir = NULL;
++
++#define       SMARTIO_MAJOR   58
++#define       MAJOR_NR        SMARTIO_MAJOR
++
++#define       PROC_NAME       "sio"
++
++static int sio_read_proc(char *buf, char **start, off_t pos, int count, int *eof, void *data)
++{
++      char    *p = buf;
++
++      p += sprintf(p, "ADS SMARTIO Status: \n");
++      p += sprintf(p, "\t Keyboard Interrupt : %lu\n", kbd_int);
++      p += sprintf(p, "\t Keypad Interrupt : %lu\n", kpd_int);
++      p += sprintf(p, "\t ADC Interrupt : %lu\n", adc_int);
++      p += sprintf(p, "\t Keyboard Sniffer : %s mode : %s\n", kbd_sniff[ SNIFFER ], kbd_sniff_mode [ SNIFFMODE ]);
++
++      return (p-buf);
++}
++
++#ifdef        CONFIG_PM
++static int pm_smartio_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      switch (rqst) {
++              case    PM_RESUME:
++                      gc_sio_init();
++                      break;
++              case    PM_SUSPEND:
++                      // 4/5/01 Woojung
++                      // It checks Keybard received pair of press/release code.
++                      // System can sleep before receiving release code
++                      if (kbd_press_flag) {
++                              interruptible_sleep_on(&keyboard_done_queue);
++                      }
++                      break;
++      }
++
++      return 0;
++}
++#endif
++
++void __init sio_init(void)
++{
++      if (register_chrdev(MAJOR_NR, "sio", &sio_fops)) {
++              printk("smartio : unable to get major %d\n", MAJOR_NR);
++              return;
++      }
++      else {
++              printk("smartio driver initialized. version %s, date:%s\n",
++                              smartio_version, smartio_date);
++
++              if (sio_reset_flag != 1) {
++                      gc_sio_init();
++                      if (request_irq(ADS_AVR_IRQ, gc_sio_interrupt,0,"sio",NULL) != 0){
++                              printk("smartio : Could not allocate IRQ!\n");
++                              return;
++                      }
++              }
++
++              if ((sio_dir = create_proc_entry(PROC_NAME, 0, parent_dir)) == NULL) {
++                      printk("smartio : Unable to create /proc entry\n");
++                      return;
++              }
++              else {
++                      sio_dir->read_proc = sio_read_proc;
++#ifdef        CONFIG_PM
++                      pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_smartio_callback);
++#endif
++              }
++      }
++}
+--- /dev/null
++++ linux-2.4.27/drivers/char/gckeymap.c
+@@ -0,0 +1,262 @@
++/* Do not edit this file! It was automatically generated by   */
++/*    loadkeys --mktable defkeymap.map > defkeymap.c          */
++
++#include <linux/types.h>
++#include <linux/keyboard.h>
++#include <linux/kd.h>
++
++u_short plain_map[NR_KEYS] = {
++      0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
++      0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009,
++      0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
++      0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
++      0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
++      0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
++      0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c,
++      0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
++      0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307,
++      0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
++      0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a,
++      0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short shift_map[NR_KEYS] = {
++      0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
++      0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009,
++      0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
++      0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53,
++      0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
++      0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
++      0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c,
++      0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
++      0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307,
++      0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
++      0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
++      0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short altgr_map[NR_KEYS] = {
++      0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
++      0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200,
++      0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
++      0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73,
++      0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200,
++      0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76,
++      0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
++      0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510,
++      0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911,
++      0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b,
++      0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516,
++      0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short ctrl_map[NR_KEYS] = {
++      0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
++      0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
++      0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
++      0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
++      0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
++      0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
++      0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c,
++      0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
++      0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307,
++      0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
++      0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a,
++      0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short shift_ctrl_map[NR_KEYS] = {
++      0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200,
++      0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
++      0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013,
++      0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
++      0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016,
++      0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
++      0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307,
++      0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
++      0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short alt_map[NR_KEYS] = {
++      0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
++      0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809,
++      0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
++      0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873,
++      0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b,
++      0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876,
++      0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c,
++      0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
++      0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907,
++      0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901,
++      0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a,
++      0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++u_short ctrl_alt_map[NR_KEYS] = {
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
++      0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813,
++      0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200,
++      0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816,
++      0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
++      0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
++      0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307,
++      0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
++      0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a,
++      0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++      0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603,
++      0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c,
++      0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
++      0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++};
++
++ushort *key_maps[MAX_NR_KEYMAPS] = {
++      plain_map, shift_map, altgr_map, 0,
++      ctrl_map, shift_ctrl_map, 0, 0,
++      alt_map, 0, 0, 0,
++      ctrl_alt_map,   0
++};
++
++unsigned int keymap_count = 7;
++
++/*
++ * Philosophy: most people do not define more strings, but they who do
++ * often want quite a lot of string space. So, we statically allocate
++ * the default and allocate dynamically in chunks of 512 bytes.
++ */
++
++char func_buf[] = {
++      '\033', '[', '[', 'A', 0,
++      '\033', '[', '[', 'B', 0,
++      '\033', '[', '[', 'C', 0,
++      '\033', '[', '[', 'D', 0,
++      '\033', '[', '[', 'E', 0,
++      '\033', '[', '1', '7', '~', 0,
++      '\033', '[', '1', '8', '~', 0,
++      '\033', '[', '1', '9', '~', 0,
++      '\033', '[', '2', '0', '~', 0,
++      '\033', '[', '2', '1', '~', 0,
++      '\033', '[', '2', '3', '~', 0,
++      '\033', '[', '2', '4', '~', 0,
++      '\033', '[', '2', '5', '~', 0,
++      '\033', '[', '2', '6', '~', 0,
++      '\033', '[', '2', '8', '~', 0,
++      '\033', '[', '2', '9', '~', 0,
++      '\033', '[', '3', '1', '~', 0,
++      '\033', '[', '3', '2', '~', 0,
++      '\033', '[', '3', '3', '~', 0,
++      '\033', '[', '3', '4', '~', 0,
++      '\033', '[', '1', '~', 0,
++      '\033', '[', '2', '~', 0,
++      '\033', '[', '3', '~', 0,
++      '\033', '[', '4', '~', 0,
++      '\033', '[', '5', '~', 0,
++      '\033', '[', '6', '~', 0,
++      '\033', '[', 'M', 0,
++      '\033', '[', 'P', 0,
++};
++
++char *funcbufptr = func_buf;
++int funcbufsize = sizeof(func_buf);
++int funcbufleft = 0;          /* space left */
++
++char *func_table[MAX_NR_FUNC] = {
++      func_buf + 0,
++      func_buf + 5,
++      func_buf + 10,
++      func_buf + 15,
++      func_buf + 20,
++      func_buf + 25,
++      func_buf + 31,
++      func_buf + 37,
++      func_buf + 43,
++      func_buf + 49,
++      func_buf + 55,
++      func_buf + 61,
++      func_buf + 67,
++      func_buf + 73,
++      func_buf + 79,
++      func_buf + 85,
++      func_buf + 91,
++      func_buf + 97,
++      func_buf + 103,
++      func_buf + 109,
++      func_buf + 115,
++      func_buf + 120,
++      func_buf + 125,
++      func_buf + 130,
++      func_buf + 135,
++      func_buf + 140,
++      func_buf + 145,
++      0,
++      0,
++      func_buf + 149,
++      0,
++};
++
++struct kbdiacr accent_table[MAX_DIACR] = {
++      {'`', 'A', '\300'},     {'`', 'a', '\340'},
++      {'\'', 'A', '\301'},    {'\'', 'a', '\341'},
++      {'^', 'A', '\302'},     {'^', 'a', '\342'},
++      {'~', 'A', '\303'},     {'~', 'a', '\343'},
++      {'"', 'A', '\304'},     {'"', 'a', '\344'},
++      {'O', 'A', '\305'},     {'o', 'a', '\345'},
++      {'0', 'A', '\305'},     {'0', 'a', '\345'},
++      {'A', 'A', '\305'},     {'a', 'a', '\345'},
++      {'A', 'E', '\306'},     {'a', 'e', '\346'},
++      {',', 'C', '\307'},     {',', 'c', '\347'},
++      {'`', 'E', '\310'},     {'`', 'e', '\350'},
++      {'\'', 'E', '\311'},    {'\'', 'e', '\351'},
++      {'^', 'E', '\312'},     {'^', 'e', '\352'},
++      {'"', 'E', '\313'},     {'"', 'e', '\353'},
++      {'`', 'I', '\314'},     {'`', 'i', '\354'},
++      {'\'', 'I', '\315'},    {'\'', 'i', '\355'},
++      {'^', 'I', '\316'},     {'^', 'i', '\356'},
++      {'"', 'I', '\317'},     {'"', 'i', '\357'},
++      {'-', 'D', '\320'},     {'-', 'd', '\360'},
++      {'~', 'N', '\321'},     {'~', 'n', '\361'},
++      {'`', 'O', '\322'},     {'`', 'o', '\362'},
++      {'\'', 'O', '\323'},    {'\'', 'o', '\363'},
++      {'^', 'O', '\324'},     {'^', 'o', '\364'},
++      {'~', 'O', '\325'},     {'~', 'o', '\365'},
++      {'"', 'O', '\326'},     {'"', 'o', '\366'},
++      {'/', 'O', '\330'},     {'/', 'o', '\370'},
++      {'`', 'U', '\331'},     {'`', 'u', '\371'},
++      {'\'', 'U', '\332'},    {'\'', 'u', '\372'},
++      {'^', 'U', '\333'},     {'^', 'u', '\373'},
++      {'"', 'U', '\334'},     {'"', 'u', '\374'},
++      {'\'', 'Y', '\335'},    {'\'', 'y', '\375'},
++      {'T', 'H', '\336'},     {'t', 'h', '\376'},
++      {'s', 's', '\337'},     {'"', 'y', '\377'},
++      {'s', 'z', '\337'},     {'i', 'j', '\377'},
++};
++
++unsigned int accent_table_size = 68;
+--- /dev/null
++++ linux-2.4.27/drivers/char/gckeymap.map
+@@ -0,0 +1,357 @@
++# Default kernel keymap. This uses 7 modifier combinations.
++keymaps 0-2,4-5,8,12
++# Change the above line into
++#     keymaps 0-2,4-6,8,12
++# in case you want the entries
++#     altgr   control keycode  83 = Boot
++#     altgr   control keycode 111 = Boot
++# below.
++#
++# In fact AltGr is used very little, and one more keymap can
++# be saved by mapping AltGr to Alt (and adapting a few entries):
++# keycode 100 = Alt
++#
++keycode   1 = Escape           Escape
++      alt     keycode   1 = Meta_Escape
++keycode   2 = one              exclam
++      alt     keycode   2 = Meta_one
++keycode   3 = two              at               at
++      control keycode   3 = nul
++      shift   control keycode   3 = nul
++      alt     keycode   3 = Meta_two
++keycode   4 = three            numbersign
++      control keycode   4 = Escape
++      alt     keycode   4 = Meta_three
++keycode   5 = four             dollar           dollar
++      control keycode   5 = Control_backslash
++      alt     keycode   5 = Meta_four
++keycode   6 = five             percent
++      control keycode   6 = Control_bracketright
++      alt     keycode   6 = Meta_five
++keycode   7 = six              asciicircum
++      control keycode   7 = Control_asciicircum
++      alt     keycode   7 = Meta_six
++keycode   8 = seven            ampersand        braceleft
++      control keycode   8 = Control_underscore
++      alt     keycode   8 = Meta_seven
++keycode   9 = eight            asterisk         bracketleft
++      control keycode   9 = Delete
++      alt     keycode   9 = Meta_eight
++keycode  10 = nine             parenleft        bracketright
++      alt     keycode  10 = Meta_nine
++keycode  11 = zero             parenright       braceright
++      alt     keycode  11 = Meta_zero
++keycode  12 = minus            underscore       backslash
++      control keycode  12 = Control_underscore
++      shift   control keycode  12 = Control_underscore
++      alt     keycode  12 = Meta_minus
++keycode  13 = equal            plus
++      alt     keycode  13 = Meta_equal
++keycode  14 = Delete           Delete
++      control keycode  14 = BackSpace
++      alt     keycode  14 = Meta_Delete
++keycode  15 = Tab              Tab
++      alt     keycode  15 = Meta_Tab
++keycode  16 = q
++keycode  17 = w
++keycode  18 = e
++      altgr   keycode  18 = Hex_E
++keycode  19 = r
++keycode  20 = t
++keycode  21 = y
++keycode  22 = u
++keycode  23 = i
++keycode  24 = o
++keycode  25 = p
++keycode  26 = bracketleft      braceleft
++      control keycode  26 = Escape
++      alt     keycode  26 = Meta_bracketleft
++keycode  27 = bracketright     braceright       asciitilde
++      control keycode  27 = Control_bracketright
++      alt     keycode  27 = Meta_bracketright
++keycode  28 = Return
++      alt     keycode  28 = Meta_Control_m
++keycode  29 = Control
++keycode  30 = a
++      altgr   keycode  30 = Hex_A
++keycode  31 = s
++keycode  32 = d
++      altgr   keycode  32 = Hex_D
++keycode  33 = f
++      altgr   keycode  33 = Hex_F
++keycode  34 = g
++keycode  35 = h
++keycode  36 = j
++keycode  37 = k
++keycode  38 = l
++keycode  39 = semicolon        colon
++      alt     keycode  39 = Meta_semicolon
++keycode  40 = apostrophe       quotedbl
++      control keycode  40 = Control_g
++      alt     keycode  40 = Meta_apostrophe
++keycode  41 = grave            asciitilde
++      control keycode  41 = nul
++      alt     keycode  41 = Meta_grave
++keycode  42 = Shift
++keycode  43 = backslash        bar
++      control keycode  43 = Control_backslash
++      alt     keycode  43 = Meta_backslash
++keycode  44 = z
++keycode  45 = x
++keycode  46 = c
++      altgr   keycode  46 = Hex_C
++keycode  47 = v
++keycode  48 = b
++      altgr   keycode  48 = Hex_B
++keycode  49 = n
++keycode  50 = m
++keycode  51 = comma            less
++      alt     keycode  51 = Meta_comma
++keycode  52 = period           greater
++      control keycode  52 = Compose
++      alt     keycode  52 = Meta_period
++keycode  53 = slash            question
++      control keycode  53 = Delete
++      alt     keycode  53 = Meta_slash
++keycode  54 = Shift
++keycode  55 = KP_Multiply
++keycode  56 = Alt
++keycode  57 = space            space
++      control keycode  57 = nul
++      alt     keycode  57 = Meta_space
++keycode  58 = Caps_Lock
++keycode  59 = F1               F11              Console_13
++      control keycode  59 = F1
++      alt     keycode  59 = Console_1
++      control alt     keycode  59 = Console_1
++keycode  60 = F2               F12              Console_14
++      control keycode  60 = F2
++      alt     keycode  60 = Console_2
++      control alt     keycode  60 = Console_2
++keycode  61 = F3               F13              Console_15
++      control keycode  61 = F3
++      alt     keycode  61 = Console_3
++      control alt     keycode  61 = Console_3
++keycode  62 = F4               F14              Console_16
++      control keycode  62 = F4
++      alt     keycode  62 = Console_4
++      control alt     keycode  62 = Console_4
++keycode  63 = F5               F15              Console_17
++      control keycode  63 = F5
++      alt     keycode  63 = Console_5
++      control alt     keycode  63 = Console_5
++keycode  64 = F6               F16              Console_18
++      control keycode  64 = F6
++      alt     keycode  64 = Console_6
++      control alt     keycode  64 = Console_6
++keycode  65 = F7               F17              Console_19
++      control keycode  65 = F7
++      alt     keycode  65 = Console_7
++      control alt     keycode  65 = Console_7
++keycode  66 = F8               F18              Console_20
++      control keycode  66 = F8
++      alt     keycode  66 = Console_8
++      control alt     keycode  66 = Console_8
++keycode  67 = F9               F19              Console_21
++      control keycode  67 = F9
++      alt     keycode  67 = Console_9
++      control alt     keycode  67 = Console_9
++keycode  68 = F10              F20              Console_22
++      control keycode  68 = F10
++      alt     keycode  68 = Console_10
++      control alt     keycode  68 = Console_10
++keycode  69 = Num_Lock
++      shift   keycode  69 = Bare_Num_Lock
++keycode  70 = Scroll_Lock      Show_Memory      Show_Registers
++      control keycode  70 = Show_State
++      alt     keycode  70 = Scroll_Lock
++keycode  71 = KP_7
++      alt     keycode  71 = Ascii_7
++      altgr   keycode  71 = Hex_7
++keycode  72 = KP_8
++      alt     keycode  72 = Ascii_8
++      altgr   keycode  72 = Hex_8
++keycode  73 = KP_9
++      alt     keycode  73 = Ascii_9
++      altgr   keycode  73 = Hex_9
++keycode  74 = KP_Subtract
++keycode  75 = KP_4
++      alt     keycode  75 = Ascii_4
++      altgr   keycode  75 = Hex_4
++keycode  76 = KP_5
++      alt     keycode  76 = Ascii_5
++      altgr   keycode  76 = Hex_5
++keycode  77 = KP_6
++      alt     keycode  77 = Ascii_6
++      altgr   keycode  77 = Hex_6
++keycode  78 = KP_Add
++keycode  79 = KP_1
++      alt     keycode  79 = Ascii_1
++      altgr   keycode  79 = Hex_1
++keycode  80 = KP_2
++      alt     keycode  80 = Ascii_2
++      altgr   keycode  80 = Hex_2
++keycode  81 = KP_3
++      alt     keycode  81 = Ascii_3
++      altgr   keycode  81 = Hex_3
++keycode  82 = KP_0
++      alt     keycode  82 = Ascii_0
++      altgr   keycode  82 = Hex_0
++keycode  83 = KP_Period
++#     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
++keycode  87 = F11              F11              Console_23
++      control keycode  87 = F11
++      alt     keycode  87 = Console_11
++      control alt     keycode  87 = Console_11
++keycode  88 = F12              F12              Console_24
++      control keycode  88 = F12
++      alt     keycode  88 = Console_12
++      control alt     keycode  88 = Console_12
++keycode  89 =
++keycode  90 =
++keycode  91 =
++keycode  92 =
++keycode  93 =
++keycode  94 =
++keycode  95 =
++keycode  96 = KP_Enter
++keycode  97 = Control
++keycode  98 = KP_Divide
++keycode  99 = Control_backslash
++      control keycode  99 = Control_backslash
++      alt     keycode  99 = Control_backslash
++keycode 100 = AltGr
++keycode 101 = Break
++keycode 102 = Find
++keycode 103 = Up
++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
++keycode 113 = F13
++keycode 114 = F14
++keycode 115 = Help
++keycode 116 = Do
++keycode 117 = F17
++keycode 118 = KP_MinPlus
++keycode 119 = Pause
++keycode 120 =
++keycode 121 =
++keycode 122 =
++keycode 123 =
++keycode 124 =
++keycode 125 =
++keycode 126 =
++keycode 127 =
++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 '`' 'A' to 'À'
++compose '`' 'a' to 'à'
++compose '\'' 'A' to 'Á'
++compose '\'' 'a' to 'á'
++compose '^' 'A' to 'Â'
++compose '^' 'a' to 'â'
++compose '~' 'A' to 'Ã'
++compose '~' 'a' to 'ã'
++compose '"' 'A' to 'Ä'
++compose '"' 'a' to 'ä'
++compose 'O' 'A' to 'Å'
++compose 'o' 'a' to 'å'
++compose '0' 'A' to 'Å'
++compose '0' 'a' to 'å'
++compose 'A' 'A' to 'Å'
++compose 'a' 'a' to 'å'
++compose 'A' 'E' to 'Æ'
++compose 'a' 'e' to 'æ'
++compose ',' 'C' to 'Ç'
++compose ',' 'c' to 'ç'
++compose '`' 'E' to 'È'
++compose '`' 'e' to 'è'
++compose '\'' 'E' to 'É'
++compose '\'' 'e' to 'é'
++compose '^' 'E' to 'Ê'
++compose '^' 'e' to 'ê'
++compose '"' 'E' to 'Ë'
++compose '"' 'e' to 'ë'
++compose '`' 'I' to 'Ì'
++compose '`' 'i' to 'ì'
++compose '\'' 'I' to 'Í'
++compose '\'' 'i' to 'í'
++compose '^' 'I' to 'Î'
++compose '^' 'i' to 'î'
++compose '"' 'I' to 'Ï'
++compose '"' 'i' to 'ï'
++compose '-' 'D' to 'Ð'
++compose '-' 'd' to 'ð'
++compose '~' 'N' to 'Ñ'
++compose '~' 'n' to 'ñ'
++compose '`' 'O' to 'Ò'
++compose '`' 'o' to 'ò'
++compose '\'' 'O' to 'Ó'
++compose '\'' 'o' to 'ó'
++compose '^' 'O' to 'Ô'
++compose '^' 'o' to 'ô'
++compose '~' 'O' to 'Õ'
++compose '~' 'o' to 'õ'
++compose '"' 'O' to 'Ö'
++compose '"' 'o' to 'ö'
++compose '/' 'O' to 'Ø'
++compose '/' 'o' to 'ø'
++compose '`' 'U' to 'Ù'
++compose '`' 'u' to 'ù'
++compose '\'' 'U' to 'Ú'
++compose '\'' 'u' to 'ú'
++compose '^' 'U' to 'Û'
++compose '^' 'u' to 'û'
++compose '"' 'U' to 'Ü'
++compose '"' 'u' to 'ü'
++compose '\'' 'Y' to 'Ý'
++compose '\'' 'y' to 'ý'
++compose 'T' 'H' to 'Þ'
++compose 't' 'h' to 'þ'
++compose 's' 's' to 'ß'
++compose '"' 'y' to 'ÿ'
++compose 's' 'z' to 'ß'
++compose 'i' 'j' to 'ÿ'
+--- linux-2.4.27/drivers/char/generic_serial.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/generic_serial.c
+@@ -883,6 +883,9 @@
+               if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
+       }
++      /*
++       * should be using tty_get_baud_rate() here -- rmk
++       */
+       baudrate = tiosp->c_cflag & CBAUD;
+       if (baudrate & CBAUDEX) {
+               baudrate &= ~CBAUDEX;
+@@ -957,6 +960,11 @@
+       unsigned long flags;
+       unsigned long page;
++      /*
++       * Do we expect to allocate tmp_buf from an interrupt routine?
++       * If not, then save_flags() cli() and restore_flags() are
++       * redundant here and should be replaced by a semaphore. -- rmk
++       */
+       save_flags (flags);
+       if (!tmp_buf) {
+               page = get_free_page(GFP_KERNEL);
+@@ -976,6 +984,11 @@
+       if (port->flags & ASYNC_INITIALIZED)
+               return 0;
++      /*
++       * Do we expect to allocate xmit_buf from an interrupt routine?
++       * If not, then save_flags() cli() and restore_flags() are
++       * redundant here and should be replaced by a semaphore. -- rmk
++       */
+       if (!port->xmit_buf) {
+               /* We may sleep in get_free_page() */
+               unsigned long tmp;
+@@ -1016,7 +1029,7 @@
+       struct serial_struct sio;
+       if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
+-              return(-EFAULT);
++              return -EFAULT;
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((sio.baud_base != port->baud_base) ||
+--- linux-2.4.27/drivers/char/keyboard.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/keyboard.c
+@@ -14,13 +14,17 @@
+  * `Sticky' modifier keys, 951006.
+  *
+  * 11-11-96: SAK should now work in the raw mode (Martin Mares)
+- * 
++ *
+  * Modified to provide 'generic' keyboard support by Hamish Macdonald
+  * Merge with the m68k keyboard driver and split-off of the PC low-level
+  * parts by Geert Uytterhoeven, May 1997
+  *
+  * 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
+  * 30-07-98: Dead keys redone, aeb@cwi.nl.
++ *
++ * 04-04-1998: Added keyboard autorepeat support (some keyboards don't
++ *   autorepeat, and some keyboard changers interfere with keyboard
++ *   autorepeat settings).     - Russell King (rmk@arm.linux.org.uk)
+  */
+ #include <linux/config.h>
+@@ -30,6 +34,7 @@
+ #include <linux/tty_flip.h>
+ #include <linux/mm.h>
+ #include <linux/string.h>
++#include <linux/timer.h>
+ #include <linux/random.h>
+ #include <linux/init.h>
+@@ -61,6 +66,14 @@
+ #define KBD_DEFLOCK 0
+ #endif
++/*
++ * Default autorepeat settings.
++ *  DEFAULT_REPEAT_TIMEOUT is the timeout from the keypress to the first repeat
++ *  DEFAULT_REPEAT_INTERVAL is the timeout between successive repeats
++ */
++#define DEFAULT_REPEAT_TIMEOUT        HZ*300/1000
++#define DEFAULT_REPEAT_INTERVAL       HZ*30/1000
++
+ void (*kbd_ledfunc)(unsigned int led);
+ EXPORT_SYMBOL(handle_scancode);
+ EXPORT_SYMBOL(kbd_ledfunc);
+@@ -82,21 +95,25 @@
+ static unsigned long key_down[256/BITS_PER_LONG];
+ static int dead_key_next;
+-/* 
++/*
+  * In order to retrieve the shift_state (for the mouse server), either
+- * the variable must be global, or a new procedure must be created to 
++ * the variable must be global, or a new procedure must be created to
+  * return the value. I chose the former way.
+  */
+ int shift_state;
+ static int npadch = -1;                       /* -1 or number assembled on pad */
+ static unsigned char diacr;
+ static char rep;                      /* flag telling character repeat */
++static int kbd_repeatkeycode= -1;
++static int kbd_repeattimeout = DEFAULT_REPEAT_TIMEOUT;
++static int kbd_repeatinterval= DEFAULT_REPEAT_INTERVAL;
+ struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+ static struct tty_struct **ttytab;
+ static struct kbd_struct * kbd = kbd_table;
+ static struct tty_struct * tty;
+ static unsigned char prev_scancode;
++static void kbd_processkeycode(unsigned char scancode, char up_flag, int autorepeat);
+ void compute_shiftstate(void);
+ typedef void (*k_hand)(unsigned char value, char up_flag);
+@@ -163,7 +180,8 @@
+  * string, and in both cases we might assume that it is
+  * in utf-8 already.
+  */
+-void to_utf8(ushort c) {
++void to_utf8(ushort c)
++{
+     if (c < 0x80)
+       put_queue(c);                   /*  0*******  */
+     else if (c < 0x800) {
+@@ -182,17 +200,23 @@
+  * Translation of escaped scancodes to keycodes.
+  * This is now user-settable (for machines were it makes sense).
+  */
+-
+ int setkeycode(unsigned int scancode, unsigned int keycode)
+ {
+-    return kbd_setkeycode(scancode, keycode);
++      return kbd_setkeycode(scancode, keycode);
+ }
+ int getkeycode(unsigned int scancode)
+ {
+-    return kbd_getkeycode(scancode);
++      return kbd_getkeycode(scancode);
+ }
++static void key_callback(unsigned long nr);
++
++static struct timer_list key_autorepeat_timer =
++{
++      function: key_callback
++};
++
+ void handle_scancode(unsigned char scancode, int down)
+ {
+       unsigned char keycode;
+@@ -201,6 +225,7 @@
+       char have_keycode;
+       pm_access(pm_kbd);
++
+       add_keyboard_randomness(scancode | up_flag);
+       tty = ttytab? ttytab[fg_console]: NULL;
+@@ -223,15 +248,15 @@
+       if (raw_mode) {
+               /*
+                *      The following is a workaround for hardware
+-               *      which sometimes send the key release event twice 
++               *      which sometimes send the key release event twice
+                */
+               unsigned char next_scancode = scancode|up_flag;
+               if (have_keycode && up_flag && next_scancode==prev_scancode) {
+                       /* unexpected 2nd release event */
+               } else {
+-                      /* 
++                      /*
+                        * Only save previous scancode if it was a key-up
+-                       * and had a single-byte scancode.  
++                       * and had a single-byte scancode.
+                        */
+                       if (!have_keycode)
+                               prev_scancode = 1;
+@@ -256,12 +281,35 @@
+        * return the keycode if in MEDIUMRAW mode.
+        */
++      kbd_processkeycode(keycode, up_flag, 0);
++
++out:
++      do_poke_blanked_console = 1;
++      schedule_console_callback();
++}
++
++static void
++kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)
++{
++      char raw_mode = (kbd->kbdmode == VC_RAW);
++
+       if (up_flag) {
+               rep = 0;
+               if(!test_and_clear_bit(keycode, key_down))
+                   up_flag = kbd_unexpected_up(keycode);
+-      } else
++      } else {
+               rep = test_and_set_bit(keycode, key_down);
++              /* If the keyboard autorepeated for us, ignore it.
++               * We do our own autorepeat processing.
++               */
++              if (rep && !autorepeat)
++                      return;
++      }
++
++      if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {
++              kbd_repeatkeycode = -1;
++              del_timer(&key_autorepeat_timer);
++      }
+ #ifdef CONFIG_MAGIC_SYSRQ             /* Handle the SysRq Hack */
+       if (keycode == SYSRQ_KEY) {
+@@ -275,6 +323,23 @@
+       }
+ #endif
++      /*
++       * Calculate the next time when we have to do some autorepeat
++       * processing.  Note that we do not do autorepeat processing
++       * while in raw mode but we do do autorepeat processing in
++       * medium raw mode.
++       */
++      if (!up_flag && !raw_mode) {
++              kbd_repeatkeycode = keycode;
++              if (vc_kbd_mode(kbd, VC_REPEAT)) {
++                      if (rep)
++                              key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;
++                      else
++                              key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;
++                      add_timer(&key_autorepeat_timer);
++              }
++      }
++
+       if (kbd->kbdmode == VC_MEDIUMRAW) {
+               /* soon keycodes will require more than one byte */
+               put_queue(keycode + up_flag);
+@@ -343,9 +408,24 @@
+ #endif
+               }
+       }
++      rep = 0;
+ out:
+-      do_poke_blanked_console = 1;
+-      schedule_console_callback();
++}
++
++/*
++ * This clears the key down arrays when the keyboard is reset.  On
++ * keyboard reset, this must be called before any keycodes are
++ * received.
++ */
++void kbd_reset_kdown(void)
++{
++      int i;
++
++      for (i = 0; i < NR_SHIFT; i++)
++              k_down[i] = 0;
++      for (i = 0; i < SIZE(key_down); i++)
++              key_down[i] = 0;
++      shift_state = 0;
+ }
+ void put_queue(int ch)
+@@ -453,7 +533,7 @@
+ static void decr_console(void)
+ {
+       int i;
+- 
++
+       for (i = fg_console-1; i != fg_console; i--) {
+               if (i == -1)
+                       i = MAX_NR_CONSOLES-1;
+@@ -531,7 +611,7 @@
+ {
+ }
+-static void do_null()
++static void do_null(void)
+ {
+       compute_shiftstate();
+ }
+@@ -646,8 +726,8 @@
+ static void do_pad(unsigned char value, char up_flag)
+ {
+-      static const char *pad_chars = "0123456789+-*/\015,.?()";
+-      static const char *app_map = "pqrstuvwxylSRQMnnmPQ";
++      static const char *pad_chars = "0123456789+-*/\015,.?()#";
++      static const char *app_map = "pqrstuvwxylSRQMnnmPQS";
+       if (up_flag)
+               return;         /* no action, if this is a key release */
+@@ -748,9 +828,10 @@
+       }
+ }
+-/* called after returning from RAW mode or when changing consoles -
+-   recompute k_down[] and shift_state from key_down[] */
+-/* maybe called when keymap is undefined, so that shiftkey release is seen */
++/* Called after returning from RAW mode or when changing consoles -
++ * recompute k_down[] and shift_state from key_down[]
++ * Maybe called when keymap is undefined so that shift key release is seen
++ */
+ void compute_shiftstate(void)
+ {
+       int i, j, k, sym, val;
+@@ -829,19 +910,22 @@
+ }
+ /*
+- * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
+- * or (ii) whatever pattern of lights people want to show using KDSETLED,
+- * or (iii) specified bits of specified words in kernel memory.
++ * The leds display either
++ * (i)   the status of NumLock, CapsLock, ScrollLock, or
++ * (ii)  whatever pattern of lights people want to show using KDSETLED, or
++ * (iii) specified bits of specified words in kernel memory.
+  */
+ static unsigned char ledstate = 0xff; /* undefined */
+ static unsigned char ledioctl;
+-unsigned char getledstate(void) {
++unsigned char getledstate(void)
++{
+     return ledstate;
+ }
+-void setledstate(struct kbd_struct *kbd, unsigned int led) {
++void setledstate(struct kbd_struct *kbd, unsigned int led)
++{
+     if (!(led & ~7)) {
+       ledioctl = led;
+       kbd->ledmode = LED_SHOW_IOCTL;
+@@ -857,7 +941,8 @@
+ } ledptrs[3];
+ void register_leds(int console, unsigned int led,
+-                 unsigned int *addr, unsigned int mask) {
++                 unsigned int *addr, unsigned int mask)
++{
+     struct kbd_struct *kbd = kbd_table + console;
+     if (led < 3) {
+       ledptrs[led].addr = addr;
+@@ -868,7 +953,8 @@
+       kbd->ledmode = LED_SHOW_FLAGS;
+ }
+-static inline unsigned char getleds(void){
++static inline unsigned char getleds(void)
++{
+     struct kbd_struct *kbd = kbd_table + fg_console;
+     unsigned char leds;
+@@ -915,6 +1001,19 @@
+ {
+       unsigned char leds = getleds();
++      if (rep && kbd_repeatkeycode != -1) {
++              tty = ttytab? ttytab[fg_console]: NULL;
++              kbd = kbd_table + fg_console;
++
++              /* This prevents the kbd_key routine from being called
++               * twice, once by this BH, and once by the interrupt
++               * routine.
++               */
++              kbd_disable_irq();
++              kbd_processkeycode(kbd_repeatkeycode, 0, 1);
++              kbd_enable_irq();
++      }
++
+       if (leds != ledstate) {
+               ledstate = leds;
+               kbd_leds(leds);
+@@ -937,6 +1036,12 @@
+       tasklet_enable(&keyboard_tasklet);
+ }
++static void key_callback(unsigned long nr)
++{
++      rep = 1;
++      tasklet_schedule(&keyboard_tasklet);
++}
++
+ typedef void (pm_kbd_func) (void);
+ pm_callback pm_kbd_request_override = NULL;
+@@ -953,7 +1058,7 @@
+       kbd0.slockstate = 0;
+       kbd0.modeflags = KBD_DEFMODE;
+       kbd0.kbdmode = VC_XLATE;
+- 
++
+       for (i = 0 ; i < MAX_NR_CONSOLES ; i++)
+               kbd_table[i] = kbd0;
+@@ -963,7 +1068,7 @@
+       tasklet_enable(&keyboard_tasklet);
+       tasklet_schedule(&keyboard_tasklet);
+-      
++
+       pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_kbd_request_override);
+       return 0;
+--- linux-2.4.27/drivers/char/mem.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/mem.c
+@@ -27,9 +27,6 @@
+ #include <asm/io.h>
+ #include <asm/pgalloc.h>
+-#ifdef CONFIG_I2C
+-extern int i2c_init_all(void);
+-#endif
+ #ifdef CONFIG_FB
+ extern void fbmem_init(void);
+ #endif
+@@ -740,9 +737,6 @@
+               printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+       memory_devfs_register();
+       rand_initialize();
+-#ifdef CONFIG_I2C
+-      i2c_init_all();
+-#endif
+ #if defined (CONFIG_FB)
+       fbmem_init();
+ #endif
+--- /dev/null
++++ linux-2.4.27/drivers/char/omaha-rtc.c
+@@ -0,0 +1,566 @@
++/*
++ *    (C) ARM Limited 2002.
++ *
++ *    Real Time Clock interface for Linux on Omaha
++ *
++ *    Based on sa1100-rtc.c
++ *
++ *    Copyright (c) 2000 Nils Faerber
++ *
++ *    Based on rtc.c by Paul Gortmaker
++ *    Date/time conversion routines taken from arch/arm/kernel/time.c
++ *                    by Linus Torvalds and Russell King
++ *            and the GNU C Library
++ *    ( ... I love the GPL ... just take what you need! ;)
++ *
++ *    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.
++ *
++ *    1.00    2001-06-08      Nicolas Pitre <nico@cam.org>
++ *    - added periodic timer capability using OSMR1
++ *    - flag compatibility with other RTC chips
++ *    - permission checks for ioctls
++ *    - major cleanup, partial rewrite
++ *
++ *    0.03    2001-03-07      CIH <cih@coventive.com>
++ *    - Modify the bug setups RTC clock.
++ *
++ *    0.02    2001-02-27      Nils Faerber <nils@@kernelconcepts.de>
++ *    - removed mktime(), added alarm irq clear
++ *
++ *    0.01    2000-10-01      Nils Faerber <nils@@kernelconcepts.de>
++ *    - initial release
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++#include <linux/mc146818rtc.h>
++
++#define       DRIVER_VERSION          "1.00"
++
++#define epoch                 1970
++
++#define TIMER_FREQ            3686400
++
++#define RTC_DEF_DIVIDER               32768 - 1
++#define RTC_DEF_TRIM          0
++
++/* Those are the bits from a classic RTC we want to mimic */
++#define RTC_IRQF              0x80    /* any of the following 3 is active */
++#define RTC_PF                        0x40
++#define RTC_AF                        0x20
++#define RTC_UF                        0x10
++
++// bitdefs for rtc registers
++#define TICNT_ENABLE  0x80    // Enable tick interrupt
++#define TICNT_PERIOD  0x7F    // Divisor required for 1Hz tick
++#define RTC_ENABLE    0x1     // Enable bit for RTC
++
++static unsigned long rtc_status;
++static unsigned long rtc_irq_data;
++static unsigned long rtc_freq = 1024;
++
++static struct fasync_struct *rtc_async_queue;
++static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
++
++extern spinlock_t rtc_lock;
++
++static const unsigned char days_in_mo[] =
++      {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
++
++#define is_leap(year) \
++      ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
++
++// all the alarm and rtc registers
++static volatile unsigned int almsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMSEC);
++static volatile unsigned int almmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMIN);
++static volatile unsigned int almhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMHOUR);
++static volatile unsigned int almday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMDAY);
++static volatile unsigned int almmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMMON);
++static volatile unsigned int almyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_ALMYEAR);
++
++static volatile unsigned int bcdsec = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDSEC);
++static volatile unsigned int bcdmin = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMIN);
++static volatile unsigned int bcdhour = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDHOUR);
++static volatile unsigned int bcdday = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDAY);
++static volatile unsigned int bcddate = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDDATE);
++static volatile unsigned int bcdmon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDMON);
++static volatile unsigned int bcdyear = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_BCDYEAR);
++
++/*
++ * Converts seconds since 1970-01-01 00:00:00 to Gregorian date.
++ */
++
++static void decodetime (unsigned long t, struct rtc_time *tval)
++{
++      long days, month, year, rem;
++
++      days = t / 86400;
++      rem = t % 86400;
++      tval->tm_hour = rem / 3600;
++      rem %= 3600;
++      tval->tm_min = rem / 60;
++      tval->tm_sec = rem % 60;
++      tval->tm_wday = (4 + days) % 7;
++
++#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
++
++      year = epoch;
++      while (days >= (365 + is_leap(year))) {
++              unsigned long yg = year + days / 365;
++              days -= ((yg - year) * 365
++                              + LEAPS_THRU_END_OF (yg - 1)
++                              - LEAPS_THRU_END_OF (year - 1));
++              year = yg;
++      }
++      tval->tm_year = year - 1900;
++      tval->tm_yday = days + 1;
++
++      month = 0;
++      if (days >= 31) {
++              days -= 31;
++              month++;
++              if (days >= (28 + is_leap(year))) {
++                      days -= (28 + is_leap(year));
++                      month++;
++                      while (days >= days_in_mo[month]) {
++                              days -= days_in_mo[month];
++                              month++;
++                      }
++              }
++      }
++      tval->tm_mon = month;
++      tval->tm_mday = days + 1;
++}
++
++// Get alarm time in seconds
++static unsigned long get_alarm_time(void)
++{
++      int sec, min,hour,date,mon,year;
++      
++      // Read data from h/w
++      year = __raw_readb(almyear);
++      mon = __raw_readb(almmon);
++      date = __raw_readb(almday);
++      hour = __raw_readb(almhour);
++      min = __raw_readb(almmin);
++      sec = __raw_readb(almsec);
++      
++      // convert all the data into binary
++      year = BCD_TO_BIN(year);
++      mon = BCD_TO_BIN(mon);
++      date = BCD_TO_BIN(date);
++      hour = BCD_TO_BIN(hour);
++      min = BCD_TO_BIN(min);
++      sec = BCD_TO_BIN(sec);
++              
++      // convert year to 19xx or 20xx as appropriate
++      if (year > 69)
++              year += 1900;
++      else
++              year += 2000;
++              
++      // Now calculate number of seconds since time began...
++      return mktime(year,mon,date,hour,min,sec);      
++}
++
++// Get rtc time in seconds
++static unsigned long get_rtc_time(void)
++{
++      int sec,min,hour,day,date,mon,year;
++      
++      // Read data from h/w
++      year = __raw_readb(bcdyear);
++      mon = __raw_readb(bcdmon);
++      date = __raw_readb(bcdday);
++      day = __raw_readb(bcddate);
++      hour = __raw_readb(bcdhour);
++      min = __raw_readb(bcdmin);
++      sec = __raw_readb(bcdsec);
++      
++      // convert all the data into binary
++      year = BCD_TO_BIN(year);
++      mon = BCD_TO_BIN(mon);
++      date = BCD_TO_BIN(date);
++      day = BCD_TO_BIN(day);
++      hour = BCD_TO_BIN(hour);
++      min = BCD_TO_BIN(min);
++      sec = BCD_TO_BIN(sec);
++      
++      // convert year to 19xx or 20xx as appropriate
++      if (year > 69)
++              year += 1900;
++      else
++              year += 2000;
++
++      // Now calculate number of seconds since time began...
++      return mktime(year,mon,date,hour,min,sec);      
++}
++
++/* Sets time of alarm */
++static void set_alarm_time(struct rtc_time *tval)
++{
++      
++      int sec,min,hour,day,mon,year;
++       
++       // Convert data from binary to 8-bit bcd
++       sec = BIN_TO_BCD(tval->tm_sec);
++       min = BIN_TO_BCD(tval->tm_min);
++       hour = BIN_TO_BCD(tval->tm_hour);
++       day = BIN_TO_BCD(tval->tm_mday);
++       mon = BIN_TO_BCD(tval->tm_mon);
++              
++      // Year is special
++      year = tval->tm_year;
++      if(year > 1999)
++              year -=2000;
++      else    
++              year -=1900;
++      
++      year = BIN_TO_BCD(year);        
++       
++       // Write all the registers
++       __raw_writeb(year,almyear);     
++       __raw_writeb(mon,almmon);
++       __raw_writeb(day,almday);
++       __raw_writeb(hour,almhour);
++       __raw_writeb(min,almmin);
++       __raw_writeb(sec,almsec);
++}
++
++/* Sets time of alarm */
++static void set_rtc_time(struct rtc_time *tval)
++{
++      
++      int sec,min,hour,day,date,mon,year;
++       
++      // Convert data from binary to 8-bit bcd
++      sec = BIN_TO_BCD(tval->tm_sec);
++      min = BIN_TO_BCD(tval->tm_min);
++      hour = BIN_TO_BCD(tval->tm_hour);
++      day = BIN_TO_BCD(tval->tm_mday);
++      date = BIN_TO_BCD(tval->tm_wday);
++      mon = BIN_TO_BCD(tval->tm_mon);
++       
++      // Year is special
++      year = tval->tm_year;
++      if(year > 1999)
++              year -=2000;
++      else    
++              year -=1900;
++      
++      year = BIN_TO_BCD(year);        
++      
++       // Write all the registers
++       __raw_writeb(year,bcdyear);     
++       __raw_writeb(mon,bcdmon);
++       __raw_writeb(date,bcddate);
++       __raw_writeb(day,bcdday);
++       __raw_writeb(hour,bcdhour);
++       __raw_writeb(min,bcdmin);
++       __raw_writeb(sec,bcdsec);
++}
++
++static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      /* update irq data & counter */
++      rtc_irq_data += 0x100;
++
++      /* wake up waiting process */
++      wake_up_interruptible(&rtc_wait);
++      kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
++}
++
++static int rtc_open(struct inode *inode, struct file *file)
++{
++      if (test_and_set_bit (1, &rtc_status))
++              return -EBUSY;
++      MOD_INC_USE_COUNT;
++      rtc_irq_data = 0;
++      return 0;
++}
++
++static int rtc_release(struct inode *inode, struct file *file)
++{
++      rtc_status = 0;
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static int rtc_fasync (int fd, struct file *filp, int on)
++{
++      return fasync_helper (fd, filp, on, &rtc_async_queue);
++}
++
++static unsigned int rtc_poll(struct file *file, poll_table *wait)
++{
++      poll_wait (file, &rtc_wait, wait);
++      return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM;
++}
++
++static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
++{
++      return -ESPIPE;
++}
++
++ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      unsigned long data;
++      ssize_t retval;
++
++      if (count < sizeof(unsigned long))
++              return -EINVAL;
++
++      add_wait_queue(&rtc_wait, &wait);
++      set_current_state(TASK_INTERRUPTIBLE);
++      for (;;) {
++              spin_lock_irq (&rtc_lock);
++              data = rtc_irq_data;
++              if (data != 0) {
++                      rtc_irq_data = 0;
++                      break;
++              }
++              spin_unlock_irq (&rtc_lock);
++
++              if (file->f_flags & O_NONBLOCK) {
++                      retval = -EAGAIN;
++                      goto out;
++              }
++
++              if (signal_pending(current)) {
++                      retval = -ERESTARTSYS;
++                      goto out;
++              }
++
++              schedule();
++      }
++
++      spin_unlock_irq (&rtc_lock);
++
++      data -= 0x100;  /* the first IRQ wasn't actually missed */
++
++      retval = put_user(data, (unsigned long *)buf);
++      if (!retval)
++              retval = sizeof(unsigned long);
++
++out:
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&rtc_wait, &wait);
++      return retval;
++}
++
++static int rtc_ioctl(struct inode *inode, struct file *file,
++                   unsigned int cmd, unsigned long arg)
++{
++      volatile unsigned int rtcalm = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCALM);
++      volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT);
++      
++      struct rtc_time tm, tm2;
++      switch (cmd) {
++      case RTC_AIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              __raw_writel(0,rtcalm);
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_AIE_ON:
++              spin_lock_irq(&rtc_lock);
++              __raw_writel(0x7F,rtcalm);
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_UIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              __raw_writel(~TICNT_ENABLE,ticnt);
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_UIE_ON:
++              spin_lock_irq(&rtc_lock);
++              __raw_writel(TICNT_ENABLE|TICNT_PERIOD,ticnt);
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_PIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              // Periodic int not available
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_PIE_ON:
++              spin_lock_irq(&rtc_lock);
++              // Periodic int not available
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_ALM_READ:
++              decodetime(get_alarm_time(),&tm);
++              break;
++      case RTC_ALM_SET:
++              if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2)))
++                      return -EFAULT;
++              decodetime(get_rtc_time(),&tm);
++              if ((unsigned)tm2.tm_hour < 24)
++                      tm.tm_hour = tm2.tm_hour;
++              if ((unsigned)tm2.tm_min < 60)
++                      tm.tm_min = tm2.tm_min;
++              if ((unsigned)tm2.tm_sec < 60)
++                      tm.tm_sec = tm2.tm_sec;
++              
++              // Munge (as per sa1100)
++              tm.tm_year+=1900;
++              tm.tm_mon+=1;   
++              
++              // Set the alarm
++              set_alarm_time(&tm);
++              return 0;
++      case RTC_RD_TIME:
++              decodetime (get_rtc_time(), &tm);
++              break;
++      case RTC_SET_TIME:
++              if (!capable(CAP_SYS_TIME))
++                      return -EACCES;
++              if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm)))
++                      return -EFAULT; 
++              tm.tm_year += 1900;
++              if (tm.tm_year < epoch || (unsigned)tm.tm_mon >= 12 ||
++                  tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] +
++                              (tm.tm_mon == 1 && is_leap(tm.tm_year))) ||
++                  (unsigned)tm.tm_hour >= 24 ||
++                  (unsigned)tm.tm_min >= 60 ||
++                  (unsigned)tm.tm_sec >= 60)
++                      return -EINVAL;
++              tm.tm_mon +=1;  // wierd: same as sa1100 though (gets month wrong otherwise!)
++              set_rtc_time(&tm);
++              return 0;
++      case RTC_IRQP_READ:
++              return put_user(rtc_freq, (unsigned long *)arg);
++      case RTC_IRQP_SET:
++              if (arg < 1 || arg > TIMER_FREQ)
++                              return -EINVAL;
++              if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
++                              return -EACCES;
++              rtc_freq = arg;
++              return 0;
++      case RTC_EPOCH_READ:
++              return put_user (epoch, (unsigned long *)arg);
++      default:
++              return -EINVAL;
++      }
++      return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0;
++}
++
++static struct file_operations rtc_fops = {
++      .owner          = THIS_MODULE,
++      .llseek         = rtc_llseek,
++      .read           = rtc_read,
++      .poll           = rtc_poll,
++      .ioctl          = rtc_ioctl,
++      .open           = rtc_open,
++      .release        = rtc_release,
++      .fasync         = rtc_fasync,
++};
++
++static struct miscdevice omahartc_miscdev = {
++      .minor          = RTC_MINOR,
++      .name           = "rtc",
++      .fops           = &rtc_fops,
++};
++
++static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      char *p = page;
++      int len;
++      struct rtc_time tm;
++
++      decodetime (get_rtc_time(), &tm);
++      p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n"
++                      "rtc_date\t: %04d-%02d-%02d\n"
++                      "rtc_epoch\t: %04d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
++      decodetime (get_alarm_time(), &tm);
++      p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n"
++                      "alrm_date\t: %04d-%02d-%02d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++      p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq);
++
++      len = (p - page) - off;
++      if (len < 0)
++              len = 0;
++
++      *eof = (len <= count) ? 1 : 0;
++      *start = page + off;
++
++      return len;
++}
++
++static int __init rtc_init(void)
++{
++      volatile unsigned int ticnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_TICINT);
++      volatile unsigned int rtccon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_RTCCON);
++      int ret;
++
++      misc_register (&omahartc_miscdev);
++      create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
++      
++      // Enable RTC
++      __raw_writel(RTC_ENABLE,rtccon);
++      
++      // Acquire 1Hz timer
++      ret = request_irq (OMAHA_INT_TICK, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL);
++      if (ret) {
++              printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_TICK);
++              goto IRQ_TICK_failed;
++      }
++
++      // Acquire RTC (Alarm interrupt)
++      ret = request_irq (OMAHA_INT_RTC, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL);
++      if (ret) {
++              printk (KERN_ERR "rtc: IRQ %d already in use.\n", OMAHA_INT_RTC);
++              goto IRQ_RTC_failed;
++      }
++
++      printk (KERN_INFO "Omaha Real Time Clock driver v" DRIVER_VERSION "\n");
++
++      // Program tick interrupt divisor to generate real 1Hz clock and enable the interrupt
++      __raw_writeb(TICNT_ENABLE|TICNT_PERIOD,ticnt);  
++
++      return 0;
++
++IRQ_TICK_failed:
++      free_irq (OMAHA_INT_TICK, NULL);
++IRQ_RTC_failed:
++      free_irq(OMAHA_INT_RTC, NULL);
++      remove_proc_entry ("driver/rtc", NULL);
++      misc_deregister (&omahartc_miscdev);
++      return ret;
++}
++
++static void __exit rtc_exit(void)
++{
++      free_irq (OMAHA_INT_TICK, NULL);
++      remove_proc_entry ("driver/rtc", NULL);
++      misc_deregister (&omahartc_miscdev);
++}
++
++module_init(rtc_init);
++module_exit(rtc_exit);
++
++MODULE_AUTHOR("ARM Limited <support@arm.com>");
++MODULE_DESCRIPTION("Omaha Realtime Clock Driver (RTC)");
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/char/omaha_wdt.c
+@@ -0,0 +1,193 @@
++/*
++ *    Watchdog driver for the Omaha
++ *    (C) ARM Limited 2002.
++ *
++ *    Based on sa1100_wdt.c
++ *
++ *      (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
++ *          Based on SoftDog driver by Alan Cox <alan@redhat.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 of the License, or (at your option) any later version.
++ *
++ *    Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
++ *    warranty for any of this software. This material is provided
++ *    "AS-IS" and at no charge.
++ *
++ *    (c) Copyright 2000           Oleg Drokin <green@crimea.edu>
++ *
++ *      27/11/2000 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++
++#define TIMER_MARGIN  8               /* (secs) Default is 1 minute */
++#define WT_TPS                7812            /* Watchdog ticks per second. */
++#define WT_ENABLE     0x21            // Enable bits for watchdog
++#define WT_CLKSEL_128 0x18            // Select 1/128 divider
++
++static int omaha_margin = TIMER_MARGIN;       /* in seconds */
++static int omahawdt_users;
++static int pre_margin;
++#ifdef MODULE
++MODULE_PARM(omaha_margin,"i");
++#endif
++
++/*
++ *    Allow only one person to hold it open
++ */
++
++static int omahadog_open(struct inode *inode, struct file *file)
++{
++      volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON);
++      volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT);
++      volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT);
++      unsigned int tmp;
++
++      if(test_and_set_bit(1,&omahawdt_users))
++              return -EBUSY;
++      MOD_INC_USE_COUNT;
++      /* Activate omaha Watchdog timer */
++      
++      /* Assume that uhal has set up pre-scaler according
++       * to the system clock frequency (don't change it!)
++       * 
++       * Ie. all counting occurs at 1MHz / 128 = 7812.5Hz
++       *
++       * Since we have 16-bit counter, maximum period is
++       * 65536/7812.5 = 8.338608 seconds!
++       */
++       
++      pre_margin = WT_TPS * omaha_margin;
++      
++      // Set count to the maximum
++      __raw_writel(pre_margin,wtcnt);
++      
++      // Set the clock division factor
++      tmp = __raw_readl(wtcon);
++      tmp |= WT_CLKSEL_128;
++      __raw_writel(tmp,wtcon);        
++      
++      // Program an initial count into WTDAT
++      __raw_writel(0x8000,wtdat);
++      
++      // enable the watchdog
++      tmp = __raw_readl(wtcon);
++      tmp |= WT_ENABLE;
++
++      __raw_writel(tmp,wtcon);
++      
++      return 0;
++}
++
++static int omahadog_release(struct inode *inode, struct file *file)
++{
++      volatile unsigned int wtcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCON);
++      unsigned int tmp;
++
++      /*
++       *      Shut off the timer.
++       *      Lock it in if it's a module and we defined ...NOWAYOUT
++       */
++#ifndef CONFIG_WATCHDOG_NOWAYOUT
++      tmp = __raw_readl(wtcon);
++      tmp &= ~WT_ENABLE;
++      __raw_writel(tmp,wtcon);
++#endif
++      omahawdt_users = 0;
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static ssize_t omahadog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
++{
++      volatile unsigned int wtcnt = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTCNT);
++
++      /*  Can't seek (pwrite) on this device  */
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++
++      /* Refresh timer. */
++      if(len) {
++              __raw_writel(pre_margin,wtcnt);
++              return 1;
++      }
++      return 0;
++}
++
++static int omahadog_ioctl(struct inode *inode, struct file *file,
++      unsigned int cmd, unsigned long arg)
++{
++      volatile unsigned int wtdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_WTDAT);
++      static struct watchdog_info ident = {
++              identity: "omaha Watchdog",
++      };
++
++      switch(cmd){
++      default:
++              return -ENOIOCTLCMD;
++      case WDIOC_GETSUPPORT:
++              return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
++      case WDIOC_GETSTATUS:
++              return put_user(0,(int *)arg);
++      case WDIOC_GETBOOTSTATUS:
++              return 0;
++      case WDIOC_KEEPALIVE:
++              __raw_writel(pre_margin,wtdat);
++              return 0;
++      }
++}
++
++static struct file_operations omahadog_fops=
++{
++      .owner          = THIS_MODULE,
++      .write          = omahadog_write,
++      .ioctl          = omahadog_ioctl,
++      .open           = omahadog_open,
++      .release        = omahadog_release,
++};
++
++static struct miscdevice omahadog_miscdev=
++{
++      .minor          = WATCHDOG_MINOR,
++      .name           = "omaha watchdog",
++      .fops           = &omahadog_fops
++};
++
++static int __init omahadog_init(void)
++{
++      int ret;
++
++      ret = misc_register(&omahadog_miscdev);
++
++      if (ret)
++              return ret;
++
++      printk("Omaha Watchdog Timer: timer margin %d sec\n", omaha_margin);
++
++      return 0;
++}
++
++static void __exit omahadog_exit(void)
++{
++      misc_deregister(&omahadog_miscdev);
++}
++
++module_init(omahadog_init);
++module_exit(omahadog_exit);
+--- linux-2.4.27/drivers/char/pcmcia/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/pcmcia/Config.in
+@@ -5,7 +5,13 @@
+ mainmenu_option next_comment
+ comment 'PCMCIA character devices'
+-dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_SERIAL
++if [ "$CONFIG_SERIAL" = "y" -o "$CONFIG_SERIAL_8250" = "y" ]; then
++  dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS y
++else
++  if [ "$CONFIG_SERIAL" = "m" -o "$CONFIG_SERIAL_8250" = "m" ]; then
++    dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS m
++  fi
++fi
+ if [ "$CONFIG_PCMCIA_SERIAL_CS" = "y" ]; then
+    define_bool CONFIG_PCMCIA_CHRDEV y
+ fi
+--- /dev/null
++++ linux-2.4.27/drivers/char/pcmcia/memory_cs.c
+@@ -0,0 +1,214 @@
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/mtd.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/ciscode.h>
++#include <pcmcia/ds.h>
++#include <pcmcia/cisreg.h>
++
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++
++static dev_info_t dev_info = "memory_cs";
++static dev_link_t *dev_list;
++
++struct memcs_dev {
++      dev_link_t      link;
++      struct map_info map;
++};
++
++static __u8 mem_cs_read8(struct map_info *map, unsigned long ofs)
++{
++      return readb(map->map_priv_1 + ofs);
++}
++
++static __u16 mem_cs_read16(struct map_info *map, unsigned long ofs)
++{
++      return readw(map->map_priv_1 + ofs);
++}
++
++static __u32 mem_cs_read32(struct map_info *map, unsigned long ofs)
++{
++      return readl(map->map_priv_1 + ofs);
++}
++
++static void mem_cs_copy_from(struct map_info *map, void *to, unsigned long ofs, ssize_t size)
++{
++      memcpy_fromio(to, map->map_priv_1 + ofs, size);
++}
++
++static void mem_cs_write8(struct map_info *map, __u8 val, unsigned long ofs)
++{
++      writeb(val, map->map_priv_1 + ofs);
++}
++
++static void mem_cs_write16(struct map_info *map, __u16 val, unsigned long ofs)
++{
++      writew(val, map->map_priv_1 + ofs);
++}
++
++static void mem_cs_write32(struct map_info *map, __u32 val, unsigned long ofs)
++{
++      writel(val, map->map_priv_1 + ofs);
++}
++
++static void mem_cs_copy_to(struct map_info *map, unsigned long ofs, const void *to, ssize_t size)
++{
++      memcpy_toio(map->map_priv_1 + ofs, from, size);
++}
++
++static void mem_cs_release(u_long arg);
++
++static void mem_cs_detach(dev_link_t *link)
++{
++      del_timer(&link->release);
++      if (link->state & DEV_CONFIG) {
++              mem_cs_release((u_long)link);
++              if (link->state & DEV_STALE_CONFIG) {
++                      link->state |= DEV_STALE_LINK;
++                      return;
++              }
++      }
++
++      if (link->handle)
++              CardServices(DeregisterClient, link->handle);
++
++      kfree(link);
++}
++
++static void mem_cs_release(u_long arg)
++{
++      dev_link_t *link = (dev_link_t *)arg;
++
++      link->dev = NULL;
++      if (link->win) {
++              CardServices(ReleaseWindow, link->win);
++      }
++      link->state &= ~DEV_CONFIG;
++
++      if (link->state & DEV_STALE_LINK)
++              mem_cs_detach(link);
++}
++
++static void mem_cs_config(dev_link_t *link)
++{
++      struct memcs_dev *dev = link->priv;
++      cs_status_t status;
++      win_req_t req;
++
++      link->state |= DEV_CONFIG;
++
++      req.Attributes = word_width ? WIN_DATA_WIDTH_16 : WIN_DATA_WIDTH_8;
++      req.Base = 0;
++      req.Size = 0;
++      req.AccessSpeed = mem_speed;
++
++      link->win = (window_handle_t)link->handle;
++
++      CS_CHECK(RequestWindow, &link->win, &req);
++
++      CS_CHECK(GetStatus, link->handle, &status);
++
++      dev->map.buswidth = word_width ? 2 : 1;
++      dev->map.size = req.Size;
++      dev->map.map_priv_1 = ioremap(req.Base, req.Size);
++}
++
++static int
++mem_cs_event(event_t event, int priority, event_callback_args_t *args)
++{
++      dev_link_t *link = args->client_data;
++
++      switch (event) {
++      case CS_EVENT_CARD_REMOVAL:
++              link->state &= ~DEV_PRESENT;
++              if (link->state & DEV_CONFIG)
++                      mod_timer(&link->release, jiffies + HZ/20);
++              break;
++
++      case CS_EVENT_CARD_INSERTION:
++              link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
++              mem_cs_config(link);
++              break;
++      }
++      return 0;
++}
++
++static dev_link_t *mem_cs_attach(void)
++{
++      struct memcs_dev *dev;
++      client_reg_t clnt;
++
++      dev = kmalloc(sizeof(*dev), GFP_KERNEL);
++      if (dev) {
++              memset(dev, 0, sizeof(*dev));
++
++              dev->map.read8     = mem_cs_read8;
++              dev->map.read16    = mem_cs_read16;
++              dev->map.read32    = mem_cs_read32;
++              dev->map.copy_from = mem_cs_copy_from;
++              dev->map.write8    = mem_cs_write8;
++              dev->map.write16   = mem_cs_write16;
++              dev->map.write32   = mem_cs_write32;
++              dev->map.copy_to   = mem_cs_copy_to;
++
++              dev->link.release.function = &mem_cs_release;
++              dev->link.release.data = (u_long)link;
++              dev->link.priv = dev;
++
++              dev->link.next = dev_list;
++              dev_list = &dev->link;
++
++              clnt.dev_info = &dev_info;
++              clnt.Attributes = INOF_IO_CLIENT | INFO_CARD_SHARE;
++              clnt.EventMask =
++                      CS_EVENT_WRITE_PROTECT  | CS_EVENT_CARD_INSERTION |
++                      CS_EVENT_CARD_REMOVAL   | CS_EVENT_BATTERY_DEAD   |
++                      CS_EVENT_BATTERY_LOW;
++
++              clnt.event_handler = &mem_cs_event;
++              clnt.Version = 0x0210;
++              clnt.event_callback_args.client_data = &dev->link;
++
++              ret = CardServices(RegisterClient, &dev->link.handle, &clnt);
++              if (ret != CS_SUCCESS) {
++                      error_info_t err = { RegisterClient, ret };
++                      CardServices(ReportError, dev->link.handle, &err);
++                      mem_cs_detach(&dev->link);
++                      dev = NULL;
++              }
++      }
++
++      return &dev->link;
++}
++
++static int __init mem_cs_init(void)
++{
++      servinfo_t serv;
++
++      CardServices(GetCardServicesInfo, &serv);
++      if (serv.Revision != CS_RELEASE_CODE) {
++              printk(KERN_NOTICE "memory_cs: Card services release "
++                      "does not match\n");
++              return -ENXIO;
++      }
++      register_pccard_driver(&dev_info, mem_cs_attach, mem_cs_detach);
++      return 0;
++}
++
++static void __exit mem_cs_exit(void)
++{
++      unregister_pccard_driver(&dev_info);
++      while (dev_list != NULL)
++              memory_detach(dev_list);
++}
++
++module_init(mem_cs_init);
++module_exit(mem_cs_exit);
++
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/char/sa1100-rtc.c
+@@ -0,0 +1,474 @@
++/*
++ *    Real Time Clock interface for Linux on StrongARM SA1100
++ *
++ *    Copyright (c) 2000 Nils Faerber
++ *
++ *    Based on rtc.c by Paul Gortmaker
++ *    Date/time conversion routines taken from arch/arm/kernel/time.c
++ *                    by Linus Torvalds and Russel King
++ *            and the GNU C Library
++ *    ( ... I love the GPL ... just take what you need! ;)
++ *
++ *    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.
++ *
++ *    1.00    2001-06-08      Nicolas Pitre <nico@cam.org>
++ *    - added periodic timer capability using OSMR1
++ *    - flag compatibility with other RTC chips
++ *    - permission checks for ioctls
++ *    - major cleanup, partial rewrite
++ *
++ *    0.03    2001-03-07      CIH <cih@coventive.com>
++ *    - Modify the bug setups RTC clock.
++ *
++ *    0.02    2001-02-27      Nils Faerber <nils@@kernelconcepts.de>
++ *    - removed mktime(), added alarm irq clear
++ *
++ *    0.01    2000-10-01      Nils Faerber <nils@@kernelconcepts.de>
++ *    - initial release
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/string.h>
++#include <linux/init.h>
++#include <linux/poll.h>
++#include <linux/proc_fs.h>
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <linux/rtc.h>
++
++#define       DRIVER_VERSION          "1.00"
++
++#define TIMER_FREQ            3686400
++
++#define RTC_DEF_DIVIDER               32768 - 1
++#define RTC_DEF_TRIM          0
++
++/* Those are the bits from a classic RTC we want to mimic */
++#define RTC_IRQF              0x80    /* any of the following 3 is active */
++#define RTC_PF                        0x40
++#define RTC_AF                        0x20
++#define RTC_UF                        0x10
++
++static unsigned long rtc_status;
++static unsigned long rtc_irq_data;
++static unsigned long rtc_freq = 1024;
++
++static struct fasync_struct *rtc_async_queue;
++static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
++
++extern spinlock_t rtc_lock;
++
++static const unsigned char days_in_mo[] =
++      {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
++
++#define is_leap(year) \
++      ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
++
++/*
++ * Converts seconds since 1970-01-01 00:00:00 to Gregorian date.
++ */
++
++static void decodetime (unsigned long t, struct rtc_time *tval)
++{
++      long days, month, year, rem;
++
++      days = t / 86400;
++      rem = t % 86400;
++      tval->tm_hour = rem / 3600;
++      rem %= 3600;
++      tval->tm_min = rem / 60;
++      tval->tm_sec = rem % 60;
++      tval->tm_wday = (4 + days) % 7;
++
++#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
++
++      year = 1970 + days / 365;
++      days -= ((year - 1970) * 365
++                      + LEAPS_THRU_END_OF (year - 1)
++                      - LEAPS_THRU_END_OF (1970 - 1));
++      if (days < 0) {
++              year -= 1;
++              days += 365 + is_leap(year);
++      }
++      tval->tm_year = year - 1900;
++      tval->tm_yday = days + 1;
++
++      month = 0;
++      if (days >= 31) {
++              days -= 31;
++              month++;
++              if (days >= (28 + is_leap(year))) {
++                      days -= (28 + is_leap(year));
++                      month++;
++                      while (days >= days_in_mo[month]) {
++                              days -= days_in_mo[month];
++                              month++;
++                      }
++              }
++      }
++      tval->tm_mon = month;
++      tval->tm_mday = days + 1;
++}
++
++static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int rtsr = RTSR;
++
++      /* clear interrupt sources */
++      RTSR = 0;
++      RTSR = (RTSR_AL|RTSR_HZ);
++
++      /* clear alarm interrupt if it has occurred */
++      if (rtsr & RTSR_AL)
++              rtsr &= ~RTSR_ALE;
++      RTSR = rtsr & (RTSR_ALE|RTSR_HZE);
++
++      /* update irq data & counter */
++      if (rtsr & RTSR_AL)
++              rtc_irq_data |= (RTC_AF|RTC_IRQF);
++      if (rtsr & RTSR_HZ)
++              rtc_irq_data |= (RTC_UF|RTC_IRQF);
++      rtc_irq_data += 0x100;
++
++      /* wake up waiting process */
++      wake_up_interruptible(&rtc_wait);
++      kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
++}
++
++static void timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      /*
++       * If we match for the first time, the periodic interrupt flag won't
++       * be set.  If it is, then we did wrap around (very unlikely but
++       * still possible) and compute the amount of missed periods.
++       * The match reg is updated only when the data is actually retrieved
++       * to avoid unnecessary interrupts.
++       */
++      OSSR = OSSR_M1; /* clear match on timer1 */
++      if (rtc_irq_data & RTC_PF) {
++              rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8;
++      } else {
++              rtc_irq_data += (0x100|RTC_PF|RTC_IRQF);
++      }
++
++      wake_up_interruptible(&rtc_wait);
++      kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
++}
++
++static int rtc_open(struct inode *inode, struct file *file)
++{
++      if (test_and_set_bit (1, &rtc_status))
++              return -EBUSY;
++      rtc_irq_data = 0;
++      return 0;
++}
++
++static int rtc_release(struct inode *inode, struct file *file)
++{
++      spin_lock_irq (&rtc_lock);
++      RTSR = 0;
++      RTSR = (RTSR_AL|RTSR_HZ);
++      OIER &= ~OIER_E1;
++      OSSR = OSSR_M1;
++      spin_unlock_irq (&rtc_lock);
++      rtc_status = 0;
++      return 0;
++}
++
++static int rtc_fasync (int fd, struct file *filp, int on)
++{
++      return fasync_helper (fd, filp, on, &rtc_async_queue);
++}
++
++static unsigned int rtc_poll(struct file *file, poll_table *wait)
++{
++      poll_wait (file, &rtc_wait, wait);
++      return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM;
++}
++
++static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
++{
++      return -ESPIPE;
++}
++
++ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      unsigned long data;
++      ssize_t retval;
++
++      if (count < sizeof(unsigned long))
++              return -EINVAL;
++
++      add_wait_queue(&rtc_wait, &wait);
++      set_current_state(TASK_INTERRUPTIBLE);
++      for (;;) {
++              spin_lock_irq (&rtc_lock);
++              data = rtc_irq_data;
++              if (data != 0) {
++                      rtc_irq_data = 0;
++                      break;
++              }
++              spin_unlock_irq (&rtc_lock);
++
++              if (file->f_flags & O_NONBLOCK) {
++                      retval = -EAGAIN;
++                      goto out;
++              }
++
++              if (signal_pending(current)) {
++                      retval = -ERESTARTSYS;
++                      goto out;
++              }
++
++              schedule();
++      }
++
++      if (data & RTC_PF) {
++              /* interpolate missed periods and set match for the next one */
++              unsigned long period = TIMER_FREQ/rtc_freq;
++              unsigned long oscr = OSCR;
++              unsigned long osmr1 = OSMR1;
++              unsigned long missed = (oscr - osmr1)/period;
++              data += missed << 8;
++              OSSR = OSSR_M1; /* clear match on timer 1 */
++              OSMR1 = osmr1 + (missed + 1)*period;
++              /* ensure we didn't miss another match in the mean time */
++              while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) {
++                      data += 0x100;
++                      OSSR = OSSR_M1; /* clear match on timer 1 */
++                      OSMR1 = osmr1 + period;
++              }
++      }
++      spin_unlock_irq (&rtc_lock);
++
++      data -= 0x100;  /* the first IRQ wasn't actually missed */
++
++      retval = put_user(data, (unsigned long *)buf);
++      if (!retval)
++              retval = sizeof(unsigned long);
++
++out:
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&rtc_wait, &wait);
++      return retval;
++}
++
++static int rtc_ioctl(struct inode *inode, struct file *file,
++                   unsigned int cmd, unsigned long arg)
++{
++      struct rtc_time tm, tm2;
++
++      switch (cmd) {
++      case RTC_AIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              RTSR &= ~RTSR_ALE;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_AIE_ON:
++              spin_lock_irq(&rtc_lock);
++              RTSR |= RTSR_ALE;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_UIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              RTSR &= ~RTSR_HZE;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_UIE_ON:
++              spin_lock_irq(&rtc_lock);
++              RTSR |= RTSR_HZE;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_PIE_OFF:
++              spin_lock_irq(&rtc_lock);
++              OIER &= ~OIER_E1;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_PIE_ON:
++              if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE))
++                      return -EACCES;
++              spin_lock_irq(&rtc_lock);
++              OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
++              OIER |= OIER_E1;
++              rtc_irq_data = 0;
++              spin_unlock_irq(&rtc_lock);
++              return 0;
++      case RTC_ALM_READ:
++              decodetime (RTAR, &tm);
++              break;
++      case RTC_ALM_SET:
++              if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2)))
++                      return -EFAULT;
++              decodetime (RCNR, &tm);
++              if ((unsigned)tm2.tm_hour < 24)
++                      tm.tm_hour = tm2.tm_hour;
++              if ((unsigned)tm2.tm_min < 60)
++                      tm.tm_min = tm2.tm_min;
++              if ((unsigned)tm2.tm_sec < 60)
++                      tm.tm_sec = tm2.tm_sec;
++              RTAR = mktime ( tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
++                              tm.tm_hour, tm.tm_min, tm.tm_sec);
++              return 0;
++      case RTC_RD_TIME:
++              decodetime (RCNR, &tm);
++              break;
++      case RTC_SET_TIME:
++              if (!capable(CAP_SYS_TIME))
++                      return -EACCES;
++              if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm)))
++                      return -EFAULT;
++              tm.tm_year += 1900;
++              if (tm.tm_year < 1970 || (unsigned)tm.tm_mon >= 12 ||
++                  tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] +
++                              (tm.tm_mon == 1 && is_leap(tm.tm_year))) ||
++                  (unsigned)tm.tm_hour >= 24 ||
++                  (unsigned)tm.tm_min >= 60 ||
++                  (unsigned)tm.tm_sec >= 60)
++                      return -EINVAL;
++              RCNR = mktime ( tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
++                              tm.tm_hour, tm.tm_min, tm.tm_sec);
++              return 0;
++      case RTC_IRQP_READ:
++              return put_user(rtc_freq, (unsigned long *)arg);
++      case RTC_IRQP_SET:
++              if (arg < 1 || arg > TIMER_FREQ)
++                              return -EINVAL;
++              if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
++                              return -EACCES;
++              rtc_freq = arg;
++              return 0;
++      case RTC_EPOCH_READ:
++              return put_user (1970, (unsigned long *)arg);
++      default:
++              return -EINVAL;
++      }
++      return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0;
++}
++
++static struct file_operations rtc_fops = {
++      owner:          THIS_MODULE,
++      llseek:         rtc_llseek,
++      read:           rtc_read,
++      poll:           rtc_poll,
++      ioctl:          rtc_ioctl,
++      open:           rtc_open,
++      release:        rtc_release,
++      fasync:         rtc_fasync,
++};
++
++static struct miscdevice sa1100rtc_miscdev = {
++      RTC_MINOR,
++      "rtc",
++      &rtc_fops
++};
++
++static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++      char *p = page;
++      int len;
++      struct rtc_time tm;
++
++      decodetime (RCNR, &tm);
++      p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n"
++                      "rtc_date\t: %04d-%02d-%02d\n"
++                      "rtc_epoch\t: %04d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1970);
++      decodetime (RTAR, &tm);
++      p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n"
++                      "alrm_date\t: %04d-%02d-%02d\n",
++                      tm.tm_hour, tm.tm_min, tm.tm_sec,
++                      tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
++      p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR);
++      p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" );
++      p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no");
++      p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no");
++      p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq);
++
++      len = (p - page) - off;
++      if (len < 0)
++              len = 0;
++
++      *eof = (len <= count) ? 1 : 0;
++      *start = page + off;
++
++      return len;
++}
++
++static int __init rtc_init(void)
++{
++      int ret;
++
++      misc_register (&sa1100rtc_miscdev);
++      create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
++      ret = request_irq (IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL);
++      if (ret) {
++              printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC1Hz);
++              goto IRQ_RTC1Hz_failed;
++      }
++      ret = request_irq (IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL);
++      if (ret) {
++              printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCAlrm);
++              goto IRQ_RTCAlrm_failed;
++      }
++      ret = request_irq (IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL);
++      if (ret) {
++              printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_OST1);
++              goto IRQ_OST1_failed;
++      }
++
++      printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n");
++
++      /*
++       * According to the manual we should be able to let RTTR be zero
++       * and then a default diviser for a 32.768KHz clock is used.
++       * Apparently this doesn't work, at least for my SA1110 rev 5.
++       * If the clock divider is uninitialized then reset it to the
++       * default value to get the 1Hz clock.
++       */
++      if (RTTR == 0) {
++              RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
++              printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n");
++              /*  The current RTC value probably doesn't make sense either */
++              RCNR = 0;
++      }
++
++      return 0;
++
++IRQ_OST1_failed:
++      free_irq (IRQ_RTCAlrm, NULL);
++IRQ_RTCAlrm_failed:
++      free_irq (IRQ_RTC1Hz, NULL);
++IRQ_RTC1Hz_failed:
++      remove_proc_entry ("driver/rtc", NULL);
++      misc_deregister (&sa1100rtc_miscdev);
++      return ret;
++}
++
++static void __exit rtc_exit(void)
++{
++      free_irq (IRQ_OST1, NULL);
++      free_irq (IRQ_RTCAlrm, NULL);
++      free_irq (IRQ_RTC1Hz, NULL);
++      remove_proc_entry ("driver/rtc", NULL);
++      misc_deregister (&sa1100rtc_miscdev);
++}
++
++module_init(rtc_init);
++module_exit(rtc_exit);
++
++MODULE_AUTHOR("Nils Faerber <nils@@kernelconcepts.de>");
++MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)");
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/char/sa1100_wdt.c
+@@ -0,0 +1,150 @@
++/*
++ *    Watchdog driver for the SA11x0
++ *
++ *      (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
++ *          Based on SoftDog driver by Alan Cox <alan@redhat.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 of the License, or (at your option) any later version.
++ *
++ *    Neither Oleg Drokin nor iXcelerator.com admit liability nor provide
++ *    warranty for any of this software. This material is provided
++ *    "AS-IS" and at no charge.
++ *
++ *    (c) Copyright 2000           Oleg Drokin <green@crimea.edu>
++ *
++ *      27/11/2000 Initial release
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/miscdevice.h>
++#include <linux/watchdog.h>
++#include <linux/reboot.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++
++#define TIMER_MARGIN  60              /* (secs) Default is 1 minute */
++
++static int sa1100_margin = TIMER_MARGIN;      /* in seconds */
++static int sa1100wdt_users;
++static int pre_margin;
++#ifdef MODULE
++MODULE_PARM(sa1100_margin,"i");
++#endif
++
++/*
++ *    Allow only one person to hold it open
++ */
++
++static int sa1100dog_open(struct inode *inode, struct file *file)
++{
++      if(test_and_set_bit(1,&sa1100wdt_users))
++              return -EBUSY;
++      MOD_INC_USE_COUNT;
++      /* Activate SA1100 Watchdog timer */
++      pre_margin=3686400 * sa1100_margin;
++      OSMR3 = OSCR + pre_margin;
++      OSSR = OSSR_M3;
++      OWER = OWER_WME;
++      OIER |= OIER_E3;
++      return 0;
++}
++
++static int sa1100dog_release(struct inode *inode, struct file *file)
++{
++      /*
++       *      Shut off the timer.
++       *      Lock it in if it's a module and we defined ...NOWAYOUT
++       */
++      OSMR3 = OSCR + pre_margin;
++#ifndef CONFIG_WATCHDOG_NOWAYOUT
++      OIER &= ~OIER_E3;
++#endif
++      sa1100wdt_users = 0;
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static ssize_t sa1100dog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
++{
++      /*  Can't seek (pwrite) on this device  */
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++
++      /* Refresh OSMR3 timer. */
++      if(len) {
++              OSMR3 = OSCR + pre_margin;
++              return 1;
++      }
++      return 0;
++}
++
++static int sa1100dog_ioctl(struct inode *inode, struct file *file,
++      unsigned int cmd, unsigned long arg)
++{
++      static struct watchdog_info ident = {
++              identity: "SA1100 Watchdog",
++      };
++
++      switch(cmd){
++      default:
++              return -ENOIOCTLCMD;
++      case WDIOC_GETSUPPORT:
++              return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
++      case WDIOC_GETSTATUS:
++              return put_user(0,(int *)arg);
++      case WDIOC_GETBOOTSTATUS:
++              return put_user((RCSR & RCSR_WDR) ? WDIOF_CARDRESET : 0, (int *)arg);
++      case WDIOC_KEEPALIVE:
++              OSMR3 = OSCR + pre_margin;
++              return 0;
++      }
++}
++
++static struct file_operations sa1100dog_fops=
++{
++      owner:          THIS_MODULE,
++      write:          sa1100dog_write,
++      ioctl:          sa1100dog_ioctl,
++      open:           sa1100dog_open,
++      release:        sa1100dog_release,
++};
++
++static struct miscdevice sa1100dog_miscdev=
++{
++      WATCHDOG_MINOR,
++      "SA1100 watchdog",
++      &sa1100dog_fops
++};
++
++static int __init sa1100dog_init(void)
++{
++      int ret;
++
++      ret = misc_register(&sa1100dog_miscdev);
++
++      if (ret)
++              return ret;
++
++      printk("SA1100 Watchdog Timer: timer margin %d sec\n", sa1100_margin);
++
++      return 0;
++}
++
++static void __exit sa1100dog_exit(void)
++{
++      misc_deregister(&sa1100dog_miscdev);
++}
++
++module_init(sa1100dog_init);
++module_exit(sa1100dog_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/char/sa1111_keyb.c
+@@ -0,0 +1,1153 @@
++/*
++ * SA1111 PS/2 keyboard/mouse driver
++ *
++ * 2000 by VASARA RESEARCH INC.
++ *
++ * Changelog:
++ *     Jun.xx,2000:    Kunihiko IMAI <imai@vasara.co.jp>
++ *                     Port to 2.4.0test1-ac19-rmk1-np1
++ *     Apr.17,2000:    Takafumi Kawana <kawana@pro.or.jp>
++ *                     Internal Release for XP860
++ *
++ *
++ * This driver is based on linux/drivers/char/pc_keyb.c
++ * Original declaration follows:
++
++ *
++ * linux/drivers/char/pc_keyb.c
++ *
++ * Separation of the PC low-level part by Geert Uytterhoeven, May 1997
++ * See keyboard.c for the whole history.
++ *
++ * Major cleanup by Martin Mares, May 1997
++ *
++ * Combined the keyboard and PS/2 mouse handling into one file,
++ * because they share the same hardware.
++ * Johan Myreen <jem@iki.fi> 1998-10-08.
++ *
++ * Code fixes to handle mouse ACKs properly.
++ * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29.
++ *
++ */
++#include <linux/config.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/mm.h>
++#include <linux/signal.h>
++#include <linux/init.h>
++#include <linux/kbd_ll.h>
++#include <linux/delay.h>
++#include <linux/random.h>
++#include <linux/poll.h>
++#include <linux/miscdevice.h>
++#include <linux/slab.h>
++#include <linux/kbd_kern.h>
++#include <linux/ioport.h>
++
++#include <asm/hardware.h>
++#include <asm/bitops.h>
++#include <asm/uaccess.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++
++#include <asm/io.h>
++
++/* Some configuration switches are present in the include file... */
++
++#include <linux/pc_keyb.h>
++#include <asm/keyboard.h>
++#include <asm/hardware/sa1111.h>
++
++#define KBD_STAT_RXB    (1<<4)
++#define KBD_STAT_RXF    (1<<5)
++#define KBD_STAT_TXB    (1<<6)
++#define KBD_STAT_TXE    (1<<7)
++#define KBD_STAT_STP    (1<<8)
++
++#define MSE_STAT_RXB    (1<<4)
++#define MSE_STAT_RXF    (1<<5)
++#define MSE_STAT_TXB    (1<<6)
++#define MSE_STAT_TXE    (1<<7)
++#define MSE_STAT_STP    (1<<8)
++
++/* Simple translation table for the SysRq keys */
++
++#ifdef CONFIG_MAGIC_SYSRQ
++unsigned char sa1111_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 */
++#endif
++
++// static void kbd_write_command_w(int data);
++static void kbd_write_output_w(int data);
++
++spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
++static void handle_kbd_event(void);
++
++/* used only by send_data - set by keyboard_interrupt */
++static volatile unsigned char reply_expected = 0;
++static volatile unsigned char acknowledge = 0;
++static volatile unsigned char resend = 0;
++
++
++#if defined CONFIG_PSMOUSE
++/*
++ *     PS/2 Auxiliary Device
++ */
++
++static int __init psaux_init(void);
++
++static struct aux_queue *queue;       /* Mouse data buffer. */
++static int aux_count = 0;
++/* used when we send commands to the mouse that expect an ACK. */
++static unsigned char mouse_reply_expected = 0;
++
++#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT)
++#define AUX_INTS_ON  (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT)
++
++#define MAX_RETRIES    60     /* some aux operations take long time */
++#endif                                /* CONFIG_PSMOUSE */
++
++/*
++ * Wait for keyboard controller input buffer to drain.
++ *
++ * Don't use 'jiffies' so that we don't depend on
++ * interrupts..
++ *
++ * Quote from PS/2 System Reference Manual:
++ *
++ * "Address hex 0060 and address hex 0064 should be written only when
++ * the input-buffer-full bit and output-buffer-full bit in the
++ * Controller Status register are set 0."
++ */
++
++static void kb_wait(void)
++{
++      unsigned long timeout = KBC_TIMEOUT;
++
++      do {
++              /*
++               * "handle_kbd_event()" will handle any incoming events
++               * while we wait - keypresses or mouse movement.
++               */
++              handle_kbd_event();
++              if (KBDSTAT & KBD_STAT_TXE)
++                      return;
++              mdelay(1);
++              timeout--;
++      }
++      while (timeout);
++#ifdef KBD_REPORT_TIMEOUTS
++      printk(KERN_WARNING "Keyboard timed out[1]\n");
++#endif
++}
++
++/*
++ * 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
++
++/* for USB 106 keyboard */
++#define E0_YEN         124
++#define E0_BACKSLASH   89
++
++
++#define E1_PAUSE   119
++
++/*
++ * The keycodes below are randomly located in 89-95,112-118,120-127.
++ * They could be thrown away (and all occurrences below replaced by 0),
++ * but that would force many users to use the `setkeycodes' utility, where
++ * they needed not before. It does not matter that there are duplicates, as
++ * long as no duplication occurs for any single keyboard.
++ */
++#define SC_LIM 89
++
++#define FOCUS_PF1 85          /* actual code! */
++#define FOCUS_PF2 89
++#define FOCUS_PF3 90
++#define FOCUS_PF4 91
++#define FOCUS_PF5 92
++#define FOCUS_PF6 93
++#define FOCUS_PF7 94
++#define FOCUS_PF8 95
++#define FOCUS_PF9 120
++#define FOCUS_PF10 121
++#define FOCUS_PF11 122
++#define FOCUS_PF12 123
++
++#define JAP_86     124
++/* tfj@olivia.ping.dk:
++ * The four keys are located over the numeric keypad, and are
++ * labelled A1-A4. It's an rc930 keyboard, from
++ * Regnecentralen/RC International, Now ICL.
++ * Scancodes: 59, 5a, 5b, 5c.
++ */
++#define RGN1 124
++#define RGN2 125
++#define RGN3 126
++#define RGN4 127
++
++static unsigned char high_keys[128 - SC_LIM] = {
++      RGN1, RGN2, RGN3, RGN4, 0, 0, 0,        /* 0x59-0x5f */
++      0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
++      0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12,       /* 0x68-0x6f */
++      0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */
++      FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7,     /* 0x78-0x7b */
++      FOCUS_PF8, JAP_86, FOCUS_PF10, 0        /* 0x7c-0x7f */
++};
++
++/* BTC */
++#define E0_MACRO   112
++/* LK450 */
++#define E0_F13     113
++#define E0_F14     114
++#define E0_HELP    115
++#define E0_DO      116
++#define E0_F17     117
++#define E0_KPMINPLUS 118
++/*
++ * My OmniKey generates e0 4c for  the "OMNI" key and the
++ * right alt key does nada. [kkoller@nyx10.cs.du.edu]
++ */
++#define E0_OK  124
++/*
++ * 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
++#define E0_MSRW        126
++#define E0_MSTM        127
++
++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, E0_KPENTER, 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, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
++      E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP,   /* 0x38-0x3f */
++      E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME,   /* 0x40-0x47 */
++      E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,      /* 0x48-0x4f */
++      E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0,   /* 0x50-0x57 */
++      0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0,       /* 0x58-0x5f */
++      0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
++      0, 0, 0, 0, 0, 0, 0, E0_MACRO,  /* 0x68-0x6f */
++      //0, 0, 0, 0, 0, 0, 0, 0,                          /* 0x70-0x77 */
++      0, 0, 0, 0, 0, E0_BACKSLASH, 0, 0,      /* 0x70-0x77 */
++      0, 0, 0, E0_YEN, 0, 0, 0, 0     /* 0x78-0x7f */
++};
++
++int sa1111_setkeycode(unsigned int scancode, unsigned int keycode)
++{
++      if (scancode < SC_LIM || scancode > 255 || keycode > 127)
++              return -EINVAL;
++      if (scancode < 128)
++              high_keys[scancode - SC_LIM] = keycode;
++      else
++              e0_keys[scancode - 128] = keycode;
++      return 0;
++}
++
++int sa1111_getkeycode(unsigned int scancode)
++{
++      return
++          (scancode < SC_LIM || scancode > 255) ? -EINVAL :
++          (scancode <
++           128) ? high_keys[scancode - SC_LIM] : e0_keys[scancode - 128];
++}
++
++static int do_acknowledge(unsigned char scancode)
++{
++      if (reply_expected) {
++              /* Unfortunately, we must recognise these codes only if we know they
++               * are known to be valid (i.e., after sending a command), because there
++               * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have
++               * keys with such codes :(
++               */
++              if (scancode == KBD_REPLY_ACK) {
++                      acknowledge = 1;
++                      reply_expected = 0;
++                      return 0;
++              } else if (scancode == KBD_REPLY_RESEND) {
++                      resend = 1;
++                      reply_expected = 0;
++                      return 0;
++              }
++              /* Should not happen... */
++#if 0
++              printk(KERN_DEBUG "keyboard reply expected - got %02x\n",
++                     scancode);
++#endif
++      }
++      return 1;
++}
++
++int
++sa1111_translate(unsigned char scancode, unsigned char *keycode,
++               char raw_mode)
++{
++      static int prev_scancode = 0;
++
++      /* special prefix scancodes.. */
++      if (scancode == 0xe0 || scancode == 0xe1) {
++              prev_scancode = scancode;
++              return 0;
++      }
++
++      /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
++      if (scancode == 0x00 || scancode == 0xff) {
++              prev_scancode = 0;
++              return 0;
++      }
++
++      scancode &= 0x7f;
++
++      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;
++                      /*
++                       *  The keyboard maintains its own internal caps lock and
++                       *  num lock statuses. In caps lock mode E0 AA precedes make
++                       *  code and E0 2A follows break code. In num lock mode,
++                       *  E0 2A precedes make code and E0 AA follows break code.
++                       *  We do our own book-keeping, so we will just ignore these.
++                       */
++                      /*
++                       *  For my keyboard there is no caps lock mode, but there are
++                       *  both Shift-L and Shift-R modes. The former mode generates
++                       *  E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
++                       *  So, we should also ignore the latter. - aeb@cwi.nl
++                       */
++                      if (scancode == 0x2a || scancode == 0x36)
++                              return 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 if (scancode >= SC_LIM) {
++              /* This happens with the FOCUS 9000 keyboard
++                 Its keys PF1..PF12 are reported to generate
++                 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
++                 Moreover, unless repeated, they do not generate
++                 key-down events, so we have to zero up_flag below */
++              /* Also, Japanese 86/106 keyboards are reported to
++                 generate 0x73 and 0x7d for \ - and \ | respectively. */
++              /* Also, some Brazilian keyboard is reported to produce
++                 0x73 and 0x7e for \ ? and KP-dot, respectively. */
++
++              *keycode = high_keys[scancode - SC_LIM];
++
++              if (!*keycode) {
++                      if (!raw_mode) {
++#ifdef KBD_REPORT_UNKN
++                              printk(KERN_INFO
++                                     "keyboard: unrecognized scancode (%02x)"
++                                     " - ignored\n", scancode);
++#endif
++                      }
++                      return 0;
++              }
++      } else
++              *keycode = scancode;
++      return 1;
++}
++
++char sa1111_unexpected_up(unsigned char keycode)
++{
++      /* unexpected, but this can happen: maybe this was a key release for a
++         FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */
++      if (keycode >= SC_LIM || keycode == 85)
++              return 0;
++      else
++              return 0200;
++}
++
++static unsigned char kbd_exists = 1;
++
++static inline void handle_keyboard_event(unsigned char scancode)
++{
++#ifdef CONFIG_VT
++      kbd_exists = 1;
++      if (do_acknowledge(scancode))
++              handle_scancode(scancode, !(scancode & 0x80));
++#endif
++      tasklet_schedule(&keyboard_tasklet);
++}
++
++/*
++ * This reads the keyboard status port, and does the
++ * appropriate action.
++ *
++ * It requires that we hold the keyboard controller
++ * spinlock.
++ */
++static void handle_kbd_event(void)
++{
++      unsigned int status = KBDSTAT;
++      unsigned int work = 10000;
++      unsigned char scancode;
++
++      while (status & KBD_STAT_RXF) {
++              while (status & KBD_STAT_RXF) {
++                      scancode = KBDDATA & 0xff;
++                      if (!(status & KBD_STAT_STP))
++                              handle_keyboard_event(scancode);
++                      if (!--work) {
++                              printk(KERN_ERR
++                                     "pc_keyb: keyboard controller jammed (0x%02X).\n",
++                                     status);
++                              return;
++                      }
++                      status = KBDSTAT;
++              }
++              work = 10000;
++      }
++
++      if (status & KBD_STAT_STP)
++              KBDSTAT = KBD_STAT_STP;
++}
++
++static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned long flags;
++
++#ifdef CONFIG_VT
++      kbd_pt_regs = regs;
++#endif
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      handle_kbd_event();
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++
++/*
++ * send_data sends a character to the keyboard and waits
++ * for an acknowledge, possibly retrying if asked to. Returns
++ * the success status.
++ *
++ * Don't use 'jiffies', so that we don't depend on interrupts
++ */
++static int send_data(unsigned char data)
++{
++      int retries = 3;
++
++      do {
++              unsigned long timeout = KBD_TIMEOUT;
++
++              acknowledge = 0;        /* Set by interrupt routine on receipt of ACK. */
++              resend = 0;
++              reply_expected = 1;
++              kbd_write_output_w(data);
++              for (;;) {
++                      if (acknowledge)
++                              return 1;
++                      if (resend)
++                              break;
++                      mdelay(1);
++                      if (!--timeout) {
++#ifdef KBD_REPORT_TIMEOUTS
++                              printk(KERN_WARNING
++                                     "keyboard: Timeout - AT keyboard not present?\n");
++#endif
++                              return 0;
++                      }
++              }
++      }
++      while (retries-- > 0);
++#ifdef KBD_REPORT_TIMEOUTS
++      printk(KERN_WARNING
++             "keyboard: Too many NACKs -- noisy kbd cable?\n");
++#endif
++      return 0;
++}
++
++void sa1111_leds(unsigned char leds)
++{
++      if (kbd_exists
++          && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) {
++              send_data(KBD_CMD_ENABLE);      /* re-enable kbd if any errors */
++              kbd_exists = 0;
++      }
++}
++
++#define KBD_NO_DATA    (-1)   /* No data */
++#define KBD_BAD_DATA   (-2)   /* Parity or other error */
++
++static int __init kbd_read_data(void)
++{
++      int retval = KBD_NO_DATA;
++      unsigned int status;
++
++      status = KBDSTAT;
++      if (status & KBD_STAT_RXF) {
++              unsigned char data = KBDDATA;
++
++              retval = data;
++              if (status & KBD_STAT_STP)
++                      retval = KBD_BAD_DATA;
++      }
++      return retval;
++}
++
++static void __init kbd_clear_input(void)
++{
++      int maxread = 100;      /* Random number */
++
++      do {
++              if (kbd_read_data() == KBD_NO_DATA)
++                      break;
++      }
++      while (--maxread);
++}
++
++static int __init kbd_wait_for_input(void)
++{
++      long timeout = KBD_INIT_TIMEOUT;
++
++      do {
++              int retval = kbd_read_data();
++              if (retval >= 0)
++                      return retval;
++              mdelay(1);
++      }
++      while (--timeout);
++      return -1;
++}
++
++#if 0
++static void kbd_write_command_w(int data)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      kb_wait();
++      kbd_write_command(data);
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++#endif
++
++static void kbd_write_output_w(int data)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      kb_wait();
++      KBDDATA = data & 0xff;
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++
++/*
++ * Test the keyboard interface.  We basically check to make sure that
++ * we can drive each line to the keyboard independently of each other.
++ */
++static int kbdif_test(void)
++{
++      int ret = 0;
++
++      KBDCR = KBDCR_ENA | KBDCR_FKC;
++      udelay(2);
++      if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBD) {
++              printk("Keyboard interface test failed[1]: %02x\n",
++                     KBDSTAT);
++              ret = -ENODEV;
++      }
++
++      KBDCR = KBDCR_ENA;
++      udelay(2);
++      if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != (KBDSTAT_KBC | KBDSTAT_KBD)) {
++              printk("Keyboard interface test failed[2]: %02x\n",
++                     KBDSTAT);
++              ret = -ENODEV;
++      }
++
++      KBDCR = KBDCR_ENA | KBDCR_FKD;
++      udelay(2);
++      if ((KBDSTAT & (KBDSTAT_KBC | KBDSTAT_KBD)) != KBDSTAT_KBC) {
++              printk("Keyboard interface test failed[3]: %02x\n",
++                     KBDSTAT);
++              ret = -ENODEV;
++      }
++
++      return ret;
++}
++
++static char *__init initialize_kbd(void)
++{
++      int status;
++
++      /*
++       * Test the keyboard interface.
++       */
++      kbdif_test();
++
++      /*
++       * Ok, drop the force low bits, and wait a while,
++       * and clear the stop bit error flag.
++       */
++      KBDCR = KBDCR_ENA;
++      udelay(4);
++      KBDSTAT = KBD_STAT_STP;
++
++      /*
++       * Ok, we're now ready to talk to the keyboard.  Reset
++       * it, just to make sure we're starting in a sane state.
++       *
++       * Set up to try again if the keyboard asks for RESEND.
++       */
++      do {
++              KBDDATA = KBD_CMD_RESET;
++              status = kbd_wait_for_input();
++              if (status == KBD_REPLY_ACK)
++                      break;
++              if (status != KBD_REPLY_RESEND)
++                      return "Keyboard reset failed, no ACK";
++      } while (1);
++
++      if (kbd_wait_for_input() != KBD_REPLY_POR)
++              return "Keyboard reset failed, no POR";
++
++      /*
++       * Set keyboard controller mode. During this, the keyboard should be
++       * in the disabled state.
++       *
++       * Set up to try again if the keyboard asks for RESEND.
++       */
++      do {
++              kbd_write_output_w(KBD_CMD_DISABLE);
++              status = kbd_wait_for_input();
++              if (status == KBD_REPLY_ACK)
++                      break;
++              if (status != KBD_REPLY_RESEND)
++                      return "Disable keyboard: no ACK";
++      } while (1);
++
++#if 0                         /*@@@ */
++      kbd_write_command_w(KBD_CCMD_WRITE_MODE);
++      kbd_write_output_w(KBD_MODE_KBD_INT
++                         | KBD_MODE_SYS | KBD_MODE_DISABLE_MOUSE |
++                         KBD_MODE_KCC);
++
++      /* ibm powerpc portables need this to use scan-code set 1 -- Cort */
++      kbd_write_command_w(KBD_CCMD_READ_MODE);
++      if (!(kbd_wait_for_input() & KBD_MODE_KCC)) {
++              /*
++               * If the controller does not support conversion,
++               * Set the keyboard to scan-code set 1.
++               */
++              kbd_write_output_w(0xF0);
++              kbd_wait_for_input();
++              kbd_write_output_w(0x01);
++              kbd_wait_for_input();
++      }
++#else
++      kbd_write_output_w(0xf0);
++      kbd_wait_for_input();
++      kbd_write_output_w(0x01);
++      kbd_wait_for_input();
++#endif
++
++
++      kbd_write_output_w(KBD_CMD_ENABLE);
++      if (kbd_wait_for_input() != KBD_REPLY_ACK)
++              return "Enable keyboard: no ACK";
++
++      /*
++       * Finally, set the typematic rate to maximum.
++       */
++      kbd_write_output_w(KBD_CMD_SET_RATE);
++      if (kbd_wait_for_input() != KBD_REPLY_ACK)
++              return "Set rate: no ACK";
++      kbd_write_output_w(0x00);
++      if (kbd_wait_for_input() != KBD_REPLY_ACK)
++              return "Set rate: no ACK";
++
++      return NULL;
++}
++
++int __init sa1111_kbd_init_hw(void)
++{
++      char *msg;
++      int ret;
++
++      if (!request_mem_region(_KBDCR, 512, "keyboard"))
++              return -EBUSY;
++
++      SKPCR |= SKPCR_PTCLKEN;
++      KBDCLKDIV = 0;
++      KBDPRECNT = 127;
++
++      /* Flush any pending input. */
++      kbd_clear_input();
++
++      msg = initialize_kbd();
++      if (msg)
++              printk(KERN_WARNING "initialize_kbd: %s\n", msg);
++
++#if defined CONFIG_PSMOUSE
++      psaux_init();
++#endif
++
++      k_setkeycode    = sa1111_setkeycode;
++      k_getkeycode    = sa1111_getkeycode;
++      k_translate     = sa1111_translate;
++      k_unexpected_up = sa1111_unexpected_up;
++      k_leds          = sa1111_leds;
++#ifdef CONFIG_MAGIC_SYSRQ
++      k_sysrq_xlate   = sa1111_sysrq_xlate;
++      k_sysrq_key     = 0x54;
++#endif
++
++      /* Ok, finally allocate the IRQ, and off we go.. */
++      ret = request_irq(IRQ_TPRXINT, keyboard_interrupt, 0, "keyboard", NULL);
++      if (ret)
++              release_mem_region(_KBDCR, 512);
++
++      return ret;
++}
++
++#if defined CONFIG_PSMOUSE
++
++static inline void handle_mouse_event(unsigned char scancode)
++{
++      if (mouse_reply_expected) {
++              if (scancode == AUX_ACK) {
++                      mouse_reply_expected--;
++                      return;
++              }
++              mouse_reply_expected = 0;
++      }
++
++      add_mouse_randomness(scancode);
++      if (aux_count) {
++              int head = queue->head;
++
++              queue->buf[head] = scancode;
++              head = (head + 1) & (AUX_BUF_SIZE - 1);
++              if (head != queue->tail) {
++                      queue->head = head;
++                      if (queue->fasync)
++                              kill_fasync(&queue->fasync, SIGIO,
++                                          POLL_IN);
++                      wake_up_interruptible(&queue->proc_list);
++              }
++      }
++}
++
++static void handle_mse_event(void)
++{
++      unsigned int msests = MSESTAT;
++      unsigned int work = 10000;
++      unsigned char scancode;
++
++      while (msests & MSE_STAT_RXF) {
++              while (msests & MSE_STAT_RXF) {
++                      scancode = MSEDATA & 0xff;
++                      if (!(msests & MSE_STAT_STP))
++                              handle_mouse_event(scancode);
++                      if (!--work) {
++                              printk(KERN_ERR
++                                     "pc_keyb: mouse controller jammed (0x%02X).\n",
++                                     msests);
++                              return;
++                       /*XXX*/}
++                      msests = MSESTAT;
++              }
++              work = 10000;
++      }
++}
++
++static void ms_wait(void)
++{
++      unsigned long timeout = KBC_TIMEOUT;
++
++      do {
++              /*
++               * "handle_kbd_event()" will handle any incoming events
++               * while we wait - keypresses or mouse movement.
++               */
++              handle_mse_event();
++              if (MSESTAT & MSE_STAT_TXE)
++                      return;
++              mdelay(1);
++              timeout--;
++      }
++      while (timeout);
++#ifdef KBD_REPORT_TIMEOUTS
++      printk(KERN_WARNING "Mouse timed out[1]\n");
++#endif
++}
++
++static void mouse_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      handle_mse_event();
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++
++/*
++ * Check if this is a dual port controller.
++ */
++static int __init detect_auxiliary_port(void)
++{
++      unsigned long flags;
++      int loops = 10;
++      int retval = 0;
++
++      /* Check if the BIOS detected a device on the auxiliary port. */
++      if (aux_device_present == 0xaa)
++              return 1;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++
++      /* Put the value 0x5A in the output buffer using the "Write
++       * Auxiliary Device Output Buffer" command (0xD3). Poll the
++       * Status Register for a while to see if the value really
++       * turns up in the Data Register. If the KBD_STAT_MOUSE_OBF
++       * bit is also set to 1 in the Status Register, we assume this
++       * controller has an Auxiliary Port (a.k.a. Mouse Port).
++       */
++      // kb_wait();
++      // kbd_write_command(KBD_CCMD_WRITE_AUX_OBUF);
++
++      SKPCR |= SKPCR_PMCLKEN;
++
++      MSECLKDIV = 0;
++      MSEPRECNT = 127;
++      MSECR = MSECR_ENA;
++      mdelay(50);
++      MSEDATA = 0xf4;
++      mdelay(50);
++
++      do {
++              unsigned int msests = MSESTAT;
++
++              if (msests & MSE_STAT_RXF) {
++                      do {
++                              msests = MSEDATA;       /* dummy read */
++                              mdelay(50);
++                              msests = MSESTAT;
++                      }
++                      while (msests & MSE_STAT_RXF);
++                      printk(KERN_INFO "Detected PS/2 Mouse Port.\n");
++                      retval = 1;
++                      break;
++              }
++              mdelay(1);
++      }
++      while (--loops);
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++
++      return retval;
++}
++
++/*
++ * Send a byte to the mouse.
++ */
++static void aux_write_dev(int val)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      // kb_wait();
++      // kbd_write_command(KBD_CCMD_WRITE_MOUSE);
++      ms_wait();
++      MSEDATA = val;
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++
++/*
++ * Send a byte to the mouse & handle returned ack
++ */
++static void aux_write_ack(int val)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      // kb_wait();
++      // kbd_write_command(KBD_CCMD_WRITE_MOUSE);
++      ms_wait();
++      MSEDATA = val;
++      /* we expect an ACK in response. */
++      mouse_reply_expected++;
++      ms_wait();
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++}
++
++static unsigned char get_from_queue(void)
++{
++      unsigned char result;
++      unsigned long flags;
++
++      spin_lock_irqsave(&kbd_controller_lock, flags);
++      result = queue->buf[queue->tail];
++      queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE - 1);
++      spin_unlock_irqrestore(&kbd_controller_lock, flags);
++      return result;
++}
++
++
++static inline int queue_empty(void)
++{
++      return queue->head == queue->tail;
++}
++
++static int fasync_aux(int fd, struct file *filp, int on)
++{
++      int retval;
++
++      retval = fasync_helper(fd, filp, on, &queue->fasync);
++      if (retval < 0)
++              return retval;
++      return 0;
++}
++
++
++/*
++ * Random magic cookie for the aux device
++ */
++#define AUX_DEV ((void *)queue)
++
++static int release_aux(struct inode *inode, struct file *file)
++{
++      fasync_aux(-1, file, 0);
++      if (--aux_count)
++              return 0;
++      // kbd_write_cmd(AUX_INTS_OFF);                     /* Disable controller ints */
++      // kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE);
++      aux_write_ack(AUX_DISABLE_DEV); /* Disable aux device */
++      MSECR &= ~MSECR_ENA;
++      free_irq(IRQ_MSRXINT, AUX_DEV);
++      return 0;
++}
++
++/*
++ * Install interrupt handler.
++ * Enable auxiliary device.
++ */
++
++static int open_aux(struct inode *inode, struct file *file)
++{
++      if (aux_count++) {
++              return 0;
++      }
++      queue->head = queue->tail = 0;  /* Flush input queue */
++      /* Don't enable the mouse controller until we've registered IRQ handler */
++      if (request_irq(IRQ_MSRXINT, mouse_interrupt, SA_SHIRQ, "PS/2 Mouse", AUX_DEV)) {
++              aux_count--;
++              return -EBUSY;
++      }
++      MSECLKDIV = 0;
++      MSEPRECNT = 127;
++      MSECR &= ~MSECR_ENA;
++      mdelay(50);
++      MSECR = MSECR_ENA;
++      mdelay(50);
++      MSEDATA = 0xf4;
++      mdelay(50);
++      if (MSESTAT & 0x0100) {
++              MSESTAT = 0x0100;       /* clear IRQ status */
++      }
++/*  kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); *//* Enable the
++   auxiliary port on
++   controller. */
++      aux_write_ack(AUX_ENABLE_DEV);  /* Enable aux device */
++      // kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
++
++      // send_data(KBD_CMD_ENABLE);   /* try to workaround toshiba4030cdt problem */
++
++      return 0;
++}
++
++/*
++ * Put bytes from input queue to buffer.
++ */
++
++static ssize_t
++read_aux(struct file *file, char *buffer, size_t count, loff_t * ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      ssize_t i = count;
++      unsigned char c;
++
++      if (queue_empty()) {
++              if (file->f_flags & O_NONBLOCK)
++                      return -EAGAIN;
++              add_wait_queue(&queue->proc_list, &wait);
++            repeat:
++              set_current_state(TASK_INTERRUPTIBLE);
++              if (queue_empty() && !signal_pending(current)) {
++                      schedule();
++                      goto repeat;
++              }
++              current->state = TASK_RUNNING;
++              remove_wait_queue(&queue->proc_list, &wait);
++      }
++      while (i > 0 && !queue_empty()) {
++              c = get_from_queue();
++              put_user(c, buffer++);
++              i--;
++      }
++      if (count - i) {
++              file->f_dentry->d_inode->i_atime = CURRENT_TIME;
++              return count - i;
++      }
++      if (signal_pending(current))
++              return -ERESTARTSYS;
++      return 0;
++}
++
++/*
++ * Write to the aux device.
++ */
++
++static ssize_t
++write_aux(struct file *file, const char *buffer, size_t count,
++        loff_t * ppos)
++{
++      ssize_t retval = 0;
++
++      if (count) {
++              ssize_t written = 0;
++
++              if (count > 32)
++                      count = 32;     /* Limit to 32 bytes. */
++              do {
++                      char c;
++                      get_user(c, buffer++);
++                      aux_write_dev(c);
++                      written++;
++              }
++              while (--count);
++              retval = -EIO;
++              if (written) {
++                      retval = written;
++                      file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
++              }
++      }
++
++      return retval;
++}
++
++static unsigned int aux_poll(struct file *file, poll_table * wait)
++{
++      poll_wait(file, &queue->proc_list, wait);
++      if (!queue_empty())
++              return POLLIN | POLLRDNORM;
++      return 0;
++}
++
++struct file_operations psaux_fops = {
++      read:           read_aux,
++      write:          write_aux,
++      poll:           aux_poll,
++      open:           open_aux,
++      release:        release_aux,
++      fasync:         fasync_aux,
++};
++
++/*
++ * Initialize driver.
++ */
++static struct miscdevice psaux_mouse = {
++      PSMOUSE_MINOR, "psaux", &psaux_fops
++};
++
++
++static int __init psaux_init(void)
++{
++      int ret;
++
++      if (!request_mem_region(_MSECR, 512, "psaux"))
++              return -EBUSY;
++
++      if (!detect_auxiliary_port()) {
++              ret = -EIO;
++              goto out;
++      }
++
++      misc_register(&psaux_mouse);
++      queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
++      memset(queue, 0, sizeof(*queue));
++      queue->head = queue->tail = 0;
++      init_waitqueue_head(&queue->proc_list);
++
++#ifdef CONFIG_PSMOUSE
++      aux_write_ack(AUX_SET_SAMPLE);
++      aux_write_ack(100);     /* 100 samples/sec */
++      aux_write_ack(AUX_SET_RES);
++      aux_write_ack(3);       /* 8 counts per mm */
++      aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
++#endif
++      ret = 0;
++
++ out:
++      if (ret)
++              release_mem_region(_MSECR, 512);
++      return ret;
++}
++
++#endif                                /* CONFIG_PSMOUSE */
+--- linux-2.4.27/drivers/char/serial.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/serial.c
+@@ -4527,6 +4527,14 @@
+       }
+       /*
++       * 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.
+        */
+--- linux-2.4.27/drivers/char/serial_21285.c
++++ /dev/null
+@@ -1,498 +0,0 @@
+-/*
+- * linux/drivers/char/serial_21285.c
+- *
+- * Driver for the serial port on the 21285 StrongArm-110 core logic chip.
+- *
+- * Based on drivers/char/serial.c
+- */
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/errno.h>
+-#include <linux/signal.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+-#include <linux/tty.h>
+-#include <linux/tty_flip.h>
+-#include <linux/serial.h>
+-#include <linux/major.h>
+-#include <linux/ptrace.h>
+-#include <linux/ioport.h>
+-#include <linux/mm.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/console.h>
+-
+-#include <asm/io.h>
+-#include <asm/irq.h>
+-#include <asm/uaccess.h>
+-#include <asm/dec21285.h>
+-#include <asm/hardware.h>
+-
+-#define BAUD_BASE             (mem_fclk_21285/64)
+-
+-#define SERIAL_21285_NAME     "ttyFB"
+-#define SERIAL_21285_MAJOR    204
+-#define SERIAL_21285_MINOR    4
+-
+-#define SERIAL_21285_AUXNAME  "cuafb"
+-#define SERIAL_21285_AUXMAJOR 205
+-#define SERIAL_21285_AUXMINOR 4
+-
+-static struct tty_driver rs285_driver, callout_driver;
+-static int rs285_refcount;
+-static struct tty_struct *rs285_table[1];
+-
+-static struct termios *rs285_termios[1];
+-static struct termios *rs285_termios_locked[1];
+-
+-static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char;
+-static struct tty_struct *rs285_tty;
+-static int rs285_use_count;
+-
+-static int rs285_write_room(struct tty_struct *tty)
+-{
+-      return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1);
+-}
+-
+-static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs)
+-{
+-      if (!rs285_tty) {
+-              disable_irq(IRQ_CONRX);
+-              return;
+-      }
+-      while (!(*CSR_UARTFLG & 0x10)) {
+-              int ch, flag;
+-              ch = *CSR_UARTDR;
+-              flag = *CSR_RXSTAT;
+-              if (flag & 4)
+-                      tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN);
+-              if (flag & 2)
+-                      flag = TTY_PARITY;
+-              else if (flag & 1)
+-                      flag = TTY_FRAME;
+-              tty_insert_flip_char(rs285_tty, ch, flag);
+-      }
+-      tty_flip_buffer_push(rs285_tty);
+-}
+-
+-static void rs285_send_xchar(struct tty_struct *tty, char ch)
+-{
+-      x_char = ch;
+-      enable_irq(IRQ_CONTX);
+-}
+-
+-static void rs285_throttle(struct tty_struct *tty)
+-{
+-      if (I_IXOFF(tty))
+-              rs285_send_xchar(tty, STOP_CHAR(tty));
+-}
+-
+-static void rs285_unthrottle(struct tty_struct *tty)
+-{
+-      if (I_IXOFF(tty)) {
+-              if (x_char)
+-                      x_char = 0;
+-              else
+-                      rs285_send_xchar(tty, START_CHAR(tty));
+-      }
+-}
+-
+-static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs)
+-{
+-      while (!(*CSR_UARTFLG & 0x20)) {
+-              if (x_char) {
+-                      *CSR_UARTDR = x_char;
+-                      x_char = 0;
+-                      continue;
+-              }
+-              if (putp == getp) {
+-                      disable_irq(IRQ_CONTX);
+-                      break;
+-              }
+-              *CSR_UARTDR = *getp;
+-              if (++getp >= wbuf + sizeof(wbuf))
+-                      getp = wbuf;
+-      }
+-      if (rs285_tty)
+-              wake_up_interruptible(&rs285_tty->write_wait);
+-}
+-
+-static inline int rs285_xmit(int ch)
+-{
+-      if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf))
+-              return 0;
+-      *putp = ch;
+-      if (++putp >= wbuf + sizeof(wbuf))
+-              putp = wbuf;
+-      enable_irq(IRQ_CONTX);
+-      return 1;
+-}
+-
+-static int rs285_write(struct tty_struct *tty, int from_user,
+-                     const u_char * buf, int count)
+-{
+-      int i;
+-
+-      if (from_user && verify_area(VERIFY_READ, buf, count))
+-              return -EINVAL;
+-
+-      for (i = 0; i < count; i++) {
+-              char ch;
+-              if (from_user)
+-                      __get_user(ch, buf + i);
+-              else
+-                      ch = buf[i];
+-              if (!rs285_xmit(ch))
+-                      break;
+-      }
+-      return i;
+-}
+-
+-static void rs285_put_char(struct tty_struct *tty, u_char ch)
+-{
+-      rs285_xmit(ch);
+-}
+-
+-static int rs285_chars_in_buffer(struct tty_struct *tty)
+-{
+-      return sizeof(wbuf) - rs285_write_room(tty);
+-}
+-
+-static void rs285_flush_buffer(struct tty_struct *tty)
+-{
+-      disable_irq(IRQ_CONTX);
+-      putp = getp = wbuf;
+-      if (x_char)
+-              enable_irq(IRQ_CONTX);
+-}
+-
+-static inline void rs285_set_cflag(int cflag)
+-{
+-      int h_lcr, baud, quot;
+-
+-      switch (cflag & CSIZE) {
+-      case CS5:
+-              h_lcr = 0x10;
+-              break;
+-      case CS6:
+-              h_lcr = 0x30;
+-              break;
+-      case CS7:
+-              h_lcr = 0x50;
+-              break;
+-      default: /* CS8 */
+-              h_lcr = 0x70;
+-              break;
+-
+-      }
+-      if (cflag & CSTOPB)
+-              h_lcr |= 0x08;
+-      if (cflag & PARENB)
+-              h_lcr |= 0x02;
+-      if (!(cflag & PARODD))
+-              h_lcr |= 0x04;
+-
+-      switch (cflag & CBAUD) {
+-      case B200:      baud = 200;             break;
+-      case B300:      baud = 300;             break;
+-      case B1200:     baud = 1200;            break;
+-      case B1800:     baud = 1800;            break;
+-      case B2400:     baud = 2400;            break;
+-      case B4800:     baud = 4800;            break;
+-      default:
+-      case B9600:     baud = 9600;            break;
+-      case B19200:    baud = 19200;           break;
+-      case B38400:    baud = 38400;           break;
+-      case B57600:    baud = 57600;           break;
+-      case B115200:   baud = 115200;          break;
+-      }
+-
+-      /*
+-       * The documented expression for selecting the divisor is:
+-       *  BAUD_BASE / baud - 1
+-       * However, typically BAUD_BASE is not divisible by baud, so
+-       * we want to select the divisor that gives us the minimum
+-       * error.  Therefore, we want:
+-       *  int(BAUD_BASE / baud - 0.5) ->
+-       *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->
+-       *  int((BAUD_BASE - (baud >> 1)) / baud)
+-       */
+-      quot = (BAUD_BASE - (baud >> 1)) / baud;
+-
+-      *CSR_UARTCON = 0;
+-      *CSR_L_UBRLCR = quot & 0xff;
+-      *CSR_M_UBRLCR = (quot >> 8) & 0x0f;
+-      *CSR_H_UBRLCR = h_lcr;
+-      *CSR_UARTCON = 1;
+-}
+-
+-static void rs285_set_termios(struct tty_struct *tty, struct termios *old)
+-{
+-      if (old && tty->termios->c_cflag == old->c_cflag)
+-              return;
+-      rs285_set_cflag(tty->termios->c_cflag);
+-}
+-
+-
+-static void rs285_stop(struct tty_struct *tty)
+-{
+-      disable_irq(IRQ_CONTX);
+-}
+-
+-static void rs285_start(struct tty_struct *tty)
+-{
+-      enable_irq(IRQ_CONTX);
+-}
+-
+-static void rs285_wait_until_sent(struct tty_struct *tty, int timeout)
+-{
+-      int orig_jiffies = jiffies;
+-      while (*CSR_UARTFLG & 8) {
+-              current->state = TASK_INTERRUPTIBLE;
+-              schedule_timeout(1);
+-              if (signal_pending(current))
+-                      break;
+-              if (timeout && time_after(jiffies, orig_jiffies + timeout))
+-                      break;
+-      }
+-      current->state = TASK_RUNNING;
+-}
+-
+-static int rs285_open(struct tty_struct *tty, struct file *filp)
+-{
+-      int line;
+-
+-      MOD_INC_USE_COUNT;
+-      line = MINOR(tty->device) - tty->driver.minor_start;
+-      if (line) {
+-              MOD_DEC_USE_COUNT;
+-              return -ENODEV;
+-      }
+-
+-      tty->driver_data = NULL;
+-      if (!rs285_tty)
+-              rs285_tty = tty;
+-
+-      enable_irq(IRQ_CONRX);
+-      rs285_use_count++;
+-      return 0;
+-}
+-
+-static void rs285_close(struct tty_struct *tty, struct file *filp)
+-{
+-      if (!--rs285_use_count) {
+-              rs285_wait_until_sent(tty, 0);
+-              disable_irq(IRQ_CONRX);
+-              disable_irq(IRQ_CONTX);
+-              rs285_tty = NULL;
+-      }
+-      MOD_DEC_USE_COUNT;
+-}
+-
+-static int __init rs285_init(void)
+-{
+-      int baud = B9600;
+-
+-      if (machine_is_personal_server())
+-              baud = B57600;
+-
+-      rs285_driver.magic = TTY_DRIVER_MAGIC;
+-      rs285_driver.driver_name = "serial_21285";
+-      rs285_driver.name = SERIAL_21285_NAME;
+-      rs285_driver.major = SERIAL_21285_MAJOR;
+-      rs285_driver.minor_start = SERIAL_21285_MINOR;
+-      rs285_driver.num = 1;
+-      rs285_driver.type = TTY_DRIVER_TYPE_SERIAL;
+-      rs285_driver.subtype = SERIAL_TYPE_NORMAL;
+-      rs285_driver.init_termios = tty_std_termios;
+-      rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL;
+-      rs285_driver.flags = TTY_DRIVER_REAL_RAW;
+-      rs285_driver.refcount = &rs285_refcount;
+-      rs285_driver.table = rs285_table;
+-      rs285_driver.termios = rs285_termios;
+-      rs285_driver.termios_locked = rs285_termios_locked;
+-
+-      rs285_driver.open = rs285_open;
+-      rs285_driver.close = rs285_close;
+-      rs285_driver.write = rs285_write;
+-      rs285_driver.put_char = rs285_put_char;
+-      rs285_driver.write_room = rs285_write_room;
+-      rs285_driver.chars_in_buffer = rs285_chars_in_buffer;
+-      rs285_driver.flush_buffer = rs285_flush_buffer;
+-      rs285_driver.throttle = rs285_throttle;
+-      rs285_driver.unthrottle = rs285_unthrottle;
+-      rs285_driver.send_xchar = rs285_send_xchar;
+-      rs285_driver.set_termios = rs285_set_termios;
+-      rs285_driver.stop = rs285_stop;
+-      rs285_driver.start = rs285_start;
+-      rs285_driver.wait_until_sent = rs285_wait_until_sent;
+-
+-      callout_driver = rs285_driver;
+-      callout_driver.name = SERIAL_21285_AUXNAME;
+-      callout_driver.major = SERIAL_21285_AUXMAJOR;
+-      callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+-
+-      if (request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL))
+-              panic("Couldn't get rx irq for rs285");
+-
+-      if (request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285", NULL))
+-              panic("Couldn't get tx irq for rs285");
+-
+-      if (tty_register_driver(&rs285_driver))
+-              printk(KERN_ERR "Couldn't register 21285 serial driver\n");
+-      if (tty_register_driver(&callout_driver))
+-              printk(KERN_ERR "Couldn't register 21285 callout driver\n");
+-
+-      return 0;
+-}
+-
+-static void __exit rs285_fini(void)
+-{
+-      unsigned long flags;
+-      int ret;
+-
+-      save_flags(flags);
+-      cli();
+-      ret = tty_unregister_driver(&callout_driver);
+-      if (ret)
+-              printk(KERN_ERR "Unable to unregister 21285 callout driver "
+-                      "(%d)\n", ret);
+-      ret = tty_unregister_driver(&rs285_driver);
+-      if (ret)
+-              printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n",
+-                      ret);
+-      free_irq(IRQ_CONTX, NULL);
+-      free_irq(IRQ_CONRX, NULL);
+-      restore_flags(flags);
+-}
+-
+-module_init(rs285_init);
+-module_exit(rs285_fini);
+-
+-#ifdef CONFIG_SERIAL_21285_CONSOLE
+-/************** console driver *****************/
+-
+-static void rs285_console_write(struct console *co, const char *s, u_int count)
+-{
+-      int i;
+-
+-      disable_irq(IRQ_CONTX);
+-      for (i = 0; i < count; i++) {
+-              while (*CSR_UARTFLG & 0x20);
+-              *CSR_UARTDR = s[i];
+-              if (s[i] == '\n') {
+-                      while (*CSR_UARTFLG & 0x20);
+-                      *CSR_UARTDR = '\r';
+-              }
+-      }
+-      enable_irq(IRQ_CONTX);
+-}
+-
+-static kdev_t rs285_console_device(struct console *c)
+-{
+-      return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
+-}
+-
+-static int __init rs285_console_setup(struct console *co, char *options)
+-{
+-      int baud = 9600;
+-      int bits = 8;
+-      int parity = 'n';
+-      int cflag = CREAD | HUPCL | CLOCAL;
+-
+-      if (machine_is_personal_server())
+-              baud = 57600;
+-
+-      if (options) {
+-              char *s = options;
+-              baud = simple_strtoul(options, NULL, 10);
+-              while (*s >= '0' && *s <= '9')
+-                      s++;
+-              if (*s)
+-                      parity = *s++;
+-              if (*s)
+-                      bits = *s - '0';
+-      }
+-
+-      /*
+-       *    Now construct a cflag setting.
+-       */
+-      switch (baud) {
+-      case 1200:
+-              cflag |= B1200;
+-              break;
+-      case 2400:
+-              cflag |= B2400;
+-              break;
+-      case 4800:
+-              cflag |= B4800;
+-              break;
+-      case 9600:
+-              cflag |= B9600;
+-              break;
+-      case 19200:
+-              cflag |= B19200;
+-              break;
+-      case 38400:
+-              cflag |= B38400;
+-              break;
+-      case 57600:
+-              cflag |= B57600;
+-              break;
+-      case 115200:
+-              cflag |= B115200;
+-              break;
+-      default:
+-              cflag |= B9600;
+-              break;
+-      }
+-      switch (bits) {
+-      case 7:
+-              cflag |= CS7;
+-              break;
+-      default:
+-              cflag |= CS8;
+-              break;
+-      }
+-      switch (parity) {
+-      case 'o':
+-      case 'O':
+-              cflag |= PARODD;
+-              break;
+-      case 'e':
+-      case 'E':
+-              cflag |= PARENB;
+-              break;
+-      }
+-      co->cflag = cflag;
+-      rs285_set_cflag(cflag);
+-      rs285_console_write(NULL, "\e[2J\e[Hboot ", 12);
+-      if (options)
+-              rs285_console_write(NULL, options, strlen(options));
+-      else
+-              rs285_console_write(NULL, "no options", 10);
+-      rs285_console_write(NULL, "\n", 1);
+-
+-      return 0;
+-}
+-
+-static struct console rs285_cons =
+-{
+-      name:           SERIAL_21285_NAME,
+-      write:          rs285_console_write,
+-      device:         rs285_console_device,
+-      setup:          rs285_console_setup,
+-      flags:          CON_PRINTBUFFER,
+-      index:          -1,
+-};
+-
+-void __init rs285_console_init(void)
+-{
+-      register_console(&rs285_cons);
+-}
+-
+-#endif        /* CONFIG_SERIAL_21285_CONSOLE */
+-
+-MODULE_LICENSE("GPL");
+-EXPORT_NO_SYMBOLS;
+--- linux-2.4.27/drivers/char/serial_amba.c
++++ /dev/null
+@@ -1,2015 +0,0 @@
+-/*
+- *  linux/drivers/char/serial_amba.c
+- *
+- *  Driver for AMBA serial ports
+- *
+- *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+- *
+- *  Copyright 1999 ARM Limited
+- *  Copyright (C) 2000 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
+- * 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 a generic driver for ARM AMBA-type serial ports.  They
+- * have a lot of 16550-like features, but are not register compatable.
+- * Note that although they do have CTS, DCD and DSR inputs, they do
+- * not have an RI input, nor do they have DTR or RTS outputs.  If
+- * required, these have to be supplied via some other means (eg, GPIO)
+- * and hooked into this driver.
+- *
+- * This could very easily become a generic serial driver for dumb UARTs
+- * (eg, {82,16x}50, 21285, SA1100).
+- */
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/errno.h>
+-#include <linux/signal.h>
+-#include <linux/sched.h>
+-#include <linux/interrupt.h>
+-#include <linux/tty.h>
+-#include <linux/tty_flip.h>
+-#include <linux/major.h>
+-#include <linux/string.h>
+-#include <linux/fcntl.h>
+-#include <linux/ptrace.h>
+-#include <linux/ioport.h>
+-#include <linux/mm.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/circ_buf.h>
+-#include <linux/serial.h>
+-#include <linux/console.h>
+-#include <linux/sysrq.h>
+-
+-#include <asm/system.h>
+-#include <asm/io.h>
+-#include <asm/irq.h>
+-#include <asm/uaccess.h>
+-#include <asm/bitops.h>
+-
+-#include <asm/hardware/serial_amba.h>
+-
+-#define SERIAL_AMBA_NAME      "ttyAM"
+-#define SERIAL_AMBA_MAJOR     204
+-#define SERIAL_AMBA_MINOR     16
+-#define SERIAL_AMBA_NR                2
+-
+-#define CALLOUT_AMBA_NAME     "cuaam"
+-#define CALLOUT_AMBA_MAJOR    205
+-#define CALLOUT_AMBA_MINOR    16
+-#define CALLOUT_AMBA_NR               SERIAL_AMBA_NR
+-
+-#ifndef TRUE
+-#define TRUE 1
+-#endif
+-#ifndef FALSE
+-#define FALSE 0
+-#endif
+-
+-#define DEBUG 0
+-#define DEBUG_LEDS 0
+-
+-#if DEBUG_LEDS
+-extern int get_leds(void);
+-extern int set_leds(int);
+-#endif
+-
+-/*
+- * Access routines for the AMBA UARTs
+- */
+-#define UART_GET_INT_STATUS(p)        IO_READ((p)->uart_base + AMBA_UARTIIR)
+-#define UART_GET_FR(p)                IO_READ((p)->uart_base + AMBA_UARTFR)
+-#define UART_GET_CHAR(p)      IO_READ((p)->uart_base + AMBA_UARTDR)
+-#define UART_PUT_CHAR(p, c)   IO_WRITE((p)->uart_base + AMBA_UARTDR, (c))
+-#define UART_GET_RSR(p)               IO_READ((p)->uart_base + AMBA_UARTRSR)
+-#define UART_GET_CR(p)                IO_READ((p)->uart_base + AMBA_UARTCR)
+-#define UART_PUT_CR(p,c)      IO_WRITE((p)->uart_base + AMBA_UARTCR, (c))
+-#define UART_GET_LCRL(p)      IO_READ((p)->uart_base + AMBA_UARTLCR_L)
+-#define UART_PUT_LCRL(p,c)    IO_WRITE((p)->uart_base + AMBA_UARTLCR_L, (c))
+-#define UART_GET_LCRM(p)      IO_READ((p)->uart_base + AMBA_UARTLCR_M)
+-#define UART_PUT_LCRM(p,c)    IO_WRITE((p)->uart_base + AMBA_UARTLCR_M, (c))
+-#define UART_GET_LCRH(p)      IO_READ((p)->uart_base + AMBA_UARTLCR_H)
+-#define UART_PUT_LCRH(p,c)    IO_WRITE((p)->uart_base + AMBA_UARTLCR_H, (c))
+-#define UART_RX_DATA(s)               (((s) & AMBA_UARTFR_RXFE) == 0)
+-#define UART_TX_READY(s)      (((s) & AMBA_UARTFR_TXFF) == 0)
+-#define UART_TX_EMPTY(p)      ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
+-
+-#define AMBA_UARTRSR_ANY      (AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE)
+-#define AMBA_UARTFR_MODEM_ANY (AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS)
+-
+-/*
+- * Things needed by tty driver
+- */
+-static struct tty_driver ambanormal_driver, ambacallout_driver;
+-static int ambauart_refcount;
+-static struct tty_struct *ambauart_table[SERIAL_AMBA_NR];
+-static struct termios *ambauart_termios[SERIAL_AMBA_NR];
+-static struct termios *ambauart_termios_locked[SERIAL_AMBA_NR];
+-
+-#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+-#define SUPPORT_SYSRQ
+-#endif
+-
+-/*
+- * Things needed internally to this driver
+- */
+-
+-/*
+- * tmp_buf is used as a temporary buffer by serial_write.  We need to
+- * lock it in case the copy_from_user blocks while swapping in a page,
+- * and some other program tries to do a serial write at the same time.
+- * Since the lock will only come under contention when the system is
+- * swapping and available memory is low, it makes sense to share one
+- * buffer across all the serial ports, since it significantly saves
+- * memory if large numbers of serial ports are open.
+- */
+-static u_char *tmp_buf;
+-static DECLARE_MUTEX(tmp_buf_sem);
+-
+-#define HIGH_BITS_OFFSET      ((sizeof(long)-sizeof(int))*8)
+-
+-/* number of characters left in xmit buffer before we ask for more */
+-#define WAKEUP_CHARS          256
+-#define AMBA_ISR_PASS_LIMIT   256
+-
+-#define EVT_WRITE_WAKEUP      0
+-
+-struct amba_icount {
+-      __u32   cts;
+-      __u32   dsr;
+-      __u32   rng;
+-      __u32   dcd;
+-      __u32   rx;
+-      __u32   tx;
+-      __u32   frame;
+-      __u32   overrun;
+-      __u32   parity;
+-      __u32   brk;
+-      __u32   buf_overrun;
+-};
+-
+-/*
+- * Static information about the port
+- */
+-struct amba_port {
+-      unsigned int            uart_base;
+-      unsigned int            irq;
+-      unsigned int            uartclk;
+-      unsigned int            fifosize;
+-      unsigned int            tiocm_support;
+-      void (*set_mctrl)(struct amba_port *, u_int mctrl);
+-};    
+-
+-/*
+- * This is the state information which is persistent across opens
+- */
+-struct amba_state {
+-      struct amba_icount      icount;
+-      unsigned int            line;
+-      unsigned int            close_delay;
+-      unsigned int            closing_wait;
+-      unsigned int            custom_divisor;
+-      unsigned int            flags;
+-      struct termios          normal_termios;
+-      struct termios          callout_termios;
+-
+-      int                     count;
+-      struct amba_info        *info;
+-};
+-
+-#define AMBA_XMIT_SIZE 1024
+-/*
+- * This is the state information which is only valid when the port is open.
+- */
+-struct amba_info {
+-      struct amba_port        *port;
+-      struct amba_state       *state;
+-      struct tty_struct       *tty;
+-      unsigned char           x_char;
+-      unsigned char           old_status;
+-      unsigned char           read_status_mask;
+-      unsigned char           ignore_status_mask;
+-      struct circ_buf         xmit;
+-      unsigned int            flags;
+-#ifdef SUPPORT_SYSRQ
+-      unsigned long           sysrq;
+-#endif
+-
+-      unsigned int            event;
+-      unsigned int            timeout;
+-      unsigned int            lcr_h;
+-      unsigned int            mctrl;
+-      int                     blocked_open;
+-      pid_t                   session;
+-      pid_t                   pgrp;
+-
+-      struct tasklet_struct   tlet;
+-
+-      wait_queue_head_t       open_wait;
+-      wait_queue_head_t       close_wait;
+-      wait_queue_head_t       delta_msr_wait;
+-};
+-
+-#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+-static struct console ambauart_cons;
+-#endif
+-static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios);
+-static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout);
+-
+-#if 1 //def CONFIG_SERIAL_INTEGRATOR
+-static void amba_set_mctrl_null(struct amba_port *port, u_int mctrl)
+-{
+-}
+-
+-static struct amba_port amba_ports[SERIAL_AMBA_NR] = {
+-      {
+-              uart_base:      IO_ADDRESS(INTEGRATOR_UART0_BASE),
+-              irq:            IRQ_UARTINT0,
+-              uartclk:        14745600,
+-              fifosize:       8,
+-              set_mctrl:      amba_set_mctrl_null,
+-      },
+-      {
+-              uart_base:      IO_ADDRESS(INTEGRATOR_UART1_BASE),
+-              irq:            IRQ_UARTINT1,
+-              uartclk:        14745600,
+-              fifosize:       8,
+-              set_mctrl:      amba_set_mctrl_null,
+-      }
+-};
+-#endif
+-
+-static struct amba_state amba_state[SERIAL_AMBA_NR];
+-
+-static void ambauart_enable_rx_interrupt(struct amba_info *info)
+-{
+-      unsigned int cr;
+-
+-      cr = UART_GET_CR(info->port);
+-      cr |= AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE;
+-      UART_PUT_CR(info->port, cr);
+-}
+-
+-static void ambauart_disable_rx_interrupt(struct amba_info *info)
+-{
+-      unsigned int cr;
+-
+-      cr = UART_GET_CR(info->port);
+-      cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE);
+-      UART_PUT_CR(info->port, cr);
+-}
+-
+-static void ambauart_enable_tx_interrupt(struct amba_info *info)
+-{
+-      unsigned int cr;
+-
+-      cr = UART_GET_CR(info->port);
+-      cr |= AMBA_UARTCR_TIE;
+-      UART_PUT_CR(info->port, cr);
+-}
+-
+-static void ambauart_disable_tx_interrupt(struct amba_info *info)
+-{
+-      unsigned int cr;
+-
+-      cr = UART_GET_CR(info->port);
+-      cr &= ~AMBA_UARTCR_TIE;
+-      UART_PUT_CR(info->port, cr);
+-}
+-
+-static void ambauart_stop(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-      save_flags(flags); cli();
+-      ambauart_disable_tx_interrupt(info);
+-      restore_flags(flags);
+-}
+-
+-static void ambauart_start(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-      save_flags(flags); cli();
+-      if (info->xmit.head != info->xmit.tail
+-          && info->xmit.buf)
+-              ambauart_enable_tx_interrupt(info);
+-      restore_flags(flags);
+-}
+-
+-
+-/*
+- * This routine is used by the interrupt handler to schedule
+- * processing in the software interrupt portion of the driver.
+- */
+-static void ambauart_event(struct amba_info *info, int event)
+-{
+-      info->event |= 1 << event;
+-      tasklet_schedule(&info->tlet);
+-}
+-
+-static void
+-#ifdef SUPPORT_SYSRQ
+-ambauart_rx_chars(struct amba_info *info, struct pt_regs *regs)
+-#else
+-ambauart_rx_chars(struct amba_info *info)
+-#endif
+-{
+-      struct tty_struct *tty = info->tty;
+-      unsigned int status, ch, rsr, flg, ignored = 0;
+-      struct amba_icount *icount = &info->state->icount;
+-      struct amba_port *port = info->port;
+-
+-      status = UART_GET_FR(port);
+-      while (UART_RX_DATA(status)) {
+-              ch = UART_GET_CHAR(port);
+-
+-              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+-                      goto ignore_char;
+-              icount->rx++;
+-
+-              flg = TTY_NORMAL;
+-
+-              /*
+-               * Note that the error handling code is
+-               * out of the main execution path
+-               */
+-              rsr = UART_GET_RSR(port);
+-              if (rsr & AMBA_UARTRSR_ANY)
+-                      goto handle_error;
+-#ifdef SUPPORT_SYSRQ
+-              if (info->sysrq) {
+-                      if (ch && time_before(jiffies, info->sysrq)) {
+-                              handle_sysrq(ch, regs, NULL, NULL);
+-                              info->sysrq = 0;
+-                              goto ignore_char;
+-                      }
+-                      info->sysrq = 0;
+-              }
+-#endif
+-      error_return:
+-              *tty->flip.flag_buf_ptr++ = flg;
+-              *tty->flip.char_buf_ptr++ = ch;
+-              tty->flip.count++;
+-      ignore_char:
+-              status = UART_GET_FR(port);
+-      }
+-out:
+-      tty_flip_buffer_push(tty);
+-      return;
+-
+-handle_error:
+-      if (rsr & AMBA_UARTRSR_BE) {
+-              rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE);
+-              icount->brk++;
+-
+-#ifdef SUPPORT_SYSRQ
+-              if (info->state->line == ambauart_cons.index) {
+-                      if (!info->sysrq) {
+-                              info->sysrq = jiffies + HZ*5;
+-                              goto ignore_char;
+-                      }
+-              }
+-#endif
+-      } else if (rsr & AMBA_UARTRSR_PE)
+-              icount->parity++;
+-      else if (rsr & AMBA_UARTRSR_FE)
+-              icount->frame++;
+-      if (rsr & AMBA_UARTRSR_OE)
+-              icount->overrun++;
+-
+-      if (rsr & info->ignore_status_mask) {
+-              if (++ignored > 100)
+-                      goto out;
+-              goto ignore_char;
+-      }
+-      rsr &= info->read_status_mask;
+-
+-      if (rsr & AMBA_UARTRSR_BE)
+-              flg = TTY_BREAK;
+-      else if (rsr & AMBA_UARTRSR_PE)
+-              flg = TTY_PARITY;
+-      else if (rsr & AMBA_UARTRSR_FE)
+-              flg = TTY_FRAME;
+-
+-      if (rsr & AMBA_UARTRSR_OE) {
+-              /*
+-               * CHECK: does overrun affect the current character?
+-               * ASSUMPTION: it does not.
+-               */
+-              *tty->flip.flag_buf_ptr++ = flg;
+-              *tty->flip.char_buf_ptr++ = ch;
+-              tty->flip.count++;
+-              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+-                      goto ignore_char;
+-              ch = 0;
+-              flg = TTY_OVERRUN;
+-      }
+-#ifdef SUPPORT_SYSRQ
+-      info->sysrq = 0;
+-#endif
+-      goto error_return;
+-}
+-
+-static void ambauart_tx_chars(struct amba_info *info)
+-{
+-      struct amba_port *port = info->port;
+-      int count;
+-
+-      if (info->x_char) {
+-              UART_PUT_CHAR(port, info->x_char);
+-              info->state->icount.tx++;
+-              info->x_char = 0;
+-              return;
+-      }
+-      if (info->xmit.head == info->xmit.tail
+-          || info->tty->stopped
+-          || info->tty->hw_stopped) {
+-              ambauart_disable_tx_interrupt(info);
+-              return;
+-      }
+-
+-      count = port->fifosize;
+-      do {
+-              UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
+-              info->xmit.tail = (info->xmit.tail + 1) & (AMBA_XMIT_SIZE - 1);
+-              info->state->icount.tx++;
+-              if (info->xmit.head == info->xmit.tail)
+-                      break;
+-      } while (--count > 0);
+-
+-      if (CIRC_CNT(info->xmit.head,
+-                   info->xmit.tail,
+-                   AMBA_XMIT_SIZE) < WAKEUP_CHARS)
+-              ambauart_event(info, EVT_WRITE_WAKEUP);
+-
+-      if (info->xmit.head == info->xmit.tail) {
+-              ambauart_disable_tx_interrupt(info);
+-      }
+-}
+-
+-static void ambauart_modem_status(struct amba_info *info)
+-{
+-      unsigned int status, delta;
+-      struct amba_icount *icount = &info->state->icount;
+-
+-      status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;
+-
+-      delta = status ^ info->old_status;
+-      info->old_status = status;
+-
+-      if (!delta)
+-              return;
+-
+-      if (delta & AMBA_UARTFR_DCD) {
+-              icount->dcd++;
+-#ifdef CONFIG_HARD_PPS
+-              if ((info->flags & ASYNC_HARDPPS_CD) &&
+-                  (status & AMBA_UARTFR_DCD)
+-                      hardpps();
+-#endif
+-              if (info->flags & ASYNC_CHECK_CD) {
+-                      if (status & AMBA_UARTFR_DCD)
+-                              wake_up_interruptible(&info->open_wait);
+-                      else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+-                                 (info->flags & ASYNC_CALLOUT_NOHUP))) {
+-                              if (info->tty)
+-                                      tty_hangup(info->tty);
+-                      }
+-              }
+-      }
+-
+-      if (delta & AMBA_UARTFR_DSR)
+-              icount->dsr++;
+-
+-      if (delta & AMBA_UARTFR_CTS) {
+-              icount->cts++;
+-
+-              if (info->flags & ASYNC_CTS_FLOW) {
+-                      status &= AMBA_UARTFR_CTS;
+-
+-                      if (info->tty->hw_stopped) {
+-                              if (status) {
+-                                      info->tty->hw_stopped = 0;
+-                                      ambauart_enable_tx_interrupt(info);
+-                                      ambauart_event(info, EVT_WRITE_WAKEUP);
+-                              }
+-                      } else {
+-                              if (!status) {
+-                                      info->tty->hw_stopped = 1;
+-                                      ambauart_disable_tx_interrupt(info);
+-                              }
+-                      }
+-              }
+-      }
+-      wake_up_interruptible(&info->delta_msr_wait);
+-
+-}
+-
+-static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs)
+-{
+-      struct amba_info *info = dev_id;
+-      unsigned int status, pass_counter = 0;
+-
+-#if DEBUG_LEDS
+-      // tell the world
+-      set_leds(get_leds() | RED_LED);
+-#endif
+-
+-      status = UART_GET_INT_STATUS(info->port);
+-      do {
+-              /*
+-               * FIXME: what about clearing the interrupts?
+-               */
+-
+-              if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS))
+-#ifdef SUPPORT_SYSRQ
+-                      ambauart_rx_chars(info, regs);
+-#else
+-                      ambauart_rx_chars(info);
+-#endif
+-              if (status & AMBA_UARTIIR_TIS)
+-                      ambauart_tx_chars(info);
+-              if (status & AMBA_UARTIIR_MIS)
+-                      ambauart_modem_status(info);
+-              if (pass_counter++ > AMBA_ISR_PASS_LIMIT)
+-                      break;
+-
+-              status = UART_GET_INT_STATUS(info->port);
+-      } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | AMBA_UARTIIR_TIS));
+-
+-#if DEBUG_LEDS
+-      // tell the world
+-      set_leds(get_leds() & ~RED_LED);
+-#endif
+-}
+-
+-static void ambauart_tasklet_action(unsigned long data)
+-{
+-      struct amba_info *info = (struct amba_info *)data;
+-      struct tty_struct *tty;
+-
+-      tty = info->tty;
+-      if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
+-              return;
+-
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-      wake_up_interruptible(&tty->write_wait);
+-}
+-
+-static int ambauart_startup(struct amba_info *info)
+-{
+-      unsigned long flags;
+-      unsigned long page;
+-      int retval = 0;
+-
+-      page = get_zeroed_page(GFP_KERNEL);
+-      if (!page)
+-              return -ENOMEM;
+-
+-      save_flags(flags); cli();
+-
+-      if (info->flags & ASYNC_INITIALIZED) {
+-              free_page(page);
+-              goto errout;
+-      }
+-
+-      if (info->xmit.buf)
+-              free_page(page);
+-      else
+-              info->xmit.buf = (unsigned char *) page;
+-
+-      /*
+-       * Allocate the IRQ
+-       */
+-      retval = request_irq(info->port->irq, ambauart_int, 0, "amba", info);
+-      if (retval) {
+-              if (capable(CAP_SYS_ADMIN)) {
+-                      if (info->tty)
+-                              set_bit(TTY_IO_ERROR, &info->tty->flags);
+-                      retval = 0;
+-              }
+-              goto errout;
+-      }
+-
+-      info->mctrl = 0;
+-      if (info->tty->termios->c_cflag & CBAUD)
+-              info->mctrl = TIOCM_RTS | TIOCM_DTR;
+-      info->port->set_mctrl(info->port, info->mctrl);
+-
+-      /*
+-       * initialise the old status of the modem signals
+-       */
+-      info->old_status = UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY;
+-
+-      /*
+-       * Finally, enable interrupts
+-       */
+-      ambauart_enable_rx_interrupt(info);
+-
+-      if (info->tty)
+-              clear_bit(TTY_IO_ERROR, &info->tty->flags);
+-      info->xmit.head = info->xmit.tail = 0;
+-
+-      /*
+-       * Set up the tty->alt_speed kludge
+-       */
+-      if (info->tty) {
+-              if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+-                      info->tty->alt_speed = 57600;
+-              if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+-                      info->tty->alt_speed = 115200;
+-              if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+-                      info->tty->alt_speed = 230400;
+-              if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+-                      info->tty->alt_speed = 460800;
+-      }
+-
+-      /*
+-       * and set the speed of the serial port
+-       */
+-      ambauart_change_speed(info, 0);
+-
+-      info->flags |= ASYNC_INITIALIZED;
+-      restore_flags(flags);
+-      return 0;
+-
+-errout:
+-      restore_flags(flags);
+-      return retval;
+-}
+-
+-/*
+- * This routine will shutdown a serial port; interrupts are disabled, and
+- * DTR is dropped if the hangup on close termio flag is on.
+- */
+-static void ambauart_shutdown(struct amba_info *info)
+-{
+-      unsigned long flags;
+-
+-      if (!(info->flags & ASYNC_INITIALIZED))
+-              return;
+-
+-      save_flags(flags); cli(); /* Disable interrupts */
+-
+-      /*
+-       * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+-       * here so the queue might never be woken up
+-       */
+-      wake_up_interruptible(&info->delta_msr_wait);
+-
+-      /*
+-       * Free the IRQ
+-       */
+-      free_irq(info->port->irq, info);
+-
+-      if (info->xmit.buf) {
+-              unsigned long pg = (unsigned long) info->xmit.buf;
+-              info->xmit.buf = NULL;
+-              free_page(pg);
+-      }
+-
+-      /*
+-       * disable all interrupts, disable the port
+-       */
+-      UART_PUT_CR(info->port, 0);
+-
+-      /* disable break condition and fifos */
+-      UART_PUT_LCRH(info->port, UART_GET_LCRH(info->port) &
+-              ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));
+-
+-      if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+-              info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS);
+-      info->port->set_mctrl(info->port, info->mctrl);
+-
+-      /* kill off our tasklet */
+-      tasklet_kill(&info->tlet);
+-      if (info->tty)
+-              set_bit(TTY_IO_ERROR, &info->tty->flags);
+-
+-      info->flags &= ~ASYNC_INITIALIZED;
+-      restore_flags(flags);
+-}
+-
+-static void ambauart_change_speed(struct amba_info *info, struct termios *old_termios)
+-{
+-      unsigned int lcr_h, baud, quot, cflag, old_cr, bits;
+-      unsigned long flags;
+-
+-      if (!info->tty || !info->tty->termios)
+-              return;
+-
+-      cflag = info->tty->termios->c_cflag;
+-
+-#if DEBUG
+-      printk("ambauart_set_cflag(0x%x) called\n", cflag);
+-#endif
+-      /* byte size and parity */
+-      switch (cflag & CSIZE) {
+-      case CS5: lcr_h = AMBA_UARTLCR_H_WLEN_5; bits = 7;  break;
+-      case CS6: lcr_h = AMBA_UARTLCR_H_WLEN_6; bits = 8;  break;
+-      case CS7: lcr_h = AMBA_UARTLCR_H_WLEN_7; bits = 9;  break;
+-      default:  lcr_h = AMBA_UARTLCR_H_WLEN_8; bits = 10; break; // CS8
+-      }
+-      if (cflag & CSTOPB) {
+-              lcr_h |= AMBA_UARTLCR_H_STP2;
+-              bits ++;
+-      }
+-      if (cflag & PARENB) {
+-              lcr_h |= AMBA_UARTLCR_H_PEN;
+-              bits++;
+-              if (!(cflag & PARODD))
+-                      lcr_h |= AMBA_UARTLCR_H_EPS;
+-      }
+-      if (info->port->fifosize > 1)
+-              lcr_h |= AMBA_UARTLCR_H_FEN;
+-
+-      do {
+-              /* Determine divisor based on baud rate */
+-              baud = tty_get_baud_rate(info->tty);
+-              if (!baud)
+-                      baud = 9600;
+-
+-              if (baud == 38400 &&
+-                  ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
+-                      quot = info->state->custom_divisor;
+-              else
+-                      quot = (info->port->uartclk / (16 * baud)) - 1;
+-
+-              if (!quot && old_termios) {
+-                      info->tty->termios->c_cflag &= ~CBAUD;
+-                      info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
+-                      old_termios = NULL;
+-              }
+-      } while (quot == 0 && old_termios);
+-
+-      /* As a last resort, if the quotient is zero, default to 9600 bps */
+-      if (!quot)
+-              quot = (info->port->uartclk / (16 * 9600)) - 1;
+-              
+-      info->timeout = (info->port->fifosize * HZ * bits * quot) /
+-                       (info->port->uartclk / 16);
+-      info->timeout += HZ/50;         /* Add .02 seconds of slop */
+-
+-      if (cflag & CRTSCTS)
+-              info->flags |= ASYNC_CTS_FLOW;
+-      else
+-              info->flags &= ~ASYNC_CTS_FLOW;
+-      if (cflag & CLOCAL)
+-              info->flags &= ~ASYNC_CHECK_CD;
+-      else
+-              info->flags |= ASYNC_CHECK_CD;
+-
+-      /*
+-       * Set up parity check flag
+-       */
+-#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+-
+-      info->read_status_mask = AMBA_UARTRSR_OE;
+-      if (I_INPCK(info->tty))
+-              info->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
+-      if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+-              info->read_status_mask |= AMBA_UARTRSR_BE;
+-
+-      /*
+-       * Characters to ignore
+-       */
+-      info->ignore_status_mask = 0;
+-      if (I_IGNPAR(info->tty))
+-              info->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
+-      if (I_IGNBRK(info->tty)) {
+-              info->ignore_status_mask |= AMBA_UARTRSR_BE;
+-              /*
+-               * If we're ignoring parity and break indicators,
+-               * ignore overruns to (for real raw support).
+-               */
+-              if (I_IGNPAR(info->tty))
+-                      info->ignore_status_mask |= AMBA_UARTRSR_OE;
+-      }
+-
+-      /* first, disable everything */
+-      save_flags(flags); cli();
+-      old_cr = UART_GET_CR(info->port) &= ~AMBA_UARTCR_MSIE;
+-
+-      if ((info->flags & ASYNC_HARDPPS_CD) ||
+-          (cflag & CRTSCTS) ||
+-          !(cflag & CLOCAL))
+-              old_cr |= AMBA_UARTCR_MSIE;
+-
+-      UART_PUT_CR(info->port, 0);
+-      restore_flags(flags);
+-
+-      /* Set baud rate */
+-      UART_PUT_LCRM(info->port, ((quot & 0xf00) >> 8));
+-      UART_PUT_LCRL(info->port, (quot & 0xff));
+-
+-      /*
+-       * ----------v----------v----------v----------v-----
+-       * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+-       * ----------^----------^----------^----------^-----
+-       */
+-      UART_PUT_LCRH(info->port, lcr_h);
+-      UART_PUT_CR(info->port, old_cr);
+-}
+-
+-static void ambauart_put_char(struct tty_struct *tty, u_char ch)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-      if (!tty || !info->xmit.buf)
+-              return;
+-
+-      save_flags(flags); cli();
+-      if (CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE) != 0) {
+-              info->xmit.buf[info->xmit.head] = ch;
+-              info->xmit.head = (info->xmit.head + 1) & (AMBA_XMIT_SIZE - 1);
+-      }
+-      restore_flags(flags);
+-}
+-
+-static void ambauart_flush_chars(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-      if (info->xmit.head == info->xmit.tail
+-          || tty->stopped
+-          || tty->hw_stopped
+-          || !info->xmit.buf)
+-              return;
+-
+-      save_flags(flags); cli();
+-      ambauart_enable_tx_interrupt(info);
+-      restore_flags(flags);
+-}
+-
+-static int ambauart_write(struct tty_struct *tty, int from_user,
+-                        const u_char * buf, int count)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-      int c, ret = 0;
+-
+-      if (!tty || !info->xmit.buf || !tmp_buf)
+-              return 0;
+-
+-      save_flags(flags);
+-      if (from_user) {
+-              down(&tmp_buf_sem);
+-              while (1) {
+-                      int c1;
+-                      c = CIRC_SPACE_TO_END(info->xmit.head,
+-                                            info->xmit.tail,
+-                                            AMBA_XMIT_SIZE);
+-                      if (count < c)
+-                              c = count;
+-                      if (c <= 0)
+-                              break;
+-
+-                      c -= copy_from_user(tmp_buf, buf, c);
+-                      if (!c) {
+-                              if (!ret)
+-                                      ret = -EFAULT;
+-                              break;
+-                      }
+-                      cli();
+-                      c1 = CIRC_SPACE_TO_END(info->xmit.head,
+-                                             info->xmit.tail,
+-                                             AMBA_XMIT_SIZE);
+-                      if (c1 < c)
+-                              c = c1;
+-                      memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+-                      info->xmit.head = (info->xmit.head + c) &
+-                                        (AMBA_XMIT_SIZE - 1);
+-                      restore_flags(flags);
+-                      buf += c;
+-                      count -= c;
+-                      ret += c;
+-              }
+-              up(&tmp_buf_sem);
+-      } else {
+-              cli();
+-              while (1) {
+-                      c = CIRC_SPACE_TO_END(info->xmit.head,
+-                                            info->xmit.tail,
+-                                            AMBA_XMIT_SIZE);
+-                      if (count < c)
+-                              c = count;
+-                      if (c <= 0)
+-                              break;
+-                      memcpy(info->xmit.buf + info->xmit.head, buf, c);
+-                      info->xmit.head = (info->xmit.head + c) &
+-                                        (AMBA_XMIT_SIZE - 1);
+-                      buf += c;
+-                      count -= c;
+-                      ret += c;
+-              }
+-              restore_flags(flags);
+-      }
+-      if (info->xmit.head != info->xmit.tail
+-          && !tty->stopped
+-          && !tty->hw_stopped)
+-              ambauart_enable_tx_interrupt(info);
+-      return ret;
+-}
+-
+-static int ambauart_write_room(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-
+-      return CIRC_SPACE(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
+-}
+-
+-static int ambauart_chars_in_buffer(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-
+-      return CIRC_CNT(info->xmit.head, info->xmit.tail, AMBA_XMIT_SIZE);
+-}
+-
+-static void ambauart_flush_buffer(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-#if DEBUG
+-      printk("ambauart_flush_buffer(%d) called\n",
+-             MINOR(tty->device) - tty->driver.minor_start);
+-#endif
+-      save_flags(flags); cli();
+-      info->xmit.head = info->xmit.tail = 0;
+-      restore_flags(flags);
+-      wake_up_interruptible(&tty->write_wait);
+-      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-          tty->ldisc.write_wakeup)
+-              (tty->ldisc.write_wakeup)(tty);
+-}
+-
+-/*
+- * This function is used to send a high-priority XON/XOFF character to
+- * the device
+- */
+-static void ambauart_send_xchar(struct tty_struct *tty, char ch)
+-{
+-      struct amba_info *info = tty->driver_data;
+-
+-      info->x_char = ch;
+-      if (ch)
+-              ambauart_enable_tx_interrupt(info);
+-}
+-
+-static void ambauart_throttle(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-
+-      if (I_IXOFF(tty))
+-              ambauart_send_xchar(tty, STOP_CHAR(tty));
+-
+-      if (tty->termios->c_cflag & CRTSCTS) {
+-              save_flags(flags); cli();
+-              info->mctrl &= ~TIOCM_RTS;
+-              info->port->set_mctrl(info->port, info->mctrl);
+-              restore_flags(flags);
+-      }
+-}
+-
+-static void ambauart_unthrottle(struct tty_struct *tty)
+-{
+-      struct amba_info *info = (struct amba_info *) tty->driver_data;
+-      unsigned long flags;
+-
+-      if (I_IXOFF(tty)) {
+-              if (info->x_char)
+-                      info->x_char = 0;
+-              else
+-                      ambauart_send_xchar(tty, START_CHAR(tty));
+-      }
+-
+-      if (tty->termios->c_cflag & CRTSCTS) {
+-              save_flags(flags); cli();
+-              info->mctrl |= TIOCM_RTS;
+-              info->port->set_mctrl(info->port, info->mctrl);
+-              restore_flags(flags);
+-      }
+-}
+-
+-static int get_serial_info(struct amba_info *info, struct serial_struct *retinfo)
+-{
+-      struct amba_state *state = info->state;
+-      struct amba_port *port = info->port;
+-      struct serial_struct tmp;
+-
+-      memset(&tmp, 0, sizeof(tmp));
+-      tmp.type           = 0;
+-      tmp.line           = state->line;
+-      tmp.port           = port->uart_base;
+-      if (HIGH_BITS_OFFSET)
+-              tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET;
+-      tmp.irq            = port->irq;
+-      tmp.flags          = 0;
+-      tmp.xmit_fifo_size = port->fifosize;
+-      tmp.baud_base      = port->uartclk / 16;
+-      tmp.close_delay    = state->close_delay;
+-      tmp.closing_wait   = state->closing_wait;
+-      tmp.custom_divisor = state->custom_divisor;
+-
+-      if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+-              return -EFAULT;
+-      return 0;
+-}
+-
+-static int set_serial_info(struct amba_info *info,
+-                         struct serial_struct *newinfo)
+-{
+-      struct serial_struct new_serial;
+-      struct amba_state *state, old_state;
+-      struct amba_port *port;
+-      unsigned long new_port;
+-      unsigned int i, change_irq, change_port;
+-      int retval = 0;
+-
+-      if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+-              return -EFAULT;
+-
+-      state = info->state;
+-      old_state = *state;
+-      port = info->port;
+-
+-      new_port = new_serial.port;
+-      if (HIGH_BITS_OFFSET)
+-              new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
+-
+-      change_irq  = new_serial.irq != port->irq;
+-      change_port = new_port != port->uart_base;
+-
+-      if (!capable(CAP_SYS_ADMIN)) {
+-              if (change_irq || change_port ||
+-                  (new_serial.baud_base != port->uartclk / 16) ||
+-                  (new_serial.close_delay != state->close_delay) ||
+-                  (new_serial.xmit_fifo_size != port->fifosize) ||
+-                  ((new_serial.flags & ~ASYNC_USR_MASK) !=
+-                   (state->flags & ~ASYNC_USR_MASK)))
+-                      return -EPERM;
+-              state->flags = ((state->flags & ~ASYNC_USR_MASK) |
+-                              (new_serial.flags & ASYNC_USR_MASK));
+-              info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+-                             (new_serial.flags & ASYNC_USR_MASK));
+-              state->custom_divisor = new_serial.custom_divisor;
+-              goto check_and_exit;
+-      }
+-
+-      if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
+-          (new_serial.baud_base < 9600))
+-              return -EINVAL;
+-
+-      if (new_serial.type && change_port) {
+-              for (i = 0; i < SERIAL_AMBA_NR; i++)
+-                      if ((port != amba_ports + i) &&
+-                          amba_ports[i].uart_base != new_port)
+-                              return -EADDRINUSE;
+-      }
+-
+-      if ((change_port || change_irq) && (state->count > 1))
+-              return -EBUSY;
+-
+-      /*
+-       * OK, past this point, all the error checking has been done.
+-       * At this point, we start making changes.....
+-       */
+-      port->uartclk = new_serial.baud_base * 16;
+-      state->flags = ((state->flags & ~ASYNC_FLAGS) |
+-                      (new_serial.flags & ASYNC_FLAGS));
+-      info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+-                     (info->flags & ASYNC_INTERNAL_FLAGS));
+-      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;
+-      info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+-      port->fifosize = new_serial.xmit_fifo_size;
+-
+-      if (change_port || change_irq) {
+-              /*
+-               * We need to shutdown the serial port at the old
+-               * port/irq combination.
+-               */
+-              ambauart_shutdown(info);
+-              port->irq = new_serial.irq;
+-              port->uart_base = new_port;
+-      }
+-
+-check_and_exit:
+-      if (!port->uart_base)
+-              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 ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+-                              info->tty->alt_speed = 57600;
+-                      if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+-                              info->tty->alt_speed = 115200;
+-                      if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+-                              info->tty->alt_speed = 230400;
+-                      if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+-                              info->tty->alt_speed = 460800;
+-                      ambauart_change_speed(info, NULL);
+-              }
+-      } else
+-              retval = ambauart_startup(info);
+-      return retval;
+-}
+-
+-
+-/*
+- * get_lsr_info - get line status register info
+- */
+-static int get_lsr_info(struct amba_info *info, unsigned int *value)
+-{
+-      unsigned int result, status;
+-      unsigned long flags;
+-
+-      save_flags(flags); cli();
+-      status = UART_GET_FR(info->port);
+-      restore_flags(flags);
+-      result = status & AMBA_UARTFR_BUSY ? TIOCSER_TEMT : 0;
+-
+-      /*
+-       * If we're about to load something into the transmit
+-       * register, we'll pretend the transmitter isn't empty to
+-       * avoid a race condition (depending on when the transmit
+-       * interrupt happens).
+-       */
+-      if (info->x_char ||
+-          ((CIRC_CNT(info->xmit.head, info->xmit.tail,
+-                     AMBA_XMIT_SIZE) > 0) &&
+-           !info->tty->stopped && !info->tty->hw_stopped))
+-              result &= TIOCSER_TEMT;
+-      
+-      return put_user(result, value);
+-}
+-
+-static int get_modem_info(struct amba_info *info, unsigned int *value)
+-{
+-      unsigned int result = info->mctrl;
+-      unsigned int status;
+-
+-      status = UART_GET_FR(info->port);
+-      if (status & AMBA_UARTFR_DCD)
+-              result |= TIOCM_CAR;
+-      if (status & AMBA_UARTFR_DSR)
+-              result |= TIOCM_DSR;
+-      if (status & AMBA_UARTFR_CTS)
+-              result |= TIOCM_CTS;
+-
+-      return put_user(result, value);
+-}
+-
+-static int set_modem_info(struct amba_info *info, unsigned int cmd,
+-                        unsigned int *value)
+-{
+-      unsigned int arg, old;
+-      unsigned long flags;
+-
+-      if (get_user(arg, value))
+-              return -EFAULT;
+-
+-      old = info->mctrl;
+-      switch (cmd) {
+-      case TIOCMBIS:
+-              info->mctrl |= arg;
+-              break;
+-
+-      case TIOCMBIC:
+-              info->mctrl &= ~arg;
+-              break;
+-
+-      case TIOCMSET:
+-              info->mctrl = arg;
+-              break;
+-
+-      default:
+-              return -EINVAL;
+-      }
+-      save_flags(flags); cli();
+-      if (old != info->mctrl)
+-              info->port->set_mctrl(info->port, info->mctrl);
+-      restore_flags(flags);
+-      return 0;
+-}
+-
+-static void ambauart_break_ctl(struct tty_struct *tty, int break_state)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-      unsigned int lcr_h;
+-
+-      save_flags(flags); cli();
+-      lcr_h = UART_GET_LCRH(info->port);
+-      if (break_state == -1)
+-              lcr_h |= AMBA_UARTLCR_H_BRK;
+-      else
+-              lcr_h &= ~AMBA_UARTLCR_H_BRK;
+-      UART_PUT_LCRH(info->port, lcr_h);
+-      restore_flags(flags);
+-}
+-
+-static int ambauart_ioctl(struct tty_struct *tty, struct file *file,
+-                         unsigned int cmd, unsigned long arg)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      struct amba_icount cprev, cnow;
+-      struct serial_icounter_struct icount;
+-      unsigned long flags;
+-
+-      if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+-          (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+-          (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+-              if (tty->flags & (1 << TTY_IO_ERROR))
+-                      return -EIO;
+-      }
+-
+-      switch (cmd) {
+-              case TIOCMGET:
+-                      return get_modem_info(info, (unsigned int *)arg);
+-              case TIOCMBIS:
+-              case TIOCMBIC:
+-              case TIOCMSET:
+-                      return set_modem_info(info, cmd, (unsigned int *)arg);
+-              case TIOCGSERIAL:
+-                      return get_serial_info(info,
+-                                             (struct serial_struct *)arg);
+-              case TIOCSSERIAL:
+-                      return set_serial_info(info,
+-                                             (struct serial_struct *)arg);
+-              case TIOCSERGETLSR: /* Get line status register */
+-                      return get_lsr_info(info, (unsigned int *)arg);
+-              /*
+-               * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+-               * - mask passed in arg for lines of interest
+-               *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+-               * Caller should use TIOCGICOUNT to see which one it was
+-               */
+-              case TIOCMIWAIT:
+-                      save_flags(flags); cli();
+-                      /* note the counters on entry */
+-                      cprev = info->state->icount;
+-                      /* Force modem status interrupts on */
+-                      UART_PUT_CR(info->port, UART_GET_CR(info->port) | AMBA_UARTCR_MSIE);
+-                      restore_flags(flags);
+-                      while (1) {
+-                              interruptible_sleep_on(&info->delta_msr_wait);
+-                              /* see if a signal did it */
+-                              if (signal_pending(current))
+-                                      return -ERESTARTSYS;
+-                              save_flags(flags); cli();
+-                              cnow = info->state->icount; /* atomic copy */
+-                              restore_flags(flags);
+-                              if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+-                                  cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+-                                      return -EIO; /* no change => error */
+-                              if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+-                                  ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+-                                  ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+-                                  ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+-                                      return 0;
+-                              }
+-                              cprev = cnow;
+-                      }
+-                      /* NOTREACHED */
+-
+-              /*
+-               * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+-               * Return: write counters to the user passed counter struct
+-               * NB: both 1->0 and 0->1 transitions are counted except for
+-               *     RI where only 0->1 is counted.
+-               */
+-              case TIOCGICOUNT:
+-                      save_flags(flags); cli();
+-                      cnow = info->state->icount;
+-                      restore_flags(flags);
+-                      icount.cts = cnow.cts;
+-                      icount.dsr = cnow.dsr;
+-                      icount.rng = cnow.rng;
+-                      icount.dcd = cnow.dcd;
+-                      icount.rx  = cnow.rx;
+-                      icount.tx  = cnow.tx;
+-                      icount.frame = cnow.frame;
+-                      icount.overrun = cnow.overrun;
+-                      icount.parity = cnow.parity;
+-                      icount.brk = cnow.brk;
+-                      icount.buf_overrun = cnow.buf_overrun;
+-
+-                      return copy_to_user((void *)arg, &icount, sizeof(icount))
+-                                      ? -EFAULT : 0;
+-
+-              default:
+-                      return -ENOIOCTLCMD;
+-      }
+-      return 0;
+-}
+-
+-static void ambauart_set_termios(struct tty_struct *tty, struct termios *old_termios)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      unsigned long flags;
+-      unsigned int cflag = tty->termios->c_cflag;
+-
+-      if ((cflag ^ old_termios->c_cflag) == 0 &&
+-          RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
+-              return;
+-
+-      ambauart_change_speed(info, old_termios);
+-
+-      /* Handle transition to B0 status */
+-      if ((old_termios->c_cflag & CBAUD) &&
+-          !(cflag & CBAUD)) {
+-              save_flags(flags); cli();
+-              info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR);
+-              info->port->set_mctrl(info->port, info->mctrl);
+-              restore_flags(flags);
+-      }
+-
+-      /* Handle transition away from B0 status */
+-      if (!(old_termios->c_cflag & CBAUD) &&
+-          (cflag & CBAUD)) {
+-              save_flags(flags); cli();
+-              info->mctrl |= TIOCM_DTR;
+-              if (!(cflag & CRTSCTS) ||
+-                  !test_bit(TTY_THROTTLED, &tty->flags))
+-                      info->mctrl |= TIOCM_RTS;
+-              info->port->set_mctrl(info->port, info->mctrl);
+-              restore_flags(flags);
+-      }
+-
+-      /* Handle turning off CRTSCTS */
+-      if ((old_termios->c_cflag & CRTSCTS) &&
+-          !(cflag & CRTSCTS)) {
+-              tty->hw_stopped = 0;
+-              ambauart_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
+-}
+-
+-static void ambauart_close(struct tty_struct *tty, struct file *filp)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      struct amba_state *state;
+-      unsigned long flags;
+-
+-      if (!info)
+-              return;
+-
+-      state = info->state;
+-
+-#if DEBUG
+-      printk("ambauart_close() called\n");
+-#endif
+-
+-      save_flags(flags); cli();
+-
+-      if (tty_hung_up_p(filp)) {
+-              MOD_DEC_USE_COUNT;
+-              restore_flags(flags);
+-              return;
+-      }
+-
+-      if ((tty->count == 1) && (state->count != 1)) {
+-              /*
+-               * Uh, oh.  tty->count is 1, which means that the tty
+-               * structure will be freed.  state->count should always
+-               * be one in these conditions.  If it's greater than
+-               * one, we've got real problems, since it means the
+-               * serial port won't be shutdown.
+-               */
+-              printk("ambauart_close: bad serial port count; tty->count is 1, "
+-                     "state->count is %d\n", state->count);
+-              state->count = 1;
+-      }
+-      if (--state->count < 0) {
+-              printk("rs_close: bad serial port count for %s%d: %d\n",
+-                     tty->driver.name, info->state->line, state->count);
+-              state->count = 0;
+-      }
+-      if (state->count) {
+-              MOD_DEC_USE_COUNT;
+-              restore_flags(flags);
+-              return;
+-      }
+-      info->flags |= ASYNC_CLOSING;
+-      restore_flags(flags);
+-      /*
+-       * Save the termios structure, since this port may have
+-       * separate termios for callout and dialin.
+-       */
+-      if (info->flags & ASYNC_NORMAL_ACTIVE)
+-              info->state->normal_termios = *tty->termios;
+-      if (info->flags & ASYNC_CALLOUT_ACTIVE)
+-              info->state->callout_termios = *tty->termios;
+-      /*
+-       * Now we wait for the transmit buffer to clear; and we notify
+-       * the line discipline to only process XON/XOFF characters.
+-       */
+-      tty->closing = 1;
+-      if (info->state->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+-              tty_wait_until_sent(tty, info->state->closing_wait);
+-      /*
+-       * At this point, we stop accepting input.  To do this, we
+-       * disable the receive line status interrupts.
+-       */
+-      if (info->flags & ASYNC_INITIALIZED) {
+-              ambauart_disable_rx_interrupt(info);
+-              /*
+-               * Before we drop DTR, make sure the UART transmitter
+-               * has completely drained; this is especially
+-               * important if there is a transmit FIFO!
+-               */
+-              ambauart_wait_until_sent(tty, info->timeout);
+-      }
+-      ambauart_shutdown(info);
+-      if (tty->driver.flush_buffer)
+-              tty->driver.flush_buffer(tty);
+-      if (tty->ldisc.flush_buffer)
+-              tty->ldisc.flush_buffer(tty);
+-      tty->closing = 0;
+-      info->event = 0;
+-      info->tty = NULL;
+-      if (info->blocked_open) {
+-              if (info->state->close_delay) {
+-                      set_current_state(TASK_INTERRUPTIBLE);
+-                      schedule_timeout(info->state->close_delay);
+-              }
+-              wake_up_interruptible(&info->open_wait);
+-      }
+-      info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+-                       ASYNC_CLOSING);
+-      wake_up_interruptible(&info->close_wait);
+-      MOD_DEC_USE_COUNT;
+-}
+-
+-static void ambauart_wait_until_sent(struct tty_struct *tty, int timeout)
+-{
+-      struct amba_info *info = (struct amba_info *) tty->driver_data;
+-      unsigned long char_time, expire;
+-      unsigned int status;
+-
+-      if (info->port->fifosize == 0)
+-              return;
+-
+-      /*
+-       * Set the check interval to be 1/5 of the estimated time to
+-       * send a single character, and make it at least 1.  The check
+-       * interval should also be less than the timeout.
+-       *
+-       * Note: we have to use pretty tight timings here to satisfy
+-       * the NIST-PCTS.
+-       */
+-      char_time = (info->timeout - HZ/50) / info->port->fifosize;
+-      char_time = char_time / 5;
+-      if (char_time == 0)
+-              char_time = 1;
+-      if (timeout && timeout < char_time)
+-              char_time = timeout;
+-      /*
+-       * If the transmitter hasn't cleared in twice the approximate
+-       * amount of time to send the entire FIFO, it probably won't
+-       * ever clear.  This assumes the UART isn't doing flow
+-       * control, which is currently the case.  Hence, if it ever
+-       * takes longer than info->timeout, this is probably due to a
+-       * UART bug of some kind.  So, we clamp the timeout parameter at
+-       * 2*info->timeout.
+-       */
+-      if (!timeout || timeout > 2 * info->timeout)
+-              timeout = 2 * info->timeout;
+-
+-      expire = jiffies + timeout;
+-#if DEBUG
+-      printk("ambauart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n",
+-             MINOR(tty->device) - tty->driver.minor_start, jiffies,
+-             expire);
+-#endif
+-      while (UART_GET_FR(info->port) & AMBA_UARTFR_BUSY) {
+-              set_current_state(TASK_INTERRUPTIBLE);
+-              schedule_timeout(char_time);
+-              if (signal_pending(current))
+-                      break;
+-              if (timeout && time_after(jiffies, expire))
+-                      break;
+-              status = UART_GET_FR(info->port);
+-      }
+-      set_current_state(TASK_RUNNING);
+-}
+-
+-static void ambauart_hangup(struct tty_struct *tty)
+-{
+-      struct amba_info *info = tty->driver_data;
+-      struct amba_state *state = info->state;
+-
+-      ambauart_flush_buffer(tty);
+-      if (info->flags & ASYNC_CLOSING)
+-              return;
+-      ambauart_shutdown(info);
+-      info->event = 0;
+-      state->count = 0;
+-      info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+-      info->tty = NULL;
+-      wake_up_interruptible(&info->open_wait);
+-}
+-
+-static int block_til_ready(struct tty_struct *tty, struct file *filp,
+-                         struct amba_info *info)
+-{
+-      DECLARE_WAITQUEUE(wait, current);
+-      struct amba_state *state = info->state;
+-      unsigned long flags;
+-      int do_clocal = 0, extra_count = 0, retval;
+-
+-      /*
+-       * If the device is in the middle of being closed, then block
+-       * until it's done, and then try again.
+-       */
+-      if (tty_hung_up_p(filp) ||
+-          (info->flags & ASYNC_CLOSING)) {
+-              if (info->flags & ASYNC_CLOSING)
+-                      interruptible_sleep_on(&info->close_wait);
+-              return (info->flags & ASYNC_HUP_NOTIFY) ?
+-                      -EAGAIN : -ERESTARTSYS;
+-      }
+-
+-      /*
+-       * If this is a callout device, then just make sure the normal
+-       * device isn't being used.
+-       */
+-      if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+-              if (info->flags & ASYNC_NORMAL_ACTIVE)
+-                      return -EBUSY;
+-              if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+-                  (info->flags & ASYNC_SESSION_LOCKOUT) &&
+-                  (info->session != current->session))
+-                      return -EBUSY;
+-              if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+-                  (info->flags & ASYNC_PGRP_LOCKOUT) &&
+-                  (info->pgrp != current->pgrp))
+-                      return -EBUSY;
+-              info->flags |= ASYNC_CALLOUT_ACTIVE;
+-              return 0;
+-      }
+-
+-      /*
+-       * If non-blocking mode is set, or the port is not enabled,
+-       * then make the check up front and then exit.
+-       */
+-      if ((filp->f_flags & O_NONBLOCK) ||
+-          (tty->flags & (1 << TTY_IO_ERROR))) {
+-              if (info->flags & ASYNC_CALLOUT_ACTIVE)
+-                      return -EBUSY;
+-              info->flags |= ASYNC_NORMAL_ACTIVE;
+-              return 0;
+-      }
+-
+-      if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+-              if (state->normal_termios.c_cflag & CLOCAL)
+-                      do_clocal = 1;
+-      } else {
+-              if (tty->termios->c_cflag & CLOCAL)
+-                      do_clocal = 1;
+-      }
+-
+-      /*
+-       * Block waiting for the carrier detect and the line to become
+-       * free (i.e., not in use by the callout).  While we are in
+-       * this loop, state->count is dropped by one, so that
+-       * rs_close() knows when to free things.  We restore it upon
+-       * exit, either normal or abnormal.
+-       */
+-      retval = 0;
+-      add_wait_queue(&info->open_wait, &wait);
+-      save_flags(flags); cli();
+-      if (!tty_hung_up_p(filp)) {
+-              extra_count = 1;
+-              state->count--;
+-      }
+-      restore_flags(flags);
+-      info->blocked_open++;
+-      while (1) {
+-              save_flags(flags); cli();
+-              if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+-                  (tty->termios->c_cflag & CBAUD)) {
+-                      info->mctrl = TIOCM_DTR | TIOCM_RTS;
+-                      info->port->set_mctrl(info->port, info->mctrl);
+-              }
+-              restore_flags(flags);
+-              set_current_state(TASK_INTERRUPTIBLE);
+-              if (tty_hung_up_p(filp) ||
+-                  !(info->flags & ASYNC_INITIALIZED)) {
+-                      if (info->flags & ASYNC_HUP_NOTIFY)
+-                              retval = -EAGAIN;
+-                      else
+-                              retval = -ERESTARTSYS;
+-                      break;
+-              }
+-              if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+-                  !(info->flags & ASYNC_CLOSING) &&
+-                  (do_clocal || (UART_GET_FR(info->port) & AMBA_UARTFR_DCD)))
+-                      break;
+-              if (signal_pending(current)) {
+-                      retval = -ERESTARTSYS;
+-                      break;
+-              }
+-              schedule();
+-      }
+-      set_current_state(TASK_RUNNING);
+-      remove_wait_queue(&info->open_wait, &wait);
+-      if (extra_count)
+-              state->count++;
+-      info->blocked_open--;
+-      if (retval)
+-              return retval;
+-      info->flags |= ASYNC_NORMAL_ACTIVE;
+-      return 0;
+-}
+-
+-static struct amba_info *ambauart_get(int line)
+-{
+-      struct amba_info *info;
+-      struct amba_state *state = amba_state + line;
+-
+-      state->count++;
+-      if (state->info)
+-              return state->info;
+-      info = kmalloc(sizeof(struct amba_info), GFP_KERNEL);
+-      if (info) {
+-              memset(info, 0, sizeof(struct amba_info));
+-              init_waitqueue_head(&info->open_wait);
+-              init_waitqueue_head(&info->close_wait);
+-              init_waitqueue_head(&info->delta_msr_wait);
+-              info->flags = state->flags;
+-              info->state = state;
+-              info->port  = amba_ports + line;
+-              tasklet_init(&info->tlet, ambauart_tasklet_action,
+-                           (unsigned long)info);
+-      }
+-      if (state->info) {
+-              kfree(info);
+-              return state->info;
+-      }
+-      state->info = info;
+-      return info;
+-}
+-
+-static int ambauart_open(struct tty_struct *tty, struct file *filp)
+-{
+-      struct amba_info *info;
+-      int retval, line = MINOR(tty->device) - tty->driver.minor_start;
+-
+-#if DEBUG
+-      printk("ambauart_open(%d) called\n", line);
+-#endif
+-
+-      // is this a line that we've got?
+-      MOD_INC_USE_COUNT;
+-      if (line >= SERIAL_AMBA_NR) {
+-              MOD_DEC_USE_COUNT;
+-              return -ENODEV;
+-      }
+-
+-      info = ambauart_get(line);
+-      if (!info)
+-              return -ENOMEM;
+-
+-      tty->driver_data = info;
+-      info->tty = tty;
+-      info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+-
+-      /*
+-       * Make sure we have the temporary buffer allocated
+-       */
+-      if (!tmp_buf) {
+-              unsigned long page = get_zeroed_page(GFP_KERNEL);
+-              if (tmp_buf)
+-                      free_page(page);
+-              else if (!page) {
+-                      MOD_DEC_USE_COUNT;
+-                      return -ENOMEM;
+-              }
+-              tmp_buf = (u_char *)page;
+-      }
+-
+-      /*
+-       * If the port is in the middle of closing, bail out now.
+-       */
+-      if (tty_hung_up_p(filp) ||
+-          (info->flags & ASYNC_CLOSING)) {
+-              if (info->flags & ASYNC_CLOSING)
+-                      interruptible_sleep_on(&info->close_wait);
+-              MOD_DEC_USE_COUNT;
+-              return -EAGAIN;
+-      }
+-
+-      /*
+-       * Start up the serial port
+-       */
+-      retval = ambauart_startup(info);
+-      if (retval) {
+-              MOD_DEC_USE_COUNT;
+-              return retval;
+-      }
+-
+-      retval = block_til_ready(tty, filp, info);
+-      if (retval) {
+-              MOD_DEC_USE_COUNT;
+-              return retval;
+-      }
+-
+-      if ((info->state->count == 1) &&
+-          (info->flags & ASYNC_SPLIT_TERMIOS)) {
+-              if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+-                      *tty->termios = info->state->normal_termios;
+-              else
+-                      *tty->termios = info->state->callout_termios;
+-      }
+-#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+-      if (ambauart_cons.cflag && ambauart_cons.index == line) {
+-              tty->termios->c_cflag = ambauart_cons.cflag;
+-              ambauart_cons.cflag = 0;
+-      }
+-#endif
+-      ambauart_change_speed(info, NULL);
+-      info->session = current->session;
+-      info->pgrp = current->pgrp;
+-      return 0;
+-}
+-
+-int __init ambauart_init(void)
+-{
+-      int i;
+-
+-      ambanormal_driver.magic = TTY_DRIVER_MAGIC;
+-      ambanormal_driver.driver_name = "serial_amba";
+-      ambanormal_driver.name = SERIAL_AMBA_NAME;
+-      ambanormal_driver.major = SERIAL_AMBA_MAJOR;
+-      ambanormal_driver.minor_start = SERIAL_AMBA_MINOR;
+-      ambanormal_driver.num = SERIAL_AMBA_NR;
+-      ambanormal_driver.type = TTY_DRIVER_TYPE_SERIAL;
+-      ambanormal_driver.subtype = SERIAL_TYPE_NORMAL;
+-      ambanormal_driver.init_termios = tty_std_termios;
+-      ambanormal_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL;
+-      ambanormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+-      ambanormal_driver.refcount = &ambauart_refcount;
+-      ambanormal_driver.table = ambauart_table;
+-      ambanormal_driver.termios = ambauart_termios;
+-      ambanormal_driver.termios_locked = ambauart_termios_locked;
+-
+-      ambanormal_driver.open = ambauart_open;
+-      ambanormal_driver.close = ambauart_close;
+-      ambanormal_driver.write = ambauart_write;
+-      ambanormal_driver.put_char = ambauart_put_char;
+-      ambanormal_driver.flush_chars = ambauart_flush_chars;
+-      ambanormal_driver.write_room = ambauart_write_room;
+-      ambanormal_driver.chars_in_buffer = ambauart_chars_in_buffer;
+-      ambanormal_driver.flush_buffer  = ambauart_flush_buffer;
+-      ambanormal_driver.ioctl = ambauart_ioctl;
+-      ambanormal_driver.throttle = ambauart_throttle;
+-      ambanormal_driver.unthrottle = ambauart_unthrottle;
+-      ambanormal_driver.send_xchar = ambauart_send_xchar;
+-      ambanormal_driver.set_termios = ambauart_set_termios;
+-      ambanormal_driver.stop = ambauart_stop;
+-      ambanormal_driver.start = ambauart_start;
+-      ambanormal_driver.hangup = ambauart_hangup;
+-      ambanormal_driver.break_ctl = ambauart_break_ctl;
+-      ambanormal_driver.wait_until_sent = ambauart_wait_until_sent;
+-      ambanormal_driver.read_proc = NULL;
+-
+-      /*
+-       * The callout device is just like the normal device except for
+-       * the major number and the subtype code.
+-       */
+-      ambacallout_driver = ambanormal_driver;
+-      ambacallout_driver.name = CALLOUT_AMBA_NAME;
+-      ambacallout_driver.major = CALLOUT_AMBA_MAJOR;
+-      ambacallout_driver.subtype = SERIAL_TYPE_CALLOUT;
+-      ambacallout_driver.read_proc = NULL;
+-      ambacallout_driver.proc_entry = NULL;
+-
+-      if (tty_register_driver(&ambanormal_driver))
+-              panic("Couldn't register AMBA serial driver\n");
+-      if (tty_register_driver(&ambacallout_driver))
+-              panic("Couldn't register AMBA callout driver\n");
+-
+-      for (i = 0; i < SERIAL_AMBA_NR; i++) {
+-              struct amba_state *state = amba_state + i;
+-              state->line             = i;
+-              state->close_delay      = 5 * HZ / 10;
+-              state->closing_wait     = 30 * HZ;
+-              state->callout_termios  = ambacallout_driver.init_termios;
+-              state->normal_termios   = ambanormal_driver.init_termios;
+-      }
+-
+-      return 0;
+-}
+-
+-__initcall(ambauart_init);
+-
+-#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+-/************** console driver *****************/
+-
+-/*
+- * This code is currently never used; console->read is never called.
+- * Therefore, although we have an implementation, we don't use it.
+- * FIXME: the "const char *s" should be fixed to "char *s" some day.
+- * (when the definition in include/linux/console.h is also fixed)
+- */
+-#ifdef used_and_not_const_char_pointer
+-static int ambauart_console_read(struct console *co, const char *s, u_int count)
+-{
+-      struct amba_port *port = &amba_ports[co->index];
+-      unsigned int status;
+-      char *w;
+-      int c;
+-#if DEBUG
+-      printk("ambauart_console_read() called\n");
+-#endif
+-
+-      c = 0;
+-      w = s;
+-      while (c < count) {
+-              status = UART_GET_FR(port);
+-              if (UART_RX_DATA(status)) {
+-                      *w++ = UART_GET_CHAR(port);
+-                      c++;
+-              } else {
+-                      // nothing more to get, return
+-                      return c;
+-              }
+-      }
+-      // return the count
+-      return c;
+-}
+-#endif
+-
+-/*
+- *    Print a string to the serial port trying not to disturb
+- *    any possible real use of the port...
+- *
+- *    The console must be locked when we get here.
+- */
+-static void ambauart_console_write(struct console *co, const char *s, u_int count)
+-{
+-      struct amba_port *port = &amba_ports[co->index];
+-      unsigned int status, old_cr;
+-      int i;
+-
+-      /*
+-       *      First save the CR then disable the interrupts
+-       */
+-      old_cr = UART_GET_CR(port);
+-      UART_PUT_CR(port, AMBA_UARTCR_UARTEN);
+-
+-      /*
+-       *      Now, do each character
+-       */
+-      for (i = 0; i < count; i++) {
+-              do {
+-                      status = UART_GET_FR(port);
+-              } while (!UART_TX_READY(status));
+-              UART_PUT_CHAR(port, s[i]);
+-              if (s[i] == '\n') {
+-                      do {
+-                              status = UART_GET_FR(port);
+-                      } while (!UART_TX_READY(status));
+-                      UART_PUT_CHAR(port, '\r');
+-              }
+-      }
+-
+-      /*
+-       *      Finally, wait for transmitter to become empty
+-       *      and restore the TCR
+-       */
+-      do {
+-              status = UART_GET_FR(port);
+-      } while (status & AMBA_UARTFR_BUSY);
+-      UART_PUT_CR(port, old_cr);
+-}
+-
+-static kdev_t ambauart_console_device(struct console *c)
+-{
+-      return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + c->index);
+-}
+-
+-static int __init ambauart_console_setup(struct console *co, char *options)
+-{
+-      struct amba_port *port;
+-      int baud = 38400;
+-      int bits = 8;
+-      int parity = 'n';
+-      u_int cflag = CREAD | HUPCL | CLOCAL;
+-      u_int lcr_h, quot;
+-
+-      if (co->index >= SERIAL_AMBA_NR)
+-              co->index = 0;
+-
+-      port = &amba_ports[co->index];
+-
+-      if (options) {
+-              char *s = options;
+-              baud = simple_strtoul(s, NULL, 10);
+-              while (*s >= '0' && *s <= '9')
+-                      s++;
+-              if (*s) parity = *s++;
+-              if (*s) bits = *s - '0';
+-      }
+-
+-      /*
+-       *    Now construct a cflag setting.
+-       */
+-      switch (baud) {
+-      case 1200:      cflag |= B1200;                 break;
+-      case 2400:      cflag |= B2400;                 break;
+-      case 4800:      cflag |= B4800;                 break;
+-      default:        cflag |= B9600;   baud = 9600;  break;
+-      case 19200:     cflag |= B19200;                break;
+-      case 38400:     cflag |= B38400;                break;
+-      case 57600:     cflag |= B57600;                break;
+-      case 115200:    cflag |= B115200;               break;
+-      }
+-      switch (bits) {
+-      case 7:   cflag |= CS7; lcr_h = AMBA_UARTLCR_H_WLEN_7;  break;
+-      default:  cflag |= CS8; lcr_h = AMBA_UARTLCR_H_WLEN_8;  break;
+-      }
+-      switch (parity) {
+-      case 'o':
+-      case 'O': cflag |= PARODD; lcr_h |= AMBA_UARTLCR_H_PEN; break;
+-      case 'e':
+-      case 'E': cflag |= PARENB; lcr_h |= AMBA_UARTLCR_H_PEN |
+-                                          AMBA_UARTLCR_H_EPS; break;
+-      }
+-
+-      co->cflag = cflag;
+-
+-      if (port->fifosize > 1)
+-              lcr_h |= AMBA_UARTLCR_H_FEN;
+-
+-      quot = (port->uartclk / (16 * baud)) - 1;
+-
+-      UART_PUT_LCRL(port, (quot & 0xff));
+-      UART_PUT_LCRM(port, (quot >> 8));
+-      UART_PUT_LCRH(port, lcr_h);
+-
+-      /* we will enable the port as we need it */
+-      UART_PUT_CR(port, 0);
+-
+-      return 0;
+-}
+-
+-static struct console ambauart_cons =
+-{
+-      name:           SERIAL_AMBA_NAME,
+-      write:          ambauart_console_write,
+-#ifdef used_and_not_const_char_pointer
+-      read:           ambauart_console_read,
+-#endif
+-      device:         ambauart_console_device,
+-      setup:          ambauart_console_setup,
+-      flags:          CON_PRINTBUFFER,
+-      index:          -1,
+-};
+-
+-void __init ambauart_console_init(void)
+-{
+-      register_console(&ambauart_cons);
+-}
+-
+-#endif /* CONFIG_SERIAL_AMBA_CONSOLE */
+-
+-MODULE_LICENSE("GPL");
+-EXPORT_NO_SYMBOLS;
+--- linux-2.4.27/drivers/char/tty_io.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/tty_io.c
+@@ -19,7 +19,7 @@
+  * Also restructured routines so that there is more of a separation
+  * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
+  * the low-level tty routines (serial.c, pty.c, console.c).  This
+- * makes for cleaner and more compact code.  -TYT, 9/17/92 
++ * makes for cleaner and more compact code.  -TYT, 9/17/92
+  *
+  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
+  * which can be dynamically activated and de-activated by the line
+@@ -41,7 +41,7 @@
+  *
+  * New TIOCLINUX variants added.
+  *    -- mj@k332.feld.cvut.cz, 19-Nov-95
+- * 
++ *
+  * Restrict vt switching via ioctl()
+  *      -- grif@cs.ucr.edu, 5-Dec-95
+  *
+@@ -151,8 +151,7 @@
+ extern void tty3215_init(void);
+ extern void tub3270_con_init(void);
+ extern void tub3270_init(void);
+-extern void rs285_console_init(void);
+-extern void sa1100_rs_console_init(void);
++extern void uart_console_init(void);
+ extern void sgi_serial_console_init(void);
+ extern void sn_sal_serial_console_init(void);
+ extern void sci_console_init(void);
+@@ -164,6 +163,7 @@
+ extern void txx9_serial_console_init(void);
+ extern void sb1250_serial_console_init(void);
+ extern void arc_console_init(void);
++extern void rs285_console_init(void);
+ extern int hvc_console_init(void);
+ #ifndef MIN
+@@ -201,7 +201,7 @@
+       else
+               sprintf(buf, name,
+                       idx + tty->driver.name_base);
+-              
++
+       return buf;
+ }
+@@ -239,7 +239,7 @@
+ #ifdef CHECK_TTY_COUNT
+       struct list_head *p;
+       int count = 0;
+-      
++
+       file_list_lock();
+       for(p = tty->tty_files.next; p != &tty->tty_files; p = p->next) {
+               if(list_entry(p, struct file, f_list)->private_data == tty)
+@@ -255,7 +255,7 @@
+                                   "!= #fd's(%d) in %s\n",
+                      kdevname(tty->device), tty->count, count, routine);
+               return count;
+-       }      
++       }
+ #endif
+       return 0;
+ }
+@@ -264,14 +264,14 @@
+ {
+       if (disc < N_TTY || disc >= NR_LDISCS)
+               return -EINVAL;
+-      
++
+       if (new_ldisc) {
+               ldiscs[disc] = *new_ldisc;
+               ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
+               ldiscs[disc].num = disc;
+       } else
+               memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
+-      
++
+       return 0;
+ }
+@@ -301,7 +301,7 @@
+       o_ldisc = tty->ldisc;
+       tty_wait_until_sent(tty, 0);
+-      
++
+       /* Shutdown the current discipline. */
+       if (tty->ldisc.close)
+               (tty->ldisc.close)(tty);
+@@ -339,7 +339,7 @@
+ {
+       int     major, minor;
+       struct tty_driver *p;
+-      
++
+       minor = MINOR(device);
+       major = MAJOR(device);
+@@ -456,7 +456,7 @@
+               redirect = NULL;
+       }
+       spin_unlock(&redirect_lock);
+-      
++
+       check_tty_count(tty, "do_tty_hangup");
+       file_list_lock();
+       for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) {
+@@ -473,7 +473,7 @@
+               filp->f_op = &hung_up_tty_fops;
+       }
+       file_list_unlock();
+-      
++
+       /* FIXME! What are the locking issues here? This may me overdoing things.. */
+       {
+               unsigned long flags;
+@@ -510,7 +510,7 @@
+                                               "error %d\n", -i);
+               }
+       }
+-      
++
+       read_lock(&tasklist_lock);
+       for_each_task(p) {
+               if ((tty->session > 0) && (p->session == tty->session) &&
+@@ -550,7 +550,7 @@
+ {
+ #ifdef TTY_DEBUG_HANGUP
+       char    buf[64];
+-      
++
+       printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
+ #endif
+       schedule_task(&tty->tq_hangup);
+@@ -650,7 +650,7 @@
+       wake_up_interruptible(&tty->write_wait);
+ }
+-static ssize_t tty_read(struct file * file, char * buf, size_t count, 
++static ssize_t tty_read(struct file * file, char * buf, size_t count,
+                       loff_t *ppos)
+ {
+       int i;
+@@ -707,7 +707,7 @@
+       size_t count)
+ {
+       ssize_t ret = 0, written = 0;
+-      
++
+       if (file->f_flags & O_NONBLOCK) {
+               if (down_trylock(&tty->atomic_write))
+                       return -EAGAIN;
+@@ -835,7 +835,7 @@
+       struct tty_struct *tty, *o_tty;
+       struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
+       struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
+-      struct tty_driver *driver;      
++      struct tty_driver *driver;
+       int retval=0;
+       int idx;
+@@ -845,7 +845,7 @@
+       idx = MINOR(device) - driver->minor_start;
+-      /* 
++      /*
+        * Check whether we need to acquire the tty semaphore to avoid
+        * race conditions.  For now, play it safe.
+        */
+@@ -859,7 +859,7 @@
+        * First time open is complex, especially for PTY devices.
+        * This code guarantees that either everything succeeds and the
+        * TTY is ready for operation, or else the table slots are vacated
+-       * and the allocated memory released.  (Except that the termios 
++       * and the allocated memory released.  (Except that the termios
+        * and locked termios may be retained.)
+        */
+@@ -938,13 +938,13 @@
+               o_tty->link = tty;
+       }
+-      /* 
++      /*
+        * All structures have been allocated, so now we install them.
+-       * Failures after this point use release_mem to clean up, so 
++       * Failures after this point use release_mem to clean up, so
+        * there's no need to null out the local pointers.
+        */
+       driver->table[idx] = tty;
+-      
++
+       if (!*tp_loc)
+               *tp_loc = tp;
+       if (!*ltp_loc)
+@@ -954,7 +954,7 @@
+       (*driver->refcount)++;
+       tty->count++;
+-      /* 
++      /*
+        * Structures all installed ... call the ldisc open routines.
+        * If we fail here just call release_mem to clean up.  No need
+        * to decrement the use counts, as release_mem doesn't care.
+@@ -988,7 +988,7 @@
+       if (driver->type == TTY_DRIVER_TYPE_PTY &&
+           driver->subtype == PTY_TYPE_MASTER) {
+               /*
+-               * special case for PTY masters: only one open permitted, 
++               * special case for PTY masters: only one open permitted,
+                * and the slave side open count is incremented as well.
+                */
+               if (tty->count) {
+@@ -1002,7 +1002,7 @@
+ success:
+       *ret_tty = tty;
+-      
++
+       /* All paths come through here to release the semaphore */
+ end_init:
+       up_tty_sem(idx);
+@@ -1080,7 +1080,7 @@
+       int     pty_master, tty_closing, o_tty_closing, do_sleep;
+       int     idx;
+       char    buf[64];
+-      
++
+       tty = (struct tty_struct *)filp->private_data;
+       if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
+               return;
+@@ -1138,7 +1138,7 @@
+                              idx, kdevname(tty->device));
+                       return;
+               }
+-              if (o_tty->termios_locked != 
++              if (o_tty->termios_locked !=
+                     tty->driver.other->termios_locked[idx]) {
+                       printk(KERN_DEBUG "release_dev: other->termios_locked["
+                                         "%d] not o_termios_locked for (%s)\n",
+@@ -1204,11 +1204,11 @@
+               printk(KERN_WARNING "release_dev: %s: read/write wait queue "
+                                   "active!\n", tty_name(tty, buf));
+               schedule();
+-      }       
++      }
+       /*
+-       * The closing flags are now consistent with the open counts on 
+-       * both sides, and we've completed the last operation that could 
++       * The closing flags are now consistent with the open counts on
++       * both sides, and we've completed the last operation that could
+        * block, so it's safe to proceed with closing.
+        */
+       if (pty_master) {
+@@ -1266,7 +1266,7 @@
+       /* check whether both sides are closing ... */
+       if (!tty_closing || (o_tty && !o_tty_closing))
+               return;
+-      
++
+ #ifdef TTY_DEBUG_HANGUP
+       printk(KERN_DEBUG "freeing tty structure...");
+ #endif
+@@ -1284,14 +1284,14 @@
+                       (o_tty->ldisc.close)(o_tty);
+               o_tty->ldisc = ldiscs[N_TTY];
+       }
+-      
++
+       /*
+-       * Make sure that the tty's task queue isn't activated. 
++       * Make sure that the tty's task queue isn't activated.
+        */
+       run_task_queue(&tq_timer);
+       flush_scheduled_tasks();
+-      /* 
++      /*
+        * The release_mem function takes care of the details of clearing
+        * the slots and preserving the termios structure.
+        */
+@@ -1482,7 +1482,7 @@
+       tty = (struct tty_struct *)filp->private_data;
+       if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_fasync"))
+               return 0;
+-      
++
+       retval = fasync_helper(fd, filp, on, &tty->fasync);
+       if (retval <= 0)
+               return retval;
+@@ -1697,7 +1697,7 @@
+ static int tty_generic_brk(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+ {
+-      if (cmd == TCSBRK && arg) 
++      if (cmd == TCSBRK && arg)
+       {
+               /* tcdrain case */
+               int retval = tty_check_change(tty);
+@@ -1718,7 +1718,7 @@
+ {
+       struct tty_struct *tty, *real_tty;
+       int retval;
+-      
++
+       tty = (struct tty_struct *)file->private_data;
+       if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
+               return -EINVAL;
+@@ -1738,7 +1738,7 @@
+                       if (tty->driver.ioctl)
+                               return tty->driver.ioctl(tty, file, cmd, arg);
+                       return -EINVAL;
+-                      
++
+               /* These two ioctl's always return success; even if */
+               /* the driver doesn't support them. */
+               case TCSBRK:
+@@ -1761,7 +1761,7 @@
+       case TIOCSBRK:
+       case TIOCCBRK:
+       case TCSBRK:
+-      case TCSBRKP:                   
++      case TCSBRKP:
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
+@@ -1824,7 +1824,7 @@
+               case TIOCSBRK:  /* Turn break on, unconditionally */
+                       tty->driver.break_ctl(tty, -1);
+                       return 0;
+-                      
++
+               case TIOCCBRK:  /* Turn break off, unconditionally */
+                       tty->driver.break_ctl(tty, 0);
+                       return 0;
+@@ -1837,7 +1837,7 @@
+                       if (!arg)
+                               return send_break(tty, HZ/4);
+                       return 0;
+-              case TCSBRKP:   /* support for POSIX tcsendbreak() */   
++              case TCSBRKP:   /* support for POSIX tcsendbreak() */
+                       return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
+       }
+       if (tty->driver.ioctl) {
+@@ -1859,7 +1859,7 @@
+  * prevent trojan horses by killing all processes associated with this
+  * tty when the user hits the "Secure Attention Key".  Required for
+  * super-paranoid applications --- see the Orange Book for more details.
+- * 
++ *
+  * This code could be nicer; ideally it should send a HUP, wait a few
+  * seconds, then send a INT, and then a KILL signal.  But you then
+  * have to coordinate with the init process, since all processes associated
+@@ -1883,7 +1883,7 @@
+       int session;
+       int             i;
+       struct file     *filp;
+-      
++
+       if (!tty)
+               return;
+       session  = tty->session;
+@@ -1968,7 +1968,7 @@
+       count = tty->flip.count;
+       tty->flip.count = 0;
+       restore_flags(flags);
+-      
++
+       tty->ldisc.receive_buf(tty, cp, fp, count);
+ }
+@@ -2000,7 +2000,7 @@
+       i = cflag & CBAUD;
+       if (i & CBAUDEX) {
+               i &= ~CBAUDEX;
+-              if (i < 1 || i+15 >= n_baud_table) 
++              if (i < 1 || i+15 >= n_baud_table)
+                       tty->termios->c_cflag &= ~CBAUDEX;
+               else
+                       i += 15;
+@@ -2013,7 +2013,7 @@
+               }
+               return(tty->alt_speed);
+       }
+-      
++
+       return baud_table[i];
+ }
+@@ -2079,7 +2079,7 @@
+                               mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+                       break;
+       }
+-      if ( (minor <  driver->minor_start) || 
++      if ( (minor <  driver->minor_start) ||
+            (minor >= driver->minor_start + driver->num) ) {
+               printk(KERN_ERR "Attempt to register invalid minor number "
+                      "with devfs (%d:%d).\n", (int)driver->major,(int)minor);
+@@ -2132,12 +2132,12 @@
+       if (!driver->put_char)
+               driver->put_char = tty_default_put_char;
+-      
++
+       driver->prev = 0;
+       driver->next = tty_drivers;
+       if (tty_drivers) tty_drivers->prev = driver;
+       tty_drivers = driver;
+-      
++
+       if ( !(driver->flags & TTY_DRIVER_NO_DEVFS) ) {
+               for(i = 0; i < driver->num; i++)
+                   tty_register_devfs(driver, 0, driver->minor_start + i);
+@@ -2156,7 +2156,7 @@
+       int     i, found = 0;
+       struct termios *tp;
+       const char *othername = NULL;
+-      
++
+       if (*driver->refcount)
+               return -EBUSY;
+@@ -2166,7 +2166,7 @@
+               else if (p->major == driver->major)
+                       othername = p->name;
+       }
+-      
++
+       if (!found)
+               return -ENOENT;
+@@ -2181,7 +2181,7 @@
+               driver->prev->next = driver->next;
+       else
+               tty_drivers = driver->next;
+-      
++
+       if (driver->next)
+               driver->next->prev = driver->prev;
+@@ -2221,7 +2221,7 @@
+       (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+       /*
+-       * Set up the standard termios.  Individual tty drivers may 
++       * Set up the standard termios.  Individual tty drivers may
+        * deviate from this; this is used as a template.
+        */
+       memset(&tty_std_termios, 0, sizeof(struct termios));
+@@ -2233,11 +2233,11 @@
+               ECHOCTL | ECHOKE | IEXTEN;
+       /*
+-       * set up the console device so that later boot sequences can 
++       * set up the console device so that later boot sequences can
+        * inform about problems etc..
+        */
+ #ifdef CONFIG_EARLY_PRINTK
+-      disable_early_printk(); 
++      disable_early_printk();
+ #endif
+ #ifdef CONFIG_HVC_CONSOLE
+       hvc_console_init();
+@@ -2288,18 +2288,12 @@
+ #ifdef CONFIG_STDIO_CONSOLE
+       stdio_console_init();
+ #endif
+-#ifdef CONFIG_SERIAL_21285_CONSOLE
+-      rs285_console_init();
+-#endif
+-#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+-      sa1100_rs_console_init();
++#ifdef CONFIG_SERIAL_CORE_CONSOLE
++      uart_console_init();
+ #endif
+ #ifdef CONFIG_ARC_CONSOLE
+       arc_console_init();
+ #endif
+-#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+-      ambauart_console_init();
+-#endif
+ #ifdef CONFIG_SERIAL_TX3912_CONSOLE
+       tx3912_console_init();
+ #endif
+@@ -2315,6 +2309,9 @@
+ #ifdef CONFIG_IP22_SERIAL
+       sgi_serial_console_init();
+ #endif
++#ifdef CONFIG_SERIAL_21285_CONSOLE
++      rs285_console_init();
++#endif
+ }
+ static struct tty_driver dev_tty_driver, dev_syscons_driver;
+@@ -2347,7 +2344,7 @@
+       dev_tty_driver.num = 1;
+       dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+       dev_tty_driver.subtype = SYSTEM_TYPE_TTY;
+-      
++
+       if (tty_register_driver(&dev_tty_driver))
+               panic("Couldn't register /dev/tty driver\n");
+@@ -2363,7 +2360,7 @@
+               panic("Couldn't register /dev/console driver\n");
+       /* console calls tty_register_driver() before kmalloc() works.
+-       * Thus, we can't devfs_register() then.  Do so now, instead. 
++       * Thus, we can't devfs_register() then.  Do so now, instead.
+        */
+ #ifdef CONFIG_VT
+       con_init_devfs();
+@@ -2381,7 +2378,7 @@
+       if (tty_register_driver(&dev_ptmx_driver))
+               panic("Couldn't register /dev/ptmx driver\n");
+ #endif
+-      
++
+ #ifdef CONFIG_VT
+       dev_console_driver = dev_tty_driver;
+       dev_console_driver.driver_name = "/dev/vc/0";
+@@ -2441,10 +2438,10 @@
+       pty_init();
+ #ifdef CONFIG_MOXA_SMARTIO
+       mxser_init();
+-#endif        
++#endif
+ #ifdef CONFIG_MOXA_INTELLIO
+       moxa_init();
+-#endif        
++#endif
+ #ifdef CONFIG_VT
+       vcs_init();
+ #endif
+--- linux-2.4.27/drivers/char/wdt285.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/wdt285.c
+@@ -151,7 +151,7 @@
+                       if (get_user(new_margin, (int *)arg))
+                               return -EFAULT;
+                       /* Arbitrary, can't find the card's limits */
+-                      if ((new_marg < 0) || (new_margin > 60))
++                      if ((new_margin < 0) || (new_margin > 60))
+                               return -EINVAL;
+                       soft_margin = new_margin;
+                       watchdog_ping();
+--- linux-2.4.27/drivers/char/wdt977.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/char/wdt977.c
+@@ -27,6 +27,7 @@
+ #include <asm/io.h>
+ #include <asm/system.h>
+ #include <asm/mach-types.h>
++#include <asm/uaccess.h>
+ #define WATCHDOG_MINOR        130
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/Kconfig
+@@ -0,0 +1,38 @@
++config CPU_FREQ_PROC_INTF
++      tristate "/proc/cpufreq interface (deprecated)"
++      depends on CPU_FREQ && PROC_FS
++      help
++        This enables the /proc/cpufreq interface for controlling
++        CPUFreq. Please note that it is recommended to use the sysfs
++        interface instead (which is built automatically). 
++        
++        For details, take a look at linux/Documentation/cpufreq. 
++        
++        If in doubt, say N.
++
++config CPU_FREQ_GOV_USERSPACE
++       tristate "'userspace' governor for userspace frequency scaling"
++       depends on CPU_FREQ
++       help
++        Enable this cpufreq governor when you either want to set the
++        CPU frequency manually or when an userspace programm shall
++          be able to set the CPU dynamically, like on LART 
++        ( http://www.lart.tudelft.nl/ )
++
++        For details, take a look at linux/Documentation/cpufreq. 
++
++        If in doubt, say Y.
++
++config CPU_FREQ_24_API
++      bool "/proc/sys/cpu/ interface (2.4. / OLD)"
++      depends on CPU_FREQ && SYSCTL && CPU_FREQ_GOV_USERSPACE
++      help
++        This enables the /proc/sys/cpu/ sysctl interface for controlling
++        the CPUFreq,"userspace" governor. This is the same interface
++        as known from the.4.-kernel patches for CPUFreq, and offers
++        the same functionality as long as "userspace" is the
++        selected governor for the specified CPU.
++      
++        For details, take a look at linux/Documentation/cpufreq. 
++
++        If in doubt, say N.
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/Makefile
+@@ -0,0 +1,4 @@
++#CPUfreq governors and cross-arch helpers
++obj-$(CONFIG_CPU_FREQ_TABLE)          += freq_table.o
++obj-$(CONFIG_CPU_FREQ_PROC_INTF)      += proc_intf.o
++obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE)  += userspace.o
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/cpufreq.c
+@@ -0,0 +1,720 @@
++/*
++ *  linux/kernel/cpufreq.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *            (C) 2002 - 2003 Dominik Brodowski <linux@brodo.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.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/notifier.h>
++#include <linux/cpufreq.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++
++#include <asm/semaphore.h>
++/**
++ * The "cpufreq driver" - the arch- or hardware-dependend low
++ * level driver of CPUFreq support, and its spinlock. This lock
++ * also protects the cpufreq_cpu_data array.
++ */
++static struct cpufreq_driver          *cpufreq_driver;
++static struct cpufreq_policy  *cpufreq_cpu_data[NR_CPUS];
++static spinlock_t             cpufreq_driver_lock = SPIN_LOCK_UNLOCKED;
++
++/* internal prototype */
++static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);
++
++
++/**
++ * Two notifier lists: the "policy" list is involved in the 
++ * validation process for a new CPU frequency policy; the 
++ * "transition" list for kernel code that needs to handle
++ * changes to devices when the CPU clock speed changes.
++ * The mutex locks both lists.
++ */
++static struct notifier_block    *cpufreq_policy_notifier_list;
++static struct notifier_block    *cpufreq_transition_notifier_list;
++static DECLARE_RWSEM          (cpufreq_notifier_rwsem);
++
++
++static LIST_HEAD(cpufreq_governor_list);
++static DECLARE_MUTEX          (cpufreq_governor_sem);
++
++/*
++ * backport info:
++ * we don't have a kobj we can use for ref-counting, so use a
++ * "unsigned int policy->use_count" and an "unload_sem" [idea from
++ * Pat Mochel's struct driver unload_sem] for proper reference counting.
++ */
++
++static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
++{
++      struct cpufreq_policy *data;
++      unsigned long flags;
++
++      if (cpu >= NR_CPUS)
++              goto err_out;
++
++      /* get the cpufreq driver */
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++
++      if (!cpufreq_driver)
++              goto err_out_unlock;
++
++      /* get the CPU */
++      data = cpufreq_cpu_data[cpu];
++
++      if (!data)
++              goto err_out_unlock;
++
++      if (!data->use_count)
++              goto err_out_unlock;
++
++      data->use_count += 1;
++
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++      return data;
++
++ err_out_unlock:
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++ err_out:
++      return NULL;
++}
++
++static void cpufreq_cpu_put(struct cpufreq_policy *data)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      data->use_count -= 1;
++      if (!data->use_count) {
++              spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++              up(&data->unload_sem);
++              return;
++      }
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++}
++
++/*********************************************************************
++ *                          SYSFS INTERFACE                          *
++ *********************************************************************/
++
++/**
++ * cpufreq_parse_governor - parse a governor string
++ */
++int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
++                              struct cpufreq_governor **governor)
++{
++      if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
++              *policy = CPUFREQ_POLICY_PERFORMANCE;
++              return 0;
++      } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {
++              *policy = CPUFREQ_POLICY_POWERSAVE;
++              return 0;
++      } else  {
++              struct cpufreq_governor *t;
++              down(&cpufreq_governor_sem);
++              if (!cpufreq_driver || !cpufreq_driver->target)
++                      goto out;
++              list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
++                      if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {
++                              *governor = t;
++                              *policy = CPUFREQ_POLICY_GOVERNOR;
++                              up(&cpufreq_governor_sem);
++                              return 0;
++                      }
++              }
++      out:
++              up(&cpufreq_governor_sem);
++      }
++      return -EINVAL;
++}
++EXPORT_SYMBOL_GPL(cpufreq_parse_governor);
++
++
++/* backport info:
++ * all the sysfs stuff is missing -- of course
++ */
++
++/**
++ * cpufreq_add_dev - add a CPU device
++ *
++ * Adds the cpufreq interface for a CPU device. 
++ */
++static int cpufreq_add_dev (unsigned int cpu)
++{
++      int ret = 0;
++      struct cpufreq_policy new_policy;
++      struct cpufreq_policy *policy;
++      unsigned long flags;
++
++      policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
++      if (!policy)
++              return -ENOMEM;
++      memset(policy, 0, sizeof(struct cpufreq_policy));
++
++      policy->cpu = cpu;
++      policy->use_count = 1;
++      init_MUTEX_LOCKED(&policy->lock);
++      init_MUTEX_LOCKED(&policy->unload_sem);
++
++      /* call driver. From then on the cpufreq must be able
++       * to accept all calls to ->verify and ->setpolicy for this CPU
++       */
++      ret = cpufreq_driver->init(policy);
++      if (ret)
++              goto err_out;
++
++      memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
++
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      cpufreq_cpu_data[cpu] = policy;
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++      up(&policy->lock);
++      
++      /* set default policy */
++      ret = cpufreq_set_policy(&new_policy);
++      if (ret)
++              goto err_out_unregister;
++
++      return 0;
++
++
++ err_out_unregister:
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      cpufreq_cpu_data[cpu] = NULL;
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++ err_out:
++      kfree(policy);
++      return ret;
++}
++
++
++/**
++ * cpufreq_remove_dev - remove a CPU device
++ *
++ * Removes the cpufreq interface for a CPU device.
++ */
++static int cpufreq_remove_dev (unsigned int cpu)
++{
++      unsigned long flags;
++      struct cpufreq_policy *data;
++
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      data = cpufreq_cpu_data[cpu];
++      if (!data) {
++              spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++              return -EINVAL;
++      }
++      cpufreq_cpu_data[cpu] = NULL;
++
++      data->use_count -= 1;
++      if (!data->use_count) {
++              spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++              up(&data->unload_sem);
++      } else {
++              spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++              /* this will sleep until data->use_count gets to zero */
++              down(&data->unload_sem);
++              up(&data->unload_sem);
++      }
++
++      if (cpufreq_driver->target)
++              __cpufreq_governor(data, CPUFREQ_GOV_STOP);
++
++      if (cpufreq_driver->exit)
++              cpufreq_driver->exit(data);
++
++      kfree(data);
++
++      return 0;
++}
++
++
++/*********************************************************************
++ *                     NOTIFIER LISTS INTERFACE                      *
++ *********************************************************************/
++
++/**
++ *    cpufreq_register_notifier - register a driver with cpufreq
++ *    @nb: notifier function to register
++ *      @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
++ *
++ *    Add a driver to one of two lists: either a list of drivers that 
++ *      are notified about clock rate changes (once before and once after
++ *      the transition), or a list of drivers that are notified about
++ *      changes in cpufreq policy.
++ *
++ *    This function may sleep, and has the same return conditions as
++ *    notifier_chain_register.
++ */
++int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
++{
++      int ret;
++
++      down_write(&cpufreq_notifier_rwsem);
++      switch (list) {
++      case CPUFREQ_TRANSITION_NOTIFIER:
++              ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb);
++              break;
++      case CPUFREQ_POLICY_NOTIFIER:
++              ret = notifier_chain_register(&cpufreq_policy_notifier_list, nb);
++              break;
++      default:
++              ret = -EINVAL;
++      }
++      up_write(&cpufreq_notifier_rwsem);
++
++      return ret;
++}
++EXPORT_SYMBOL(cpufreq_register_notifier);
++
++
++/**
++ *    cpufreq_unregister_notifier - unregister a driver with cpufreq
++ *    @nb: notifier block to be unregistered
++ *      @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
++ *
++ *    Remove a driver from the CPU frequency notifier list.
++ *
++ *    This function may sleep, and has the same return conditions as
++ *    notifier_chain_unregister.
++ */
++int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
++{
++      int ret;
++
++      down_write(&cpufreq_notifier_rwsem);
++      switch (list) {
++      case CPUFREQ_TRANSITION_NOTIFIER:
++              ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb);
++              break;
++      case CPUFREQ_POLICY_NOTIFIER:
++              ret = notifier_chain_unregister(&cpufreq_policy_notifier_list, nb);
++              break;
++      default:
++              ret = -EINVAL;
++      }
++      up_write(&cpufreq_notifier_rwsem);
++
++      return ret;
++}
++EXPORT_SYMBOL(cpufreq_unregister_notifier);
++
++
++/*********************************************************************
++ *                              GOVERNORS                            *
++ *********************************************************************/
++
++
++int __cpufreq_driver_target(struct cpufreq_policy *policy,
++                          unsigned int target_freq,
++                          unsigned int relation)
++{
++      return cpufreq_driver->target(policy, target_freq, relation);
++}
++EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
++
++
++int cpufreq_driver_target(struct cpufreq_policy *policy,
++                        unsigned int target_freq,
++                        unsigned int relation)
++{
++      unsigned int ret;
++
++      policy = cpufreq_cpu_get(policy->cpu);
++      if (!policy)
++              return -EINVAL;
++
++      down(&policy->lock);
++
++      ret = __cpufreq_driver_target(policy, target_freq, relation);
++
++      up(&policy->lock);
++
++      cpufreq_cpu_put(policy);
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(cpufreq_driver_target);
++
++
++static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event)
++{
++      int ret = 0;
++
++      switch (policy->policy) {
++      case CPUFREQ_POLICY_POWERSAVE: 
++              if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) {
++                      ret = __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L);
++              }
++              break;
++      case CPUFREQ_POLICY_PERFORMANCE:
++              if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) {
++                      ret = __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H);
++              }
++              break;
++      case CPUFREQ_POLICY_GOVERNOR:
++              ret = policy->governor->governor(policy, event);
++              break;
++      default:
++              ret = -EINVAL;
++      }
++
++      return ret;
++}
++
++
++int cpufreq_governor(unsigned int cpu, unsigned int event)
++{
++      int ret = 0;
++      struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
++
++      if (!policy)
++              return -EINVAL;
++
++      down(&policy->lock);
++      ret = __cpufreq_governor(policy, event);
++      up(&policy->lock);
++
++      cpufreq_cpu_put(policy);
++
++      return ret;
++}
++EXPORT_SYMBOL_GPL(cpufreq_governor);
++
++
++int cpufreq_register_governor(struct cpufreq_governor *governor)
++{
++      struct cpufreq_governor *t;
++
++      if (!governor)
++              return -EINVAL;
++
++      if (!strnicmp(governor->name,"powersave",CPUFREQ_NAME_LEN))
++              return -EBUSY;
++      if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN))
++              return -EBUSY;
++
++      down(&cpufreq_governor_sem);
++      
++      list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
++              if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
++                      up(&cpufreq_governor_sem);
++                      return -EBUSY;
++              }
++      }
++      list_add(&governor->governor_list, &cpufreq_governor_list);
++
++      up(&cpufreq_governor_sem);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_register_governor);
++
++
++void cpufreq_unregister_governor(struct cpufreq_governor *governor)
++{
++      /* backport info: 
++       * As the module usage count isn't assured in 2.4., check for removal
++       * of running cpufreq governor
++       */
++      unsigned int i;
++
++      if (!governor)
++              return;
++
++      down(&cpufreq_governor_sem);
++
++      for (i=0; i<NR_CPUS; i++) {
++              struct cpufreq_policy *policy = cpufreq_cpu_get(i);
++              if (!policy)
++                      goto done;
++              down(&policy->lock);
++
++              if (policy->policy != CPUFREQ_POLICY_GOVERNOR)
++                      goto unlock_done;
++              if (policy->governor != governor)
++                      goto unlock_done;
++
++              /* stop old one, start performance [always present] */
++              __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
++              policy->policy = CPUFREQ_POLICY_PERFORMANCE;
++              __cpufreq_governor(policy, CPUFREQ_GOV_START);
++
++      unlock_done:
++              up(&policy->lock);
++      done:
++              cpufreq_cpu_put(policy);
++      }
++      list_del(&governor->governor_list);
++      up(&cpufreq_governor_sem);
++      return;
++}
++EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
++
++
++
++/*********************************************************************
++ *                          POLICY INTERFACE                         *
++ *********************************************************************/
++
++/**
++ * cpufreq_get_policy - get the current cpufreq_policy
++ * @policy: struct cpufreq_policy into which the current cpufreq_policy is written
++ *
++ * Reads the current cpufreq policy.
++ */
++int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu)
++{
++      struct cpufreq_policy *cpu_policy;
++      if (!policy)
++              return -EINVAL;
++
++      cpu_policy = cpufreq_cpu_get(cpu);
++      if (!cpu_policy)
++              return -EINVAL;
++
++      down(&cpu_policy->lock);
++      memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy));
++      up(&cpu_policy->lock);
++
++      cpufreq_cpu_put(cpu_policy);
++
++      return 0;
++}
++EXPORT_SYMBOL(cpufreq_get_policy);
++
++
++/**
++ *    cpufreq_set_policy - set a new CPUFreq policy
++ *    @policy: policy to be set.
++ *
++ *    Sets a new CPU frequency and voltage scaling policy.
++ */
++int cpufreq_set_policy(struct cpufreq_policy *policy)
++{
++      int ret = 0;
++      struct cpufreq_policy *data;
++
++      if (!policy)
++              return -EINVAL;
++
++      data = cpufreq_cpu_get(policy->cpu);
++      if (!data)
++              return -EINVAL;
++
++      /* lock this CPU */
++      down(&data->lock);
++
++      memcpy(&policy->cpuinfo, 
++             &data->cpuinfo, 
++             sizeof(struct cpufreq_cpuinfo));
++
++      /* verify the cpu speed can be set within this limit */
++      ret = cpufreq_driver->verify(policy);
++      if (ret)
++              goto error_out;
++
++      down_read(&cpufreq_notifier_rwsem);
++
++      /* adjust if necessary - all reasons */
++      notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
++                          policy);
++
++      /* adjust if necessary - hardware incompatibility*/
++      notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE,
++                          policy);
++
++      /* verify the cpu speed can be set within this limit,
++         which might be different to the first one */
++      ret = cpufreq_driver->verify(policy);
++      if (ret) {
++              up_read(&cpufreq_notifier_rwsem);
++              goto error_out;
++      }
++
++      /* notification of the new policy */
++      notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
++                          policy);
++
++      up_read(&cpufreq_notifier_rwsem);
++
++      data->min    = policy->min;
++      data->max    = policy->max;
++
++      if (cpufreq_driver->setpolicy) {
++              data->policy = policy->policy;
++              ret = cpufreq_driver->setpolicy(policy);
++      } else {
++              if ((policy->policy != data->policy) || 
++                  ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != data->governor))) {
++                      /* save old, working values */
++                      unsigned int old_pol = data->policy;
++                      struct cpufreq_governor *old_gov = data->governor;
++
++                      /* end old governor */
++                      __cpufreq_governor(data, CPUFREQ_GOV_STOP);
++
++                      /* start new governor */
++                      data->policy = policy->policy;
++                      data->governor = policy->governor;
++                      if (__cpufreq_governor(data, CPUFREQ_GOV_START)) {
++                              /* new governor failed, so re-start old one */
++                              data->policy = old_pol;
++                              data->governor = old_gov;
++                              __cpufreq_governor(data, CPUFREQ_GOV_START);
++                      }
++                      /* might be a policy change, too, so fall through */
++              }
++              __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
++      }
++
++ error_out:
++      up(&data->lock);
++      cpufreq_cpu_put(data);
++
++      return ret;
++}
++EXPORT_SYMBOL(cpufreq_set_policy);
++
++
++
++/*********************************************************************
++ *            EXTERNALLY AFFECTING FREQUENCY CHANGES                 *
++ *********************************************************************/
++
++/**
++ * adjust_jiffies - adjust the system "loops_per_jiffy"
++ *
++ * This function alters the system "loops_per_jiffy" for the clock
++ * speed change. Note that loops_per_jiffy cannot be updated on SMP
++ * systems as each CPU might be scaled differently. So, use the arch 
++ * per-CPU loops_per_jiffy value wherever possible.
++ */
++#ifndef CONFIG_SMP
++static unsigned long l_p_j_ref = 0;
++static unsigned int  l_p_j_ref_freq = 0;
++
++static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
++{
++      if (!l_p_j_ref_freq) {
++              l_p_j_ref = loops_per_jiffy;
++              l_p_j_ref_freq = ci->old;
++      }
++      if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||
++          (val == CPUFREQ_POSTCHANGE && ci->old > ci->new))
++              loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);
++}
++#else
++#define adjust_jiffies(x...) do {} while (0)
++#endif
++
++
++/**
++ * cpufreq_notify_transition - call notifier chain and adjust_jiffies on frequency transition
++ *
++ * This function calls the transition notifiers and the "adjust_jiffies" function. It is called
++ * twice on all CPU frequency changes that have external effects. 
++ */
++void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
++{
++      down_read(&cpufreq_notifier_rwsem);
++      switch (state) {
++      case CPUFREQ_PRECHANGE:
++              notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs);
++              adjust_jiffies(CPUFREQ_PRECHANGE, freqs);
++              break;
++      case CPUFREQ_POSTCHANGE:
++              adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
++              notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs);
++              cpufreq_cpu_data[freqs->cpu]->cur = freqs->new;
++              break;
++      }
++      up_read(&cpufreq_notifier_rwsem);
++}
++EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
++
++
++
++/*********************************************************************
++ *               REGISTER / UNREGISTER CPUFREQ DRIVER                *
++ *********************************************************************/
++
++/**
++ * cpufreq_register_driver - register a CPU Frequency driver
++ * @driver_data: A struct cpufreq_driver containing the values#
++ * submitted by the CPU Frequency driver.
++ *
++ *   Registers a CPU Frequency driver to this core code. This code 
++ * returns zero on success, -EBUSY when another driver got here first
++ * (and isn't unregistered in the meantime). 
++ *
++ */
++int cpufreq_register_driver(struct cpufreq_driver *driver_data)
++{
++      unsigned long flags;
++      unsigned int i;
++
++      if (!driver_data || !driver_data->verify || !driver_data->init ||
++          ((!driver_data->setpolicy) && (!driver_data->target)))
++              return -EINVAL;
++
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      if (cpufreq_driver) {
++              spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++              return -EBUSY;
++      }
++      cpufreq_driver = driver_data;
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++      for (i=0; i<NR_CPUS; i++) {
++              if (cpu_online(i)) 
++                      cpufreq_add_dev(i);
++      }
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_register_driver);
++
++
++/**
++ * cpufreq_unregister_driver - unregister the current CPUFreq driver
++ *
++ *    Unregister the current CPUFreq driver. Only call this if you have 
++ * the right to do so, i.e. if you have succeeded in initialising before!
++ * Returns zero if successful, and -EINVAL if the cpufreq_driver is
++ * currently not initialised.
++ */
++int cpufreq_unregister_driver(struct cpufreq_driver *driver)
++{
++      unsigned long flags;
++      unsigned int i;
++
++      if (!cpufreq_driver || (driver != cpufreq_driver))
++              return -EINVAL;
++
++      for (i=0; i<NR_CPUS; i++) {
++              if (cpu_online(i)) 
++                      cpufreq_remove_dev(i);
++      }
++
++      spin_lock_irqsave(&cpufreq_driver_lock, flags);
++      cpufreq_driver = NULL;
++      spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/freq_table.c
+@@ -0,0 +1,203 @@
++/*
++ * linux/drivers/cpufreq/freq_table.c
++ *
++ * Copyright (C) 2002 - 2003 Dominik Brodowski
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++
++/*********************************************************************
++ *                     FREQUENCY TABLE HELPERS                       *
++ *********************************************************************/
++
++int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
++                                  struct cpufreq_frequency_table *table)
++{
++      unsigned int min_freq = ~0;
++      unsigned int max_freq = 0;
++      unsigned int i = 0;
++
++      for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
++              unsigned int freq = table[i].frequency;
++              if (freq == CPUFREQ_ENTRY_INVALID)
++                      continue;
++              if (freq < min_freq)
++                      min_freq = freq;
++              if (freq > max_freq)
++                      max_freq = freq;
++      }
++
++      policy->min = policy->cpuinfo.min_freq = min_freq;
++      policy->max = policy->cpuinfo.max_freq = max_freq;
++
++      if (policy->min == ~0)
++              return -EINVAL;
++      else
++              return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
++
++
++int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
++                                 struct cpufreq_frequency_table *table)
++{
++      unsigned int next_larger = ~0;
++      unsigned int i = 0;
++      unsigned int count = 0;
++
++      if (!cpu_online(policy->cpu))
++              return -EINVAL;
++
++      cpufreq_verify_within_limits(policy, 
++                                   policy->cpuinfo.min_freq, 
++                                   policy->cpuinfo.max_freq);
++
++      for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
++              unsigned int freq = table[i].frequency;
++              if (freq == CPUFREQ_ENTRY_INVALID)
++                      continue;
++              if ((freq >= policy->min) && (freq <= policy->max))
++                      count++;
++              else if ((next_larger > freq) && (freq > policy->max))
++                      next_larger = freq;
++      }
++
++      if (!count)
++              policy->max = next_larger;
++
++      cpufreq_verify_within_limits(policy, 
++                                   policy->cpuinfo.min_freq, 
++                                   policy->cpuinfo.max_freq);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
++
++
++int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
++                                 struct cpufreq_frequency_table *table,
++                                 unsigned int target_freq,
++                                 unsigned int relation,
++                                 unsigned int *index)
++{
++      struct cpufreq_frequency_table optimal = { .index = ~0, };
++      struct cpufreq_frequency_table suboptimal = { .index = ~0, };
++      unsigned int i;
++
++      switch (relation) {
++      case CPUFREQ_RELATION_H:
++              optimal.frequency = 0;
++              suboptimal.frequency = ~0;
++              break;
++      case CPUFREQ_RELATION_L:
++              optimal.frequency = ~0;
++              suboptimal.frequency = 0;
++              break;
++      }
++
++      if (!cpu_online(policy->cpu))
++              return -EINVAL;
++
++      for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
++              unsigned int freq = table[i].frequency;
++              if (freq == CPUFREQ_ENTRY_INVALID)
++                      continue;
++              if ((freq < policy->min) || (freq > policy->max))
++                      continue;
++              switch(relation) {
++              case CPUFREQ_RELATION_H:
++                      if (freq <= target_freq) {
++                              if (freq >= optimal.frequency) {
++                                      optimal.frequency = freq;
++                                      optimal.index = i;
++                              }
++                      } else {
++                              if (freq <= suboptimal.frequency) {
++                                      suboptimal.frequency = freq;
++                                      suboptimal.index = i;
++                              }
++                      }
++                      break;
++              case CPUFREQ_RELATION_L:
++                      if (freq >= target_freq) {
++                              if (freq <= optimal.frequency) {
++                                      optimal.frequency = freq;
++                                      optimal.index = i;
++                              }
++                      } else {
++                              if (freq >= suboptimal.frequency) {
++                                      suboptimal.frequency = freq;
++                                      suboptimal.index = i;
++                              }
++                      }
++                      break;
++              }
++      }
++      if (optimal.index > i) {
++              if (suboptimal.index > i)
++                      return -EINVAL;
++              *index = suboptimal.index;
++      } else
++              *index = optimal.index;
++      
++      return 0;
++}
++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
++
++static struct cpufreq_frequency_table *show_table[NR_CPUS];
++/**
++ * show_scaling_governor - show the current policy for the specified CPU
++ */
++static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf)
++{
++      unsigned int i = 0;
++      unsigned int cpu = policy->cpu;
++      ssize_t count = 0;
++      struct cpufreq_frequency_table *table;
++
++      if (!show_table[cpu])
++              return -ENODEV;
++
++      table = show_table[cpu];
++
++      for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
++              if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
++                      continue;
++              count += sprintf(&buf[count], "%d ", table[i].frequency);
++      }
++      count += sprintf(&buf[count], "\n");
++
++      return count;
++
++}
++
++struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
++      .attr = { .name = "scaling_available_frequencies", .mode = 0444 },
++      .show = show_available_freqs,
++};
++EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
++
++/*
++ * if you use these, you must assure that the frequency table is valid
++ * all the time between get_attr and put_attr!
++ */
++void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, 
++                                    unsigned int cpu)
++{
++      show_table[cpu] = table;
++}
++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
++
++void cpufreq_frequency_table_put_attr(unsigned int cpu)
++{
++      show_table[cpu] = NULL;
++}
++EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
++
++
++MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
++MODULE_DESCRIPTION ("CPUfreq frequency table helpers");
++MODULE_LICENSE ("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/proc_intf.c
+@@ -0,0 +1,244 @@
++/*
++ * linux/drivers/cpufreq/proc_intf.c
++ *
++ * Copyright (C) 2002 - 2003 Dominik Brodowski
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/cpufreq.h>
++#include <linux/ctype.h>
++#include <linux/proc_fs.h>
++#include <asm/uaccess.h>
++
++
++/**
++ * cpufreq_parse_policy - parse a policy string
++ * @input_string: the string to parse.
++ * @policy: the policy written inside input_string
++ *
++ * This function parses a "policy string" - something the user echo'es into
++ * /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy.
++ * If there are invalid/missing entries, they are replaced with current
++ * cpufreq policy.
++ */
++static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *policy)
++{
++      unsigned int            min = 0;
++      unsigned int            max = 0;
++      unsigned int            cpu = 0;
++      char                    str_governor[16];
++      struct cpufreq_policy   current_policy;
++      unsigned int            result = -EFAULT;
++
++      if (!policy)
++              return -EINVAL;
++
++      policy->min = 0;
++      policy->max = 0;
++      policy->policy = 0;
++      policy->cpu = CPUFREQ_ALL_CPUS;
++
++      if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max, str_governor) == 4) 
++      {
++              policy->min = min;
++              policy->max = max;
++              policy->cpu = cpu;
++              result = 0;
++              goto scan_policy;
++      }
++      if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max, str_governor) == 4)
++      {
++              if (!cpufreq_get_policy(&current_policy, cpu)) {
++                      policy->min = (min * current_policy.cpuinfo.max_freq) / 100;
++                      policy->max = (max * current_policy.cpuinfo.max_freq) / 100;
++                      policy->cpu = cpu;
++                      result = 0;
++                      goto scan_policy;
++              }
++      }
++
++      if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) == 3) 
++      {
++              policy->min = min;
++              policy->max = max;
++              result = 0;
++              goto scan_policy;
++      }
++
++      if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor) == 3)
++      {
++              if (!cpufreq_get_policy(&current_policy, cpu)) {
++                      policy->min = (min * current_policy.cpuinfo.max_freq) / 100;
++                      policy->max = (max * current_policy.cpuinfo.max_freq) / 100;
++                      result = 0;
++                      goto scan_policy;
++              }
++      }
++
++      return -EINVAL;
++
++scan_policy:
++      result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor);
++
++      return result;
++}
++
++/**
++ * cpufreq_proc_read - read /proc/cpufreq
++ *
++ * This function prints out the current cpufreq policy.
++ */
++static int cpufreq_proc_read (
++      char                    *page,
++      char                    **start,
++      off_t                   off,
++      int                     count,
++      int                     *eof,
++      void                    *data)
++{
++      char                    *p = page;
++      int                     len = 0;
++      struct cpufreq_policy   policy;
++      unsigned int            min_pctg = 0;
++      unsigned int            max_pctg = 0;
++      unsigned int            i = 0;
++
++      if (off != 0)
++              goto end;
++
++      p += sprintf(p, "          minimum CPU frequency  -  maximum CPU frequency  -  policy\n");
++      for (i=0;i<NR_CPUS;i++) {
++              if (!cpu_online(i))
++                      continue;
++
++              if (cpufreq_get_policy(&policy, i))
++                      continue;
++
++              if (!policy.cpuinfo.max_freq)
++                      continue;
++
++              min_pctg = (policy.min * 100) / policy.cpuinfo.max_freq;
++              max_pctg = (policy.max * 100) / policy.cpuinfo.max_freq;
++
++              p += sprintf(p, "CPU%3d    %9d kHz (%3d %%)  -  %9d kHz (%3d %%)  -  ",
++                           i , policy.min, min_pctg, policy.max, max_pctg);
++              switch (policy.policy) {
++              case CPUFREQ_POLICY_POWERSAVE:
++                      p += sprintf(p, "powersave\n");
++                      break;
++              case CPUFREQ_POLICY_PERFORMANCE:
++                      p += sprintf(p, "performance\n");
++                      break;
++              case CPUFREQ_POLICY_GOVERNOR:
++                      p += snprintf(p, CPUFREQ_NAME_LEN, "%s\n", policy.governor->name);
++                      break;
++              default:
++                      p += sprintf(p, "INVALID\n");
++                      break;
++              }
++      }
++end:
++      len = (p - page);
++      if (len <= off+count) 
++              *eof = 1;
++      *start = page + off;
++      len -= off;
++      if (len>count) 
++              len = count;
++      if (len<0) 
++              len = 0;
++
++      return len;
++}
++
++
++/**
++ * cpufreq_proc_write - handles writing into /proc/cpufreq
++ *
++ * This function calls the parsing script and then sets the policy
++ * accordingly.
++ */
++static int cpufreq_proc_write (
++        struct file           *file,
++        const char            *buffer,
++        unsigned long         count,
++        void                  *data)
++{
++      int                     result = 0;
++      char                    proc_string[42] = {'\0'};
++      struct cpufreq_policy   policy;
++      unsigned int            i = 0;
++
++
++      if ((count > sizeof(proc_string) - 1))
++              return -EINVAL;
++      
++      if (copy_from_user(proc_string, buffer, count))
++              return -EFAULT;
++      
++      proc_string[count] = '\0';
++
++      result = cpufreq_parse_policy(proc_string, &policy);
++      if (result)
++              return -EFAULT;
++
++      if (policy.cpu == CPUFREQ_ALL_CPUS)
++      {
++              for (i=0; i<NR_CPUS; i++) 
++              {
++                      policy.cpu = i;
++                      if (cpu_online(i))
++                              cpufreq_set_policy(&policy);
++              }
++      } 
++      else
++              cpufreq_set_policy(&policy);
++
++      return count;
++}
++
++
++/**
++ * cpufreq_proc_init - add "cpufreq" to the /proc root directory
++ *
++ * This function adds "cpufreq" to the /proc root directory.
++ */
++static int __init cpufreq_proc_init (void)
++{
++      struct proc_dir_entry *entry = NULL;
++
++        /* are these acceptable values? */
++      entry = create_proc_entry("cpufreq", S_IFREG|S_IRUGO|S_IWUSR, 
++                                &proc_root);
++
++      if (!entry) {
++              printk(KERN_ERR "unable to create /proc/cpufreq entry\n");
++              return -EIO;
++      } else {
++              entry->read_proc = cpufreq_proc_read;
++              entry->write_proc = cpufreq_proc_write;
++      }
++
++      return 0;
++}
++
++
++/**
++ * cpufreq_proc_exit - removes "cpufreq" from the /proc root directory.
++ *
++ * This function removes "cpufreq" from the /proc root directory.
++ */
++static void __exit cpufreq_proc_exit (void)
++{
++      remove_proc_entry("cpufreq", &proc_root);
++      return;
++}
++
++MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>");
++MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface");
++MODULE_LICENSE ("GPL");
++
++module_init(cpufreq_proc_init);
++module_exit(cpufreq_proc_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/cpufreq/userspace.c
+@@ -0,0 +1,591 @@
++/*
++ *  drivers/cpufreq/userspace.c
++ *
++ *  Copyright (C)  2001 Russell King
++ *            (C)  2002 - 2003 Dominik Brodowski <linux@brodo.de>
++ *
++ * $Id: userspace.c,v 1.4 2003/03/07 10:30:20 db 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/kernel.h>
++#include <linux/module.h>
++#include <linux/smp.h>
++#include <linux/init.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++#include <linux/ctype.h>
++#include <linux/cpufreq.h>
++#include <linux/sysctl.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/sysfs.h>
++
++#include <asm/uaccess.h>
++
++#define CTL_CPU_VARS_SPEED_MAX(cpunr) { \
++                .ctl_name     = CPU_NR_FREQ_MAX, \
++                .data         = &cpu_max_freq[cpunr], \
++                .procname     = "speed-max", \
++                .maxlen               = sizeof(cpu_max_freq[cpunr]),\
++                .mode         = 0444, \
++                .proc_handler = proc_dointvec, }
++
++#define CTL_CPU_VARS_SPEED_MIN(cpunr) { \
++                .ctl_name     = CPU_NR_FREQ_MIN, \
++                .data         = &cpu_min_freq[cpunr], \
++                .procname     = "speed-min", \
++                .maxlen               = sizeof(cpu_min_freq[cpunr]),\
++                .mode         = 0444, \
++                .proc_handler = proc_dointvec, }
++
++#define CTL_CPU_VARS_SPEED(cpunr) { \
++                .ctl_name     = CPU_NR_FREQ, \
++                .procname     = "speed", \
++                .mode         = 0644, \
++                .proc_handler = cpufreq_procctl, \
++                .strategy     = cpufreq_sysctl, \
++                .extra1               = (void*) (cpunr), }
++
++#define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\
++                CTL_CPU_VARS_SPEED_MAX(cpunr), \
++                CTL_CPU_VARS_SPEED_MIN(cpunr), \
++                CTL_CPU_VARS_SPEED(cpunr),  \
++                { .ctl_name = 0, }, }
++
++/* the ctl_table entry for each CPU */
++#define CPU_ENUM(s) { \
++                .ctl_name     = (CPU_NR + s), \
++                .procname     = #s, \
++                .mode         = 0555, \
++                .child                = ctl_cpu_vars_##s }
++
++/**
++ * A few values needed by the userspace governor
++ */
++static unsigned int   cpu_max_freq[NR_CPUS];
++static unsigned int   cpu_min_freq[NR_CPUS];
++static unsigned int   cpu_cur_freq[NR_CPUS];
++static unsigned int   cpu_is_managed[NR_CPUS];
++static struct cpufreq_policy current_policy[NR_CPUS];
++
++static DECLARE_MUTEX  (userspace_sem); 
++
++
++/* keep track of frequency transitions */
++static int 
++userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
++                       void *data)
++{
++        struct cpufreq_freqs *freq = data;
++
++      cpu_cur_freq[freq->cpu] = freq->new;
++
++        return 0;
++}
++
++static struct notifier_block userspace_cpufreq_notifier_block = {
++        .notifier_call  = userspace_cpufreq_notifier
++};
++
++
++/** 
++ * cpufreq_set - set the CPU frequency
++ * @freq: target frequency in kHz
++ * @cpu: CPU for which the frequency is to be set
++ *
++ * Sets the CPU frequency to freq.
++ */
++int cpufreq_set(unsigned int freq, unsigned int cpu)
++{
++      int ret = -EINVAL;
++
++      down(&userspace_sem);
++      if (!cpu_is_managed[cpu])
++              goto err;
++
++      if (freq < cpu_min_freq[cpu])
++              freq = cpu_min_freq[cpu];
++      if (freq > cpu_max_freq[cpu])
++              freq = cpu_max_freq[cpu];
++
++      ret = cpufreq_driver_target(&current_policy[cpu], freq, 
++            CPUFREQ_RELATION_L);
++
++ err:
++      up(&userspace_sem);
++      return ret;
++}
++EXPORT_SYMBOL_GPL(cpufreq_set);
++
++
++/** 
++ * cpufreq_setmax - set the CPU to the maximum frequency
++ * @cpu - affected cpu;
++ *
++ * Sets the CPU frequency to the maximum frequency supported by
++ * this CPU.
++ */
++int cpufreq_setmax(unsigned int cpu)
++{
++      if (!cpu_is_managed[cpu] || !cpu_online(cpu))
++              return -EINVAL;
++      return cpufreq_set(cpu_max_freq[cpu], cpu);
++}
++EXPORT_SYMBOL_GPL(cpufreq_setmax);
++
++
++/** 
++ * cpufreq_get - get the current CPU frequency (in kHz)
++ * @cpu: CPU number
++ *
++ * Get the CPU current (static) CPU frequency
++ */
++unsigned int cpufreq_get(unsigned int cpu)
++{
++      return cpu_cur_freq[cpu];
++}
++EXPORT_SYMBOL(cpufreq_get);
++
++
++#ifdef CONFIG_CPU_FREQ_24_API
++
++
++/*********************** cpufreq_sysctl interface ********************/
++static int
++cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
++              void *buffer, size_t *lenp)
++{
++      char buf[16], *p;
++      int cpu = (int) ctl->extra1;
++      int len, left = *lenp;
++
++      if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
++              *lenp = 0;
++              return 0;
++      }
++
++      if (write) {
++              unsigned int freq;
++
++              len = left;
++              if (left > sizeof(buf))
++                      left = sizeof(buf);
++              if (copy_from_user(buf, buffer, left))
++                      return -EFAULT;
++              buf[sizeof(buf) - 1] = '\0';
++
++              freq = simple_strtoul(buf, &p, 0);
++              cpufreq_set(freq, cpu);
++      } else {
++              len = sprintf(buf, "%d\n", cpufreq_get(cpu));
++              if (len > left)
++                      len = left;
++              if (copy_to_user(buffer, buf, len))
++                      return -EFAULT;
++      }
++
++      *lenp = len;
++      filp->f_pos += len;
++      return 0;
++}
++
++static int
++cpufreq_sysctl(ctl_table *table, int *name, int nlen,
++             void *oldval, size_t *oldlenp,
++             void *newval, size_t newlen, void **context)
++{
++      int cpu = (int) table->extra1;
++
++      if (!cpu_online(cpu))
++              return -EINVAL;
++
++      if (oldval && oldlenp) {
++              size_t oldlen;
++
++              if (get_user(oldlen, oldlenp))
++                      return -EFAULT;
++
++              if (oldlen != sizeof(unsigned int))
++                      return -EINVAL;
++
++              if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
++                  put_user(sizeof(unsigned int), oldlenp))
++                      return -EFAULT;
++      }
++      if (newval && newlen) {
++              unsigned int freq;
++
++              if (newlen != sizeof(unsigned int))
++                      return -EINVAL;
++
++              if (get_user(freq, (unsigned int *)newval))
++                      return -EFAULT;
++
++              cpufreq_set(freq, cpu);
++      }
++      return 1;
++}
++
++/* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */
++/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
++        CTL_TABLE_CPU_VARS(0);
++#if NR_CPUS > 1
++      CTL_TABLE_CPU_VARS(1);
++#endif
++#if NR_CPUS > 2
++      CTL_TABLE_CPU_VARS(2);
++#endif
++#if NR_CPUS > 3
++      CTL_TABLE_CPU_VARS(3);
++#endif
++#if NR_CPUS > 4
++      CTL_TABLE_CPU_VARS(4);
++#endif
++#if NR_CPUS > 5
++      CTL_TABLE_CPU_VARS(5);
++#endif
++#if NR_CPUS > 6
++      CTL_TABLE_CPU_VARS(6);
++#endif
++#if NR_CPUS > 7
++      CTL_TABLE_CPU_VARS(7);
++#endif
++#if NR_CPUS > 8
++      CTL_TABLE_CPU_VARS(8);
++#endif
++#if NR_CPUS > 9
++      CTL_TABLE_CPU_VARS(9);
++#endif
++#if NR_CPUS > 10
++      CTL_TABLE_CPU_VARS(10);
++#endif
++#if NR_CPUS > 11
++      CTL_TABLE_CPU_VARS(11);
++#endif
++#if NR_CPUS > 12
++      CTL_TABLE_CPU_VARS(12);
++#endif
++#if NR_CPUS > 13
++      CTL_TABLE_CPU_VARS(13);
++#endif
++#if NR_CPUS > 14
++      CTL_TABLE_CPU_VARS(14);
++#endif
++#if NR_CPUS > 15
++      CTL_TABLE_CPU_VARS(15);
++#endif
++#if NR_CPUS > 16
++      CTL_TABLE_CPU_VARS(16);
++#endif
++#if NR_CPUS > 17
++      CTL_TABLE_CPU_VARS(17);
++#endif
++#if NR_CPUS > 18
++      CTL_TABLE_CPU_VARS(18);
++#endif
++#if NR_CPUS > 19
++      CTL_TABLE_CPU_VARS(19);
++#endif
++#if NR_CPUS > 20
++      CTL_TABLE_CPU_VARS(20);
++#endif
++#if NR_CPUS > 21
++      CTL_TABLE_CPU_VARS(21);
++#endif
++#if NR_CPUS > 22
++      CTL_TABLE_CPU_VARS(22);
++#endif
++#if NR_CPUS > 23
++      CTL_TABLE_CPU_VARS(23);
++#endif
++#if NR_CPUS > 24
++      CTL_TABLE_CPU_VARS(24);
++#endif
++#if NR_CPUS > 25
++      CTL_TABLE_CPU_VARS(25);
++#endif
++#if NR_CPUS > 26
++      CTL_TABLE_CPU_VARS(26);
++#endif
++#if NR_CPUS > 27
++      CTL_TABLE_CPU_VARS(27);
++#endif
++#if NR_CPUS > 28
++      CTL_TABLE_CPU_VARS(28);
++#endif
++#if NR_CPUS > 29
++      CTL_TABLE_CPU_VARS(29);
++#endif
++#if NR_CPUS > 30
++      CTL_TABLE_CPU_VARS(30);
++#endif
++#if NR_CPUS > 31
++      CTL_TABLE_CPU_VARS(31);
++#endif
++#if NR_CPUS > 32
++#error please extend CPU enumeration
++#endif
++
++/* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
++static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
++      CPU_ENUM(0),
++#if NR_CPUS > 1
++      CPU_ENUM(1),
++#endif
++#if NR_CPUS > 2
++      CPU_ENUM(2),
++#endif
++#if NR_CPUS > 3
++      CPU_ENUM(3),
++#endif
++#if NR_CPUS > 4
++      CPU_ENUM(4),
++#endif
++#if NR_CPUS > 5
++      CPU_ENUM(5),
++#endif
++#if NR_CPUS > 6
++      CPU_ENUM(6),
++#endif
++#if NR_CPUS > 7
++      CPU_ENUM(7),
++#endif
++#if NR_CPUS > 8
++      CPU_ENUM(8),
++#endif
++#if NR_CPUS > 9
++      CPU_ENUM(9),
++#endif
++#if NR_CPUS > 10
++      CPU_ENUM(10),
++#endif
++#if NR_CPUS > 11
++      CPU_ENUM(11),
++#endif
++#if NR_CPUS > 12
++      CPU_ENUM(12),
++#endif
++#if NR_CPUS > 13
++      CPU_ENUM(13),
++#endif
++#if NR_CPUS > 14
++      CPU_ENUM(14),
++#endif
++#if NR_CPUS > 15
++      CPU_ENUM(15),
++#endif
++#if NR_CPUS > 16
++      CPU_ENUM(16),
++#endif
++#if NR_CPUS > 17
++      CPU_ENUM(17),
++#endif
++#if NR_CPUS > 18
++      CPU_ENUM(18),
++#endif
++#if NR_CPUS > 19
++      CPU_ENUM(19),
++#endif
++#if NR_CPUS > 20
++      CPU_ENUM(20),
++#endif
++#if NR_CPUS > 21
++      CPU_ENUM(21),
++#endif
++#if NR_CPUS > 22
++      CPU_ENUM(22),
++#endif
++#if NR_CPUS > 23
++      CPU_ENUM(23),
++#endif
++#if NR_CPUS > 24
++      CPU_ENUM(24),
++#endif
++#if NR_CPUS > 25
++      CPU_ENUM(25),
++#endif
++#if NR_CPUS > 26
++      CPU_ENUM(26),
++#endif
++#if NR_CPUS > 27
++      CPU_ENUM(27),
++#endif
++#if NR_CPUS > 28
++      CPU_ENUM(28),
++#endif
++#if NR_CPUS > 29
++      CPU_ENUM(29),
++#endif
++#if NR_CPUS > 30
++      CPU_ENUM(30),
++#endif
++#if NR_CPUS > 31
++      CPU_ENUM(31),
++#endif
++#if NR_CPUS > 32
++#error please extend CPU enumeration
++#endif
++      {
++              .ctl_name       = 0,
++      }
++};
++
++static ctl_table ctl_cpu[2] = {
++      {
++              .ctl_name       = CTL_CPU,
++              .procname       = "cpu",
++              .mode           = 0555,
++              .child          = ctl_cpu_table,
++      },
++      {
++              .ctl_name       = 0,
++      }
++};
++
++struct ctl_table_header *cpufreq_sysctl_table;
++
++static inline void cpufreq_sysctl_init(void)
++{
++      cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0);
++}
++
++static inline void cpufreq_sysctl_exit(void)
++{
++      unregister_sysctl_table(cpufreq_sysctl_table);
++}
++
++#else
++#define cpufreq_sysctl_init() do {} while(0)
++#define cpufreq_sysctl_exit() do {} while(0)
++#endif /* CONFIG_CPU_FREQ_24API */
++
++
++/************************** sysfs interface ************************/
++static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
++{
++      return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]);
++}
++
++static ssize_t 
++store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) 
++{
++      unsigned int freq = 0;
++      unsigned int ret;
++
++      ret = sscanf (buf, "%u", &freq);
++      if (ret != 1)
++              return -EINVAL;
++
++      cpufreq_set(freq, policy->cpu);
++
++      return count;
++}
++
++static struct freq_attr freq_attr_scaling_setspeed = {
++      .attr = { .name = "scaling_setspeed", .mode = 0644 },
++      .show = show_speed,
++      .store = store_speed,
++};
++
++static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
++                                 unsigned int event)
++{
++      unsigned int cpu = policy->cpu;
++      switch (event) {
++      case CPUFREQ_GOV_START:
++              if ((!cpu_online(cpu)) || (!try_module_get(THIS_MODULE)) ||
++                  !policy->cur)
++                      return -EINVAL;
++              down(&userspace_sem);
++              cpu_is_managed[cpu] = 1;                
++              cpu_min_freq[cpu] = policy->min;
++              cpu_max_freq[cpu] = policy->max;
++              cpu_cur_freq[cpu] = policy->cur;
++              sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
++              memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
++              up(&userspace_sem);
++              break;
++      case CPUFREQ_GOV_STOP:
++              down(&userspace_sem);
++              cpu_is_managed[cpu] = 0;
++              cpu_min_freq[cpu] = 0;
++              cpu_max_freq[cpu] = 0;
++              sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
++              up(&userspace_sem);
++              module_put(THIS_MODULE);
++              break;
++      case CPUFREQ_GOV_LIMITS:
++              down(&userspace_sem);
++              cpu_min_freq[cpu] = policy->min;
++              cpu_max_freq[cpu] = policy->max;
++              if (policy->max < cpu_cur_freq[cpu])
++                      cpufreq_driver_target(&current_policy[cpu], policy->max, 
++                            CPUFREQ_RELATION_H);
++              else if (policy->min > cpu_cur_freq[cpu])
++                      cpufreq_driver_target(&current_policy[cpu], policy->min, 
++                            CPUFREQ_RELATION_L);
++              memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
++              up(&userspace_sem);
++              break;
++      }
++      return 0;
++}
++
++/* on ARM SA1100 we need to rely on the values of cpufreq_get() - because 
++ * of this, cpu_cur_freq[] needs to be set early.
++ */
++#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100)
++extern unsigned int sa11x0_getspeed(void);
++
++static void cpufreq_sa11x0_compat(void)
++{
++      cpu_cur_freq[0] = sa11x0_getspeed();
++}
++#else
++#define cpufreq_sa11x0_compat() do {} while(0)
++#endif
++
++
++static struct cpufreq_governor cpufreq_gov_userspace = {
++      .name           = "userspace",
++      .governor       = cpufreq_governor_userspace,
++      .owner          = THIS_MODULE,
++};
++EXPORT_SYMBOL(cpufreq_gov_userspace);
++
++static int already_init = 0;
++
++int cpufreq_gov_userspace_init(void)
++{
++      if (!already_init) {
++              down(&userspace_sem);
++              cpufreq_sa11x0_compat();
++              cpufreq_sysctl_init();
++              cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
++              already_init = 1;
++              up(&userspace_sem);
++      }
++      return cpufreq_register_governor(&cpufreq_gov_userspace);
++}
++EXPORT_SYMBOL(cpufreq_gov_userspace_init);
++
++
++static void __exit cpufreq_gov_userspace_exit(void)
++{
++      cpufreq_unregister_governor(&cpufreq_gov_userspace);
++        cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
++      cpufreq_sysctl_exit();
++}
++
++
++MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
++MODULE_LICENSE ("GPL");
++
++module_init(cpufreq_gov_userspace_init);
++module_exit(cpufreq_gov_userspace_exit);
+--- linux-2.4.27/drivers/i2c/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/Config.in
+@@ -17,6 +17,15 @@
+          int  '    GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12
+          int  '    GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13
+       fi
++
++      dep_tristate '  Guide GPIO adapter' CONFIG_I2C_GUIDE $CONFIG_I2C_ALGOBIT
++      if [ "$CONFIG_ARCH_OMAHA" = "y" ]; then
++         dep_tristate '  Omaha I2C interface' CONFIG_I2C_OMAHA $CONFIG_I2C
++      fi
++      if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
++         dep_tristate '  Frodo I2C adapter' CONFIG_I2C_FRODO $CONFIG_I2C_ALGOBIT
++         dep_tristate '  SA1100 I2C GPIO adapter' CONFIG_I2C_BIT_SA1100_GPIO $CONFIG_I2C_ALGOBIT
++      fi
+    fi
+    dep_tristate 'NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C
+@@ -49,6 +58,10 @@
+       dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C
+    fi
++   if [ "$CONFIG_ARCH_AT91RM9200" = "y" ] ; then
++      dep_tristate 'Atmel AT91RM9200 I2C Two-Wire interface (TWI)' CONFIG_I2C_AT91 $CONFIG_I2C
++   fi
++
+    if [ "$CONFIG_SIBYTE_SB1xxx_SOC" = "y" ]; then
+       dep_tristate 'SiByte SMBus interface' CONFIG_I2C_ALGO_SIBYTE $CONFIG_I2C
+       dep_tristate '  MAX1617 Temperature Sensor' CONFIG_I2C_MAX1617 $CONFIG_I2C_ALGO_SIBYTE
+--- linux-2.4.27/drivers/i2c/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/Makefile
+@@ -8,12 +8,21 @@
+                  i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \
+                  i2c-proc.o
++# Init order: core, chardev, bit adapters, pcf adapters
++
+ obj-$(CONFIG_I2C)             += i2c-core.o
+ obj-$(CONFIG_I2C_CHARDEV)     += i2c-dev.o
++
++# Bit adapters
+ obj-$(CONFIG_I2C_ALGOBIT)     += i2c-algo-bit.o
+ obj-$(CONFIG_I2C_PHILIPSPAR)  += i2c-philips-par.o
+ obj-$(CONFIG_I2C_ELV)         += i2c-elv.o
+ obj-$(CONFIG_I2C_VELLEMAN)    += i2c-velleman.o
++obj-$(CONFIG_I2C_GUIDE)               += i2c-guide.o
++obj-$(CONFIG_I2C_FRODO)               += i2c-frodo.o
++obj-$(CONFIG_I2C_OMAHA)               += i2c-omaha.o
++
++# PCF adapters
+ obj-$(CONFIG_I2C_ALGOPCF)     += i2c-algo-pcf.o
+ obj-$(CONFIG_I2C_ELEKTOR)     += i2c-elektor.o
+ obj-$(CONFIG_ITE_I2C_ALGO)    += i2c-algo-ite.o
+@@ -30,4 +39,3 @@
+ # This is needed for automatic patch generation: sensors code ends here
+ include $(TOPDIR)/Rules.make
+-
+--- linux-2.4.27/drivers/i2c/i2c-algo-bit.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-algo-bit.c
+@@ -169,7 +169,14 @@
+  * 1 if the device acknowledged
+  * 0 if the device did not ack
+  * -ETIMEDOUT if an error occurred (while raising the scl line)
+- */
++
++ * tsong@iders.ca: an instruction to disable any timeconsuming interrupt
++   here except the heart beat of timer2 should be added before setsda(adap, sb).
++   because when interrupt occurs during
++   scl is set high, the interrupt service routine is served and may take long,
++   after the interrupt returns, sda could be sampled by the device(slave)
++   more than once, and this cause error problem.
++*/
+ static int i2c_outb(struct i2c_adapter *i2c_adap, char c)
+ {
+       int i;
+@@ -582,9 +589,7 @@
+               printk("\n");
+       }
+-#ifdef MODULE
+       MOD_INC_USE_COUNT;
+-#endif
+       i2c_add_adapter(adap);
+       return 0;
+@@ -600,15 +605,13 @@
+       DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name));
+-#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+-#endif
+       return 0;
+ }
+-int __init i2c_algo_bit_init (void)
++static int __init i2c_algo_bit_init (void)
+ {
+-      printk(KERN_INFO "i2c-algo-bit.o: i2c bit algorithm module\n");
++      printk(KERN_DEBUG "i2c-algo-bit.o: i2c bit algorithm module\n");
+       return 0;
+ }
+@@ -617,7 +620,6 @@
+ EXPORT_SYMBOL(i2c_bit_add_bus);
+ EXPORT_SYMBOL(i2c_bit_del_bus);
+-#ifdef MODULE
+ MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+ MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm");
+ MODULE_LICENSE("GPL");
+@@ -631,12 +633,4 @@
+ MODULE_PARM_DESC(i2c_debug,
+             "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol");
+-int init_module(void) 
+-{
+-      return i2c_algo_bit_init();
+-}
+-
+-void cleanup_module(void) 
+-{
+-}
+-#endif
++module_init(i2c_algo_bit_init);
+--- linux-2.4.27/drivers/i2c/i2c-algo-pcf.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-algo-pcf.c
+@@ -475,9 +475,7 @@
+               return i;
+       }
+-#ifdef MODULE
+       MOD_INC_USE_COUNT;
+-#endif
+       i2c_add_adapter(adap);
+@@ -515,13 +513,11 @@
+               return res;
+       DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name));
+-#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+-#endif
+       return 0;
+ }
+-int __init i2c_algo_pcf_init (void)
++static int __init i2c_algo_pcf_init (void)
+ {
+       printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n");
+       return 0;
+@@ -531,7 +527,6 @@
+ EXPORT_SYMBOL(i2c_pcf_add_bus);
+ EXPORT_SYMBOL(i2c_pcf_del_bus);
+-#ifdef MODULE
+ MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
+ MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm");
+ MODULE_LICENSE("GPL");
+@@ -543,13 +538,4 @@
+ MODULE_PARM_DESC(i2c_debug,
+         "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol");
+-
+-int init_module(void) 
+-{
+-      return i2c_algo_pcf_init();
+-}
+-
+-void cleanup_module(void) 
+-{
+-}
+-#endif
++module_init(i2c_algo_pcf_init);
+--- linux-2.4.27/drivers/i2c/i2c-core.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-core.c
+@@ -41,7 +41,7 @@
+ /* exclusive access to the bus */
+ #define I2C_LOCK(adap) down(&adap->lock)
+-#define I2C_UNLOCK(adap) up(&adap->lock) 
++#define I2C_UNLOCK(adap) up(&adap->lock)
+ #define ADAP_LOCK()   down(&adap_lock)
+ #define ADAP_UNLOCK() up(&adap_lock)
+@@ -67,7 +67,7 @@
+ static int driver_count;
+ /**** debug level */
+-static int i2c_debug=1;
++static int i2c_debug = 0;
+ /* ---------------------------------------------------
+  * /proc entry declarations
+@@ -77,14 +77,14 @@
+ #ifdef CONFIG_PROC_FS
+ static int i2cproc_init(void);
+-static int i2cproc_cleanup(void);
++static void i2cproc_cleanup(void);
+-static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, 
++static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
+                                 loff_t *ppos);
+ static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
+                            int *eof , void *private);
+-/* To implement the dynamic /proc/bus/i2c-? files, we need our own 
++/* To implement the dynamic /proc/bus/i2c-? files, we need our own
+    implementation of the read hook */
+ static struct file_operations i2cproc_operations = {
+       read:           i2cproc_bus_read,
+@@ -101,8 +101,8 @@
+ /* ---------------------------------------------------
+- * registering functions 
+- * --------------------------------------------------- 
++ * registering functions
++ * ---------------------------------------------------
+  */
+ /* -----
+@@ -119,7 +119,7 @@
+               if (NULL == adapters[i])
+                       break;
+       if (I2C_ADAP_MAX == i) {
+-              printk(KERN_WARNING 
++              printk(KERN_WARNING
+                      " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n",
+                       adap->name);
+               res = -ENOMEM;
+@@ -129,7 +129,7 @@
+       adapters[i] = adap;
+       adap_count++;
+       ADAP_UNLOCK();
+-      
++
+       /* init data types */
+       init_MUTEX(&adap->lock);
+@@ -157,18 +157,18 @@
+ #endif /* def CONFIG_PROC_FS */
+       /* inform drivers of new adapters */
+-      DRV_LOCK();     
++      DRV_LOCK();
+       for (j=0;j<I2C_DRIVER_MAX;j++)
+-              if (drivers[j]!=NULL && 
++              if (drivers[j]!=NULL &&
+                   (drivers[j]->flags&(I2C_DF_NOTIFY|I2C_DF_DUMMY)))
+                       /* We ignore the return code; if it fails, too bad */
+                       drivers[j]->attach_adapter(adap);
+       DRV_UNLOCK();
+-      
++
+       DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n",
+                  adap->name,i));
+-      return 0;       
++      return 0;
+ ERROR1:
+@@ -203,13 +203,13 @@
+        * this or hell will break loose...
+        */
+       DRV_LOCK();
+-      for (j = 0; j < I2C_DRIVER_MAX; j++) 
++      for (j = 0; j < I2C_DRIVER_MAX; j++)
+               if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY))
+                       if ((res = drivers[j]->attach_adapter(adap))) {
+                               printk(KERN_WARNING "i2c-core.o: can't detach adapter %s "
+                                      "while detaching driver %s: driver not "
+                                      "detached!",adap->name,drivers[j]->name);
+-                              goto ERROR1;    
++                              goto ERROR1;
+                       }
+       DRV_UNLOCK();
+@@ -241,8 +241,8 @@
+       adapters[i] = NULL;
+       adap_count--;
+-      
+-      ADAP_UNLOCK();  
++
++      ADAP_UNLOCK();
+       DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name));
+       return 0;
+@@ -269,7 +269,7 @@
+               if (NULL == drivers[i])
+                       break;
+       if (I2C_DRIVER_MAX == i) {
+-              printk(KERN_WARNING 
++              printk(KERN_WARNING
+                      " i2c-core.o: register_driver(%s) "
+                      "- enlarge I2C_DRIVER_MAX.\n",
+                       driver->name);
+@@ -279,11 +279,11 @@
+       drivers[i] = driver;
+       driver_count++;
+-      
++
+       DRV_UNLOCK();   /* driver was successfully added */
+-      
++
+       DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name));
+-      
++
+       ADAP_LOCK();
+       /* now look for instances of driver on our adapters
+@@ -314,11 +314,11 @@
+               return -ENODEV;
+       }
+       /* Have a look at each adapter, if clients of this driver are still
+-       * attached. If so, detach them to be able to kill the driver 
++       * attached. If so, detach them to be able to kill the driver
+        * afterwards.
+        */
+       DEB2(printk(KERN_DEBUG "i2c-core.o: unregister_driver - looking for clients.\n"));
+-      /* removing clients does not depend on the notify flag, else 
++      /* removing clients does not depend on the notify flag, else
+        * invalid operation might (will!) result, when using stale client
+        * pointers.
+        */
+@@ -333,7 +333,7 @@
+               /* DUMMY drivers do not register their clients, so we have to
+                * use a trick here: we call driver->attach_adapter to
+                * *detach* it! Of course, each dummy driver should know about
+-               * this or hell will break loose...  
++               * this or hell will break loose...
+                */
+                       if ((res = driver->attach_adapter(adap))) {
+                               printk(KERN_WARNING "i2c-core.o: while unregistering "
+@@ -345,9 +345,9 @@
+                               return res;
+                       }
+               } else {
+-                      for (j=0;j<I2C_CLIENT_MAX;j++) { 
++                      for (j=0;j<I2C_CLIENT_MAX;j++) {
+                               struct i2c_client *client = adap->clients[j];
+-                              if (client != NULL && 
++                              if (client != NULL &&
+                                   client->driver == driver) {
+                                       DEB2(printk(KERN_DEBUG "i2c-core.o: "
+                                                   "detaching client %s:\n",
+@@ -376,7 +376,7 @@
+       drivers[i] = NULL;
+       driver_count--;
+       DRV_UNLOCK();
+-      
++
+       DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name));
+       return 0;
+ }
+@@ -384,7 +384,7 @@
+ int i2c_check_addr (struct i2c_adapter *adapter, int addr)
+ {
+       int i;
+-      for (i = 0; i < I2C_CLIENT_MAX ; i++) 
++      for (i = 0; i < I2C_CLIENT_MAX ; i++)
+               if (adapter->clients[i] && (adapter->clients[i]->addr == addr))
+                       return -EBUSY;
+       return 0;
+@@ -402,7 +402,7 @@
+               if (NULL == adapter->clients[i])
+                       break;
+       if (I2C_CLIENT_MAX == i) {
+-              printk(KERN_WARNING 
++              printk(KERN_WARNING
+                      " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n",
+                       client->name);
+               return -ENOMEM;
+@@ -410,9 +410,9 @@
+       adapter->clients[i] = client;
+       adapter->client_count++;
+-      
+-      if (adapter->client_register) 
+-              if (adapter->client_register(client)) 
++
++      if (adapter->client_register)
++              if (adapter->client_register(client))
+                       printk(KERN_DEBUG "i2c-core.o: warning: client_register seems "
+                              "to have failed for client %02x at adapter %s\n",
+                              client->addr,adapter->name);
+@@ -421,7 +421,7 @@
+       if(client->flags & I2C_CLIENT_ALLOW_USE)
+               client->usage_count = 0;
+-      
++
+       return 0;
+ }
+@@ -440,12 +440,12 @@
+                       client->name);
+               return -ENODEV;
+       }
+-      
+-      if( (client->flags & I2C_CLIENT_ALLOW_USE) && 
++
++      if( (client->flags & I2C_CLIENT_ALLOW_USE) &&
+           (client->usage_count>0))
+               return -EBUSY;
+-      
+-      if (adapter->client_unregister != NULL) 
++
++      if (adapter->client_unregister != NULL)
+               if ((res = adapter->client_unregister(client))) {
+                       printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, "
+                              "client not detached",client->name);
+@@ -471,7 +471,7 @@
+ void i2c_dec_use_client(struct i2c_client *client)
+ {
+-      
++
+       if (client->driver->dec_use != NULL)
+               client->driver->dec_use(client);
+@@ -479,65 +479,65 @@
+               client->adapter->dec_use(client->adapter);
+ }
+-struct i2c_client *i2c_get_client(int driver_id, int adapter_id, 
++struct i2c_client *i2c_get_client(int driver_id, int adapter_id,
+                                       struct i2c_client *prev)
+ {
+       int i,j;
+-      
++
+       /* Will iterate through the list of clients in each adapter of adapters-list
+-         in search for a client that matches the search criteria. driver_id or 
+-         adapter_id are ignored if set to 0. If both are ignored this returns 
++         in search for a client that matches the search criteria. driver_id or
++         adapter_id are ignored if set to 0. If both are ignored this returns
+          first client found. */
+-      
+-      i = j = 0;  
+-      
+-      /* set starting point */ 
++
++      i = j = 0;
++
++      /* set starting point */
+       if(prev)
+       {
+               if(!(prev->adapter))
+                       return (struct i2c_client *) -EINVAL;
+-              
++
+               for(j=0; j < I2C_ADAP_MAX; j++)
+                       if(prev->adapter == adapters[j])
+                               break;
+-              
++
+               /* invalid starting point? */
+               if (I2C_ADAP_MAX == j) {
+                       printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n",
+                               prev->name);
+                       return (struct i2c_client *) -ENODEV;
+-              }       
+-              
++              }
++
+               for(i=0; i < I2C_CLIENT_MAX; i++)
+                       if(prev == adapters[j]->clients[i])
+                               break;
+-              
++
+               /* invalid starting point? */
+               if (I2C_CLIENT_MAX == i) {
+                       printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n",
+                               prev->name);
+                       return (struct i2c_client *) -ENODEV;
+-              }       
+-              
++              }
++
+               i++; /* start from one after prev */
+       }
+-      
++
+       for(; j < I2C_ADAP_MAX; j++)
+       {
+               if(!adapters[j])
+                       continue;
+-                      
++
+               if(adapter_id && (adapters[j]->id != adapter_id))
+                       continue;
+-              
++
+               for(; i < I2C_CLIENT_MAX; i++)
+               {
+                       if(!adapters[j]->clients[i])
+                               continue;
+-                              
++
+                       if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id))
+                               continue;
+-                      if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE)       
++                      if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE)
+                               return adapters[j]->clients[i];
+               }
+               i = 0;
+@@ -549,12 +549,12 @@
+ int i2c_use_client(struct i2c_client *client)
+ {
+       if(client->flags & I2C_CLIENT_ALLOW_USE) {
+-              if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) 
++              if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE)
+                       client->usage_count++;
+               else {
+-                      if(client->usage_count > 0) 
++                      if(client->usage_count > 0)
+                               return -EBUSY;
+-                       else 
++                       else
+                               client->usage_count++;
+               }
+       }
+@@ -575,9 +575,9 @@
+                       return -EPERM;
+               }
+       }
+-      
++
+       i2c_dec_use_client(client);
+-      
++
+       return 0;
+ }
+@@ -589,7 +589,7 @@
+ #ifdef CONFIG_PROC_FS
+ /* This function generates the output for /proc/bus/i2c */
+-int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, 
++int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof,
+                  void *private)
+ {
+       int i;
+@@ -615,7 +615,7 @@
+ }
+ /* This function generates the output for /proc/bus/i2c-? */
+-ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, 
++ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
+                          loff_t *ppos)
+ {
+       struct inode * inode = file->f_dentry->d_inode;
+@@ -626,7 +626,7 @@
+       int order[I2C_CLIENT_MAX];
+       if (count > 4000)
+-              return -EINVAL; 
++              return -EINVAL;
+       len_total = file->f_pos + count;
+       /* Too bad if this gets longer (unlikely) */
+       if (len_total > 4000)
+@@ -641,12 +641,12 @@
+                          sorted by address */
+                       order_nr=0;
+                       for (j = 0; j < I2C_CLIENT_MAX; j++) {
+-                              if ((client = adapters[i]->clients[j]) && 
++                              if ((client = adapters[i]->clients[j]) &&
+                                   (client->driver->id != I2C_DRIVERID_I2CDEV))  {
+-                                      for(k = order_nr; 
+-                                          (k > 0) && 
++                                      for(k = order_nr;
++                                          (k > 0) &&
+                                           adapters[i]->clients[order[k-1]]->
+-                                                   addr > client->addr; 
++                                                   addr > client->addr;
+                                           k--)
+                                               order[k] = order[k-1];
+                                       order[k] = j;
+@@ -665,7 +665,7 @@
+                       len = len - file->f_pos;
+                       if (len > count)
+                               len = count;
+-                      if (len < 0) 
++                      if (len < 0)
+                               len = 0;
+                       if (copy_to_user (buf,kbuf+file->f_pos, len)) {
+                               kfree(kbuf);
+@@ -689,7 +689,7 @@
+               printk("i2c-core.o: /proc/bus/ does not exist");
+               i2cproc_cleanup();
+               return -ENOENT;
+-      } 
++      }
+       proc_bus_i2c = create_proc_entry("i2c",0,proc_bus);
+       if (!proc_bus_i2c) {
+               printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c");
+@@ -702,14 +702,13 @@
+       return 0;
+ }
+-int i2cproc_cleanup(void)
++static void i2cproc_cleanup(void)
+ {
+       if (i2cproc_initialized >= 1) {
+               remove_proc_entry("i2c",proc_bus);
+               i2cproc_initialized -= 2;
+       }
+-      return 0;
+ }
+@@ -751,10 +750,10 @@
+               msg.flags = client->flags & I2C_M_TEN;
+               msg.len = count;
+               (const char *)msg.buf = buf;
+-      
++
+               DEB2(printk(KERN_DEBUG "i2c-core.o: master_send: writing %d bytes on %s.\n",
+                       count,client->adapter->name));
+-      
++
+               I2C_LOCK(adap);
+               ret = adap->algo->master_xfer(adap,&msg,1);
+               I2C_UNLOCK(adap);
+@@ -784,14 +783,14 @@
+               DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: reading %d bytes on %s.\n",
+                       count,client->adapter->name));
+-      
++
+               I2C_LOCK(adap);
+               ret = adap->algo->master_xfer(adap,&msg,1);
+               I2C_UNLOCK(adap);
+-      
++
+               DEB2(printk(KERN_DEBUG "i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n",
+                       ret, count, client->addr));
+-      
++
+               /* if everything went ok (i.e. 1 msg transmitted), return #bytes
+               * transmitted, else error code.
+               */
+@@ -852,7 +851,7 @@
+               found = 0;
+               for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) {
+-                      if (((adap_id == address_data->force[i]) || 
++                      if (((adap_id == address_data->force[i]) ||
+                            (address_data->force[i] == ANY_I2C_BUS)) &&
+                            (addr == address_data->force[i+1])) {
+                               DEB2(printk(KERN_DEBUG "i2c-core.o: found force parameter for adapter %d, addr %04x\n",
+@@ -862,7 +861,7 @@
+                               found = 1;
+                       }
+               }
+-              if (found) 
++              if (found)
+                       continue;
+               /* If this address is in one of the ignores, we can forget about
+@@ -870,7 +869,7 @@
+               for (i = 0;
+                    !found && (address_data->ignore[i] != I2C_CLIENT_END);
+                    i += 2) {
+-                      if (((adap_id == address_data->ignore[i]) || 
++                      if (((adap_id == address_data->ignore[i]) ||
+                           ((address_data->ignore[i] == ANY_I2C_BUS))) &&
+                           (addr == address_data->ignore[i+1])) {
+                               DEB2(printk(KERN_DEBUG "i2c-core.o: found ignore parameter for adapter %d, "
+@@ -890,11 +889,11 @@
+                               found = 1;
+                       }
+               }
+-              if (found) 
++              if (found)
+                       continue;
+-              /* Now, we will do a detection, but only if it is in the normal or 
+-                 probe entries */  
++              /* Now, we will do a detection, but only if it is in the normal or
++                 probe entries */
+               for (i = 0;
+                    !found && (address_data->normal_i2c[i] != I2C_CLIENT_END);
+                    i += 1) {
+@@ -939,7 +938,7 @@
+                                           "addr %04x\n", adap_id,addr));
+                       }
+               }
+-              if (!found) 
++              if (!found)
+                       continue;
+               /* OK, so we really should examine this address. First check
+@@ -1087,11 +1086,11 @@
+                             I2C_SMBUS_I2C_BLOCK_DATA,&data);
+ }
+-/* Simulate a SMBus command using the i2c protocol 
++/* Simulate a SMBus command using the i2c protocol
+    No checking of parameters is done!  */
+-static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, 
++static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
+                                    unsigned short flags,
+-                                   char read_write, u8 command, int size, 
++                                   char read_write, u8 command, int size,
+                                    union i2c_smbus_data * data)
+ {
+       /* So we need to generate a series of msgs. In the case of writing, we
+@@ -1101,7 +1100,7 @@
+       unsigned char msgbuf0[34];
+       unsigned char msgbuf1[34];
+       int num = read_write == I2C_SMBUS_READ?2:1;
+-      struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, 
++      struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
+                                 { addr, flags | I2C_M_RD, 0, msgbuf1 }
+                               };
+       int i;
+@@ -1179,7 +1178,7 @@
+                       case I2C_SMBUS_BYTE_DATA:
+                               data->byte = msgbuf1[0];
+                               break;
+-                      case I2C_SMBUS_WORD_DATA: 
++                      case I2C_SMBUS_WORD_DATA:
+                       case I2C_SMBUS_PROC_CALL:
+                               data->word = msgbuf1[0] | (msgbuf1[1] << 8);
+                               break;
+@@ -1189,7 +1188,7 @@
+ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
+-                   char read_write, u8 command, int size, 
++                   char read_write, u8 command, int size,
+                    union i2c_smbus_data * data)
+ {
+       s32 res;
+@@ -1207,7 +1206,7 @@
+ /* You should always define `functionality'; the 'else' is just for
+-   backward compatibility. */ 
++   backward compatibility. */
+ u32 i2c_get_functionality (struct i2c_adapter *adap)
+ {
+       if (adap->algo->functionality)
+@@ -1233,122 +1232,12 @@
+       init_MUTEX(&adap_lock);
+       init_MUTEX(&driver_lock);
+-      
+-      i2cproc_init();
+-      
+-      return 0;
+-}
+-
+-#ifndef MODULE
+-#ifdef CONFIG_I2C_CHARDEV
+-      extern int i2c_dev_init(void);
+-#endif
+-#ifdef CONFIG_I2C_ALGOBIT
+-      extern int i2c_algo_bit_init(void);
+-#endif
+-#ifdef CONFIG_I2C_PHILIPSPAR
+-      extern int i2c_bitlp_init(void);
+-#endif
+-#ifdef CONFIG_I2C_ELV
+-      extern int i2c_bitelv_init(void);
+-#endif
+-#ifdef CONFIG_I2C_VELLEMAN
+-      extern int i2c_bitvelle_init(void);
+-#endif
+-#ifdef CONFIG_I2C_BITVIA
+-      extern int i2c_bitvia_init(void);
+-#endif
+-#ifdef CONFIG_I2C_ALGOPCF
+-      extern int i2c_algo_pcf_init(void);     
+-#endif
+-#ifdef CONFIG_I2C_ELEKTOR
+-      extern int i2c_pcfisa_init(void);
+-#endif
+-
+-#ifdef CONFIG_I2C_ALGO8XX
+-      extern int i2c_algo_8xx_init(void);
+-#endif
+-#ifdef CONFIG_I2C_RPXLITE
+-      extern int i2c_rpx_init(void);
+-#endif
+-
+-#ifdef CONFIG_I2C_ALGO_SIBYTE
+-      extern int i2c_algo_sibyte_init(void);
+-      extern int i2c_sibyte_init(void);
+-#endif
+-#ifdef CONFIG_I2C_MAX1617
+-      extern int i2c_max1617_init(void);
+-#endif
+-
+-#ifdef CONFIG_I2C_PROC
+-      extern int sensors_init(void);
+-#endif
+-
+-/* This is needed for automatic patch generation: sensors code starts here */
+-/* This is needed for automatic patch generation: sensors code ends here   */
+-
+-int __init i2c_init_all(void)
+-{
+-      /* --------------------- global ----- */
+-      i2c_init();
+-
+-#ifdef CONFIG_I2C_CHARDEV
+-      i2c_dev_init();
+-#endif
+-      /* --------------------- bit -------- */
+-#ifdef CONFIG_I2C_ALGOBIT
+-      i2c_algo_bit_init();
+-#endif
+-#ifdef CONFIG_I2C_PHILIPSPAR
+-      i2c_bitlp_init();
+-#endif
+-#ifdef CONFIG_I2C_ELV
+-      i2c_bitelv_init();
+-#endif
+-#ifdef CONFIG_I2C_VELLEMAN
+-      i2c_bitvelle_init();
+-#endif
+-
+-      /* --------------------- pcf -------- */
+-#ifdef CONFIG_I2C_ALGOPCF
+-      i2c_algo_pcf_init();    
+-#endif
+-#ifdef CONFIG_I2C_ELEKTOR
+-      i2c_pcfisa_init();
+-#endif
+-
+-      /* --------------------- 8xx -------- */
+-#ifdef CONFIG_I2C_ALGO8XX
+-      i2c_algo_8xx_init();
+-#endif
+-#ifdef CONFIG_I2C_RPXLITE
+-      i2c_rpx_init();
+-#endif
+-
+-      /* --------------------- SiByte -------- */
+-#ifdef CONFIG_I2C_ALGO_SIBYTE
+-      i2c_algo_sibyte_init();
+-      i2c_sibyte_init();
+-#endif
+-#ifdef CONFIG_I2C_MAX1617
+-      i2c_max1617_init();
+-#endif
+-
+-      /* -------------- proc interface ---- */
+-#ifdef CONFIG_I2C_PROC
+-      sensors_init();
+-#endif
+-/* This is needed for automatic patch generation: sensors code starts here */
+-/* This is needed for automatic patch generation: sensors code ends here */
++      i2cproc_init();
+       return 0;
+ }
+-#endif
+-
+-
+-
+ EXPORT_SYMBOL(i2c_add_adapter);
+ EXPORT_SYMBOL(i2c_del_adapter);
+ EXPORT_SYMBOL(i2c_add_driver);
+@@ -1385,7 +1274,6 @@
+ EXPORT_SYMBOL(i2c_get_functionality);
+ EXPORT_SYMBOL(i2c_check_functionality);
+-#ifdef MODULE
+ MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+ MODULE_DESCRIPTION("I2C-Bus main module");
+ MODULE_LICENSE("GPL");
+@@ -1393,13 +1281,5 @@
+ MODULE_PARM(i2c_debug, "i");
+ MODULE_PARM_DESC(i2c_debug,"debug level");
+-int init_module(void) 
+-{
+-      return i2c_init();
+-}
+-
+-void cleanup_module(void) 
+-{
+-      i2cproc_cleanup();
+-}
+-#endif
++module_init(i2c_init);
++module_exit(i2cproc_cleanup);
+--- linux-2.4.27/drivers/i2c/i2c-dev.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-dev.c
+@@ -1,5 +1,5 @@
+ /*
+-    i2c-dev.c - i2c-bus driver, char device interface  
++    i2c-dev.c - i2c-bus driver, char device interface
+     Copyright (C) 1995-97 Simon G. Vogl
+     Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
+@@ -25,7 +25,7 @@
+ /* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
+-/* The devfs code is contributed by Philipp Matthias Hahn 
++/* The devfs code is contributed by Philipp Matthias Hahn
+    <pmhahn@titan.lahn.de> */
+ /* $Id: i2c-dev.c,v 1.40 2001/08/25 01:28:01 mds Exp $ */
+@@ -50,19 +50,14 @@
+ #include <linux/i2c.h>
+ #include <linux/i2c-dev.h>
+-#ifdef MODULE
+-extern int init_module(void);
+-extern int cleanup_module(void);
+-#endif /* def MODULE */
+-
+ /* struct file_operations changed too often in the 2.1 series for nice code */
+-static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, 
++static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
+                             loff_t *offset);
+-static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, 
++static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count,
+                              loff_t *offset);
+-static int i2cdev_ioctl (struct inode *inode, struct file *file, 
++static int i2cdev_ioctl (struct inode *inode, struct file *file,
+                          unsigned int cmd, unsigned long arg);
+ static int i2cdev_open (struct inode *inode, struct file *file);
+@@ -73,13 +68,8 @@
+ static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
+                            void *arg);
+-#ifdef MODULE
+-static
+-#else
+-extern
+-#endif
+-       int __init i2c_dev_init(void);
+-static int i2cdev_cleanup(void);
++static int __init i2c_dev_init(void);
++static void i2cdev_cleanup(void);
+ static struct file_operations i2cdev_fops = {
+       owner:          THIS_MODULE,
+@@ -185,7 +175,7 @@
+       return ret;
+ }
+-int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, 
++int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
+                   unsigned long arg)
+ {
+       struct i2c_client *client = (struct i2c_client *)file->private_data;
+@@ -198,14 +188,14 @@
+       unsigned long funcs;
+ #ifdef DEBUG
+-      printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", 
++      printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n",
+              MINOR(inode->i_rdev),cmd, arg);
+ #endif /* DEBUG */
+       switch ( cmd ) {
+       case I2C_SLAVE:
+       case I2C_SLAVE_FORCE:
+-              if ((arg > 0x3ff) || 
++              if ((arg > 0x3ff) ||
+                   (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
+                       return -EINVAL;
+               if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg))
+@@ -224,8 +214,8 @@
+                                    sizeof(unsigned long)))?-EFAULT:0;
+       case I2C_RDWR:
+-              if (copy_from_user(&rdwr_arg, 
+-                                 (struct i2c_rdwr_ioctl_data *)arg, 
++              if (copy_from_user(&rdwr_arg,
++                                 (struct i2c_rdwr_ioctl_data *)arg,
+                                  sizeof(rdwr_arg)))
+                       return -EFAULT;
+@@ -233,9 +223,9 @@
+                * be sent at once */
+               if (rdwr_arg.nmsgs > 42)
+                       return -EINVAL;
+-              
++
+               rdwr_pa = (struct i2c_msg *)
+-                      kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), 
++                      kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
+                       GFP_KERNEL);
+               if (rdwr_pa == NULL) return -ENOMEM;
+@@ -312,9 +302,9 @@
+                                  (struct i2c_smbus_ioctl_data *) arg,
+                                  sizeof(struct i2c_smbus_ioctl_data)))
+                       return -EFAULT;
+-              if ((data_arg.size != I2C_SMBUS_BYTE) && 
++              if ((data_arg.size != I2C_SMBUS_BYTE) &&
+                   (data_arg.size != I2C_SMBUS_QUICK) &&
+-                  (data_arg.size != I2C_SMBUS_BYTE_DATA) && 
++                  (data_arg.size != I2C_SMBUS_BYTE_DATA) &&
+                   (data_arg.size != I2C_SMBUS_WORD_DATA) &&
+                   (data_arg.size != I2C_SMBUS_PROC_CALL) &&
+                   (data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
+@@ -325,9 +315,9 @@
+ #endif
+                       return -EINVAL;
+               }
+-              /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, 
++              /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,
+                  so the check is valid if size==I2C_SMBUS_QUICK too. */
+-              if ((data_arg.read_write != I2C_SMBUS_READ) && 
++              if ((data_arg.read_write != I2C_SMBUS_READ) &&
+                   (data_arg.read_write != I2C_SMBUS_WRITE)) {
+ #ifdef DEBUG
+                       printk(KERN_DEBUG "i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n",
+@@ -339,7 +329,7 @@
+               /* Note that command values are always valid! */
+               if ((data_arg.size == I2C_SMBUS_QUICK) ||
+-                  ((data_arg.size == I2C_SMBUS_BYTE) && 
++                  ((data_arg.size == I2C_SMBUS_BYTE) &&
+                   (data_arg.read_write == I2C_SMBUS_WRITE)))
+                       /* These are special: we do not use data */
+                       return i2c_smbus_xfer(client->adapter, client->addr,
+@@ -358,13 +348,13 @@
+               if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||
+                   (data_arg.size == I2C_SMBUS_BYTE))
+                       datasize = sizeof(data_arg.data->byte);
+-              else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || 
++              else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||
+                        (data_arg.size == I2C_SMBUS_PROC_CALL))
+                       datasize = sizeof(data_arg.data->word);
+               else /* size == I2C_SMBUS_BLOCK_DATA */
+                       datasize = sizeof(data_arg.data->block);
+-              if ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
++              if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
+                   (data_arg.read_write == I2C_SMBUS_WRITE)) {
+                       if (copy_from_user(&temp, data_arg.data, datasize))
+                               return -EFAULT;
+@@ -372,7 +362,7 @@
+               res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,
+                     data_arg.read_write,
+                     data_arg.command,data_arg.size,&temp);
+-              if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || 
++              if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||
+                             (data_arg.read_write == I2C_SMBUS_READ))) {
+                       if (copy_to_user(data_arg.data, &temp, datasize))
+                               return -EFAULT;
+@@ -479,7 +469,7 @@
+       return -1;
+ }
+-int __init i2c_dev_init(void)
++static int __init i2c_dev_init(void)
+ {
+       int res;
+@@ -509,7 +499,7 @@
+       return 0;
+ }
+-int i2cdev_cleanup(void)
++static void i2cdev_cleanup(void)
+ {
+       int res;
+@@ -517,9 +507,9 @@
+               if ((res = i2c_del_driver(&i2cdev_driver))) {
+                       printk("i2c-dev.o: Driver deregistration failed, "
+                              "module not removed.\n");
+-                      return res;
++                      return;
+               }
+-      i2cdev_initialized --;
++              i2cdev_initialized --;
+       }
+       if (i2cdev_initialized >= 1) {
+@@ -531,30 +521,17 @@
+ #endif
+                       printk("i2c-dev.o: unable to release major %d for i2c bus\n",
+                              I2C_MAJOR);
+-                      return res;
++                      return;
+               }
+               i2cdev_initialized --;
+       }
+-      return 0;
+ }
+ EXPORT_NO_SYMBOLS;
+-#ifdef MODULE
+-
+ MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+ MODULE_DESCRIPTION("I2C /dev entries driver");
+ MODULE_LICENSE("GPL");
+-int init_module(void)
+-{
+-      return i2c_dev_init();
+-}
+-
+-int cleanup_module(void)
+-{
+-      return i2cdev_cleanup();
+-}
+-
+-#endif /* def MODULE */
+-
++module_init(i2c_dev_init);
++module_exit(i2cdev_cleanup);
+--- linux-2.4.27/drivers/i2c/i2c-elektor.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-elektor.c
+@@ -181,16 +181,12 @@
+ static void pcf_isa_inc_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_INC_USE_COUNT;
+-#endif
+ }
+ static void pcf_isa_dec_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+-#endif
+ }
+@@ -219,7 +215,7 @@
+       pcf_isa_unreg,
+ };
+-int __init i2c_pcfisa_init(void) 
++static int __init i2c_pcfisa_init(void) 
+ {
+ #ifdef __alpha__
+       /* check to see we have memory mapped PCF8584 connected to the 
+@@ -289,10 +285,14 @@
+       return 0;
+ }
++static void i2c_pcfisa_exit(void)
++{
++      i2c_pcf_del_bus(&pcf_isa_ops);
++      pcf_isa_exit();
++}
+ EXPORT_NO_SYMBOLS;
+-#ifdef MODULE
+ MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
+ MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
+ MODULE_LICENSE("GPL");
+@@ -304,15 +304,5 @@
+ MODULE_PARM(mmapped, "i");
+ MODULE_PARM(i2c_debug, "i");
+-int init_module(void) 
+-{
+-      return i2c_pcfisa_init();
+-}
+-
+-void cleanup_module(void) 
+-{
+-      i2c_pcf_del_bus(&pcf_isa_ops);
+-      pcf_isa_exit();
+-}
+-
+-#endif
++module_init(i2c_pcfisa_init);
++module_exit(i2c_pcfisa_exit);
+--- linux-2.4.27/drivers/i2c/i2c-elv.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-elv.c
+@@ -75,7 +75,7 @@
+               PortData |=2;
+       }
+       outb(PortData, DATA);
+-} 
++}
+ static int bit_elv_getscl(void *data)
+ {
+@@ -90,7 +90,7 @@
+ static int bit_elv_init(void)
+ {
+       if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) {
+-              return -ENODEV; 
++              return -ENODEV;
+       } else {
+                                               /* test for ELV adap.   */
+               if (inb(base+1) & 0x80) {       /* BUSY should be high  */
+@@ -131,16 +131,12 @@
+ static void bit_elv_inc_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_INC_USE_COUNT;
+-#endif
+ }
+ static void bit_elv_dec_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+-#endif
+ }
+ /* ------------------------------------------------------------------------
+@@ -164,10 +160,10 @@
+       bit_elv_inc_use,
+       bit_elv_dec_use,
+       bit_elv_reg,
+-      bit_elv_unreg,  
++      bit_elv_unreg,
+ };
+-int __init i2c_bitelv_init(void)
++static int __init i2c_bitelv_init(void)
+ {
+       printk(KERN_INFO "i2c-elv.o: i2c ELV parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+       if (base==0) {
+@@ -194,24 +190,19 @@
+ }
++static void __exit i2c_bitelv_exit(void)
++{
++      i2c_bit_del_bus(&bit_elv_ops);
++      bit_elv_exit();
++}
++
+ EXPORT_NO_SYMBOLS;
+-#ifdef MODULE
+ MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+ MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter");
+ MODULE_LICENSE("GPL");
+ MODULE_PARM(base, "i");
+-int init_module(void)
+-{
+-      return i2c_bitelv_init();
+-}
+-
+-void cleanup_module(void)
+-{
+-      i2c_bit_del_bus(&bit_elv_ops);
+-      bit_elv_exit();
+-}
+-
+-#endif
++module_init(i2c_bitelv_init);
++module_exit(i2c_bitelv_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-frodo.c
+@@ -0,0 +1,114 @@
++
++/*
++ * linux/drivers/i2c/i2c-frodo.c
++ *
++ * Author: Abraham van der Merwe <abraham@2d3d.co.za>
++ *
++ * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110
++ * Development board (Frodo).
++ *
++ * This source code 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/version.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++
++#include <asm/hardware.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++static void frodo_setsda (void *data,int state)
++{
++      if (state)
++              frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT);
++      else
++              frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SDA_OUT);
++}
++
++static void frodo_setscl (void *data,int state)
++{
++      if (state)
++              frodo_cpld_set (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT);
++      else
++              frodo_cpld_clear (FRODO_CPLD_I2C,FRODO_I2C_SCL_OUT);
++}
++
++static int frodo_getsda (void *data)
++{
++      return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SDA_IN) != 0);
++}
++
++static int frodo_getscl (void *data)
++{
++      return ((frodo_cpld_read (FRODO_CPLD_I2C) & FRODO_I2C_SCL_IN) != 0);
++}
++
++static struct i2c_algo_bit_data bit_frodo_data = {
++      setsda:         frodo_setsda,
++      setscl:         frodo_setscl,
++      getsda:         frodo_getsda,
++      getscl:         frodo_getscl,
++      udelay:         80,
++      mdelay:         80,
++      timeout:        100
++};
++
++static int frodo_client_register (struct i2c_client *client)
++{
++      return (0);
++}
++
++static int frodo_client_unregister (struct i2c_client *client)
++{
++      return (0);
++}
++
++static void frodo_inc_use (struct i2c_adapter *adapter)
++{
++      MOD_INC_USE_COUNT;
++}
++
++static void frodo_dec_use (struct i2c_adapter *adapter)
++{
++      MOD_DEC_USE_COUNT;
++}
++
++static struct i2c_adapter frodo_ops = {
++      name:                           "Frodo adapter driver",
++      id:                                     I2C_HW_B_FRODO,
++      algo:                           NULL,
++      algo_data:                      &bit_frodo_data,
++      inc_use:                        frodo_inc_use,
++      dec_use:                        frodo_dec_use,
++      client_register:        frodo_client_register,
++      client_unregister:      frodo_client_unregister
++};
++
++static int __init i2c_frodo_init (void)
++{
++      return (i2c_bit_add_bus (&frodo_ops));
++}
++
++EXPORT_NO_SYMBOLS;
++
++static void __exit i2c_frodo_exit (void)
++{
++      i2c_bit_del_bus (&frodo_ops);
++}
++
++MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
++MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo");
++MODULE_LICENSE ("GPL");
++EXPORT_NO_SYMBOLS;
++
++module_init (i2c_frodo_init);
++module_exit (i2c_frodo_exit);
++
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-guide.c
+@@ -0,0 +1,199 @@
++/************************************************************************************\
++Copyright     : Copyright (C) 1995-2000 Simon G. Vogl
++                Copyright 2002 IDERs Incorporated
++File Name     : i2c-guide.c
++Description   : this i2c driver uses the GPIO port B pin 0 and pin 1 on the cs89712.
++Notes         : To change the bit rate, change the structure i2c_algo_bit_data
++                : to 10 10 100
++Contact               : tsong@iders.ca
++License               : This source code 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/kernel.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/string.h>  /* for 2.0 kernels to get NULL   */
++#include <asm/errno.h>     /* for 2.0 kernels to get ENODEV */
++#include <asm/io.h>
++
++#include <asm/hardware/cs89712.h>   // io operation ep_writel()
++#include <asm/hardware/clps7111.h>   // io operation clps_writel()
++#include <asm/arch-clps711x/hardware.h>   // io operation clps_writel()
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++/* ----- global defines -----------------------------------------------       */
++
++#define DEB(x)                /* should be reasonable open, close &c.         */
++#define DEB2(x)       /* low level debugging - very slow              */
++#define DEBE(x)       x       /* error messages                               */
++                                      /*  Pin Port  Inverted  name    */
++#define I2C_SDA               0x08            /*  port B ctrl pin 3   (inv)   */
++#define I2C_SCL               0x04            /*  port B ctrl pin 2   (inv)   */
++
++#define I2C_SDAIN     0x08            /*  use the same pin with output        */
++#define I2C_SCLIN     0x04            /*  use the same pin with output        */
++
++#define I2C_DMASK     0xf7            /* inverse of I2C_SDA  */
++#define I2C_CMASK     0xfb            /* inverse of I2c_SCL  */
++
++#define PORTB_PIN0_SDA_OUTPUT 0x08               /* pin 3 direction  of port B output */
++#define PORTB_PIN0_SDA_INPUT  0xf7               /* pin 3 direction of port B input  */
++
++#define PORTB_PIN1_SCL_OUTPUT 0x04               /* pin 2 direction of port B output  */
++#define PORTB_PIN1_SCL_INPUT  0xfb               /* pin 2 direction of port B input  */
++
++int base = 0;
++#define DEFAULT_BASE PBDR
++
++/* ----- local functions --------------------------------------------------- */
++
++static void bit_guide_setscl(void* data, int state)
++{
++      if (state) {
++              // set port B pin2 input
++              clps_writeb((clps_readb(PBDDR)) & PORTB_PIN1_SCL_INPUT, PBDDR);
++      }
++      else {
++              // clear
++              clps_writeb((clps_readb(PBDR)) & I2C_CMASK,  PBDR);
++              // set port B pin2 output
++              clps_writeb((clps_readb(PBDDR)) | PORTB_PIN1_SCL_OUTPUT, PBDDR);
++      }
++}
++
++static void bit_guide_setsda(void* data, int state)
++{
++      if (state) {
++              clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR);
++              // float pin 0 (actually  drive high by pull up resistor)
++              // clps_writeb((clps_readb(PBDR)) | I2C_SDA, PBDR);   // set Jan4 ori: eff
++              // printk("set sda high, state=%i\n",state);
++      }
++      else {
++              // clear
++              clps_writeb((clps_readb(PBDR)) & I2C_DMASK,  PBDR);
++              // set port B pin 0 output
++              clps_writeb((clps_readb(PBDDR)) | PORTB_PIN0_SDA_OUTPUT, PBDDR);
++      }
++}
++
++static int bit_guide_getscl(void *data)
++{
++      return ( 0 != ( (clps_readb(PBDR)) & I2C_SCLIN  ) );
++}
++
++static int bit_guide_getsda(void *data)
++{
++      // set port B pin 0 input Jan4 ori eff
++      clps_writeb((clps_readb(PBDDR)) & PORTB_PIN0_SDA_INPUT, PBDDR);
++      return ( 0 != ( (clps_readb(PBDR) ) & I2C_SDAIN ) );
++}
++
++static int bit_guide_init(void)
++{
++      bit_guide_setsda((void*)base,1);
++      bit_guide_setscl((void*)base,1);
++      return 0;
++}
++
++static int bit_guide_reg(struct i2c_client *client)
++{
++      return 0;
++}
++
++static int bit_guide_unreg(struct i2c_client *client)
++{
++      return 0;
++}
++
++static void bit_guide_inc_use(struct i2c_adapter *adap)
++{
++#ifdef MODULE
++      MOD_INC_USE_COUNT;
++#endif
++}
++
++static void bit_guide_dec_use(struct i2c_adapter *adap)
++{
++#ifdef MODULE
++      MOD_DEC_USE_COUNT;
++#endif
++}
++
++/* ------------------------------------------------------------------------
++ * Encapsulate the above functions in the correct operations structure.
++ * This is only done when more than one hardware adapter is supported.
++ */
++
++/* last line (us, ms, timout)
++ * us dominates the bit rate: 10us  means: 100Kbit/sec(25 means 40kbps)
++ *                            10ms  not known
++ *                            100ms timeout
++ */
++static struct i2c_algo_bit_data bit_guide_data = {
++      NULL,
++      bit_guide_setsda,
++      bit_guide_setscl,
++      bit_guide_getsda,
++      bit_guide_getscl,
++      50, 10, 100,    /* orginal (non-guide) value 10, 10, 100  */
++};
++
++static struct i2c_adapter bit_guide_ops = {
++      "Guide Port B: PIN2-SCL/PIN3-SDA",
++      I2C_HW_B_GUIDE,
++      NULL,
++      &bit_guide_data,
++      bit_guide_inc_use,
++      bit_guide_dec_use,
++      bit_guide_reg,
++      bit_guide_unreg,
++};
++
++static int __init  i2c_bitguide_init(void)
++{
++      printk("i2c-guide.o: Guide i2c port B adapter module.\n");
++      clps_writeb((clps_readb(PBDDR)) & 0xfd, PBDDR);  //  set service reuest pb1 as input
++      if (base==0) {
++              /* probe some values */
++              base=DEFAULT_BASE;
++              bit_guide_data.data=(void*)DEFAULT_BASE;
++              if (bit_guide_init()==0) {
++                      if(i2c_bit_add_bus(&bit_guide_ops) < 0)
++                              return -ENODEV;
++              } else {
++                      return -ENODEV;
++              }
++      } else {
++              bit_guide_data.data=(void*)base;
++              if (bit_guide_init()==0) {
++                      if(i2c_bit_add_bus(&bit_guide_ops) < 0)
++                              return -ENODEV;
++              } else {
++                      return -ENODEV;
++              }
++      }
++      printk("i2c-guide.o: found device at %#x.\n",base);
++      return 0;
++}
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("T. C. Song  <tsong@iders.ca>");
++MODULE_DESCRIPTION("I2C-Bus adapter routines for Guide (cs89712) GPIO port B");
++MODULE_LICENSE("GPL");
++
++MODULE_PARM(base, "i");
++
++module_init(i2c_bitguide_init);
++/* for completeness, we should have a module_exit() function, but the
++   GUIDE requires this to always be loaded.  If it is unloaded, the
++   operation of the GUIDE is undefined.
++   Nobody has written the i2c_bitguide_exit() routine yet, so it is not included.
++module_exit(i2c_bitguide_exit);
++*/
+--- /dev/null
++++ linux-2.4.27/drivers/i2c/i2c-omaha.c
+@@ -0,0 +1,276 @@
++/* ------------------------------------------------------------------------- *
++    Copyright ARM Limited 2002. All rights reserved.
++ 
++    i2c driver for Omaha                                           
++ 
++    Notes:Based on i2c-elv.c
++ 
++    The S3C2400X01 has better support for I2C, but bit oriented operations 
++    are directly supported by the other I2C layers, so we use that method
++    of performing I2C operations.
++
++    Copyright (C) 1995-2000 Simon G. Vogl
++
++    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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
++/* ------------------------------------------------------------------------- */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <linux/ioport.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++#include <asm/io.h>
++#include <asm/hardware.h>
++
++/* ----- global defines ----------------------------------------------- */
++#define DEB(x) if (i2c_debug>=1) x;
++#define DEB2(x) if (i2c_debug>=2) x;
++#define DEB3(x) if (i2c_debug>=3) x
++#define DEBE(x)       x       // error messages
++#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/
++#define DEBPROTO(x) if (i2c_debug>=9) { x; }
++      /* debug the protocol by showing transferred bits */
++
++/* Register and bitdefs for Omaha */
++
++// Port G control registers
++static volatile unsigned int pgcon = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON);
++static volatile unsigned int pgdat = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGDAT);
++
++static volatile unsigned int opencr = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_OPENCR);
++
++static int base = IO_ADDRESS(PLAT_PERIPHERAL_BASE+OMAHA_PGCON);
++
++// Open drain control registers
++#define OPC_CMD               BIT2
++#define OPC_DAT               BIT3
++
++// data bits in GPIO Port G data register
++#define OMAHA_SDA     BIT5
++#define OMAHA_SCL     BIT6
++#define IIC_WP                BIT3    // Write Protect for EEPROM
++
++// input/out select bits in GPIO G control register
++#define IIC_BITS      (BIT12|BIT10|BIT6);
++
++
++/* ----- local functions ----------------------------------------------       */
++
++
++static void bit_omaha_setscl(void *data, int state)
++{
++      unsigned int tmp;
++      
++      if (state)
++      {
++              tmp = __raw_readl(pgdat);
++              tmp |= OMAHA_SCL;
++              __raw_writel(tmp,pgdat);
++      }
++      else
++      {
++              tmp = __raw_readl(pgdat);
++              tmp &= ~OMAHA_SCL;
++              __raw_writel(tmp,pgdat);
++      }
++}
++
++static void bit_omaha_setsda(void *data, int state)
++{
++      unsigned int tmp;
++      
++      // ensure that sda is an output at the moment
++      tmp = __raw_readl(pgcon);
++      tmp = tmp | BIT10;
++      __raw_writel(tmp,pgcon);
++              
++      if (state)
++      {
++              tmp = __raw_readl(pgdat);
++              tmp |= OMAHA_SDA;
++              __raw_writel(tmp,pgdat);
++      }
++      else
++      {
++              tmp = __raw_readl(pgdat);
++              tmp &= ~OMAHA_SDA;
++              __raw_writel(tmp,pgdat);
++      }
++} 
++
++static int bit_omaha_getscl(void *data)
++{
++      if (__raw_readl(pgdat) & OMAHA_SCL)
++              return 1;
++      else
++              return 0;
++}
++
++static int bit_omaha_getsda(void *data)
++{
++      unsigned int tmp;
++      
++      // ensure that sda is an output at the moment
++      tmp = __raw_readl(pgcon);
++      tmp = tmp & ~BIT10;
++      __raw_writel(tmp,pgcon);
++      
++      if (__raw_readl(pgdat) & OMAHA_SDA)
++              return 1;
++      else
++              return 0;
++}
++
++static int bit_omaha_init(void)
++{
++      // Have we got some mmapped space?
++      if (request_region(base, 0x100, "i2c (omaha bus adapter)") < 0 )
++      {
++              printk("i2c-omaha.o: requested I/O region (0x%08x) is in use.\n", base);
++              return -ENODEV;
++      }
++
++      return 0;
++}
++
++
++static int bit_omaha_reg(struct i2c_client *client)
++{
++      return 0;
++}
++
++
++static int bit_omaha_unreg(struct i2c_client *client)
++{
++      return 0;
++}
++
++static void bit_omaha_inc_use(struct i2c_adapter *adap)
++{
++      MOD_INC_USE_COUNT;
++}
++
++static void bit_omaha_dec_use(struct i2c_adapter *adap)
++{
++      MOD_DEC_USE_COUNT;
++}
++
++
++
++/* ------------------------------------------------------------------------
++ * Encapsulate the above functions in the correct operations structure.
++ * This is only done when more than one hardware adapter is supported.
++ */
++static struct i2c_algo_bit_data bit_omaha_data = {
++      NULL,
++      bit_omaha_setsda,
++      bit_omaha_setscl,
++      bit_omaha_getsda,
++      bit_omaha_getscl,
++      10, 10, 20,             /*      waits, timeout */
++};
++
++static struct i2c_adapter bit_omaha_ops = {
++      "BIT-Type Omaha I2C adapter",
++      I2C_HW_B_OMAHA,
++      NULL,
++      &bit_omaha_data,
++      bit_omaha_inc_use,
++      bit_omaha_dec_use,
++      bit_omaha_reg,
++      bit_omaha_unreg,
++};     
++
++static int __init i2c_omaha_init (void)
++{
++      unsigned int tmp;
++
++      printk("i2c-omaha.o: i2c omaha adapter module\n");
++      
++      if (bit_omaha_init() == 0) {
++              if(i2c_bit_add_bus(&bit_omaha_ops) < 0)
++              {
++                      printk("Could not add bus!\n");
++                      return -ENODEV;
++              }
++      } else {
++              printk("Could not pcf_omaha_init\n");
++              return -ENODEV;
++      }       
++      
++      // Program Port G bits to output function
++      tmp = __raw_readl(pgcon);
++      tmp |= IIC_BITS;                
++      __raw_writel(tmp,pgcon);
++      
++      // Ensure SDA and SCL are open-drain
++      tmp = __raw_readl(opencr);
++      tmp = tmp | OPC_CMD | OPC_DAT;
++      __raw_writel(tmp,opencr);
++
++      bit_omaha_setsda((void*)base,1);
++      bit_omaha_setscl((void*)base,1);
++
++      // Disable WP
++      tmp = __raw_readl(pgdat);
++      tmp = tmp & ~IIC_WP;
++      __raw_writel(tmp,pgdat);
++                                      
++      return 0;
++}
++
++static void bit_omaha_exit(void)
++{
++      release_region(base , 2);
++}
++
++static void i2c_omaha_exit(void)
++{
++
++      i2c_bit_del_bus(&bit_omaha_ops);
++
++      bit_omaha_exit();
++
++}
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("ARM Limited <support@arm.com>");
++MODULE_DESCRIPTION("I2C-Bus adapter routines for Omaha");
++MODULE_LICENSE("GPL");
++                      
++MODULE_PARM(base, "i");
++MODULE_PARM(irq, "i");
++MODULE_PARM(clock, "i");
++MODULE_PARM(own, "i");
++MODULE_PARM(mmapped, "i");
++MODULE_PARM(i2c_debug, "i");
++
++
++module_init(i2c_omaha_init);
++module_exit(i2c_omaha_exit);
++
++
+--- linux-2.4.27/drivers/i2c/i2c-philips-par.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-philips-par.c
+@@ -16,7 +16,7 @@
+     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.              */
+-/* ------------------------------------------------------------------------- */ 
++/* ------------------------------------------------------------------------- */
+ /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+    Frodo Looijaard <frodol@dds.nl> */
+@@ -72,7 +72,7 @@
+ static void bit_lp_setscl(void *data, int state)
+ {
+-      /*be cautious about state of the control register - 
++      /*be cautious about state of the control register -
+               touch only the one bit needed*/
+       if (state) {
+               parport_write_control((struct parport *) data,
+@@ -126,7 +126,7 @@
+ static int bit_lp_getsda2(void *data)
+ {
+-      return (parport_read_status((struct parport *) data) & 
++      return (parport_read_status((struct parport *) data) &
+                                    PARPORT_STATUS_BUSY) ? 0 : 1;
+ }
+@@ -154,7 +154,7 @@
+  * Encapsulate the above functions in the correct operations structure.
+  * This is only done when more than one hardware adapter is supported.
+  */
+- 
++
+ static struct i2c_algo_bit_data bit_lp_data = {
+       NULL,
+       bit_lp_setsda,
+@@ -162,7 +162,7 @@
+       bit_lp_getsda,
+       bit_lp_getscl,
+       80, 80, 100,            /*      waits, timeout */
+-}; 
++};
+ static struct i2c_algo_bit_data bit_lp_data2 = {
+       NULL,
+@@ -171,7 +171,7 @@
+       bit_lp_getsda2,
+       NULL,
+       80, 80, 100,            /*      waits, timeout */
+-}; 
++};
+ static struct i2c_adapter bit_lp_ops = {
+       "Philips Parallel port adapter",
+@@ -197,7 +197,7 @@
+       printk(KERN_DEBUG "i2c-philips-par.o: attaching to %s\n", port->name);
+       adapter->pdev = parport_register_device(port, "i2c-philips-par",
+-                                              NULL, NULL, NULL, 
++                                              NULL, NULL, NULL,
+                                               PARPORT_FLAG_EXCL,
+                                               NULL);
+       if (!adapter->pdev) {
+@@ -257,16 +257,16 @@
+       NULL
+ };
+-int __init i2c_bitlp_init(void)
++static int __init i2c_bitlp_init(void)
+ {
+       printk(KERN_INFO "i2c-philips-par.o: i2c Philips parallel port adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+       parport_register_driver(&i2c_driver);
+-      
++
+       return 0;
+ }
+-void __exit i2c_bitlp_exit(void)
++static void __exit i2c_bitlp_exit(void)
+ {
+       parport_unregister_driver(&i2c_driver);
+ }
+@@ -279,14 +279,5 @@
+ MODULE_PARM(type, "i");
+-#ifdef MODULE
+-int init_module(void)
+-{
+-      return i2c_bitlp_init();
+-}
+-
+-void cleanup_module(void)
+-{
+-      i2c_bitlp_exit();
+-}
+-#endif
++module_init(i2c_bitlp_init);
++module_exit(i2c_bitlp_exit);
+--- linux-2.4.27/drivers/i2c/i2c-velleman.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/i2c/i2c-velleman.c
+@@ -65,7 +65,7 @@
+       } else {
+               outb(inb(CTRL) | I2C_SCL, CTRL);
+       }
+-      
++
+ }
+ static void bit_velle_setsda(void *data, int state)
+@@ -75,8 +75,8 @@
+       } else {
+               outb(inb(CTRL) | I2C_SDA, CTRL);
+       }
+-      
+-} 
++
++}
+ static int bit_velle_getscl(void *data)
+ {
+@@ -95,7 +95,7 @@
+                    base));
+               return -ENODEV;
+       } else {
+-              request_region(base, (base == 0x3bc)? 3 : 8, 
++              request_region(base, (base == 0x3bc)? 3 : 8,
+                       "i2c (Vellemann adapter)");
+               bit_velle_setsda((void*)base,1);
+               bit_velle_setscl((void*)base,1);
+@@ -104,7 +104,7 @@
+ }
+ static void __exit bit_velle_exit(void)
+-{     
++{
+       release_region( base , (base == 0x3bc)? 3 : 8 );
+ }
+@@ -121,16 +121,12 @@
+ static void bit_velle_inc_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_INC_USE_COUNT;
+-#endif
+ }
+ static void bit_velle_dec_use(struct i2c_adapter *adap)
+ {
+-#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+-#endif
+ }
+ /* ------------------------------------------------------------------------
+@@ -158,7 +154,7 @@
+       bit_velle_unreg,
+ };
+-int __init  i2c_bitvelle_init(void)
++static int __init i2c_bitvelle_init(void)
+ {
+       printk(KERN_INFO "i2c-velleman.o: i2c Velleman K8000 adapter module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+       if (base==0) {
+@@ -184,24 +180,19 @@
+       return 0;
+ }
++static void __exit i2c_bitvelle_exit(void)
++{
++      i2c_bit_del_bus(&bit_velle_ops);
++      bit_velle_exit();
++}
++
+ EXPORT_NO_SYMBOLS;
+-#ifdef MODULE
+ MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
+ MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter");
+ MODULE_LICENSE("GPL");
+ MODULE_PARM(base, "i");
+-int init_module(void) 
+-{
+-      return i2c_bitvelle_init();
+-}
+-
+-void cleanup_module(void) 
+-{
+-      i2c_bit_del_bus(&bit_velle_ops);
+-      bit_velle_exit();
+-}
+-
+-#endif
++module_init(i2c_bitvelle_init);
++module_exit(i2c_bitvelle_exit);
+--- linux-2.4.27/drivers/ide/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/Config.in
+@@ -107,6 +107,9 @@
+        define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
+        dep_bool '    RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
+       fi
++      if [ "$CONFIG_ARCH_RISCSTATION" = "y" ]; then
++         dep_bool '    RiscStation IDE' CONFIG_BLK_DEV_IDE_RISCSTATION $CONFIG_ARCH_RISCSTATION
++      fi
+       if [ "$CONFIG_AMIGA" = "y" ]; then
+        dep_bool '  Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA
+        dep_mbool '    Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL
+--- linux-2.4.27/drivers/ide/arm/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/arm/Makefile
+@@ -6,6 +6,7 @@
+ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)      += icside.o
+ obj-$(CONFIG_BLK_DEV_IDE_RAPIDE)      += rapide.o
++obj-$(CONFIG_BLK_DEV_IDE_RISCSTATION) += rstation-ide.o
+ EXTRA_CFLAGS  := -I../
+--- linux-2.4.27/drivers/ide/arm/icside.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/arm/icside.c
+@@ -1,7 +1,7 @@
+ /*
+  * linux/drivers/ide/arm/icside.c
+  *
+- * Copyright (c) 1996,1997 Russell King.
++ * Copyright (c) 1996-2003 Russell King.
+  *
+  * Changelog:
+  *  08-Jun-1996       RMK     Created
+@@ -26,24 +26,6 @@
+ #include <asm/ecard.h>
+ #include <asm/io.h>
+-#include "ide-noise.h"
+-
+-/*
+- * FIXME: We want to drop the the MACRO CRAP!
+- *
+- * ec->iops->in{b/w/l}
+- * ec->iops->in{b/w/l}_p
+- * ec->iops->out{b/w/l}
+- * ec->iops->out{b/w/l}_p
+- *
+- * the new core supports clean MMIO calls and other goodies
+- */
+-
+-/*
+- * Maximum number of interfaces per card
+- */
+-#define MAX_IFS       2
+-
+ #define ICS_IDENT_OFFSET              0x8a0
+ #define ICS_ARCIN_V5_INTRSTAT         0x000
+@@ -86,17 +68,20 @@
+       ICS_ARCIN_V6_IDESTEPPING
+ };
+-static const card_ids icside_cids[] = {
+-      { MANU_ICS,  PROD_ICS_IDE  },
+-      { MANU_ICS2, PROD_ICS2_IDE },
+-      { 0xffff, 0xffff }
++struct icside_state {
++      unsigned int channel;
++      unsigned int enabled;
++      unsigned long irq_port;
++      unsigned long slot_port;
++      unsigned int type;
++      ide_hwif_t *hwif[2];
+ };
+-typedef enum {
+-      ics_if_unknown,
+-      ics_if_arcin_v5,
+-      ics_if_arcin_v6
+-} iftype_t;
++#define ICS_TYPE_A3IN 0
++#define ICS_TYPE_A3USER       1
++#define ICS_TYPE_V6   3
++#define ICS_TYPE_V5   15
++#define ICS_TYPE_NOTYPE       ((unsigned int)-1)
+ /* ---------------- Version 5 PCB Support Functions --------------------- */
+ /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+@@ -104,8 +89,10 @@
+  */
+ static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ {
+-      unsigned int memc_port = (unsigned int)ec->irq_data;
+-      outb(0, memc_port + ICS_ARCIN_V5_INTROFFSET);
++      struct icside_state *state = ec->irq_data;
++      unsigned int base = state->irq_port;
++
++      outb(0, base + ICS_ARCIN_V5_INTROFFSET);
+ }
+ /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+@@ -113,17 +100,15 @@
+  */
+ static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ {
+-      unsigned int memc_port = (unsigned int)ec->irq_data;
+-      inb(memc_port + ICS_ARCIN_V5_INTROFFSET);
++      struct icside_state *state = ec->irq_data;
++      unsigned int base = state->irq_port;
++
++      inb(base + ICS_ARCIN_V5_INTROFFSET);
+ }
+ static const expansioncard_ops_t icside_ops_arcin_v5 = {
+-      icside_irqenable_arcin_v5,
+-      icside_irqdisable_arcin_v5,
+-      NULL,
+-      NULL,
+-      NULL,
+-      NULL
++      .irqenable      = icside_irqenable_arcin_v5,
++      .irqdisable     = icside_irqdisable_arcin_v5,
+ };
+@@ -133,10 +118,21 @@
+  */
+ static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ {
+-      unsigned int ide_base_port = (unsigned int)ec->irq_data;
++      struct icside_state *state = ec->irq_data;
++      unsigned int base = state->irq_port;
+-      outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+-      outb(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
++      state->enabled = 1;
++
++      switch (state->channel) {
++      case 0:
++              outb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
++              inb(base + ICS_ARCIN_V6_INTROFFSET_2);
++              break;
++      case 1:
++              outb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
++              inb(base + ICS_ARCIN_V6_INTROFFSET_1);
++              break;
++      }
+ }
+ /* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+@@ -144,10 +140,12 @@
+  */
+ static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ {
+-      unsigned int ide_base_port = (unsigned int)ec->irq_data;
++      struct icside_state *state = ec->irq_data;
+-      inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+-      inb(ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
++      state->enabled = 0;
++
++      inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
++      inb (state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+ }
+ /* Prototype: icside_irqprobe(struct expansion_card *ec)
+@@ -155,70 +153,49 @@
+  */
+ static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+ {
+-      unsigned int ide_base_port = (unsigned int)ec->irq_data;
++      struct icside_state *state = ec->irq_data;
+-      return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+-             inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
++      return inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
++             inb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+ }
+ static const expansioncard_ops_t icside_ops_arcin_v6 = {
+-      icside_irqenable_arcin_v6,
+-      icside_irqdisable_arcin_v6,
+-      icside_irqpending_arcin_v6,
+-      NULL,
+-      NULL,
+-      NULL
++      .irqenable      = icside_irqenable_arcin_v6,
++      .irqdisable     = icside_irqdisable_arcin_v6,
++      .irqpending     = icside_irqpending_arcin_v6,
+ };
+-/* Prototype: icside_identifyif (struct expansion_card *ec)
+- * Purpose  : identify IDE interface type
+- * Notes    : checks the description string
++/*
++ * Handle routing of interrupts.  This is called before
++ * we write the command to the drive.
+  */
+-static iftype_t __init icside_identifyif (struct expansion_card *ec)
++static void icside_maskproc(ide_drive_t *drive, int mask)
+ {
+-      unsigned int addr;
+-      iftype_t iftype;
+-      int id = 0;
+-
+-      iftype = ics_if_unknown;
+-
+-      addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+-
+-      id = inb(addr) & 1;
+-      id |= (inb(addr + 1) & 1) << 1;
+-      id |= (inb(addr + 2) & 1) << 2;
+-      id |= (inb(addr + 3) & 1) << 3;
+-
+-      switch (id) {
+-      case 0: /* A3IN */
+-              printk("icside: A3IN unsupported\n");
+-              break;
+-
+-      case 1: /* A3USER */
+-              printk("icside: A3USER unsupported\n");
+-              break;
++      ide_hwif_t *hwif = HWIF(drive);
++      struct icside_state *state = hwif->hwif_data;
++      unsigned long flags;
+-      case 3: /* ARCIN V6 */
+-              printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+-              iftype = ics_if_arcin_v6;
+-              break;
++      local_irq_save(flags);
+-      case 15:/* ARCIN V5 (no id) */
+-              printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+-              iftype = ics_if_arcin_v5;
+-              break;
++      state->channel = hwif->channel;
+-      default:/* we don't know - complain very loudly */
+-              printk("icside: ***********************************\n");
+-              printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+-              printk("icside: ***********************************\n");
+-              printk("icside: please report this to linux@arm.linux.org.uk\n");
+-              printk("icside: defaulting to ARCIN V5\n");
+-              iftype = ics_if_arcin_v5;
+-              break;
++      if (state->enabled && !mask) {
++              switch (hwif->channel) {
++              case 0:
++                      outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
++                      inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
++                      break;
++              case 1:
++                      outb(0, state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
++                      inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
++                      break;
++              }
++      } else {
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+       }
+-      return iftype;
++      local_irq_restore(flags);
+ }
+ #ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+@@ -234,125 +211,138 @@
+ #define NR_ENTRIES 256
+ #define TABLE_SIZE (NR_ENTRIES * 8)
+-static int ide_build_sglist(ide_hwif_t *hwif, struct request *rq)
++static void ide_build_sglist(ide_drive_t *drive, struct request *rq)
+ {
+-      struct buffer_head *bh;
++      ide_hwif_t *hwif = HWIF(drive);
+       struct scatterlist *sg = hwif->sg_table;
++      struct buffer_head *bh;
+       int nents = 0;
+-      if (rq->cmd == READ)
+-              hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+-      else
+-              hwif->sg_dma_direction = PCI_DMA_TODEVICE;
+-      bh = rq->bh;
+-      do {
+-              unsigned char *virt_addr = bh->b_data;
+-              unsigned int size = bh->b_size;
++      BUG_ON(hwif->sg_dma_active);
+-              while ((bh = bh->b_reqnext) != NULL) {
+-                      if ((virt_addr + size) != (unsigned char *)bh->b_data)
+-                              break;
+-                      size += bh->b_size;
+-              }
+-              memset(&sg[nents], 0, sizeof(*sg));
+-              sg[nents].address = virt_addr;
+-              sg[nents].length = size;
+-              nents++;
+-      } while (bh != NULL);
++      if (rq->cmd == IDE_DRIVE_TASKFILE) {
++              ide_task_t *args = rq->special;
+-      return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction);
+-}
++              if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
++                      hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
++              else
++                      hwif->sg_dma_direction = PCI_DMA_TODEVICE;
+-static int
+-icside_build_dmatable(ide_drive_t *drive, int ddir)
+-{
+-      return HWIF(drive)->sg_nents = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq, ddir);
+-}
++              memset(sg, 0, sizeof(*sg));
++              sg->address = rq->buffer;
++              sg->length = rq->nr_sectors * SECTOR_SIZE;
++              nents = 1;
++      } else {
++              if (rq->cmd == READ)
++                      hwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
++              else
++                      hwif->sg_dma_direction = PCI_DMA_TODEVICE;
+-/* Teardown mappings after DMA has completed.  */
+-static void icside_destroy_dmatable(ide_drive_t *drive)
+-{
+-      struct scatterlist *sg = HWIF(drive)->sg_table;
+-      int nents = HWIF(drive)->sg_nents;
++              bh = rq->bh;
++              do {
++                      unsigned long lastend;
+-      pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction);
++                      memset(sg, 0, sizeof(*sg));
++                      sg->page = bh->b_page;
++                      lastend = bh_phys(bh);
++
++                      do {
++                              lastend += bh->b_size;
++                              sg->length += bh->b_size;
++
++                              bh = bh->b_reqnext;
++                              if (bh == NULL)
++                                      break;
++                      } while (lastend == bh_phys(bh));
++
++                      sg++;
++                      nents++;
++              } while (bh != NULL);
++      }
++
++      nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction);
++
++      hwif->sg_nents = nents;
+ }
+-static int
+-icside_config_if(ide_drive_t *drive, int xfer_mode)
++
++/*
++ * Configure the IOMD to give the appropriate timings for the transfer
++ * mode being requested.  We take the advice of the ATA standards, and
++ * calculate the cycle time based on the transfer mode, and the EIDE
++ * MW DMA specs that the drive provides in the IDENTIFY command.
++ *
++ * We have the following IOMD DMA modes to choose from:
++ *
++ *    Type    Active          Recovery        Cycle
++ *    A       250 (250)       312 (550)       562 (800)
++ *    B       187             250             437
++ *    C       125 (125)       125 (375)       250 (500)
++ *    D       62              125             187
++ *
++ * (figures in brackets are actual measured timings)
++ *
++ * However, we also need to take care of the read/write active and
++ * recovery timings:
++ *
++ *                    Read    Write
++ *    Mode    Active  -- Recovery --  Cycle   IOMD type
++ *    MW0     215     50      215     480     A
++ *    MW1     80      50      50      150     C
++ *    MW2     70      25      25      120     C
++ */
++static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode)
+ {
+-      int func = ide_dma_off;
++      int on = 0, cycle_time = 0, use_dma_info = 0;
++
++      /*
++       * Limit the transfer speed to MW_DMA_2.
++       */
++      if (xfer_mode > XFER_MW_DMA_2)
++              xfer_mode = XFER_MW_DMA_2;
+       switch (xfer_mode) {
+       case XFER_MW_DMA_2:
+-              /*
+-               * The cycle time is limited to 250ns by the r/w
+-               * pulse width (90ns), however we should still
+-               * have a maximum burst transfer rate of 8MB/s.
+-               */
+-              drive->drive_data = 250;
++              cycle_time = 250;
++              use_dma_info = 1;
+               break;
+       case XFER_MW_DMA_1:
+-              drive->drive_data = 250;
++              cycle_time = 250;
++              use_dma_info = 1;
+               break;
+       case XFER_MW_DMA_0:
+-              drive->drive_data = 480;
++              cycle_time = 480;
+               break;
+-      default:
+-              drive->drive_data = 0;
++      case XFER_SW_DMA_2:
++      case XFER_SW_DMA_1:
++      case XFER_SW_DMA_0:
++              cycle_time = 480;
+               break;
+       }
+-      if (!drive->init_speed)
+-              drive->init_speed = (u8) xfer_mode;
++      /*
++       * If we're going to be doing MW_DMA_1 or MW_DMA_2, we should
++       * take care to note the values in the ID...
++       */
++      if (use_dma_info && drive->id->eide_dma_time > cycle_time)
++              cycle_time = drive->id->eide_dma_time;
+-      if (drive->drive_data &&
+-          ide_config_drive_speed(drive, (u8) xfer_mode) == 0)
+-              func = ide_dma_on;
++      drive->drive_data = cycle_time;
++
++      if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0)
++              on = 1;
+       else
+               drive->drive_data = 480;
+       printk("%s: %s selected (peak %dMB/s)\n", drive->name,
+               ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+-      drive->current_speed = (u8) xfer_mode;
+-
+-      return func;
+-}
+-
+-static int
+-icside_set_speed(ide_drive_t *drive, u8 speed)
+-{
+-      return icside_config_if(drive, speed);
+-}
+-
+-/*
+- * dma_intr() is the handler for disk read/write DMA interrupts
+- */
+-static ide_startstop_t icside_dmaintr(ide_drive_t *drive)
+-{
+-      u8 dma_stat     = HWIF(drive)->ide_dma_end(drive);
+-      /* get drive status */
+-      u8 stat         = HWIF(drive)->INB(IDE_STATUS_REG);
+-      int i;
++      drive->current_speed = xfer_mode;
+-      if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+-              if (!dma_stat) {
+-                      struct request *rq = HWGROUP(drive)->rq;
+-                      rq = HWGROUP(drive)->rq;
+-                      for (i = rq->nr_sectors; i > 0;) {
+-                              i -= rq->current_nr_sectors;
+-                              DRIVER(drive)->end_request(drive, 1);
+-                      }
+-                      return ide_stopped;
+-              }
+-              printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", 
+-                     drive->name, dma_stat);
+-      }
+-      return DRIVER(drive)->error(drive, "dma_intr", stat);
++      return on;
+ }
+ /*
+@@ -361,19 +351,19 @@
+  * This should be defined in one place only.
+  */
+ struct drive_list_entry {
+-      char * id_model;
+-      char * id_firmware;
++      const char * id_model;
++      const char * id_firmware;
+ };
+-static struct drive_list_entry drive_whitelist [] = {
++static const struct drive_list_entry drive_whitelist [] = {
+       { "Micropolis 2112A",                   "ALL"           },
+       { "CONNER CTMA 4000",                   "ALL"           },
+       { "CONNER CTT8000-A",                   "ALL"           },
+       { "ST34342A",                           "ALL"           },
+-      { NULL,                                 0               }
++      { NULL,                                 NULL            }
+ };
+-static struct drive_list_entry drive_blacklist [] = {
++static const struct drive_list_entry drive_blacklist [] = {
+       { "WDC AC11000H",                       "ALL"           },
+       { "WDC AC22100H",                       "ALL"           },
+       { "WDC AC32500H",                       "ALL"           },
+@@ -407,10 +397,11 @@
+       { "PLEXTOR CD-R PX-W8432T",             "ALL"           },
+       { "ATAPI CD-ROM DRIVE 40X MAXIMUM",     "ALL"           },
+       { "_NEC DV5800A",                       "ALL"           },
+-      { NULL,                                 0               }
++      { NULL,                                 NULL            }
+ };
+-static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
++static int
++in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table)
+ {
+       for ( ; drive_table->id_model ; drive_table++)
+               if ((!strcmp(drive_table->id_model, id->model)) &&
+@@ -420,41 +411,52 @@
+       return 0;
+ }
+-/*
+- *  For both Blacklisted and Whitelisted drives.
+- *  This is setup to be called as an extern for future support
+- *  to other special driver code.
+- */
+-int check_drive_good_lists (ide_drive_t *drive)
++static int icside_dma_host_off(ide_drive_t *drive)
+ {
+-      struct hd_driveid *id = drive->id;
+-      return in_drive_list(id, drive_whitelist);
++      return 0;
+ }
+-int check_drive_bad_lists (ide_drive_t *drive)
++static int icside_dma_off_quietly(ide_drive_t *drive)
+ {
+-      struct hd_driveid *id = drive->id;
+-      int blacklist = in_drive_list(id, drive_blacklist);
+-      if (blacklist)
+-              printk("%s: Disabling DMA for %s\n", drive->name, id->model);
+-      return(blacklist);
++      drive->using_dma = 0;
++      return icside_dma_host_off(drive);
+ }
+-int icside_dma_check(ide_drive_t *drive)
++static int icside_dma_off(ide_drive_t *drive)
++{
++      printk("%s: DMA disabled\n", drive->name);
++      return icside_dma_off_quietly(drive);
++}
++
++static int icside_dma_host_on(ide_drive_t *drive)
++{
++      return 0;
++}
++
++static int icside_dma_on(ide_drive_t *drive)
++{
++      drive->using_dma = 1;
++      return icside_dma_host_on(drive);
++}
++
++static int icside_dma_check(ide_drive_t *drive)
+ {
+       struct hd_driveid *id = drive->id;
+       ide_hwif_t *hwif = HWIF(drive);
+-      int autodma = hwif->autodma;
+       int xfer_mode = XFER_PIO_2;
++      int on;
+-      if (!id || !(id->capability & 1) || !autodma)
+-              return hwif->ide_dma_off_quietly(drive);
++      if (!id || !(id->capability & 1) || !hwif->autodma)
++              goto out;
+       /*
+        * Consult the list of known "bad" drives
+        */
+-      if (check_drive_bad_lists(drive))
+-              return hwif->ide_dma_off(drive);
++      if (in_drive_list(id, drive_blacklist)) {
++              printk("%s: Disabling DMA for %s (blacklisted)\n",
++                      drive->name, id->model);
++              goto out;
++      }
+       /*
+        * Enable DMA on any drive that has multiword DMA
+@@ -473,192 +475,241 @@
+       /*
+        * Consult the list of known "good" drives
+        */
+-      if (check_drive_good_lists(drive)) {
++      if (in_drive_list(id, drive_whitelist)) {
+               if (id->eide_dma_time > 150)
+                       goto out;
+               xfer_mode = XFER_MW_DMA_1;
+       }
+ out:
+-      if (icside_config_if(drive, xfer_mode))
+-              return hwif->ide_dma_on(drive);
+-      return hwif->ide_dma_off(drive);
+-}
++      on = icside_set_speed(drive, xfer_mode);
+-int icside_dma_verbose(ide_drive_t *drive)
+-{
+-      printk(", DMA");
+-      return 1;
++      if (on)
++              return icside_dma_on(drive);
++      else
++              return icside_dma_off(drive);
+ }
+-int icside_dma_test_irq(ide_drive_t *drive)
++static int icside_dma_end(ide_drive_t *drive)
+ {
+       ide_hwif_t *hwif = HWIF(drive);
+-      return inb((unsigned long)hwif->hw.priv) & 1;
+-}
+-int icside_dma_host_off(ide_drive_t *drive)
+-{
+-      return 0;
+-}
++      drive->waiting_for_dma = 0;
+-int icside_dma_off_quietly(ide_drive_t *drive)
+-{
+-      drive->using_dma = 0;
+-      return icside_dma_host_off(drive);
+-}
++      disable_dma(hwif->hw.dma);
+-int icside_dma_off(ide_drive_t *drive)
+-{
+-      printk("%s: DMA disabled\n", drive->name);
+-      return icside_dma_off_quietly(drive);
+-}
++      /* Teardown mappings after DMA has completed. */
++      pci_unmap_sg(NULL, hwif->sg_table, hwif->sg_nents,
++                   hwif->sg_dma_direction);
+-int icside_dma_host_on(ide_drive_t *drive)
+-{
+-      return 0;
+-}
++      hwif->sg_dma_active = 0;
+-int icside_dma_on(ide_drive_t *drive)
+-{
+-      drive->using_dma = 1;
+-      return icside_dma_host_on(drive);
++      return get_dma_residue(hwif->hw.dma) != 0;
+ }
+-int icside_dma_begin(ide_drive_t *drive)
++static int icside_dma_begin(ide_drive_t *drive)
+ {
+       ide_hwif_t *hwif = HWIF(drive);
++      /* We can not enable DMA on both channels simultaneously. */
++      BUG_ON(dma_channel_active(hwif->hw.dma));
+       enable_dma(hwif->hw.dma);
+       return 0;
+ }
+-int icside_dma_end(ide_drive_t *drive)
++static int icside_dma_count(ide_drive_t *drive)
+ {
+-      ide_hwif_t *hwif = HWIF(drive);
+- 
+-      drive->waiting_for_dma = 0;
+-      disable_dma(hwif->hw.dma);
+-      icside_destroy_dmatable(drive);
+-      return get_dma_residue(hwif->hw.dma) != 0;
++      return icside_dma_begin(drive);
+ }
+-int icside_dma_count (ide_drive_t *drive)
++/*
++ * dma_intr() is the handler for disk read/write DMA interrupts
++ */
++static ide_startstop_t icside_dmaintr(ide_drive_t *drive)
+ {
+-        return icside_dma_begin(drive);
++      unsigned int stat;
++      int dma_stat;
++
++      dma_stat = icside_dma_end(drive);
++      stat = HWIF(drive)->INB(IDE_STATUS_REG);
++      if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) {
++              if (!dma_stat) {
++                      struct request *rq = HWGROUP(drive)->rq;
++                      int i;
++
++                      for (i = rq->nr_sectors; i > 0; ) {
++                              i -= rq->current_nr_sectors;
++                              DRIVER(drive)->end_request(drive, 1);
++                      }
++
++                      return ide_stopped;
++              }
++              printk(KERN_ERR "%s: bad DMA status (dma_stat=%x)\n",
++                     drive->name, dma_stat);
++      }
++
++      return DRIVER(drive)->error(drive, __FUNCTION__, stat);
+ }
+-int icside_dma_read(ide_drive_t *drive)
++static int
++icside_dma_common(ide_drive_t *drive, struct request *rq,
++                unsigned int dma_mode)
+ {
+-      ide_hwif_t *hwif        = HWIF(drive);
+-//    ide_task_t *args        = HWGROUP(drive)->rq->special;
+-      int count               = 0;
+-      u8 lba48                = (drive->addressing == 1) ? 1 : 0;
+-      task_ioreg_t command    = WIN_NOP;
++      ide_hwif_t *hwif = HWIF(drive);
+-      count = icside_build_dmatable(drive, PCI_DMA_FROMDEVICE);
+-      if (!count)
+-              return 1;
+-      disable_dma(hwif->hw.dma);
++      /*
++       * We can not enable DMA on both channels.
++       */
++      BUG_ON(hwif->sg_dma_active);
++      BUG_ON(dma_channel_active(hwif->hw.dma));
+-      /* Route the DMA signals to
+-       * to the correct interface.
++      ide_build_sglist(drive, rq);
++
++      /*
++       * Ensure that we have the right interrupt routed.
+        */
+-      HWIF(drive)->OUTB(hwif->select_data, hwif->config_data);
++      icside_maskproc(drive, 0);
+-      /* Select the correct timing
+-       * for this drive
++      /*
++       * Route the DMA signals to the correct interface.
++       */
++      outb(hwif->select_data, hwif->config_data);
++
++      /*
++       * Select the correct timing for this drive.
+        */
+       set_dma_speed(hwif->hw.dma, drive->drive_data);
+-      set_dma_sg(hwif->hw.dma, HWIF(drive)->sg_table, count);
+-      set_dma_mode(hwif->hw.dma, DMA_MODE_READ);
++      /*
++       * Tell the DMA engine about the SG table and
++       * data direction.
++       */
++      set_dma_sg(hwif->hw.dma, hwif->sg_table, hwif->sg_nents);
++      set_dma_mode(hwif->hw.dma, dma_mode);
++
++      return 0;
++}
++
++static int icside_dma_read(ide_drive_t *drive)
++{
++      struct request *rq = HWGROUP(drive)->rq;
++      task_ioreg_t cmd;
++
++      if (icside_dma_common(drive, rq, DMA_MODE_READ))
++              return 1;
+       drive->waiting_for_dma = 1;
++
+       if (drive->media != ide_disk)
+               return 0;
+-      if (HWGROUP(drive)->handler != NULL)    /* paranoia check */
+-              BUG();
+-      ide_set_handler(drive, &icside_dmaintr, WAIT_CMD, NULL);
+       /*
+        * FIX ME to use only ACB ide_task_t args Struct
+        */
+ #if 0
+       {
+-              ide_task_t *args = HWGROUP(drive)->rq->special;
+-              command = args->tfRegister[IDE_COMMAND_OFFSET];
++              ide_task_t *args = rq->special;
++              cmd = args->tfRegister[IDE_COMMAND_OFFSET];
+       }
+ #else
+-      command = (lba48) ? WIN_READDMA_EXT : WIN_READDMA;
+-              if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) {
+-              ide_task_t *args = HWGROUP(drive)->rq->special;
+-              command = args->tfRegister[IDE_COMMAND_OFFSET];
++      if (rq->cmd == IDE_DRIVE_TASKFILE) {
++              ide_task_t *args = rq->special;
++              cmd = args->tfRegister[IDE_COMMAND_OFFSET];
++      } else if (drive->addressing == 1) {
++              cmd = WIN_READDMA_EXT;
++      } else {
++              cmd = WIN_READDMA;
+       }
+ #endif
+-      /* issue cmd to drive */
+-      HWIF(drive)->OUTB(command, IDE_COMMAND_REG);
+-      return icside_dma_count(drive);
++      ide_execute_command(drive, cmd, icside_dmaintr, 2*WAIT_CMD, NULL);
++
++      return icside_dma_begin(drive);
+ }
+-int icside_dma_write(ide_drive_t *drive)
++static int icside_dma_write(ide_drive_t *drive)
+ {
+-      ide_hwif_t *hwif        = HWIF(drive);
+-//    ide_task_t *args        = HWGROUP(drive)->rq->special;
+-      int count               = 0;
+-      u8 lba48                = (drive->addressing == 1) ? 1 : 0;
+-      task_ioreg_t command    = WIN_NOP;
++      struct request *rq = HWGROUP(drive)->rq;
++      task_ioreg_t cmd;
+-      count = icside_build_dmatable(drive, PCI_DMA_TODEVICE);
+-      if (!count)
++      if (icside_dma_common(drive, rq, DMA_MODE_WRITE))
+               return 1;
+-      disable_dma(hwif->hw.dma);
+-
+-      /* Route the DMA signals to
+-       * to the correct interface.
+-       */
+-      HWIF(drive)->OUTB(hwif->select_data, hwif->config_data);
+-
+-      /* Select the correct timing
+-       * for this drive
+-       */
+-      set_dma_speed(hwif->hw.dma, drive->drive_data);
+-
+-      set_dma_sg(hwif->hw.dma, HWIF(drive)->sg_table, count);
+-      set_dma_mode(hwif->hw.dma, DMA_MODE_WRITE);
+       drive->waiting_for_dma = 1;
++
+       if (drive->media != ide_disk)
+               return 0;
+-      if (HWGROUP(drive)->handler != NULL)
+-              BUG();
+-      ide_set_handler(drive, &icside_dmaintr, WAIT_CMD, NULL);
+       /*
+        * FIX ME to use only ACB ide_task_t args Struct
+        */
+ #if 0
+       {
+-              ide_task_t *args = HWGROUP(drive)->rq->special;
+-              command = args->tfRegister[IDE_COMMAND_OFFSET];
++              ide_task_t *args = rq->special;
++              cmd = args->tfRegister[IDE_COMMAND_OFFSET];
+       }
+ #else
+-      command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA;
+-      if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) {
+-              ide_task_t *args = HWGROUP(drive)->rq->special;
+-              command = args->tfRegister[IDE_COMMAND_OFFSET];
++      if (rq->cmd == IDE_DRIVE_TASKFILE) {
++              ide_task_t *args = rq->special;
++              cmd = args->tfRegister[IDE_COMMAND_OFFSET];
++      } else if (drive->addressing == 1) {
++              cmd = WIN_WRITEDMA_EXT;
++      } else {
++              cmd = WIN_WRITEDMA;
+       }
+ #endif
+-      /* issue cmd to drive */
+-      HWIF(drive)->OUTB(command, IDE_COMMAND_REG);
+-      return icside_dma_count(drive);
++      ide_execute_command(drive, cmd, icside_dmaintr, 2*WAIT_CMD, NULL);
++
++      return icside_dma_begin(drive);
+ }
+-static int
+-icside_setup_dma(ide_hwif_t *hwif, int autodma)
++static int icside_dma_test_irq(ide_drive_t *drive)
++{
++      ide_hwif_t *hwif = HWIF(drive);
++      struct icside_state *state = hwif->hwif_data;
++
++      return inb(state->irq_port +
++                 (hwif->channel ?
++                      ICS_ARCIN_V6_INTRSTAT_2 :
++                      ICS_ARCIN_V6_INTRSTAT_1)) & 1;
++}
++
++static int icside_dma_verbose(ide_drive_t *drive)
++{
++      printk(", %s (peak %dMB/s)",
++              ide_xfer_verbose(drive->current_speed),
++              2000 / drive->drive_data);
++      return 1;
++}
++
++static int icside_dma_timeout(ide_drive_t *drive)
+ {
++      printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
++
++      if (icside_dma_test_irq(drive))
++              return 0;
++
++      ide_dump_status(drive, "DMA timeout",
++              HWIF(drive)->INB(IDE_STATUS_REG));
++
++      return icside_dma_end(drive);
++}
++
++static int icside_dma_lostirq(ide_drive_t *drive)
++{
++      printk(KERN_ERR "%s: IRQ lost\n", drive->name);
++      return 1;
++}
++
++static int icside_dma_init(ide_hwif_t *hwif)
++{
++      int autodma = 0;
++
++#ifdef CONFIG_IDEDMA_ICS_AUTO
++      autodma = 1;
++#endif
++
+       printk("    %s: SG-DMA", hwif->name);
+       hwif->sg_table = kmalloc(sizeof(struct scatterlist) * NR_ENTRIES,
+@@ -666,40 +717,53 @@
+       if (!hwif->sg_table)
+               goto failed;
+-      hwif->dmatable_cpu = NULL;
+-      hwif->dmatable_dma = 0;
+-      hwif->speedproc = icside_set_speed;
+-      hwif->autodma = autodma;
++      hwif->atapi_dma         = 1;
++      hwif->mwdma_mask        = 7; /* MW0..2 */
++      hwif->swdma_mask        = 7; /* SW0..2 */
+-      hwif->ide_dma_check = icside_dma_check;
+-      hwif->ide_dma_host_off = icside_dma_host_off;
++      hwif->dmatable_cpu      = NULL;
++      hwif->dmatable_dma      = 0;
++      hwif->speedproc         = icside_set_speed;
++      hwif->autodma           = autodma;
++
++      hwif->ide_dma_check     = icside_dma_check;
++      hwif->ide_dma_host_off  = icside_dma_host_off;
+       hwif->ide_dma_off_quietly = icside_dma_off_quietly;
+-      hwif->ide_dma_off = icside_dma_off;
+-      hwif->ide_dma_host_on = icside_dma_host_on;
+-      hwif->ide_dma_on = icside_dma_on;
+-      hwif->ide_dma_read = icside_dma_read;
+-      hwif->ide_dma_write = icside_dma_write;
+-      hwif->ide_dma_count = icside_dma_count;
+-      hwif->ide_dma_begin = icside_dma_begin;
+-      hwif->ide_dma_end = icside_dma_end;
+-      hwif->ide_dma_verbose = icside_dma_verbose;
+-      hwif->ide_dma_bad_drive = check_drive_bad_lists;
+-      hwif->ide_dma_good_drive = check_drive_good_lists;
+-      hwif->ide_dma_test_irq = icside_dma_test_irq;
++      hwif->ide_dma_off       = icside_dma_off;
++      hwif->ide_dma_host_on   = icside_dma_host_on;
++      hwif->ide_dma_on        = icside_dma_on;
++      hwif->ide_dma_read      = icside_dma_read;
++      hwif->ide_dma_write     = icside_dma_write;
++      hwif->ide_dma_count     = icside_dma_count;
++      hwif->ide_dma_begin     = icside_dma_begin;
++      hwif->ide_dma_end       = icside_dma_end;
++      hwif->ide_dma_test_irq  = icside_dma_test_irq;
++      hwif->ide_dma_verbose   = icside_dma_verbose;
++      hwif->ide_dma_timeout   = icside_dma_timeout;
++      hwif->ide_dma_lostirq   = icside_dma_lostirq;
+-      printk(" capable%s\n", autodma ?
+-              ", auto-enable" : "");
++      printk(" capable%s\n", hwif->autodma ? ", auto-enable" : "");
+       return 1;
+ failed:
+-      printk(" -- ERROR, unable to allocate DMA table\n");
++      printk(" disabled, unable to allocate DMA table\n");
+       return 0;
+ }
++
++static void icside_dma_exit(ide_hwif_t *hwif)
++{
++      if (hwif->sg_table) {
++              kfree(hwif->sg_table);
++              hwif->sg_table = NULL;
++      }
++}
++#else
++#define icside_dma_init(hwif) (0)
++#define icside_dma_exit(hwif) do { } while (0)
+ #endif
+-static ide_hwif_t *
+-icside_find_hwif(unsigned long dataport)
++static ide_hwif_t *icside_find_hwif(unsigned long dataport)
+ {
+       ide_hwif_t *hwif;
+       int index;
+@@ -716,13 +780,13 @@
+                       goto found;
+       }
+-      return NULL;
++      hwif = NULL;
+ found:
+       return hwif;
+ }
+ static ide_hwif_t *
+-icside_setup(unsigned long base, struct cardinfo *info, int irq)
++icside_setup(unsigned long base, struct cardinfo *info, struct expansion_card *ec)
+ {
+       unsigned long port = base + info->dataoffset;
+       ide_hwif_t *hwif;
+@@ -740,8 +804,8 @@
+               }
+               hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+               hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+-              hwif->hw.irq  = irq;
+-              hwif->irq     = irq;
++              hwif->hw.irq  = ec->irq;
++              hwif->irq     = ec->irq;
+               hwif->hw.dma  = NO_DMA;
+               hwif->noprobe = 0;
+               hwif->chipset = ide_acorn;
+@@ -750,33 +814,39 @@
+       return hwif;
+ }
+-static int __init icside_register_v5(struct expansion_card *ec, int autodma)
++static int __init
++icside_register_v5(struct icside_state *state, struct expansion_card *ec)
+ {
+       unsigned long slot_port;
+       ide_hwif_t *hwif;
+       slot_port = ecard_address(ec, ECARD_MEMC, 0);
++      state->irq_port = slot_port;
++
+       ec->irqaddr  = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
+       ec->irqmask  = 1;
+-      ec->irq_data = (void *)slot_port;
+-      ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v5;
++      ec->irq_data = state;
++      ec->ops      = &icside_ops_arcin_v5;
+       /*
+        * Be on the safe side - disable interrupts
+        */
+       inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
+-      hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
++      hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec);
+-      return hwif ? 0 : -1;
++      state->hwif[0] = hwif;
++
++      return hwif ? 0 : -ENODEV;
+ }
+-static int __init icside_register_v6(struct expansion_card *ec, int autodma)
++static int __init
++icside_register_v6(struct icside_state *state, struct expansion_card *ec)
+ {
+       unsigned long slot_port, port;
+       ide_hwif_t *hwif, *mate;
+-      int sel = 0;
++      unsigned int sel = 0;
+       slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+       port      = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+@@ -788,88 +858,185 @@
+       outb(sel, slot_port);
+-      ec->irq_data = (void *)port;
+-      ec->ops      = (expansioncard_ops_t *)&icside_ops_arcin_v6;
+-
+       /*
+        * Be on the safe side - disable interrupts
+        */
+       inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+       inb(port + ICS_ARCIN_V6_INTROFFSET_2);
+-      hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
+-      mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
++      /*
++       * Find and register the interfaces.
++       */
++      hwif = icside_setup(port, &icside_cardinfo_v6_1, ec);
++      mate = icside_setup(port, &icside_cardinfo_v6_2, ec);
+-#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+-      if (ec->dma != NO_DMA) {
+-              if (request_dma(ec->dma, hwif->name))
+-                      goto no_dma;
++      if (!hwif || !mate)
++              return -ENODEV;
+-              if (hwif) {
+-                      hwif->config_data = slot_port;
+-                      hwif->select_data = sel;
+-                      hwif->hw.dma  = ec->dma;
+-                      hwif->hw.priv = (void *)
+-                                      (port + ICS_ARCIN_V6_INTRSTAT_1);
+-                      hwif->channel = 0;
+-                      icside_setup_dma(hwif, autodma);
+-                      hwif->drives[0].autodma = autodma;
+-                      hwif->drives[1].autodma = autodma;
+-              }
+-              if (mate) {
+-                      mate->config_data = slot_port;
+-                      mate->select_data = sel | 1;
+-                      mate->hw.dma  = ec->dma;
+-                      mate->hw.priv = (void *)
+-                                      (port + ICS_ARCIN_V6_INTRSTAT_2);
+-                      mate->channel = 1;
+-                      icside_setup_dma(mate, autodma);
+-                      mate->drives[0].autodma = autodma;
+-                      mate->drives[1].autodma = autodma;
+-              }
++      state->irq_port   = port;
++      state->slot_port  = slot_port;
++      state->hwif[0]    = hwif;
++      state->hwif[1]    = mate;
++
++      ec->irq_data      = state;
++      ec->ops           = &icside_ops_arcin_v6;
++
++      hwif->maskproc    = icside_maskproc;
++      hwif->channel     = 0;
++      hwif->hwif_data   = state;
++      hwif->mate        = mate;
++      hwif->serialized  = 1;
++      hwif->config_data = slot_port;
++      hwif->select_data = sel;
++      hwif->hw.dma      = ec->dma;
++
++      mate->maskproc    = icside_maskproc;
++      mate->channel     = 1;
++      mate->hwif_data   = state;
++      mate->mate        = hwif;
++      mate->serialized  = 1;
++      mate->config_data = slot_port;
++      mate->select_data = sel | 1;
++      mate->hw.dma      = ec->dma;
++
++      if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) {
++              icside_dma_init(hwif);
++              icside_dma_init(mate);
+       }
+-no_dma:
+-#endif
+-      return hwif || mate ? 0 : -1;
++      return 0;
+ }
+-int __init icside_init(void)
++static int __init icside_probe(struct expansion_card *ec, const struct ecard_id *id)
+ {
+-      int autodma = 0;
++      struct icside_state *state;
++      int ret;
+-#ifdef CONFIG_IDEDMA_ICS_AUTO
+-      autodma = 1;
+-#endif
++      state = kmalloc(sizeof(struct icside_state), GFP_KERNEL);
++      if (!state) {
++              ret = -ENOMEM;
++              goto out;
++      }
+-      ecard_startfind ();
++      memset(state, 0, sizeof(struct icside_state));
++      state->type     = ICS_TYPE_NOTYPE;
+-      do {
+-              struct expansion_card *ec;
+-              int result;
++      {
++              unsigned int addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
++              unsigned int type;
+-              ec = ecard_find(0, icside_cids);
+-              if (ec == NULL)
+-                      break;
++              type = inb(addr) & 1;
++              type |= (inb(addr + 1) & 1) << 1;
++              type |= (inb(addr + 2) & 1) << 2;
++              type |= (inb(addr + 3) & 1) << 3;
+-              ecard_claim(ec);
++              state->type = type;
++      }
+-              switch (icside_identifyif(ec)) {
+-              case ics_if_arcin_v5:
+-                      result = icside_register_v5(ec, autodma);
+-                      break;
++      switch (state->type) {
++      case ICS_TYPE_A3IN:
++              printk(KERN_WARNING "icside: A3IN unsupported\n");
++              ret = -ENODEV;
++              break;
+-              case ics_if_arcin_v6:
+-                      result = icside_register_v6(ec, autodma);
+-                      break;
++      case ICS_TYPE_A3USER:
++              printk(KERN_WARNING "icside: A3USER unsupported\n");
++              ret = -ENODEV;
++              break;
+-              default:
+-                      result = -1;
+-                      break;
+-              }
++      case ICS_TYPE_V5:
++              ret = icside_register_v5(state, ec);
++              break;
+-              if (result)
+-                      ecard_release(ec);
+-      } while (1);
++      case ICS_TYPE_V6:
++              ret = icside_register_v6(state, ec);
++              break;
+-      return 0;
++      default:
++              printk(KERN_WARNING "icside: unknown interface type\n");
++              ret = -ENODEV;
++              break;
++      }
++
++      if (ret == 0) {
++              ecard_set_drvdata(ec, state);
++      } else {
++              kfree(state);
++      }
++ out:
++      return ret;
++}
++
++static void __devexit icside_remove(struct expansion_card *ec)
++{
++      struct icside_state *state = ecard_get_drvdata(ec);
++
++      switch (state->type) {
++      case ICS_TYPE_V5:
++              /* FIXME: tell IDE to stop using the interface */
++
++              /* Disable interrupts */
++              inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET);
++              break;
++
++      case ICS_TYPE_V6:
++              /* FIXME: tell IDE to stop using the interface */
++              icside_dma_exit(state->hwif[1]);
++              icside_dma_exit(state->hwif[0]);
++
++              if (ec->dma != NO_DMA)
++                      free_dma(ec->dma);
++
++              /* Disable interrupts */
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
++
++              /* Reset the ROM pointer/EASI selection */
++              outb(0, state->slot_port);
++              break;
++      }
++
++      ecard_set_drvdata(ec, NULL);
++      ec->ops = NULL;
++      ec->irq_data = NULL;
++
++      kfree(state);
++}
++
++static void icside_shutdown(struct expansion_card *ec)
++{
++      struct icside_state *state = ecard_get_drvdata(ec);
++
++      switch (state->type) {
++      case ICS_TYPE_V5:
++              /* Disable interrupts */
++              inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET);
++              break;
++
++      case ICS_TYPE_V6:
++              /* Disable interrupts */
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
++              inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
++
++              /* Reset the ROM pointer/EASI selection */
++              outb(0, state->slot_port);
++              break;
++      }
++}
++
++static const struct ecard_id icside_ids[] = {
++      { MANU_ICS,  PROD_ICS_IDE  },
++      { MANU_ICS2, PROD_ICS2_IDE },
++      { 0xffff, 0xffff }
++};
++
++static struct ecard_driver icside_driver = {
++      .probe          = icside_probe,
++      .remove         = __devexit_p(icside_remove),
++      .shutdown       = icside_shutdown,
++      .id_table       = icside_ids,
++};
++
++int __init icside_init(void)
++{
++      return ecard_register_driver(&icside_driver);
+ }
+--- /dev/null
++++ linux-2.4.27/drivers/ide/arm/rstation-ide.c
+@@ -0,0 +1,78 @@
++/*
++ * linux/drivers/ide/rs-ide.c
++ *
++ * Copyright (c) 2002 Ben Dooks
++ * Copyright (c) 2002 Simtec Electronics
++ *
++ * Simple RiscStation IDE support
++*/
++
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/blkdev.h>
++#include <linux/errno.h>
++#include <linux/ide.h>
++#include <linux/init.h>
++
++#include <asm/hardware/iomd.h>
++#include <asm/mach-types.h>
++#include <asm/io.h>
++
++#ifndef CONFIG_ARCH_RISCSTATION
++#error "compiling this code for non-riscstation hardware is dangerous!"
++#endif
++
++#define DRV_PREFIX "ide-rs"
++
++#define IRQ_PRI  (40+3)
++#define IRQ_SEC  (40+4)
++
++#define PORT_BASE ((0x2b800 - 0x10000) >> 2)
++#define SEC_OFF (0x400 >> 2)
++
++int __init rside_reg(unsigned long base, unsigned int irq);
++
++int __init rside_init(void)
++{
++    int iotcr;
++
++    if (!machine_is_riscstation()) {
++      printk(DRV_PREFIX ": hardware is not a RiscStation!\n");
++      return 0;
++    }
++
++    /* select correct area cycle time */
++
++    iotcr = inb(IOMD_IOTCR);
++    outb((iotcr & ~3) | 1, IOMD_IOTCR);
++
++    /* register h/w */
++
++    rside_reg(PORT_BASE, IRQ_PRI);
++    rside_reg(PORT_BASE + SEC_OFF, IRQ_SEC);
++
++    return 0;
++}
++
++
++int __init rside_reg(unsigned long port, unsigned int irq)
++{
++    unsigned long addr, i;
++    hw_regs_t hw;
++
++    hw.irq = irq;
++
++    addr = port;
++
++    for (i = IDE_DATA_OFFSET; i  <= IDE_STATUS_OFFSET; i++) {
++      hw.io_ports[i] = (ide_ioreg_t)addr;
++      addr += 0x40 >> 2;
++    }
++
++    hw.io_ports[IDE_CONTROL_OFFSET] = port + ((0xb80 - 0x800) >> 2);
++
++    printk(DRV_PREFIX ": registering channel at %08lx, %08lx, irq %d\n",
++         port, hw.io_ports[IDE_CONTROL_OFFSET], irq);
++
++    return ide_register_hw(&hw, NULL);
++}
+--- linux-2.4.27/drivers/ide/ide-probe.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/ide-probe.c
+@@ -1297,11 +1297,11 @@
+                       hwif->name, hwif->major);
+               return (hwif->present = 0);
+       }
+-      
++
+       if (init_irq(hwif)) {
+               int i = hwif->irq;
+               /*
+-               *      It failed to initialise. Find the default IRQ for 
++               *      It failed to initialise. Find the default IRQ for
+                *      this port and try that.
+                */
+               if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) {
+@@ -1319,7 +1319,7 @@
+               printk("%s: probed IRQ %d failed, using default.\n",
+                       hwif->name, hwif->irq);
+       }
+-      
++
+       init_gendisk(hwif);
+       blk_dev[hwif->major].data = hwif;
+       blk_dev[hwif->major].queue = ide_get_queue;
+--- linux-2.4.27/drivers/ide/ide-proc.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/ide-proc.c
+@@ -425,6 +425,7 @@
+               case ide_cy82c693:      name = "cy82c693";      break;
+               case ide_4drives:       name = "4drives";       break;
+               case ide_pmac:          name = "pmac";          break;
++              case ide_acorn:         name = "acorn";         break;
+               default:                name = "(unknown)";     break;
+       }
+       len = sprintf(page, "%s\n", name);
+--- linux-2.4.27/drivers/ide/ide.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/ide.c
+@@ -218,23 +218,14 @@
+ static void init_hwif_data (unsigned int index)
+ {
+       unsigned int unit;
+-      hw_regs_t hw;
+       ide_hwif_t *hwif = &ide_hwifs[index];
+       /* bulk initialize hwif & drive info with zeros */
+       memset(hwif, 0, sizeof(ide_hwif_t));
+-      memset(&hw, 0, sizeof(hw_regs_t));
+       /* fill in any non-zero initial values */
+       hwif->index     = index;
+-      ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq);
+-      memcpy(&hwif->hw, &hw, sizeof(hw));
+-      memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports));
+-      hwif->noprobe   = !hwif->io_ports[IDE_DATA_OFFSET];
+-#ifdef CONFIG_BLK_DEV_HD
+-      if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+-              hwif->noprobe = 1; /* may be overridden by ide_setup() */
+-#endif /* CONFIG_BLK_DEV_HD */
++      hwif->noprobe   = 1;
+       hwif->major     = ide_hwif_to_major[index];
+       hwif->name[0]   = 'i';
+       hwif->name[1]   = 'd';
+@@ -276,6 +267,28 @@
+ }
+ /*
++ * Old compatability function - initialise ports using ide_default_io_base
++ */
++static void ide_old_init_default_hwifs(void)
++{
++      unsigned int index;
++      ide_ioreg_t base;
++      ide_hwif_t *hwif;
++
++      for (index = 0; index < MAX_HWIFS; index++) {
++              hwif = &ide_hwifs[index];
++
++              base = ide_default_io_base(index);
++
++              if (base) {
++                      ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->hw.irq);
++                      memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
++                      hwif->noprobe = 0;
++              }
++      }
++}
++
++/*
+  * init_ide_data() sets reasonable default values into all fields
+  * of all instances of the hwifs and drives, but only on the first call.
+  * Subsequent calls have no effect (they don't wipe out anything).
+@@ -307,6 +320,7 @@
+               init_hwif_data(index);
+       /* Add default hw interfaces */
++      ide_old_init_default_hwifs();
+       ide_init_default_hwifs();
+       idebus_parameter = 0;
+@@ -2530,6 +2544,12 @@
+               rapide_init();
+       }
+ #endif /* CONFIG_BLK_DEV_IDE_RAPIDE */
++#ifdef CONFIG_BLK_DEV_IDE_RISCSTATION
++      {
++          extern void rside_init(void);
++          rside_init();
++      }
++#endif /* CONFIG_BLK_DEV_IDE_RISCSTATION */
+ #ifdef CONFIG_BLK_DEV_GAYLE
+       {
+               extern void gayle_init(void);
+--- linux-2.4.27/drivers/ide/pci/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/pci/Makefile
+@@ -16,7 +16,6 @@
+ obj-$(CONFIG_BLK_DEV_HPT34X)          += hpt34x.o
+ obj-$(CONFIG_BLK_DEV_HPT366)          += hpt366.o
+ #obj-$(CONFIG_BLK_DEV_HPT37X)         += hpt37x.o
+-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)      += icside.o
+ obj-$(CONFIG_BLK_DEV_IT8172)          += it8172.o
+ obj-$(CONFIG_BLK_DEV_NS87415)         += ns87415.o
+ obj-$(CONFIG_BLK_DEV_OPTI621)         += opti621.o
+--- linux-2.4.27/drivers/ide/pci/sl82c105.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/ide/pci/sl82c105.c
+@@ -37,7 +37,7 @@
+ #ifdef DEBUG
+ #define DBG(arg) printk arg
+ #else
+-#define DBG(fmt,...)
++#define DBG(fmt...)
+ #endif
+ /*
+  * SL82C105 PCI config register 0x40 bits.
+--- /dev/null
++++ linux-2.4.27/drivers/ide/pci/sl82c105.c.2419
+@@ -0,0 +1,380 @@
++/*
++ * linux/drivers/ide/sl82c105.c
++ *
++ * SL82C105/Winbond 553 IDE driver
++ *
++ * Maintainer unknown.
++ *
++ * Changelog:
++ *
++ * 15/11/1998 RMK     Drive tuning added from Rebel.com's kernel
++ *                    sources
++ * 30/03/2002 RMK     Add fixes specified in W83C553F errata.
++ *                    (with special thanks to Todd Inglett)
++ */
++
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/timer.h>
++#include <linux/mm.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/blkdev.h>
++#include <linux/hdreg.h>
++#include <linux/pci.h>
++#include <linux/ide.h>
++
++#include <asm/io.h>
++#include <asm/dma.h>
++
++#include "ide_modes.h"
++
++extern char *ide_xfer_verbose (byte xfer_rate);
++
++/*
++ * SL82C105 PCI config register 0x40 bits.
++ */
++#define CTRL_IDE_IRQB (1 << 30)
++#define CTRL_IDE_IRQA (1 << 28)
++#define CTRL_LEGIRQ   (1 << 11)
++#define CTRL_P1F16    (1 << 5)
++#define CTRL_P1EN     (1 << 4)
++#define CTRL_P0F16    (1 << 1)
++#define       CTRL_P0EN       (1 << 0)
++
++/*
++ * Convert a PIO mode and cycle time to the required on/off
++ * times for the interface.  This has protection against run-away
++ * timings.
++ */
++static unsigned int get_timing_sl82c105(ide_pio_data_t *p)
++{
++      unsigned int cmd_on;
++      unsigned int cmd_off;
++
++      cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30;
++      cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30;
++
++      if (cmd_on > 32)
++              cmd_on = 32;
++      if (cmd_on == 0)
++              cmd_on = 1;
++
++      if (cmd_off > 32)
++              cmd_off = 32;
++      if (cmd_off == 0)
++              cmd_off = 1;
++
++      return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00);
++}
++
++/*
++ * Configure the drive and chipset for PIO
++ */
++static void config_for_pio(ide_drive_t *drive, int pio, int report)
++{
++      ide_hwif_t *hwif = HWIF(drive);
++      struct pci_dev *dev = hwif->pci_dev;
++      ide_pio_data_t p;
++      unsigned short drv_ctrl = 0x909;
++      unsigned int xfer_mode, reg;
++
++      reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
++
++      pio = ide_get_best_pio_mode(drive, pio, 5, &p);
++
++      switch (pio) {
++      default:
++      case 0:         xfer_mode = XFER_PIO_0;         break;
++      case 1:         xfer_mode = XFER_PIO_1;         break;
++      case 2:         xfer_mode = XFER_PIO_2;         break;
++      case 3:         xfer_mode = XFER_PIO_3;         break;
++      case 4:         xfer_mode = XFER_PIO_4;         break;
++      }
++
++      if (ide_config_drive_speed(drive, xfer_mode) == 0)
++              drv_ctrl = get_timing_sl82c105(&p);
++
++      if (drive->using_dma == 0) {
++              /*
++               * If we are actually using MW DMA, then we can not
++               * reprogram the interface drive control register.
++               */
++              pci_write_config_word(dev, reg, drv_ctrl);
++              pci_read_config_word(dev, reg, &drv_ctrl);
++
++              if (report) {
++                      printk("%s: selected %s (%dns) (%04X)\n", drive->name,
++                             ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl);
++              }
++      }
++}
++
++/*
++ * Configure the drive and the chipset for DMA
++ */
++static int config_for_dma(ide_drive_t *drive)
++{
++      ide_hwif_t *hwif = HWIF(drive);
++      struct pci_dev *dev = hwif->pci_dev;
++      unsigned short drv_ctrl = 0x909;
++      unsigned int reg;
++
++      reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0);
++
++      if (ide_config_drive_speed(drive, XFER_MW_DMA_2) == 0)
++              drv_ctrl = 0x0240;
++
++      pci_write_config_word(dev, reg, drv_ctrl);
++
++      return 0;
++}
++
++
++/*
++ * Check to see if the drive and
++ * chipset is capable of DMA mode
++ */
++static int sl82c105_check_drive(ide_drive_t *drive)
++{
++      ide_dma_action_t dma_func = ide_dma_off_quietly;
++
++      do {
++              struct hd_driveid *id = drive->id;
++              ide_hwif_t *hwif = HWIF(drive);
++
++              if (!hwif->autodma)
++                      break;
++
++              if (!id || !(id->capability & 1))
++                      break;
++
++              /* Consult the list of known "bad" drives */
++              if (ide_dmaproc(ide_dma_bad_drive, drive)) {
++                      dma_func = ide_dma_off;
++                      break;
++              }
++
++              if (id->field_valid & 2) {
++                      if  (id->dma_mword & 7 || id->dma_1word & 7)
++                              dma_func = ide_dma_on;
++                      break;
++              }
++
++              if (ide_dmaproc(ide_dma_good_drive, drive)) {
++                      dma_func = ide_dma_on;
++                      break;
++              }
++      } while (0);
++
++      return HWIF(drive)->dmaproc(dma_func, drive);
++}
++
++/*
++ * The SL82C105 holds off all IDE interrupts while in DMA mode until
++ * all DMA activity is completed.  Sometimes this causes problems (eg,
++ * when the drive wants to report an error condition).
++ *
++ * 0x7e is a "chip testing" register.  Bit 2 resets the DMA controller
++ * state machine.  We need to kick this to work around various bugs.
++ */
++static inline void sl82c105_reset_host(struct pci_dev *dev)
++{
++      u16 val;
++
++      pci_read_config_word(dev, 0x7e, &val);
++      pci_write_config_word(dev, 0x7e, val | (1 << 2));
++      pci_write_config_word(dev, 0x7e, val & ~(1 << 2));
++}
++
++/*
++ * If we get an IRQ timeout, it might be that the DMA state machine
++ * got confused.  Fix from Todd Inglett.  Details from Winbond.
++ *
++ * This function is called when the IDE timer expires, the drive
++ * indicates that it is READY, and we were waiting for DMA to complete.
++ */
++static int sl82c105_lostirq(ide_drive_t *drive)
++{
++      ide_hwif_t *hwif = HWIF(drive);
++      struct pci_dev *dev = hwif->pci_dev;
++      u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
++      unsigned long dma_base = hwif->dma_base;
++
++      printk("sl82c105: lost IRQ: resetting host\n");
++
++      /*
++       * Check the raw interrupt from the drive.
++       */
++      pci_read_config_dword(dev, 0x40, &val);
++      if (val & mask)
++              printk("sl82c105: drive was requesting IRQ, but host lost it\n");
++
++      /*
++       * Was DMA enabled?  If so, disable it - we're resetting the
++       * host.  The IDE layer will be handling the drive for us.
++       */
++      val = inb(dma_base);
++      if (val & 1) {
++              outb(val & ~1, dma_base);
++              printk("sl82c105: DMA was enabled\n");
++      }
++
++      sl82c105_reset_host(dev);
++
++      /* ide_dmaproc would return 1, so we do as well */
++      return 1;
++}
++
++/*
++ * ATAPI devices can cause the SL82C105 DMA state machine to go gaga.
++ * Winbond recommend that the DMA state machine is reset prior to
++ * setting the bus master DMA enable bit.
++ *
++ * The generic IDE core will have disabled the BMEN bit before this
++ * function is called.
++ */
++static void sl82c105_before_bm_enable(ide_drive_t *drive)
++{
++      ide_hwif_t *hwif = HWIF(drive);
++      struct pci_dev *dev = hwif->pci_dev;
++
++      sl82c105_reset_host(dev);
++}
++
++/*
++ * Our very own dmaproc.  We need to intercept various calls
++ * to fix up the SL82C105 specific behaviour.
++ */
++static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
++{
++      switch (func) {
++      case ide_dma_check:
++              return sl82c105_check_drive(drive);
++
++      case ide_dma_on:
++              if (config_for_dma(drive))
++                      func = ide_dma_off;
++              /* fall through */
++
++      case ide_dma_off_quietly:
++      case ide_dma_off:
++              config_for_pio(drive, 4, 0);
++              break;
++
++      case ide_dma_read:
++      case ide_dma_write:
++      case ide_dma_begin:
++      case ide_dma_timeout:
++              sl82c105_before_bm_enable(drive);
++              break;
++
++      case ide_dma_lostirq:
++              return sl82c105_lostirq(drive);
++
++      default:
++              break;
++      }
++      return ide_dmaproc(func, drive);
++}
++
++/*
++ * We only deal with PIO mode here - DMA mode 'using_dma' is not
++ * initialised at the point that this function is called.
++ */
++static void tune_sl82c105(ide_drive_t *drive, byte pio)
++{
++      config_for_pio(drive, pio, 1);
++
++      /*
++       * We support 32-bit I/O on this interface, and it
++       * doesn't have problems with interrupts.
++       */
++      drive->io_32bit = 1;
++      drive->unmask = 1;
++}
++
++/*
++ * Return the revision of the Winbond bridge
++ * which this function is part of.
++ */
++static unsigned int sl82c105_bridge_revision(struct pci_dev *dev)
++{
++      struct pci_dev *bridge;
++      unsigned char rev;
++
++      /*
++       * The bridge should be part of the same device, but function 0.
++       */
++      bridge = pci_find_slot(dev->bus->number,
++                             PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
++      if (!bridge)
++              return -1;
++
++      /*
++       * Make sure it is a Winbond 553 and is an ISA bridge.
++       */
++      if (bridge->vendor != PCI_VENDOR_ID_WINBOND ||
++          bridge->device != PCI_DEVICE_ID_WINBOND_83C553 ||
++          bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA)
++              return -1;
++
++      /*
++       * We need to find function 0's revision, not function 1
++       */
++      pci_read_config_byte(bridge, PCI_REVISION_ID, &rev);
++
++      return rev;
++}
++
++/*
++ * Enable the PCI device
++ */
++unsigned int __init pci_init_sl82c105(struct pci_dev *dev, const char *msg)
++{
++      u32 val;
++
++      pci_read_config_dword(dev, 0x40, &val);
++      val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1EN | CTRL_P1F16;
++      pci_write_config_dword(dev, 0x40, val);
++
++      return dev->irq;
++}
++
++void __init dma_init_sl82c105(ide_hwif_t *hwif, unsigned long dma_base)
++{
++      unsigned int bridge_rev;
++      byte dma_state;
++
++      dma_state = inb(dma_base + 2);
++      bridge_rev = sl82c105_bridge_revision(hwif->pci_dev);
++      if (bridge_rev <= 5) {
++              hwif->autodma = 0;
++              hwif->drives[0].autotune = 1;
++              hwif->drives[1].autotune = 1;
++              printk("    %s: Winbond 553 bridge revision %d, BM-DMA disabled\n",
++                     hwif->name, bridge_rev);
++              dma_state &= ~0x60;
++      } else {
++              dma_state |= 0x60;
++              hwif->autodma = 1;
++      }
++      outb(dma_state, dma_base + 2);
++
++      ide_setup_dma(hwif, dma_base, 8);
++
++      if (bridge_rev <= 5)
++              hwif->dmaproc = NULL;
++      else
++              hwif->dmaproc = sl82c105_dmaproc;
++}
++
++/*
++ * Initialise the chip
++ */
++void __init ide_init_sl82c105(ide_hwif_t *hwif)
++{
++      hwif->tuneproc = tune_sl82c105;
++}
++
+--- linux-2.4.27/drivers/input/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/input/Config.in
+@@ -15,5 +15,6 @@
+ 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.27/drivers/input/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/input/Makefile
+@@ -24,6 +24,7 @@
+ obj-$(CONFIG_INPUT_MOUSEDEV)  += mousedev.o
+ obj-$(CONFIG_INPUT_JOYDEV)    += joydev.o
+ obj-$(CONFIG_INPUT_EVDEV)     += evdev.o
++obj-$(CONFIG_INPUT_MX1TS)     += mx1ts.o
+ obj-$(CONFIG_INPUT_UINPUT)    += uinput.o
+ # The global Rules.make.
+--- /dev/null
++++ linux-2.4.27/drivers/input/mx1ts.c
+@@ -0,0 +1,508 @@
++/*
++ *  linux/drivers/misc/mx1ts.c
++ *
++ *  Copyright (C) 2003 Blue Mug, Inc. for Motorola, Inc.
++ *
++ *  Cloned from ucb1x00_ts.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/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++
++#include <linux/input.h>
++
++#include "mx1ts.h"
++
++#define DEV_IRQ_ID    "mx1-ts"
++
++struct mx1_ts {
++      struct input_dev        idev;
++#ifdef CONFIG_PM
++      struct pm_dev           *pmdev;
++#endif
++
++      wait_queue_head_t       irq_wait;
++      struct completion       init_exit;
++      int                     use_count;
++      u16                     x_res;
++      u16                     y_res;
++
++      int                     restart:1;
++};
++
++static struct mx1_ts mx1ts;
++static u8 mx1_performing_auto_calibration = 0;
++static u16 mx1_cal_auto_zero = 0;
++static u16 mx1_cal_range_x = 0;
++static u16 mx1_cal_range_y = 0;
++
++static int mx1_ts_startup(struct mx1_ts *ts);
++static void mx1_ts_shutdown(struct mx1_ts *ts);
++
++static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs);
++static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs);
++static void mx1_ts_compare_int(int irq, void *dev_id, struct pt_regs *regs);
++
++static void mx1_ts_enable_pen_touch_interrupt(void);
++static void mx1_ts_disable_pen_touch_interrupt(void);
++static void mx1_ts_enable_pen_up_interrupt(void);
++static void mx1_ts_disable_pen_up_interrupt(void);
++static void mx1_ts_enable_auto_sample(void);
++static void mx1_ts_disable_auto_sample(void);
++static void mx1_ts_start_auto_calibration(void);
++
++static inline void mx1_reg_write(unsigned int reg, unsigned int val)
++{
++      *((volatile unsigned int *)reg) = val;
++}
++
++static inline unsigned int mx1_reg_read(unsigned int reg)
++{
++      return *((volatile unsigned int *)reg);
++}
++
++static inline void mx1_reg_clear_bit(unsigned int reg, unsigned int bit)
++{
++      *((volatile unsigned int *)reg) &= ~bit;
++}
++
++static inline void mx1_reg_set_bit(unsigned int reg, unsigned int bit)
++{
++      *((volatile unsigned int *)reg) |= bit;
++}
++
++static inline void mx1_ts_evt_add(struct mx1_ts *ts, u16 pressure, u16 x, u16 y)
++{
++      input_report_abs(&ts->idev, ABS_X, (int)x - 32768);
++      input_report_abs(&ts->idev, ABS_Y, (int)y - 32768);
++      input_report_abs(&ts->idev, ABS_PRESSURE, (int)pressure);
++}
++
++static inline void mx1_ts_flush_fifo(void)
++{
++      int i;
++      for (i = 0; i < 12; i++)
++              if (mx1_reg_read(ASP_ISTATR) & (ASP_PFF | ASP_PDR))
++                      mx1_reg_read(ASP_PADFIFO);
++}
++
++static int mx1_ts_open(struct input_dev *idev)
++{
++      struct mx1_ts *ts = (struct mx1_ts *)idev;
++
++      mx1_performing_auto_calibration = 0;
++      return mx1_ts_startup(ts);
++}
++
++static void mx1_ts_close(struct input_dev *idev)
++{
++      struct mx1_ts *ts = (struct mx1_ts *)idev;
++
++      mx1_ts_shutdown(ts);
++}
++
++static inline int mx1_ts_enable_irqs(void)
++{
++      int result;
++
++      result = request_irq(ASP_PENDATA_IRQ,
++                           mx1_ts_pendata_int,
++                           SA_INTERRUPT,
++                           DEV_IRQ_ID,
++                           DEV_IRQ_ID);
++      if (result) {
++              printk("Couldn't request pen data IRQ.\n");
++              return result;
++      }
++
++      result = request_irq(ASP_TOUCH_IRQ,
++                           mx1_ts_touch_int,
++                           SA_INTERRUPT,
++                           DEV_IRQ_ID,
++                           DEV_IRQ_ID);
++      if (result) {
++              printk("Couldn't request pen touch IRQ.\n");
++              free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID);
++              return result;
++      }
++
++      return result;
++}
++
++static inline int mx1_ts_disable_irqs(void)
++{
++      free_irq(ASP_PENDATA_IRQ, DEV_IRQ_ID);
++      free_irq(ASP_TOUCH_IRQ, DEV_IRQ_ID);
++
++      return 0;
++}
++
++static inline int mx1_ts_register(struct mx1_ts *ts)
++{
++      ts->idev.name      = "Touchscreen panel";
++      ts->idev.open      = mx1_ts_open;
++      ts->idev.close     = mx1_ts_close;
++
++      __set_bit(EV_ABS, ts->idev.evbit);
++      __set_bit(ABS_X, ts->idev.absbit);
++      __set_bit(ABS_Y, ts->idev.absbit);
++      __set_bit(ABS_PRESSURE, ts->idev.absbit);
++
++      ts->idev.absmin[ABS_X] = 0;
++      ts->idev.absmax[ABS_X] = (u32)0x0000FFFF;
++      ts->idev.absfuzz[ABS_X] = 50;
++      ts->idev.absflat[ABS_X] = 0;
++
++      ts->idev.absmin[ABS_Y] = 0;
++      ts->idev.absmax[ABS_Y] = (u32)0x0000FFFF;
++      ts->idev.absfuzz[ABS_Y] = 50;
++      ts->idev.absflat[ABS_Y] = 0;
++
++      input_register_device(&ts->idev);
++
++      return 0;
++}
++
++static inline void mx1_ts_deregister(struct mx1_ts *ts)
++{
++      input_unregister_device(&ts->idev);
++}
++
++/*
++ * Handle the touch interrupt, generated when the pen is pressed/
++ * released.
++ */
++static void mx1_ts_touch_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      /* Clear the interrupt. */
++      mx1_reg_set_bit(ASP_ISTATR, ASP_PEN);
++
++      mx1_ts_disable_pen_touch_interrupt();
++      mx1_ts_start_auto_calibration();
++      mx1_ts_enable_pen_up_interrupt();
++}
++
++/*
++ * Handle the pen data ready interrupt, generated when pen data is
++ * in the FIFO.
++ */
++static void mx1_ts_pendata_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      static unsigned int auto_zero, pen_x, pen_y, pen_u;
++
++      if (mx1_reg_read(ASP_ISTATR) & 0x400) {
++              mx1_reg_set_bit(ASP_ISTATR, 0x400);
++
++              mx1_ts_disable_auto_sample();
++              mx1_ts_disable_pen_up_interrupt();
++              mx1_ts_enable_pen_touch_interrupt();
++
++              mx1_ts_evt_add(&mx1ts, 0, pen_x, pen_y);
++
++              mx1_ts_flush_fifo();
++
++              return;
++      }
++
++      if (mx1_performing_auto_calibration) {
++              unsigned int value;
++
++              mx1_cal_auto_zero = mx1_reg_read(ASP_PADFIFO) & 0xFFFF;
++              mx1_cal_range_x = mx1_reg_read(ASP_PADFIFO) & 0xFFFF;
++              mx1_cal_range_y = mx1_reg_read(ASP_PADFIFO) & 0xFFFF;
++
++              if ((mx1_cal_auto_zero >= mx1_cal_range_x) ||
++                  (mx1_cal_auto_zero >= mx1_cal_range_y)) {
++                      /* Invalid data. */
++                      mx1_ts_start_auto_calibration();
++                      return;
++              }
++
++              mx1_cal_range_x -= mx1_cal_auto_zero;
++              mx1_cal_range_y -= mx1_cal_auto_zero;
++
++              value = mx1_reg_read(ASP_ACNTLCR);
++              value &= ~0x04000000; /* XXX Undocumented. */
++              mx1_reg_write(ASP_ACNTLCR, value);
++
++              mx1_performing_auto_calibration = 0;
++
++              mx1_ts_enable_auto_sample();
++      } else {
++              /* There could be more than one sample in the FIFO, but we're
++               * only going to read one per call. The interrupt will be
++               * generated as long as there is data in the FIFO. */
++
++              if ((mx1_reg_read(ASP_ISTATR) & ASP_PDR) != ASP_PDR) {
++                      return;
++              }
++
++              auto_zero = mx1_reg_read(ASP_PADFIFO);
++              if (auto_zero > (mx1_cal_auto_zero + 0x200)) {
++                      return;
++              }
++
++              pen_x = mx1_reg_read(ASP_PADFIFO);
++              pen_y = mx1_reg_read(ASP_PADFIFO);
++              pen_u = mx1_reg_read(ASP_PADFIFO);
++
++              pen_x = (u32)(((pen_x - mx1_cal_auto_zero) << 16) /
++                            mx1_cal_range_x);
++              pen_y = (u32)(((pen_y - mx1_cal_auto_zero) << 16) /
++                            mx1_cal_range_y);
++
++              mx1_ts_evt_add(&mx1ts, pen_u, pen_x, pen_y);
++      }
++}
++
++static void mx1_ts_reset_asp(void)
++{
++      unsigned int value;
++
++      mx1_ts_flush_fifo();
++
++      /* Soft reset the ASP module */
++        mx1_reg_write(ASP_ACNTLCR, ASP_SWRST);
++
++      /* Read back the reset value of the control register */
++      value = mx1_reg_read(ASP_ACNTLCR);
++
++      /* Enable the clock and wait for a short while */
++      value |= ASP_CLKEN;
++        mx1_reg_write(ASP_ACNTLCR, value);
++      udelay(100);
++
++      /* Set the value of the conrtol register. */
++      value = ASP_CLKEN | ASP_NM | ASP_SW6 | ASP_BGE;
++        mx1_reg_write(ASP_ACNTLCR, value);
++
++      /* Set the clock divide ratio to 2. */
++      mx1_reg_write(ASP_CLKDIV, 0x01);
++
++      /* Set the sample rate control register. These values should yield
++         * about 150 samples per second, which seems to give good smooth
++         * lines. */
++      value = (0x2 << ASP_DMCNT_SCALE) |      /* Decimation ratio is 3 */
++              (0x1 << ASP_IDLECNT_SCALE) |    /* Idle count is 1 clock */
++              (0x2 << ASP_DSCNT_SCALE);       /* Data setup is 2 clocks */
++      mx1_reg_write(ASP_PSMPLRG, value);
++
++      /* Disable the compare function. */
++      mx1_reg_write(ASP_CMPCNTL, 0);
++}
++
++static void mx1_ts_enable_auto_sample(void)
++{
++      unsigned int value;
++
++      mx1_ts_flush_fifo();
++
++      value = mx1_reg_read(ASP_ACNTLCR);
++
++      /* Set the mode to X then Y */
++      value &= ~ASP_MODE_MASK;
++      value |= ASP_MODE_ONLY_Y;
++
++      /* Enable auto zero. */
++      value |= ASP_AZE;
++
++      /* Enable auto sample. */
++      value |= ASP_AUTO;
++
++      /* Enable pen A/D. */
++      value |= ASP_PADE;
++      mx1_reg_write(ASP_ACNTLCR, value);
++
++      /* Enable pen data ready and full interrupt. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value |= ASP_PFFE | ASP_PDRE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_disable_auto_sample(void)
++{
++      unsigned int value;
++
++      value = mx1_reg_read(ASP_ACNTLCR);
++
++      /* Set the mode to none */
++      value &= ~ASP_MODE_MASK;
++
++      /* Disable auto zero. */
++      value &= ~ASP_AZE;
++
++      /* Disable auto sample. */
++      value &= ~ASP_AUTO;
++
++      /* Disable pen A/D. */
++      value &= ~ASP_PADE;
++      mx1_reg_write(ASP_ACNTLCR, value);
++
++      /* Disable pen data ready and full interrupt. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value &= ~(ASP_PFFE | ASP_PDRE);
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_enable_pen_touch_interrupt(void)
++{
++      unsigned int value;
++
++      /* Enable pen touch interrupt. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value |= ASP_EDGE | ASP_PIRQE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_disable_pen_touch_interrupt(void)
++{
++      unsigned int value;
++
++      /* Enable pen touch interrupt. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value &= ~ASP_PIRQE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_enable_pen_up_interrupt(void)
++{
++      unsigned int value;
++
++      /* Enable pen up interrupt. XXX: This feature is undocumented. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value |= ASP_PUPE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_disable_pen_up_interrupt(void)
++{
++      unsigned int value;
++
++      /* Enable pen up interrupt. XXX: This feature is undocumented. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value &= ~ASP_PUPE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static void mx1_ts_start_auto_calibration(void)
++{
++      unsigned int value;
++
++      mx1_performing_auto_calibration = 1;
++
++      value = mx1_reg_read(ASP_ACNTLCR);
++
++      /* Set the mode to X then Y */
++      value &= ~ASP_MODE_MASK;
++      value |= ASP_MODE_ONLY_X;
++
++      /* Enable auto zero. */
++      value |= ASP_AZE;
++
++      /* Enable auto calibrate. XXX: Undocumented bitfield. */
++      value |= 0x04000000;
++
++      /* Enable auto sample. */
++      value |= ASP_AUTO;
++
++      /* Enable pen A/D. */
++      value |= ASP_PADE;
++      mx1_reg_write(ASP_ACNTLCR, value);
++
++      /* Enable pen data ready and full interrupt. */
++      value = mx1_reg_read(ASP_ICNTLR);
++      value |= ASP_PFFE | ASP_PDRE | ASP_PUPE;
++      mx1_reg_write(ASP_ICNTLR, value);
++}
++
++static int mx1_ts_startup(struct mx1_ts *ts)
++{
++      int ret = 0;
++
++      if (ts->use_count++ != 0)
++              goto out;
++
++        /*
++         * Reset the ASP.
++         */
++        mx1_ts_reset_asp();
++
++
++      /*
++       * XXX: Figure out if we need this...
++       * If we do this at all, we should allow the user to
++       * measure and read the X and Y resistance at any time.
++       */
++      //ts->x_res = mx1_ts_read_xres(ts);
++      //ts->y_res = mx1_ts_read_yres(ts);
++
++      mx1_ts_enable_pen_touch_interrupt();
++
++ out:
++      if (ret)
++              ts->use_count--;
++      return ret;
++}
++
++/*
++ * Release touchscreen resources.  Disable IRQs.
++ */
++static void mx1_ts_shutdown(struct mx1_ts *ts)
++{
++      if (--ts->use_count == 0) {
++              unsigned int value;
++
++              /* Turn off the ADC and associated circuitry. */
++              value = mx1_reg_read(ASP_ACNTLCR);
++              value &= !(ASP_CLKEN | ASP_PADE | ASP_BGE);
++              mx1_reg_write(ASP_ACNTLCR, value);
++      }
++}
++
++/*
++ * Initialization.
++ */
++static int __init mx1_ts_init(void)
++{
++      int ret = 0;
++      struct mx1_ts *ts = &mx1ts;
++
++      mx1_ts_reset_asp();
++
++      /*
++       * Enable the IRQ's
++       */
++      if ((ret = mx1_ts_enable_irqs()))
++              return ret;
++
++      return mx1_ts_register(ts);
++}
++
++static void __exit mx1_ts_exit(void)
++{
++      struct mx1_ts *ts = &mx1ts;
++
++      mx1_ts_disable_irqs();
++      mx1_ts_deregister(ts);
++}
++
++module_init(mx1_ts_init);
++module_exit(mx1_ts_exit);
++
++MODULE_AUTHOR("Jon McClintock <jonm@bluemug.com>");
++MODULE_DESCRIPTION("MX1 touchscreen driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/input/mx1ts.h
+@@ -0,0 +1,108 @@
++/*
++ *  linux/drivers/misc/mx1ts.h
++ *
++ *  Copyright (C) 2003 Blue Mug, Inc. for Motorola, 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.
++ *
++ */
++
++/* Interrupt numbers */
++#define ASP_COMPARE_IRQ         5
++#define ASP_PENDATA_IRQ         33
++#define ASP_TOUCH_IRQ           46
++
++/* Analog signal processor (ASP) control registers */
++#define ASP_ACNTLCR           0xF0215010      /* Control register */
++#define ASP_PSMPLRG           0xF0215014      /* Pen A/D sampe rate control */
++#define ASP_CMPCNTL           0xF0215030      /* Compare control register */
++#define ASP_ICNTLR            0xF0215018      /* Interrupt control register */
++#define ASP_ISTATR            0xF021501C      /* Interrupt status register */
++#define ASP_PADFIFO           0xF0215000      /* Pen sample FIFO */
++#define ASP_CLKDIV            0xF021502C      /* Clock divide register */
++
++/* ASP control register bits */
++#define ASP_CLKEN               (1 << 25)       /* Clock enable */
++#define ASP_SWRST               (1 << 23)       /* Software reset */
++#define ASP_U_SEL               (1 << 21)       /* U-channel resistor select */
++#define ASP_AZ_SEL              (1 << 20)       /* Auto-zero position select */
++#define ASP_LVM                 (1 << 19)       /* Low voltage output */
++#define ASP_NM                  (1 << 18)       /* Normal voltage output */
++#define ASP_HPM                 (1 << 17)       /* High voltage output */
++#define ASP_GLO                 (1 << 16)       /* Low gain enable */
++#define ASP_AZE                 (1 << 15)       /* Auto-zero enable */
++#define ASP_AUTO                (1 << 14)       /* Auto sampling */
++#define ASP_SW8                 (1 << 11)       /* Switch control 8 */
++#define ASP_SW7                 (1 << 10)
++#define ASP_SW6                 (1 << 9)
++#define ASP_SW5                 (1 << 8)
++#define ASP_SW4                 (1 << 7)
++#define ASP_SW3                 (1 << 6)
++#define ASP_SW2                 (1 << 5)
++#define ASP_SW1                 (1 << 4)        /* Switch control 1 */
++#define ASP_VDAE                (1 << 3)        /* Voice D/A enable */
++#define ASP_VADE                (1 << 2)        /* Voice A/D enable */
++#define ASP_PADE                (1 << 1)        /* Pen A/D enable */
++#define ASP_BGE                 (1 << 0)        /* Bandgap enable */
++
++#define ASP_MODE_MASK           0x00003000
++#define ASP_MODE_NONE           0x00000000
++#define ASP_MODE_ONLY_X         0x00001000
++#define ASP_MODE_ONLY_Y         0x00002000
++#define ASP_MODE_ONLY_U         0x00003000
++
++/* ASP Pen A/D sample rate control register */
++#define ASP_DMCNT_MASK          (0x00007000)    /* Decimation ratio count */
++#define ASP_DMCNT_SCALE         (12)
++#define ASP_BIT_SELECT_MASK     (0x00000C00)    /* Bit select */
++#define ASP_BIT_SELECT_SCALE    (10)
++#define ASP_IDLECNT_MASK        (0x000003F0)    /* Idle count */
++#define ASP_IDLECNT_SCALE       (4)
++#define ASP_DSCNT_MASK          (0x0000000F)    /* Data setup count */
++#define ASP_DSCNT_SCALE         (0)
++
++/* ASP compare control register */
++#define ASP_INT                 (1 << 19)       /* Interrupt status */
++#define ASP_CC                  (1 << 18)       /* Trigger on greater than */
++#define ASP_INSEL_MASK          (0x00030000)
++#define ASP_INSEL_DISABLE       (0x00000000)
++#define ASP_INSEL_X             (0x00010000)
++#define ASP_INSEL_Y             (0x00020000)
++#define ASP_INSEL_U             (0x00030000)
++#define ASP_COMPARE_VAL_MASK    (0x0000FFFF)
++#define ASP_COMPARE_VAL_SCALE   (0)
++
++/* ASP interrupt control register bits */
++#define ASP_PUPE                (1 << 10)       /* Pen up XXX undocumented */
++#define ASP_VDDMAE              (1 << 8)        /* VDAC FIFO empty DMA */
++#define ASP_VADMAE              (1 << 7)        /* VADC FIFO full DMA */
++#define ASP_POL                 (1 << 6)        /* Pen interrupt polarity */
++#define ASP_EDGE                (1 << 5)        /* Edge trigger enable */
++#define ASP_PIRQE               (1 << 4)        /* Pen interrupt enable */
++#define ASP_VDAFEE              (1 << 3)        /* VDAC FIFO empty interrupt */
++#define ASP_VADFFE              (1 << 2)        /* VADC FIFO full interrupt */
++#define ASP_PFFE                (1 << 1)        /* Pen FIFO full interrupt */
++#define ASP_PDRE                (1 << 0)        /* Pen data ready interrupt */
++
++/* ASP interrupt/error status register bits */
++#define ASP_PUP                 (1 << 10)       /* Pen up XXX undocumented */
++#define ASP_BGR                 (1 << 9)        /* Bandgap ready */
++#define ASP_VOV                 (1 << 8)        /* Voice sample data overflow */
++#define ASP_POV                 (1 << 7)        /* Pen sample data overflow */
++#define ASP_PEN                 (1 << 6)        /* Pen interrupt */
++#define ASP_VDAFF               (1 << 5)        /* VDAC FIFO full */
++#define ASP_VDAFE               (1 << 4)        /* VDAC FIFO empty */
++#define ASP_VADFF               (1 << 3)        /* VADC FIFO full */
++#define ASP_VADDR               (1 << 2)        /* VADC data ready */
++#define ASP_PFF                 (1 << 1)        /* Pen sample FIFO full */
++#define ASP_PDR                 (1 << 0)        /* Pen data ready */
++
++/* ASP Clock divide register */
++#define ASP_PADC_CLK_MASK       (0x0000001F)
++#define ASP_PADC_CLK_SCALE      (0)
++#define ASP_VADC_CLK_MASK       (0x000003E0)
++#define ASP_VADC_CLK_SCALE      (5)
++#define ASP_VDAC_CLK_MASK       (0x00003C00)
++#define ASP_VDAC_CLK_SCALE      (10)
+--- /dev/null
++++ linux-2.4.27/drivers/l3/Config.in
+@@ -0,0 +1,21 @@
++#
++# L3 bus configuration
++#
++mainmenu_option next_comment
++comment 'L3 serial bus support'
++
++tristate 'L3 support' CONFIG_L3
++dep_bool '  L3 bit-banging interfaces' CONFIG_L3_ALGOBIT $CONFIG_L3
++dep_bool '    SA11x0 GPIO adapter' CONFIG_L3_BIT_SA1100_GPIO $CONFIG_L3_ALGOBIT $CONFIG_ARCH_SA1100
++
++comment 'Other L3 adapters'
++dep_bool '  SA1111 adapter' CONFIG_L3_SA1111 $CONFIG_L3
++endmenu
++
++# i2c must come before this
++if [ "$CONFIG_L3_BIT_SA1100_GPIO" = "y" -o \
++     "$CONFIG_I2C_BIT_SA1100_GPIO" = "y" ]; then
++   define_bool CONFIG_BIT_SA1100_GPIO y
++else
++   define_bool CONFIG_BIT_SA1100_GPIO n
++fi
+--- /dev/null
++++ linux-2.4.27/drivers/l3/Makefile
+@@ -0,0 +1,23 @@
++#
++# Makefile for the L3 bus driver.
++#
++
++O_TARGET := l3.o
++
++export-objs   := l3-core.o l3-algo-bit.o
++l3-y          :=
++l3-n          :=
++l3-drv-y      :=
++l3-drv-n      :=
++
++# Link order:
++#  (core, adapters, algorithms, drivers) then clients
++
++l3-$(CONFIG_L3_ALGOBIT)               += l3-algo-bit.o
++l3-$(CONFIG_BIT_SA1100_GPIO)  += l3-bit-sa1100.o
++l3-$(CONFIG_L3_SA1111)                += l3-sa1111.o
++
++obj-$(CONFIG_L3)              += l3-core.o $(l3-y) $(l3-drv-y)
++
++include $(TOPDIR)/Rules.make
++
+--- /dev/null
++++ linux-2.4.27/drivers/l3/l3-algo-bit.c
+@@ -0,0 +1,175 @@
++/*
++ * L3 bus algorithm module.
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ *  Note that L3 buses can share the same pins as I2C buses, so we must
++ *  _not_ generate an I2C start condition.  An I2C start condition is
++ *  defined as a high-to-low transition of the data line while the clock
++ *  is high.  Therefore, we must only change the data line while the
++ *  clock is low.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/algo-bit.h>
++
++#define setdat(adap,val)      adap->setdat(adap->data, val)
++#define setclk(adap,val)      adap->setclk(adap->data, val)
++#define setmode(adap,val)     adap->setmode(adap->data, val)
++#define setdatin(adap)                adap->setdir(adap->data, 1)
++#define setdatout(adap)               adap->setdir(adap->data, 0)
++#define getdat(adap)          adap->getdat(adap->data)
++
++/*
++ * Send one byte of data to the chip.  Data is latched into the chip on
++ * the rising edge of the clock.
++ */
++static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte)
++{
++      int i;
++
++      for (i = 0; i < 8; i++) {
++              setclk(adap, 0);
++              udelay(adap->data_hold);
++              setdat(adap, byte & 1);
++              udelay(adap->data_setup);
++              setclk(adap, 1);
++              udelay(adap->clock_high);
++              byte >>= 1;
++      }
++}
++
++/*
++ * Send a set of bytes to the chip.  We need to pulse the MODE line
++ * between each byte, but never at the start nor at the end of the
++ * transfer.
++ */
++static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len)
++{
++      int i;
++
++      for (i = 0; i < len; i++) {
++              if (i) {
++                      udelay(adap->mode_hold);
++                      setmode(adap, 0);
++                      udelay(adap->mode);
++              }
++              setmode(adap, 1);
++              udelay(adap->mode_setup);
++              sendbyte(adap, buf[i]);
++      }
++}
++
++/*
++ * Read one byte of data from the chip.  Data is latched into the chip on
++ * the rising edge of the clock.
++ */
++static unsigned int readbyte(struct l3_algo_bit_data *adap)
++{
++      unsigned int byte = 0;
++      int i;
++
++      for (i = 0; i < 8; i++) {
++              setclk(adap, 0);
++              udelay(adap->data_hold + adap->data_setup);
++              setclk(adap, 1);
++              if (getdat(adap))
++                      byte |= 1 << i;
++              udelay(adap->clock_high);
++      }
++
++      return byte;
++}
++
++/*
++ * Read a set of bytes from the chip.  We need to pulse the MODE line
++ * between each byte, but never at the start nor at the end of the
++ * transfer.
++ */
++static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len)
++{
++      int i;
++
++      for (i = 0; i < len; i++) {
++              if (i) {
++                      udelay(adap->mode_hold);
++                      setmode(adap, 0);
++              }
++              setmode(adap, 1);
++              udelay(adap->mode_setup);
++              buf[i] = readbyte(adap);
++      }
++}
++
++static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num)
++{
++      struct l3_algo_bit_data *adap = l3_adap->algo_data;
++      int i;
++
++      /*
++       * If we share an I2C bus, ensure that it is in STOP mode
++       */
++      setclk(adap, 1);
++      setdat(adap, 1);
++      setmode(adap, 1);
++      setdatout(adap);
++      udelay(adap->mode);
++
++      for (i = 0; i < num; i++) {
++              struct l3_msg *pmsg = &msgs[i];
++
++              if (!(pmsg->flags & L3_M_NOADDR)) {
++                      setmode(adap, 0);
++                      udelay(adap->mode_setup);
++                      sendbyte(adap, pmsg->addr);
++                      udelay(adap->mode_hold);
++              }
++
++              if (pmsg->flags & L3_M_RD) {
++                      setdatin(adap);
++                      readbytes(adap, pmsg->buf, pmsg->len);
++              } else {
++                      setdatout(adap);
++                      sendbytes(adap, pmsg->buf, pmsg->len);
++              }
++      }
++
++      /*
++       * Ensure that we leave the bus in I2C stop mode.
++       */
++      setclk(adap, 1);
++      setdat(adap, 1);
++      setmode(adap, 0);
++      setdatin(adap);
++
++      return num;
++}
++
++static struct l3_algorithm l3_bit_algo = {
++      name:   "L3 bit-shift algorithm",
++      xfer:   l3_xfer,
++};
++
++int l3_bit_add_bus(struct l3_adapter *adap)
++{
++      adap->algo = &l3_bit_algo;
++      return l3_add_adapter(adap);
++}
++
++int l3_bit_del_bus(struct l3_adapter *adap)
++{
++      return l3_del_adapter(adap);
++}
++
++EXPORT_SYMBOL(l3_bit_add_bus);
++EXPORT_SYMBOL(l3_bit_del_bus);
+--- /dev/null
++++ linux-2.4.27/drivers/l3/l3-bit-sa1100.c
+@@ -0,0 +1,277 @@
++/*
++ *  linux/drivers/l3/l3-bit-sa1100.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ * 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.
++ *
++ *  This is a combined I2C and L3 bus driver.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/i2c-algo-bit.h>
++#include <linux/l3/algo-bit.h>
++
++#include <asm/system.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/arch/assabet.h>
++
++#define NAME "l3-bit-sa1100-gpio"
++
++struct bit_data {
++      unsigned int    sda;
++      unsigned int    scl;
++      unsigned int    l3_mode;
++};
++
++static int getsda(void *data)
++{
++      struct bit_data *bits = data;
++
++      return GPLR & bits->sda;
++}
++
++#ifdef CONFIG_I2C_BIT_SA1100_GPIO
++static void i2c_setsda(void *data, int state)
++{
++      struct bit_data *bits = data;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      if (state)
++              GPDR &= ~bits->sda;
++      else {
++              GPCR = bits->sda;
++              GPDR |= bits->sda;
++      }
++      local_irq_restore(flags);
++}
++
++static void i2c_setscl(void *data, int state)
++{
++      struct bit_data *bits = data;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      if (state)
++              GPDR &= ~bits->scl;
++      else {
++              GPCR = bits->scl;
++              GPDR |= bits->scl;
++      }
++      local_irq_restore(flags);
++}
++
++static int i2c_getscl(void *data)
++{
++      struct bit_data *bits = data;
++
++      return GPLR & bits->scl;
++}
++
++static struct i2c_algo_bit_data i2c_bit_data = {
++      setsda:         i2c_setsda,
++      setscl:         i2c_setscl,
++      getsda:         getsda,
++      getscl:         i2c_getscl,
++      udelay:         10,
++      mdelay:         10,
++      timeout:        100,
++};
++
++static struct i2c_adapter i2c_adapter = {
++      name:                   NAME,
++      algo_data:              &i2c_bit_data,
++//    inc_use:                i2c_inc_use,
++//    dec_use:                i2c_dec_use,
++};
++
++#define LOCK  &i2c_adapter.lock
++
++static int __init i2c_init(struct bit_data *bits)
++{
++      i2c_bit_data.data = bits;
++      return i2c_bit_add_bus(&i2c_adapter);
++}
++
++static void i2c_exit(void)
++{
++      i2c_bit_del_bus(&i2c_adapter);
++}
++
++#else
++static DECLARE_MUTEX(l3_lock);
++#define LOCK          &l3_lock
++#define i2c_init(bits)        (0)
++#define i2c_exit()    do { } while (0)
++#endif
++
++#ifdef CONFIG_L3_BIT_SA1100_GPIO
++/*
++ * iPAQs need the clock line driven hard high and low.
++ */
++static void l3_setscl(void *data, int state)
++{
++      struct bit_data *bits = data;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      if (state)
++              GPSR = bits->scl;
++      else
++              GPCR = bits->scl;
++      GPDR |= bits->scl;
++      local_irq_restore(flags);
++}
++
++static void l3_setsda(void *data, int state)
++{
++      struct bit_data *bits = data;
++
++      if (state)
++              GPSR = bits->sda;
++      else
++              GPCR = bits->sda;
++}
++
++static void l3_setdir(void *data, int in)
++{
++      struct bit_data *bits = data;
++      unsigned long flags;
++
++      local_irq_save(flags);
++      if (in)
++              GPDR &= ~bits->sda;
++      else
++              GPDR |= bits->sda;
++      local_irq_restore(flags);
++}
++
++static void l3_setmode(void *data, int state)
++{
++      struct bit_data *bits = data;
++
++      if (state)
++              GPSR = bits->l3_mode;
++      else
++              GPCR = bits->l3_mode;
++}
++
++static struct l3_algo_bit_data l3_bit_data = {
++      data:           NULL,
++      setdat:         l3_setsda,
++      setclk:         l3_setscl,
++      setmode:        l3_setmode,
++      setdir:         l3_setdir,
++      getdat:         getsda,
++      data_hold:      1,
++      data_setup:     1,
++      clock_high:     1,
++      mode_hold:      1,
++      mode_setup:     1,
++};
++
++static struct l3_adapter l3_adapter = {
++      owner:          THIS_MODULE,
++      name:           NAME,
++      algo_data:      &l3_bit_data,
++      lock:           LOCK,
++};
++
++static int __init l3_init(struct bit_data *bits)
++{
++      l3_bit_data.data = bits;
++      return l3_bit_add_bus(&l3_adapter);
++}
++
++static void __exit l3_exit(void)
++{
++      l3_bit_del_bus(&l3_adapter);
++}
++#else
++#define l3_init(bits) (0)
++#define l3_exit()     do { } while (0)
++#endif
++
++static struct bit_data bit_data;
++
++static int __init bus_init(void)
++{
++      struct bit_data *bit = &bit_data;
++      unsigned long flags;
++      int ret;
++
++      if (machine_is_assabet() || machine_is_pangolin()) {
++              bit->sda     = GPIO_GPIO15;
++              bit->scl     = GPIO_GPIO18;
++              bit->l3_mode = GPIO_GPIO17;
++      }
++
++#if defined(CONFIG_SA1100_H3600) || defined(CONFIG_SA1100_H3100)  
++      if (machine_is_h3600() || machine_is_h3100()) {
++              bit->sda     = GPIO_H3600_L3_DATA;
++              bit->scl     = GPIO_H3600_L3_CLOCK;
++              bit->l3_mode = GPIO_H3600_L3_MODE;
++      }
++#endif
++
++#ifdef CONFIG_SA1100_STORK
++      if (machine_is_stork()) {
++              bit->sda     = GPIO_STORK_L3_I2C_SDA;
++              bit->scl     = GPIO_STORK_L3_I2C_SCL;
++              bit->l3_mode = GPIO_STORK_L3_MODE;
++      }
++#endif
++
++      if (!bit->sda)
++              return -ENODEV;
++
++      /*
++       * Default level for L3 mode is low.
++       * We set SCL and SDA high (i2c idle state).
++       */
++      local_irq_save(flags);
++      GPDR &= ~(bit->scl | bit->sda);
++      GPCR = bit->l3_mode | bit->scl | bit->sda;
++      GPDR |= bit->l3_mode;
++      local_irq_restore(flags);
++
++      if (machine_is_assabet()) {
++              /*
++               * Release reset on UCB1300, ADI7171 and UDA1341.  We
++               * need to do this here so that we can communicate on
++               * the I2C/L3 buses.
++               */
++              ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
++              mdelay(1);
++              ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST);
++              mdelay(1);
++              ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
++      }
++
++      ret = i2c_init(bit);
++      if (ret == 0 && bit->l3_mode) {
++              ret = l3_init(bit);
++              if (ret)
++                      i2c_exit();
++      }
++
++      return ret;
++}
++
++static void __exit bus_exit(void)
++{
++      l3_exit();
++      i2c_exit();
++}
++
++module_init(bus_init);
++module_exit(bus_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/l3/l3-core.c
+@@ -0,0 +1,377 @@
++/*
++ *  linux/drivers/l3/l3-core.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ *  General structure taken from i2c-core.c by Simon G. Vogl
++ *
++ * 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.
++ *
++ *  See linux/Documentation/l3 for further documentation.
++ */
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/proc_fs.h>
++#include <linux/kmod.h>
++#include <linux/init.h>
++#include <linux/l3/l3.h>
++
++static DECLARE_MUTEX(adapter_lock);
++static LIST_HEAD(adapter_list);
++
++static DECLARE_MUTEX(driver_lock);
++static LIST_HEAD(driver_list);
++
++/**
++ * l3_add_adapter - register a new L3 bus adapter
++ * @adap: l3_adapter structure for the registering adapter
++ *
++ * Make the adapter available for use by clients using name adap->name.
++ * The adap->adapters list is initialised by this function.
++ *
++ * Returns 0;
++ */
++int l3_add_adapter(struct l3_adapter *adap)
++{
++      INIT_LIST_HEAD(&adap->clients);
++      down(&adapter_lock);
++      list_add(&adap->adapters, &adapter_list);
++      up(&adapter_lock);
++      return 0;       
++}
++
++/**
++ * l3_del_adapter - unregister a L3 bus adapter
++ * @adap: l3_adapter structure to unregister
++ *
++ * Remove an adapter from the list of available L3 Bus adapters.
++ *
++ * Returns 0;
++ */
++int l3_del_adapter(struct l3_adapter *adap)
++{
++      down(&adapter_lock);
++      list_del(&adap->adapters);
++      up(&adapter_lock);
++      return 0;
++}
++
++static struct l3_adapter *__l3_get_adapter(const char *name)
++{
++      struct list_head *l;
++
++      list_for_each(l, &adapter_list) {
++              struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters);
++
++              if (strcmp(adap->name, name) == 0)
++                      return adap;
++      }
++
++      return NULL;
++}
++
++/**
++ * l3_get_adapter - get a reference to an adapter
++ * @name: driver name
++ *
++ * Obtain a l3_adapter structure for the specified adapter.  If the adapter
++ * is not currently load, then load it.  The adapter will be locked in core
++ * until all references are released via l3_put_adapter.
++ */
++struct l3_adapter *l3_get_adapter(const char *name)
++{
++      struct l3_adapter *adap;
++      int try;
++
++      for (try = 0; try < 2; try ++) {
++              down(&adapter_lock);
++              adap = __l3_get_adapter(name);
++              if (adap && !try_inc_mod_count(adap->owner))
++                      adap = NULL;
++              up(&adapter_lock);
++
++              if (adap)
++                      break;
++
++              if (try == 0)
++                      request_module(name);
++      }
++
++      return adap;
++}
++
++/**
++ * l3_put_adapter - release a reference to an adapter
++ * @adap: driver to release reference
++ *
++ * Indicate to the L3 core that you no longer require the adapter reference.
++ * The adapter module may be unloaded when there are no references to its
++ * data structure.
++ *
++ * You must not use the reference after calling this function.
++ */
++void l3_put_adapter(struct l3_adapter *adap)
++{
++      if (adap && adap->owner)
++              __MOD_DEC_USE_COUNT(adap->owner);
++}
++
++/**
++ * l3_add_driver - register a new L3 device driver
++ * @driver - driver structure to make available
++ *
++ * Make the driver available for use by clients using name driver->name.
++ * The driver->drivers list is initialised by this function.
++ *
++ * Returns 0;
++ */
++int l3_add_driver(struct l3_driver *driver)
++{
++      down(&driver_lock);
++      list_add(&driver->drivers, &driver_list);
++      up(&driver_lock);
++      return 0;
++}
++
++/**
++ * l3_del_driver - unregister a L3 device driver
++ * @driver: driver to remove
++ *
++ * Remove an driver from the list of available L3 Bus device drivers.
++ *
++ * Returns 0;
++ */
++int l3_del_driver(struct l3_driver *driver)
++{
++      down(&driver_lock);
++      list_del(&driver->drivers);
++      up(&driver_lock);
++      return 0;
++}
++
++static struct l3_driver *__l3_get_driver(const char *name)
++{
++      struct list_head *l;
++
++      list_for_each(l, &driver_list) {
++              struct l3_driver *drv = list_entry(l, struct l3_driver, drivers);
++
++              if (strcmp(drv->name, name) == 0)
++                      return drv;
++      }
++
++      return NULL;
++}
++
++/**
++ * l3_get_driver - get a reference to a driver
++ * @name: driver name
++ *
++ * Obtain a l3_driver structure for the specified driver.  If the driver is
++ * not currently load, then load it.  The driver will be locked in core
++ * until all references are released via l3_put_driver.
++ */
++struct l3_driver *l3_get_driver(const char *name)
++{
++      struct l3_driver *drv;
++      int try;
++
++      for (try = 0; try < 2; try ++) {
++              down(&adapter_lock);
++              drv = __l3_get_driver(name);
++              if (drv && !try_inc_mod_count(drv->owner))
++                      drv = NULL;
++              up(&adapter_lock);
++
++              if (drv)
++                      break;
++
++              if (try == 0)
++                      request_module(name);
++      }
++
++      return drv;
++}
++
++/**
++ * l3_put_driver - release a reference to a driver
++ * @drv: driver to release reference
++ *
++ * Indicate to the L3 core that you no longer require the driver reference.
++ * The driver module may be unloaded when there are no references to its
++ * data structure.
++ *
++ * You must not use the reference after calling this function.
++ */
++void l3_put_driver(struct l3_driver *drv)
++{
++      if (drv && drv->owner)
++              __MOD_DEC_USE_COUNT(drv->owner);
++}
++
++/**
++ * l3_attach_client - attach a client to an adapter and driver
++ * @client: client structure to attach
++ * @adap: adapter (module) name
++ * @drv: driver (module) name
++ *
++ * Attempt to attach a client (a user of a device driver) to a particular
++ * driver and adapter.  If the specified driver or adapter aren't registered,
++ * request_module is used to load the relevant modules.
++ *
++ * Returns 0 on success, or negative error code.
++ */
++int l3_attach_client(struct l3_client *client, const char *adap, const char *drv)
++{
++      struct l3_adapter *adapter = l3_get_adapter(adap);
++      struct l3_driver  *driver = l3_get_driver(drv);
++      int ret = -ENOENT;
++
++      if (!adapter)
++              printk(KERN_ERR "%s: unable to get adapter: %s\n",
++                      __FUNCTION__, adap);
++      if (!driver)
++              printk(KERN_ERR "%s: unable to get driver: %s\n",
++                      __FUNCTION__, drv);
++
++      if (adapter && driver) {
++              ret = 0;
++
++              client->adapter = adapter;
++              client->driver  = driver;
++
++              list_add(&client->__adap, &adapter->clients);
++
++              if (driver->attach_client)
++                      ret = driver->attach_client(client);
++      }
++
++      if (ret) {
++              l3_put_driver(driver);
++              l3_put_adapter(adapter);
++      }
++      return ret;
++}
++
++/**
++ * l3_detach_client - detach a client from an adapter and driver
++ * @client: client structure to detach
++ *
++ * Detach the client from the adapter and driver.
++ */
++int l3_detach_client(struct l3_client *client)
++{
++      struct l3_adapter *adapter = client->adapter;
++      struct l3_driver  *driver = client->driver;
++
++      driver->detach_client(client);
++
++      client->adapter = NULL;
++      client->driver  = NULL;
++
++      l3_put_driver(driver);
++      l3_put_adapter(adapter);
++
++      list_del(&client->__adap);
++
++      return 0;
++}
++
++/**
++ * l3_transfer - transfer information on an L3 bus
++ * @adap: adapter structure to perform transfer on
++ * @msgs: array of l3_msg structures describing transfer
++ * @num: number of l3_msg structures
++ *
++ * Transfer the specified messages to/from a device on the L3 bus.
++ *
++ * Returns number of messages successfully transferred, otherwise negative
++ * error code.
++ */
++int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num)
++{
++      int ret = -ENOSYS;
++
++      if (adap->algo->xfer) {
++              down(adap->lock);
++              ret = adap->algo->xfer(adap, msgs, num);
++              up(adap->lock);
++      }
++      return ret;
++}
++
++/**
++ * l3_write - send data to a device on an L3 bus
++ * @client: registered client structure
++ * @addr: L3 bus address
++ * @buf: buffer for bytes to send
++ * @len: number of bytes to send
++ *
++ * Send len bytes pointed to by buf to device address addr on the L3 bus
++ * described by client.
++ *
++ * Returns the number of bytes transferred, or negative error code.
++ */
++int l3_write(struct l3_client *client, int addr, const char *buf, int len)
++{
++      struct l3_adapter *adap = client->adapter;
++      struct l3_msg msg;
++      int ret;
++
++      msg.addr = addr;
++      msg.flags = 0;
++      msg.buf = (char *)buf;
++      msg.len = len;
++
++      ret = l3_transfer(adap, &msg, 1);
++      return ret == 1 ? len : ret;
++}
++
++/**
++ * l3_read - receive data from a device on an L3 bus
++ * @client: registered client structure
++ * @addr: L3 bus address
++ * @buf: buffer for bytes to receive
++ * @len: number of bytes to receive
++ *
++ * Receive len bytes from device address addr on the L3 bus described by
++ * client to a buffer pointed to by buf.
++ *
++ * Returns the number of bytes transferred, or negative error code.
++ */
++int l3_read(struct l3_client *client, int addr, char *buf, int len)
++{
++      struct l3_adapter *adap = client->adapter;
++      struct l3_msg msg;
++      int ret;
++
++      msg.addr = addr;
++      msg.flags = L3_M_RD;
++      msg.buf = buf;
++      msg.len = len;
++
++      ret = l3_transfer(adap, &msg, 1);
++      return ret == 1 ? len : ret;
++}
++
++EXPORT_SYMBOL(l3_add_adapter);
++EXPORT_SYMBOL(l3_del_adapter);
++EXPORT_SYMBOL(l3_get_adapter);
++EXPORT_SYMBOL(l3_put_adapter);
++
++EXPORT_SYMBOL(l3_add_driver);
++EXPORT_SYMBOL(l3_del_driver);
++EXPORT_SYMBOL(l3_get_driver);
++EXPORT_SYMBOL(l3_put_driver);
++
++EXPORT_SYMBOL(l3_attach_client);
++EXPORT_SYMBOL(l3_detach_client);
++
++EXPORT_SYMBOL(l3_transfer);
++EXPORT_SYMBOL(l3_write);
++EXPORT_SYMBOL(l3_read);
+--- /dev/null
++++ linux-2.4.27/drivers/l3/l3-sa1111.c
+@@ -0,0 +1,118 @@
++/*
++ * L3 SA1111 algorithm/adapter module.
++ *
++ *  By Russell King,
++ *    gratuitously ripped from sa1111-uda1341.c by John Dorsey.
++ *
++ * 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/init.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/l3/l3.h>
++
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/mach-types.h>
++#include <asm/arch/assabet.h>
++#include <asm/hardware/sa1111.h>
++
++static inline unsigned char l3_sa1111_recv_byte(unsigned char addr)
++{
++      unsigned char dat;
++
++      L3_CAR = addr;
++      while ((SASR0 & SASR0_L3RD) == 0)
++              mdelay(1);
++      dat = L3_CDR;
++      SASCR = SASCR_RDD;
++      return dat;
++}
++
++static void l3_sa1111_recv_msg(struct l3_msg *msg)
++{
++      int len = msg->len;
++      char *p = msg->buf;
++
++      if (len > 1) {
++              SACR1 |= SACR1_L3MB;
++              while ((len--) > 1)
++                      *p++ = l3_sa1111_recv_byte(msg->addr);
++      }
++      SACR1 &= ~SACR1_L3MB;
++      *p = l3_sa1111_recv_byte(msg->addr);
++}
++
++static inline void l3_sa1111_send_byte(unsigned char addr, unsigned char dat)
++{
++      L3_CAR = addr;
++      L3_CDR = dat;
++      while ((SASR0 & SASR0_L3WD) == 0)
++              mdelay(1);
++      SASCR = SASCR_DTS;
++}
++
++static void l3_sa1111_send_msg(struct l3_msg *msg)
++{
++      int len = msg->len;
++      char *p = msg->buf;
++
++      if (len > 1) {
++              SACR1 |= SACR1_L3MB;
++              while ((len--) > 1)
++                      l3_sa1111_send_byte(msg->addr, *p++);
++      }
++      SACR1 &= ~SACR1_L3MB;
++      l3_sa1111_send_byte(msg->addr, *p);
++}
++
++static int l3_sa1111_xfer(struct l3_adapter *adap, struct l3_msg msgs[], int num)
++{
++      int i;
++
++      for (i = 0; i < num; i++) {
++              struct l3_msg *pmsg = &msgs[i];
++
++              if (pmsg->flags & L3_M_RD)
++                      l3_sa1111_recv_msg(pmsg);
++              else
++                      l3_sa1111_send_msg(pmsg);
++      }
++
++      return num;
++}
++
++static struct l3_algorithm l3_sa1111_algo = {
++      name:           "L3 SA1111 algorithm",
++      xfer:           l3_sa1111_xfer,
++};
++
++static DECLARE_MUTEX(sa1111_lock);
++
++static struct l3_adapter l3_sa1111_adapter = {
++      owner:          THIS_MODULE,
++      name:           "l3-sa1111",
++      algo:           &l3_sa1111_algo,
++      lock:           &sa1111_lock,
++};
++
++static int __init l3_sa1111_init(void)
++{
++      int ret = -ENODEV;
++      if ((machine_is_assabet() && machine_has_neponset()) ||
++          machine_is_jornada720() || machine_is_accelent_sa() ||
++          machine_is_badge4())
++              ret = l3_add_adapter(&l3_sa1111_adapter);
++      return ret;
++}
++
++static void __exit l3_sa1111_exit(void)
++{
++      l3_del_adapter(&l3_sa1111_adapter);
++}
++
++module_init(l3_sa1111_init);
++module_exit(l3_sa1111_exit);
+--- linux-2.4.27/drivers/media/video/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/media/video/Config.in
+@@ -52,5 +52,8 @@
+ if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_HIGHMEM64G" != "y" ]; then
+   dep_tristate '  Sony Vaio Picturebook Motion Eye Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_MEYE $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_SONYPI
+ fi
++# unfortunately, this depends on having CONFIG_FB_CYBER2000
++# set as well - we hook off of the VGA driver
++dep_tristate '  NetWinder Video for Linux (EXPERIMENTAL)' CONFIG_VIDEO_CYBERPRO $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL $CONFIG_ARCH_NETWINDER
+ endmenu
+--- linux-2.4.27/drivers/media/video/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/media/video/Makefile
+@@ -16,7 +16,7 @@
+ obj-n         :=
+ obj-          :=
+-SUB_DIRS     := 
++SUB_DIRS     :=
+ MOD_SUB_DIRS := $(SUB_DIRS)
+ ALL_SUB_DIRS := $(SUB_DIRS)
+@@ -47,7 +47,8 @@
+ obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o i2c-old.o
+ obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o
+ obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o
+-obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o
++obj-$(CONFIG_VIDEO_CYBERPRO) += cyberpro.o i2c-old.o saa7111.o
++obj-$(CONFIG_VIDEO_LML33) += bt856.o bt819.o
+ obj-$(CONFIG_VIDEO_PMS) += pms.o
+ obj-$(CONFIG_VIDEO_PLANB) += planb.o
+ obj-$(CONFIG_VIDEO_VINO) += saa7191.o indycam.o vino.o
+--- /dev/null
++++ linux-2.4.27/drivers/media/video/cyberpro.c
+@@ -0,0 +1,2091 @@
++/*
++ * CyberPro 2000 video capture driver for the Rebel.com NetWinder
++ *
++ * (C) 1999-2000 Russell King
++ *
++ *  Re-written from Rebel.com's vidcap driver.
++ *
++ * Architecture
++ * ------------
++ *  The NetWinder video capture consists of a SAA7111 video decoder chip
++ *  connected to the CyberPro feature bus.  The video data is captured to
++ *  the VGA memory, where the CyberPro can overlay (by chromakeying) the
++ *  data onto the VGA display.
++ *
++ *  The CyberPro also has some nifty features, including a second overlay
++ *  and picture in picture mode.  We do not currently use these features.
++ *
++ * Power Saving
++ * ------------
++ *  Please note that rev.5 NetWinders have the ability to hold the SAA7111
++ *  decoder chip into reset, which saves power.  The only time at which
++ *  this is done is when the driver is unloaded, which implies that this
++ *  is compiled as a module.
++ *
++ *  In this case, you will want the kernel to automatically load this
++ *  driver when required.  Place the following line in /etc/modules.conf
++ *  to enable this:
++ *
++ *   alias char-major-81-0 cyberpro
++ *
++ *  The relevant modules will be automatically loaded by modprobe on a
++ *  as and when needed basis.
++ *
++ * Capture resolution
++ * ------------------
++ *  The maximum useful capture resolution is:
++ *     625-line UK: 716x576
++ *     525-line US: ?
++ *
++ * Bugs
++ * ----
++ *  1. The CyberPro chip seems to be prone to randomly scribbling over VGA
++ *     memory [hopefully fixed with new capture enable/freeze stuff]
++ *  2. read()ing pauses video capture, and sometimes triggers bug 1.
++ *  3. mmap() is not supported (requires BM-DMA - see bug 4)
++ *  4. Really, we want to do scatter BM-DMA.  Is the CyberPro capable of this?
++ *     The Cyberpro seems to randomly scribble to various PCI addresses if you
++ *     transfer >16 words.
++ *  5. We shouldn't ignore O_NONBLOCK when reading a frame.
++ *  6. The incoming stream on the NetWinder is CCIR656, which is YUV422.
++ *     CyberPro docs also call the format we capture and overlay "YUV422",
++ *     but we actually seem to have Y, U, Y, V bytes (is this YUYV format?)
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/videodev.h>
++#include <linux/video_decoder.h>
++#include <linux/mm.h>
++#include <linux/i2c-old.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/kmod.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/pgtable.h>
++#include <asm/pgalloc.h>
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("CyberPro v4l video grabber");
++MODULE_LICENSE("GPL");
++
++#include "../../video/cyber2000fb.h"
++
++/*
++ * These enable various experimental features.  Most of these
++ * are just plain broken or just don't work at the moment.
++ */
++/*
++ * Enable this if you want mmap() access. (see bug 4)
++ */
++#undef USE_MMAP
++
++/*
++ * Enable this if you want mmio access. (slow)
++ */
++#define USE_MMIO
++
++/*
++ * The V4L API is unclear whether VIDIOCSCAPTURE call is allowed while
++ * capture is running.  The default is to disallow the call.
++ *
++ * Define this if you do want to allow the call while capture is active.
++ */
++#undef        ALLOW_SCAPTURE_WHILE_CAP
++
++/*
++ * We capture two frames
++ */
++#define NR_FRAMES     2
++
++/*
++ * One frame of video is 202 pages, assuming YUV422 format, 716x576
++ */
++#define NR_PAGES      202
++
++struct src_info {
++      unsigned int    offset;         /* offset of source data        */
++      unsigned int    x;              /* source x                     */
++      unsigned int    y;              /* source y                     */
++      unsigned int    width;          /* source width                 */
++      unsigned int    height;         /* source height                */
++      unsigned int    format;         /* source format                */
++};
++
++struct dst_info {
++      unsigned int    x;              /* destination x                */
++      unsigned int    y;              /* destination y                */
++      unsigned int    width;          /* destination width            */
++      unsigned int    height;         /* destination height           */
++      unsigned int    chromakey;      /* chromakey                    */
++      unsigned int    flags;          /* flags (eg, chromakey enable) */
++};
++
++struct cyberpro_vidinfo;
++
++struct win_info {
++      void (*init)(struct cyberpro_vidinfo *dp, struct win_info *wi);
++      void (*set_src)(struct cyberpro_vidinfo *dp, struct win_info *wi);
++      void (*set_win)(struct cyberpro_vidinfo *dp, struct win_info *wi);
++      void (*ctl)(struct cyberpro_vidinfo *dp, struct win_info *wi, int on_off);
++
++      /* public */
++      struct src_info src;
++      struct dst_info dst;
++
++      /* private */
++      unsigned short  vid_fifo_ctl;
++      unsigned char   vid_fmt;
++      unsigned char   vid_disp_ctl1;
++      unsigned char   vid_fifo_ctl1;
++      unsigned char   vid_misc_ctl1;
++};
++
++struct framebuf {
++      unsigned int            offset;         /* mmap offset for this frame   */
++      unsigned int            status;
++#define FRAME_FREE    0
++#define FRAME_DONE    1
++#define FRAME_WAITING 2
++#define FRAME_GRABBING        3
++
++      /*
++       * Bus-Master DMA stuff.  Note that we should
++       * probably use the kiovec stuff instead.
++       */
++      unsigned long           bus_addr[NR_PAGES];     /* list of pages                */
++      struct page             *pages[NR_PAGES];
++      void                    *buffer;
++      int dbg;
++};
++
++struct cyberpro_vidinfo {
++      struct video_device     *dev;
++      struct i2c_bus          *bus;
++      struct cyberpro_info    info;           /* host information             */
++      unsigned char           *regs;
++      unsigned int            irq;            /* PCI interrupt number         */
++
++      /* hardware configuration */
++      unsigned int            stream_fmt;     /* format of stream from decoder*/
++
++      /* software settings */
++      unsigned int            decoder:1;      /* decoder loaded               */
++      unsigned int            interlace:1;    /* interlace                    */
++      unsigned int            buf_set:1;      /* VIDIOCSFBUF has been issued  */
++      unsigned int            win_set:1;      /* VIDIOCSWIN has been issued   */
++      unsigned int            cap_active:1;   /* capture is active            */
++      unsigned int            ovl_active:1;   /* overlay is active            */
++      unsigned int            mmaped:1;       /* buffer is mmap()d            */
++      unsigned int            unused:25;
++
++      unsigned int            users;          /* number of users              */
++      unsigned long           cap_mem_offset; /* capture framebuffer offset   */
++      void *                  buffer;         /* kernel capture buffer        */
++      unsigned int            norm;           /* video standard               */
++
++      struct video_capability cap;            /* capabilities                 */
++      struct video_picture    pic;            /* current picture settings     */
++      struct video_buffer     buf;            /* display parameters           */
++      struct video_capture    capt;           /* video capture params         */
++
++      struct win_info         *ovl;           /* overlay window set           */
++      struct win_info         ext;            /* "Extended" window info       */
++      struct win_info         v2;             /* "V2" window info             */
++      struct win_info         x2;             /* "X2" window info             */
++
++      unsigned int            bm_offset;      /* Cap memory bus master offset */
++      unsigned int            bm_index;       /* Cap page index               */
++
++#ifdef USE_MMAP
++      unsigned int            frame_idx;      /* currently grabbing frame     */
++      unsigned int            frame_size;
++      struct framebuf frame[NR_FRAMES];
++      wait_queue_head_t       frame_wait;
++#endif
++
++      wait_queue_head_t       vbl_wait;
++
++      /*
++       * cyberpro registers
++       */
++      unsigned char   cap_mode1;
++      unsigned char   cap_mode2;
++      unsigned char   cap_miscctl;
++      unsigned char   vfac1;
++      unsigned char   vfac3;
++};
++
++/*
++ * Our access methods.
++ */
++#define cyberpro_writel(val,reg,dp)   writel(val, (dp)->regs + (reg))
++#define cyberpro_writew(val,reg,dp)   writew(val, (dp)->regs + (reg))
++#define cyberpro_writeb(val,reg,dp)   writeb(val, (dp)->regs + (reg))
++
++#define cyberpro_readb(reg,dp)        readb((dp)->regs + (reg))
++
++static inline void
++cyberpro_grphw(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp)
++{
++      cyberpro_writew((reg & 255) | val << 8, 0x3ce, dp);
++}
++
++static void cyberpro_grphw8(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp)
++{
++      cyberpro_grphw(reg, val, dp);
++}
++
++static unsigned char cyberpro_grphr8(int reg, struct cyberpro_vidinfo *dp)
++{
++      cyberpro_writeb(reg, 0x3ce, dp);
++      return cyberpro_readb(0x3cf, dp);
++}
++
++static void cyberpro_grphw16(int reg, unsigned int val, struct cyberpro_vidinfo *dp)
++{
++      cyberpro_grphw(reg, val, dp);
++      cyberpro_grphw(reg + 1, val >> 8, dp);
++}
++
++static void cyberpro_grphw24(int reg, unsigned int val, struct cyberpro_vidinfo *dp)
++{
++      cyberpro_grphw(reg, val, dp);
++      cyberpro_grphw(reg + 1, val >> 8, dp);
++      cyberpro_grphw(reg + 2, val >> 16, dp);
++}
++
++#if 0
++static void
++cyberpro_dbg_dump(void)
++{
++      int i;
++      unsigned char idx[] =
++              { 0x30, 0x3e, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d,
++                0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad };
++      printk(KERN_DEBUG);
++      for (i = 0; i < sizeof(idx); i++)
++              printk("%02x ", idx[i]);
++      printk("\n" KERN_DEBUG);
++      for (i = 0; i < sizeof(idx); i++)
++              printk("%02x ", cyberpro_grphr8(idx[i]));
++      printk("\n");
++}
++#endif
++
++/*
++ * On the NetWinder, we can put the SAA7111 to sleep by holding
++ * it in reset.
++ *
++ * Note: once we have initialised the SAA7111, we can't put it back to
++ * sleep and expect it to keep its settings.  Maybe a better solution
++ * is to register/de-register the i2c bus in open/release?
++ */
++static void
++decoder_sleep(int sleep)
++{
++#ifdef CONFIG_ARCH_NETWINDER
++      extern spinlock_t gpio_lock;
++
++      spin_lock_irq(&gpio_lock);
++      cpld_modify(CPLD_7111_DISABLE, sleep ? CPLD_7111_DISABLE : 0);
++      spin_unlock_irq(&gpio_lock);
++
++      if (!sleep) {
++              /*
++               * wait 20ms for device to wake up
++               */
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              schedule_timeout(HZ / 50);
++      }
++#endif
++}
++
++/* -------------------------------- I2C support ---------------------------- */
++
++#define I2C_DELAY     100
++
++static void
++cyberpro_i2c_setlines(struct i2c_bus *bus, int ctrl, int data)
++{
++      struct cyberpro_vidinfo *dp = bus->data;
++      int v;
++
++      v = (ctrl ? EXT_LATCH2_I2C_CLKEN : 0x00) | (data ? EXT_LATCH2_I2C_DATEN : 0x00);
++      cyberpro_grphw8(EXT_LATCH2, v, dp);
++
++      udelay(I2C_DELAY);
++}
++
++static int
++cyberpro_i2c_getdataline(struct i2c_bus *bus)
++{
++      struct cyberpro_vidinfo *dp = bus->data;
++      unsigned long flags;
++      int v;
++
++      save_flags(flags);
++      cli();
++
++      v = cyberpro_grphr8(EXT_LATCH2, dp);
++
++      restore_flags(flags);
++
++      return v & EXT_LATCH2_I2C_DAT ? 1 : 0;
++}
++
++static void
++cyberpro_i2c_attach(struct i2c_bus *bus, int id)
++{
++      struct cyberpro_vidinfo *dp = bus->data;
++      int zero = 0;
++
++      if (id == I2C_DRIVERID_VIDEODECODER) {
++              __u16 norm = dp->norm;
++              i2c_control_device(bus, id, DECODER_SET_NORM, &norm);
++              i2c_control_device(bus, id, DECODER_SET_PICTURE, &dp->pic);
++              i2c_control_device(bus, id, DECODER_ENABLE_OUTPUT, &zero);
++
++              dp->decoder = 1;
++      }
++}
++
++static void
++cyberpro_i2c_detach(struct i2c_bus *bus, int id)
++{
++      struct cyberpro_vidinfo *dp = bus->data;
++
++      if (id == I2C_DRIVERID_VIDEODECODER)
++              dp->decoder = 0;
++}
++
++static struct i2c_bus cyberpro_i2c_bus = {
++      name:                   "",
++      id:                     I2C_BUSID_CYBER2000,
++      bus_lock:               SPIN_LOCK_UNLOCKED,
++      attach_inform:          cyberpro_i2c_attach,
++      detach_inform:          cyberpro_i2c_detach,
++      i2c_setlines:           cyberpro_i2c_setlines,
++      i2c_getdataline:        cyberpro_i2c_getdataline,
++};
++
++/*------------------------- Extended Overlay Window -------------------------
++ * Initialise 1st overlay window (works)
++ */
++static void
++cyberpro_ext_init(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      wi->vid_fifo_ctl  = 0xf87c;
++      wi->vid_fmt       = EXT_VID_FMT_YUV422;
++      wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF |
++                          EXT_VID_DISP_CTL1_NOCLIP;
++      wi->vid_fifo_ctl1 = EXT_VID_FIFO_CTL1_INTERLEAVE |
++                          EXT_VID_FIFO_CTL1_OE_HIGH;
++      wi->vid_misc_ctl1 = 0;
++
++      cyberpro_grphw8 (EXT_VID_DISP_CTL1,  wi->vid_disp_ctl1, dp);
++      cyberpro_grphw16(EXT_DDA_X_INIT,     0x0800, dp);
++      cyberpro_grphw16(EXT_DDA_Y_INIT,     0x0800, dp);
++      cyberpro_grphw16(EXT_VID_FIFO_CTL,   wi->vid_fifo_ctl, dp);
++      cyberpro_grphw8 (EXT_VID_FIFO_CTL1,  wi->vid_fifo_ctl1, dp);
++}
++
++/*
++ * Set the source parameters for the extended window
++ */
++static void
++cyberpro_ext_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int phase, pitch;
++
++      pitch = (wi->src.width >> 2) & 0x0fff;
++      phase = (wi->src.width + 3) >> 2;
++
++      wi->vid_fmt &= ~7;
++      switch (wi->src.format) {
++      case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565;    break;
++      case VIDEO_PALETTE_RGB24:  wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break;
++      case VIDEO_PALETTE_RGB32:  wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break;
++      case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555;    break;
++      case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422;    break;
++      }
++
++      cyberpro_grphw24(EXT_MEM_START, wi->src.offset, dp);
++      cyberpro_grphw16(EXT_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp);
++      cyberpro_grphw8 (EXT_SRC_WIN_WIDTH, phase, dp);
++      cyberpro_grphw8 (EXT_VID_FMT, wi->vid_fmt, dp);
++}
++
++/*
++ * Set overlay1 window
++ */
++static void
++cyberpro_ext_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int xscale, yscale;
++      unsigned int xoff, yoff;
++
++      /*
++       * Note: the offset does not appear to be influenced by
++       * hardware scrolling.
++       */
++      xoff = yoff = 0;
++
++      xoff += wi->dst.x;
++      yoff += wi->dst.y;
++
++      xscale = wi->src.width;
++
++      if (wi->dst.width >= wi->src.width * 2) {
++              wi->vid_fmt |= EXT_VID_FMT_DBL_H_PIX;
++              xscale *= 2;
++      } else {
++              wi->vid_fmt &= ~EXT_VID_FMT_DBL_H_PIX;
++      }
++
++      xscale = ((xscale - /*2*/0) * 4096) / wi->dst.width;
++      yscale = ((wi->src.height - /*2*/0) * 4096) / wi->dst.height;
++
++      cyberpro_grphw16(EXT_X_START, xoff, dp);
++      cyberpro_grphw16(EXT_X_END,   xoff + wi->dst.width, dp);
++      cyberpro_grphw16(EXT_Y_START, yoff, dp);
++      cyberpro_grphw16(EXT_Y_END,   yoff + wi->dst.height, dp);
++      cyberpro_grphw24(EXT_COLOUR_COMPARE, wi->dst.chromakey, dp);
++      cyberpro_grphw16(EXT_DDA_X_INC, xscale, dp);
++      cyberpro_grphw16(EXT_DDA_Y_INC, yscale, dp);
++      cyberpro_grphw8(EXT_VID_FMT, wi->vid_fmt, dp);
++
++      if (wi->dst.flags & VIDEO_WINDOW_CHROMAKEY)
++              wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_IGNORE_CCOMP;
++      else
++              wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_IGNORE_CCOMP;
++}
++
++/*
++ * Enable or disable the 1st overlay window.  Note that for anything
++ * useful to be displayed, we must have capture enabled.
++ */
++static void
++cyberpro_ext_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on)
++{
++      if (on)
++              wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++      else
++              wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++
++      cyberpro_grphw8(EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);
++}
++
++/*------------------------------- V2 Overlay Window -------------------------
++ * Initialise 2nd overlay window (guesswork)
++ */
++static void
++cyberpro_v2_init(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      wi->vid_fifo_ctl  = 0xf87c;
++      wi->vid_fmt       = EXT_VID_FMT_YUV422;
++      wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF |
++                          EXT_VID_DISP_CTL1_NOCLIP;
++      wi->vid_fifo_ctl1 = 0x06;
++      wi->vid_misc_ctl1 = 0;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp);
++      cyberpro_grphw8 (Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);
++      /* No DDA init values */
++      cyberpro_grphw16(Y_V2_VID_FIFO_CTL,  wi->vid_fifo_ctl, dp);
++      cyberpro_grphw8 (Y_V2_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp);
++}
++
++/*
++ * Set the source parameters for the v2 window
++ */
++static void
++cyberpro_v2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int phase, pitch;
++
++      pitch = (wi->src.width >> 2) & 0x0fff;
++      phase = (wi->src.width + 3) >> 2;
++
++      wi->vid_fmt &= ~7;
++      switch (wi->src.format) {
++      case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565;    break;
++      case VIDEO_PALETTE_RGB24:  wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break;
++      case VIDEO_PALETTE_RGB32:  wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break;
++      case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555;    break;
++      case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422;    break;
++      }
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_X, dp);
++      cyberpro_grphw24(X_V2_VID_MEM_START, wi->src.offset, dp);
++      cyberpro_grphw16(X_V2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp);
++      cyberpro_grphw8 (X_V2_VID_SRC_WIN_WIDTH, phase, dp);
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp);
++      cyberpro_grphw8(Y_V2_VID_FMT, wi->vid_fmt, dp);
++}
++
++/*
++ * Set v2 window
++ */
++static void
++cyberpro_v2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int xscale, yscale;
++      unsigned int xoff, yoff;
++
++      /*
++       * Note: the offset does not appear to be influenced by
++       * hardware scrolling.
++       */
++      xoff = yoff = 0;
++
++      xoff += wi->dst.x;
++      yoff += wi->dst.y;
++
++      xscale = (wi->src.width  * 4096) / wi->dst.width;
++      yscale = (wi->src.height * 4096) / wi->dst.height;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_X, dp);
++      cyberpro_grphw16(X_V2_X_START,  xoff, dp);
++      cyberpro_grphw16(X_V2_X_END,    xoff + wi->dst.width, dp);
++      cyberpro_grphw16(X_V2_Y_START,  yoff, dp);
++      cyberpro_grphw16(X_V2_Y_END,    yoff + wi->dst.height, dp);
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp);
++      cyberpro_grphw16(Y_V2_DDA_X_INC, xscale, dp);
++      cyberpro_grphw16(Y_V2_DDA_Y_INC, yscale, dp);
++}
++
++/*
++ * Enable or disable the 2nd overlay window.  Note that for anything
++ * useful to be displayed, we must have capture enabled.
++ */
++static void
++cyberpro_v2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on)
++{
++      if (on)
++              wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++      else
++              wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp);
++      cyberpro_grphw8(Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);
++}
++
++/*--------------------------- X2 Overlay Window -----------------------------
++ * Initialise 3rd overlay window (guesswork)
++ */
++static void
++cyberpro_x2_init(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      wi->vid_fmt       = EXT_VID_FMT_YUV422;
++      wi->vid_disp_ctl1 = 0x40;
++      wi->vid_misc_ctl1 = 0;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_K, dp);
++      cyberpro_grphw8 (K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);
++      cyberpro_grphw16(K_X2_DDA_X_INIT, 0x0800, dp);
++      cyberpro_grphw16(K_X2_DDA_Y_INIT, 0x0800, dp);
++}
++
++/*
++ * Set the source parameters for the x2 window
++ */
++static void
++cyberpro_x2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int phase, pitch;
++
++      pitch = (wi->src.width >> 2) & 0x0fff;
++      phase = (wi->src.width + 3) >> 2;
++
++      wi->vid_fmt &= ~7;
++      switch (wi->src.format) {
++      case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565;    break;
++      case VIDEO_PALETTE_RGB24:  wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break;
++      case VIDEO_PALETTE_RGB32:  wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break;
++      case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555;    break;
++      case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422;    break;
++      }
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_J, dp);
++      cyberpro_grphw24(J_X2_VID_MEM_START, wi->src.offset, dp);
++      cyberpro_grphw16(J_X2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp);
++      cyberpro_grphw8 (J_X2_VID_SRC_WIN_WIDTH, phase, dp);
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_K, dp);
++      cyberpro_grphw8(K_X2_VID_FMT, wi->vid_fmt, dp);
++}
++
++/*
++ * Set x2 window
++ */
++static void
++cyberpro_x2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi)
++{
++      unsigned int xscale, yscale;
++      unsigned int xoff, yoff;
++
++      /*
++       * Note: the offset does not appear to be influenced by
++       * hardware scrolling.
++       */
++      xoff = yoff = 0;
++
++      xoff += wi->dst.x;
++      yoff += wi->dst.y;
++
++      xscale = (wi->src.width  * 4096) / wi->dst.width;
++      yscale = (wi->src.height * 4096) / wi->dst.height;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_J, dp);
++      cyberpro_grphw16(J_X2_X_START,  xoff, dp);
++      cyberpro_grphw16(J_X2_X_END,    xoff + wi->dst.width, dp);
++      cyberpro_grphw16(J_X2_Y_START,  yoff, dp);
++      cyberpro_grphw16(J_X2_Y_END,    yoff + wi->dst.height, dp);
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_K, dp);
++      cyberpro_grphw16(K_X2_DDA_X_INC, xscale, dp);
++      cyberpro_grphw16(K_X2_DDA_Y_INC, yscale, dp);
++}
++
++/*
++ * Enable or disable the 3rd overlay window.  Note that for anything
++ * useful to be displayed, we must have capture enabled.
++ */
++static void
++cyberpro_x2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on)
++{
++      if (on)
++              wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++      else
++              wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW;
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_K, dp);
++      cyberpro_grphw8(K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);
++}
++
++/* ------------------------------------------------------------------------- */
++
++#if 0
++static void reset_seq(struct cyberpro_vidinfo *dp)
++{
++      unsigned char ext_mem_ctl = cyberpro_grphr8(0x70, dp);
++
++      cyberpro_grphw8(ext_mem_ctl | 0x80, 0x70, dp);
++      cyberpro_grphw8(ext_mem_ctl, 0x70, dp);
++}
++#endif
++
++#ifdef USE_MMAP
++/*
++ * Buffer support
++ */
++static int
++cyberpro_alloc_frame_buffer(struct cyberpro_vidinfo *dp,
++                          struct framebuf *frame)
++{
++      unsigned long addr;
++      void *buffer;
++      int pgidx;
++
++      if (frame->buffer)
++              return 0;
++
++      /*
++       * Allocate frame buffer
++       */
++      buffer = vmalloc(NR_PAGES * PAGE_SIZE);
++
++      if (frame->buffer) {
++              vfree(buffer);
++              return 0;
++      }
++
++      if (!buffer)
++              return -ENOMEM;
++
++      printk("Buffer allocated @ %p [", buffer);
++
++      frame->buffer = buffer;
++      frame->dbg = 1;
++
++      /*
++       * Don't leak information from the kernel.
++       */
++      memset(buffer, 0x5a, NR_PAGES * PAGE_SIZE);
++
++      /*
++       * Now, reserve all the pages, and calculate
++       * each pages' bus address.
++       */
++      addr = (unsigned long)buffer;
++      for (pgidx = 0; pgidx < NR_PAGES; pgidx++, addr += PAGE_SIZE) {
++              struct page *page;
++              pgd_t *pgd;
++              pmd_t *pmd;
++              pte_t *pte;
++
++              /*
++               * The page should be present.  If not,
++               * vmalloc has gone nuts.
++               */
++              pgd = pgd_offset_k(addr);
++              if (pgd_none(*pgd))
++                      BUG();
++              pmd = pmd_offset(pgd, addr);
++              if (pmd_none(*pmd))
++                      BUG();
++              pte = pte_offset(pmd, addr);
++              if (!pte_present(*pte))
++                      BUG();
++
++              page = pte_page(*pte);
++
++              frame->bus_addr[pgidx] = virt_to_bus((void *)page_address(page));
++              frame->pages[pgidx] = page;
++              SetPageReserved(page);
++
++              printk("%08lx (%08lx) ", page_address(page), frame->bus_addr[pgidx]);
++      }
++      printk("\n");
++
++      return 0;
++}
++
++static void
++cyberpro_frames_free_one(struct cyberpro_vidinfo *dp, struct framebuf *frame)
++{
++      void *buffer;
++      int pgidx;
++
++      frame->status = FRAME_FREE;
++      buffer = frame->buffer;
++      frame->buffer = NULL;
++
++      if (buffer) {
++              for (pgidx = 0; pgidx < NR_PAGES; pgidx++) {
++                      frame->bus_addr[pgidx] = 0;
++                      ClearPageReserved(frame->pages[pgidx]);
++                      frame->pages[pgidx] = NULL;
++              }
++              vfree(buffer);
++      }
++}
++
++static void
++cyberpro_busmaster_frame(struct cyberpro_vidinfo *dp, struct framebuf *frame)
++{
++      unsigned long bus_addr;
++
++      bus_addr = frame->bus_addr[dp->bm_index];
++
++      if (frame->dbg) {
++              printk("Frame%d: %06x -> %08lx\n",
++                      dp->frame_idx,
++                      dp->bm_offset,
++                      bus_addr);
++      }
++
++      cyber2000_outw(dp->bm_offset, BM_VID_ADDR_LOW);
++      cyber2000_outw(dp->bm_offset >> 16, BM_VID_ADDR_HIGH);
++
++      cyber2000_outw(bus_addr, BM_ADDRESS_LOW);
++      cyber2000_outw(bus_addr >> 16, BM_ADDRESS_HIGH);
++
++      /*
++       * One page-full only
++       */
++      cyber2000_outw(1023, BM_LENGTH);
++
++      /*
++       * Load length
++       */
++      cyber2000_outw(BM_CONTROL_INIT, BM_CONTROL);
++
++      /*
++       * Enable transfer
++       */
++      cyber2000_outw(BM_CONTROL_ENABLE|BM_CONTROL_IRQEN, BM_CONTROL);
++
++      dp->bm_offset += 1024;
++      dp->bm_index += 1;
++}
++
++static void cyberpro_busmaster_interrupt(struct cyberpro_vidinfo *dp)
++{
++      struct framebuf *frame = dp->frame + dp->frame_idx;
++
++      /*
++       * Disable Busmaster operations
++       */
++      cyber2000_outw(0, BM_CONTROL);
++
++      if (frame->status == FRAME_GRABBING) {
++              /*
++               * We are still grabbing this frame to system
++               * memory.  Transfer next page if there are
++               * more, or else flag this frame as complete.
++               */
++              if (dp->bm_index < NR_PAGES)
++                      cyberpro_busmaster_frame(dp);
++              else {
++                      unsigned int idx;
++
++                      frame->status = FRAME_DONE;
++                      frame->dbg = 0;
++
++                      idx = dp->frame_idx + 1;
++                      if (idx >= NR_FRAMES)
++                              idx = 0;
++
++                      dp->frame_idx = idx;
++
++                      wake_up(&dp->frame_wait);
++              }
++      }
++}
++
++static void cyberpro_frames_vbl(struct cyberpro_vidinfo *dp, unsigned int stat)
++{
++      struct framebuf *frame = dp->frame + dp->frame_idx;
++
++      /*
++       * No point capturing frames if the grabber isn't active.
++       */
++      if (stat & EXT_ROM_UCB4GH_FREEZE)
++              return;
++
++      /*
++       * If the next buffer is ready for grabbing,
++       * set up the bus master registers for the
++       * transfer.
++       */
++      if (frame->status == FRAME_WAITING) {
++              frame->status = FRAME_GRABBING;
++
++              dp->bm_offset = dp->cap_mem_offset;
++              dp->bm_index  = 0;
++
++              cyberpro_busmaster_frame(dp, frame);
++      }
++}
++
++static void __init cyberpro_frames_init(struct cyberpro_vidinfo *dp)
++{
++      unsigned int offset, maxsize;
++      int i;
++
++      init_waitqueue_head(&dp->frame_wait);
++
++      maxsize = 2 * dp->cap.maxwidth * dp->cap.maxheight;
++      dp->frame_size = PAGE_ALIGN(maxsize);
++      dp->frame_idx = 0;
++
++      for (i = offset = 0; i < NR_FRAMES; i++) {
++              dp->frame[i].offset = offset;
++              dp->frame[i].status = FRAME_FREE;
++              offset += dp->frame_size;
++      }
++}
++
++static void cyberpro_frames_free(struct cyberpro_vidinfo *dp)
++{
++      int i;
++
++      dp->mmaped = 0;
++
++      /*
++       * Free all frame buffers
++       */
++      for (i = 0; i < NR_FRAMES; i++)
++              cyberpro_frames_free_one(dp, dp->frame + i);
++}
++
++#else
++#define cyberpro_frames_vbl(dp,stat) do { } while (0)
++#define cyberpro_frames_init(dp)     do { } while (0)
++#define cyberpro_frames_free(dp)     do { } while (0)
++#endif
++
++/*
++ * CyberPro Interrupts
++ * -------------------
++ *
++ *  We don't really know how to signal an IRQ clear to the chip.  However,
++ *  disabling and re-enabling the capture interrupt enable seems to do what
++ *  we want.
++ */
++static void cyberpro_interrupt(int nr, void *dev_id, struct pt_regs *regs)
++{
++      struct cyberpro_vidinfo *dp = dev_id;
++      unsigned char old_grphidx;
++      unsigned int status;
++
++      /*
++       * Save old graphics index register
++       */
++      old_grphidx = cyberpro_readb(0x3ce, dp);
++
++      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++
++      /*
++       * Was it due to the Capture VSYNC?
++       */
++      if (status & EXT_ROM_UCB4GH_INTSTAT) {
++              /*
++               * Frob the IRQ enable bit to drop the request.
++               */
++              cyberpro_grphw8(VFAC_CTL3, dp->vfac3 & ~VFAC_CTL3_CAP_IRQ, dp);
++              cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp);
++
++              cyberpro_frames_vbl(dp, status);
++              wake_up(&dp->vbl_wait);
++      }
++
++      /*
++       * Restore graphics controller index
++       */
++      cyberpro_writeb(old_grphidx, 0x3ce, dp);
++
++#ifdef USE_MMAP
++      /*
++       * Do Bus-Master IRQ stuff
++       */
++      if (cyber2000_inb(BM_CONTROL) & (1 << 7))
++              cyberpro_busmaster_interrupt(dp);
++#endif
++}
++
++static void cyberpro_capture(struct cyberpro_vidinfo *dp, int on)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      unsigned int status;
++
++      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++
++      add_wait_queue(&dp->vbl_wait, &wait);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++
++      if (!!on ^ !(status & EXT_ROM_UCB4GH_FREEZE)) {
++              if (on) {
++                      schedule_timeout(40 * HZ / 1000);
++                      dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC);
++                      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++
++                      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++              } else {
++                      dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC;
++                      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++
++                      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++                      if (!(status & EXT_ROM_UCB4GH_FREEZE))
++                              schedule_timeout(40 * HZ / 1000);
++              }
++      }
++
++      current->state = TASK_RUNNING;
++      remove_wait_queue(&dp->vbl_wait, &wait);
++}
++
++static void cyberpro_capture_one(struct cyberpro_vidinfo *dp)
++{
++      struct task_struct *tsk = current;
++      DECLARE_WAITQUEUE(wait, tsk);
++      unsigned int status;
++      unsigned long policy, rt_priority;
++
++      policy = tsk->policy;
++      rt_priority = tsk->rt_priority;
++
++      tsk->policy = SCHED_FIFO;
++      tsk->rt_priority = 1;
++
++      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++
++      add_wait_queue(&dp->vbl_wait, &wait);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++
++      schedule_timeout(40 * HZ / 1000);
++      dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC);
++      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++
++      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      schedule_timeout(40 * HZ / 1000);
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      schedule_timeout(40 * HZ / 1000);
++
++      dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC;
++      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp);
++
++      current->state = TASK_RUNNING;
++      remove_wait_queue(&dp->vbl_wait, &wait);
++
++      tsk->policy = policy;
++      tsk->rt_priority = rt_priority;
++}
++
++static void cyberpro_capture_set_win(struct cyberpro_vidinfo *dp)
++{
++      unsigned int xstart, xend, ystart, yend;
++
++      xstart = 4 + dp->capt.x;
++      xend   = xstart + dp->capt.width;
++
++      if (dp->cap_mode1 & EXT_CAP_MODE1_8BIT) {
++              /* 8-bit capture */
++              xstart *= 2;
++              xend   *= 2;
++      }
++
++      xstart -= 1;
++      xend   -= 1;
++
++      ystart = 18 + dp->capt.y;
++      yend   = ystart + dp->capt.height / 2;
++
++      cyberpro_grphw16(CAP_X_START, xstart, dp);
++      cyberpro_grphw16(CAP_X_END, xend + 1, dp);
++      cyberpro_grphw16(CAP_Y_START, ystart, dp);
++      cyberpro_grphw16(CAP_Y_END, yend + 2, dp);
++
++      /*
++       * This should take account of capt.decimation
++       */
++      cyberpro_grphw16(CAP_DDA_X_INIT, 0x0800, dp);
++      cyberpro_grphw16(CAP_DDA_X_INC, 0x1000, dp);
++      cyberpro_grphw16(CAP_DDA_Y_INIT, 0x0800, dp);
++      cyberpro_grphw16(CAP_DDA_Y_INC, 0x1000, dp);
++
++      cyberpro_grphw8(CAP_PITCH, dp->capt.width >> 2, dp);
++}
++
++static void cyberpro_set_interlace(struct cyberpro_vidinfo *dp)
++{
++      /*
++       * set interlace mode
++       */
++      if (dp->interlace) {
++              dp->vfac3 |= VFAC_CTL3_CAP_INTERLACE;
++              dp->cap_miscctl &= ~CAP_CTL_MISC_ODDEVEN;
++              dp->ovl->src.height = dp->capt.height;
++      } else {
++              dp->vfac3 &= ~VFAC_CTL3_CAP_INTERLACE;
++              dp->cap_miscctl |= CAP_CTL_MISC_ODDEVEN;
++              dp->ovl->src.height = dp->capt.height / 2;
++      }
++
++      cyberpro_grphw8(VFAC_CTL3,    dp->vfac3, dp);
++      cyberpro_grphw8(CAP_CTL_MISC, dp->cap_miscctl, dp);
++
++      dp->ovl->set_src(dp, dp->ovl);
++
++      if (dp->win_set)
++              dp->ovl->set_win(dp, dp->ovl);
++}
++
++/*
++ * Calculate and set the address of the capture buffer.  Note we
++ * also update the extended memory buffer for the overlay window.
++ *
++ *  base:         phys base address of display
++ *  width:        pixel width of display
++ *  height:       height of display
++ *  depth:        depth of display (8/16/24)
++ *  bytesperline: number of bytes on a line
++ *
++ * We place the capture buffer 16K after the screen.
++ */
++static int
++cyberpro_set_buffer(struct cyberpro_vidinfo *dp, struct video_buffer *b)
++{
++      unsigned long screensize, maxbufsz;
++
++      if (b->height <= 0 || b->width <= 0 || b->bytesperline <= 0)
++              return -EINVAL;
++
++      maxbufsz = dp->cap.maxwidth * dp->cap.maxheight * 2;
++      screensize = b->height * b->bytesperline + 16384;
++
++      if ((screensize + maxbufsz) >= dp->info.fb_size)
++              return -EINVAL;
++
++      dp->buf.base         = b->base;
++      dp->buf.width        = b->width;
++      dp->buf.height       = b->height;
++      dp->buf.depth        = b->depth;
++      dp->buf.bytesperline = b->bytesperline;
++      dp->cap_mem_offset   = screensize >> 2;
++
++      cyberpro_grphw24(CAP_MEM_START, dp->cap_mem_offset, dp);
++
++      /*
++       * Setup the overlay source information.
++       */
++      dp->ovl->src.offset = dp->cap_mem_offset;
++      dp->ovl->set_src(dp, dp->ovl);
++
++      return 0;
++}
++
++static void cyberpro_hw_init(struct cyberpro_vidinfo *dp)
++{
++      unsigned char old;
++
++      /*
++       * Enable access to bus-master registers
++       */
++      dp->info.enable_extregs(dp->info.info);
++
++      dp->vfac1 = VFAC_CTL1_PHILIPS |
++                  VFAC_CTL1_FREEZE_CAPTURE |
++                  VFAC_CTL1_FREEZE_CAPTURE_SYNC;
++      dp->vfac3 = VFAC_CTL3_CAP_IRQ;
++
++      dp->cap_miscctl = CAP_CTL_MISC_DISPUSED |
++                        CAP_CTL_MISC_SYNCTZOR |
++                        CAP_CTL_MISC_SYNCTZHIGH;
++
++      /*
++       * Setup bus-master mode
++       */
++      cyberpro_grphw8(BM_CTRL1, 0x88, dp);
++      cyberpro_grphw8(PCI_BM_CTL, PCI_BM_CTL_ENABLE, dp);
++      cyberpro_grphw8(BM_CTRL0, 0x44, dp);
++      cyberpro_grphw8(BM_CTRL1, 0x84, dp);
++
++      cyberpro_grphw24(CAP_MEM_START, 0, dp);
++
++      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++      cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp);
++      cyberpro_grphw8(VFAC_CTL2, 0, dp);
++
++      cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp);
++      cyberpro_grphw8(EXT_TV_CTL, 0x80, dp);
++
++      cyberpro_grphw8(EXT_CAP_CTL1, 0x3f, dp);        /* disable PIP */
++      cyberpro_grphw8(EXT_CAP_CTL2, 0xc0 | EXT_CAP_CTL2_ODDFRAMEIRQ, dp);
++
++      /*
++       * Configure capture mode to match the
++       * external video processor format
++       */
++      cyberpro_grphw8(EXT_CAP_MODE1, dp->cap_mode1, dp);
++      cyberpro_grphw8(EXT_CAP_MODE2, dp->cap_mode2, dp);
++
++      /* setup overlay */
++      cyberpro_grphw16(EXT_FIFO_CTL, 0x1010, dp);
++//    cyberpro_grphw16(EXT_FIFO_CTL, 0x1b0f, dp);
++
++      /*
++       * Always reset the capture parameters on each open.
++       */
++      dp->capt.x          = 0;
++      dp->capt.y          = 0;
++      dp->capt.width      = dp->cap.maxwidth;
++      dp->capt.height     = dp->cap.maxheight;
++      dp->capt.decimation = 0;
++      dp->capt.flags      = 0;
++
++      cyberpro_capture_set_win(dp);
++
++      /*
++       * Enable VAFC
++       */
++      old = cyberpro_grphr8(EXT_LATCH1, dp);
++      cyberpro_grphw8(EXT_LATCH1, old | EXT_LATCH1_VAFC_EN, dp);
++
++      /*
++       * Enable capture (we hope that VSYNC=1)
++       */
++      dp->vfac1 |= VFAC_CTL1_CAPTURE;
++      cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp);
++
++      /*
++       * The overlay source format is always the
++       * same as the capture stream format.
++       */
++      dp->ovl->src.width  = dp->capt.width;
++      dp->ovl->src.height = dp->capt.height;
++      dp->ovl->src.format = dp->stream_fmt;
++
++      /*
++       * Initialise the overlay windows
++       */
++      dp->ext.init(dp, &dp->ext);
++      dp->v2.init(dp, &dp->v2);
++      dp->x2.init(dp, &dp->x2);
++}
++
++static void cyberpro_deinit(struct cyberpro_vidinfo *dp)
++{
++      unsigned char old;
++
++      /*
++       * Stop any bus-master activity
++       */
++      cyberpro_writew(0, BM_CONTROL, dp);
++
++      /*
++       * Shut down overlay
++       */
++      if (dp->ovl_active)
++              dp->ovl->ctl(dp, dp->ovl, 0);
++      dp->ovl_active = 0;
++
++      /*
++       * Shut down capture
++       */
++      if (dp->cap_active)
++              cyberpro_capture(dp, 0);
++      dp->cap_active = 0;
++
++      /*
++       * Disable all capture
++       */
++      cyberpro_grphw8(VFAC_CTL1, 0, dp);
++
++      /*
++       * Disable VAFC
++       */
++      old = cyberpro_grphr8(EXT_LATCH1, dp);
++      cyberpro_grphw8(EXT_LATCH1, old & ~EXT_LATCH1_VAFC_EN, dp);
++
++      /*
++       * Disable interrupt (this allows it to float)
++       */
++      dp->vfac3 &= ~VFAC_CTL3_CAP_IRQ;
++      cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp);
++
++      /*
++       * Switch off bus-master mode
++       */
++      cyberpro_grphw8(PCI_BM_CTL, 0, dp);
++
++      /*
++       * Disable access to bus-master registers
++       */
++      dp->info.disable_extregs(dp->info.info);
++}
++
++static int cyberpro_grabber_open(struct video_device *dev, int flags)
++{
++      struct cyberpro_vidinfo *dp = dev->priv;
++      int ret, one = 1;
++
++      MOD_INC_USE_COUNT;
++
++      ret = -EBUSY;
++      if (flags || dp->users)
++              goto out;
++
++      dp->users += 1;
++
++      if (dp->users == 1) {
++              ret = request_irq(dp->irq, cyberpro_interrupt, SA_SHIRQ,
++                                dp->info.dev_name, dp);
++
++              if (ret) {
++                      dp->users -= 1;
++                      goto out;
++              }
++
++              /*
++               * Initialise the VGA chip
++               */
++              cyberpro_hw_init(dp);
++
++              /*
++               * Enable the IRQ.  This allows the IRQ to work as expected
++               * even if the IRQ line is missing the pull-up resistor.
++               */
++              enable_irq(dp->irq);
++
++              i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER,
++                                 DECODER_ENABLE_OUTPUT, &one);
++      }
++
++      ret = 0;
++out:
++      if (ret)
++              MOD_DEC_USE_COUNT;
++      return ret;
++}
++
++static void cyberpro_grabber_close(struct video_device *dev)
++{
++      struct cyberpro_vidinfo *dp = dev->priv;
++
++      if (dp->users == 1) {
++              int zero = 0;
++
++              /*
++               * Disable the IRQ.  This prevents problems with missing
++               * pull-up resistors on the PCI interrupt line.
++               */
++              disable_irq(dp->irq);
++
++              cyberpro_frames_free(dp);
++
++              /*
++               * Turn off the SAA7111 decoder
++               */
++              i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER,
++                                 DECODER_ENABLE_OUTPUT, &zero);
++
++              /*
++               * Disable grabber
++               */
++              cyberpro_deinit(dp);
++
++              free_irq(dp->irq, dp);
++      }
++
++      dp->users -= 1;
++
++      MOD_DEC_USE_COUNT;
++}
++
++/*
++ * Our general plan here is:
++ *  1. Set the CyberPro to perform a BM-DMA of one frame to this memory
++ *  2. Copy the frame to the userspace
++ *
++ * However, BM-DMA seems to be unreliable at the moment, especially on
++ * rev. 4 NetWinders.
++ */
++static long
++cyberpro_grabber_read(struct video_device *dev, char *buf,
++                    unsigned long count, int noblock)
++{
++      struct cyberpro_vidinfo *dp = dev->priv;
++      int ret = -EINVAL;
++
++#ifdef USE_MMIO
++      unsigned long maxbufsz = dp->capt.width * dp->capt.height * 2;
++      char *disp = dp->info.fb + (dp->cap_mem_offset << 2);
++
++      /*
++       * If the buffer is mmap()'d, we shouldn't be using read()
++       */
++      if (dp->mmaped)
++              return -EINVAL;
++
++      if (count > maxbufsz)
++              count = maxbufsz;
++
++      if (dp->cap_active)
++              cyberpro_capture(dp, 0);
++      else
++              cyberpro_capture_one(dp);
++
++      ret = (int)count;
++      if (copy_to_user(buf, disp, count))
++              ret = -EFAULT;
++
++      /*
++       * unfreeze capture
++       */
++      if (dp->cap_active)
++              cyberpro_capture(dp, 1);
++#endif
++
++      return ret;
++}
++
++/*
++ * We don't support writing to the grabber
++ * (In theory, we could allow writing to a separate region of VGA memory,
++ * and display this using the second overlay window.  This would allow us
++ * to do video conferencing for example).
++ */
++static long
++cyberpro_grabber_write(struct video_device *dev, const char *buf,
++                     unsigned long count, int noblock)
++{
++      return -EINVAL;
++}
++
++static int
++cyberpro_grabber_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
++{
++      struct cyberpro_vidinfo *dp = dev->priv;
++
++      switch (cmd) {
++      case VIDIOCGCAP:
++              return copy_to_user(arg, &dp->cap, sizeof(dp->cap))
++                      ? -EFAULT : 0;
++
++      case VIDIOCGCHAN:
++      {
++              struct video_channel chan;
++
++              chan.channel    = 0;
++              strcpy(chan.name, "Composite");
++              chan.tuners     = 0;
++              chan.flags      = 0;
++              chan.type       = VIDEO_TYPE_CAMERA;
++              chan.norm       = dp->norm;
++
++              return copy_to_user(arg, &chan, sizeof(chan)) ? -EFAULT : 0;
++      }
++
++      case VIDIOCGPICT:
++              return copy_to_user(arg, &dp->pic, sizeof(dp->pic))
++                      ? -EINVAL : 0;
++
++      case VIDIOCGWIN:
++      {
++              struct video_window win;
++
++              win.x         = dp->ovl->dst.x;
++              win.y         = dp->ovl->dst.y;
++              win.width     = dp->ovl->dst.width;
++              win.height    = dp->ovl->dst.height;
++              win.chromakey = dp->ovl->dst.chromakey;
++              win.flags     = VIDEO_WINDOW_CHROMAKEY |
++                              (dp->interlace ? VIDEO_WINDOW_INTERLACE : 0);
++              win.clips     = NULL;
++              win.clipcount = 0;
++
++              return copy_to_user(arg, &win, sizeof(win))
++                      ? -EINVAL : 0;
++      }
++
++      case VIDIOCGFBUF:
++              return copy_to_user(arg, &dp->buf, sizeof(dp->buf))
++                      ? -EINVAL : 0;
++
++      case VIDIOCGUNIT:
++      {
++              struct video_unit unit;
++
++              unit.video    = dev->minor;
++              unit.vbi      = VIDEO_NO_UNIT;
++              unit.radio    = VIDEO_NO_UNIT;
++              unit.audio    = VIDEO_NO_UNIT;
++              unit.teletext = VIDEO_NO_UNIT;
++
++              return copy_to_user(arg, &unit, sizeof(unit))
++                      ? -EINVAL : 0;
++      }
++
++      case VIDIOCGCAPTURE:
++              return copy_to_user(arg, &dp->capt, sizeof(dp->capt))
++                              ? -EFAULT : 0;
++
++      case VIDIOCSCHAN:
++      {
++              struct video_decoder_capability vdc;
++              struct video_channel v;
++              int ok;
++
++              if (copy_from_user(&v, arg, sizeof(v)))
++                      return -EFAULT;
++
++              if (v.channel != 0)
++                      return -EINVAL;
++
++              i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER,
++                                 DECODER_GET_CAPABILITIES, &vdc);
++
++              switch (v.norm) {
++              case VIDEO_MODE_PAL:
++                      ok = vdc.flags & VIDEO_DECODER_PAL;
++                      break;
++              case VIDEO_MODE_NTSC:
++                      ok = vdc.flags & VIDEO_DECODER_NTSC;
++                      break;
++              case VIDEO_MODE_AUTO:
++                      ok = vdc.flags & VIDEO_DECODER_AUTO;
++                      break;
++              default:
++                      ok = 0;
++              }
++              if (!ok)
++                      return -EINVAL;
++
++              dp->norm = v.norm;
++
++              i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER,
++                                 DECODER_SET_NORM, &v.norm);
++
++              return 0;
++      }
++
++      case VIDIOCSPICT:
++      {
++              struct video_picture p;
++
++              if (copy_from_user(&p, arg, sizeof(p)))
++                      return -EFAULT;
++
++              if (p.palette != dp->stream_fmt ||
++                  p.depth != 8)
++                      return -EINVAL;
++
++              dp->pic = p;
++
++              /* p.depth sets the capture depth */
++              /* p.palette sets the capture palette */
++
++              i2c_control_device(dp->bus, I2C_DRIVERID_VIDEODECODER,
++                                 DECODER_SET_PICTURE, &p);
++
++              return 0;
++      }
++
++      case VIDIOCSWIN:        /* set the size & position of the overlay window */
++      {
++              struct video_window w;
++              int diff;
++
++              if (!dp->buf_set)
++                      return -EINVAL;
++
++              if (copy_from_user(&w, arg, sizeof(w)))
++                      return -EFAULT;
++
++              if (w.clipcount)
++                      return -EINVAL;
++
++              /*
++               * Bound the overlay window by the size of the screen
++               */
++              if (w.x < 0)
++                      w.x = 0;
++              if (w.y < 0)
++                      w.y = 0;
++
++              if (w.x > dp->buf.width)
++                      w.x = dp->buf.width;
++              if (w.y > dp->buf.height)
++                      w.y = dp->buf.height;
++
++              if (w.width < dp->capt.width)
++                      w.width = dp->capt.width;
++              if (w.height < dp->capt.height)
++                      w.height = dp->capt.height;
++
++              if (w.x + w.width > dp->buf.width)
++                      w.width = dp->buf.width - w.x;
++              if (w.y + w.height > dp->buf.height)
++                      w.height = dp->buf.height - w.y;
++
++              /*
++               * We've tried to make the values fit, but
++               * they just won't.
++               */
++              if (w.width < dp->capt.width || w.height < dp->capt.height)
++                      return -EINVAL;
++
++              diff = dp->ovl->dst.x != w.x ||
++                     dp->ovl->dst.y != w.y ||
++                     dp->ovl->dst.width != w.width  ||
++                     dp->ovl->dst.height != w.height ||
++                     dp->ovl->dst.chromakey != w.chromakey ||
++                     dp->ovl->dst.flags != w.flags;
++
++              if (!dp->win_set || diff) {
++                      dp->ovl->dst.x         = w.x;
++                      dp->ovl->dst.y         = w.y;
++                      dp->ovl->dst.width     = w.width;
++                      dp->ovl->dst.height    = w.height;
++                      dp->ovl->dst.chromakey = w.chromakey;
++                      dp->ovl->dst.flags     = w.flags;
++
++                      if (dp->ovl_active)
++                              dp->ovl->ctl(dp, dp->ovl, 0);
++
++                      dp->ovl->set_win(dp, dp->ovl);
++
++                      if (dp->ovl_active)
++                              dp->ovl->ctl(dp, dp->ovl, 1);
++
++                      diff = w.flags & VIDEO_WINDOW_INTERLACE ? 1 : 0;
++                      if (!dp->win_set || dp->interlace != diff) {
++                              dp->interlace = diff;
++                              cyberpro_set_interlace(dp);
++                      }
++              }
++
++              dp->win_set = 1;
++
++              return 0;
++      }
++
++      case VIDIOCSFBUF:       /* set frame buffer info */
++      {
++              struct video_buffer b;
++              int ret;
++
++              if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
++                      return -EPERM;
++
++              if (dp->cap_active)
++                      return -EINVAL;
++
++              if (copy_from_user(&b, arg, sizeof(b)))
++                      return -EFAULT;
++
++              ret = cyberpro_set_buffer(dp, &b);
++              if (ret == 0) {
++                      dp->buf_set = 1;
++                      dp->win_set = 0;
++              }
++
++              return ret;
++      }
++
++      case VIDIOCCAPTURE:
++      {
++              int on;
++
++              if (get_user(on, (int *)arg))
++                      return -EFAULT;
++
++              if (( on &&  dp->ovl_active) ||
++                  (!on && !dp->ovl_active))
++                      return 0;
++
++              if (on && (!dp->buf_set || !dp->win_set))
++                      return -EINVAL;
++
++              cyberpro_capture(dp, on);
++              dp->cap_active = on;
++              dp->ovl->ctl(dp, dp->ovl, on);
++              dp->ovl_active = on;
++
++              return 0;
++      }
++
++#ifdef USE_MMAP
++      case VIDIOCSYNC:
++      {
++              DECLARE_WAITQUEUE(wait, current);
++              int buf;
++
++              /*
++               * The buffer must have been mmaped
++               * for this call to work.
++               */
++              if (!dp->mmaped)
++                      return -EINVAL;
++
++              if (get_user(buf, (int *)arg))
++                      return -EFAULT;
++
++              if (buf < 0 || buf >= NR_FRAMES)
++                      return -EINVAL;
++
++              switch (dp->frame[buf].status) {
++              case FRAME_FREE:
++                      return -EINVAL;
++
++              case FRAME_WAITING:
++              case FRAME_GRABBING:
++                      add_wait_queue(&dp->frame_wait, &wait);
++                      while (1) {
++                              set_current_state(TASK_INTERRUPTIBLE);
++                              if (signal_pending(current))
++                                      break;
++                              if (dp->frame[buf].status == FRAME_DONE)
++                                      break;
++                              schedule();
++                      }
++                      set_current_state(TASK_RUNNING);
++                      remove_wait_queue(&dp->frame_wait, &wait);
++                      if (signal_pending(current))
++                              return -EINTR;
++                      /*FALLTHROUGH*/
++              case FRAME_DONE:
++                      dp->frame[buf].status = FRAME_FREE;
++                      break;
++              }
++              return 0;
++      }
++
++      case VIDIOCMCAPTURE:
++      {
++              struct video_mmap vmap;
++
++              /*
++               * The buffer must have been mmaped
++               * for this call to work.
++               */
++              if (!dp->mmaped)
++                      return -EINVAL;
++
++              if (copy_from_user(&vmap, arg, sizeof(vmap)))
++                      return -EFAULT;
++
++              /*
++               * We can only capture in our source format/size.
++               */
++              if (vmap.frame  >= NR_FRAMES ||
++                  vmap.format != dp->stream_fmt ||
++                  vmap.width  != dp->capt.width ||
++                  vmap.height != dp->capt.height)
++                      return -EINVAL;
++
++              if (dp->frame[vmap.frame].status == FRAME_WAITING ||
++                  dp->frame[vmap.frame].status == FRAME_GRABBING)
++                      return -EBUSY;
++
++              dp->frame[vmap.frame].status = FRAME_WAITING;
++              return 0;
++      }
++
++      case VIDIOCGMBUF:
++      {
++              struct video_mbuf vmb;
++              unsigned int i;
++
++              vmb.frames = NR_FRAMES;
++              vmb.size   = dp->frame_size * NR_FRAMES;
++
++              for (i = 0; i < NR_FRAMES; i++)
++                      vmb.offsets[i] = dp->frame[i].offset;
++
++              return copy_to_user(arg, &vmb, sizeof(vmb)) ? -EFAULT : 0;
++      }
++#endif
++
++      case VIDIOCSCAPTURE:
++      {
++              struct video_capture capt;
++
++#ifndef ALLOW_SCAPTURE_WHILE_CAP
++              if (dp->cap_active)
++                      return -EINVAL;
++#endif
++
++              if (copy_from_user(&capt, arg, sizeof(capt)))
++                      return -EFAULT;
++
++              if (capt.x < 0 || capt.width < 0 ||
++                  capt.y < 0 || capt.height < 0 ||
++                  capt.x + capt.width > dp->cap.maxwidth ||
++                  capt.y + capt.height > dp->cap.maxheight)
++                      return -EINVAL;
++
++              /*
++               * The capture width must be a multiple of 4
++               */
++              if (dp->capt.width & 3)
++                      return -EINVAL;
++
++              dp->capt.x = capt.x;
++              dp->capt.y = capt.y;
++              dp->capt.width = capt.width;
++              dp->capt.height = capt.height;
++#ifdef ALLOW_SCAPTURE_WHILE_CAP
++              if (dp->ovl_active)
++                      dp->ovl->ctl(dp, dp->ovl, 0);
++              if (dp->cap_active)
++                      cyberpro_capture(dp, 0);
++#endif
++              cyberpro_capture_set_win(dp);
++
++              /*
++               * Update the overlay window information
++               */
++              dp->ovl->src.width  = capt.width;
++              dp->ovl->src.height = capt.height;
++
++              dp->ovl->set_src(dp, dp->ovl);
++              if (dp->win_set)
++                      dp->ovl->set_win(dp, dp->ovl);
++
++#ifdef ALLOW_SCAPTURE_WHILE_CAP
++              if (dp->cap_active)
++                      cyberpro_capture(dp, 1);
++              if (dp->ovl_active)
++                      dp->ovl->ctl(dp, dp->ovl, 1);
++#endif
++              return 0;
++      }
++
++      case VIDIOCGTUNER:      /* no tuner */
++      case VIDIOCSTUNER:
++              return -EINVAL;
++      }
++
++      return -EINVAL;
++}
++
++#ifdef USE_MMAP
++static int
++cyberpro_grabber_mmap(struct video_device *dev, const char *addr, unsigned long size)
++{
++      struct cyberpro_vidinfo *dp = dev->priv;
++      unsigned long vaddr = (unsigned long)addr;
++      pgprot_t prot;
++      int frame_idx, ret = -EINVAL;
++
++#if defined(__arm__)
++      prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_USER | L_PTE_WRITE | L_PTE_DIRTY);
++#elif defined(__i386__)
++      prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED);
++      if (boot_cpu_data.x86 > 3)
++              pgprot_val(prot) |= _PAGE_PCD;
++#else
++#error "Unsupported architecture"
++#endif
++
++      /*
++       * The mmap() request must have the correct size.
++       */
++      if (size != NR_FRAMES * dp->frame_size)
++              goto out;
++
++      /*
++       * If it's already mapped, don't re-do
++       */
++      if (dp->mmaped)
++              goto out;
++      dp->mmaped = 1;
++
++      /*
++       * Map in each frame
++       */
++      for (frame_idx = 0; frame_idx < NR_FRAMES; frame_idx++) {
++              struct framebuf *frame;
++              int pgidx;
++
++              frame = dp->frame + frame_idx;
++
++              ret = cyberpro_alloc_frame_buffer(dp, frame);
++
++              /*
++               * If an error occurs, we can be lazy and leave what we've
++               * been able to do.  Our release function will free any
++               * allocated buffers, and do_mmap_pgoff() will zap any
++               * inserted mappings.
++               */
++              if (ret)
++                      goto out2;
++
++              /*
++               * Map in each page on a page by page basis.  This is just
++               * a little on the inefficient side, but it's only run once.
++               */
++              for (pgidx = 0; pgidx < NR_PAGES; pgidx++) {
++                      unsigned long virt;
++
++                      virt = page_address(frame->pages[pgidx]);
++
++                      ret = remap_page_range(vaddr, virt_to_phys((void *)virt),
++                                             PAGE_SIZE, prot);
++
++                      if (ret)
++                              goto out2;
++
++                      vaddr += PAGE_SIZE;
++              }
++      }
++
++ out2:
++      if (ret)
++              dp->mmaped = 0;
++ out:
++      return ret;
++}
++#endif
++
++static int __init cyberpro_grabber_init_done(struct video_device *dev)
++{
++      struct cyberpro_vidinfo *dp;
++      struct cyberpro_info *info = dev->priv;
++      int ret;
++
++      dp = kmalloc(sizeof(*dp), GFP_KERNEL);
++      if (!dp)
++              return -ENOMEM;
++
++      memset(dp, 0, sizeof(*dp));
++
++      dev->priv               = dp;
++      dp->info                = *info;
++      dp->dev                 = dev;
++      dp->bus                 = &cyberpro_i2c_bus;
++      dp->regs                = info->regs;
++      dp->irq                 = info->dev->irq;
++
++      strcpy(dp->cap.name, dev->name);
++      dp->cap.type            = dev->type;
++      dp->cap.channels        = 1;
++      dp->cap.audios          = 0;
++      dp->cap.minwidth        = 32;
++      dp->cap.maxwidth        = 716;
++      dp->cap.minheight       = 32;
++      dp->cap.maxheight       = 576;
++
++      dp->pic.brightness      = 32768;
++      dp->pic.hue             = 32768;
++      dp->pic.colour          = 32768;
++      dp->pic.contrast        = 32768;
++      dp->pic.whiteness       = 0;
++      dp->pic.depth           = 8;
++      dp->pic.palette         = VIDEO_PALETTE_YUV422;
++
++      /* dp->buf is setup by the user         */
++      /* dp->cap_mem_offset setup by dp->buf  */
++
++      dp->norm                = VIDEO_MODE_AUTO;
++
++      /*
++       * The extended overlay window
++       */
++      dp->ext.init            = cyberpro_ext_init;
++      dp->ext.set_src         = cyberpro_ext_set_src;
++      dp->ext.set_win         = cyberpro_ext_set_win;
++      dp->ext.ctl             = cyberpro_ext_ctl;
++
++      /*
++       * The V2 overlay window
++       */
++      dp->v2.init             = cyberpro_v2_init;
++      dp->v2.set_src          = cyberpro_v2_set_src;
++      dp->v2.set_win          = cyberpro_v2_set_win;
++      dp->v2.ctl              = cyberpro_v2_ctl;
++
++      /*
++       * The X2 overlay window
++       */
++      dp->x2.init             = cyberpro_x2_init;
++      dp->x2.set_src          = cyberpro_x2_set_src;
++      dp->x2.set_win          = cyberpro_x2_set_win;
++      dp->x2.ctl              = cyberpro_x2_ctl;
++
++      /*
++       * Set the overlay window which we shall be using
++       */
++      dp->ovl = &dp->ext;
++
++      dp->cap_mode1 = EXT_CAP_MODE1_ALTFIFO;
++
++      /*
++       * Initialise hardware specific values.
++       *  - CCIR656 8bit mode (YUV422 data)
++       *  - Ignore Hgood signal
++       *  - Invert Odd/Even field signal
++       */
++      dp->cap_mode1 |= EXT_CAP_MODE1_CCIR656 | EXT_CAP_MODE1_8BIT;
++      dp->cap_mode2  = EXT_CAP_MODE2_FIXSONY | EXT_CAP_MODE2_DATEND |
++                       EXT_CAP_MODE2_CCIRINVOE;
++      dp->stream_fmt = VIDEO_PALETTE_YUV422;
++
++
++      init_waitqueue_head(&dp->vbl_wait);
++      cyberpro_frames_init(dp);
++
++      /*
++       * wake up the decoder
++       */
++      decoder_sleep(0);
++
++      dp->bus->data = dp;
++      strncpy(dp->bus->name, dev->name, sizeof(dp->bus->name));
++
++      pci_set_master(dp->info.dev);
++
++      ret = i2c_register_bus(dp->bus);
++
++      /*
++       * If we successfully registered the bus, but didn't initialise
++       * the decoder (because its driver is not present), request
++       * that it is loaded.
++       */
++      if (ret == 0 && !dp->decoder)
++              request_module("saa7111");
++
++      /*
++       * If that didn't work, then we're out of luck.
++       */
++      if (ret == 0 && !dp->decoder) {
++              i2c_unregister_bus(dp->bus);
++              ret = -ENXIO;
++      }
++
++      if (ret) {
++              kfree(dp);
++
++              /*
++               * put the decoder back to sleep
++               */
++              decoder_sleep(1);
++      }
++
++      return ret;
++}
++
++static struct video_device cyberpro_grabber = {
++      name:           "",
++      type:           VID_TYPE_CAPTURE   | VID_TYPE_OVERLAY |
++                      VID_TYPE_CHROMAKEY | VID_TYPE_SCALES  |
++                      VID_TYPE_SUBCAPTURE,
++      hardware:       0,
++      open:           cyberpro_grabber_open,
++      close:          cyberpro_grabber_close,
++      read:           cyberpro_grabber_read,
++      write:          cyberpro_grabber_write,
++      ioctl:          cyberpro_grabber_ioctl,
++#ifdef USE_MMAP
++      mmap:           cyberpro_grabber_mmap,
++#endif
++      initialize:     cyberpro_grabber_init_done,
++};
++
++int init_cyber2000fb_viddev(void)
++{
++      struct cyberpro_info info;
++
++      if (!cyber2000fb_attach(&info, 0))
++              return -ENXIO;
++
++      strncpy(cyberpro_grabber.name, info.dev_name, sizeof(cyberpro_grabber.name));
++
++      cyberpro_grabber.priv = &info;
++
++      return video_register_device(&cyberpro_grabber, VFL_TYPE_GRABBER, -1);
++}
++
++/*
++ * This can be cleaned up when the SAA7111 code is fixed.
++ */
++#ifdef MODULE
++static int __init cyberpro_init(void)
++{
++      disable_irq(35);
++      return init_cyber2000fb_viddev();
++}
++
++static void __exit cyberpro_exit(void)
++{
++      video_unregister_device(&cyberpro_grabber);
++      kfree(cyberpro_grabber.priv);
++      i2c_unregister_bus(&cyberpro_i2c_bus);
++
++      /*
++       * put the decoder back to sleep
++       */
++      decoder_sleep(1);
++
++      cyber2000fb_detach(0);
++}
++
++module_init(cyberpro_init);
++module_exit(cyberpro_exit);
++#endif
+--- linux-2.4.27/drivers/media/video/i2c-old.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/media/video/i2c-old.c
+@@ -36,11 +36,20 @@
+ static struct i2c_driver *drivers[I2C_DRIVER_MAX];
+ static int bus_count = 0, driver_count = 0;
++extern int saa7111_init(void);
++extern int saa7185_init(void);
++extern int bt819_init(void);
++extern int bt856_init(void);
++
+ int i2c_init(void)
+ {
+       printk(KERN_INFO "i2c: initialized%s\n",
+               scan ? " (i2c bus scan enabled)" : "");
++#if defined(CONFIG_VIDEO_CYBERPRO)
++      saa7111_init();
++#endif
++
+       return 0;
+ }
+@@ -52,10 +61,10 @@
+       int i,j,ack=1;
+       unsigned char addr;
+       LOCK_FLAGS;
+-    
++
+       /* probe for device */
+       LOCK_I2C_BUS(bus);
+-      for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) 
++      for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2)
+       {
+               i2c_start(bus);
+               ack = i2c_sendbyte(bus,addr,0);
+@@ -87,8 +96,8 @@
+       device->addr = addr;
+       /* Attach */
+-      
+-      if (driver->attach(device)!=0) 
++
++      if (driver->attach(device)!=0)
+       {
+               kfree(device);
+               return;
+@@ -114,7 +123,7 @@
+       for (i = 0; i < I2C_DEVICE_MAX; i++)
+               if (device == device->driver->devices[i])
+                       break;
+-      if (I2C_DEVICE_MAX == i) 
++      if (I2C_DEVICE_MAX == i)
+       {
+               printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n",
+                       device->name);
+@@ -126,7 +135,7 @@
+       for (i = 0; i < I2C_DEVICE_MAX; i++)
+               if (device == device->bus->devices[i])
+                       break;
+-      if (I2C_DEVICE_MAX == i) 
++      if (I2C_DEVICE_MAX == i)
+       {
+               printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n",
+                      device->name);
+@@ -158,19 +167,19 @@
+       busses[i] = bus;
+       bus_count++;
+       REGPRINT(printk("i2c: bus registered: %s\n",bus->name));
+-      
++
+       MOD_INC_USE_COUNT;
+-      if (scan) 
++      if (scan)
+       {
+               /* scan whole i2c bus */
+               LOCK_I2C_BUS(bus);
+-              for (i = 0; i < 256; i+=2) 
++              for (i = 0; i < 256; i+=2)
+               {
+                       i2c_start(bus);
+                       ack = i2c_sendbyte(bus,i,0);
+                       i2c_stop(bus);
+-                      if (!ack) 
++                      if (!ack)
+                       {
+                               printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n",
+                                       bus->name,i);
+@@ -198,20 +207,20 @@
+       for (i = 0; i < I2C_BUS_MAX; i++)
+               if (bus == busses[i])
+                       break;
+-      if (I2C_BUS_MAX == i) 
++      if (I2C_BUS_MAX == i)
+       {
+               printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n",
+                       bus->name);
+               return -ENODEV;
+       }
+-      
++
+       MOD_DEC_USE_COUNT;
+-      
++
+       busses[i] = NULL;
+       bus_count--;
+       REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name));
+-      return 0;    
++      return 0;
+ }
+ /* ----------------------------------------------------------------------- */
+@@ -231,9 +240,9 @@
+       drivers[i] = driver;
+       driver_count++;
+-      
++
+       MOD_INC_USE_COUNT;
+-      
++
+       REGPRINT(printk("i2c: driver registered: %s\n",driver->name));
+       /* Probe available busses */
+@@ -256,7 +265,7 @@
+       for (i = 0; i < I2C_DRIVER_MAX; i++)
+               if (driver == drivers[i])
+                       break;
+-      if (I2C_DRIVER_MAX == i) 
++      if (I2C_DRIVER_MAX == i)
+       {
+               printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n",
+                       driver->name);
+@@ -264,7 +273,7 @@
+       }
+       MOD_DEC_USE_COUNT;
+-      
++
+       drivers[i] = NULL;
+       driver_count--;
+       REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name));
+@@ -328,7 +337,7 @@
+ int i2c_ack(struct i2c_bus *bus)
+ {
+       int ack;
+-    
++
+       I2C_SET(bus,0,1);
+       I2C_SET(bus,1,1);
+       ack = I2C_GET(bus);
+@@ -339,7 +348,7 @@
+ int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack)
+ {
+       int i, ack;
+-    
++
+       I2C_SET(bus,0,0);
+       for (i=7; i>=0; i--)
+               (data&(1<<i)) ? i2c_one(bus) : i2c_zero(bus);
+@@ -354,9 +363,9 @@
+ {
+       int i;
+       unsigned char data=0;
+-    
++
+       I2C_SET(bus,0,1);
+-      for (i=7; i>=0; i--) 
++      for (i=7; i>=0; i--)
+       {
+               I2C_SET(bus,1,1);
+               if (I2C_GET(bus))
+@@ -373,7 +382,7 @@
+ int i2c_read(struct i2c_bus *bus, unsigned char addr)
+ {
+       int ret;
+-    
++
+       if (bus->i2c_read)
+               return bus->i2c_read(bus, addr);
+--- linux-2.4.27/drivers/media/video/saa7111.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/media/video/saa7111.c
+@@ -20,9 +20,9 @@
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+-
+-#include <linux/module.h>
++#include <linux/config.h>
+ #include <linux/init.h>
++#include <linux/module.h>
+ #include <linux/delay.h>
+ #include <linux/errno.h>
+ #include <linux/fs.h>
+@@ -149,7 +149,11 @@
+               0x0d, 0x00,     /* 0d - HUE=0 */
+               0x0e, 0x01,     /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
+               0x0f, 0x00,     /* 0f - reserved */
++#ifndef CONFIG_ARCH_NETWINDER
+               0x10, 0x48,     /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
++#else
++              0x10, 0xc8,     /* 10 - OFTS=YUV-CCIR656, HDEL=0, VLRN=1, YDEL=0 */
++#endif
+               0x11, 0x1c,     /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+               0x12, 0x00,     /* 12 - output control 2 */
+               0x13, 0x00,     /* 13 - output control 3 */
+--- linux-2.4.27/drivers/message/i2o/i2o_core.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/message/i2o/i2o_core.c
+@@ -1665,14 +1665,14 @@
+       }
+       memset(status, 0, 4);
+       
+-      msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
+-      msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
+-      msg[2]=core_context;
+-      msg[3]=0;
+-      msg[4]=0;
+-      msg[5]=0;
+-      msg[6]=virt_to_bus(status);
+-      msg[7]=0;       /* 64bit host FIXME */
++      writel(EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0, msg + 0);
++      writel(I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID, msg + 1);
++      writel(core_context, msg + 2);
++      writel(0, msg + 3);
++      writel(0, msg + 4);
++      writel(0, msg + 5);
++      writel(virt_to_bus(status), msg + 6);
++      writel(0, msg + 7);     /* 64bit host FIXME */
+       i2o_post_message(c,m);
+@@ -1781,15 +1781,15 @@
+               return -ETIMEDOUT;      
+       msg=(u32 *)(c->mem_offset+m);
+-      msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
+-      msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID;
+-      msg[2]=core_context;
+-      msg[3]=0;
+-      msg[4]=0;
+-      msg[5]=0;
+-      msg[6]=virt_to_bus(c->status_block);
+-      msg[7]=0;   /* 64bit host FIXME */
+-      msg[8]=sizeof(i2o_status_block); /* always 88 bytes */
++      writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, msg + 0);
++      writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, msg + 1);
++      writel(core_context, msg + 2);
++      writel(0, msg + 3);
++      writel(0, msg + 4);
++      writel(0, msg + 5);
++      writel(virt_to_bus(c->status_block), msg + 6);
++      writel(0, msg + 7);   /* 64bit host FIXME */
++      writel(sizeof(i2o_status_block), msg + 8); /* always 88 bytes */
+       i2o_post_message(c,m);
+@@ -2193,15 +2193,15 @@
+       }
+       memset(status, 0, 4);
+-      msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6;
+-      msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID;
+-      msg[2]= core_context;
+-      msg[3]= 0x0106;                         /* Transaction context */
+-      msg[4]= 4096;                           /* Host page frame size */
++      writel(EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6, msg + 0);
++      writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, msg + 1);
++      writel(core_context, msg + 2);
++      writel(0x0106, msg + 3);                /* Transaction context */
++      writel(PAGE_SIZE, msg + 4);             /* Host page frame size */
+       /* Frame size is in words. 256 bytes a frame for now */
+-      msg[5]= MSG_FRAME_SIZE<<16|0x80;        /* Outbound msg frame size in words and Initcode */
+-      msg[6]= 0xD0000004;                     /* Simple SG LE, EOB */
+-      msg[7]= virt_to_bus(status);
++      writel(MSG_FRAME_SIZE<<16|0x80, msg + 5);/* Outbound msg frame size in words and Initcode */
++      writel(0xD0000004, msg + 6);            /* Simple SG LE, EOB */
++      writel(virt_to_bus(status), msg + 7);
+       i2o_post_message(c,m);
+       
+--- linux-2.4.27/drivers/message/i2o/i2o_pci.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/message/i2o/i2o_pci.c
+@@ -390,4 +390,4 @@
+ MODULE_PARM_DESC(dpt, "Set this if you want to drive DPT cards normally handled by dpt_i2o");
+ module_init(i2o_pci_core_attach);
+ module_exit(i2o_pci_core_detach);
+- 
+\ No newline at end of file
++ 
+--- linux-2.4.27/drivers/misc/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/misc/Config.in
+@@ -1,7 +1,17 @@
+ #
+-# Misc strange devices
++# MCP drivers
+ #
+ mainmenu_option next_comment
+-comment 'Misc devices'
++comment 'Multimedia Capabilities Port drivers'
++
++bool 'Multimedia drivers' CONFIG_MCP
++
++# Interface drivers
++dep_bool 'Support SA1100 MCP interface' CONFIG_MCP_SA1100 $CONFIG_MCP $CONFIG_ARCH_SA1100
++
++# Chip drivers
++dep_tristate 'Support for UCB1200 / UCB1300' CONFIG_MCP_UCB1200 $CONFIG_MCP
++dep_tristate '  Audio / Telephony interface support' CONFIG_MCP_UCB1200_AUDIO $CONFIG_MCP_UCB1200 $CONFIG_SOUND
++dep_tristate '  Touchscreen interface support' CONFIG_MCP_UCB1200_TS $CONFIG_MCP_UCB1200
+ endmenu
+--- linux-2.4.27/drivers/misc/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/misc/Makefile
+@@ -11,6 +11,14 @@
+ O_TARGET := misc.o
++export-objs                   := mcp-core.o mcp-sa1100.o ucb1x00-core.o
++
++obj-$(CONFIG_MCP)             += mcp-core.o
++obj-$(CONFIG_MCP_SA1100)      += mcp-sa1100.o
++obj-$(CONFIG_MCP_UCB1200)     += ucb1x00-core.o
++obj-$(CONFIG_MCP_UCB1200_AUDIO)       += ucb1x00-audio.o
++obj-$(CONFIG_MCP_UCB1200_TS)  += ucb1x00-ts.o
++
+ include $(TOPDIR)/Rules.make
+ fastdep:
+--- /dev/null
++++ linux-2.4.27/drivers/misc/mcp-core.c
+@@ -0,0 +1,155 @@
++/*
++ *  linux/drivers/misc/mcp-core.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ * 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.
++ *
++ *  Generic MCP (Multimedia Communications Port) layer.  All MCP locking
++ *  is solely held within this file.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/smp.h>
++
++#include <asm/dma.h>
++#include <asm/system.h>
++
++#include "mcp.h"
++
++/**
++ *    mcp_set_telecom_divisor - set the telecom divisor
++ *    @mcp: MCP interface structure
++ *    @div: SIB clock divisor
++ *
++ *    Set the telecom divisor on the MCP interface.  The resulting
++ *    sample rate is SIBCLOCK/div.
++ */
++void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
++{
++      spin_lock_irq(&mcp->lock);
++      mcp->set_telecom_divisor(mcp, div);
++      spin_unlock_irq(&mcp->lock);
++}
++
++/**
++ *    mcp_set_audio_divisor - set the audio divisor
++ *    @mcp: MCP interface structure
++ *    @div: SIB clock divisor
++ *
++ *    Set the audio divisor on the MCP interface.
++ */
++void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
++{
++      spin_lock_irq(&mcp->lock);
++      mcp->set_audio_divisor(mcp, div);
++      spin_unlock_irq(&mcp->lock);
++}
++
++/**
++ *    mcp_reg_write - write a device register
++ *    @mcp: MCP interface structure
++ *    @reg: 4-bit register index
++ *    @val: 16-bit data value
++ *
++ *    Write a device register.  The MCP interface must be enabled
++ *    to prevent this function hanging.
++ */
++void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&mcp->lock, flags);
++      mcp->reg_write(mcp, reg, val);
++      spin_unlock_irqrestore(&mcp->lock, flags);
++}
++
++/**
++ *    mcp_reg_read - read a device register
++ *    @mcp: MCP interface structure
++ *    @reg: 4-bit register index
++ *
++ *    Read a device register and return its value.  The MCP interface
++ *    must be enabled to prevent this function hanging.
++ */
++unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
++{
++      unsigned long flags;
++      unsigned int val;
++
++      spin_lock_irqsave(&mcp->lock, flags);
++      val = mcp->reg_read(mcp, reg);
++      spin_unlock_irqrestore(&mcp->lock, flags);
++
++      return val;
++}
++
++/**
++ *    mcp_enable - enable the MCP interface
++ *    @mcp: MCP interface to enable
++ *
++ *    Enable the MCP interface.  Each call to mcp_enable will need
++ *    a corresponding call to mcp_disable to disable the interface.
++ */
++void mcp_enable(struct mcp *mcp)
++{
++      spin_lock_irq(&mcp->lock);
++      if (mcp->use_count++ == 0)
++              mcp->enable(mcp);
++      spin_unlock_irq(&mcp->lock);
++}
++
++/**
++ *    mcp_disable - disable the MCP interface
++ *    @mcp: MCP interface to disable
++ *
++ *    Disable the MCP interface.  The MCP interface will only be
++ *    disabled once the number of calls to mcp_enable matches the
++ *    number of calls to mcp_disable.
++ */
++void mcp_disable(struct mcp *mcp)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&mcp->lock, flags);
++      if (--mcp->use_count == 0)
++              mcp->disable(mcp);
++      spin_unlock_irqrestore(&mcp->lock, flags);
++}
++
++
++/*
++ * This needs re-working
++ */
++static struct mcp *mcp_if;
++
++struct mcp *mcp_get(void)
++{
++      return mcp_if;
++}
++
++int mcp_register(struct mcp *mcp)
++{
++      if (mcp_if)
++              return -EBUSY;
++      if (mcp->owner)
++              __MOD_INC_USE_COUNT(mcp->owner);
++      mcp_if = mcp;
++      return 0;
++}
++
++EXPORT_SYMBOL(mcp_set_telecom_divisor);
++EXPORT_SYMBOL(mcp_set_audio_divisor);
++EXPORT_SYMBOL(mcp_reg_write);
++EXPORT_SYMBOL(mcp_reg_read);
++EXPORT_SYMBOL(mcp_enable);
++EXPORT_SYMBOL(mcp_disable);
++EXPORT_SYMBOL(mcp_get);
++EXPORT_SYMBOL(mcp_register);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("Core multimedia communications port driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/misc/mcp-sa1100.c
+@@ -0,0 +1,180 @@
++/*
++ *  linux/drivers/misc/mcp-sa1100.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ * 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.
++ *
++ *  SA1100 MCP (Multimedia Communications Port) driver.
++ *
++ *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/system.h>
++
++#include "mcp.h"
++
++static void
++mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
++{
++      unsigned int mccr0;
++
++      divisor /= 32;
++
++      mccr0 = Ser4MCCR0 & ~0x00007f00;
++      mccr0 |= divisor << 8;
++      Ser4MCCR0 = mccr0;
++}
++
++static void
++mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
++{
++      unsigned int mccr0;
++
++      divisor /= 32;
++
++      mccr0 = Ser4MCCR0 & ~0x0000007f;
++      mccr0 |= divisor;
++      Ser4MCCR0 = mccr0;
++}
++
++/*
++ * Write data to the device.  The bit should be set after 3 subframe
++ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
++ * We really should try doing something more productive while we
++ * wait.
++ */
++static void
++mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val)
++{
++      int ret = -ETIME;
++      int i;
++
++      Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
++
++      for (i = 0; i < 2; i++) {
++              udelay(mcp->rw_timeout);
++              if (Ser4MCSR & MCSR_CWC) {
++                      ret = 0;
++                      break;
++              }
++      }
++
++      if (ret < 0)
++              printk(KERN_WARNING "mcp: write timed out\n");
++}
++
++/*
++ * Read data from the device.  The bit should be set after 3 subframe
++ * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
++ * We really should try doing something more productive while we
++ * wait.
++ */
++static unsigned int
++mcp_sa1100_read(struct mcp *mcp, unsigned int reg)
++{
++      int ret = -ETIME;
++      int i;
++
++      Ser4MCDR2 = reg << 17 | MCDR2_Rd;
++
++      for (i = 0; i < 2; i++) {
++              udelay(mcp->rw_timeout);
++              if (Ser4MCSR & MCSR_CRC) {
++                      ret = Ser4MCDR2 & 0xffff;
++                      break;
++              }
++      }
++
++      if (ret < 0)
++              printk(KERN_WARNING "mcp: read timed out\n");
++
++      return ret;
++}
++
++static void mcp_sa1100_enable(struct mcp *mcp)
++{
++      Ser4MCSR = -1;
++      Ser4MCCR0 |= MCCR0_MCE;
++}
++
++static void mcp_sa1100_disable(struct mcp *mcp)
++{
++      Ser4MCCR0 &= ~MCCR0_MCE;
++}
++
++struct mcp mcp_sa1100 = {
++      owner:                  THIS_MODULE,
++      lock:                   SPIN_LOCK_UNLOCKED,
++      sclk_rate:              11981000,
++      dma_audio_rd:           DMA_Ser4MCP0Rd,
++      dma_audio_wr:           DMA_Ser4MCP0Wr,
++      dma_telco_rd:           DMA_Ser4MCP1Rd,
++      dma_telco_wr:           DMA_Ser4MCP1Wr,
++      set_telecom_divisor:    mcp_sa1100_set_telecom_divisor,
++      set_audio_divisor:      mcp_sa1100_set_audio_divisor,
++      reg_write:              mcp_sa1100_write,
++      reg_read:               mcp_sa1100_read,
++      enable:                 mcp_sa1100_enable,
++      disable:                mcp_sa1100_disable,
++};
++
++/*
++ * This needs re-working
++ */
++static int mcp_sa1100_init(void)
++{
++      struct mcp *mcp = &mcp_sa1100;
++      int ret = -ENODEV;
++
++      if (machine_is_accelent_sa()    ||
++          machine_is_adsbitsy()       || machine_is_assabet()        ||
++          machine_is_cerf()           || machine_is_flexanet()       ||
++          machine_is_freebird()       || machine_is_graphicsclient() ||
++          machine_is_graphicsmaster() || machine_is_lart()           ||
++          machine_is_omnimeter()      || machine_is_pfs168()         ||
++          machine_is_shannon()        || machine_is_simpad()         ||
++          machine_is_simputer()       || machine_is_yopy()) {
++              /*
++               * Setup the PPC unit correctly.
++               */
++              PPDR &= ~PPC_RXD4;
++              PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
++              PSDR |= PPC_RXD4;
++              PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
++              PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
++
++              Ser4MCSR = -1;
++              Ser4MCCR1 = 0;
++              Ser4MCCR0 = 0x00007f7f | MCCR0_ADM;
++
++              /*
++               * Calculate the read/write timeout (us) from the bit clock
++               * rate.  This is the period for 3 64-bit frames.  Always
++               * round this time up.
++               */
++              mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
++                                mcp->sclk_rate;
++
++              ret = mcp_register(mcp);
++      }
++
++      return ret;
++}
++
++module_init(mcp_sa1100_init);
++EXPORT_SYMBOL(mcp_sa1100_init);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/misc/mcp.h
+@@ -0,0 +1,44 @@
++/*
++ *  linux/drivers/misc/mcp.h
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ */
++#ifndef MCP_H
++#define MCP_H
++
++struct mcp {
++      struct module   *owner;
++      spinlock_t      lock;
++      int             use_count;
++      unsigned int    sclk_rate;
++      unsigned int    rw_timeout;
++      dma_device_t    dma_audio_rd;
++      dma_device_t    dma_audio_wr;
++      dma_device_t    dma_telco_rd;
++      dma_device_t    dma_telco_wr;
++      void            (*set_telecom_divisor)(struct mcp *, unsigned int);
++      void            (*set_audio_divisor)(struct mcp *, unsigned int);
++      void            (*reg_write)(struct mcp *, unsigned int, unsigned int);
++      unsigned int    (*reg_read)(struct mcp *, unsigned int);
++      void            (*enable)(struct mcp *);
++      void            (*disable)(struct mcp *);
++};
++
++void mcp_set_telecom_divisor(struct mcp *, unsigned int);
++void mcp_set_audio_divisor(struct mcp *, unsigned int);
++void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
++unsigned int mcp_reg_read(struct mcp *, unsigned int);
++void mcp_enable(struct mcp *);
++void mcp_disable(struct mcp *);
++
++/* noddy implementation alert! */
++struct mcp *mcp_get(void);
++int mcp_register(struct mcp *);
++
++#define mcp_get_sclk_rate(mcp)        ((mcp)->sclk_rate)
++
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00-audio.c
+@@ -0,0 +1,378 @@
++/*
++ *  linux/drivers/misc/ucb1x00-audio.c
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/list.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++
++#include "ucb1x00.h"
++
++#include "../drivers/sound/sa1100-audio.h"
++
++#define MAGIC 0x41544154
++
++struct ucb1x00_audio {
++      struct file_operations  fops;
++      struct file_operations  mops;
++      struct ucb1x00          *ucb;
++      audio_stream_t          output_stream;
++      audio_stream_t          input_stream;
++      audio_state_t           state;
++      unsigned int            rate;
++      int                     dev_id;
++      int                     mix_id;
++      unsigned int            daa_oh_bit;
++      unsigned int            telecom;
++      unsigned int            magic;
++      unsigned int            ctrl_a;
++      unsigned int            ctrl_b;
++
++      /* mixer info */
++      unsigned int            mod_cnt;
++      unsigned short          output_level;
++      unsigned short          input_level;
++};
++
++#define REC_MASK      (SOUND_MASK_VOLUME | SOUND_MASK_MIC)
++#define DEV_MASK      REC_MASK
++
++static int
++ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg)
++{
++      struct ucb1x00_audio *ucba;
++      unsigned int val, gain;
++      int ret = 0;
++
++      ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops);
++
++      if (_IOC_TYPE(cmd) != 'M')
++              return -EINVAL;
++
++      if (cmd == SOUND_MIXER_INFO) {
++              struct mixer_info mi;
++
++              strncpy(mi.id, "UCB1x00", sizeof(mi.id));
++              strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name));
++              mi.modify_counter = ucba->mod_cnt;
++              return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0;
++      }
++
++      if (_IOC_DIR(cmd) & _IOC_WRITE) {
++              unsigned int left, right;
++
++              ret = get_user(val, (unsigned int *)arg);
++              if (ret)
++                      goto out;
++
++              left  = val & 255;
++              right = val >> 8;
++
++              if (left > 100)
++                      left = 100;
++              if (right > 100)
++                      right = 100;
++
++              gain = (left + right) / 2;
++
++              ret = -EINVAL;
++              if (!ucba->telecom) {
++                      switch(_IOC_NR(cmd)) {
++                      case SOUND_MIXER_VOLUME:
++                              ucba->output_level = gain | gain << 8;
++                              ucba->mod_cnt++;
++                              ucba->ctrl_b = (ucba->ctrl_b & 0xff00) |
++                                             ((gain * 31) / 100);
++                              ucb1x00_reg_write(ucba->ucb, UCB_AC_B,
++                                                ucba->ctrl_b);
++                              ret = 0;
++                              break;
++
++                      case SOUND_MIXER_MIC:
++                              ucba->input_level = gain | gain << 8;
++                              ucba->mod_cnt++;
++                              ucba->ctrl_a = (ucba->ctrl_a & 0x7f) |
++                                             (((gain * 31) / 100) << 7);
++                              ucb1x00_reg_write(ucba->ucb, UCB_AC_A,
++                                                ucba->ctrl_a);
++                              ret = 0;
++                              break;
++                      }
++              }
++      }
++
++      if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
++              switch (_IOC_NR(cmd)) {
++              case SOUND_MIXER_VOLUME:
++                      val = ucba->output_level;
++                      break;
++
++              case SOUND_MIXER_MIC:
++                      val = ucba->input_level;
++                      break;
++
++              case SOUND_MIXER_RECSRC:
++              case SOUND_MIXER_RECMASK:
++                      val = ucba->telecom ? 0 : REC_MASK;
++                      break;
++
++              case SOUND_MIXER_DEVMASK:
++                      val = ucba->telecom ? 0 : DEV_MASK;
++                      break;
++
++              case SOUND_MIXER_CAPS:
++              case SOUND_MIXER_STEREODEVS:
++                      val = 0;
++                      break;
++
++              default:
++                      val = 0;
++                      ret = -EINVAL;
++                      break;
++              }
++
++              if (ret == 0)
++                      ret = put_user(val, (int *)arg);
++      }
++ out:
++      return ret;
++}
++
++static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate)
++{
++      unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32;
++      unsigned int div;
++
++      div = (div_rate + (rate / 2)) / rate;
++      if (div < 6)
++              div = 6;
++      if (div > 127)
++              div = 127;
++
++      ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div;
++
++      if (ucba->telecom) {
++              ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0);
++              ucb1x00_set_telecom_divisor(ucba->ucb, div * 32);
++              ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a);
++              ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b);
++      } else {
++              ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0);
++              ucb1x00_set_audio_divisor(ucba->ucb, div * 32);
++              ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a);
++              ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b);
++      }
++
++      ucba->rate = div_rate / div;
++
++      return ucba->rate;
++}
++
++static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba)
++{
++      return ucba->rate;
++}
++
++static void ucb1x00_audio_startup(void *data)
++{
++      struct ucb1x00_audio *ucba = data;
++
++      ucb1x00_enable(ucba->ucb);
++      ucb1x00_audio_setrate(ucba, ucba->rate);
++
++      ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA);
++
++      /*
++       * Take off-hook
++       */
++      if (ucba->daa_oh_bit)
++              ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit);
++}
++
++static void ucb1x00_audio_shutdown(void *data)
++{
++      struct ucb1x00_audio *ucba = data;
++
++      /*
++       * Place on-hook
++       */
++      if (ucba->daa_oh_bit)
++              ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0);
++
++      ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0);
++      ucb1x00_disable(ucba->ucb);
++}
++
++static int
++ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      struct ucb1x00_audio *ucba;
++      int val, ret = 0;
++
++      ucba = list_entry(file->f_op, struct ucb1x00_audio, fops);
++
++      /*
++       * Make sure we have our magic number
++       */
++      if (ucba->magic != MAGIC)
++              return -ENODEV;
++
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *)arg);
++              if (ret)
++                      return ret;
++              if (val != 0)
++                      return -EINVAL;
++              val = 0;
++              break;
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              val = 1;
++              break;
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (int *)arg);
++              if (ret)
++                      return ret;
++              val = ucb1x00_audio_setrate(ucba, val);
++              break;
++
++      case SOUND_PCM_READ_RATE:
++              val = ucb1x00_audio_getrate(ucba);
++              break;
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              val = AFMT_S16_LE;
++              break;
++
++      default:
++              return ucb1x00_mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return put_user(val, (int *)arg);
++}
++
++static int ucb1x00_audio_open(struct inode *inode, struct file *file)
++{
++      struct ucb1x00_audio *ucba;
++
++      ucba = list_entry(file->f_op, struct ucb1x00_audio, fops);
++
++      return sa1100_audio_attach(inode, file, &ucba->state);
++}
++
++static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb)
++{
++      struct ucb1x00_audio *ucba;
++
++      ucba = kmalloc(sizeof(*ucba), GFP_KERNEL);
++      if (ucba) {
++              memset(ucba, 0, sizeof(*ucba));
++
++              ucba->magic = MAGIC;
++              ucba->ucb = ucb;
++              ucba->fops.owner = THIS_MODULE;
++              ucba->fops.open  = ucb1x00_audio_open;
++              ucba->mops.owner = THIS_MODULE;
++              ucba->mops.ioctl = ucb1x00_mixer_ioctl;
++              ucba->state.output_stream = &ucba->output_stream;
++              ucba->state.input_stream = &ucba->input_stream;
++              ucba->state.data = ucba;
++              ucba->state.hw_init = ucb1x00_audio_startup;
++              ucba->state.hw_shutdown = ucb1x00_audio_shutdown;
++              ucba->state.client_ioctl = ucb1x00_audio_ioctl;
++
++              /* There is a bug in the StrongARM causes corrupt MCP data to be sent to
++               * the codec when the FIFOs are empty and writes are made to the OS timer
++               * match register 0. To avoid this we must make sure that data is always
++               * sent to the codec.
++               */
++              ucba->state.need_tx_for_rx = 1;
++
++              init_MUTEX(&ucba->state.sem);
++              ucba->rate = 8000;
++      }
++      return ucba;
++}
++
++static struct ucb1x00_audio *audio, *telecom;
++
++static int __init ucb1x00_audio_init(void)
++{
++      struct ucb1x00 *ucb = ucb1x00_get();
++      struct ucb1x00_audio *a;
++
++      if (!ucb)
++              return -ENODEV;
++
++      a = ucb1x00_audio_alloc(ucb);
++      if (a) {
++              a->state.input_dma  = ucb->mcp->dma_audio_rd;
++              a->state.input_id   = "UCB1x00 audio in";
++              a->state.output_dma = ucb->mcp->dma_audio_wr;
++              a->state.output_id  = "UCB1x00 audio out";
++              a->dev_id = register_sound_dsp(&a->fops, -1);
++              a->mix_id = register_sound_mixer(&a->mops, -1);
++              a->ctrl_a = 0;
++              a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA;
++              audio = a;
++      }
++
++      a = ucb1x00_audio_alloc(ucb);
++      if (a) {
++#if 0
++              a->daa_oh_bit = UCB_IO_8;
++
++              ucb1x00_enable(ucb);
++              ucb1x00_io_write(ucb, a->daa_oh_bit, 0);
++              ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit);
++              ucb1x00_disable(ucb);
++#endif
++
++              a->telecom = 1;
++              a->state.input_dma  = ucb->mcp->dma_telco_rd;
++              a->state.input_id   = "UCB1x00 telco in";
++              a->state.output_dma = ucb->mcp->dma_telco_wr;
++              a->state.output_id  = "UCB1x00 telco out";
++              a->dev_id = register_sound_dsp(&a->fops, -1);
++              a->mix_id = register_sound_mixer(&a->mops, -1);
++              a->ctrl_a = 0;
++              a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA;
++              telecom = a;
++      }
++
++      return 0;
++}
++
++static void __exit ucb1x00_audio_exit(void)
++{
++      unregister_sound_dsp(telecom->dev_id);
++      unregister_sound_dsp(audio->dev_id);
++      unregister_sound_mixer(telecom->mix_id);
++      unregister_sound_mixer(audio->mix_id);
++}
++
++module_init(ucb1x00_audio_init);
++module_exit(ucb1x00_audio_exit);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 telecom/audio driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00-core.c
+@@ -0,0 +1,651 @@
++/*
++ *  linux/drivers/misc/ucb1x00-core.c
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  The UCB1x00 core driver provides basic services for handling IO,
++ *  the ADC, interrupts, and accessing registers.  It is designed
++ *  such that everything goes through this layer, thereby providing
++ *  a consistent locking methodology, as well as allowing the drivers
++ *  to be used on other non-MCP-enabled hardware platforms.
++ *
++ *  Note that all locks are private to this file.  Nothing else may
++ *  touch them.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/interrupt.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/arch/shannon.h>
++
++#include "ucb1x00.h"
++
++/**
++ *    ucb1x00_io_set_dir - set IO direction
++ *    @ucb: UCB1x00 structure describing chip
++ *    @in:  bitfield of IO pins to be set as inputs
++ *    @out: bitfield of IO pins to be set as outputs
++ *
++ *    Set the IO direction of the ten general purpose IO pins on
++ *    the UCB1x00 chip.  The @in bitfield has priority over the
++ *    @out bitfield, in that if you specify a pin as both input
++ *    and output, it will end up as an input.
++ *
++ *    ucb1x00_enable must have been called to enable the comms
++ *    before using this function.
++ *
++ *    This function takes a spinlock, disabling interrupts.
++ */
++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&ucb->io_lock, flags);
++      ucb->io_dir |= out;
++      ucb->io_dir &= ~in;
++
++      ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir);
++      spin_unlock_irqrestore(&ucb->io_lock, flags);
++}
++
++/**
++ *    ucb1x00_io_write - set or clear IO outputs
++ *    @ucb:   UCB1x00 structure describing chip
++ *    @set:   bitfield of IO pins to set to logic '1'
++ *    @clear: bitfield of IO pins to set to logic '0'
++ *
++ *    Set the IO output state of the specified IO pins.  The value
++ *    is retained if the pins are subsequently configured as inputs.
++ *    The @clear bitfield has priority over the @set bitfield -
++ *    outputs will be cleared.
++ *
++ *    ucb1x00_enable must have been called to enable the comms
++ *    before using this function.
++ *
++ *    This function takes a spinlock, disabling interrupts.
++ */
++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave(&ucb->io_lock, flags);
++      ucb->io_out |= set;
++      ucb->io_out &= ~clear;
++
++      ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out);
++      spin_unlock_irqrestore(&ucb->io_lock, flags);
++}
++
++/**
++ *    ucb1x00_io_read - read the current state of the IO pins
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Return a bitfield describing the logic state of the ten
++ *    general purpose IO pins.
++ *
++ *    ucb1x00_enable must have been called to enable the comms
++ *    before using this function.
++ *
++ *    This function does not take any semaphores or spinlocks.
++ */
++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb)
++{
++      return ucb1x00_reg_read(ucb, UCB_IO_DATA);
++}
++
++/*
++ * UCB1300 data sheet says we must:
++ *  1. enable ADC     => 5us (including reference startup time)
++ *  2. select input   => 51*tsibclk  => 4.3us
++ *  3. start conversion       => 102*tsibclk => 8.5us
++ * (tsibclk = 1/11981000)
++ * Period between SIB 128-bit frames = 10.7us
++ */
++
++/**
++ *    ucb1x00_adc_enable - enable the ADC converter
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Enable the ucb1x00 and ADC converter on the UCB1x00 for use.
++ *    Any code wishing to use the ADC converter must call this
++ *    function prior to using it.
++ *
++ *    This function takes the ADC semaphore to prevent two or more
++ *    concurrent uses, and therefore may sleep.  As a result, it
++ *    can only be called from process context, not interrupt
++ *    context.
++ *
++ *    You should release the ADC as soon as possible using
++ *    ucb1x00_adc_disable.
++ */
++void ucb1x00_adc_enable(struct ucb1x00 *ucb)
++{
++      down(&ucb->adc_sem);
++
++      ucb->adc_cr |= UCB_ADC_ENA;
++
++      ucb1x00_enable(ucb);
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
++}
++
++/**
++ *    ucb1x00_adc_read - read the specified ADC channel
++ *    @ucb: UCB1x00 structure describing chip
++ *    @adc_channel: ADC channel mask
++ *    @sync: wait for syncronisation pulse.
++ *
++ *    Start an ADC conversion and wait for the result.  Note that
++ *    synchronised ADC conversions (via the ADCSYNC pin) must wait
++ *    until the trigger is asserted and the conversion is finished.
++ *
++ *    This function currently spins waiting for the conversion to
++ *    complete (2 frames max without sync).
++ *
++ *    If called for a synchronised ADC conversion, it may sleep
++ *    with the ADC semaphore held.
++ */
++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync)
++{
++      unsigned int val;
++
++      if (sync)
++              adc_channel |= UCB_ADC_SYNC_ENA;
++
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel);
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START);
++
++      for (;;) {
++              val = ucb1x00_reg_read(ucb, UCB_ADC_DATA);
++              if (val & UCB_ADC_DAT_VAL)
++                      break;
++              /* yield to other processes */
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(1);
++      }
++
++      return UCB_ADC_DAT(val);
++}
++
++/**
++ *    ucb1x00_adc_disable - disable the ADC converter
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Disable the ADC converter and release the ADC semaphore.
++ */
++void ucb1x00_adc_disable(struct ucb1x00 *ucb)
++{
++      ucb->adc_cr &= ~UCB_ADC_ENA;
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr);
++      ucb1x00_disable(ucb);
++
++      up(&ucb->adc_sem);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_pm (struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      struct ucb1x00 *ucb = (struct ucb1x00 *)dev->data;
++      unsigned int isr;
++
++      if (rqst == PM_RESUME) {
++              ucb1x00_enable(ucb);
++              isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
++              ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++              ucb1x00_disable(ucb);
++      }
++
++      return 0;
++}
++#endif
++
++/*
++ * UCB1x00 Interrupt handling.
++ *
++ * The UCB1x00 can generate interrupts when the SIBCLK is stopped.
++ * Since we need to read an internal register, we must re-enable
++ * SIBCLK to talk to the chip.  We leave the clock running until
++ * we have finished processing all interrupts from the chip.
++ */
++static void ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs)
++{
++      struct ucb1x00 *ucb = devid;
++      struct ucb1x00_irq *irq;
++      unsigned int isr, i;
++
++      ucb1x00_enable(ucb);
++      isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++      for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++)
++              if (isr & 1 && irq->fn)
++                      irq->fn(i, irq->devid);
++      ucb1x00_disable(ucb);
++}
++
++/**
++ *    ucb1x00_hook_irq - hook a UCB1x00 interrupt
++ *    @ucb:   UCB1x00 structure describing chip
++ *    @idx:   interrupt index
++ *    @fn:    function to call when interrupt is triggered
++ *    @devid: device id to pass to interrupt handler
++ *
++ *    Hook the specified interrupt.  You can only register one handler
++ *    for each interrupt source.  The interrupt source is not enabled
++ *    by this function; use ucb1x00_enable_irq instead.
++ *
++ *    Interrupt handlers will be called with other interrupts enabled.
++ *
++ *    Returns zero on success, or one of the following errors:
++ *     -EINVAL if the interrupt index is invalid
++ *     -EBUSY if the interrupt has already been hooked
++ */
++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid)
++{
++      struct ucb1x00_irq *irq;
++      int ret = -EINVAL;
++
++      if (idx < 16) {
++              irq = ucb->irq_handler + idx;
++              ret = -EBUSY;
++
++              spin_lock_irq(&ucb->lock);
++              if (irq->fn == NULL) {
++                      irq->devid = devid;
++                      irq->fn = fn;
++                      ret = 0;
++              }
++              spin_unlock_irq(&ucb->lock);
++      }
++      return ret;
++}
++
++/**
++ *    ucb1x00_enable_irq - enable an UCB1x00 interrupt source
++ *    @ucb: UCB1x00 structure describing chip
++ *    @idx: interrupt index
++ *    @edges: interrupt edges to enable
++ *
++ *    Enable the specified interrupt to trigger on %UCB_RISING,
++ *    %UCB_FALLING or both edges.  The interrupt should have been
++ *    hooked by ucb1x00_hook_irq.
++ */
++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
++{
++      unsigned long flags;
++
++      if (idx < 16) {
++              spin_lock_irqsave(&ucb->lock, flags);
++
++              ucb1x00_enable(ucb);
++              if (edges & UCB_RISING) {
++                      ucb->irq_ris_enbl |= 1 << idx;
++                      ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++              }
++              if (edges & UCB_FALLING) {
++                      ucb->irq_fal_enbl |= 1 << idx;
++                      ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++              }
++              ucb1x00_disable(ucb);
++              spin_unlock_irqrestore(&ucb->lock, flags);
++      }
++}
++
++/**
++ *    ucb1x00_disable_irq - disable an UCB1x00 interrupt source
++ *    @ucb: UCB1x00 structure describing chip
++ *    @edges: interrupt edges to disable
++ *
++ *    Disable the specified interrupt triggering on the specified
++ *    (%UCB_RISING, %UCB_FALLING or both) edges.
++ */
++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges)
++{
++      unsigned long flags;
++
++      if (idx < 16) {
++              spin_lock_irqsave(&ucb->lock, flags);
++
++              ucb1x00_enable(ucb);
++              if (edges & UCB_RISING) {
++                      ucb->irq_ris_enbl &= ~(1 << idx);
++                      ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++              }
++              if (edges & UCB_FALLING) {
++                      ucb->irq_fal_enbl &= ~(1 << idx);
++                      ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++              }
++              ucb1x00_disable(ucb);
++              spin_unlock_irqrestore(&ucb->lock, flags);
++      }
++}
++
++/**
++ *    ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt
++ *    @ucb: UCB1x00 structure describing chip
++ *    @idx: interrupt index
++ *    @devid: device id.
++ *
++ *    Disable the interrupt source and remove the handler.  devid must
++ *    match the devid passed when hooking the interrupt.
++ *
++ *    Returns zero on success, or one of the following errors:
++ *     -EINVAL if the interrupt index is invalid
++ *     -ENOENT if devid does not match
++ */
++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid)
++{
++      struct ucb1x00_irq *irq;
++      int ret;
++
++      if (idx >= 16)
++              goto bad;
++
++      irq = ucb->irq_handler + idx;
++      ret = -ENOENT;
++
++      spin_lock_irq(&ucb->lock);
++      if (irq->devid == devid) {
++              ucb->irq_ris_enbl &= ~(1 << idx);
++              ucb->irq_fal_enbl &= ~(1 << idx);
++
++              ucb1x00_enable(ucb);
++              ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);
++              ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);
++              ucb1x00_disable(ucb);
++
++              irq->fn = NULL;
++              irq->devid = NULL;
++              ret = 0;
++      }
++      spin_unlock_irq(&ucb->lock);
++      return ret;
++
++bad:
++      printk(KERN_ERR "%s: freeing bad irq %d\n", __FUNCTION__, idx);
++      return -EINVAL;
++}
++
++/*
++ * Try to probe our interrupt, rather than relying on lots of
++ * hard-coded machine dependencies.  For reference, the expected
++ * IRQ mappings are:
++ *
++ *    Machine         Default IRQ
++ *    adsbitsy        IRQ_GPCIN4
++ *    cerf            IRQ_GPIO_UCB1200_IRQ
++ *    flexanet        IRQ_GPIO_GUI
++ *    freebird        IRQ_GPIO_FREEBIRD_UCB1300_IRQ
++ *    graphicsclient  IRQ_GRAPHICSCLIENT_UCB1200
++ *    graphicsmaster  IRQ_GRAPHICSMASTER_UCB1200
++ *    lart            LART_IRQ_UCB1200
++ *    omnimeter       IRQ_GPIO23
++ *    pfs168          IRQ_GPIO_UCB1300_IRQ
++ *    simpad          IRQ_GPIO_UCB1300_IRQ
++ *    shannon         SHANNON_IRQ_GPIO_IRQ_CODEC
++ *    yopy            IRQ_GPIO_UCB1200_IRQ
++ */
++static int __init ucb1x00_detect_irq(struct ucb1x00 *ucb)
++{
++      unsigned long mask;
++
++      mask = probe_irq_on();
++      if (!mask)
++              return NO_IRQ;
++
++      /*
++       * Enable the ADC interrupt.
++       */
++      ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
++      ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++      /*
++       * Cause an ADC interrupt.
++       */
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
++
++      /*
++       * Wait for the conversion to complete.
++       */
++      while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0);
++      ucb1x00_reg_write(ucb, UCB_ADC_CR, 0);
++
++      /*
++       * Disable and clear interrupt.
++       */
++      ucb1x00_reg_write(ucb, UCB_IE_RIS, 0);
++      ucb1x00_reg_write(ucb, UCB_IE_FAL, 0);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
++      ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);
++
++      /*
++       * Read triggered interrupt.
++       */
++      return probe_irq_off(mask);
++}
++
++/*
++ * This configures the UCB1x00 layer depending on the machine type
++ * we're running on.  The UCB1x00 drivers should not contain any
++ * machine dependencies.
++ *
++ * We can get rid of some of these dependencies by using existing
++ * facilities provided by the kernel - namely IRQ probing.  The
++ * machine specific files are expected to setup the IRQ levels on
++ * initialisation.  With any luck, we'll get rid of all the
++ * machine dependencies here.
++ */
++static int __init ucb1x00_configure(struct ucb1x00 *ucb)
++{
++      unsigned int irq_gpio_pin = 0;
++      int irq, default_irq = NO_IRQ;
++
++      if (machine_is_adsbitsy())
++              default_irq = IRQ_GPCIN4;
++
++//    if (machine_is_assabet())
++//            default_irq = IRQ_GPIO23;
++
++#ifdef CONFIG_SA1100_CERF
++      if (machine_is_cerf())
++              default_irq = IRQ_GPIO_UCB1200_IRQ;
++#endif
++#ifdef CONFIG_SA1100_FREEBIRD
++      if (machine_is_freebird())
++              default_irq = IRQ_GPIO_FREEBIRD_UCB1300_IRQ;
++#endif
++#if defined(CONFIG_SA1100_GRAPHICSCLIENT)
++//    if (machine_is_graphicsclient())
++//            default_irq = IRQ_GRAPHICSCLIENT_UCB1200;
++#endif
++#if defined(CONFIG_SA1100_GRAPICSMASTER)
++      if (machine_is_graphicsmaster())
++              default_irq = IRQ_GRAPHICSMASTER_UCB1200;
++#endif
++#ifdef CONFIG_SA1100_LART
++      if (machine_is_lart()) {
++              default_irq = LART_IRQ_UCB1200;
++              irq_gpio_pin = LART_GPIO_UCB1200;
++      }
++#endif
++      if (machine_is_omnimeter())
++              default_irq = IRQ_GPIO23;
++
++#ifdef CONFIG_SA1100_PFS168
++      if (machine_is_pfs168())
++              default_irq = IRQ_GPIO_UCB1300_IRQ;
++#endif
++#ifdef CONFIG_SA1100_SIMPAD
++      if (machine_is_simpad())
++              default_irq = IRQ_GPIO_UCB1300_IRQ;
++#endif
++#ifdef CONFIG_SA1100_SIMPUTER
++      if (machine_is_simputer()) {
++              default_irq = IRQ_GPIO_UCB1300_IRQ;
++              irq_gpio_pin = GPIO_UCB1300_IRQ;
++    }
++#endif
++      if (machine_is_shannon())
++              default_irq = SHANNON_IRQ_GPIO_IRQ_CODEC;
++#ifdef CONFIG_SA1100_YOPY
++      if (machine_is_yopy())
++              default_irq = IRQ_GPIO_UCB1200_IRQ;
++#endif
++#ifdef CONFIG_SA1100_ACCELENT
++      if (machine_is_accelent_sa()) {
++              ucb->irq = IRQ_GPIO_UCB1200_IRQ;
++              irq_gpio_pin = GPIO_UCB1200_IRQ;
++      }
++#endif
++
++      /*
++       * Eventually, this will disappear.
++       */
++      if (irq_gpio_pin)
++              set_GPIO_IRQ_edge(irq_gpio_pin, GPIO_RISING_EDGE);
++
++      irq = ucb1x00_detect_irq(ucb);
++      if (irq != NO_IRQ) {
++              if (default_irq != NO_IRQ && irq != default_irq)
++                      printk(KERN_ERR "UCB1x00: probed IRQ%d != default IRQ%d\n",
++                              irq, default_irq);
++              if (irq == default_irq)
++                      printk(KERN_ERR "UCB1x00: probed IRQ%d correctly. "
++                              "Please remove machine dependencies from "
++                              "ucb1x00-core.c\n", irq);
++              ucb->irq = irq;
++      } else {
++              printk(KERN_ERR "UCB1x00: IRQ probe failed, using IRQ%d\n",
++                      default_irq);
++              ucb->irq = default_irq;
++      }
++
++      return ucb->irq == NO_IRQ ? -ENODEV : 0;
++}
++
++struct ucb1x00 *my_ucb;
++
++/**
++ *    ucb1x00_get - get the UCB1x00 structure describing a chip
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Return the UCB1x00 structure describing a chip.
++ *
++ *    FIXME: Currently very noddy indeed, which currently doesn't
++ *    matter since we only support one chip.
++ */
++struct ucb1x00 *ucb1x00_get(void)
++{
++      return my_ucb;
++}
++
++static int __init ucb1x00_init(void)
++{
++      struct mcp *mcp;
++      unsigned int id;
++      int ret = -ENODEV;
++
++      mcp = mcp_get();
++      if (!mcp)
++              goto no_mcp;
++
++      mcp_enable(mcp);
++      id = mcp_reg_read(mcp, UCB_ID);
++
++      if (id != UCB_ID_1200 && id != UCB_ID_1300) {
++              printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
++              goto out;
++      }
++
++      my_ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL);
++      ret = -ENOMEM;
++      if (!my_ucb)
++              goto out;
++
++      if (machine_is_shannon()) {
++              /* reset the codec */
++              GPDR |= SHANNON_GPIO_CODEC_RESET;
++              GPCR = SHANNON_GPIO_CODEC_RESET;
++              GPSR = SHANNON_GPIO_CODEC_RESET;
++
++      }
++
++      memset(my_ucb, 0, sizeof(struct ucb1x00));
++
++      spin_lock_init(&my_ucb->lock);
++      spin_lock_init(&my_ucb->io_lock);
++      sema_init(&my_ucb->adc_sem, 1);
++
++      my_ucb->id  = id;
++      my_ucb->mcp = mcp;
++
++      ret = ucb1x00_configure(my_ucb);
++      if (ret)
++              goto out;
++
++      ret = request_irq(my_ucb->irq, ucb1x00_irq, 0, "UCB1x00", my_ucb);
++      if (ret) {
++              printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
++                      my_ucb->irq, ret);
++              kfree(my_ucb);
++              my_ucb = NULL;
++              goto out;
++      }
++
++#ifdef CONFIG_PM
++      my_ucb->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_pm);
++      if (my_ucb->pmdev == NULL)
++              printk("ucb1x00: unable to register in PM.\n");
++      else
++              my_ucb->pmdev->data = my_ucb;
++#endif
++
++out:
++      mcp_disable(mcp);
++no_mcp:
++      return ret;
++}
++
++static void __exit ucb1x00_exit(void)
++{
++      free_irq(my_ucb->irq, my_ucb);
++      kfree(my_ucb);
++}
++
++module_init(ucb1x00_init);
++module_exit(ucb1x00_exit);
++
++EXPORT_SYMBOL(ucb1x00_get);
++
++EXPORT_SYMBOL(ucb1x00_io_set_dir);
++EXPORT_SYMBOL(ucb1x00_io_write);
++EXPORT_SYMBOL(ucb1x00_io_read);
++
++EXPORT_SYMBOL(ucb1x00_adc_enable);
++EXPORT_SYMBOL(ucb1x00_adc_read);
++EXPORT_SYMBOL(ucb1x00_adc_disable);
++
++EXPORT_SYMBOL(ucb1x00_hook_irq);
++EXPORT_SYMBOL(ucb1x00_free_irq);
++EXPORT_SYMBOL(ucb1x00_enable_irq);
++EXPORT_SYMBOL(ucb1x00_disable_irq);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 core driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00-ts.c
+@@ -0,0 +1,664 @@
++/*
++ *  linux/drivers/misc/ucb1x00-ts.c
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * 21-Jan-2002 <jco@ict.es> :
++ *
++ * Added support for synchronous A/D mode. This mode is useful to
++ * avoid noise induced in the touchpanel by the LCD, provided that
++ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
++ * It is important to note that the signal connected to the ADCSYNC
++ * pin should provide pulses even when the LCD is blanked, otherwise
++ * a pen touch needed to unblank the LCD will never be read.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/pm.h>
++
++#include <asm/dma.h>
++#include <asm/semaphore.h>
++
++#include "ucb1x00.h"
++
++/*
++ * Define this if you want the UCB1x00 stuff to talk to the input layer
++ */
++#undef USE_INPUT
++
++#ifndef USE_INPUT
++
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/poll.h>
++
++/*
++ * This structure is nonsense - millisecs is not very useful
++ * since the field size is too small.  Also, we SHOULD NOT
++ * be exposing jiffies to user space directly.
++ */
++struct ts_event {
++      u16             pressure;
++      u16             x;
++      u16             y;
++      u16             pad;
++      struct timeval  stamp;
++};
++
++#define NR_EVENTS     16
++
++#else
++
++#include <linux/input.h>
++
++#endif
++
++struct ucb1x00_ts {
++#ifdef USE_INPUT
++      struct input_dev        idev;
++#endif
++      struct ucb1x00          *ucb;
++#ifdef CONFIG_PM
++      struct pm_dev           *pmdev;
++#endif
++
++      wait_queue_head_t       irq_wait;
++      struct semaphore        sem;
++      struct completion       init_exit;
++      struct task_struct      *rtask;
++      int                     use_count;
++      u16                     x_res;
++      u16                     y_res;
++
++#ifndef USE_INPUT
++      struct fasync_struct    *fasync;
++      wait_queue_head_t       read_wait;
++      u8                      evt_head;
++      u8                      evt_tail;
++      struct ts_event         events[NR_EVENTS];
++#endif
++      int                     restart:1;
++      int                     adcsync:1;
++};
++
++static struct ucb1x00_ts ucbts;
++static int adcsync = UCB_NOSYNC;
++
++static int ucb1x00_ts_startup(struct ucb1x00_ts *ts);
++static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts);
++
++#ifndef USE_INPUT
++
++#define ucb1x00_ts_evt_pending(ts)    ((volatile u8)(ts)->evt_head != (ts)->evt_tail)
++#define ucb1x00_ts_evt_get(ts)                ((ts)->events + (ts)->evt_tail)
++#define ucb1x00_ts_evt_pull(ts)               ((ts)->evt_tail = ((ts)->evt_tail + 1) & (NR_EVENTS - 1))
++#define ucb1x00_ts_evt_clear(ts)      ((ts)->evt_head = (ts)->evt_tail = 0)
++
++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
++{
++      int next_head;
++
++      next_head = (ts->evt_head + 1) & (NR_EVENTS - 1);
++      if (next_head != ts->evt_tail) {
++              ts->events[ts->evt_head].pressure = pressure;
++              ts->events[ts->evt_head].x = x;
++              ts->events[ts->evt_head].y = y;
++              do_gettimeofday(&ts->events[ts->evt_head].stamp);
++              ts->evt_head = next_head;
++
++              if (ts->fasync)
++                      kill_fasync(&ts->fasync, SIGIO, POLL_IN);
++              wake_up_interruptible(&ts->read_wait);
++      }
++}
++
++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
++{
++      ucb1x00_ts_evt_add(ts, 0, 0, 0);
++}
++
++/*
++ * User space driver interface.
++ */
++static ssize_t
++ucb1x00_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      struct ucb1x00_ts *ts = filp->private_data;
++      char *ptr = buffer;
++      int err = 0;
++
++      add_wait_queue(&ts->read_wait, &wait);
++      while (count >= sizeof(struct ts_event)) {
++              err = -ERESTARTSYS;
++              if (signal_pending(current))
++                      break;
++
++              if (ucb1x00_ts_evt_pending(ts)) {
++                      struct ts_event *evt = ucb1x00_ts_evt_get(ts);
++
++                      err = copy_to_user(ptr, evt, sizeof(struct ts_event));
++                      ucb1x00_ts_evt_pull(ts);
++
++                      if (err)
++                              break;
++
++                      ptr += sizeof(struct ts_event);
++                      count -= sizeof(struct ts_event);
++                      continue;
++              }
++
++              set_current_state(TASK_INTERRUPTIBLE);
++              err = -EAGAIN;
++              if (filp->f_flags & O_NONBLOCK)
++                      break;
++              schedule();
++      }
++      current->state = TASK_RUNNING;
++      remove_wait_queue(&ts->read_wait, &wait);
++ 
++      return ptr == buffer ? err : ptr - buffer;
++}
++
++static unsigned int ucb1x00_ts_poll(struct file *filp, poll_table *wait)
++{
++      struct ucb1x00_ts *ts = filp->private_data;
++      int ret = 0;
++
++      poll_wait(filp, &ts->read_wait, wait);
++      if (ucb1x00_ts_evt_pending(ts))
++              ret = POLLIN | POLLRDNORM;
++
++      return ret;
++}
++
++static int ucb1x00_ts_fasync(int fd, struct file *filp, int on)
++{
++      struct ucb1x00_ts *ts = filp->private_data;
++
++      return fasync_helper(fd, filp, on, &ts->fasync);
++}
++
++static int ucb1x00_ts_open(struct inode *inode, struct file *filp)
++{
++      struct ucb1x00_ts *ts = &ucbts;
++      int ret = 0;
++
++      ret = ucb1x00_ts_startup(ts);
++      if (ret == 0)
++              filp->private_data = ts;
++
++      return ret;
++}
++
++/*
++ * Release touchscreen resources.  Disable IRQs.
++ */
++static int ucb1x00_ts_release(struct inode *inode, struct file *filp)
++{
++      struct ucb1x00_ts *ts = filp->private_data;
++
++      down(&ts->sem);
++      ucb1x00_ts_fasync(-1, filp, 0);
++      ucb1x00_ts_shutdown(ts);
++      up(&ts->sem);
++
++      return 0;
++}
++
++static struct file_operations ucb1x00_fops = {
++      owner:          THIS_MODULE,
++      read:           ucb1x00_ts_read,
++      poll:           ucb1x00_ts_poll,
++      open:           ucb1x00_ts_open,
++      release:        ucb1x00_ts_release,
++      fasync:         ucb1x00_ts_fasync,
++};
++
++/*
++ * The official UCB1x00 touchscreen is a miscdevice:
++ *   10 char        Non-serial mice, misc features
++ *                   14 = /dev/touchscreen/ucb1x00  UCB 1x00 touchscreen
++ */
++static struct miscdevice ucb1x00_ts_dev = {
++      minor:  14,
++      name:   "touchscreen/ucb1x00",
++      fops:   &ucb1x00_fops,
++};
++
++static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts)
++{
++      init_waitqueue_head(&ts->read_wait);
++      return misc_register(&ucb1x00_ts_dev);
++}
++
++static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts)
++{
++      misc_deregister(&ucb1x00_ts_dev);
++}
++
++#else
++
++#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)
++{
++      input_report_abs(&ts->idev, ABS_X, x);
++      input_report_abs(&ts->idev, ABS_Y, y);
++      input_report_abs(&ts->idev, ABS_PRESSURE, pressure);
++}
++
++static int ucb1x00_ts_open(struct input_dev *idev)
++{
++      struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
++
++      return ucb1x00_ts_startup(ts);
++}
++
++static void ucb1x00_ts_close(struct input_dev *idev)
++{
++      struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev;
++
++      down(&ts->sem);
++      ucb1x00_ts_shutdown(ts);
++      up(&ts->sem);
++}
++
++static inline int ucb1x00_ts_register(struct ucb1x00_ts *ts)
++{
++      ts->idev.name      = "Touchscreen panel";
++      ts->idev.idproduct = ts->ucb->id;
++      ts->idev.open      = ucb1x00_ts_open;
++      ts->idev.close     = ucb1x00_ts_close;
++
++      __set_bit(EV_ABS, ts->idev.evbit);
++      __set_bit(ABS_X, ts->idev.absbit);
++      __set_bit(ABS_Y, ts->idev.absbit);
++      __set_bit(ABS_PRESSURE, ts->idev.absbit);
++
++      input_register_device(&ts->idev);
++
++      return 0;
++}
++
++static inline void ucb1x00_ts_deregister(struct ucb1x00_ts *ts)
++{
++      input_unregister_device(&ts->idev);
++}
++
++#endif
++
++/*
++ * Switch to interrupt mode.
++ */
++static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
++{
++      ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++                      UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
++                      UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
++                      UCB_TS_CR_MODE_INT);
++}
++
++/*
++ * Switch to pressure mode, and read pressure.  We don't need to wait
++ * here, since both plates are being driven.
++ */
++static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
++{
++      ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++                      UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
++                      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);
++}
++
++/*
++ * Switch to X position mode and measure Y plate.  We switch the plate
++ * configuration in pressure mode, then switch to position mode.  This
++ * gives a faster response time.  Even so, we need to wait about 55us
++ * for things to stabilise.
++ */
++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);
++      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);
++
++      return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync);
++}
++
++/*
++ * Switch to Y position mode and measure X plate.  We switch the plate
++ * configuration in pressure mode, then switch to position mode.  This
++ * gives a faster response time.  Even so, we need to wait about 55us
++ * for things to stabilise.
++ */
++static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
++{
++      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_MODE_POS | UCB_TS_CR_BIAS_ENA);
++
++      udelay(55);
++
++      return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync);
++}
++
++/*
++ * Switch to X plate resistance mode.  Set MX to ground, PX to
++ * supply.  Measure current.
++ */
++static inline unsigned int ucb1x00_ts_read_xres(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);
++      return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
++}
++
++/*
++ * Switch to Y plate resistance mode.  Set MY to ground, PY to
++ * supply.  Measure current.
++ */
++static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
++{
++      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);
++      return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
++}
++
++/*
++ * This is a RT kernel thread that handles the ADC accesses
++ * (mainly so we can use semaphores in the UCB1200 core code
++ * to serialise accesses to the ADC).
++ */
++static int ucb1x00_thread(void *_ts)
++{
++      struct ucb1x00_ts *ts = _ts;
++      struct task_struct *tsk = current;
++      DECLARE_WAITQUEUE(wait, tsk);
++      int valid;
++
++      ts->rtask = tsk;
++
++      daemonize();
++      reparent_to_init();
++      strcpy(tsk->comm, "ktsd");
++      tsk->tty = NULL;
++      /*
++       * 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;
++
++      /* only want to receive SIGKILL */
++      spin_lock_irq(&tsk->sigmask_lock);
++      siginitsetinv(&tsk->blocked, sigmask(SIGKILL));
++      recalc_sigpending(tsk);
++      spin_unlock_irq(&tsk->sigmask_lock);
++
++      complete(&ts->init_exit);
++
++      valid = 0;
++
++      add_wait_queue(&ts->irq_wait, &wait);
++      for (;;) {
++              unsigned int x, y, p, val;
++              signed long timeout;
++
++              ts->restart = 0;
++
++              ucb1x00_adc_enable(ts->ucb);
++
++              x = ucb1x00_ts_read_xpos(ts);
++              y = ucb1x00_ts_read_ypos(ts);
++              p = ucb1x00_ts_read_pressure(ts);
++
++              /*
++               * Switch back to interrupt mode.
++               */
++              ucb1x00_ts_mode_int(ts);
++              ucb1x00_adc_disable(ts->ucb);
++
++              set_task_state(tsk, TASK_UNINTERRUPTIBLE);
++              schedule_timeout(HZ / 100);
++              if (signal_pending(tsk))
++                      break;
++
++              ucb1x00_enable(ts->ucb);
++              val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
++
++              if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) {
++                      set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++                      ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
++                      ucb1x00_disable(ts->ucb);
++
++                      /*
++                       * If we spat out a valid sample set last time,
++                       * spit out a "pen off" sample here.
++                       */
++                      if (valid) {
++                              ucb1x00_ts_event_release(ts);
++                              valid = 0;
++                      }
++
++                      timeout = MAX_SCHEDULE_TIMEOUT;
++              } else {
++                      ucb1x00_disable(ts->ucb);
++
++                      /*
++                       * Filtering is policy.  Policy belongs in user
++                       * space.  We therefore leave it to user space
++                       * to do any filtering they please.
++                       */
++                      if (!ts->restart) {
++                              ucb1x00_ts_evt_add(ts, p, x, y);
++                              valid = 1;
++                      }
++
++                      set_task_state(tsk, TASK_INTERRUPTIBLE);
++                      timeout = HZ / 100;
++              }
++
++              schedule_timeout(timeout);
++              if (signal_pending(tsk))
++                      break;
++      }
++
++      remove_wait_queue(&ts->irq_wait, &wait);
++
++      ts->rtask = NULL;
++      ucb1x00_ts_evt_clear(ts);
++      complete_and_exit(&ts->init_exit, 0);
++}
++
++/*
++ * We only detect touch screen _touches_ with this interrupt
++ * handler, and even then we just schedule our task.
++ */
++static void ucb1x00_ts_irq(int idx, void *id)
++{
++      struct ucb1x00_ts *ts = id;
++      ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
++      wake_up(&ts->irq_wait);
++}
++
++static int ucb1x00_ts_startup(struct ucb1x00_ts *ts)
++{
++      int ret = 0;
++
++      if (down_interruptible(&ts->sem))
++              return -EINTR;
++
++      if (ts->use_count++ != 0)
++              goto out;
++
++      if (ts->rtask)
++              panic("ucb1x00: rtask running?");
++
++      init_waitqueue_head(&ts->irq_wait);
++      ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
++      if (ret < 0)
++              goto out;
++
++      /*
++       * If we do this at all, we should allow the user to
++       * measure and read the X and Y resistance at any time.
++       */
++      ucb1x00_adc_enable(ts->ucb);
++      ts->x_res = ucb1x00_ts_read_xres(ts);
++      ts->y_res = ucb1x00_ts_read_yres(ts);
++      ucb1x00_adc_disable(ts->ucb);
++
++      init_completion(&ts->init_exit);
++      ret = kernel_thread(ucb1x00_thread, ts, 0);
++      if (ret >= 0) {
++              wait_for_completion(&ts->init_exit);
++              ret = 0;
++      } else {
++              ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
++      }
++
++ out:
++      if (ret)
++              ts->use_count--;
++      up(&ts->sem);
++      return ret;
++}
++
++/*
++ * Release touchscreen resources.  Disable IRQs.
++ */
++static void ucb1x00_ts_shutdown(struct ucb1x00_ts *ts)
++{
++      if (--ts->use_count == 0) {
++              if (ts->rtask) {
++                      send_sig(SIGKILL, ts->rtask, 1);
++                      wait_for_completion(&ts->init_exit);
++              }
++
++              ucb1x00_enable(ts->ucb);
++              ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
++              ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
++              ucb1x00_disable(ts->ucb);
++      }
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_ts_pm (struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      struct ucb1x00_ts *ts = (struct ucb1x00_ts *) (dev->data);
++
++      if (rqst == PM_RESUME && ts->rtask != NULL) {
++              /*
++               * Restart the TS thread to ensure the
++               * TS interrupt mode is set up again
++               * after sleep.
++               */
++              ts->restart = 1;
++              wake_up(&ts->irq_wait);
++      }
++      return 0;
++}
++#endif
++
++
++/*
++ * Initialisation.
++ */
++static int __init ucb1x00_ts_init(void)
++{
++      struct ucb1x00_ts *ts = &ucbts;
++
++      ts->ucb = ucb1x00_get();
++      if (!ts->ucb)
++              return -ENODEV;
++
++      ts->adcsync = adcsync;
++      init_MUTEX(&ts->sem);
++
++#ifdef CONFIG_PM
++      ts->pmdev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, ucb1x00_ts_pm);
++      if (ts->pmdev == NULL)
++              printk("ucb1x00_ts: unable to register in PM.\n");
++      else
++              ts->pmdev->data = ts;
++#endif
++      return ucb1x00_ts_register(ts);
++}
++
++static void __exit ucb1x00_ts_exit(void)
++{
++      struct ucb1x00_ts *ts = &ucbts;
++
++      ucb1x00_ts_deregister(ts);
++
++#ifdef CONFIG_PM
++      if (ts->pmdev)
++              pm_unregister(ts->pmdev);
++#endif
++}
++
++#ifndef MODULE
++
++/*
++ * Parse kernel command-line options.
++ *
++ * syntax : ucbts=[sync|nosync],...
++ */
++static int __init ucb1x00_ts_setup(char *str)
++{
++      char *p;
++
++      while ((p = strsep(&str, ",")) != NULL) {
++              if (strcmp(p, "sync") == 0)
++                      adcsync = UCB_SYNC;
++      }
++
++      return 1;
++}
++
++__setup("ucbts=", ucb1x00_ts_setup);
++
++#else
++
++MODULE_PARM(adcsync, "i");
++MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal");
++
++#endif
++
++module_init(ucb1x00_ts_init);
++module_exit(ucb1x00_ts_exit);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/misc/ucb1x00.h
+@@ -0,0 +1,232 @@
++/*
++ *  linux/drivers/misc/ucb1x00.h
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ */
++#ifndef UCB1200_H
++#define UCB1200_H
++
++#define UCB_IO_DATA   0x00
++#define UCB_IO_DIR    0x01
++
++#define UCB_IO_0              (1 << 0)
++#define UCB_IO_1              (1 << 1)
++#define UCB_IO_2              (1 << 2)
++#define UCB_IO_3              (1 << 3)
++#define UCB_IO_4              (1 << 4)
++#define UCB_IO_5              (1 << 5)
++#define UCB_IO_6              (1 << 6)
++#define UCB_IO_7              (1 << 7)
++#define UCB_IO_8              (1 << 8)
++#define UCB_IO_9              (1 << 9)
++
++#define UCB_IE_RIS    0x02
++#define UCB_IE_FAL    0x03
++#define UCB_IE_STATUS 0x04
++#define UCB_IE_CLEAR  0x04
++#define UCB_IE_ADC            (1 << 11)
++#define UCB_IE_TSPX           (1 << 12)
++#define UCB_IE_TSMX           (1 << 13)
++#define UCB_IE_TCLIP          (1 << 14)
++#define UCB_IE_ACLIP          (1 << 15)
++
++#define UCB_IRQ_TSPX          12
++
++#define UCB_TC_A      0x05
++#define UCB_TC_A_LOOP         (1 << 7)        /* UCB1200 */
++#define UCB_TC_A_AMPL         (1 << 7)        /* UCB1300 */
++
++#define UCB_TC_B      0x06
++#define UCB_TC_B_VOICE_ENA    (1 << 3)
++#define UCB_TC_B_CLIP         (1 << 4)
++#define UCB_TC_B_ATT          (1 << 6)
++#define UCB_TC_B_SIDE_ENA     (1 << 11)
++#define UCB_TC_B_MUTE         (1 << 13)
++#define UCB_TC_B_IN_ENA               (1 << 14)
++#define UCB_TC_B_OUT_ENA      (1 << 15)
++
++#define UCB_AC_A      0x07
++#define UCB_AC_B      0x08
++#define UCB_AC_B_LOOP         (1 << 8)
++#define UCB_AC_B_MUTE         (1 << 13)
++#define UCB_AC_B_IN_ENA               (1 << 14)
++#define UCB_AC_B_OUT_ENA      (1 << 15)
++
++#define UCB_TS_CR     0x09
++#define UCB_TS_CR_TSMX_POW    (1 << 0)
++#define UCB_TS_CR_TSPX_POW    (1 << 1)
++#define UCB_TS_CR_TSMY_POW    (1 << 2)
++#define UCB_TS_CR_TSPY_POW    (1 << 3)
++#define UCB_TS_CR_TSMX_GND    (1 << 4)
++#define UCB_TS_CR_TSPX_GND    (1 << 5)
++#define UCB_TS_CR_TSMY_GND    (1 << 6)
++#define UCB_TS_CR_TSPY_GND    (1 << 7)
++#define UCB_TS_CR_MODE_INT    (0 << 8)
++#define UCB_TS_CR_MODE_PRES   (1 << 8)
++#define UCB_TS_CR_MODE_POS    (2 << 8)
++#define UCB_TS_CR_BIAS_ENA    (1 << 11)
++#define UCB_TS_CR_TSPX_LOW    (1 << 12)
++#define UCB_TS_CR_TSMX_LOW    (1 << 13)
++
++#define UCB_ADC_CR    0x0a
++#define UCB_ADC_SYNC_ENA      (1 << 0)
++#define UCB_ADC_VREFBYP_CON   (1 << 1)
++#define UCB_ADC_INP_TSPX      (0 << 2)
++#define UCB_ADC_INP_TSMX      (1 << 2)
++#define UCB_ADC_INP_TSPY      (2 << 2)
++#define UCB_ADC_INP_TSMY      (3 << 2)
++#define UCB_ADC_INP_AD0               (4 << 2)
++#define UCB_ADC_INP_AD1               (5 << 2)
++#define UCB_ADC_INP_AD2               (6 << 2)
++#define UCB_ADC_INP_AD3               (7 << 2)
++#define UCB_ADC_EXT_REF               (1 << 5)
++#define UCB_ADC_START         (1 << 7)
++#define UCB_ADC_ENA           (1 << 15)
++
++#define UCB_ADC_DATA  0x0b
++#define UCB_ADC_DAT_VAL               (1 << 15)
++#define UCB_ADC_DAT(x)                (((x) & 0x7fe0) >> 5)
++
++#define UCB_ID                0x0c
++#define UCB_ID_1200           0x1004
++#define UCB_ID_1300           0x1005
++
++#define UCB_MODE      0x0d
++#define UCB_MODE_DYN_VFLAG_ENA        (1 << 12)
++#define UCB_MODE_AUD_OFF_CAN  (1 << 13)
++
++#include "mcp.h"
++
++struct ucb1x00;
++
++struct ucb1x00_irq {
++      void *devid;
++      void (*fn)(int, void *);
++};
++
++struct ucb1x00 {
++      spinlock_t              lock;
++      struct mcp              *mcp;
++      struct pm_dev           *pmdev;
++      unsigned int            irq;
++      struct semaphore        adc_sem;
++      spinlock_t              io_lock;
++      u16                     id;
++      u16                     io_dir;
++      u16                     io_out;
++      u16                     adc_cr;
++      u16                     irq_fal_enbl;
++      u16                     irq_ris_enbl;
++      struct ucb1x00_irq      irq_handler[16];
++};
++
++/**
++ *    ucb1x00_clkrate - return the UCB1x00 SIB clock rate
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Return the SIB clock rate in Hz.
++ */
++static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb)
++{
++      return mcp_get_sclk_rate(ucb->mcp);
++}
++
++/**
++ *    ucb1x00_enable - enable the UCB1x00 SIB clock
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Enable the SIB clock.  This can be called multiple times.
++ */
++static inline void ucb1x00_enable(struct ucb1x00 *ucb)
++{
++      mcp_enable(ucb->mcp);
++}
++
++/**
++ *    ucb1x00_disable - disable the UCB1x00 SIB clock
++ *    @ucb: UCB1x00 structure describing chip
++ *
++ *    Disable the SIB clock.  The SIB clock will only be disabled
++ *    when the number of ucb1x00_enable calls match the number of
++ *    ucb1x00_disable calls.
++ */
++static inline void ucb1x00_disable(struct ucb1x00 *ucb)
++{
++      mcp_disable(ucb->mcp);
++}
++
++/**
++ *    ucb1x00_reg_write - write a UCB1x00 register
++ *    @ucb: UCB1x00 structure describing chip
++ *    @reg: UCB1x00 4-bit register index to write
++ *    @val: UCB1x00 16-bit value to write
++ *
++ *    Write the UCB1x00 register @reg with value @val.  The SIB
++ *    clock must be running for this function to return.
++ */
++static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val)
++{
++      mcp_reg_write(ucb->mcp, reg, val);
++}
++
++/**
++ *    ucb1x00_reg_read - read a UCB1x00 register
++ *    @ucb: UCB1x00 structure describing chip
++ *    @reg: UCB1x00 4-bit register index to write
++ *
++ *    Read the UCB1x00 register @reg and return its value.  The SIB
++ *    clock must be running for this function to return.
++ */
++static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg)
++{
++      return mcp_reg_read(ucb->mcp, reg);
++}
++/**
++ *    ucb1x00_set_audio_divisor - 
++ *    @ucb: UCB1x00 structure describing chip
++ *    @div: SIB clock divisor
++ */
++static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div)
++{
++      mcp_set_audio_divisor(ucb->mcp, div);
++}
++
++/**
++ *    ucb1x00_set_telecom_divisor -
++ *    @ucb: UCB1x00 structure describing chip
++ *    @div: SIB clock divisor
++ */
++static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div)
++{
++      mcp_set_telecom_divisor(ucb->mcp, div);
++}
++
++struct ucb1x00 *ucb1x00_get(void);
++
++void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int);
++void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int);
++unsigned int ucb1x00_io_read(struct ucb1x00 *ucb);
++
++#define UCB_NOSYNC    (0)
++#define UCB_SYNC      (1)
++
++unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync);
++void ucb1x00_adc_enable(struct ucb1x00 *ucb);
++void ucb1x00_adc_disable(struct ucb1x00 *ucb);
++
++/*
++ * Which edges of the IRQ do you want to control today?
++ */
++#define UCB_RISING    (1 << 0)
++#define UCB_FALLING   (1 << 1)
++
++int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid);
++void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
++void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges);
++int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid);
++
++#endif
+--- linux-2.4.27/drivers/mtd/chips/cfi_probe.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/chips/cfi_probe.c
+@@ -65,6 +65,10 @@
+               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 */
++      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))
+@@ -84,6 +88,8 @@
+                       /* 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);
+                       /* If the QRY marker goes away, it's an alias */
+                       if (!qry_present(map, chips[i].start, cfi)) {
+@@ -96,7 +102,8 @@
+                        * 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);
+                       if (qry_present(map, base, cfi)) {
+                               printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
+                                      map->name, base, chips[i].start);
+@@ -119,6 +126,10 @@
+       /* 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 mode\n",
+              map->name, cfi->interleave, cfi->device_type*8, base,
+              map->buswidth*8);
+@@ -165,6 +176,20 @@
+       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);
+@@ -182,6 +207,9 @@
+       /* 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);
++
+       return 1;
+ }
+--- linux-2.4.27/drivers/mtd/chips/jedec_probe.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/chips/jedec_probe.c
+@@ -100,6 +100,8 @@
+ #define M29W040B      0x00E3
+ /* SST */
++#define SST29EE020    0x0010
++#define SST29LE020    0x0012
+ #define SST29EE512    0x005d
+ #define SST29LE512    0x003d
+ #define SST39LF800    0x2781
+@@ -839,6 +841,24 @@
+               }
+         }, {
+               mfr_id: MANUFACTURER_SST,
++              dev_id: SST29EE020,
++              name: "SST 29EE020",
++              DevSize: SIZE_256KiB,
++              CmdSet: P_ID_SST_PAGE,
++              NumEraseRegions: 1,
++              regions: {ERASEINFO(0x01000,64),
++              }
++        }, {
++              mfr_id: MANUFACTURER_SST,
++              dev_id: SST29LE020,
++              name: "SST 29LE020",
++              DevSize: SIZE_256KiB,
++              CmdSet: P_ID_SST_PAGE,
++              NumEraseRegions: 1,
++              regions: {ERASEINFO(0x01000,64),
++              }
++        }, {
++              mfr_id: MANUFACTURER_SST,
+               dev_id: SST39LF020,
+               name: "SST 39LF020",
+               DevSize: SIZE_256KiB,
+@@ -937,7 +957,20 @@
+       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) {
++              /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/
++              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(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
+--- linux-2.4.27/drivers/mtd/devices/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/devices/Config.in
+@@ -17,6 +17,15 @@
+ 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
+--- linux-2.4.27/drivers/mtd/devices/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/devices/Makefile
+@@ -21,6 +21,7 @@
+ 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
+ include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/devices/syncflash.c
+@@ -0,0 +1,615 @@
++/*
++ * MTD driver for Micron SyncFlash flash memory.
++ *
++ * Author: Jon McClintock <jonm@bluemug.com>
++ *
++ * Based loosely upon the LART flash driver, authored by Abraham vd Merwe
++ * <abraham@2d3d.co.za>.
++ *
++ * Copyright 2003, Blue Mug, Inc. for Motorola, Inc.
++ *
++ * This code 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.
++ *
++ * References:
++ *
++ *    [1] Micron SyncFlash homepage
++ *            - http://www.syncflash.com/
++ *
++ *    [2] MT28S4M16LC -- 4Mx16 SyncFlash memory datasheet
++ *            - http://syncflash.com/pdfs/datasheets/mt28s4m16lc_6.pdf
++ *
++ *    [3] MTD internal API documentation
++ *            - http://www.linux-mtd.infradead.org/tech/
++ *
++ * Limitations:
++ *
++ *    Even though this driver is written for Micron SyncFlash, it is quite
++ *    specific to the Motorola MX1 ADS development board.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/version.h>
++#include <linux/errno.h>
++#include <linux/mtd/mtd.h>
++#include <asm/io.h>
++
++/* partition support */
++#define HAVE_PARTITIONS
++#ifdef HAVE_PARTITIONS
++#include <linux/mtd/partitions.h>
++#endif
++
++#ifndef CONFIG_ARCH_MX1ADS
++#error The SyncFlash driver currently only supports the MX1 ADS platform.
++#endif
++
++/*
++ * General flash configuration parameters.
++ */
++#define BUSWIDTH              4
++#define FLASH_BLOCKSIZE               (256 * 1024 * BUSWIDTH)
++#define FLASH_NUMBLOCKS               16
++
++#define BUSWIDTH              4
++#define FLASH_ADDRESS         IO_ADDRESS(MX1ADS_FLASH_BASE)
++
++#define FLASH_MANUFACTURER    0x002C002C
++#define FLASH_DEVICE_ID               0x00D300D3
++
++/*
++ * The size and extent of the bootloader in flash.
++ */
++#define NUM_BOOTLOADER_BLOCKS 1
++#define BOOTLOADER_START      0x00000000
++#define BOOTLOADER_LEN                (NUM_BOOTLOADER_BLOCKS * FLASH_BLOCKSIZE)
++
++/*
++ * The size and extent of the kernel in flash.
++ */
++#define NUM_KERNEL_BLOCKS     1
++#define KERNEL_START          (BOOTLOADER_START + BOOTLOADER_LEN)
++#define KERNEL_LEN            (NUM_KERNEL_BLOCKS * FLASH_BLOCKSIZE)
++
++/* File system */
++#define NUM_FILESYSTEM_BLOCKS 14
++#define FILESYSTEM_START      (KERNEL_START + KERNEL_LEN)
++#define FILESYSTEM_LEN                (NUM_FILESYSTEM_BLOCKS * FLASH_BLOCKSIZE)
++
++
++/*
++ * SDRAM controller register location and values. These are very specific
++ * to the MX1.
++ */
++#define SDRAMC_REGISTER               IO_ADDRESS(0x00221004)
++
++/*
++ * This the mask we use to get the start of a block from a given address.
++ */
++#define BLOCK_MASK    (0xFFF00000)
++
++/*
++ * This is the A10 address line of the SyncFlash; it's used to initiate
++ * a precharge command.
++ */
++#define SYNCFLASH_A10 (0x00100000)
++
++/*
++ * SDRAM controller MODE settings.
++ */
++#define CMD_NORMAL    (0x81020300)                    /* Normal Mode */
++#define CMD_PREC      (CMD_NORMAL + 0x10000000)       /* Precharge command */
++#define CMD_AUTO      (CMD_NORMAL + 0x20000000)       /* Auto refresh */
++#define CMD_LMR               (CMD_NORMAL + 0x30000000)       /* Load Mode Register */
++#define CMD_LCR               (CMD_NORMAL + 0x60000000)       /* LCR Command */
++#define CMD_PROGRAM   (CMD_NORMAL + 0x70000000)       /* SyncFlash Program */
++
++/*
++ * SyncFlash LCR Commands adjusted for the DBMX1 AHB internal address bus .
++ */
++#define LCR_READ_STATUS               (0x0001C000)    /* 0x70 */
++#define LCR_READ_CONFIG               (0x00024000)    /* 0x90 */
++#define LCR_ERASE_CONFIRM     (0x00008000)    /* 0x20 */
++#define LCR_ERASE_NVMODE      (0x0000C000)    /* 0x30 */
++#define LCR_PROG_NVMODE               (0x00028000)    /* 0xA0 */
++#define LCR_SR_CLEAR          (0x00014000)    /* 0x50 */
++
++/*
++ * Status register bits
++ */
++#define SR_VPS_ERROR          (1 << 8)        /* Power-Up status error */
++#define SR_ISM_READY          (1 << 7)        /* State machine isn't busy */
++#define SR_ERASE_ERROR                (1 << 5)        /* Erase/Unprotect error */
++#define SR_PROGRAM_ERROR      (1 << 4)        /* Program/Protect error */
++#define SR_DEVICE_PROTECTED   (1 << 3)        /* Device is protected */
++#define SR_ISM_STATUS_H               (1 << 2)        /* Bank ISM status, high bit */
++#define SR_ISM_STATUS_L               (1 << 1)        /* Bank ISM status, low bit */
++#define SR_DEVICE_ISM_STATUS  (1 << 0)        /* ISM is device-level */
++
++#define SR_ERROR              (SR_VPS_ERROR|SR_ERASE_ERROR|SR_PROGRAM_ERROR|SR_DEVICE_PROTECTED)
++
++#define STATUS_VALUE(a)               ((a) | ((a) << 16))
++
++/*
++ * Device configuration register offsets
++ */
++#define DC_MANUFACTURER               (0 * BUSWIDTH)
++#define DC_DEVICE_ID          (1 * BUSWIDTH)
++#define DC_BLOCK_PROTECT      (2 * BUSWIDTH)
++#define DC_DEVICE_PROTECT     (3 * BUSWIDTH)
++
++#define FL_WORD(addr) (*(volatile unsigned long*)(addr))
++
++static char module_name[] = "syncflash";
++
++inline __u8 read8 (__u32 offset)
++{
++      return *(volatile __u8 *) (FLASH_ADDRESS + offset);
++}
++
++inline __u32 read32 (__u32 offset)
++{
++      return *(volatile __u32 *) (FLASH_ADDRESS + offset);
++}
++
++inline void write32 (__u32 x,__u32 offset)
++{
++      *(volatile __u32 *) (FLASH_ADDRESS + offset) = x;
++}
++
++static __u32 read_device_configuration_register(__u32 reg_number)
++{
++      __u32 tmp;
++
++      /* Setup the SDRAM controller to issue an LCR command. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_LCR;
++
++      /* Perform a read to issue the Read Device Configuration Command. */
++      tmp = read32(LCR_READ_CONFIG);
++
++      /* Return the SDRAM controller to normal mode. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;
++
++      /* Return the value of the specified register. */
++      tmp = read32(reg_number);
++
++      return tmp;
++}
++
++/*
++ * Get the status of the flash devices.
++ */
++static __u32 flash_read_status()
++{
++      __u32 status, tmp;
++
++      /* Enter the SyncFlash Program READ/WRITE mode. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM;
++
++      /* Read the status register. */
++      status = read32(LCR_READ_STATUS);
++
++      /* Clear the status register. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_LCR;
++      tmp = read32(LCR_SR_CLEAR);
++
++      /* Return to Normal mode. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;
++
++      return status;
++}
++
++/*
++ * Loop until both write state machines are ready.
++ */
++static __u32 flash_status_wait()
++{
++      __u32 status;
++      do {
++              status = flash_read_status();
++      } while ((status & STATUS_VALUE(SR_ISM_READY)) !=
++                      STATUS_VALUE(SR_ISM_READY));
++      return status;
++}
++
++/*
++ * Loop until the Write State machine is ready, then do a full error
++ * check.  Clear status and leave the flash in Read Array mode; return
++ * 0 for no error, -1 for error.
++ */
++static int flash_status_full_check()
++{
++      __u32 status;
++
++      status = flash_status_wait() & STATUS_VALUE(SR_ERROR);
++      return status ? -EIO : 0;
++}
++
++/*
++ * Return the flash to the normal mode.
++ */
++static void flash_normal_mode()
++{
++      __u32 tmp;
++
++      /* First issue a precharge all command. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_PREC;
++      tmp = read32(SYNCFLASH_A10);
++
++      /* Now place the SDRAM controller in Normal mode. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;
++}
++
++/*
++ * Probe for SyncFlash memory on MX1ADS board.
++ *
++ * Returns 1 if we found SyncFlash memory, 0 otherwise.
++ */
++static int flash_probe (void)
++{
++      __u32 manufacturer, device_id;
++
++      /* For some reason, the first read doesn't work, so we do it
++       * twice. */
++      manufacturer = read_device_configuration_register(DC_MANUFACTURER);
++      manufacturer = read_device_configuration_register(DC_MANUFACTURER);
++      device_id = read_device_configuration_register(DC_DEVICE_ID);
++
++      printk("SyncFlash probe: manufacturer 0x%08lx, device_id 0x%08lx\n",
++              manufacturer, device_id);
++      return (manufacturer == FLASH_MANUFACTURER &&
++              device_id == FLASH_DEVICE_ID);
++}
++
++/*
++ * Erase one block of flash memory at offset ``offset'' which is any
++ * address within the block which should be erased.
++ *
++ * Returns 0 if successful, -1 otherwise.
++ */
++static inline int erase_block (__u32 offset)
++{
++      __u32 tmp;
++
++      /* Mask off the lower bits of the address to get the first address
++       * in the flash block. */
++      offset &= (__u32)BLOCK_MASK;
++
++      /* Perform a read and precharge of the bank before the LCR|ACT|WRIT
++       * sequence to avoid the inadvertent precharge command occurring
++       * during the LCR_ACT_WRIT sequence. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;
++      tmp = read32(offset);
++      FL_WORD(SDRAMC_REGISTER) = CMD_PREC;
++      tmp = read32(offset);
++
++      /* Now start the actual erase. */
++
++      /* LCR|ACT|WRIT sequence */
++      FL_WORD(SDRAMC_REGISTER) = CMD_LCR;
++      write32(0, offset + LCR_ERASE_CONFIRM);
++
++      /* Return to normal mode to issue the erase confirm. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_NORMAL;
++      write32(0xD0D0D0D0, offset);
++
++      if (flash_status_full_check()) {
++              printk (KERN_WARNING "%s: erase error at address 0x%.8x.\n",
++                      module_name, offset);
++              return (-1);
++      }
++
++      flash_normal_mode();
++
++      return 0;
++}
++
++static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
++{
++      __u32 addr,len;
++      int i,first;
++
++      /* sanity checks */
++      if (instr->addr + instr->len > mtd->size) return (-EINVAL);
++
++      /*
++       * check that both start and end of the requested erase are
++       * aligned with the erasesize at the appropriate addresses.
++       *
++       * 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.
++       */
++      for (i = 0; (i < mtd->numeraseregions) &&
++                  (instr->addr >= mtd->eraseregions[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 (instr->addr & (mtd->eraseregions[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.
++       *
++       * as before, drop back one to point at the region in which
++       * the address actually falls
++       */
++      for (;
++           (i < mtd->numeraseregions) &&
++           ((instr->addr + instr->len) >= mtd->eraseregions[i].offset) ;
++           i++) ;
++      i--;
++
++      /* is the end aligned on a block boundary? */
++      if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))
++              return (-EINVAL);
++
++      addr = instr->addr;
++      len = instr->len;
++
++      i = first;
++
++      /* now erase those blocks */
++      while (len)
++      {
++              if (erase_block (addr))
++              {
++                      instr->state = MTD_ERASE_FAILED;
++                      return (-EIO);
++              }
++
++              addr += mtd->eraseregions[i].erasesize;
++              len -= mtd->eraseregions[i].erasesize;
++
++              if (addr == (mtd->eraseregions[i].offset +
++                              (mtd->eraseregions[i].erasesize *
++                               mtd->eraseregions[i].numblocks)))
++                      i++;
++      }
++
++      instr->state = MTD_ERASE_DONE;
++      if (instr->callback) instr->callback (instr);
++
++      return (0);
++}
++
++static int flash_read (struct mtd_info *mtd, loff_t from,
++                     size_t len, size_t *retlen, u_char *buf)
++{
++      /* Sanity checks. */
++      if (!len) return (0);
++      if (from + len > mtd->size) return (-EINVAL);
++
++      /* Ensure that we are in normal mode. */
++      flash_normal_mode();
++
++      /* We always read len bytes. */
++      *retlen = len;
++
++      /* first, we read bytes until we reach a dword boundary */
++      if (from & (BUSWIDTH - 1))
++      {
++              int gap = BUSWIDTH - (from & (BUSWIDTH - 1));
++              while (len && gap--) *buf++ = read8(from++), len--;
++      }
++
++      /* now we read dwords until we reach a non-dword boundary */
++      while (len >= BUSWIDTH)
++      {
++              *((__u32 *) buf) = read32(from);
++
++              buf += BUSWIDTH;
++              from += BUSWIDTH;
++              len -= BUSWIDTH;
++      }
++
++      /* top up the last unaligned bytes */
++      if (len & (BUSWIDTH - 1))
++              while (len--) *buf++ = read8(from++);
++
++      return (0);
++}
++
++/*
++ * Write one dword ``x'' to flash memory at offset ``offset''. ``offset''
++ * must be 32 bits, i.e. it must be on a dword boundary.
++ *
++ * Returns 0 if successful, -1 otherwise.
++ */
++static int flash_write_dword(__u32 offset, __u32 x)
++{
++      __u32 tmp;
++
++      /* First issue a precharge all command. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_PREC;
++      tmp = read32(SYNCFLASH_A10);
++
++      /* Enter the SyncFlash programming mode. */
++      FL_WORD(SDRAMC_REGISTER) = CMD_PROGRAM;
++      write32(x, offset);
++
++      /* Wait for the write to complete. */
++      flash_status_wait();
++
++      /* Return to normal mode. */
++      flash_normal_mode();
++
++      return 0;
++}
++
++static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen,const u_char *buf)
++{
++      __u8 tmp[4];
++      int i,n;
++
++      *retlen = 0;
++
++      /* Sanity checks */
++      if (!len) return (0);
++      if (to + len > mtd->size) return (-EINVAL);
++
++      /* First, we write a 0xFF.... padded byte until we reach a
++       * dword boundary. */
++      if (to & (BUSWIDTH - 1))
++      {
++              __u32 aligned = to & ~(BUSWIDTH - 1);
++              int gap = to - aligned;
++
++              i = n = 0;
++
++              while (gap--) tmp[i++] = 0xFF;
++              while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--;
++              while (i < BUSWIDTH) tmp[i++] = 0xFF;
++
++              if (flash_write_dword(aligned, *((__u32 *) tmp)))
++                      return (-EIO);
++
++              to += n;
++              buf += n;
++              *retlen += n;
++      }
++
++      /* Now we write dwords until we reach a non-dword boundary. */
++      while (len >= BUSWIDTH)
++      {
++              if (flash_write_dword (to,*((__u32 *) buf))) return (-EIO);
++
++              to += BUSWIDTH;
++              buf += BUSWIDTH;
++              *retlen += BUSWIDTH;
++              len -= BUSWIDTH;
++      }
++
++      /* Top up the last unaligned bytes, padded with 0xFF.... */
++      if (len & (BUSWIDTH - 1))
++      {
++              i = n = 0;
++
++              while (len--) tmp[i++] = buf[n++];
++              while (i < BUSWIDTH) tmp[i++] = 0xFF;
++
++              if (flash_write_dword (to,*((__u32 *) tmp))) return (-EIO);
++
++              *retlen += n;
++      }
++
++      return flash_status_full_check();
++}
++
++
++
++#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
++
++static struct mtd_info mtd;
++
++static struct mtd_erase_region_info erase_regions[] =
++{
++      /* flash blocks */
++      {
++              offset:         0x00000000,
++              erasesize:      FLASH_BLOCKSIZE,
++              numblocks:      FLASH_NUMBLOCKS
++      },
++};
++
++#ifdef HAVE_PARTITIONS
++static struct mtd_partition syncflash_partitions[] =
++{
++   /* bootloader */
++   {
++             name: "bootloader",
++           offset: BOOTLOADER_START,
++             size: BOOTLOADER_LEN,
++       mask_flags: 0
++   },
++   /* Kernel */
++   {
++             name: "kernel",
++           offset: KERNEL_START,                      /* MTDPART_OFS_APPEND */
++             size: KERNEL_LEN,
++       mask_flags: 0
++   },
++   /* file system */
++   {
++             name: "file system",
++           offset: FILESYSTEM_START,                  /* MTDPART_OFS_APPEND */
++             size: FILESYSTEM_LEN,                    /* MTDPART_SIZ_FULL */
++       mask_flags: 0
++   }
++};
++#endif
++
++int __init syncflash_init (void)
++{
++      int result;
++
++      memset (&mtd,0,sizeof (mtd));
++
++      printk ("MTD driver for Micron SyncFlash.\n");
++      printk ("%s: Probing for SyncFlash on MX1ADS...\n",module_name);
++
++      if (!flash_probe ())
++      {
++              printk (KERN_WARNING "%s: Found no SyncFlash devices\n",
++                      module_name);
++              return (-ENXIO);
++      }
++
++      printk ("%s: Found a SyncFlash device.\n",module_name);
++
++      mtd.name = module_name;
++      mtd.type = MTD_NORFLASH;
++      mtd.flags = MTD_CAP_NORFLASH;
++      mtd.size = FLASH_BLOCKSIZE * FLASH_NUMBLOCKS;
++
++      mtd.erasesize = FLASH_BLOCKSIZE;
++      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;
++
++#ifndef HAVE_PARTITIONS
++      result = add_mtd_device(&mtd);
++#else
++      result = add_mtd_partitions(&mtd,
++                                  syncflash_partitions,
++                                  NB_OF(syncflash_partitions));
++#endif
++
++      return (result);
++}
++
++void __exit syncflash_exit (void)
++{
++#ifndef HAVE_PARTITIONS
++      del_mtd_device (&mtd);
++#else
++      del_mtd_partitions (&mtd);
++#endif
++}
++
++module_init (syncflash_init);
++module_exit (syncflash_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jon McClintock <jonm@bluemug.com>");
++MODULE_DESCRIPTION("MTD driver for Micron MT28S4M16LC SyncFlash on MX1ADS board");
++
++
+--- linux-2.4.27/drivers/mtd/maps/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/maps/Config.in
+@@ -81,10 +81,10 @@
+    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 the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310
+-   dep_tristate '  CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI  $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT
+-   dep_tristate '  CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET
++   dep_tristate '  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 '  NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12
+-   dep_tristate '  CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI
++   dep_tristate '  CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_ARCH_EDB7212 $CONFIG_MTD_CFI
+    dep_tristate '  JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE
+    dep_tristate '  JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE  $CONFIG_ARCH_CEIVA
+ fi
+--- linux-2.4.27/drivers/mtd/maps/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/maps/Makefile
+@@ -3,11 +3,7 @@
+ #
+ # $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $
+-BELOW25               := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
+-
+-ifeq ($(BELOW25),y)
+ O_TARGET      := mapslink.o
+-endif
+ # Chip mappings
+ obj-$(CONFIG_MTD_CDB89712)    += cdb89712.o
+@@ -17,7 +13,7 @@
+ obj-$(CONFIG_MTD_DC21285)     += dc21285.o
+ obj-$(CONFIG_MTD_DILNETPC)    += dilnetpc.o
+ obj-$(CONFIG_MTD_ELAN_104NC)  += elan-104nc.o
+-obj-$(CONFIG_MTD_EPXA10DB)    += epxa10db-flash.o
++obj-$(CONFIG_MTD_EPXA)                += epxa-flash.o
+ obj-$(CONFIG_MTD_IQ80310)     += iq80310.o
+ obj-$(CONFIG_MTD_L440GX)      += l440gx.o
+ obj-$(CONFIG_MTD_AMD76XROM)   += amd76xrom.o
+@@ -29,9 +25,9 @@
+ obj-$(CONFIG_MTD_OCTAGON)     += octagon-5066.o
+ ifneq ($(CONFIG_MTD_PHYSMAP),n)
+   ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8)
+-    obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o 
++    obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o
+   else
+-    obj-$(CONFIG_MTD_PHYSMAP) += physmap.o 
++    obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
+   endif
+ endif
+ obj-$(CONFIG_MTD_PNC2000)     += pnc2000.o
+@@ -39,6 +35,9 @@
+ 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
+--- linux-2.4.27/drivers/mtd/maps/dc21285.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/maps/dc21285.c
+@@ -11,6 +11,7 @@
+ #include <linux/module.h>
+ #include <linux/types.h>
+ #include <linux/kernel.h>
++#include <linux/delay.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/map.h>
+@@ -18,9 +19,9 @@
+ #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)
+ {
+@@ -44,6 +45,9 @@
+ void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr)
+ {
++      if(machine_is_netwinder()) {
++              nw_en_write();
++      }
+       *CSR_ROMWRITEREG = adr & 3;
+       adr &= ~3;
+       *(__u8*)(map->map_priv_1 + adr) = d;
+@@ -51,6 +55,9 @@
+ void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
+ {
++      if(machine_is_netwinder()) {
++              nw_en_write();
++      }
+       *CSR_ROMWRITEREG = adr & 3;
+       adr &= ~3;
+       *(__u16*)(map->map_priv_1 + adr) = d;
+@@ -58,6 +65,9 @@
+ void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr)
+ {
++      if(machine_is_netwinder()) {
++              nw_en_write();
++      }
+       *(__u32*)(map->map_priv_1 + adr) = d;
+ }
+@@ -105,6 +115,27 @@
+ };
++static void
++nw_en_write(void) {
++#ifdef CONFIG_ARCH_NETWINDER
++      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);
++#endif
++}
++
+ /* Partition stuff */
+ static struct mtd_partition *dc21285_parts;
+                     
+@@ -112,6 +143,9 @@
+ int __init init_dc21285(void)
+ {
++      int nr_parts = 0;
++      char *part_type = "none";
++
+       /* Determine buswidth */
+       switch (*CSR_SA110_CNTL & (3<<14)) {
+               case SA110_CNTL_ROMWIDTH_8: 
+@@ -137,24 +171,64 @@
+               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) {
++              /* no recognised device so unmap and exit */
++              iounmap((void *)dc21285_map.map_priv_1);
++              return -ENXIO;
++      }
+                       
+-              /* partition fixup */
++      dc21285_mtd->module = THIS_MODULE;
++      /*
++       * Dynamic partition selection stuff (might override the static ones)
++       */
+ #ifdef CONFIG_MTD_REDBOOT_PARTS
+-              nrparts = parse_redboot_partitions(mymtd, &dc21285_parts);
++      if (nr_parts == 0) {
++              int ret = parse_redboot_partitions(dc21285_mtd, &dc21285_parts);
++
++              if (ret > 0) {
++                      part_type = "RedBoot";
++                      nr_parts = ret;
++              }
++              else
++              {
++                      dc21285_parts=NULL; /* ensure partition table remains clear */
++              }
++      }
+ #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);
++#ifdef CONFIG_MTD_CMDLINE_PARTS
++      if (nr_parts == 0) {
++              int ret = parse_cmdline_partitions(dc21285_mtd, &dc21285_parts, "sa1100");
++              if (ret > 0) {
++                      part_type = "Command Line";
++                      nr_parts = ret;
++              }
++              else
++              {
++                      dc21285_parts=NULL; /* ensure partition table remains clear */
+               }
++      }
++#endif
++
++      if (nr_parts == 0) {
++              printk(KERN_NOTICE "DC21285 Flash: no partition info available, registering whole flash at once\n");
++              add_mtd_device(dc21285_mtd);
++      } 
++#ifdef CONFIG_MTD_PARTITIONS
++      else 
++      {
++              printk(KERN_NOTICE "DC21285 Flash: Using %s partition definition\n", part_type);
++              add_mtd_partitions(dc21285_mtd, &dc21285_parts, nr_parts);
++      }
++#endif
++      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,20 +241,15 @@
+               *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;
+ }
+ static void __exit cleanup_dc21285(void)
+ {
+-      if (mymtd) {
+-              del_mtd_device(mymtd);
+-              map_destroy(mymtd);
+-              mymtd = NULL;
++      if (dc21285_mtd) {
++              del_mtd_device(dc21285_mtd);
++              map_destroy(dc21285_mtd);
++              dc21285_mtd = NULL;
+       }
+       if (dc21285_map.map_priv_1) {
+               iounmap((void *)dc21285_map.map_priv_1);
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/maps/epxa-flash.c
+@@ -0,0 +1,234 @@
++/*
++ * Flash memory access on EPXA based devices
++ *
++ * (C) 2000 Nicolas Pitre <nico@cam.org>
++ *  Copyright (C) 2001 Altera Corporation
++ *  Copyright (C) 2001 Red Hat, Inc.
++ *
++ * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <asm/io.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#ifdef CONFIG_EPXA10DB
++#define BOARD_NAME "EPXA10DB"
++#else
++#define BOARD_NAME "EPXA1DB"
++#endif
++
++static int nr_parts = 0;
++static struct mtd_partition *parts;
++
++static struct mtd_info *mymtd;
++
++extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
++static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++
++static __u8 epxa_read8(struct map_info *map, unsigned long ofs)
++{
++      return __raw_readb(map->map_priv_1 + ofs);
++}
++
++static __u16 epxa_read16(struct map_info *map, unsigned long ofs)
++{
++      return __raw_readw(map->map_priv_1 + ofs);
++}
++
++static __u32 epxa_read32(struct map_info *map, unsigned long ofs)
++{
++      return __raw_readl(map->map_priv_1 + ofs);
++}
++
++static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++      memcpy_fromio(to, map->map_priv_1 + from, len);
++}
++
++static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++      __raw_writeb(d, map->map_priv_1 + adr);
++      mb();
++}
++
++static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++      __raw_writew(d, map->map_priv_1 + adr);
++      mb();
++}
++
++static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++      __raw_writel(d, map->map_priv_1 + adr);
++      mb();
++}
++
++static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++      memcpy_toio(map->map_priv_1 + to, from, len);
++}
++
++static struct map_info epxa_map = {
++      .name      = "EPXA flash",
++      .size      = FLASH_SIZE,
++      .buswidth  = 2,
++      .read8     = epxa_read8,
++      .read16    = epxa_read16,
++      .read32    = epxa_read32,
++      .copy_from = epxa_copy_from,
++      .write8    = epxa_write8,
++      .write16   = epxa_write16,
++      .write32   = epxa_write32,
++      .copy_to   = epxa_copy_to
++};
++
++static int __init epxa_mtd_init(void)
++{
++      int i;
++
++      printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
++      epxa_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_START, FLASH_SIZE);
++      if (!epxa_map.map_priv_1) {
++              printk("Failed to ioremap %s flash\n",BOARD_NAME);
++              return -EIO;
++      }
++
++      mymtd = do_map_probe("cfi_probe", &epxa_map);
++      if (!mymtd) {
++              iounmap((void *)epxa_map.map_priv_1);
++              return -ENXIO;
++      }
++
++      mymtd->module = THIS_MODULE;
++
++      /* Unlock the flash device. */
++      if(mymtd->unlock){
++              for (i=0; i<mymtd->numeraseregions;i++){
++                      int j;
++                      for(j=0;j<mymtd->eraseregions[i].numblocks;j++){
++                              mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize);
++                      }
++              }
++      }
++
++#ifdef CONFIG_MTD_REDBOOT_PARTS
++      nr_parts = parse_redboot_partitions(mymtd, &parts);
++
++      if (nr_parts > 0) {
++              add_mtd_partitions(mymtd, parts, nr_parts);
++              return 0;
++      }
++#endif
++#ifdef CONFIG_MTD_AFS_PARTS
++      nr_parts = parse_afs_partitions(mymtd, &parts);
++
++      if (nr_parts > 0) {
++              add_mtd_partitions(mymtd, parts, nr_parts);
++              return 0;
++      }
++#endif
++
++      /* No recognised partitioning schemes found - use defaults */
++      nr_parts = epxa_default_partitions(mymtd, &parts);
++      if (nr_parts > 0) {
++              add_mtd_partitions(mymtd, parts, nr_parts);
++              return 0;
++      }
++
++      /* If all else fails... */
++      add_mtd_device(mymtd);
++      return 0;
++}
++
++static void __exit epxa_mtd_cleanup(void)
++{
++      if (mymtd) {
++              if (nr_parts)
++                      del_mtd_partitions(mymtd);
++              else
++                      del_mtd_device(mymtd);
++              map_destroy(mymtd);
++      }
++      if (epxa_map.map_priv_1) {
++              iounmap((void *)epxa_map.map_priv_1);
++              epxa_map.map_priv_1 = 0;
++      }
++}
++
++
++/*
++ * This will do for now, once we decide which bootldr we're finally
++ * going to use then we'll remove this function and do it properly
++ *
++ * Partions are currently (as offsets from base of flash):
++ * 0x00000000 - 0x003FFFFF - bootloader (!)
++ * 0x00400000 - 0x00FFFFFF - Flashdisk
++ */
++
++static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts)
++{
++      struct mtd_partition *parts;
++      int ret;
++      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)+1, GFP_KERNEL);
++      if (!parts) {
++              ret = -ENOMEM;
++              goto out;
++      }
++      memzero(parts,npartitions*sizeof(*parts)+strlen(name));
++
++      names = (char *)&parts[npartitions];
++      parts[0].name = names;
++      names += strlen(name) + 1;
++      strcpy(parts[0].name, name);
++
++#ifdef CONFIG_EPXA10DB_R2
++      parts[0].size       = FLASH_SIZE-0x00400000;
++      parts[0].offset     = 0x00400000;
++#elif defined CONFIG_EPXA10DB_R3
++      parts[0].size       = 0x00800000;
++      parts[0].offset     = 0x00800000;
++#else
++      parts[0].size       = FLASH_SIZE-0x00180000;
++      parts[0].offset     = 0x00180000;
++#endif
++      ret = npartitions;
++
++ out:
++      *pparts = parts;
++      return ret;
++}
++
++
++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.27/drivers/mtd/maps/epxa10db-flash.c
++++ /dev/null
+@@ -1,233 +0,0 @@
+-/*
+- * Flash memory access on EPXA based devices
+- *
+- * (C) 2000 Nicolas Pitre <nico@cam.org>
+- *  Copyright (C) 2001 Altera Corporation
+- *  Copyright (C) 2001 Red Hat, Inc.
+- *
+- * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ 
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+- */
+-
+-#include <linux/config.h>
+-#include <linux/module.h>
+-#include <linux/types.h>
+-#include <linux/kernel.h>
+-#include <asm/io.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/mtd/map.h>
+-#include <linux/mtd/partitions.h>
+-
+-#include <asm/hardware.h>
+-#ifdef CONFIG_EPXA10DB
+-#define BOARD_NAME "EPXA10DB"
+-#else
+-#define BOARD_NAME "EPXA1DB"
+-#endif
+-
+-static int nr_parts = 0;
+-static struct mtd_partition *parts;
+-
+-static struct mtd_info *mymtd;
+-
+-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
+-static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-
+-static __u8 epxa_read8(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readb(map->map_priv_1 + ofs);
+-}
+-
+-static __u16 epxa_read16(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readw(map->map_priv_1 + ofs);
+-}
+-
+-static __u32 epxa_read32(struct map_info *map, unsigned long ofs)
+-{
+-      return __raw_readl(map->map_priv_1 + ofs);
+-}
+-
+-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+-{
+-      memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
+-}
+-
+-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr)
+-{
+-      __raw_writeb(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr)
+-{
+-      __raw_writew(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr)
+-{
+-      __raw_writel(d, map->map_priv_1 + adr);
+-      mb();
+-}
+-
+-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+-{
+-      memcpy_toio((void *)(map->map_priv_1 + to), from, len);
+-}
+-
+-
+-
+-static struct map_info epxa_map = {
+-      name:           "EPXA flash",
+-      size:           FLASH_SIZE,
+-      buswidth:       2,
+-      read8:          epxa_read8,
+-      read16:         epxa_read16,
+-      read32:         epxa_read32,
+-      copy_from:      epxa_copy_from,
+-      write8:         epxa_write8,
+-      write16:        epxa_write16,
+-      write32:        epxa_write32,
+-      copy_to:        epxa_copy_to
+-};
+-
+-
+-static int __init epxa_mtd_init(void)
+-{
+-      int i;
+-      
+-      printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
+-      epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+-      if (!epxa_map.map_priv_1) {
+-              printk("Failed to ioremap %s flash\n",BOARD_NAME);
+-              return -EIO;
+-      }
+-
+-      mymtd = do_map_probe("cfi_probe", &epxa_map);
+-      if (!mymtd) {
+-              iounmap((void *)epxa_map.map_priv_1);
+-              return -ENXIO;
+-      }
+-
+-      mymtd->module = THIS_MODULE;
+-
+-      /* Unlock the flash device. */
+-      if(mymtd->unlock){
+-              for (i=0; i<mymtd->numeraseregions;i++){
+-                      int j;
+-                      for(j=0;j<mymtd->eraseregions[i].numblocks;j++){
+-                              mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize);
+-                      }
+-              }
+-      }
+-
+-#ifdef CONFIG_MTD_REDBOOT_PARTS
+-      nr_parts = parse_redboot_partitions(mymtd, &parts);
+-
+-      if (nr_parts > 0) {
+-              add_mtd_partitions(mymtd, parts, nr_parts);
+-              return 0;
+-      }
+-#endif
+-#ifdef CONFIG_MTD_AFS_PARTS
+-      nr_parts = parse_afs_partitions(mymtd, &parts);
+-
+-      if (nr_parts > 0) {
+-              add_mtd_partitions(mymtd, parts, nr_parts);
+-              return 0;
+-      }
+-#endif
+-
+-      /* No recognised partitioning schemes found - use defaults */
+-      nr_parts = epxa_default_partitions(mymtd, &parts);
+-      if (nr_parts > 0) {
+-              add_mtd_partitions(mymtd, parts, nr_parts);
+-              return 0;
+-      }
+-
+-      /* If all else fails... */
+-      add_mtd_device(mymtd);
+-      return 0;
+-}
+-
+-static void __exit epxa_mtd_cleanup(void)
+-{
+-      if (mymtd) {
+-              if (nr_parts)
+-                      del_mtd_partitions(mymtd);
+-              else
+-                      del_mtd_device(mymtd);
+-              map_destroy(mymtd);
+-      }
+-      if (epxa_map.map_priv_1) {
+-              iounmap((void *)epxa_map.map_priv_1);
+-              epxa_map.map_priv_1 = 0;
+-      }
+-}
+-
+-
+-/* 
+- * This will do for now, once we decide which bootldr we're finally 
+- * going to use then we'll remove this function and do it properly
+- *
+- * Partions are currently (as offsets from base of flash):
+- * 0x00000000 - 0x003FFFFF - bootloader (!)
+- * 0x00400000 - 0x00FFFFFF - Flashdisk
+- */
+-
+-static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts)
+-{
+-      struct mtd_partition *parts;
+-      int ret, i;
+-      int npartitions = 0;
+-      char *names; 
+-      const char *name = "jffs";
+-
+-      printk("Using default partitions for %s\n",BOARD_NAME);
+-      npartitions=1;
+-      parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL);
+-      memzero(parts,npartitions*sizeof(*parts)+strlen(name));
+-      if (!parts) {
+-              ret = -ENOMEM;
+-              goto out;
+-      }
+-      i=0;
+-      names = (char *)&parts[npartitions];    
+-      parts[i].name = names;
+-      names += strlen(name) + 1;
+-      strcpy(parts[i].name, name);
+-
+-#ifdef CONFIG_EPXA10DB
+-      parts[i].size = FLASH_SIZE-0x00400000;
+-      parts[i].offset = 0x00400000;
+-#else
+-      parts[i].size = FLASH_SIZE-0x00180000;
+-      parts[i].offset = 0x00180000;
+-#endif
+-
+- out:
+-      *pparts = parts;
+-      return npartitions;
+-}
+-
+-
+-module_init(epxa_mtd_init);
+-module_exit(epxa_mtd_cleanup);
+-
+-MODULE_AUTHOR("Clive Davies");
+-MODULE_DESCRIPTION("Altera epxa mtd flash map");
+-MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/mtd/maps/neponset-flash.c
+@@ -0,0 +1,109 @@
++/*
++ * Flash memory access on SA11x0 based devices
++ * 
++ * (C) 2000 Nicolas Pitre <nico@cam.org>
++ * 
++ * $Id: neponset-flash.c,v 1.18 2001/07/14 00:59:17 thockin Exp $
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/arch/assabet.h>
++
++static __u8 read8(struct map_info *map, unsigned long ofs)
++{
++      return readb(map->map_priv_1 + ofs);
++}
++
++static __u16 read16(struct map_info *map, unsigned long ofs)
++{
++      return readw(map->map_priv_1 + ofs);
++}
++
++static __u32 read32(struct map_info *map, unsigned long ofs)
++{
++      return readl(map->map_priv_1 + ofs);
++}
++
++static void 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 write8(struct map_info *map, __u8 d, unsigned long adr)
++{
++      writeb(d, map->map_priv_1 + adr);
++}
++
++static void write16(struct map_info *map, __u16 d, unsigned long adr)
++{
++      writew(d, map->map_priv_1 + adr);
++}
++
++static void write32(struct map_info *map, __u32 d, unsigned long adr)
++{
++      writel(d, map->map_priv_1 + adr);
++}
++
++static void copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++      memcpy_toio(map->map_priv_1 + to, from, len);
++}
++
++#define MAX_SZ (32 * 1024 * 1024)
++
++static struct map_info neponset_map = {
++      name:           "Neponset",
++      size:           MAX_SZ,
++      buswidth:       4,
++      read8:          read8,
++      read16:         read16,
++      read32:         read32,
++      copy_from:      copy_from,
++      write8:         write8,
++      write16:        write16,
++      write32:        write32,
++      copy_to:        copy_to,
++};
++
++extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts);
++
++static struct mtd_info *neponset_mtd;
++
++int __init neponset_mtd_init(void)
++{
++      if (!machine_is_assabet() || !machine_has_neponset())
++              return -ENODEV;
++
++      neponset_map.map_priv_1 = (unsigned int)ioremap(0x08000000, MAX_SZ);
++      if (!neponset_map.map_priv_1)
++              return -ENOMEM;
++
++      neponset_mtd = do_map_probe("cfi_probe", &neponset_map);
++      if (!neponset_mtd)
++              return -ENXIO;
++      neponset_mtd->module = THIS_MODULE;
++      add_mtd_device(neponset_mtd);
++      return 0;
++}
++
++static void __exit neponset_mtd_cleanup(void)
++{
++      if (neponset_mtd)
++              map_destroy(neponset_mtd);
++      if (neponset_map.map_priv_1)
++              iounmap((void *)neponset_map.map_priv_1);
++}
++
++module_init(neponset_mtd_init);
++module_exit(neponset_mtd_cleanup);
+--- linux-2.4.27/drivers/mtd/maps/sa1100-flash.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/maps/sa1100-flash.c
+@@ -97,6 +97,32 @@
+  * 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[] = {
+@@ -123,6 +149,32 @@
+ };
+ #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:           "zImage",
++              size:           0x100000,
++              offset:         MTDPART_OFS_APPEND,
++              mask_flags:     MTD_WRITEABLE,  /* force read-only */
++      }, {
++              name:           "ramdisk.gz",
++              size:           0x300000,
++              offset:         MTDPART_OFS_APPEND,
++              mask_flags:     MTD_WRITEABLE,  /* force read-only */
++      }, {
++              name:           "User FS",
++              size:           MTDPART_SIZ_FULL,
++              offset:         MTDPART_OFS_APPEND,
++      }
++};
++#endif
++
+ #ifdef CONFIG_SA1100_ASSABET
+ /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */
+ #define ASSABET4_FLASH_SIZE           0x00400000
+@@ -438,7 +490,7 @@
+ #endif
+ #ifdef CONFIG_SA1100_GRAPHICSMASTER
+-#define GRAPHICSMASTER_FLASH_SIZE     0x01000000
++#define GRAPHICSMASTER_FLASH_SIZE     0x02000000
+ static struct mtd_partition graphicsmaster_partitions[] = {
+       {
+               name:           "zImage",
+@@ -507,6 +559,38 @@
+ }
+ #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:           "config",
++              size:           0x00040000,
++              offset:         MTDPART_OFS_APPEND,
++      }, {
++              name:           "kernel",
++              size:           0x00100000,
++              offset:         MTDPART_OFS_APPEND,
++      }, {
++              name:           "initrd",
++              size:           0x00180000,
++              offset:         MTDPART_OFS_APPEND,
++      }, {
++              name:           "rootfs",
++              size:           0x700000,
++              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[] = {
+@@ -555,12 +639,12 @@
+               offset:         0x00540000,
+       }, {
+               name:           "JORNADA720 usr local",
+-              size:           0  /* will expand to the end of the flash */
++              size:           0,  /* will expand to the end of the flash */
+               offset:         0x00d00000,
+       }
+ };
+-static void jornada720_set_vpp(int vpp)
++static void jornada720_set_vpp(struct map_info *map, int vpp)
+ {
+       if (vpp)
+               PPSR |= 0x80;
+@@ -571,6 +655,27 @@
+ #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,
++      }
++};
++#endif
++
+ #ifdef CONFIG_SA1100_PANGOLIN
+ #define PANGOLIN_FLASH_SIZE           0x04000000
+ static struct mtd_partition pangolin_partitions[] = {
+@@ -699,6 +804,32 @@
+ };
+ #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[] = {
+@@ -766,7 +897,7 @@
+ #endif
+ extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+-extern int parse_bootldr_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;
+@@ -787,6 +918,14 @@
+        */
+       part_type = "static";
++#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;
+@@ -795,6 +934,14 @@
+               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;
+@@ -869,6 +1016,13 @@
+               sa1100_map.set_vpp = h3600_set_vpp;
+       }
+ #endif
++#ifdef CONFIG_SA1100_HACKKIT
++      if (machine_is_hackkit()) {
++              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;
+@@ -884,6 +1038,13 @@
+               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;
+@@ -919,6 +1080,13 @@
+               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;
+@@ -953,7 +1121,9 @@
+        * 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("cfi_probe", &sa1100_map);
++      mymtd = do_map_probe("jedec_probe", &sa1100_map);
++      if (!mymtd)
++              mymtd = do_map_probe("cfi_probe", &sa1100_map);
+       ret = -ENXIO;
+       if (!mymtd)
+               goto out_err;
+--- linux-2.4.27/drivers/mtd/nand/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/mtd/nand/Config.in
+@@ -33,4 +33,8 @@
+ fi
+ fi
++if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
++   dep_tristate '  SmartMedia Card on Atmel AT91RM9200' CONFIG_MTD_AT91_SMARTMEDIA $CONFIG_MTD_NAND
++fi
++  
+ endmenu
+--- linux-2.4.27/drivers/net/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/Config.in
+@@ -30,9 +30,15 @@
+       if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+          source drivers/acorn/net/Config.in
+       fi
++      if [ "$CONFIG_ARCH_AT91RM9200" = "y" ]; then
++         tristate '  AT91RM9200 Ethernet support' CONFIG_AT91_ETHER
++         if [ "$CONFIG_AT91_ETHER" = "y" -o "$CONFIG_AT91_ETHER" = "m" ]; then
++            bool '      RMII interface?   ' CONFIG_AT91_ETHER_RMII
++         fi
++      fi
+    fi
+    if [ "$CONFIG_ARCH_CAMELOT" = "y" ]; then
+-      tristate ' Altera Ether00 support' CONFIG_ETHER00
++      tristate '  Altera Ether00 support' CONFIG_ETHER00
+    fi
+    if [ "$CONFIG_PPC" = "y" ]; then
+       dep_tristate '  MACE (Power Mac ethernet) support' CONFIG_MACE $CONFIG_ALL_PPC
+--- linux-2.4.27/drivers/net/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/Makefile
+@@ -244,6 +244,7 @@
+ # non-drivers/net drivers who want mii lib
+ obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o
+ obj-$(CONFIG_USB_USBNET) += mii.o
++obj-$(CONFIG_AT91_ETHER) += mii.o
+ obj-$(CONFIG_IBMVETH) += ibmveth.o
+@@ -270,4 +271,3 @@
+ rcpci.o: $(rcpci-objs)
+       $(LD) -r -o $@ $(rcpci-objs)
+-
+--- linux-2.4.27/drivers/net/am79c961a.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/am79c961a.c
+@@ -54,25 +54,36 @@
+ #ifdef __arm__
+ static void write_rreg(u_long base, u_int reg, u_int val)
+ {
+-      __asm__("str%?h %1, [%2]        @ NET_RAP
+-              str%?h  %0, [%2, #-4]   @ NET_RDP
+-              " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
++      __asm__("str%?h %1, [%2]        @ NET_RAP\n\t"
++              "str%?h %0, [%2, #-4]   @ NET_RDP"
++              : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
+ }
+ static inline unsigned short read_rreg(u_long base_addr, u_int reg)
+ {
+       unsigned short v;
+-      __asm__("str%?h %1, [%2]        @ NET_RAP
+-              ldr%?h  %0, [%2, #-4]   @ NET_RDP
+-              " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464));
++      __asm__("str%?h %1, [%2]        @ NET_RAP\n\t"
++              "ldr%?h %0, [%2, #-4]   @ NET_RDP"
++              : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464));
+       return v;
+ }
+ static inline void write_ireg(u_long base, u_int reg, u_int val)
+ {
+-      __asm__("str%?h %1, [%2]        @ NET_RAP
+-              str%?h  %0, [%2, #8]    @ NET_IDP
+-              " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
++      __asm__("str%?h %1, [%2]        @ NET_RAP\n\t"
++              "str%?h %0, [%2, #8]    @ NET_IDP"
++              : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
++}
++
++static inline unsigned short read_ireg(u_long base_addr, u_int reg)
++{
++      u_short v;
++      __asm__(
++      "str%?h %1, [%2]        @ NAT_RAP\n\t"
++      "str%?h %0, [%2, #8]    @ NET_IDP\n\t"
++      : "=r" (v)
++      : "r" (reg), "r" (ISAIO_BASE + 0x0464));
++      return v;
+ }
+ #define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
+@@ -91,16 +102,16 @@
+       }
+       while (length > 8) {
+               unsigned int tmp, tmp2;
+-              __asm__ __volatile__("
+-                      ldm%?ia %1!, {%2, %3}
+-                      str%?h  %2, [%0], #4
+-                      mov%?   %2, %2, lsr #16
+-                      str%?h  %2, [%0], #4
+-                      str%?h  %3, [%0], #4
+-                      mov%?   %3, %3, lsr #16
+-                      str%?h  %3, [%0], #4
+-              " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
+-                : "0" (offset), "1" (buf));
++              __asm__ __volatile__(
++                      "ldm%?ia        %1!, {%2, %3}\n\t"
++                      "str%?h %2, [%0], #4\n\t"
++                      "mov%?  %2, %2, lsr #16\n\t"
++                      "str%?h %2, [%0], #4\n\t"
++                      "str%?h %3, [%0], #4\n\t"
++                      "mov%?  %3, %3, lsr #16\n\t"
++                      "str%?h %3, [%0], #4"
++              : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
++              : "0" (offset), "1" (buf));
+               length -= 8;
+       }
+       while (length > 0) {
+@@ -118,36 +129,36 @@
+       length = (length + 1) & ~1;
+       if ((int)buf & 2) {
+               unsigned int tmp;
+-              __asm__ __volatile__("
+-                      ldr%?h  %2, [%0], #4
+-                      str%?b  %2, [%1], #1
+-                      mov%?   %2, %2, lsr #8
+-                      str%?b  %2, [%1], #1
+-              " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
++              __asm__ __volatile__(
++                      "ldr%?h %2, [%0], #4\n\t"
++                      "str%?b %2, [%1], #1\n\t"
++                      "mov%?  %2, %2, lsr #8\n\t"
++                      "str%?b %2, [%1], #1"
++              : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
+               length -= 2;
+       }
+       while (length > 8) {
+               unsigned int tmp, tmp2, tmp3;
+-              __asm__ __volatile__("
+-                      ldr%?h  %2, [%0], #4
+-                      ldr%?h  %3, [%0], #4
+-                      orr%?   %2, %2, %3, lsl #16
+-                      ldr%?h  %3, [%0], #4
+-                      ldr%?h  %4, [%0], #4
+-                      orr%?   %3, %3, %4, lsl #16
+-                      stm%?ia %1!, {%2, %3}
+-              " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
+-                : "0" (offset), "1" (buf));
++              __asm__ __volatile__(
++                      "ldr%?h %2, [%0], #4\n\t"
++                      "ldr%?h %3, [%0], #4\n\t"
++                      "orr%?  %2, %2, %3, lsl #16\n\t"
++                      "ldr%?h %3, [%0], #4\n\t"
++                      "ldr%?h %4, [%0], #4\n\t"
++                      "orr%?  %3, %3, %4, lsl #16\n\t"
++                      "stm%?ia        %1!, {%2, %3}"
++              : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
++              : "0" (offset), "1" (buf));
+               length -= 8;
+       }
+       while (length > 0) {
+               unsigned int tmp;
+-              __asm__ __volatile__("
+-                      ldr%?h  %2, [%0], #4
+-                      str%?b  %2, [%1], #1
+-                      mov%?   %2, %2, lsr #8
+-                      str%?b  %2, [%1], #1
+-              " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
++              __asm__ __volatile__(
++                      "ldr%?h %2, [%0], #4\n\t"
++                      "str%?b %2, [%1], #1\n\t"
++                      "mov%?  %2, %2, lsr #8\n\t"
++                      "str%?b %2, [%1], #1"
++              : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
+               length -= 2;
+       }
+ }
+@@ -254,9 +265,27 @@
+       write_rreg (dev->base_addr, BASERXH, 0);
+       write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+       write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
++      write_rreg (dev->base_addr, CSR4, CSR4_APAD_XMIT|CSR4_MFCOM|CSR4_RCVCCOM|CSR4_TXSTRTM|CSR4_JABM);
+       write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
+ }
++static void am79c961_timer(unsigned long data)
++{
++      struct net_device *dev = (struct net_device *)data;
++      struct dev_priv *priv = (struct dev_priv *)dev->priv;
++      unsigned int lnkstat, carrier;
++
++      lnkstat = read_ireg(dev->base_addr, ISALED0) & ISALED0_LNKST;
++      carrier = netif_carrier_ok(dev);
++
++      if (lnkstat && !carrier)
++              netif_carrier_on(dev);
++      else if (!lnkstat && carrier)
++              netif_carrier_off(dev);
++
++      mod_timer(&priv->timer, jiffies + 5*HZ);
++}
++
+ /*
+  * Open/initialize the board.
+  */
+@@ -274,6 +303,11 @@
+       am79c961_init_for_open(dev);
++      netif_carrier_off(dev);
++
++      priv->timer.expires = jiffies;
++      add_timer(&priv->timer);
++
+       netif_start_queue(dev);
+       return 0;
+@@ -288,7 +322,10 @@
+       struct dev_priv *priv = (struct dev_priv *)dev->priv;
+       unsigned long flags;
++      del_timer_sync(&priv->timer);
++
+       netif_stop_queue(dev);
++      netif_carrier_off(dev);
+       spin_lock_irqsave(priv->chip_lock, flags);
+       write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+@@ -413,15 +450,6 @@
+       unsigned int hdraddr, bufaddr;
+       unsigned int head;
+       unsigned long flags;
+-      
+-      /* FIXME: I thought the 79c961 could do padding - RMK ??? */
+-      if(length < ETH_ZLEN)
+-      {
+-              skb = skb_padto(skb, ETH_ZLEN);
+-              if(skb == NULL)
+-                      return 0;
+-              length = ETH_ZLEN;
+-      }
+       head = priv->txhead;
+       hdraddr = priv->txhdr + (head << 3);
+@@ -431,7 +459,7 @@
+               head = 0;
+       am_writebuffer (dev, bufaddr, skb->data, length);
+-      am_writeword (dev, hdraddr + 4, -length);
++      am_writeword (dev, hdraddr + 4, -skb->len);
+       am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
+       priv->txhead = head;
+@@ -448,6 +476,8 @@
+       if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
+               netif_stop_queue(dev);
++      priv->stats.tx_bytes += skb->len;
++
+       dev_kfree_skb(skb);
+       return 0;
+@@ -520,6 +550,7 @@
+ am79c961_tx(struct net_device *dev, struct dev_priv *priv)
+ {
+       do {
++              signed short len;
+               u_int hdraddr;
+               u_int status;
+@@ -555,6 +586,8 @@
+                       continue;
+               }
+               priv->stats.tx_packets ++;
++              len = am_readword (dev, hdraddr + 4);
++              priv->stats.tx_bytes += -len;
+       } while (priv->txtail != priv->txhead);
+       netif_wake_queue(dev);
+@@ -565,17 +598,23 @@
+ {
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct dev_priv *priv = (struct dev_priv *)dev->priv;
+-      u_int status;
++      u_int status, n = 100;
+-      status = read_rreg(dev->base_addr, CSR0);
+-      write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
++      do {
++              status = read_rreg(dev->base_addr, CSR0);
++              write_rreg(dev->base_addr, CSR0, status &
++                         (CSR0_IENA|CSR0_TINT|CSR0_RINT|
++                          CSR0_MERR|CSR0_MISS|CSR0_CERR|CSR0_BABL));
+-      if (status & CSR0_RINT)
+-              am79c961_rx(dev, priv);
+-      if (status & CSR0_TINT)
+-              am79c961_tx(dev, priv);
+-      if (status & CSR0_MISS)
+-              priv->stats.rx_dropped ++;
++              if (status & CSR0_RINT)
++                      am79c961_rx(dev, priv);
++              if (status & CSR0_TINT)
++                      am79c961_tx(dev, priv);
++              if (status & CSR0_MISS)
++                      priv->stats.rx_dropped ++;
++              if (status & CSR0_CERR)
++                      mod_timer(&priv->timer, jiffies);
++      } while (--n && status & (CSR0_RINT | CSR0_TINT));
+ }
+ /*
+@@ -587,10 +626,10 @@
+ {
+       struct dev_priv *priv = (struct dev_priv *)dev->priv;
+-      spin_lock_irq(priv->chip_lock);
++      spin_lock_irq(&priv->chip_lock);
+       write_rreg (dev->base_addr, CSR0, CSR0_STOP);
+       write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
+-      spin_unlock_irq(priv->chip_lock);
++      spin_unlock_irq(&priv->chip_lock);
+       am79c961_ramtest(dev, 0x66);
+       am79c961_ramtest(dev, 0x99);
+@@ -655,6 +694,11 @@
+               printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
+       }
++      spin_lock_init(&priv->chip_lock);
++      init_timer(&priv->timer);
++      priv->timer.data = (unsigned long)dev;
++      priv->timer.function = am79c961_timer;
++
+       if (am79c961_hw_init(dev))
+               goto release;
+--- linux-2.4.27/drivers/net/am79c961a.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/am79c961a.h
+@@ -58,6 +58,18 @@
+ #define CSR3_BABLM    0x4000
+ #define CSR3_MASKALL  0x5F00
++#define CSR4          4
++#define CSR4_JABM     0x0001
++#define CSR4_JAB      0x0002
++#define CSR4_TXSTRTM  0x0004
++#define CSR4_TXSTRT   0x0008
++#define CSR4_RCVCCOM  0x0010
++#define CSR4_RCVCCO   0x0020
++#define CSR4_MFCOM    0x0100
++#define CSR4_MFCO     0x0200
++#define CSR4_ASTRP_RCV        0x0400
++#define CSR4_APAD_XMIT        0x0800
++
+ #define CTRL1         5
+ #define CTRL1_SPND    0x0001
+@@ -93,6 +105,8 @@
+ #define SIZERXR               76
+ #define SIZETXR               78
++#define CSR_MFC               112
++
+ #define RMD_ENP               0x0100
+ #define RMD_STP               0x0200
+ #define RMD_CRC               0x0800
+@@ -112,6 +126,9 @@
+ #define TST_UFLO      0x4000
+ #define TST_BUFF      0x8000
++#define ISALED0               0x0004
++#define ISALED0_LNKST 0x8000
++
+ struct dev_priv {
+     struct net_device_stats stats;
+     unsigned long     rxbuffer[RX_BUFFERS];
+@@ -123,6 +140,7 @@
+     unsigned long     rxhdr;
+     unsigned long     txhdr;
+     spinlock_t                chip_lock;
++    struct timer_list timer;
+ };
+ extern int    am79c961_probe (struct net_device *dev);
+--- linux-2.4.27/drivers/net/cirrus.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/cirrus.c
+@@ -75,6 +75,7 @@
+ typedef struct {
+       struct net_device_stats stats;
+       u16 txlen;
++      u16 txafter;    /* Default is After5 (0) */
+ } cirrus_t;
+ typedef struct {
+@@ -230,13 +231,19 @@
+       cirrus_t *priv = (cirrus_t *) dev->priv;
+       u16 status;
++      /* Tx start must be done with irq disabled
++       * else status can be wrong */
++      disable_irq (dev->irq);
++
+       netif_stop_queue (dev);
+-      cirrus_write (dev,PP_TxCMD,TxStart (After5));
++      cirrus_write (dev,PP_TxCMD,TxStart (priv->txafter));
+       cirrus_write (dev,PP_TxLength,skb->len);
+       status = cirrus_read (dev,PP_BusST);
++      enable_irq (dev->irq);
++
+       if ((status & TxBidErr)) {
+               printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len);
+               priv->stats.tx_errors++;
+@@ -249,7 +256,6 @@
+               printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name);
+               priv->stats.tx_errors++;
+               priv->txlen = 0;
+-              /* FIXME: store skb and send it in interrupt handler */
+               return (1);
+       }
+@@ -310,11 +316,18 @@
+                       }
+                       if ((RegContent (status) & TxUnderrun)) {
+                               priv->stats.tx_errors++;
+-                              priv->stats.tx_fifo_errors++;
++                              /* Shift start tx, if underruns come too often */
++                              switch (priv->stats.tx_fifo_errors++) {
++                              case 3: priv->txafter = After381; break;
++                              case 6: priv->txafter = After1021; break;
++                              case 9: priv->txafter = AfterAll; break;
++                              }
++                      }
++                      /* Wakeup only for tx events ! */
++                      if ((RegContent (status) & (TxUnderrun | Rdy4Tx))) {
++                              priv->txlen = 0;
++                              netif_wake_queue (dev);
+                       }
+-                      /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
+-                      priv->txlen = 0;
+-                      netif_wake_queue (dev);
+                       break;
+               case TxCOL:
+@@ -428,7 +441,7 @@
+       else
+               cirrus_clear (dev,PP_RxCTL,PromiscuousA);
+-      if ((dev->flags & IFF_ALLMULTI) && dev->mc_list)
++      if ((dev->flags & IFF_ALLMULTI) || dev->mc_list)
+               cirrus_set (dev,PP_RxCTL,MulticastA);
+       else
+               cirrus_clear (dev,PP_RxCTL,MulticastA);
+--- linux-2.4.27/drivers/net/cs89x0.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/cs89x0.c
+@@ -115,6 +115,7 @@
+ */
++#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
+ #include <linux/types.h>
+@@ -427,18 +428,18 @@
+       /* if they give us an odd I/O address, then do ONE write to
+            the address port, to get it back to address zero, where we
+            expect to find the EISA signature word. An IO with a base of 0x3
+-         will skip the test for the ADD_PORT. */
++         will skip the test for the ADD_PORT. */
+       if (ioaddr & 1) {
+               if (net_debug > 1)
+                       printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr);
+-              if ((ioaddr & 2) != 2)
++              if ((ioaddr & 2) != 2)
+                       if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) {
+                               printk(KERN_ERR "%s: bad signature 0x%x\n",
+                                       dev->name, inw((ioaddr & ~3)+ ADD_PORT));
+                               retval = -ENODEV;
+                               goto out2;
+                       }
+-              ioaddr &= ~3;
++              ioaddr &= ~3;
+               outw(PP_ChipID, ioaddr + ADD_PORT);
+       }
+ printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT));
+@@ -446,7 +447,7 @@
+       if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) {
+               printk(KERN_ERR "%s: incorrect signature 0x%x\n",
+                       dev->name, inw(ioaddr + DATA_PORT));
+-              retval = -ENODEV;
++              retval = -ENODEV;
+               goto out2;
+       }
+@@ -477,7 +478,7 @@
+              dev->base_addr);
+       reset_chip(dev);
+-   
++
+         /* Here we read the current configuration of the chip. If there
+          is no Extended EEPROM then the idea is to not disturb the chip
+          configuration, it should have been correctly setup by automatic
+--- linux-2.4.27/drivers/net/ether00.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/ether00.c
+@@ -38,6 +38,7 @@
+ #include <asm/arch/ether00.h>
+ #include <asm/arch/tdkphy.h>
++static int ether00_get_ethernet_address(struct net_device* dev);
+ MODULE_AUTHOR("Clive Davies");
+ MODULE_DESCRIPTION("Altera Ether00 IP core driver");
+@@ -734,8 +735,11 @@
+       int result,tmp;
+       struct net_priv* priv;
+-      if (!is_valid_ether_addr(dev->dev_addr))
+-              return -EINVAL;
++      if (!ether00_get_ethernet_address(dev)){
++        printk("%s: Invalid ethernet MAC address.  Please set using "
++               "ifconfig\n", dev->name);
++        return -EINVAL;
++      }
+       dev->base_addr=(unsigned int)ioremap_nocache(base,SZ_4K);
+@@ -906,10 +910,9 @@
+ }
+-static void ether00_get_ethernet_address(struct net_device* dev)
++static int ether00_get_ethernet_address(struct net_device* dev)
+ {
+       struct mtd_info *mymtd=NULL;
+-      int i;
+       size_t retlen;
+       /*
+@@ -926,11 +929,7 @@
+ #ifdef CONFIG_ARCH_CAMELOT
+ #ifdef CONFIG_MTD
+       /* get the mtd_info structure for the first mtd device*/
+-      for(i=0;i<MAX_MTD_DEVICES;i++){
+-              mymtd=get_mtd_device(NULL,i);
+-              if(!mymtd||!strcmp(mymtd->name,"EPXA10DB flash"))
+-                      break;
+-      }
++      mymtd=get_mtd_device(NULL,0);
+       if(!mymtd || !mymtd->read_user_prot_reg){
+               printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name);
+@@ -947,9 +946,7 @@
+ #endif
+ #endif
+-      if (!is_valid_ether_addr(dev->dev_addr))
+-              printk("%s: Invalid ethernet MAC address.  Please set using "
+-                      "ifconfig\n", dev->name);
++      return (is_valid_ether_addr(dev->dev_addr));
+ }
+@@ -966,8 +963,6 @@
+       dev->tx_timeout=ether00_tx_timeout;
+       dev->watchdog_timeo=TX_TIMEOUT;
+-      ether00_get_ethernet_address(dev);
+-
+       SET_MODULE_OWNER(dev);
+       return 0;
+ }
+--- linux-2.4.27/drivers/net/irda/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/irda/Config.in
+@@ -40,7 +40,7 @@
+ dep_tristate 'VIA IrCC (Experimental)' CONFIG_VIA_IRCC_FIR $CONFIG_IRDA
+ fi
+ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+-   dep_tristate 'SA1100 Internal IR' CONFIG_SA1100_FIR $CONFIG_IRDA
++   dep_tristate 'SA1100 Internal IR' CONFIG_SA1100_FIR $CONFIG_IRDA $CONFIG_EXPERIMENTAL
+ fi
+ endmenu
+--- linux-2.4.27/drivers/net/irda/sa1100_ir.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/irda/sa1100_ir.c
+@@ -38,11 +38,7 @@
+ #include <asm/arch/assabet.h>
+-#ifndef CONFIG_SA1100_H3600
+-#define clr_h3600_egpio(x)    do { } while (0)
+-#define set_h3600_egpio(x)    do { } while (0)
+-#endif
+-
++/* Yopy wants fixing */
+ #ifndef GPIO_IRDA_FIR
+ #define GPIO_IRDA_FIR         (0)
+ #endif
+@@ -174,8 +170,8 @@
+               if (machine_is_assabet())
+                       ASSABET_BCR_clear(ASSABET_BCR_IRDA_FSEL);
+-              if (machine_is_h3600())
+-                      clr_h3600_egpio(EGPIO_H3600_IR_FSEL);
++              if (machine_is_h3xxx())
++                      clr_h3600_egpio(IPAQ_EGPIO_IR_FSEL);
+               if (machine_is_yopy())
+                       PPSR &= ~GPIO_IRDA_FIR;
+@@ -199,8 +195,8 @@
+               if (machine_is_assabet())
+                       ASSABET_BCR_set(ASSABET_BCR_IRDA_FSEL);
+-              if (machine_is_h3600())
+-                      set_h3600_egpio(EGPIO_H3600_IR_FSEL);
++              if (machine_is_h3xxx())
++                      set_h3600_egpio(IPAQ_EGPIO_IR_FSEL);
+               if (machine_is_yopy())
+                       PPSR |= GPIO_IRDA_FIR;
+@@ -246,10 +242,7 @@
+ static inline int
+ sa1100_irda_set_power_h3600(struct sa1100_irda *si, unsigned int state)
+ {
+-      if (state)
+-              set_h3600_egpio(EGPIO_H3600_IR_ON);
+-      else
+-              clr_h3600_egpio(EGPIO_H3600_IR_ON);
++      assign_h3600_egpio( IPAQ_EGPIO_IR_ON, state );
+       return 0;
+ }
+@@ -283,7 +276,7 @@
+       if (machine_is_assabet())
+               ret = sa1100_irda_set_power_assabet(si, state);
+-      if (machine_is_h3600())
++      if (machine_is_h3xxx())
+               ret = sa1100_irda_set_power_h3600(si, state);
+       if (machine_is_yopy())
+               ret = sa1100_irda_set_power_yopy(si, state);
+@@ -727,11 +720,6 @@
+       netif_wake_queue(dev);
+ }
+-/*
+- * Note that we will never build up a backlog of frames; the protocol is a
+- * half duplex protocol which basically means we transmit a frame, we
+- * receive a frame, we transmit the next frame etc.
+- */
+ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+       struct sa1100_irda *si = dev->priv;
+@@ -758,6 +746,8 @@
+       }
+       if (!IS_FIR(si)) {
++              netif_stop_queue(dev);
++
+               si->tx_buff.data = si->tx_buff.head;
+               si->tx_buff.len  = async_wrap_skb(skb, si->tx_buff.data,
+                                                 si->tx_buff.truesize);
+--- linux-2.4.27/drivers/net/irda/w83977af_ir.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/irda/w83977af_ir.c
+@@ -205,7 +205,7 @@
+       /* FIXME: The HP HDLS-1100 does not support 1152000! */
+       self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+-              IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
++              IR_115200/*|IR_576000|IR_1152000|(IR_4000000 << 8)*/;
+       /* The HP HDLS-1100 needs 1 ms according to the specs */
+       self->qos.min_turn_time.bits = qos_mtt_bits;
+@@ -1341,7 +1341,7 @@
+       case SIOCSBANDWIDTH: /* Set bandwidth */
+               if (!capable(CAP_NET_ADMIN)) {
+                       ret = -EPERM;
+-                      goto out;
++                      break;
+               }
+               w83977af_change_speed(self, irq->ifr_baudrate);
+               break;
+--- linux-2.4.27/drivers/net/smc9194.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/smc9194.c
+@@ -12,8 +12,8 @@
+  .   AUI/TP selection  ( mine has 10Base2/10BaseT select )
+  .
+  . Arguments:
+- .    io               = for the base address
+- .    irq      = for the IRQ
++ .    io     = for the base address
++ .    irq    = for the IRQ
+  .    ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
+  .
+  . author:
+@@ -51,12 +51,21 @@
+  .                             allocation
+  .      08/20/00  Arnaldo Melo   fix kfree(skb) in smc_hardware_send_packet
+  .      12/15/00  Christian Jullien fix "Warning: kfree_skb on hard IRQ"
++ .    06/23/01  Russell King   Separate out IO functions for different bus
++ .                             types.
++ .                             Use dev->name instead of CARDNAME for printk
++ .                             Add ethtool support, full duplex support
++ .                             Add LAN91C96 support.
+  .      11/08/01 Matt Domsch     Use common crc32 function
+  ----------------------------------------------------------------------------*/
++#define DRV_NAME      "smc9194"
++#define DRV_VERSION   "0.15"
++
+ static const char version[] =
+-      "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n";
++      DRV_NAME ".c:v" DRV_VERSION " 12/15/00 by Erik Stahlman (erik@vt.edu)\n";
++#include <linux/config.h>
+ #include <linux/module.h>
+ #include <linux/version.h>
+ #include <linux/kernel.h>
+@@ -69,16 +78,26 @@
+ #include <linux/in.h>
+ #include <linux/slab.h>
+ #include <linux/string.h>
++#include <linux/delay.h>
+ #include <linux/init.h>
+ #include <linux/crc32.h>
+-#include <asm/bitops.h>
+-#include <asm/io.h>
+ #include <linux/errno.h>
++#include <linux/ethtool.h>
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/skbuff.h>
++#include <asm/bitops.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#ifdef CONFIG_ARCH_SA1100
++#include <asm/hardware.h>
++#include <asm/arch/assabet.h>
++#endif
++
+ #include "smc9194.h"
+ /*------------------------------------------------------------------------
+  .
+@@ -152,29 +171,27 @@
+  -------------------------------------------------------------------------*/
+ #define CARDNAME "SMC9194"
++static const char *chip_ids[15] = { 
++      NULL,
++      NULL,
++      NULL,
++      "SMC91C90/91C92",       /* 3 */
++      "SMC91C94/91C96",       /* 4 */
++      "SMC91C95",             /* 5 */
++      NULL,
++      "SMC91C100",            /* 7 */
++      "SMC91C100FD",          /* 8 */
++      NULL,
++      NULL,
++      NULL, 
++      NULL,
++      NULL,
++      NULL
++};
+-/* store this information for the driver.. */
+-struct smc_local {
+-      /*
+-         these are things that the kernel wants me to keep, so users
+-         can find out semi-useless statistics of how well the card is
+-         performing
+-      */
+-      struct net_device_stats stats;
+-
+-      /*
+-         If I have to wait until memory is available to send
+-         a packet, I will store the skbuff here, until I get the
+-         desired memory.  Then, I'll send it out and free it.
+-      */
+-      struct sk_buff * saved_skb;
+-
+-      /*
+-       . This keeps track of how many packets that I have
+-       . sent out.  When an TX_EMPTY interrupt comes, I know
+-       . that all of these have been sent.
+-      */
+-      int     packets_waiting;
++static const char * interfaces[2] = {
++      "TP",
++      "AUI"
+ };
+@@ -202,6 +219,11 @@
+ static int smc_open(struct net_device *dev);
+ /*
++ . This handles the ethtool interface
++*/
++static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
++
++/*
+  . Our watchdog timed out. Called by the networking layer
+ */
+ static void smc_timeout(struct net_device *dev);
+@@ -217,11 +239,11 @@
+  . This routine allows the proc file system to query the driver's
+  . statistics.
+ */
+-static struct net_device_stats * smc_query_statistics( struct net_device *dev);
++static struct net_device_stats * smc_query_statistics(struct net_device *dev);
+ /*
+- . Finally, a call to set promiscuous mode ( for TCPDUMP and related
+- . programs ) and multicast modes.
++ . Finally, a call to set promiscuous mode (for TCPDUMP and related
++ . programs) and multicast modes.
+ */
+ static void smc_set_multicast_list(struct net_device *dev);
+@@ -240,12 +262,12 @@
+  . This is a separate procedure to handle the receipt of a packet, to
+  . leave the interrupt code looking slightly cleaner
+ */
+-static inline void smc_rcv( struct net_device *dev );
++static inline void smc_rcv(struct net_device *dev);
+ /*
+  . This handles a TX interrupt, which is only called when an error
+  . relating to a packet is sent.
+ */
+-static inline void smc_tx( struct net_device * dev );
++static inline void smc_tx(struct net_device * dev);
+ /*
+  ------------------------------------------------------------
+@@ -261,39 +283,287 @@
+ */
+ static int smc_probe(struct net_device *dev, int ioaddr);
+-/*
+- . A rather simple routine to print out a packet for debugging purposes.
+-*/
+-#if SMC_DEBUG > 2
+-static void print_packet( byte *, int );
+-#endif
+-
+-#define tx_done(dev) 1
+-
+ /* this is called to actually send the packet to the chip */
+-static void smc_hardware_send_packet( struct net_device * dev );
++static void smc_hardware_send_packet(struct net_device * dev);
+ /* Since I am not sure if I will have enough room in the chip's ram
+  . to store the packet, I call this routine, which either sends it
+  . now, or generates an interrupt when the card is ready for the
+  . packet */
+-static int  smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev );
++static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *dev);
+ /* this does a soft reset on the device */
+-static void smc_reset( int ioaddr );
++static void smc_reset(struct net_device *dev);
+ /* Enable Interrupts, Receive, and Transmit */
+-static void smc_enable( int ioaddr );
++static void smc_enable(struct net_device *dev);
+ /* this puts the device in an inactive state */
+-static void smc_shutdown( int ioaddr );
++static void smc_shutdown(struct net_device *dev);
+ /* This routine will find the IRQ of the driver if one is not
+  . specified in the input to the device.  */
+-static int smc_findirq( int ioaddr );
++static int smc_findirq(struct net_device *dev);
++#ifndef CONFIG_ASSABET_NEPONSET
+ /*
+- . Function: smc_reset( int ioaddr )
++ * These functions allow us to handle IO addressing as we wish - this
++ * ethernet controller can be connected to a variety of busses.  Some
++ * busses do not support 16 bit or 32 bit transfers.  --rmk
++ */
++static inline u8 smc_inb(u_int base, u_int reg)
++{
++      return inb(base + reg);
++}
++
++static inline u16 smc_inw(u_int base, u_int reg)
++{
++      return inw(base + reg);
++}
++
++static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len)
++{
++      u_int port = base + reg;
++#ifdef USE_32_BIT
++      /* QUESTION:  Like in the TX routine, do I want
++         to send the DWORDs or the bytes first, or some
++         mixture.  A mixture might improve already slow PIO
++         performance  */
++      PRINTK3((" Reading %d dwords (and %d bytes) \n",
++              len >> 2, len & 3));
++      insl(port, data, len >> 2);
++      /* read the left over bytes */
++      insb(port, data + (len & ~3), len & 3);
++#else
++      PRINTK3((" Reading %d words and %d byte(s) \n",
++              len >> 1, len & 1));
++      insw(port, data, len >> 1);
++      if (len & 1) {
++              data += len & ~1;
++              *data = inb(port);
++      }
++#endif
++}
++
++static inline void smc_outb(u8 val, u_int base, u_int reg)
++{
++      outb(val, base + reg);
++}
++
++static inline void smc_outw(u16 val, u_int base, u_int reg)
++{
++      outw(val, base + reg);
++}
++
++static inline void smc_outl(u32 val, u_int base, u_int reg)
++{
++      u_int port = base + reg;
++#ifdef USE_32_BIT
++      outl(val, port);
++#else
++      outw(val, port);
++      outw(val >> 16, port);
++#endif
++}
++
++static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len)
++{
++      u_int port = base + reg;
++#ifdef USE_32_BIT
++      if (len & 2) {
++              outsl(port, data, len >> 2);
++              outw(*((word *)(data + (len & ~3))), port);
++      }
++      else
++              outsl(port, data, len >> 2);
++#else
++      outsw(port, data, len >> 1);
++#endif
++}
++
++
++/*-------------------------------------------------------------------------
++ .  I define some macros to make it easier to do somewhat common
++ . or slightly complicated, repeated tasks. 
++ --------------------------------------------------------------------------*/
++
++/* select a register bank, 0 to 3  */
++
++#define SMC_SELECT_BANK(x)                            \
++      {                                               \
++              smc_outw(x, ioaddr, BANK_SELECT);       \
++      } 
++
++/* define a small delay for the reset */
++#define SMC_DELAY()                                   \
++      {                                               \
++              smc_inw(ioaddr, RCR);                   \
++              smc_inw(ioaddr, RCR);                   \
++              smc_inw(ioaddr, RCR);                   \
++      }
++
++/* this enables an interrupt in the interrupt mask register */
++#define SMC_ENABLE_INT(x)                             \
++      {                                               \
++              byte mask;                              \
++              mask = smc_inb(ioaddr, INT_MASK);       \
++              mask |= (x);                            \
++              smc_outb(mask, ioaddr, INT_MASK);       \
++      }
++
++/* this sets the absolutel interrupt mask */
++#define SMC_SET_INT(x)                                        \
++      {                                               \
++              smc_outw((x), INT_MASK);                \
++      }
++
++#else
++
++#undef SMC_IO_EXTENT
++#define SMC_IO_EXTENT (16 << 2)
++
++/*
++ * These functions allow us to handle IO addressing as we wish - this
++ * ethernet controller can be connected to a variety of busses.  Some
++ * busses do not support 16 bit or 32 bit transfers.  --rmk
++ */
++static inline u8 smc_inb(u_int base, u_int reg)
++{
++      u_int port = base + reg * 4;
++
++      return readb(port);
++}
++
++static inline u16 smc_inw(u_int base, u_int reg)
++{
++      u_int port = base + reg * 4;
++
++      return readb(port) | readb(port + 4) << 8;
++}
++
++static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len)
++{
++      u_int port = base + reg * 4;
++
++      insb(port, data, len);
++}
++
++static inline void smc_outb(u8 val, u_int base, u_int reg)
++{
++      u_int port = base + reg * 4;
++
++      writeb(val, port);
++}
++
++static inline void smc_outw(u16 val, u_int base, u_int reg)
++{
++      u_int port = base + reg * 4;
++
++      writeb(val, port);
++      writeb(val >> 8, port + 4);
++}
++
++static inline void smc_outl(u32 val, u_int base, u_int reg)
++{
++      u_int port = base + reg * 4;
++
++      writeb(val, port);
++      writeb(val >> 8, port + 4);
++      writeb(val >> 16, port + 8);
++      writeb(val >> 24, port + 12);
++}
++
++static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len)
++{
++      u_int port = base + reg * 4;
++
++      outsb(port, data, len & ~1);
++}
++
++/*-------------------------------------------------------------------------
++ .  I define some macros to make it easier to do somewhat common
++ . or slightly complicated, repeated tasks. 
++ --------------------------------------------------------------------------*/
++
++/* select a register bank, 0 to 3  */
++
++#define SMC_SELECT_BANK(x)                            \
++      {                                               \
++              smc_outb(x, ioaddr, BANK_SELECT);       \
++      } 
++
++/* define a small delay for the reset */
++#define SMC_DELAY()                                   \
++      {                                               \
++              smc_inb(ioaddr, RCR);                   \
++              smc_inb(ioaddr, RCR);                   \
++              smc_inb(ioaddr, RCR);                   \
++      }
++
++/* this enables an interrupt in the interrupt mask register */
++#define SMC_ENABLE_INT(x)                             \
++      {                                               \
++              byte mask;                              \
++              mask = smc_inb(ioaddr, INT_MASK);       \
++              mask |= (x);                            \
++              smc_outb(mask, ioaddr, INT_MASK);       \
++      }
++
++/* this sets the absolutel interrupt mask */
++#define SMC_SET_INT(x)                                        \
++      {                                               \
++              smc_outb((x), ioaddr, INT_MASK);        \
++      }
++
++#endif
++
++/*
++ . A rather simple routine to print out a packet for debugging purposes.
++*/
++#if SMC_DEBUG > 2
++static void print_packet(byte * buf, int length)
++{
++      int i;
++      int remainder;
++      int lines;
++
++      printk("Packet of length %d \n", length);
++      lines = length / 16;
++      remainder = length % 16;
++
++      for (i = 0; i < lines ; i ++) {
++              int cur;
++
++              for (cur = 0; cur < 8; cur ++) {
++                      byte a, b;
++
++                      a = *(buf ++);
++                      b = *(buf ++);
++                      printk("%02x%02x ", a, b);
++              }
++              printk("\n");
++      }
++      for (i = 0; i < remainder/2 ; i++) {
++              byte a, b;
++
++              a = *(buf ++);
++              b = *(buf ++);
++              printk("%02x%02x ", a, b);
++      }
++      if (remainder & 1) {
++              byte a;
++
++              a = *buf++;
++              printk("%02x", a);
++      }
++      printk("\n");
++}
++#else
++#define print_packet(buf,len) do { } while (0)
++#endif
++
++/*
++ . Function: smc_reset(struct net_device *dev)
+  . Purpose:
+  .    This sets the SMC91xx chip to its normal state, hopefully from whatever
+  .    mess that any other DOS driver has put it in.
+@@ -309,36 +579,37 @@
+  .    5.  clear all interrupts
+  .
+ */
+-static void smc_reset( int ioaddr )
++static void smc_reset(struct net_device *dev)
+ {
++      u_int ioaddr = dev->base_addr;
++
+       /* This resets the registers mostly to defaults, but doesn't
+          affect EEPROM.  That seems unnecessary */
+-      SMC_SELECT_BANK( 0 );
+-      outw( RCR_SOFTRESET, ioaddr + RCR );
++      SMC_SELECT_BANK(0);
++      smc_outw(RCR_SOFTRESET, ioaddr, RCR);
+       /* this should pause enough for the chip to be happy */
+-      SMC_DELAY( );
++      SMC_DELAY();
+       /* Set the transmit and receive configuration registers to
+          default values */
+-      outw( RCR_CLEAR, ioaddr + RCR );
+-      outw( TCR_CLEAR, ioaddr + TCR );
++      smc_outw(RCR_CLEAR, ioaddr, RCR);
++      smc_outw(TCR_CLEAR, ioaddr, TCR);
+       /* set the control register to automatically
+          release successfully transmitted packets, to make the best
+          use out of our limited memory */
+-      SMC_SELECT_BANK( 1 );
+-      outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
++      SMC_SELECT_BANK(1);
++      smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL);
+       /* Reset the MMU */
+-      SMC_SELECT_BANK( 2 );
+-      outw( MC_RESET, ioaddr + MMU_CMD );
++      SMC_SELECT_BANK(2);
++      smc_outw(MC_RESET, ioaddr, MMU_CMD);
+       /* Note:  It doesn't seem that waiting for the MMU busy is needed here,
+          but this is a place where future chipsets _COULD_ break.  Be wary
+          of issuing another MMU command right after this */
+-
+-      outb( 0, ioaddr + INT_MASK );
++      SMC_SET_INT(0);
+ }
+ /*
+@@ -349,20 +620,21 @@
+  .    2.  Enable the receiver
+  .    3.  Enable interrupts
+ */
+-static void smc_enable( int ioaddr )
++static void smc_enable(struct net_device *dev)
+ {
+-      SMC_SELECT_BANK( 0 );
++      u_int ioaddr = dev->base_addr;
++      SMC_SELECT_BANK(0);
+       /* see the header file for options in TCR/RCR NORMAL*/
+-      outw( TCR_NORMAL, ioaddr + TCR );
+-      outw( RCR_NORMAL, ioaddr + RCR );
++      smc_outw(TCR_NORMAL, ioaddr, TCR);
++      smc_outw(RCR_NORMAL, ioaddr, RCR);
+       /* now, enable interrupts */
+-      SMC_SELECT_BANK( 2 );
+-      outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
++      SMC_SELECT_BANK(2);
++      SMC_SET_INT(SMC_INTERRUPT_MASK);
+ }
+ /*
+- . Function: smc_shutdown
++ . Function: smc_shutdown(struct net_device *dev)
+  . Purpose:  closes down the SMC91xxx chip.
+  . Method:
+  .    1. zero the interrupt mask
+@@ -375,26 +647,28 @@
+  .    the manual says that it will wake up in response to any I/O requests
+  .    in the register space.   Empirical results do not show this working.
+ */
+-static void smc_shutdown( int ioaddr )
++static void smc_shutdown(struct net_device *dev)
+ {
++      u_int ioaddr = dev->base_addr;
++
+       /* no more interrupts for me */
+-      SMC_SELECT_BANK( 2 );
+-      outb( 0, ioaddr + INT_MASK );
++      SMC_SELECT_BANK(2);
++      SMC_SET_INT(0);
+       /* and tell the card to stay away from that nasty outside world */
+-      SMC_SELECT_BANK( 0 );
+-      outb( RCR_CLEAR, ioaddr + RCR );
+-      outb( TCR_CLEAR, ioaddr + TCR );
++      SMC_SELECT_BANK(0);
++      smc_outb(RCR_CLEAR, ioaddr, RCR);
++      smc_outb(TCR_CLEAR, ioaddr, TCR);
+ #if 0
+       /* finally, shut the chip down */
+-      SMC_SELECT_BANK( 1 );
+-      outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL  );
++      SMC_SELECT_BANK(1);
++      smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL);
+ #endif
+ }
+ /*
+- . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds )
++ . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds)
+  . Purpose:
+  .    This sets the internal hardware table to filter out unwanted multicast
+  .    packets before they take up memory.
+@@ -411,26 +685,28 @@
+ */
+-static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) {
++static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs)
++{
++      u_int ioaddr = dev->base_addr;
+       int                     i;
+-      unsigned char           multicast_table[ 8 ];
++      unsigned char           multicast_table[8];
+       struct dev_mc_list      * cur_addr;
+       /* table for flipping the order of 3 bits */
+       unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+       /* start with a table of all zeros: reject all */
+-      memset( multicast_table, 0, sizeof( multicast_table ) );
++      memset(multicast_table, 0, sizeof(multicast_table));
+       cur_addr = addrs;
+-      for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next  ) {
++      for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) {
+               int position;
+               /* do we have a pointer here? */
+-              if ( !cur_addr )
++              if (!cur_addr)
+                       break;
+               /* make sure this is a multicast address - shouldn't this
+                  be a given if we have it here ? */
+-              if ( !( *cur_addr->dmi_addr & 1 ) )
++              if (!(*cur_addr->dmi_addr & 1))
+                       continue;
+               /* only use the low order bits */
+@@ -442,15 +718,15 @@
+       }
+       /* now, the table can be loaded into the chipset */
+-      SMC_SELECT_BANK( 3 );
++      SMC_SELECT_BANK(3);
+-      for ( i = 0; i < 8 ; i++ ) {
+-              outb( multicast_table[i], ioaddr + MULTICAST1 + i );
++      for (i = 0; i < 8 ; i++) {
++              smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i);
+       }
+ }
+ /*
+- . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
++ . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *)
+  . Purpose:
+  .    Attempt to allocate memory for a packet, if chip-memory is not
+  .    available, then tell the card to generate an interrupt when it
+@@ -465,10 +741,10 @@
+  . o  (NO): Enable interrupts and let the interrupt handler deal with it.
+  . o  (YES):Send it now.
+ */
+-static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev )
++static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev)
+ {
+       struct smc_local *lp    = (struct smc_local *)dev->priv;
+-      unsigned short ioaddr   = dev->base_addr;
++      u_int ioaddr            = dev->base_addr;
+       word                    length;
+       unsigned short          numPages;
+       word                    time_out;
+@@ -477,15 +753,16 @@
+       /* Well, I want to send the packet.. but I don't know
+          if I can send it right now...  */
+-      if ( lp->saved_skb) {
++      if (lp->saved_skb) {
+               /* THIS SHOULD NEVER HAPPEN. */
+               lp->stats.tx_aborted_errors++;
+-              printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
++              printk("%s: Bad Craziness - sent packet while busy.\n",
++                      dev->name);
+               return 1;
+       }
+       length = skb->len;
+-
++              
+       if(length < ETH_ZLEN)
+       {
+               skb = skb_padto(skb, ETH_ZLEN);
+@@ -497,18 +774,18 @@
+               length = ETH_ZLEN;
+       }
+       lp->saved_skb = skb;
+-              
++
+       /*
+       ** The MMU wants the number of pages to be the number of 256 bytes
+-      ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
++      ** 'pages', minus 1 (since a packet can't ever have 0 pages :))
+       **
+       ** Pkt size for allocating is data length +6 (for additional status words,
+       ** length and ctl!) If odd size last byte is included in this header.
+       */
+-      numPages =  ((length & 0xfffe) + 6) / 256;
++      numPages = ((length & 0xfffe) + 6) / 256;
+-      if (numPages > 7 ) {
+-              printk(CARDNAME": Far too big packet error. \n");
++      if (numPages > 7) {
++              printk("%s: Far too big packet error.\n", dev->name);
+               /* freeing the packet is a good thing here... but should
+                . any packets of this size get down here?   */
+               dev_kfree_skb (skb);
+@@ -517,12 +794,13 @@
+               netif_wake_queue(dev);
+               return 0;
+       }
++
+       /* either way, a packet is waiting now */
+       lp->packets_waiting++;
+       /* now, try to allocate the memory */
+-      SMC_SELECT_BANK( 2 );
+-      outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
++      SMC_SELECT_BANK(2);
++      smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD);
+       /*
+       . Performance Hack
+       .
+@@ -539,21 +817,21 @@
+       do {
+               word    status;
+-              status = inb( ioaddr + INTERRUPT );
+-              if ( status & IM_ALLOC_INT ) {
++              status = smc_inb(ioaddr, INTERRUPT);
++              if (status & IM_ALLOC_INT) {
+                       /* acknowledge the interrupt */
+-                      outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
+-                      break;
++                      smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT);
++                      break;
+               }
+-      } while ( -- time_out );
++      } while (-- time_out);
+-      if ( !time_out ) {
++      if (!time_out) {
+               /* oh well, wait until the chip finds memory later */
+-              SMC_ENABLE_INT( IM_ALLOC_INT );
+-                      PRINTK2((CARDNAME": memory allocation deferred. \n"));
++              SMC_ENABLE_INT(IM_ALLOC_INT);
++              PRINTK2(("%s: memory allocation deferred.\n", dev->name));
+               /* it's deferred, but I'll handle it later */
+-                      return 0;
+-      }
++              return 0;
++      }
+       /* or YES! I can send the packet now.. */
+       smc_hardware_send_packet(dev);
+       netif_wake_queue(dev);
+@@ -561,46 +839,46 @@
+ }
+ /*
+- . Function:  smc_hardware_send_packet(struct net_device * )
++ . Function:  smc_hardware_send_packet(struct net_device *)
+  . Purpose:
+  .    This sends the actual packet to the SMC9xxx chip.
+  .
+  . Algorithm:
+  .    First, see if a saved_skb is available.
+- .            ( this should NOT be called if there is no 'saved_skb'
++ .            (this should NOT be called if there is no 'saved_skb'
+  .    Now, find the packet number that the chip allocated
+  .    Point the data pointers at it in memory
+  .    Set the length word in the chip's memory
+  .    Dump the packet to chip memory
+- .    Check if a last byte is needed ( odd length packet )
++ .    Check if a last byte is needed (odd length packet)
+  .            if so, set the control flag right
+  .    Tell the card to send it
+  .    Enable the transmit interrupt, so I know if it failed
+  .    Free the kernel data if I actually sent it.
+ */
+-static void smc_hardware_send_packet( struct net_device * dev )
++static void smc_hardware_send_packet(struct net_device *dev)
+ {
+       struct smc_local *lp = (struct smc_local *)dev->priv;
+-      byte                    packet_no;
+-      struct sk_buff *        skb = lp->saved_skb;
+-      word                    length;
+-      unsigned short          ioaddr;
+-      byte                    * buf;
+-
+-      ioaddr = dev->base_addr;
++      struct sk_buff *skb = lp->saved_skb;
++      word length, lastword;
++      u_int ioaddr = dev->base_addr;
++      byte packet_no;
++      byte *buf;
+-      if ( !skb ) {
+-              PRINTK((CARDNAME": In XMIT with no packet to send \n"));
++      if (!skb) {
++              PRINTK(("%s: In XMIT with no packet to send\n", dev->name));
+               return;
+       }
++
+       length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+       buf = skb->data;
+       /* If I get here, I _know_ there is a packet slot waiting for me */
+-      packet_no = inb( ioaddr + PNR_ARR + 1 );
+-      if ( packet_no & 0x80 ) {
++      packet_no = smc_inb(ioaddr, PNR_ARR + 1);
++      if (packet_no & 0x80) {
+               /* or isn't there?  BAD CHIP! */
+-              printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n");
++              printk(KERN_DEBUG "%s: Memory allocation failed.\n",
++                      dev->name);
+               dev_kfree_skb_any(skb);
+               lp->saved_skb = NULL;
+               netif_wake_queue(dev);
+@@ -608,26 +886,19 @@
+       }
+       /* we have a packet address, so tell the card to use it */
+-      outb( packet_no, ioaddr + PNR_ARR );
++      smc_outb(packet_no, ioaddr, PNR_ARR);
+       /* point to the beginning of the packet */
+-      outw( PTR_AUTOINC , ioaddr + POINTER );
++      smc_outw(PTR_AUTOINC, ioaddr, POINTER);
+-      PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length ));
+-#if SMC_DEBUG > 2
+-      print_packet( buf, length );
+-#endif
++      PRINTK3(("%s: Trying to xmit packet of length %x\n",
++              dev->name, length));
+-      /* send the packet length ( +6 for status, length and ctl byte )
+-         and the status word ( set to zeros ) */
+-#ifdef USE_32_BIT
+-      outl(  (length +6 ) << 16 , ioaddr + DATA_1 );
+-#else
+-      outw( 0, ioaddr + DATA_1 );
+-      /* send the packet length ( +6 for status words, length, and ctl*/
+-      outb( (length+6) & 0xFF,ioaddr + DATA_1 );
+-      outb( (length+6) >> 8 , ioaddr + DATA_1 );
+-#endif
++      print_packet(buf, length);
++
++      /* send the packet length (+6 for status, length and ctl byte)
++         and the status word (set to zeros) */
++      smc_outl((length + 6) << 16, ioaddr, DATA_1);
+       /* send the actual data
+        . I _think_ it's faster to send the longs first, and then
+@@ -636,32 +907,22 @@
+        . a good idea to check which is optimal?  But that could take
+        . almost as much time as is saved?
+       */
+-#ifdef USE_32_BIT
+-      if ( length & 0x2  ) {
+-              outsl(ioaddr + DATA_1, buf,  length >> 2 );
+-              outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+-      }
+-      else
+-              outsl(ioaddr + DATA_1, buf,  length >> 2 );
+-#else
+-      outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
+-#endif
+-      /* Send the last byte, if there is one.   */
++      smc_outs(ioaddr, DATA_1, buf, length);
+-      if ( (length & 1) == 0 ) {
+-              outw( 0, ioaddr + DATA_1 );
+-      } else {
+-              outb( buf[length -1 ], ioaddr + DATA_1 );
+-              outb( 0x20, ioaddr + DATA_1);
+-      }
++      /* Send the last byte, if there is one.   */
++      if ((length & 1) == 0)
++              lastword = 0;
++      else
++              lastword = 0x2000 | buf[length - 1];
++      smc_outw(lastword, ioaddr, DATA_1);
+       /* enable the interrupts */
+-      SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
++      SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT);
+       /* and let the chipset deal with it */
+-      outw( MC_ENQUEUE , ioaddr + MMU_CMD );
++      smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD);
+-      PRINTK2((CARDNAME": Sent packet of length %d \n",length));
++      PRINTK2(("%s: Sent packet of length %d\n", dev->name, length));
+       lp->saved_skb = NULL;
+       dev_kfree_skb_any (skb);
+@@ -676,7 +937,7 @@
+ /*-------------------------------------------------------------------------
+  |
+- | smc_init( struct net_device * dev )
++ | smc_init(struct net_device * dev)
+  |   Input parameters:
+  |    dev->base_addr == 0, try to find all possible locations
+  |    dev->base_addr == 1, return failure code
+@@ -691,6 +952,65 @@
+ */
+ int __init smc_init(struct net_device *dev)
+ {
++      int ret = -ENODEV;
++#if defined(CONFIG_ASSABET_NEPONSET)
++      if (machine_is_assabet() && machine_has_neponset()) {
++              unsigned int *addr;
++              unsigned char ecor;
++              unsigned long flags;
++
++              NCR_0 |= NCR_ENET_OSC_EN;
++              dev->irq = IRQ_NEPONSET_SMC9196;
++
++              /*
++               * Map the attribute space.  This is overkill, but clean.
++               */
++              addr = ioremap(0x18000000 + (1 << 25), 64 * 1024 * 4);
++              if (!addr)
++                      return -ENOMEM;
++
++              /*
++               * Reset the device.  We must disable IRQs around this.
++               */
++              local_irq_save(flags);
++              ecor = readl(addr + ECOR) & ~ECOR_RESET;
++              writel(ecor | ECOR_RESET, addr + ECOR);
++              udelay(100);
++
++              /*
++               * The device will ignore all writes to the enable bit while
++               * reset is asserted, even if the reset bit is cleared in the
++               * same write.  Must clear reset first, then enable the device.
++               */
++              writel(ecor, addr + ECOR);
++              writel(ecor | ECOR_ENABLE, addr + ECOR);
++
++              /*
++               * Force byte mode.
++               */
++              writel(readl(addr + ECSR) | ECSR_IOIS8, addr + ECSR);
++              local_irq_restore(flags);
++
++              iounmap(addr);
++
++              /*
++               * Wait for the chip to wake up.
++               */
++              mdelay(1);
++
++              /*
++               * Map the real registers.
++               */
++              addr = ioremap(0x18000000, 8 * 1024);
++              if (!addr)
++                      return -ENOMEM;
++
++              ret = smc_probe(dev, (int)addr);
++              if (ret)
++                      iounmap(addr);
++      }
++
++#elif defined(CONFIG_ISA)
+       int i;
+       int base_addr = dev->base_addr;
+@@ -708,7 +1028,8 @@
+                       return 0;
+       /* couldn't find anything */
+-      return -ENODEV;
++#endif
++      return ret;
+ }
+ /*----------------------------------------------------------------------
+@@ -718,10 +1039,11 @@
+  . interrupt, so an auto-detect routine can detect it, and find the IRQ,
+  ------------------------------------------------------------------------
+ */
+-int __init smc_findirq( int ioaddr )
++int __init smc_findirq(struct net_device *dev)
+ {
+       int     timeout = 20;
+       unsigned long cookie;
++      u_int ioaddr = dev->base_addr;
+       /* I have to do a STI() here, because this is called from
+@@ -737,26 +1059,25 @@
+        * when done.
+        */
+-
++      /* enable ALLOCation interrupts ONLY. */
+       SMC_SELECT_BANK(2);
+-      /* enable ALLOCation interrupts ONLY */
+-      outb( IM_ALLOC_INT, ioaddr + INT_MASK );
++      SMC_SET_INT(IM_ALLOC_INT);
+       /*
+        . Allocate 512 bytes of memory.  Note that the chip was just
+        . reset so all the memory is available
+       */
+-      outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
++      smc_outw(MC_ALLOC | 1, ioaddr, MMU_CMD);
+       /*
+        . Wait until positive that the interrupt has been generated
+       */
+-      while ( timeout ) {
++      while (timeout) {
+               byte    int_status;
+-              int_status = inb( ioaddr + INTERRUPT );
++              int_status = smc_inb(ioaddr, INTERRUPT);
+-              if ( int_status & IM_ALLOC_INT )
++              if (int_status & IM_ALLOC_INT)
+                       break;          /* got the interrupt */
+               timeout--;
+       }
+@@ -775,7 +1096,7 @@
+       SMC_DELAY();
+       /* and disable all interrupts again */
+-      outb( 0, ioaddr + INT_MASK );
++      SMC_SET_INT(0);
+       /* clear hardware interrupts again, because that's how it
+          was when I was called... */
+@@ -785,8 +1106,87 @@
+       return probe_irq_off(cookie);
+ }
++static int __init smc_probe_chip(struct net_device *dev, int ioaddr)
++{
++      unsigned int temp;
++
++      /* First, see if the high byte is 0x33 */
++      temp = smc_inw(ioaddr, BANK_SELECT);
++      if ((temp & 0xFF00) != 0x3300)
++              return -ENODEV;
++
++      /* The above MIGHT indicate a device, but I need to write to further
++         test this.  */
++      smc_outw(0, ioaddr, BANK_SELECT);
++      temp = smc_inw(ioaddr, BANK_SELECT);
++      if ((temp & 0xFF00) != 0x3300)
++              return -ENODEV;
++
++#ifndef CONFIG_ASSABET_NEPONSET
++      /* well, we've already written once, so hopefully another time won't
++         hurt.  This time, I need to switch the bank register to bank 1,
++         so I can access the base address register */
++      SMC_SELECT_BANK(1);
++      temp = smc_inw(ioaddr, BASE);
++      if (ioaddr != (temp >> 3 & 0x3E0)) {
++              printk("%s: IOADDR %x doesn't match configuration (%x)."
++                      "Probably not a SMC chip\n", dev->name,
++                      ioaddr, (base_address_register >> 3) & 0x3E0);
++              /* well, the base address register didn't match.  Must not have
++                 been a SMC chip after all. */
++              return -ENODEV;
++      }
++#endif
++
++      return 0;
++}
++
++/*
++ . If dev->irq is 0, then the device has to be banged on to see
++ . what the IRQ is.
++ .
++ . This banging doesn't always detect the IRQ, for unknown reasons.
++ . a workaround is to reset the chip and try again.
++ .
++ . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
++ . be what is requested on the command line.   I don't do that, mostly
++ . because the card that I have uses a non-standard method of accessing
++ . the IRQs, and because this _should_ work in most configurations.
++ .
++ . Specifying an IRQ is done with the assumption that the user knows
++ . what (s)he is doing.  No checking is done!!!!
++ .
++*/
++static int __init smc_probe_irq(struct net_device *dev)
++{
++      if (dev->irq < 2) {
++              int     trials;
++
++              trials = 3;
++              while (trials--) {
++                      dev->irq = smc_findirq(dev);
++                      if (dev->irq)
++                              break;
++                      /* kick the card and try again */
++                      smc_reset(dev);
++              }
++      }
++      if (dev->irq == 0) {
++              printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
++                      dev->name);
++              return -ENODEV;
++      }
++
++      /*
++       * Some machines (eg, PCs) need to cannonicalize their IRQs.
++       */
++      dev->irq = irq_cannonicalize(dev->irq);
++
++      return 0;
++}
++
+ /*----------------------------------------------------------------------
+- . Function: smc_probe( int ioaddr )
++ . Function: smc_probe(struct net_device *dev, int ioaddr)
+  .
+  . Purpose:
+  .    Tests to see if a given ioaddr points to an SMC9xxx chip.
+@@ -816,16 +1216,14 @@
+ */
+ static int __init smc_probe(struct net_device *dev, int ioaddr)
+ {
++      struct smc_local *smc;
+       int i, memory, retval;
+       static unsigned version_printed;
+-      unsigned int bank;
+       const char *version_string;
+-      const char *if_string;
+       /* registers */
+       word revision_register;
+-      word base_address_register;
+       word configuration_register;
+       word memory_info_register;
+       word memory_cfg_register;
+@@ -834,44 +1232,24 @@
+       if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name))
+               return -EBUSY;
+-      /* First, see if the high byte is 0x33 */
+-      bank = inw( ioaddr + BANK_SELECT );
+-      if ( (bank & 0xFF00) != 0x3300 ) {
+-              retval = -ENODEV;
+-              goto err_out;
+-      }
+-      /* The above MIGHT indicate a device, but I need to write to further
+-              test this.  */
+-      outw( 0x0, ioaddr + BANK_SELECT );
+-      bank = inw( ioaddr + BANK_SELECT );
+-      if ( (bank & 0xFF00 ) != 0x3300 ) {
+-              retval = -ENODEV;
+-              goto err_out;
+-      }
+-      /* well, we've already written once, so hopefully another time won't
+-         hurt.  This time, I need to switch the bank register to bank 1,
+-         so I can access the base address register */
+-      SMC_SELECT_BANK(1);
+-      base_address_register = inw( ioaddr + BASE );
+-      if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) )  {
+-              printk(CARDNAME ": IOADDR %x doesn't match configuration (%x)."
+-                      "Probably not a SMC chip\n",
+-                      ioaddr, base_address_register >> 3 & 0x3E0 );
+-              /* well, the base address register didn't match.  Must not have
+-                 been a SMC chip after all. */
+-              retval = -ENODEV;
++      /*
++       * Do the basic probes.
++       */
++      retval = smc_probe_chip(dev, ioaddr);
++      if (retval)
+               goto err_out;
+-      }
+       /*  check if the revision register is something that I recognize.
+           These might need to be added to later, as future revisions
+           could be added.  */
+       SMC_SELECT_BANK(3);
+-      revision_register  = inw( ioaddr + REVISION );
+-      if ( !chip_ids[ ( revision_register  >> 4 ) & 0xF  ] ) {
++      revision_register = smc_inw(ioaddr, REVISION);
++      version_string = chip_ids[(revision_register >> 4) & 15];
++      if (!version_string) {
+               /* I don't recognize this chip, so... */
+-              printk(CARDNAME ": IO %x: Unrecognized revision register:"
+-                      " %x, Contact author. \n", ioaddr, revision_register );
++              printk("%s: IO %x: unrecognized revision register: %x, "
++                      "contact author.\n", dev->name, ioaddr,
++                      revision_register);
+               retval = -ENODEV;
+               goto err_out;
+@@ -882,138 +1260,122 @@
+          against the hardware address, or do some other tests. */
+       if (version_printed++ == 0)
+-              printk("%s", version);
++              printk(KERN_INFO "%s", version);
+       /* fill in some of the fields */
+       dev->base_addr = ioaddr;
+       /*
+-       . Get the MAC address ( bank 1, regs 4 - 9 )
++       . Get the MAC address (bank 1, regs 4 - 9)
+       */
+-      SMC_SELECT_BANK( 1 );
+-      for ( i = 0; i < 6; i += 2 ) {
++      SMC_SELECT_BANK(1);
++      for (i = 0; i < 6; i += 2) {
+               word    address;
+-              address = inw( ioaddr + ADDR0 + i  );
+-              dev->dev_addr[ i + 1] = address >> 8;
+-              dev->dev_addr[ i ] = address & 0xFF;
++              address = smc_inw(ioaddr, ADDR0 + i);
++              dev->dev_addr[i + 1] = address >> 8;
++              dev->dev_addr[i] = address & 0xFF;
+       }
++      if (!is_valid_ether_addr(dev->dev_addr))
++              printk("%s: Invalid ethernet MAC address.  Please set using "
++                      "ifconfig\n", dev->name);
++
+       /* get the memory information */
+-      SMC_SELECT_BANK( 0 );
+-      memory_info_register = inw( ioaddr + MIR );
+-      memory_cfg_register  = inw( ioaddr + MCR );
+-      memory = ( memory_cfg_register >> 9 )  & 0x7;  /* multiplier */
+-      memory *= 256 * ( memory_info_register & 0xFF );
++      SMC_SELECT_BANK(0);
++      memory_info_register = smc_inw(ioaddr, MIR);
++      memory_cfg_register = smc_inw(ioaddr, MCR);
++      memory = (memory_cfg_register >> 9) & 0x7;  /* multiplier */
++      memory *= 256 * (memory_info_register & 0xFF);
++
++      /* now, reset the chip, and put it into a known state */
++      smc_reset(dev);
+       /*
+-       Now, I want to find out more about the chip.  This is sort of
+-       redundant, but it's cleaner to have it in both, rather than having
+-       one VERY long probe procedure.
+-      */
+-      SMC_SELECT_BANK(3);
+-      revision_register  = inw( ioaddr + REVISION );
+-      version_string = chip_ids[ ( revision_register  >> 4 ) & 0xF  ];
+-      if ( !version_string ) {
+-              /* I shouldn't get here because this call was done before.... */
+-              retval = -ENODEV;
++       * Ok, now that we have everything in a
++       * sane state, probe for the interrupt.
++       */
++      retval = smc_probe_irq(dev);
++      if (retval)
+               goto err_out;
+-      }
+-      /* is it using AUI or 10BaseT ? */
+-      if ( dev->if_port == 0 ) {
+-              SMC_SELECT_BANK(1);
+-              configuration_register = inw( ioaddr + CONFIG );
+-              if ( configuration_register & CFG_AUI_SELECT )
+-                      dev->if_port = 2;
+-              else
+-                      dev->if_port = 1;
++      /* Initialize the private structure. */
++      if (dev->priv == NULL) {
++              dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL);
++              if (dev->priv == NULL) {
++                      retval = -ENOMEM;
++                      goto err_out;
++              }
+       }
+-      if_string = interfaces[ dev->if_port - 1 ];
+-      /* now, reset the chip, and put it into a known state */
+-      smc_reset( ioaddr );
++      smc = dev->priv;
++
++      /* set the private data to zero by default */
++      memset(smc, 0, sizeof(struct smc_local));
+       /*
+-       . If dev->irq is 0, then the device has to be banged on to see
+-       . what the IRQ is.
+-       .
+-       . This banging doesn't always detect the IRQ, for unknown reasons.
+-       . a workaround is to reset the chip and try again.
+-       .
+-       . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+-       . be what is requested on the command line.   I don't do that, mostly
+-       . because the card that I have uses a non-standard method of accessing
+-       . the IRQs, and because this _should_ work in most configurations.
+-       .
+-       . Specifying an IRQ is done with the assumption that the user knows
+-       . what (s)he is doing.  No checking is done!!!!
+-       .
+-      */
+-      if ( dev->irq < 2 ) {
+-              int     trials;
++       * Get the interface characteristics.
++       * is it using AUI or 10BaseT ?
++       */
++      switch (dev->if_port) {
++      case IF_PORT_10BASET:
++              smc->port = PORT_TP;
++              break;
+-              trials = 3;
+-              while ( trials-- ) {
+-                      dev->irq = smc_findirq( ioaddr );
+-                      if ( dev->irq )
+-                              break;
+-                      /* kick the card and try again */
+-                      smc_reset( ioaddr );
++      case IF_PORT_AUI:
++              smc->port = PORT_AUI;
++              break;
++
++      default:
++              SMC_SELECT_BANK(1);
++              configuration_register = smc_inw(ioaddr, CONFIG);
++              if (configuration_register & CFG_AUI_SELECT) {
++                      dev->if_port = IF_PORT_AUI;
++                      smc->port = PORT_AUI;
++              } else {
++                      dev->if_port = IF_PORT_10BASET;
++                      smc->port = PORT_TP;
+               }
+-      }
+-      if (dev->irq == 0 ) {
+-              printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
+-              retval = -ENODEV;
+-              goto err_out;
++              break;
+       }
+-      /* now, print out the card info, in a short format.. */
++      /* all interfaces are half-duplex by default */
++      smc->duplex = DUPLEX_HALF;
+-      printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
+-              version_string, revision_register & 0xF, ioaddr, dev->irq,
+-              if_string, memory );
++      /* now, print out the card info, in a short format.. */
++      printk("%s: %s (rev %d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
++              version_string, revision_register & 15, ioaddr, dev->irq,
++              interfaces[smc->port], memory);
+       /*
+        . Print the Ethernet address
+       */
+       printk("ADDR: ");
+       for (i = 0; i < 5; i++)
+-              printk("%2.2x:", dev->dev_addr[i] );
+-      printk("%2.2x \n", dev->dev_addr[5] );
+-
+-
+-      /* Initialize the private structure. */
+-      if (dev->priv == NULL) {
+-              dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL);
+-              if (dev->priv == NULL) {
+-                      retval = -ENOMEM;
+-                      goto err_out;
+-              }
+-      }
+-      /* set the private data to zero by default */
+-      memset(dev->priv, 0, sizeof(struct smc_local));
++              printk("%2.2x:", dev->dev_addr[i]);
++      printk("%2.2x\n", dev->dev_addr[5]);
+       /* Fill in the fields of the device structure with ethernet values. */
+       ether_setup(dev);
+       /* Grab the IRQ */
+-              retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
+-              if (retval) {
++      retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
++      if (retval) {
+               printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+                       dev->irq, retval);
+               kfree(dev->priv);
+               dev->priv = NULL;
+-              goto err_out;
+-              }
++              goto err_out;
++      }
+-      dev->open                       = smc_open;
+-      dev->stop                       = smc_close;
+-      dev->hard_start_xmit            = smc_wait_to_send_packet;
+-      dev->tx_timeout                 = smc_timeout;
+-      dev->watchdog_timeo             = HZ/20;
+-      dev->get_stats                  = smc_query_statistics;
+-      dev->set_multicast_list         = smc_set_multicast_list;
++      dev->open               = smc_open;
++      dev->stop               = smc_close;
++      dev->hard_start_xmit    = smc_wait_to_send_packet;
++      dev->tx_timeout         = smc_timeout;
++      dev->watchdog_timeo     = HZ/20;
++      dev->get_stats          = smc_query_statistics;
++      dev->set_multicast_list = smc_set_multicast_list;
++      dev->do_ioctl           = smc_ioctl;
+       return 0;
+@@ -1022,42 +1384,43 @@
+       return retval;
+ }
+-#if SMC_DEBUG > 2
+-static void print_packet( byte * buf, int length )
++/*
++ * This is responsible for setting the chip appropriately
++ * for the interface type.  This should only be called while
++ * the interface is up and running.
++ */
++static void smc_set_port(struct net_device *dev)
+ {
+-#if 0
+-      int i;
+-      int remainder;
+-      int lines;
+-
+-      printk("Packet of length %d \n", length );
+-      lines = length / 16;
+-      remainder = length % 16;
+-
+-      for ( i = 0; i < lines ; i ++ ) {
+-              int cur;
++      struct smc_local *smc = dev->priv;
++      u_int ioaddr = dev->base_addr;
++      u_int val;
+-              for ( cur = 0; cur < 8; cur ++ ) {
+-                      byte a, b;
++      SMC_SELECT_BANK(1);
++      val = smc_inw(ioaddr, CONFIG);
++      switch (smc->port) {
++      case PORT_TP:
++              val &= ~CFG_AUI_SELECT;
++              break;
+-                      a = *(buf ++ );
+-                      b = *(buf ++ );
+-                      printk("%02x%02x ", a, b );
+-              }
+-              printk("\n");
++      case PORT_AUI:
++              val |= CFG_AUI_SELECT;
++              break;
+       }
+-      for ( i = 0; i < remainder/2 ; i++ ) {
+-              byte a, b;
++      smc_outw(val, ioaddr, CONFIG);
+-              a = *(buf ++ );
+-              b = *(buf ++ );
+-              printk("%02x%02x ", a, b );
++      SMC_SELECT_BANK(0);
++      val = smc_inw(ioaddr, TCR);
++      switch (smc->duplex) {
++      case DUPLEX_HALF:
++              val &= ~TCR_FDSE;
++              break;
++
++      case DUPLEX_FULL:
++              val |= TCR_FDSE;
++              break;
+       }
+-      printk("\n");
+-#endif
++      smc_outw(val, ioaddr, TCR);
+ }
+-#endif
+-
+ /*
+  * Open and Initialize the board
+@@ -1067,48 +1430,141 @@
+  */
+ static int smc_open(struct net_device *dev)
+ {
+-      int     ioaddr = dev->base_addr;
++      struct smc_local *smc = dev->priv;
++      u_int ioaddr = dev->base_addr;
++      int i;
+-      int     i;      /* used to set hw ethernet address */
++      /*
++       * Check that the address is valid.  If its not, refuse
++       * to bring the device up.  The user must specify an
++       * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
++       */
++      if (!is_valid_ether_addr(dev->dev_addr))
++              return -EINVAL;
+       /* clear out all the junk that was put here before... */
+-      memset(dev->priv, 0, sizeof(struct smc_local));
++      smc->saved_skb = NULL;
++      smc->packets_waiting = 0;
+       /* reset the hardware */
+-
+-      smc_reset( ioaddr );
+-      smc_enable( ioaddr );
++      smc_reset(dev);
++      smc_enable(dev);
+       /* Select which interface to use */
+-
+-      SMC_SELECT_BANK( 1 );
+-      if ( dev->if_port == 1 ) {
+-              outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
+-                      ioaddr + CONFIG );
+-      }
+-      else if ( dev->if_port == 2 ) {
+-              outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
+-                      ioaddr + CONFIG );
+-      }
++      smc_set_port(dev);
+       /*
+-              According to Becker, I have to set the hardware address
++              According to Becker, I have to set the hardware address
+               at this point, because the (l)user can set it with an
+               ioctl.  Easily done...
+       */
+-      SMC_SELECT_BANK( 1 );
+-      for ( i = 0; i < 6; i += 2 ) {
++      SMC_SELECT_BANK(1);
++      for (i = 0; i < 6; i += 2) {
+               word    address;
+-              address = dev->dev_addr[ i + 1 ] << 8 ;
+-              address  |= dev->dev_addr[ i ];
+-              outw( address, ioaddr + ADDR0 + i );
++              address = dev->dev_addr[i + 1] << 8 ;
++              address |= dev->dev_addr[i];
++              smc_outw(address, ioaddr, ADDR0 + i);
+       }
+       
+       netif_start_queue(dev);
+       return 0;
+ }
++/*
++ * This is our template.  Fill the rest in at run-time
++ */
++static const struct ethtool_cmd ecmd_template = {
++      supported:      SUPPORTED_10baseT_Half |
++                      SUPPORTED_10baseT_Full |
++                      SUPPORTED_TP |
++                      SUPPORTED_AUI,
++      speed:          SPEED_10,
++      autoneg:        AUTONEG_DISABLE,
++      maxtxpkt:       1,
++      maxrxpkt:       1,
++      transceiver:    XCVR_INTERNAL,
++};
++
++static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
++{
++      struct smc_local *smc = dev->priv;
++      u32 etcmd;
++      int ret = -EINVAL;
++
++      if (cmd != SIOCETHTOOL)
++              return -EOPNOTSUPP;
++
++      if (get_user(etcmd, (u32 *)rq->ifr_data))
++              return -EFAULT;
++
++      switch (etcmd) {
++      case ETHTOOL_GSET: {
++              struct ethtool_cmd ecmd = ecmd_template;
++
++              ecmd.cmd = etcmd;
++              ecmd.port = smc->port;
++              ecmd.duplex = smc->duplex;
++
++              ret = copy_to_user(rq->ifr_data, &ecmd, sizeof(ecmd))
++                              ? -EFAULT : 0;
++              break;
++      }
++
++      case ETHTOOL_SSET: {
++              struct ethtool_cmd ecmd;
++
++              ret = -EPERM;
++              if (!capable(CAP_NET_ADMIN))
++                      break;
++
++              ret = -EFAULT;
++              if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd)))
++                      break;
++
++              /*
++               * Sanity-check the arguments.
++               */
++              ret = -EINVAL;
++              if (ecmd.autoneg != AUTONEG_DISABLE)
++                      break;
++              if (ecmd.speed != SPEED_10)
++                      break;
++              if (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL)
++                      break;
++              if (ecmd.port != PORT_TP && ecmd.port != PORT_AUI)
++                      break;
++
++              smc->port   = ecmd.port;
++              smc->duplex = ecmd.duplex;
++
++              if (netif_running(dev))
++                      smc_set_port(dev);
++
++              ret = 0;
++              break;
++      }
++
++      case ETHTOOL_GDRVINFO: {
++              struct ethtool_drvinfo edrv;
++
++              memset(&edrv, 0, sizeof(edrv));
++
++              edrv.cmd = etcmd;
++              strcpy(edrv.driver, DRV_NAME);
++              strcpy(edrv.version, DRV_VERSION);
++              sprintf(edrv.bus_info, "ISA:%8.8lx:%d",
++                      dev->base_addr, dev->irq);
++
++              ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv))
++                              ? -EFAULT : 0;
++              break;
++      }
++      }
++
++      return ret;
++}
++
+ /*--------------------------------------------------------
+  . Called by the kernel to send a packet out into the void
+  . of the net.  This routine is largely based on
+@@ -1120,12 +1576,10 @@
+ {
+       /* If we get here, some higher level has decided we are broken.
+          There should really be a "kick me" function call instead. */
+-      printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n",
+-              tx_done(dev) ? "IRQ conflict" :
+-              "network cable problem");
++      printk(KERN_WARNING "%s: transmit timed out\n", dev->name);
+       /* "kick" the adaptor */
+-      smc_reset( dev->base_addr );
+-      smc_enable( dev->base_addr );
++      smc_reset(dev);
++      smc_enable(dev);
+       dev->trans_start = jiffies;
+       /* clear anything saved */
+       ((struct smc_local *)dev->priv)->saved_skb = NULL;
+@@ -1145,10 +1599,10 @@
+  .
+  ---------------------------------------------------------------------*/
+-static void smc_interrupt(int irq, void * dev_id,  struct pt_regs * regs)
++static void smc_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+ {
+       struct net_device *dev  = dev_id;
+-      int ioaddr              = dev->base_addr;
++      u_int ioaddr            = dev->base_addr;
+       struct smc_local *lp    = (struct smc_local *)dev->priv;
+       byte    status;
+@@ -1161,45 +1615,45 @@
+-      PRINTK3((CARDNAME": SMC interrupt started \n"));
++      PRINTK3(("%s: SMC interrupt started\n", dev->name));
+-      saved_bank = inw( ioaddr + BANK_SELECT );
++      saved_bank = smc_inw(ioaddr, BANK_SELECT);
+       SMC_SELECT_BANK(2);
+-      saved_pointer = inw( ioaddr + POINTER );
++      saved_pointer = smc_inw(ioaddr, POINTER);
+-      mask = inb( ioaddr + INT_MASK );
++      mask = smc_inb(ioaddr, INT_MASK);
+       /* clear all interrupts */
+-      outb( 0, ioaddr + INT_MASK );
++      SMC_SET_INT(0);
+       /* set a timeout value, so I don't stay here forever */
+       timeout = 4;
+-      PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask ));
++      PRINTK2((KERN_WARNING "%s: MASK IS %x\n", dev->name, mask));
+       do {
+               /* read the status flag, and mask it */
+-              status = inb( ioaddr + INTERRUPT ) & mask;
+-              if (!status )
++              status = smc_inb(ioaddr, INTERRUPT) & mask;
++              if (!status)
+                       break;
+-              PRINTK3((KERN_WARNING CARDNAME
+-                      ": Handling interrupt status %x \n", status ));
++              PRINTK3((KERN_WARNING "%s: handling interrupt status %x\n",
++                      dev->name, status));
+               if (status & IM_RCV_INT) {
+                       /* Got a packet(s). */
+-                      PRINTK2((KERN_WARNING CARDNAME
+-                              ": Receive Interrupt\n"));
++                      PRINTK2((KERN_WARNING "%s: receive interrupt\n",
++                              dev->name));
+                       smc_rcv(dev);
+-              } else if (status & IM_TX_INT ) {
+-                      PRINTK2((KERN_WARNING CARDNAME
+-                              ": TX ERROR handled\n"));
++              } else if (status & IM_TX_INT) {
++                      PRINTK2((KERN_WARNING "%s: TX ERROR handled\n",
++                              dev->name));
+                       smc_tx(dev);
+-                      outb(IM_TX_INT, ioaddr + INTERRUPT );
+-              } else if (status & IM_TX_EMPTY_INT ) {
++                      smc_outb(IM_TX_INT, ioaddr, INTERRUPT);
++              } else if (status & IM_TX_EMPTY_INT) {
+                       /* update stats */
+-                      SMC_SELECT_BANK( 0 );
+-                      card_stats = inw( ioaddr + COUNTER );
++                      SMC_SELECT_BANK(0);
++                      card_stats = smc_inw(ioaddr, COUNTER);
+                       /* single collisions */
+                       lp->stats.collisions += card_stats & 0xF;
+                       card_stats >>= 4;
+@@ -1208,60 +1662,63 @@
+                       /* these are for when linux supports these statistics */
+-                      SMC_SELECT_BANK( 2 );
+-                      PRINTK2((KERN_WARNING CARDNAME
+-                              ": TX_BUFFER_EMPTY handled\n"));
+-                      outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
++                      SMC_SELECT_BANK(2);
++                      PRINTK2((KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n",
++                              dev->name));
++                      smc_outb(IM_TX_EMPTY_INT, ioaddr, INTERRUPT);
+                       mask &= ~IM_TX_EMPTY_INT;
+                       lp->stats.tx_packets += lp->packets_waiting;
+                       lp->packets_waiting = 0;
+-              } else if (status & IM_ALLOC_INT ) {
+-                      PRINTK2((KERN_DEBUG CARDNAME
+-                              ": Allocation interrupt \n"));
++              } else if (status & IM_ALLOC_INT) {
++                      PRINTK2((KERN_DEBUG "%s: Allocation interrupt\n",
++                              dev->name));
+                       /* clear this interrupt so it doesn't happen again */
+                       mask &= ~IM_ALLOC_INT;
+-                      smc_hardware_send_packet( dev );
++                      smc_hardware_send_packet(dev);
+                       /* enable xmit interrupts based on this */
+-                      mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
++                      mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
+                       /* and let the card send more packets to me */
+                       netif_wake_queue(dev);
+                       
+-                      PRINTK2((CARDNAME": Handoff done successfully.\n"));
+-              } else if (status & IM_RX_OVRN_INT ) {
++                      PRINTK2(("%s: Handoff done successfully.\n",
++                              dev->name));
++              } else if (status & IM_RX_OVRN_INT) {
+                       lp->stats.rx_errors++;
+                       lp->stats.rx_fifo_errors++;
+-                      outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
+-              } else if (status & IM_EPH_INT ) {
+-                      PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n"));
+-              } else if (status & IM_ERCV_INT ) {
+-                      PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n"));
+-                      outb( IM_ERCV_INT, ioaddr + INTERRUPT );
++                      smc_outb(IM_RX_OVRN_INT, ioaddr, INTERRUPT);
++              } else if (status & IM_EPH_INT) {
++                      PRINTK(("%s: UNSUPPORTED: EPH INTERRUPT\n",
++                              dev->name));
++              } else if (status & IM_ERCV_INT) {
++                      PRINTK(("%s: UNSUPPORTED: ERCV INTERRUPT\n",
++                              dev->name));
++                      smc_outb(IM_ERCV_INT, ioaddr, INTERRUPT);
+               }
+-      } while ( timeout -- );
++      } while (timeout --);
+       /* restore state register */
+-      SMC_SELECT_BANK( 2 );
+-      outb( mask, ioaddr + INT_MASK );
++      SMC_SELECT_BANK(2);
++      SMC_SET_INT(mask);
+-      PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask ));
+-      outw( saved_pointer, ioaddr + POINTER );
++      PRINTK3((KERN_WARNING "%s: MASK is now %x\n", dev->name, mask));
++      smc_outw(saved_pointer, ioaddr, POINTER);
+-      SMC_SELECT_BANK( saved_bank );
++      SMC_SELECT_BANK(saved_bank);
+-      PRINTK3((CARDNAME ": Interrupt done\n"));
++      PRINTK3(("%s: Interrupt done\n", dev->name));
+       return;
+ }
+ /*-------------------------------------------------------------
+  .
+- . smc_rcv -  receive a packet from the card
++ . smc_rcv - receive a packet from the card
+  .
+- . There is ( at least ) a packet waiting to be read from
++ . There is (at least) a packet waiting to be read from
+  . chip-memory.
+  .
+  . o Read the status
+@@ -1272,55 +1729,57 @@
+ static void smc_rcv(struct net_device *dev)
+ {
+       struct smc_local *lp = (struct smc_local *)dev->priv;
+-      int     ioaddr = dev->base_addr;
++      u_int   ioaddr = dev->base_addr;
+       int     packet_number;
+       word    status;
+       word    packet_length;
+       /* assume bank 2 */
+-      packet_number = inw( ioaddr + FIFO_PORTS );
++      packet_number = smc_inw(ioaddr, FIFO_PORTS);
+-      if ( packet_number & FP_RXEMPTY ) {
++      if (packet_number & FP_RXEMPTY) {
+               /* we got called , but nothing was on the FIFO */
+-              PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n"));
++              PRINTK(("%s: WARNING: smc_rcv with nothing on FIFO.\n",
++                      dev->name));
+               /* don't need to restore anything */
+               return;
+       }
+       /*  start reading from the start of the packet */
+-      outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
++      smc_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr, POINTER);
+       /* First two words are status and packet_length */
+-      status          = inw( ioaddr + DATA_1 );
+-      packet_length   = inw( ioaddr + DATA_1 );
++      status          = smc_inw(ioaddr, DATA_1);
++      packet_length   = smc_inw(ioaddr, DATA_1);
+       packet_length &= 0x07ff;  /* mask off top bits */
+-      PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
++      PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length));
+       /*
+        . the packet length contains 3 extra words :
+        . status, length, and an extra word with an odd byte .
+       */
+       packet_length -= 6;
+-      if ( !(status & RS_ERRORS ) ){
++      if (!(status & RS_ERRORS)){
+               /* do stuff to make a new packet */
+               struct sk_buff  * skb;
+               byte            * data;
+               /* read one extra byte */
+-              if ( status & RS_ODDFRAME )
++              if (status & RS_ODDFRAME)
+                       packet_length++;
+               /* set multicast stats */
+-              if ( status & RS_MULTICAST )
++              if (status & RS_MULTICAST)
+                       lp->stats.multicast++;
+-              skb = dev_alloc_skb( packet_length + 5);
++              skb = dev_alloc_skb(packet_length + 5);
+-              if ( skb == NULL ) {
+-                      printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");
++              if (skb == NULL) {
++                      printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
++                              dev->name);
+                       lp->stats.rx_dropped++;
+                       goto done;
+               }
+@@ -1330,36 +1789,15 @@
+                ! in the worse case
+               */
+-              skb_reserve( skb, 2 );   /* 16 bit alignment */
++              skb_reserve(skb, 2);   /* 16 bit alignment */
+               skb->dev = dev;
+-              data = skb_put( skb, packet_length);
++              data = skb_put(skb, packet_length);
+-#ifdef USE_32_BIT
+-              /* QUESTION:  Like in the TX routine, do I want
+-                 to send the DWORDs or the bytes first, or some
+-                 mixture.  A mixture might improve already slow PIO
+-                 performance  */
+-              PRINTK3((" Reading %d dwords (and %d bytes) \n",
+-                      packet_length >> 2, packet_length & 3 ));
+-              insl(ioaddr + DATA_1 , data, packet_length >> 2 );
+-              /* read the left over bytes */
+-              insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
+-                      packet_length & 0x3  );
+-#else
+-              PRINTK3((" Reading %d words and %d byte(s) \n",
+-                      (packet_length >> 1 ), packet_length & 1 ));
+-              insw(ioaddr + DATA_1 , data, packet_length >> 1);
+-              if ( packet_length & 1 ) {
+-                      data += packet_length & ~1;
+-                      *(data++) = inb( ioaddr + DATA_1 );
+-              }
+-#endif
+-#if   SMC_DEBUG > 2
+-                      print_packet( data, packet_length );
+-#endif
++              smc_ins(ioaddr, DATA_1, data, packet_length);
++              print_packet(data, packet_length);
+-              skb->protocol = eth_type_trans(skb, dev );
++              skb->protocol = eth_type_trans(skb, dev);
+               netif_rx(skb);
+               dev->last_rx = jiffies;
+               lp->stats.rx_packets++;
+@@ -1368,15 +1806,17 @@
+               /* error ... */
+               lp->stats.rx_errors++;
+-              if ( status & RS_ALGNERR )  lp->stats.rx_frame_errors++;
+-              if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
++              if (status & RS_ALGNERR)
++                      lp->stats.rx_frame_errors++;
++              if (status & (RS_TOOSHORT | RS_TOOLONG))
+                       lp->stats.rx_length_errors++;
+-              if ( status & RS_BADCRC)        lp->stats.rx_crc_errors++;
++              if (status & RS_BADCRC)
++                      lp->stats.rx_crc_errors++;
+       }
+ done:
+       /*  error or good, tell the card to get rid of this packet */
+-      outw( MC_RELEASE, ioaddr + MMU_CMD );
++      smc_outw(MC_RELEASE, ioaddr, MMU_CMD);
+ }
+@@ -1389,62 +1829,64 @@
+  . Algorithm:
+  .    Save pointer and packet no
+  .    Get the packet no from the top of the queue
+- .    check if it's valid ( if not, is this an error??? )
++ .    check if it's valid (if not, is this an error???)
+  .    read the status word
+  .    record the error
+- .    ( resend?  Not really, since we don't want old packets around )
++ .    (resend?  Not really, since we don't want old packets around)
+  .    Restore saved values
+  ************************************************************************/
+-static void smc_tx( struct net_device * dev )
++static void smc_tx(struct net_device * dev)
+ {
+-      int     ioaddr = dev->base_addr;
++      u_int ioaddr = dev->base_addr;
+       struct smc_local *lp = (struct smc_local *)dev->priv;
+       byte saved_packet;
+       byte packet_no;
+       word tx_status;
+-      /* assume bank 2  */
++      /* assume bank 2 */
+-      saved_packet = inb( ioaddr + PNR_ARR );
+-      packet_no = inw( ioaddr + FIFO_PORTS );
++      saved_packet = smc_inb(ioaddr, PNR_ARR);
++      packet_no = smc_inw(ioaddr, FIFO_PORTS);
+       packet_no &= 0x7F;
+       /* select this as the packet to read from */
+-      outb( packet_no, ioaddr + PNR_ARR );
++      smc_outb(packet_no, ioaddr, PNR_ARR);
+       /* read the first word from this packet */
+-      outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
++      smc_outw(PTR_AUTOINC | PTR_READ, ioaddr, POINTER);
+-      tx_status = inw( ioaddr + DATA_1 );
+-      PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status ));
++      tx_status = smc_inw(ioaddr, DATA_1);
++      PRINTK3(("%s: TX DONE STATUS: %4x\n", dev->name, tx_status));
+       lp->stats.tx_errors++;
+-      if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++;
+-      if ( tx_status & TS_LATCOL  ) {
+-              printk(KERN_DEBUG CARDNAME
+-                      ": Late collision occurred on last xmit.\n");
++      if (tx_status & TS_LOSTCAR)
++              lp->stats.tx_carrier_errors++;
++      if (tx_status & TS_LATCOL) {
++              printk(KERN_DEBUG "%s: Late collision occurred on "
++                      "last xmit.\n", dev->name);
+               lp->stats.tx_window_errors++;
+       }
+ #if 0
+-              if ( tx_status & TS_16COL ) { ... }
++              if (tx_status & TS_16COL) { ... }
+ #endif
+-      if ( tx_status & TS_SUCCESS ) {
+-              printk(CARDNAME": Successful packet caused interrupt \n");
++      if (tx_status & TS_SUCCESS) {
++              printk("%s: Successful packet caused interrupt\n",
++                      dev->name);
+       }
+       /* re-enable transmit */
+-      SMC_SELECT_BANK( 0 );
+-      outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
++      SMC_SELECT_BANK(0);
++      smc_outw(smc_inw(ioaddr, TCR) | TCR_ENABLE, ioaddr, TCR);
+       /* kill the packet */
+-      SMC_SELECT_BANK( 2 );
+-      outw( MC_FREEPKT, ioaddr + MMU_CMD );
++      SMC_SELECT_BANK(2);
++      smc_outw(MC_FREEPKT, ioaddr, MMU_CMD);
+       /* one less packet waiting for me */
+       lp->packets_waiting--;
+-      outb( saved_packet, ioaddr + PNR_ARR );
++      smc_outb(saved_packet, ioaddr, PNR_ARR);
+       return;
+ }
+@@ -1460,7 +1902,7 @@
+ {
+       netif_stop_queue(dev);
+       /* clear everything */
+-      smc_shutdown( dev->base_addr );
++      smc_shutdown(dev);
+       /* Update the statistics here. */
+       return 0;
+@@ -1481,16 +1923,16 @@
+  .
+  . This routine will, depending on the values passed to it,
+  . either make it accept multicast packets, go into
+- . promiscuous mode ( for TCPDUMP and cousins ) or accept
++ . promiscuous mode (for TCPDUMP and cousins) or accept
+  . a select set of multicast packets
+ */
+ static void smc_set_multicast_list(struct net_device *dev)
+ {
+-      short ioaddr = dev->base_addr;
++      u_int ioaddr = dev->base_addr;
+       SMC_SELECT_BANK(0);
+-      if ( dev->flags & IFF_PROMISC )
+-              outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
++      if (dev->flags & IFF_PROMISC)
++              smc_outw(smc_inw(ioaddr, RCR) | RCR_PROMISC, ioaddr, RCR);
+ /* BUG?  I never disable promiscuous mode if multicasting was turned on.
+    Now, I turn off promiscuous mode, but I don't do anything to multicasting
+@@ -1502,34 +1944,34 @@
+          checked before the table is
+       */
+       else if (dev->flags & IFF_ALLMULTI)
+-              outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
++              smc_outw(smc_inw(ioaddr, RCR) | RCR_ALMUL, ioaddr, RCR);
+       /* We just get all multicast packets even if we only want them
+        . from one source.  This will be changed at some future
+        . point. */
+-      else if (dev->mc_count )  {
++      else if (dev->mc_count) {
+               /* support hardware multicasting */
+               /* be sure I get rid of flags I might have set */
+-              outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+-                      ioaddr + RCR );
++              smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL),
++                      ioaddr, RCR);
+               /* NOTE: this has to set the bank, so make sure it is the
+                  last thing called.  The bank is set to zero at the top */
+-              smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list );
++              smc_setmulticast(dev, dev->mc_count, dev->mc_list);
+       }
+-      else  {
+-              outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+-                      ioaddr + RCR );
++      else {
++              smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL),
++                      ioaddr, RCR);
+               /*
+                 since I'm disabling all multicast entirely, I need to
+                 clear the multicast list
+               */
+-              SMC_SELECT_BANK( 3 );
+-              outw( 0, ioaddr + MULTICAST1 );
+-              outw( 0, ioaddr + MULTICAST2 );
+-              outw( 0, ioaddr + MULTICAST3 );
+-              outw( 0, ioaddr + MULTICAST4 );
++              SMC_SELECT_BANK(3);
++              smc_outw(0, ioaddr, MULTICAST1);
++              smc_outw(0, ioaddr, MULTICAST2);
++              smc_outw(0, ioaddr, MULTICAST3);
++              smc_outw(0, ioaddr, MULTICAST4);
+       }
+ }
+@@ -1550,21 +1992,26 @@
+ int init_module(void)
+ {
+-      int result;
+-
+       if (io == 0)
+-              printk(KERN_WARNING
+-              CARDNAME": You shouldn't use auto-probing with insmod!\n" );
++              printk(KERN_WARNING CARDNAME
++                      ": You shouldn't use auto-probing with insmod!\n");
++
++      /*
++       * Note: dev->if_port has changed to be 2.4 compliant.
++       * We keep the ifport insmod parameter the same though.
++       */
++      switch (ifport) {
++      case 1: devSMC9194.if_port = IF_PORT_10BASET;   break;
++      case 2: devSMC9194.if_port = IF_PORT_AUI;       break;
++      default: devSMC9194.if_port = 0;                break;
++      }
+       /* copy the parameters from insmod into the device structure */
+       devSMC9194.base_addr = io;
+       devSMC9194.irq       = irq;
+-      devSMC9194.if_port      = ifport;
+-      devSMC9194.init         = smc_init;
+-      if ((result = register_netdev(&devSMC9194)) != 0)
+-              return result;
++      devSMC9194.init      = smc_init;
+-      return 0;
++      return register_netdev(&devSMC9194);
+ }
+ void cleanup_module(void)
+--- linux-2.4.27/drivers/net/smc9194.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/net/smc9194.h
+@@ -63,10 +63,11 @@
+ #define       TCR             0       /* transmit control register */
+ #define TCR_ENABLE    0x0001  /* if this is 1, we can transmit */ 
++#define       TCR_PAD_ENABLE  0x0080  /* pads short packets to 64 bytes */
++#define       TCR_MON_CNS     0x0400  /* monitors the carrier status */
+ #define TCR_FDUPLX            0x0800  /* receive packets sent out */
+ #define TCR_STP_SQET  0x1000  /* stop transmitting if Signal quality error */
+-#define       TCR_MON_CNS     0x0400  /* monitors the carrier status */
+-#define       TCR_PAD_ENABLE  0x0080  /* pads short packets to 64 bytes */
++#define TCR_FDSE      0x8000  /* full duplex, switched ethernet */
+ #define       TCR_CLEAR       0       /* do NOTHING */
+ /* the normal settings for the TCR register : */ 
+@@ -107,7 +108,10 @@
+ #define       CTL_CR_ENABLE           0x40
+ #define       CTL_TE_ENABLE           0x0020
+ #define CTL_AUTO_RELEASE      0x0800
+-#define       CTL_EPROM_ACCESS        0x0003 /* high if Eprom is being read */
++#define CTL_EPROM_SELECT      0x0004
++#define CTL_EPROM_RELOAD      0x0002
++#define CTL_EPROM_STORE               0x0001
++#define       CTL_EPROM_ACCESS        (CTL_EPROM_RELOAD | CTL_EPROM_STORE) /* high if Eprom is being read */
+ /* BANK 2 */
+ #define MMU_CMD               0
+@@ -130,7 +134,6 @@
+ #define PTR_READ      0x2000
+ #define       PTR_RCV         0x8000
+ #define       PTR_AUTOINC     0x4000
+-#define PTR_AUTO_INC  0x0040
+ #define       DATA_1          8
+ #define       DATA_2          10
+@@ -162,17 +165,6 @@
+ #define CHIP_9195     5
+ #define CHIP_91100    7
+-static const char * chip_ids[ 15 ] =  { 
+-      NULL, NULL, NULL, 
+-      /* 3 */ "SMC91C90/91C92",
+-      /* 4 */ "SMC91C94",
+-      /* 5 */ "SMC91C95",
+-      NULL,
+-      /* 7 */ "SMC91C100", 
+-      /* 8 */ "SMC91C100FD", 
+-      NULL, NULL, NULL, 
+-      NULL, NULL, NULL};  
+-
+ /* 
+  . Transmit status bits 
+ */
+@@ -192,40 +184,20 @@
+ #define RS_MULTICAST  0x0001
+ #define RS_ERRORS     (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) 
+-static const char * interfaces[ 2 ] = { "TP", "AUI" };
+-
+-/*-------------------------------------------------------------------------
+- .  I define some macros to make it easier to do somewhat common
+- . or slightly complicated, repeated tasks. 
+- --------------------------------------------------------------------------*/
+-
+-/* select a register bank, 0 to 3  */
+-
+-#define SMC_SELECT_BANK(x)  { outw( x, ioaddr + BANK_SELECT ); } 
+-
+-/* define a small delay for the reset */
+-#define SMC_DELAY() { inw( ioaddr + RCR );\
+-                      inw( ioaddr + RCR );\
+-                      inw( ioaddr + RCR );  }
+-
+-/* this enables an interrupt in the interrupt mask register */
+-#define SMC_ENABLE_INT(x) {\
+-              unsigned char mask;\
+-              SMC_SELECT_BANK(2);\
+-              mask = inb( ioaddr + INT_MASK );\
+-              mask |= (x);\
+-              outb( mask, ioaddr + INT_MASK ); \
+-}
+-
+-/* this disables an interrupt from the interrupt mask register */
++/*
++ * SMC91C96 ethernet config and status registers.
++ * These are in the "attribute" space.
++ */
++#define ECOR          0x8000
++#define ECOR_RESET    0x80
++#define ECOR_LEVEL_IRQ        0x40
++#define ECOR_WR_ATTRIB        0x04
++#define ECOR_ENABLE   0x01
+-#define SMC_DISABLE_INT(x) {\
+-              unsigned char mask;\
+-              SMC_SELECT_BANK(2);\
+-              mask = inb( ioaddr + INT_MASK );\
+-              mask &= ~(x);\
+-              outb( mask, ioaddr + INT_MASK ); \
+-}
++#define ECSR          0x8002
++#define ECSR_IOIS8    0x20
++#define ECSR_PWRDWN   0x04
++#define ECSR_INT      0x02
+ /*----------------------------------------------------------------------
+  . Define the interrupts that I want to receive from the card
+@@ -237,5 +209,36 @@
+  --------------------------------------------------------------------------*/
+ #define SMC_INTERRUPT_MASK   (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) 
++/* store this information for the driver.. */
++struct smc_local {
++      /*
++         these are things that the kernel wants me to keep, so users
++         can find out semi-useless statistics of how well the card is
++         performing
++      */
++      struct net_device_stats stats;
++
++      /*
++         If I have to wait until memory is available to send
++         a packet, I will store the skbuff here, until I get the
++         desired memory.  Then, I'll send it out and free it.
++      */
++      struct sk_buff * saved_skb;
++
++      /*
++       . This keeps track of how many packets that I have
++       . sent out.  When an TX_EMPTY interrupt comes, I know
++       . that all of these have been sent.
++      */
++      int     packets_waiting;
++
++      /*
++       . Interface status.  These correspond to the parameters
++       . in the ethtool_cmd structure.
++      */
++      u8      duplex;
++      u8      port;
++};
++
+ #endif  /* _SMC_9194_H_ */
+--- linux-2.4.27/drivers/parport/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/parport/Config.in
+@@ -27,7 +27,8 @@
+       dep_tristate '    Support for PCMCIA management for PC-style ports' CONFIG_PARPORT_PC_PCMCIA $CONFIG_PCMCIA $CONFIG_PARPORT_PC $CONFIG_HOTPLUG
+    fi
+    if [ "$CONFIG_ARM" = "y" ]; then
+-      dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
++      dep_tristate '  Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT $CONFIG_ARCH_ARC
++      dep_tristate '  Accelent SA1110 IDP' CONFIG_PARPORT_IDP $CONFIG_PARPORT $CONFIG_SA1100_ACCELENT
+    fi
+    if [ "$CONFIG_AMIGA" = "y" ]; then
+       dep_tristate '  Amiga builtin port' CONFIG_PARPORT_AMIGA $CONFIG_PARPORT
+--- linux-2.4.27/drivers/parport/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/parport/Makefile
+@@ -28,6 +28,7 @@
+ obj-$(CONFIG_PARPORT_ATARI)   += parport_atari.o
+ obj-$(CONFIG_PARPORT_SUNBPP)  += parport_sunbpp.o
+ obj-$(CONFIG_PARPORT_GSC)     += parport_gsc.o
++obj-$(CONFIG_PARPORT_IDP)     += parport_idp.o
+ obj-$(CONFIG_PARPORT_IP22)    += parport_ip22.o
+ include $(TOPDIR)/Rules.make
+--- linux-2.4.27/drivers/parport/init.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/parport/init.c
+@@ -164,6 +164,9 @@
+ #ifdef CONFIG_PARPORT_SUNBPP
+       parport_sunbpp_init();
+ #endif
++#ifdef CONFIG_PARPORT_IDP
++      parport_idp_init();
++#endif
+       return 0;
+ }
+--- /dev/null
++++ linux-2.4.27/drivers/parport/parport_idp.c
+@@ -0,0 +1,247 @@
++/* Low-level polled-mode parallel port routines for the Accelent IDP
++ *
++ * Author: Rich Dulabahn <rich@accelent.com>
++ *
++ * Inspiration taken from parport_amiga.c and parport_atari.c.
++ *
++ * To use, under menuconfig:
++ *   1)  Turn on <*> Accelent IDP under Parallel port setup
++ *   2)  Turn on <*> Parallel printer support under Character devices
++ *
++ * This will give you parport0 configured as /dev/lp0
++ *
++ * To make the correct /dev/lp* entries, enter /dev and type this:
++ *
++ * mknod lp0 c 6 0
++ * mknod lp1 c 6 1
++ * mknod lp2 c 6 2
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/parport.h>
++#include <asm/hardware.h>
++
++/*
++ * Parallel data port is port H, data
++ * Parallel data direction is port H, direction
++ * Control port is port I, data, lowest 4 bits
++ * Status port is port G, data, upper 5 bits
++ */
++
++#define INPUTPOWERHANDLER             0
++/* masks */
++#define CONTROL_MASK                  0x0f
++#define STATUS_MASK                   0xf8
++
++#undef DEBUG
++
++#ifdef DEBUG
++#define DPRINTK  printk
++#else
++#define DPRINTK(stuff...)
++#endif
++
++static struct parport *this_port = NULL;
++
++static unsigned char
++parport_idp_read_data(struct parport *p)
++{
++      unsigned char c;
++
++      c = IDP_FPGA_PORTH_DATA;
++      DPRINTK("read_data:0x%x\n",c);
++      return c;
++}
++
++static void
++parport_idp_write_data(struct parport *p, unsigned char data)
++{
++      IDP_FPGA_PORTH_DATA = data;
++      DPRINTK("write_data:0x%x\n",data);
++}
++
++static unsigned char
++parport_idp_read_control(struct parport *p)
++{
++      unsigned char c;
++
++      c = IDP_FPGA_PORTI_DATA & CONTROL_MASK;
++      DPRINTK("read_control:0x%x\n",c);
++      return c;
++}
++
++static void
++parport_idp_write_control(struct parport *p, unsigned char control)
++{
++      unsigned int temp;
++
++      temp = IDP_FPGA_PORTH_DATA;
++      temp &= ~CONTROL_MASK;
++      IDP_FPGA_PORTI_DATA = (temp | (control & CONTROL_MASK));
++DPRINTK("write_control:0x%x\n",control);
++}
++
++static unsigned char
++parport_idp_frob_control(struct parport *p, unsigned char mask,
++                         unsigned char val)
++{
++      unsigned char c;
++
++/* From the parport-lowlevel.txt file...*/
++/* This is equivalent to reading from the control register, masking out
++the bits in mask, exclusive-or'ing with the bits in val, and writing
++the result to the control register. */
++
++/* Easy enough, right? */
++
++      c = parport_idp_read_control(p);
++      parport_idp_write_control(p, (c & ~mask) ^ val);
++      DPRINTK("frob_control:0x%x\n",c);
++      return c;
++}
++
++static unsigned char
++parport_idp_read_status(struct parport *p)
++{
++      unsigned char c;
++
++      c = IDP_FPGA_PORTG_DATA & STATUS_MASK;
++      c ^= 0x80;  /* toggle S7 bit, active low */
++      DPRINTK("read_status:0x%x\n",c);
++      return c;
++}
++
++static void
++parport_idp_init_state(struct pardevice *d, struct parport_state *s)
++{
++}
++
++static void
++parport_idp_save_state(struct parport *p, struct parport_state *s)
++{
++}
++
++static void
++parport_idp_restore_state(struct parport *p, struct parport_state *s)
++{
++}
++
++static void
++parport_idp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++}
++
++static void
++parport_idp_enable_irq(struct parport *p)
++{
++}
++
++static void
++parport_idp_disable_irq(struct parport *p)
++{
++}
++
++static void
++parport_idp_data_forward(struct parport *p)
++{
++      IDP_FPGA_PORTH_DIR = 0x00; /* 0 sets to output */
++      DPRINTK("data_forward:0x%x\n",0);
++}
++
++static void
++parport_idp_data_reverse(struct parport *p)
++{
++      IDP_FPGA_PORTH_DIR = 0xff; /* and 1 sets to input */
++      DPRINTK("data_reverse:0x%x\n",0xff);
++}
++
++static void
++parport_idp_inc_use_count(void)
++{
++      MOD_INC_USE_COUNT;
++}
++
++static void
++parport_idp_dec_use_count(void)
++{
++      MOD_DEC_USE_COUNT;
++}
++
++static struct parport_operations parport_idp_ops = {
++      parport_idp_write_data,
++      parport_idp_read_data,
++
++      parport_idp_write_control,
++      parport_idp_read_control,
++      parport_idp_frob_control,
++
++      parport_idp_read_status,
++
++      parport_idp_enable_irq,
++      parport_idp_disable_irq,
++
++      parport_idp_data_forward,
++      parport_idp_data_reverse,
++
++      parport_idp_init_state,
++      parport_idp_save_state,
++      parport_idp_restore_state,
++
++      parport_idp_inc_use_count,
++      parport_idp_dec_use_count,
++
++      parport_ieee1284_epp_write_data,
++      parport_ieee1284_epp_read_data,
++      parport_ieee1284_epp_write_addr,
++      parport_ieee1284_epp_read_addr,
++
++      parport_ieee1284_ecp_write_data,
++      parport_ieee1284_ecp_read_data,
++      parport_ieee1284_ecp_write_addr,
++
++      parport_ieee1284_write_compat,
++      parport_ieee1284_read_nibble,
++      parport_ieee1284_read_byte,
++};
++
++
++int __init
++parport_idp_init(void)
++{
++      struct parport *p;
++
++      p = parport_register_port((unsigned long)0,PARPORT_IRQ_NONE,PARPORT_DMA_NONE,&parport_idp_ops);
++
++      if (!p) return 0;  /* return 0 on failure */
++
++      this_port=p;
++      printk("%s: Accelent IDP parallel port registered.\n", p->name);
++      parport_proc_register(p);
++      parport_announce_port(p);
++
++      return 1;
++}
++
++#ifdef MODULE
++
++MODULE_AUTHOR("Rich Dulabahn");
++MODULE_DESCRIPTION("Parport Driver for Accelent IDP");
++MODULE_SUPPORTED_DEVICE("Accelent IDP builtin Parallel Port");
++MODULE_LICENSE("GPL");
++
++int
++init_module(void)
++{
++      return parport_idp_init() ? 0 : -ENODEV;
++}
++
++void
++cleanup_module(void)
++{
++      parport_proc_unregister(this_port);
++      parport_unregister_port(this_port);
++}
++#endif
++
+--- linux-2.4.27/drivers/pci/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/pci/Makefile
+@@ -13,7 +13,7 @@
+ export-objs := pci.o
+-obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o
++obj-$(CONFIG_PCI) += pci.o quirks.o compat.o names.o bridge.o 
+ obj-$(CONFIG_PROC_FS) += proc.o
+ ifndef CONFIG_SPARC64
+--- /dev/null
++++ linux-2.4.27/drivers/pci/bridge.c
+@@ -0,0 +1,149 @@
++
++/*
++ *    Copyright (c) 2001 Red Hat, Inc. All rights reserved.
++ *
++ *    This software may be freely redistributed under the terms
++ *    of the GNU public license.
++ * 
++ *    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.
++ *
++ *    Author: Arjan van de Ven <arjanv@redhat.com>
++ *
++ */
++
++
++/*
++ * Generic PCI driver for PCI bridges for powermanagement purposes
++ *
++ */
++
++#include <linux/config.h> 
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++
++static struct pci_device_id bridge_pci_table[] __devinitdata = {
++        {/* handle all PCI bridges */
++      class:          ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
++      class_mask:     ~0,
++      vendor:         PCI_ANY_ID,
++      device:         PCI_ANY_ID,
++      subvendor:      PCI_ANY_ID,
++      subdevice:      PCI_ANY_ID,
++      },
++        {0,},
++};
++
++static int bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id);
++static int pci_bridge_save_state_bus(struct pci_bus *bus, int force);
++int pci_generic_resume_compare(struct pci_dev *pdev);
++
++int pci_bridge_force_restore = 0;
++
++
++
++
++static int __init bridge_setup(char *str)
++{
++      if (!strcmp(str,"force"))
++              pci_bridge_force_restore = 1;
++      else if (!strcmp(str,"noforce"))
++              pci_bridge_force_restore = 0;
++      return 0;
++}
++
++__setup("resume=",bridge_setup);
++
++
++static int pci_bridge_save_state_bus(struct pci_bus *bus, int force)
++{
++      struct list_head *list;
++      int error = 0;
++
++      list_for_each(list, &bus->children) {
++              error = pci_bridge_save_state_bus(pci_bus_b(list),force);
++              if (error) return error;
++      }
++      list_for_each(list, &bus->devices) {
++              pci_generic_suspend_save(pci_dev_b(list),0);
++      }
++      return 0;
++}
++
++
++static int pci_bridge_restore_state_bus(struct pci_bus *bus, int force)
++{
++      struct list_head *list;
++      int error = 0;
++      static int printed_warning=0;
++
++      list_for_each(list, &bus->children) {
++              error = pci_bridge_restore_state_bus(pci_bus_b(list),force);
++              if (error) return error;
++      }
++      list_for_each(list, &bus->devices) {
++              if (force)
++                      pci_generic_resume_restore(pci_dev_b(list));
++              else {
++                      error = pci_generic_resume_compare(pci_dev_b(list));
++                      if (error && !printed_warning++) { 
++                              printk(KERN_WARNING "resume warning: bios doesn't restore PCI state properly\n");
++                              printk(KERN_WARNING "resume warning: if resume failed, try booting with resume=force\n");
++                      }
++                      if (error)
++                              return error;
++              }
++      }
++      return 0;
++}
++
++static int bridge_suspend(struct pci_dev *dev, u32 force)
++{
++      pci_generic_suspend_save(dev,force);
++      if (dev->subordinate)
++              pci_bridge_save_state_bus(dev->subordinate,force);
++      return 0;
++}
++
++static int bridge_resume(struct pci_dev *dev)
++{
++
++      pci_generic_resume_restore(dev);
++      if (dev->subordinate)
++              pci_bridge_restore_state_bus(dev->subordinate,pci_bridge_force_restore);
++      return 0;
++}
++
++
++MODULE_DEVICE_TABLE(pci, bridge_pci_table);
++static struct pci_driver bridge_ops = {
++        name:           "PCI Bridge",   
++        id_table:       bridge_pci_table,
++        probe:          bridge_probe,    
++        suspend:      bridge_suspend,
++        resume:       bridge_resume
++};
++
++static int __devinit bridge_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++      return 0;
++}
++
++static int __init bridge_init(void) 
++{
++        pci_register_driver(&bridge_ops);
++        return 0;
++}
++
++static void __exit bridge_exit(void)
++{
++        pci_unregister_driver(&bridge_ops);
++} 
++
++
++module_init(bridge_init)
++module_exit(bridge_exit)
++
+--- linux-2.4.27/drivers/pci/pci.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pci/pci.c
+@@ -359,6 +359,48 @@
+       return 0;
+ }
++int 
++pci_compare_state(struct pci_dev *dev, u32 *buffer)
++{
++      int i;
++      unsigned int temp;
++
++      if (buffer) {
++              for (i = 0; i < 16; i++) {
++                      pci_read_config_dword(dev,i*4,&temp);
++                      if (temp!=buffer[i])
++                              return 1;
++              }
++      }
++      return 0;
++}
++
++int pci_generic_suspend_save(struct pci_dev *pdev, u32 state)
++{
++      if (pdev)
++              pci_save_state(pdev,pdev->saved_state);
++      return 0;
++}
++
++int pci_generic_resume_restore(struct pci_dev *pdev)
++{
++      if (pdev)
++              pci_restore_state(pdev,pdev->saved_state);
++      return 0;               
++}
++
++int pci_generic_resume_compare(struct pci_dev *pdev)
++{
++      int retval=0;
++      if (pdev)
++              retval = pci_compare_state(pdev,pdev->saved_state);
++      return retval;          
++}
++
++EXPORT_SYMBOL(pci_generic_suspend_save);
++EXPORT_SYMBOL(pci_generic_resume_restore);
++EXPORT_SYMBOL(pci_generic_resume_compare);
++
+ /**
+  * pci_enable_device_bars - Initialize some of a device for use
+  * @dev: PCI device to be initialized
+--- linux-2.4.27/drivers/pci/setup-bus.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pci/setup-bus.c
+@@ -12,6 +12,8 @@
+ /*
+  * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
+  *         PCI-PCI bridges cleanup, sorted resource allocation.
++ * May 2001, Russell King <rmk@arm.linux.org.uk>
++ *           Allocate prefetchable memory regions where available.
+  * Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
+  *         Converted to allocation in 3 passes, which gives
+  *         tighter packing. Prefetchable range support.
+@@ -160,8 +162,10 @@
+       pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
+       /* Check if we have VGA behind the bridge.
+-         Enable ISA in either case (FIXME!). */
+-      l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04;
++         Enable ISA in either case. */
++      l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ?
++              PCI_BRIDGE_CTL_VGA | PCI_BRIDGE_CTL_NO_ISA :
++              PCI_BRIDGE_CTL_NO_ISA;
+       pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l);
+ }
+--- linux-2.4.27/drivers/pcmcia/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/Config.in
+@@ -14,21 +14,19 @@
+ tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
+ if [ "$CONFIG_PCMCIA" != "n" ]; then
++   # yes, I really mean the following...
++   if [ "$CONFIG_ISA" = "y" -o "$CONFIG_ARCH_SA1100" = "y" ]; then
++      define_bool CONFIG_PCMCIA_PROBE y
++   fi
+    if [ "$CONFIG_PCI" != "n" ]; then
+       bool '  CardBus support' CONFIG_CARDBUS
+    fi
++   dep_bool '  i82092 compatible bridge support' CONFIG_I82092 $CONFIG_PCI
++   bool '  i82365 compatible bridge support' CONFIG_I82365
+    bool '  Databook TCIC host bridge support' CONFIG_TCIC
+    if [ "$CONFIG_HD64465" = "y" ]; then
+       dep_tristate '  HD64465 host bridge support' CONFIG_HD64465_PCMCIA $CONFIG_PCMCIA
+    fi
+-   dep_bool '  i82092 compatible bridge support' CONFIG_I82092 $CONFIG_PCI
+-   bool '  i82365 compatible bridge support' CONFIG_I82365
+-   if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+-      dep_tristate '  SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_PCMCIA
+-   fi
+-   if [ "$CONFIG_8xx" = "y" ]; then
+-      dep_tristate '  M8xx support' CONFIG_PCMCIA_M8XX $CONFIG_PCMCIA
+-   fi
+    if [ "$CONFIG_SOC_AU1X00" = "y" ]; then
+       dep_tristate '  Au1x00 PCMCIA support' CONFIG_PCMCIA_AU1X00 $CONFIG_PCMCIA 
+       if [ "$CONFIG_PCMCIA_AU1X00" != "n" ]; then
+@@ -44,5 +42,9 @@
+       dep_tristate '  NEC VRC4173 CARDU support' CONFIG_PCMCIA_VRC4173 $CONFIG_PCMCIA
+    fi
+ fi
++if [ "$CONFIG_ARM" = "y" ]; then
++   dep_tristate '  CLPS6700 support' CONFIG_PCMCIA_CLPS6700 $CONFIG_ARCH_CLPS711X $CONFIG_PCMCIA
++   dep_tristate '  SA1100 support' CONFIG_PCMCIA_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_PCMCIA
++fi
+ endmenu
+--- linux-2.4.27/drivers/pcmcia/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/Makefile
+@@ -65,15 +65,18 @@
+ au1000_ss-objs-$(CONFIG_PCMCIA_DB1X00)                += au1000_db1x00.o
+ au1000_ss-objs-$(CONFIG_PCMCIA_XXS1500)       += au1000_xxs1500.o
++obj-$(CONFIG_PCMCIA_CLPS6700) += clps6700.o
+ obj-$(CONFIG_PCMCIA_SA1100)   += sa1100_cs.o
+-obj-$(CONFIG_PCMCIA_M8XX)     += m8xx_pcmcia.o
+ obj-$(CONFIG_PCMCIA_SIBYTE)   += sibyte_generic.o
+ sa1100_cs-objs-y                              := sa1100_generic.o
++sa1100_cs-objs-$(CONFIG_SA1100_ADSAGC)                += sa1100_graphicsmaster.o sa1111_generic.o
+ sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY)      += sa1100_adsbitsy.o sa1111_generic.o
++sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSYPLUS)  += sa1100_adsbitsyplus.o sa1111_generic.o
+ sa1100_cs-objs-$(CONFIG_SA1100_ASSABET)               += sa1100_assabet.o
+ sa1100_cs-objs-$(CONFIG_ASSABET_NEPONSET)     += sa1100_neponset.o sa1111_generic.o
+ sa1100_cs-objs-$(CONFIG_SA1100_BADGE4)                += sa1100_badge4.o sa1111_generic.o
++sa1100_cs-objs-$(CONFIG_SA1100_CONSUS)                += sa1100_neponset.o sa1111_generic.o
+ sa1100_cs-objs-$(CONFIG_SA1100_CERF)          += sa1100_cerf.o
+ sa1100_cs-objs-$(CONFIG_SA1100_FLEXANET)      += sa1100_flexanet.o
+ sa1100_cs-objs-$(CONFIG_SA1100_FREEBIRD)      += sa1100_freebird.o
+--- linux-2.4.27/drivers/pcmcia/cistpl.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/cistpl.c
+@@ -286,7 +286,7 @@
+       s->cis_mem.flags &= ~MAP_ACTIVE;
+       s->ss_entry->set_mem_map(s->sock, &s->cis_mem);
+       if (!(s->cap.features & SS_CAP_STATIC_MAP))
+-          release_mem_region(s->cis_mem.sys_start, s->cap.map_size);
++          release_mem_resource(s->cis_mem.sys_start, s->cap.map_size);
+       bus_iounmap(s->cap.bus, s->cis_virt);
+       s->cis_mem.sys_start = 0;
+       s->cis_virt = NULL;
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/clps6700.c
+@@ -0,0 +1,498 @@
++/*
++ *  linux/drivers/pcmcia/clps6700.c
++ *
++ *  Copyright (C) 2000 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
++ * 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/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/proc_fs.h>
++#include <linux/spinlock.h>
++#include <linux/init.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/page.h>
++
++#include <asm/arch/syspld.h>
++#include <asm/hardware/clps7111.h>
++
++#include <pcmcia/version.h>
++#include <pcmcia/cs_types.h>
++#include <pcmcia/ss.h>
++
++#include "clps6700.h"
++
++#define DEBUG
++
++MODULE_AUTHOR("Russell King");
++MODULE_DESCRIPTION("CL-PS6700 PCMCIA socket driver");
++
++#define NR_CLPS6700   2
++
++struct clps6700_skt {
++      u_int                   nr;
++      u_int                   physbase;
++      u_int                   regbase;
++      u_int                   pmr;
++      u_int                   cpcr;
++      u_int                   cpcr_3v3;
++      u_int                   cpcr_5v0;
++      u_int                   cur_pmr;
++      u_int                   cur_cicr;
++      u_int                   cur_pcimr;
++      u_int                   cur_cpcr;
++      void                    (*handler)(void *, u_int);
++      void                    *handler_info;
++
++      u_int                   ev_pending;
++      spinlock_t              ev_lock;
++};
++
++static struct clps6700_skt *skts[NR_CLPS6700];
++
++static int clps6700_sock_init(u_int sock)
++{
++      struct clps6700_skt *skt = skts[sock];
++
++      skt->cur_cicr  = 0;
++      skt->cur_pmr   = skt->pmr;
++      skt->cur_pcimr = 0;
++      skt->cur_cpcr  = skt->cpcr;
++
++#ifdef DEBUG
++      printk("skt%d: sock_init()\n", sock);
++#endif
++
++      __raw_writel(skt->cur_pmr,      skt->regbase + PMR);
++      __raw_writel(skt->cur_cpcr,     skt->regbase + CPCR);
++      __raw_writel(0x01f8,            skt->regbase + SICR);
++      __raw_writel(0x0000,            skt->regbase + DMACR);
++      __raw_writel(skt->cur_cicr,     skt->regbase + CICR);
++      __raw_writel(0x1f00,            skt->regbase + CITR0A);
++      __raw_writel(0x0000,            skt->regbase + CITR0B);
++      __raw_writel(0x1f00,            skt->regbase + CITR1A);
++      __raw_writel(0x0000,            skt->regbase + CITR1B);
++      __raw_writel(skt->cur_pcimr,    skt->regbase + PCIMR);
++
++      /*
++       * Enable Auto Idle Mode in PM register
++       */
++      __raw_writel(-1,                skt->regbase + PCIRR1);
++      __raw_writel(-1,                skt->regbase + PCIRR2);
++      __raw_writel(-1,                skt->regbase + PCIRR3);
++
++      return 0;
++}
++
++static int clps6700_suspend(u_int sock)
++{
++      return 0;
++}
++
++static int clps6700_register_callback(u_int sock, void (*handler)(void *, u_int), void *info)
++{
++      struct clps6700_skt *skt = skts[sock];
++
++#ifdef DEBUG
++      printk("skt%d: register_callback: %p (%p)\n", sock, handler, info);
++#endif
++
++      skt->handler_info = info;
++      skt->handler = handler;
++
++      return 0;
++}
++
++static int clps6700_inquire_socket(u_int sock, socket_cap_t *cap)
++{
++      cap->features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP | SS_CAP_MEM_ALIGN;
++      cap->irq_mask = 0;              /* available IRQs for this socket */
++      cap->map_size = PAGE_SIZE;      /* minimum mapping size */
++      cap->pci_irq  = 0;              /* PCI interrupt number */
++      cap->cb_dev   = NULL;
++      cap->bus      = NULL;
++      return 0;
++}
++
++static int __clps6700_get_status(struct clps6700_skt *skt)
++{
++      unsigned int v, val;
++
++      v = __raw_readl(skt->regbase + PCIILR);
++      val = 0;
++      if ((v & (PCM_CD1 | PCM_CD2)) == 0)
++              val |= SS_DETECT;
++      if ((v & (PCM_BVD2 | PCM_BVD1)) == PCM_BVD1)
++              val |= SS_BATWARN;
++      if ((v & PCM_BVD2) == 0)
++              val |= SS_BATDEAD;
++
++      if (v & PCM_RDYL)
++              val |= SS_READY;
++      if (v & PCM_VS1)
++              val |= SS_3VCARD;
++      if (v & PCM_VS2)
++              val |= SS_XVCARD;
++
++#ifdef DEBUG
++      printk("skt%d: PCIILR: %08x -> (%s %s %s %s %s %s)\n",
++              skt->nr, v,
++              val & SS_READY   ? "rdy" : "---",
++              val & SS_DETECT  ? "det" : "---",
++              val & SS_BATWARN ? "bw"  : "--",
++              val & SS_BATDEAD ? "bd"  : "--",
++              val & SS_3VCARD  ? "3v"  : "--",
++              val & SS_XVCARD  ? "xv"  : "--");
++#endif
++      return val;
++}
++
++static int clps6700_get_status(u_int sock, u_int *valp)
++{
++      struct clps6700_skt *skt = skts[sock];
++
++      *valp = __clps6700_get_status(skt);
++
++      return 0; /* not used! */
++}
++
++static int clps6700_get_socket(u_int sock, socket_state_t *state)
++{
++      return -EINVAL;
++}
++
++static int clps6700_set_socket(u_int sock, socket_state_t *state)
++{
++      struct clps6700_skt *skt = skts[sock];
++      unsigned long flags;
++      u_int cpcr = skt->cur_cpcr, pmr = skt->cur_pmr, cicr = skt->cur_cicr;
++      u_int pcimr = 0;
++
++      cicr &= ~(CICR_ENABLE | CICR_RESET | CICR_IOMODE);
++
++      if (state->flags & SS_PWR_AUTO)
++              pmr |= PMR_DCAR | PMR_PDCR;
++
++      /*
++       * Note! We must NOT assert the Card Enable bit until reset has
++       * been de-asserted.  Some cards indicate not ready, which then
++       * hangs our next access.  (Bug in CLPS6700?)
++       */
++      if (state->flags & SS_RESET)
++              cicr |= CICR_RESET | CICR_RESETOE;
++      else if (state->flags & SS_OUTPUT_ENA)
++              cicr |= CICR_ENABLE;
++
++      if (state->flags & SS_IOCARD) {
++              cicr |= CICR_IOMODE;
++
++/*            if (state->csc_mask & SS_STSCHG)*/
++      } else {
++              if (state->csc_mask & SS_BATDEAD)
++                      pcimr |= PCM_BVD2;
++              if (state->csc_mask & SS_BATWARN)
++                      pcimr |= PCM_BVD1;
++              if (state->csc_mask & SS_READY)
++                      pcimr |= PCM_RDYL;
++      }
++
++      if (state->csc_mask & SS_DETECT)
++              pcimr |= PCM_CD1 | PCM_CD2;
++
++      switch (state->Vcc) {
++      case 0:                         break;
++      case 33: cpcr |= skt->cpcr_3v3; pmr |= PMR_CPE; break;
++      case 50: cpcr |= skt->cpcr_5v0; pmr |= PMR_CPE; break;
++      default: return -EINVAL;
++      }
++
++#ifdef DEBUG
++      printk("skt%d: PMR: %04x, CPCR: %04x, CICR: %04x PCIMR: %04x "
++              "(Vcc = %d, flags = %c%c%c%c, csc = %c%c%c%c%c)\n",
++              sock, pmr, cpcr, cicr, pcimr, state->Vcc,
++              state->flags & SS_RESET      ? 'r' : '-',
++              state->flags & SS_PWR_AUTO   ? 'p' : '-',
++              state->flags & SS_IOCARD     ? 'i' : '-',
++              state->flags & SS_OUTPUT_ENA ? 'o' : '-',
++              state->csc_mask & SS_STSCHG  ? 's' : '-',
++              state->csc_mask & SS_BATDEAD ? 'd' : '-',
++              state->csc_mask & SS_BATWARN ? 'w' : '-',
++              state->csc_mask & SS_READY   ? 'r' : '-',
++              state->csc_mask & SS_DETECT  ? 'c' : '-');
++#endif
++
++      save_flags_cli(flags);
++
++      if (skt->cur_cpcr != cpcr) {
++              skt->cur_cpcr = cpcr;
++              __raw_writel(skt->cur_cpcr, skt->regbase + CPCR);
++      }
++
++      if (skt->cur_pmr != pmr) {
++              skt->cur_pmr = pmr;
++              __raw_writel(skt->cur_pmr, skt->regbase + PMR);
++      }
++      if (skt->cur_pcimr != pcimr) {
++              skt->cur_pcimr = pcimr;
++              __raw_writel(skt->cur_pcimr, skt->regbase + PCIMR);
++      }
++      if (skt->cur_cicr != cicr) {
++              skt->cur_cicr = cicr;
++              __raw_writel(skt->cur_cicr, skt->regbase + CICR);
++      }
++
++      restore_flags(flags);
++
++      return 0;
++}
++
++static int clps6700_get_io_map(u_int sock, struct pccard_io_map *io)
++{
++      return -EINVAL;
++}
++
++static int clps6700_set_io_map(u_int sock, struct pccard_io_map *io)
++{
++      printk("skt%d: iomap: %d: speed %d, flags %X start %X stop %X\n",
++              sock, io->map, io->speed, io->flags, io->start, io->stop);
++      return 0;
++}
++
++static int clps6700_get_mem_map(u_int sock, struct pccard_mem_map *mem)
++{
++      return -EINVAL;
++}
++
++/*
++ * Set the memory map attributes for this socket.  (ie, mem->speed)
++ * Note that since we have SS_CAP_STATIC_MAP set, we don't need to do
++ * any mapping here at all; we just need to return the address (suitable
++ * for ioremap) to map the requested space in mem->sys_start.
++ *
++ * flags & MAP_ATTRIB indicates whether we want attribute space.
++ */
++static int clps6700_set_mem_map(u_int sock, struct pccard_mem_map *mem)
++{
++      struct clps6700_skt *skt = skts[sock];
++      u_int off;
++
++      printk("skt%d: memmap: %d: speed %d, flags %X start %lX stop %lX card %X\n",
++              sock, mem->map, mem->speed, mem->flags, mem->sys_start,
++              mem->sys_stop, mem->card_start);
++
++      if (mem->flags & MAP_ATTRIB)
++              off = CLPS6700_ATTRIB_BASE;
++      else
++              off = CLPS6700_MEM_BASE;
++
++      mem->sys_start  = skt->physbase + off;
++      mem->sys_start += mem->card_start;
++
++      return 0;
++}
++
++static void clps6700_proc_setup(u_int sock, struct proc_dir_entry *base)
++{
++}
++
++static struct pccard_operations clps6700_operations = {
++      init:                   clps6700_sock_init,
++      suspend:                clps6700_suspend,
++      register_callback:      clps6700_register_callback,
++      inquire_socket:         clps6700_inquire_socket,
++      get_status:             clps6700_get_status,
++      get_socket:             clps6700_get_socket,
++      set_socket:             clps6700_set_socket,
++      get_io_map:             clps6700_get_io_map,
++      set_io_map:             clps6700_set_io_map,
++      get_mem_map:            clps6700_get_mem_map,
++      set_mem_map:            clps6700_set_mem_map,
++      proc_setup:             clps6700_proc_setup
++};
++
++static void clps6700_bh(void *dummy)
++{
++      int i;
++
++      for (i = 0; i < NR_CLPS6700; i++) {
++              struct clps6700_skt *skt = skts[i];
++              unsigned long flags;
++              u_int events;
++
++              if (!skt)
++                      continue;
++
++              /*
++               * Note!  We must read the pending event state
++               * with interrupts disabled, otherwise we race
++               * with our own interrupt routine!
++               */
++              spin_lock_irqsave(&skt->ev_lock, flags);
++              events = skt->ev_pending;
++              skt->ev_pending = 0;
++              spin_unlock_irqrestore(&skt->ev_lock, flags);   
++
++              if (skt->handler && events)
++                      skt->handler(skt->handler_info, events);
++      }
++}
++
++static struct tq_struct clps6700_task = {
++      routine:        clps6700_bh
++};
++
++static void clps6700_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct clps6700_skt *skt = dev_id;
++      u_int val, events;
++
++      val = __raw_readl(skt->regbase + PCISR);
++      if (!val)
++              return;
++
++      __raw_writel(val, skt->regbase + PCICR);
++
++      events = 0;
++      if (val & (PCM_CD1 | PCM_CD2))
++              events |= SS_DETECT;
++      if (val & PCM_BVD1)
++              events |= SS_BATWARN;
++      if (val & PCM_BVD2)
++              events |= SS_BATDEAD;
++      if (val & PCM_RDYL)
++              events |= SS_READY;
++
++      spin_lock(&skt->ev_lock);
++      skt->ev_pending |= events;
++      spin_unlock(&skt->ev_lock);
++      schedule_task(&clps6700_task);
++}
++
++static int __init clps6700_init_skt(int nr)
++{
++      struct clps6700_skt *skt;
++      int ret;
++
++      skt = kmalloc(sizeof(struct clps6700_skt), GFP_KERNEL);
++      if (!skt)
++              return -ENOMEM;
++
++      memset(skt, 0, sizeof(struct clps6700_skt));
++
++      spin_lock_init(&skt->ev_lock);
++
++      skt->nr       = nr;
++      skt->physbase = nr ? CS5_PHYS_BASE : CS4_PHYS_BASE;
++      skt->pmr      = PMR_AUTOIDLE | PMR_MCPE | PMR_CDWEAK;
++      skt->cpcr     = CPCR_PDIR(PCTL1|PCTL0);
++      skt->cpcr_3v3 = CPCR_PON(PCTL0);
++      skt->cpcr_5v0 = CPCR_PON(PCTL0);        /* we only do 3v3 */
++
++      skt->cur_pmr  = skt->pmr;
++
++      skt->regbase = (u_int)ioremap(skt->physbase + CLPS6700_REG_BASE,
++                                      CLPS6700_REG_SIZE);
++      ret = -ENOMEM;
++      if (!skt->regbase)
++              goto err_free;
++
++      skts[nr] = skt;
++
++      ret = request_irq(IRQ_EINT3, clps6700_interrupt,
++                        SA_SHIRQ, "pcmcia", skt);
++
++      if (ret) {
++              printk(KERN_ERR "clps6700: unable to grab irq%d (%d)\n",
++                     IRQ_EINT3, ret);
++              goto err_unmap;
++      }
++      return 0;
++
++err_unmap:
++      iounmap((void *)skt->regbase);
++err_free:
++      kfree(skt);
++      skts[nr] = NULL;
++      return ret;
++}
++
++static void clps6700_free_resources(void)
++{
++      int i;
++
++      for (i = NR_CLPS6700; i >= 0; i--) {
++              struct clps6700_skt *skt = skts[i];
++
++              skts[i] = NULL;
++              if (skt == NULL)
++                      continue;
++
++              free_irq(IRQ_EINT3, skt);
++              if (skt->regbase) {
++                      __raw_writel(skt->pmr,  skt->regbase + PMR);
++                      __raw_writel(skt->cpcr, skt->regbase + CPCR);
++                      __raw_writel(0,         skt->regbase + CICR);
++                      __raw_writel(0,         skt->regbase + PCIMR);
++              }
++              iounmap((void *)skt->regbase);
++              kfree(skt);
++      }
++}
++
++static int __init clps6700_init(void)
++{
++      unsigned int v;
++      int err, nr;
++
++      PLD_CF = 0;
++      v = clps_readl(SYSCON2) | SYSCON2_PCCARD1 | SYSCON2_PCCARD2;
++      clps_writel(v, SYSCON2);
++      v = clps_readl(SYSCON1) | SYSCON1_EXCKEN;
++      clps_writel(v, SYSCON1);
++
++      for (nr = 0; nr < NR_CLPS6700; nr++) {
++              err = clps6700_init_skt(nr);
++              if (err)
++                      goto free;
++      }
++
++      err = register_ss_entry(nr, &clps6700_operations);
++      if (err)
++              goto free;
++
++      return 0;
++
++free:
++      clps6700_free_resources();
++      /*
++       * An error occurred.  Unmap and free all CLPS6700
++       */
++      return err;
++}
++
++static void __exit clps6700_exit(void)
++{
++      unregister_ss_entry(&clps6700_operations);
++      clps6700_free_resources();
++}
++
++module_init(clps6700_init);
++module_exit(clps6700_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/clps6700.h
+@@ -0,0 +1,85 @@
++#define PCISR 0x0000          /* PC Card Interrupt Status Register            */
++#define PCIMR 0x0400          /* PC Card Interrupt Mask Register              */
++#define PCICR 0x0800          /* PC Card Interrupt Clear Register             */
++#define PCIOSR        0x0c00          /* PC Card Interrupt Output Select Regsiter     */
++#define PCIRR1        0x1000          /* PC Card Interrupt Reserved Register 1        */
++#define PCIRR2        0x1400          /* PC Card Interrupt Reserved Register 2        */
++#define PCIRR3        0x1800          /* PC Card Interrupt Reserved Register 3        */
++#define PCIILR        0x1c00          /* PC Card Interrupt Input Level Register       */
++#define SICR  0x2000          /* System Interface Configuration Register      */
++#define CICR  0x2400          /* Card Interface Configuration Register        */
++#define PMR   0x2800          /* Power Management Register                    */
++#define CPCR  0x2c00          /* Card Power Control Register                  */
++#define CITR0A        0x3000          /* Card Interface Timing Register 0A            */
++#define CITR0B        0x3400          /* Card Interface Timing Register 0B            */
++#define CITR1A        0x3800          /* Card Interface Timing Register 1A            */
++#define CITR1B        0x3c00          /* Card Interface Timing Register 1B            */
++#define DMACR 0x4000          /* DMA Control Register                         */
++#define DIR   0x4400          /* Device Information Register                  */
++
++#define CLPS6700_ATTRIB_BASE  0x00000000
++#define CLPS6700_IO_BASE      0x04000000
++#define CLPS6700_MEM_BASE     0x08000000
++#define CLPS6700_REG_BASE     0x0c000000
++#define CLPS6700_REG_SIZE     0x00005000
++
++
++#define PMR_AUTOIDLE  (1 << 0)        /* auto idle mode                       */
++#define PMR_FORCEIDLE (1 << 1)        /* force idle mode                      */
++#define PMR_PDCS      (1 << 2)        /* Power down card on standby           */
++#define PMR_PDCR      (1 << 3)        /* Power down card on removal           */
++#define PMR_DCAR      (1 << 4)        /* Disable card access on removal       */
++#define PMR_CPE               (1 << 5)        /* Card power enable                    */
++#define PMR_MCPE      (1 << 6)        /* Monitor card power enable            */
++#define PMR_PDREQLSEL (1 << 7)        /* If set, PDREQL is a GPIO pin         */
++#define PMR_DISSTBY   (1 << 8)        /* Disable standby                      */
++#define PMR_ACCSTBY   (1 << 9)        /* Complete card accesses before standby*/
++#define PMR_CDUNPROT  (0 << 10)       /* Card detect inputs unprotected       */
++#define PMR_CDPROT    (1 << 10)       /* Card detect inputs protected         */
++#define PMR_CDWEAK    (2 << 10)       /* Weak pullup except in standby        */
++#define PMR_CDWEAKAL  (3 << 10)       /* Weak pullup                          */
++
++#define CPCR_PON(x)   ((x)&7)         /* PCTL[2:0] value when PMR_CPE = 1     */
++#define CPCR_POFF(x)  (((x)&7)<<3)    /* PCTL[2:0] value when PMR_CPE = 0     */
++#define CPCR_PDIR(x)  (((x)&7)<<6)    /* PCTL[2:0] direction                  */
++#define CPCR_CON(x)   (((x)&1)<<9)    /* GPIO value when PMR_CPE = 1          */
++#define CPCR_COFF(x)  (((x)&1)<<10)   /* GPIO value when PMR_CPE = 0          */
++#define CPCR_CDIR(x)  (((x)&1)<<11)   /* GPIO direction (PMR_PDREQLSEL = 1)   */
++#define CPCR_VS(x)    (((x)&3)<<12)   /* VS[2:1] output value                 */
++#define CPCR_VSDIR(x) (((x)&3)<<14)   /* VS[2:1] direction                    */
++
++#define PCTL0         (1 << 0)
++#define PCTL1         (1 << 1)
++#define PCTL2         (1 << 2)
++
++#define CICR_ASRTMR1  (1 << 0)        /* Timer 1 select for attribute read    */
++#define CICR_ASWTMR1  (1 << 1)        /* Timer 1 select for attribute write   */
++#define CICR_IOSRTMR1 (1 << 2)        /* Timer 1 select for IO read           */
++#define CICR_IOSWTMR1 (1 << 3)        /* Timer 1 select for IO write          */
++#define CICR_MEMSRTMR1        (1 << 4)        /* Timer 1 select for memory read       */
++#define CICR_MEMSWTMR1        (1 << 5)        /* Timer 1 select for memory write      */
++#define CICR_AUTOIOSZ (1 << 6)        /* Auto size I/O accesses               */
++#define CICR_CAW      (1 << 7)        /* Card access width                    */
++#define CICR_IOMODE   (1 << 8)        /* IO mode select                       */
++#define CICR_ENABLE   (1 << 10)       /* Card enable                          */
++#define CICR_RESETOE  (1 << 11)       /* Card reset output enable             */
++#define CICR_RESET    (1 << 12)       /* Card reset                           */
++
++
++#define RD_FAIL               (1 << 14)
++#define WR_FAIL               (1 << 13)
++#define IDLE          (1 << 12)
++
++#define FFOTHLD               (1 << 11)
++#define PCM_RDYL      (1 << 10)
++#define PCM_WP                (1 << 9)
++#define PCTL          (1 << 8)
++
++#define PDREQ_L               (1 << 6)
++#define PCM_VS2               (1 << 5)
++#define PCM_VS1               (1 << 4)
++
++#define PCM_CD2               (1 << 3)
++#define PCM_CD1               (1 << 2)
++#define PCM_BVD2      (1 << 1)
++#define PCM_BVD1      (1 << 0)
+--- linux-2.4.27/drivers/pcmcia/cs.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/cs.c
+@@ -3,7 +3,7 @@
+     Kernel Card Services -- core services
+     cs.c 1.271 2000/10/02 20:27:49
+-    
++
+     The contents of this file are subject to the Mozilla Public
+     License Version 1.1 (the "License"); you may not use this file
+     except in compliance with the License. You may obtain a copy of
+@@ -28,7 +28,7 @@
+     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 MPL or the GPL.
+-    
++
+ ======================================================================*/
+ #include <linux/module.h>
+@@ -92,7 +92,7 @@
+ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+ MODULE_DESCRIPTION("Linux Kernel Card Services " CS_RELEASE
+                  "\n  options:" OPTIONS);
+-MODULE_LICENSE("Dual MPL/GPL");         
++MODULE_LICENSE("Dual MPL/GPL");
+ #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+@@ -123,7 +123,7 @@
+ static const char *version =
+ "cs.c 1.279 2001/10/13 00:08:28 (David Hinds)";
+ #endif
+- 
++
+ /*====================================================================*/
+ socket_state_t dead_socket = {
+@@ -299,7 +299,7 @@
+     Low-level PC Card interface drivers need to register with Card
+     Services using these calls.
+-    
++
+ ======================================================================*/
+ static int setup_socket(socket_info_t *);
+@@ -331,7 +331,7 @@
+     s->use_bus_pm = use_bus_pm;
+     s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
+     spin_lock_init(&s->lock);
+-    
++
+     for (i = 0; i < sockets; i++)
+       if (socket_table[i] == NULL) break;
+     socket_table[i] = s;
+@@ -365,7 +365,7 @@
+     for (ns = 0; ns < nsock; ns++) {
+       pcmcia_register_socket (ns, ss_entry, 0);
+     }
+-    
++
+     return 0;
+ } /* register_ss_entry */
+@@ -457,7 +457,7 @@
+ static void shutdown_socket(socket_info_t *s)
+ {
+     client_t **c;
+-    
++
+     DEBUG(1, "cs: shutdown_socket(%p)\n", s);
+     /* Blank out the socket state */
+@@ -561,7 +561,7 @@
+     have several causes: card insertion, a call to reset_socket, or
+     recovery from a suspend/resume cycle.  Unreset_socket() sends
+     a CS event that matches the cause of the reset.
+-    
++
+ ======================================================================*/
+ static void reset_socket(socket_info_t *s)
+@@ -616,7 +616,7 @@
+           s->state &= ~SOCKET_SETUP_PENDING;
+       } else {
+           send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+-          if (s->reset_handle) { 
++          if (s->reset_handle) {
+                   s->reset_handle->event_callback_args.info = NULL;
+                   EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE,
+                         CS_EVENT_PRI_LOW);
+@@ -631,7 +631,7 @@
+     valid clients.  Parse_events() interprets the event bits from
+     a card status change report.  Do_shutdown() handles the high
+     priority stuff associated with a card removal.
+-    
++
+ ======================================================================*/
+ static int send_event(socket_info_t *s, event_t event, int priority)
+@@ -641,7 +641,7 @@
+     DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n",
+         s->sock, event, priority);
+     ret = 0;
+-    for (; client; client = client->next) { 
++    for (; client; client = client->next) {
+       if (client->state & (CLIENT_UNBOUND|CLIENT_STALE))
+           continue;
+       if (client->EventMask & event) {
+@@ -675,10 +675,17 @@
+ static void parse_events(void *info, u_int events)
+ {
+     socket_info_t *s = info;
++
+     if (events & SS_DETECT) {
+       int status;
+       get_socket_status(s, &status);
++
++      /*
++       * If our socket state indicates that a card is present and
++       * either the socket has not been suspended (for some reason)
++       * or the card has been removed, shut down the socket first.
++       */
+       if ((s->state & SOCKET_PRESENT) &&
+           (!(s->state & SOCKET_SUSPEND) ||
+            !(status & SS_DETECT)))
+@@ -716,7 +723,7 @@
+     This does not comply with the latest PC Card spec for handling
+     power management events.
+-    
++
+ ======================================================================*/
+ void pcmcia_suspend_socket (socket_info_t *s)
+@@ -773,7 +780,7 @@
+ /*======================================================================
+     Special stuff for managing IO windows, because they are scarce.
+-    
++
+ ======================================================================*/
+ static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base,
+@@ -862,7 +869,7 @@
+     Access_configuration_register() reads and writes configuration
+     registers in attribute memory.  Memory window 0 is reserved for
+     this and the tuple reading services.
+-    
++
+ ======================================================================*/
+ int pcmcia_access_configuration_register(client_handle_t handle,
+@@ -872,7 +879,7 @@
+     config_t *c;
+     int addr;
+     u_char val;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle);
+@@ -890,7 +897,7 @@
+       return CS_CONFIGURATION_LOCKED;
+     addr = (c->ConfigBase + reg->Offset) >> 1;
+-    
++
+     switch (reg->Action) {
+     case CS_READ:
+       read_cis_mem(s, 1, addr, 1, &val);
+@@ -913,7 +920,7 @@
+     It is normally called by Driver Services after it has identified
+     a newly inserted card.  An instance of that driver will then be
+     eligible to register as a client of this socket.
+-    
++
+ ======================================================================*/
+ int pcmcia_bind_device(bind_req_t *req)
+@@ -949,23 +956,23 @@
+     region.  It is normally called by Driver Services after it has
+     identified a memory device type.  An instance of the corresponding
+     driver will then be able to register to control this region.
+-    
++
+ ======================================================================*/
+ int pcmcia_bind_mtd(mtd_bind_t *req)
+ {
+     socket_info_t *s;
+     memory_handle_t region;
+-    
++
+     if (CHECK_SOCKET(req->Socket))
+       return CS_BAD_SOCKET;
+     s = SOCKET(req);
+-    
++
+     if (req->Attributes & REGION_TYPE_AM)
+       region = s->a_region;
+     else
+       region = s->c_region;
+-    
++
+     while (region) {
+       if (region->info.CardOffset == req->CardOffset) break;
+       region = region->info.next;
+@@ -973,7 +980,7 @@
+     if (!region || (region->mtd != NULL))
+       return CS_BAD_OFFSET;
+     strncpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN);
+-    
++
+     DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n",
+         req->Attributes, req->CardOffset, (char *)req->dev_info);
+     return CS_SUCCESS;
+@@ -988,7 +995,7 @@
+     memory_handle_t region;
+     u_long flags;
+     int i, sn;
+-    
++
+     DEBUG(1, "cs: deregister_client(%p)\n", handle);
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+@@ -1007,7 +1014,7 @@
+       for (region = s->c_region; region; region = region->info.next)
+           if (region->mtd == handle) region->mtd = NULL;
+     }
+-    
++
+     sn = handle->Socket; s = socket_table[sn];
+     if ((handle->state & CLIENT_STALE) ||
+@@ -1032,7 +1039,7 @@
+     if (--s->real_clients == 0)
+         register_callback(s, NULL, NULL);
+-    
++
+     return CS_SUCCESS;
+ } /* deregister_client */
+@@ -1043,7 +1050,7 @@
+ {
+     socket_info_t *s;
+     config_t *c;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle);
+@@ -1055,7 +1062,7 @@
+           return CS_BAD_ARGS;
+     } else
+       config->Function = handle->Function;
+-    
++
+ #ifdef CONFIG_CARDBUS
+     if (s->state & SOCKET_CARDBUS) {
+       u_char fn = config->Function;
+@@ -1076,16 +1083,16 @@
+       return CS_SUCCESS;
+     }
+ #endif
+-    
++
+     c = (s->config != NULL) ? &s->config[config->Function] : NULL;
+-    
++
+     if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
+       config->Attributes = 0;
+       config->Vcc = s->socket.Vcc;
+       config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+       return CS_SUCCESS;
+     }
+-    
++
+     /* !!! This is a hack !!! */
+     memcpy(&config->Attributes, &c->Attributes, sizeof(config_t));
+     config->Attributes |= CONF_VALID_CLIENT;
+@@ -1099,14 +1106,14 @@
+     config->NumPorts2 = c->io.NumPorts2;
+     config->Attributes2 = c->io.Attributes2;
+     config->IOAddrLines = c->io.IOAddrLines;
+-    
++
+     return CS_SUCCESS;
+ } /* get_configuration_info */
+ /*======================================================================
+     Return information about this version of Card Services.
+-    
++
+ ======================================================================*/
+ int pcmcia_get_card_services_info(servinfo_t *info)
+@@ -1124,7 +1131,7 @@
+     Note that get_first_client() *does* recognize the Socket field
+     in the request structure.
+-    
++
+ ======================================================================*/
+ int pcmcia_get_first_client(client_handle_t *handle, client_req_t *req)
+@@ -1239,7 +1246,7 @@
+     Get the current socket state bits.  We don't support the latched
+     SocketState yet: I haven't seen any point for it.
+-    
++
+ ======================================================================*/
+ int pcmcia_get_status(client_handle_t handle, cs_status_t *status)
+@@ -1247,7 +1254,7 @@
+     socket_info_t *s;
+     config_t *c;
+     int val;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle);
+@@ -1263,7 +1270,7 @@
+       return CS_NO_CARD;
+     if (s->state & SOCKET_SETUP_PENDING)
+       status->CardState |= CS_EVENT_CARD_INSERTION;
+-    
++
+     /* Get info from the PRR, if necessary */
+     if (handle->Function == BIND_FN_ALL) {
+       if (status->Function && (status->Function >= s->functions))
+@@ -1309,7 +1316,7 @@
+ /*======================================================================
+     Change the card address of an already open memory window.
+-    
++
+ ======================================================================*/
+ int pcmcia_get_mem_page(window_handle_t win, memreq_t *req)
+@@ -1338,7 +1345,7 @@
+ /*======================================================================
+     Modify a locked socket configuration
+-    
++
+ ======================================================================*/
+ int pcmcia_modify_configuration(client_handle_t handle,
+@@ -1346,7 +1353,7 @@
+ {
+     socket_info_t *s;
+     config_t *c;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle); c = CONFIG(handle);
+@@ -1354,7 +1361,7 @@
+       return CS_NO_CARD;
+     if (!(c->state & CONFIG_LOCKED))
+       return CS_CONFIGURATION_LOCKED;
+-    
++
+     if (mod->Attributes & CONF_IRQ_CHANGE_VALID) {
+       if (mod->Attributes & CONF_ENABLE_IRQ) {
+           c->Attributes |= CONF_ENABLE_IRQ;
+@@ -1406,7 +1413,7 @@
+       win->ctl.flags |= MAP_USE_WAIT;
+     win->ctl.speed = req->AccessSpeed;
+     set_mem_map(win->sock, &win->ctl);
+-    
++
+     return CS_SUCCESS;
+ } /* modify_window */
+@@ -1416,7 +1423,7 @@
+     caller with a socket.  The driver must have already been bound
+     to a socket with bind_device() -- in fact, bind_device()
+     allocates the client structure that will be used.
+-    
++
+ ======================================================================*/
+ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req)
+@@ -1424,7 +1431,7 @@
+     client_t *client;
+     socket_info_t *s;
+     socket_t ns;
+-    
++
+     /* Look for unbound client with matching dev_info */
+     client = NULL;
+     for (ns = 0; ns < sockets; ns++) {
+@@ -1464,7 +1471,7 @@
+     if (s->state & SOCKET_CARDBUS)
+       client->state |= CLIENT_CARDBUS;
+-    
++
+     if ((!(s->state & SOCKET_CARDBUS)) && (s->functions == 0) &&
+       (client->Function != BIND_FN_ALL)) {
+       cistpl_longlink_mfc_t mfc;
+@@ -1479,7 +1486,7 @@
+               return CS_OUT_OF_RESOURCE;
+       memset(s->config, 0, sizeof(config_t) * s->functions);
+     }
+-    
++
+     DEBUG(1, "cs: register_client(): client 0x%p, sock %d, dev %s\n",
+         client, client->Socket, client->dev_info);
+     if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE)
+@@ -1501,13 +1508,13 @@
+     pccard_io_map io = { 0, 0, 0, 0, 1 };
+     socket_info_t *s;
+     int i;
+-    
++
+     if (CHECK_HANDLE(handle) ||
+       !(handle->state & CLIENT_CONFIG_LOCKED))
+       return CS_BAD_HANDLE;
+     handle->state &= ~CLIENT_CONFIG_LOCKED;
+     s = SOCKET(handle);
+-    
++
+ #ifdef CONFIG_CARDBUS
+     if (handle->state & CLIENT_CARDBUS) {
+       cb_disable(s);
+@@ -1515,7 +1522,7 @@
+       return CS_SUCCESS;
+     }
+ #endif
+-    
++
+     if (!(handle->state & CLIENT_STALE)) {
+       config_t *c = CONFIG(handle);
+       if (--(s->lock_count) == 0) {
+@@ -1536,7 +1543,7 @@
+           }
+       c->state &= ~CONFIG_LOCKED;
+     }
+-    
++
+     return CS_SUCCESS;
+ } /* release_configuration */
+@@ -1547,25 +1554,25 @@
+     the actual socket configuration, so if the client is "stale", we
+     don't bother checking the port ranges against the current socket
+     values.
+-    
++
+ ======================================================================*/
+ int pcmcia_release_io(client_handle_t handle, io_req_t *req)
+ {
+     socket_info_t *s;
+-    
++
+     if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ))
+       return CS_BAD_HANDLE;
+     handle->state &= ~CLIENT_IO_REQ;
+     s = SOCKET(handle);
+-    
++
+ #ifdef CONFIG_CARDBUS
+     if (handle->state & CLIENT_CARDBUS) {
+       cb_release(s);
+       return CS_SUCCESS;
+     }
+ #endif
+-    
++
+     if (!(handle->state & CLIENT_STALE)) {
+       config_t *c = CONFIG(handle);
+       if (c->state & CONFIG_LOCKED)
+@@ -1581,7 +1588,7 @@
+     release_io_space(s, req->BasePort1, req->NumPorts1);
+     if (req->NumPorts2)
+       release_io_space(s, req->BasePort2, req->NumPorts2);
+-    
++
+     return CS_SUCCESS;
+ } /* release_io */
+@@ -1594,7 +1601,7 @@
+       return CS_BAD_HANDLE;
+     handle->state &= ~CLIENT_IRQ_REQ;
+     s = SOCKET(handle);
+-    
++
+     if (!(handle->state & CLIENT_STALE)) {
+       config_t *c = CONFIG(handle);
+       if (c->state & CONFIG_LOCKED)
+@@ -1608,16 +1615,16 @@
+           s->irq.AssignedIRQ = 0;
+       }
+     }
+-    
++
+     if (req->Attributes & IRQ_HANDLE_PRESENT) {
+       bus_free_irq(s->cap.bus, req->AssignedIRQ, req->Instance);
+     }
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+     if (req->AssignedIRQ != s->cap.pci_irq)
+       undo_irq(req->Attributes, req->AssignedIRQ);
+ #endif
+-    
++
+     return CS_SUCCESS;
+ } /* cs_release_irq */
+@@ -1626,7 +1633,7 @@
+ int pcmcia_release_window(window_handle_t win)
+ {
+     socket_info_t *s;
+-    
++
+     if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+       return CS_BAD_HANDLE;
+     s = win->sock;
+@@ -1640,11 +1647,11 @@
+     /* Release system memory */
+     if(!(s->cap.features & SS_CAP_STATIC_MAP))
+-      release_mem_region(win->base, win->size);
++      release_mem_resource(win->base, win->size);
+     win->handle->state &= ~CLIENT_WIN_REQ(win->index);
+     win->magic = 0;
+-    
++
+     return CS_SUCCESS;
+ } /* release_window */
+@@ -1658,13 +1665,13 @@
+     socket_info_t *s;
+     config_t *c;
+     pccard_io_map iomap;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+     if (!(s->state & SOCKET_PRESENT))
+       return CS_NO_CARD;
+-    
++
+ #ifdef CONFIG_CARDBUS
+     if (handle->state & CLIENT_CARDBUS) {
+       if (!(req->IntType & INT_CARDBUS))
+@@ -1677,7 +1684,7 @@
+       return CS_SUCCESS;
+     }
+ #endif
+-    
++
+     if (req->IntType & INT_CARDBUS)
+       return CS_UNSUPPORTED_MODE;
+     c = CONFIG(handle);
+@@ -1692,9 +1699,9 @@
+     s->socket.Vpp = req->Vpp1;
+     if (set_socket(s, &s->socket))
+       return CS_BAD_VPP;
+-    
++
+     c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1;
+-    
++
+     /* Pick memory or I/O card, DMA mode, interrupt */
+     c->IntType = req->IntType;
+     c->Attributes = req->Attributes;
+@@ -1712,7 +1719,7 @@
+       s->socket.io_irq = 0;
+     set_socket(s, &s->socket);
+     s->lock_count++;
+-    
++
+     /* Set up CIS configuration registers */
+     base = c->ConfigBase = req->ConfigBase;
+     c->Present = c->CardValues = req->Present;
+@@ -1757,7 +1764,7 @@
+       u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1;
+       write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b);
+     }
+-    
++
+     /* Configure I/O windows */
+     if (c->state & CONFIG_IO_REQ) {
+       iomap.speed = io_speed;
+@@ -1779,24 +1786,24 @@
+               s->io[i].Config++;
+           }
+     }
+-    
++
+     c->state |= CONFIG_LOCKED;
+     handle->state |= CLIENT_CONFIG_LOCKED;
+     return CS_SUCCESS;
+ } /* request_configuration */
+ /*======================================================================
+-  
++
+     Request_io() reserves ranges of port addresses for a socket.
+     I have not implemented range sharing or alias addressing.
+-    
++
+ ======================================================================*/
+ int pcmcia_request_io(client_handle_t handle, io_req_t *req)
+ {
+     socket_info_t *s;
+     config_t *c;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle);
+@@ -1855,7 +1862,7 @@
+     hooked, we don't guarantee that an irq will still be available
+     when the configuration is locked.  Now that I think about it,
+     there might be a way to fix this using a dummy handler.
+-    
++
+ ======================================================================*/
+ int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
+@@ -1863,7 +1870,7 @@
+     socket_info_t *s;
+     config_t *c;
+     int ret = CS_IN_USE, irq = 0;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(handle);
+@@ -1875,7 +1882,7 @@
+     if (c->state & CONFIG_IRQ_REQ)
+       return CS_IN_USE;
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+     if (s->irq.AssignedIRQ != 0) {
+       /* If the interrupt is already assigned, it must match */
+       irq = s->irq.AssignedIRQ;
+@@ -1909,7 +1916,7 @@
+     if (req->Attributes & IRQ_HANDLE_PRESENT) {
+       if (bus_request_irq(s->cap.bus, irq, req->Handler,
+-                          ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || 
++                          ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+                            (s->functions > 1) ||
+                            (irq == s->cap.pci_irq)) ? SA_SHIRQ : 0,
+                           handle->dev_info, req->Instance))
+@@ -1919,7 +1926,7 @@
+     c->irq.Attributes = req->Attributes;
+     s->irq.AssignedIRQ = req->AssignedIRQ = irq;
+     s->irq.Config++;
+-    
++
+     c->state |= CONFIG_IRQ_REQ;
+     handle->state |= CLIENT_IRQ_REQ;
+     return CS_SUCCESS;
+@@ -1938,7 +1945,7 @@
+     window_t *win;
+     u_long align;
+     int w;
+-    
++
+     if (CHECK_HANDLE(*handle))
+       return CS_BAD_HANDLE;
+     s = SOCKET(*handle);
+@@ -2005,7 +2012,7 @@
+     /* Return window handle */
+     req->Base = win->ctl.sys_start;
+     *wh = win;
+-    
++
+     return CS_SUCCESS;
+ } /* request_window */
+@@ -2014,14 +2021,14 @@
+     I'm not sure which "reset" function this is supposed to use,
+     but for now, it uses the low-level interface's reset, not the
+     CIS register.
+-    
++
+ ======================================================================*/
+ int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
+ {
+     int i, ret;
+     socket_info_t *s;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+@@ -2049,14 +2056,14 @@
+     These shut down or wake up a socket.  They are sort of user
+     initiated versions of the APM suspend and resume actions.
+-    
++
+ ======================================================================*/
+ int pcmcia_suspend_card(client_handle_t handle, client_req_t *req)
+ {
+     int i;
+     socket_info_t *s;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+@@ -2077,7 +2084,7 @@
+ {
+     int i;
+     socket_info_t *s;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+@@ -2095,7 +2102,7 @@
+ /*======================================================================
+     These handle user requests to eject or insert a card.
+-    
++
+ ======================================================================*/
+ int pcmcia_eject_card(client_handle_t handle, client_req_t *req)
+@@ -2103,7 +2110,7 @@
+     int i, ret;
+     socket_info_t *s;
+     u_long flags;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+@@ -2119,9 +2126,9 @@
+     spin_lock_irqsave(&s->lock, flags);
+     do_shutdown(s);
+     spin_unlock_irqrestore(&s->lock, flags);
+-    
++
+     return CS_SUCCESS;
+-    
++
+ } /* eject_card */
+ int pcmcia_insert_card(client_handle_t handle, client_req_t *req)
+@@ -2129,7 +2136,7 @@
+     int i, status;
+     socket_info_t *s;
+     u_long flags;
+-    
++
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+     i = handle->Socket; s = socket_table[i];
+@@ -2157,7 +2164,7 @@
+     Maybe this should send a CS_EVENT_CARD_INSERTION event if we
+     haven't sent one to this client yet?
+-    
++
+ ======================================================================*/
+ int pcmcia_set_event_mask(client_handle_t handle, eventmask_t *mask)
+@@ -2189,7 +2196,7 @@
+       printk(KERN_NOTICE);
+     else
+       printk(KERN_NOTICE "%s: ", handle->dev_info);
+-    
++
+     for (i = 0; i < SERVICE_COUNT; i++)
+       if (service_table[i].key == err->func) break;
+     if (i < SERVICE_COUNT)
+@@ -2347,13 +2354,13 @@
+     default:
+       return CS_UNSUPPORTED_FUNCTION; break;
+     }
+-    
++
+ } /* CardServices */
+ /*======================================================================
+     OS-specific module glue goes here
+-    
++
+ ======================================================================*/
+ /* in alpha order */
+ EXPORT_SYMBOL(pcmcia_access_configuration_register);
+@@ -2450,4 +2457,3 @@
+ module_exit(exit_pcmcia_cs);
+ /*====================================================================*/
+-
+--- linux-2.4.27/drivers/pcmcia/ds.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/ds.c
+@@ -55,6 +55,7 @@
+ #include <pcmcia/bulkmem.h>
+ #include <pcmcia/cistpl.h>
+ #include <pcmcia/ds.h>
++#include <linux/devfs_fs_kernel.h>
+ /*====================================================================*/
+@@ -880,6 +881,8 @@
+ EXPORT_SYMBOL(register_pccard_driver);
+ EXPORT_SYMBOL(unregister_pccard_driver);
++static devfs_handle_t devfs_handle;
++
+ /*====================================================================*/
+ int __init init_pcmcia_ds(void)
+@@ -957,8 +960,13 @@
+     if (i == -EBUSY)
+       printk(KERN_NOTICE "unable to find a free device # for "
+              "Driver Services\n");
+-    else
++    else {
+       major_dev = i;
++      devfs_handle = devfs_register(NULL, "pcmcia", DEVFS_FL_DEFAULT,
++                                    major_dev, 0,
++                                    S_IFCHR | S_IRUSR | S_IWUSR,
++                                    &ds_fops, NULL);
++    }
+ #ifdef CONFIG_PROC_FS
+     if (proc_pccard)
+@@ -983,7 +991,9 @@
+       remove_proc_entry("drivers", proc_pccard);
+ #endif
+     if (major_dev != -1)
+-      unregister_chrdev(major_dev, "pcmcia");
++      devfs_unregister_chrdev(major_dev, "pcmcia");
++      devfs_unregister(devfs_handle);
++
+     for (i = 0; i < sockets; i++)
+       pcmcia_deregister_client(socket_table[i].handle);
+     sockets = 0;
+--- linux-2.4.27/drivers/pcmcia/i82365.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/i82365.c
+@@ -28,7 +28,7 @@
+     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 MPL or the GPL.
+-    
++
+ ======================================================================*/
+ #include <linux/module.h>
+@@ -65,6 +65,15 @@
+ #include "ricoh.h"
+ #include "o2micro.h"
++#ifdef CONFIG_ARCH_EBSA110
++#define I365_MASK             (1 << 6)
++#define SOCKIRQ2REG(sock,irq) ((irq) ? ((sock) ? 3 : 4) : 0)
++#define REG2SOCKIRQ(sock,reg) (6)
++#else
++#define SOCKIRQ2REG(sock,irq) (irq)
++#define REG2SOCKIRQ(sock,reg) (reg)
++#endif
++
+ #ifdef PCMCIA_DEBUG
+ static int pc_debug = PCMCIA_DEBUG;
+ MODULE_PARM(pc_debug, "i");
+@@ -173,13 +182,15 @@
+ } socket_info_t;
+ /* Where we keep track of our sockets... */
+-static int sockets = 0;
+-static socket_info_t socket[8] = {
+-    { 0, }, /* ... */
+-};
++static int sockets /* = 0 */;
++static socket_info_t socket[8] /* = {
++    { 0, },
++} */;
+ /* Default ISA interrupt mask */
++#ifndef I365_MASK
+ #define I365_MASK     0xdeb8  /* irq 15,14,12,11,10,9,7,5,4,3 */
++#endif
+ static int grab_irq;
+ static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED;
+@@ -303,7 +314,7 @@
+     The VIA controllers also use these routines, as they are mostly
+     Cirrus lookalikes, without the timing registers.
+-    
++
+ ======================================================================*/
+ #define flip(v,b,f) (v = ((f)<0) ? v : ((f) ? ((v)|(b)) : ((v)&(~b))))
+@@ -389,7 +400,7 @@
+     Code to save and restore global state information for Vadem VG468
+     and VG469 controllers, and to set and report global configuration
+     options.
+-    
++
+ ======================================================================*/
+ static void vg46x_get_state(u_short s)
+@@ -411,7 +422,7 @@
+ static u_int __init vg46x_set_opts(u_short s, char *buf)
+ {
+     vg46x_state_t *p = &socket[s].state.vg46x;
+-    
++
+     flip(p->ctl, VG468_CTL_ASYNC, async_clock);
+     flip(p->ema, VG469_MODE_CABLE, cable_mode);
+     if (p->ctl & VG468_CTL_ASYNC)
+@@ -436,7 +447,7 @@
+ /*======================================================================
+     Generic routines to get and set controller options
+-    
++
+ ======================================================================*/
+ static void get_bridge_state(u_short s)
+@@ -489,7 +500,7 @@
+ /*======================================================================
+     Interrupt testing code, for ISA and PCI interrupts
+-    
++
+ ======================================================================*/
+ static volatile u_int irq_hits;
+@@ -517,7 +528,7 @@
+     }
+     /* Generate one interrupt */
+-    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
++    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (SOCKIRQ2REG(sock, irq) << 4));
+     i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
+     udelay(1000);
+@@ -526,7 +537,7 @@
+     /* mask all interrupts */
+     i365_set(sock, I365_CSCINT, 0);
+     DEBUG(2, "    hits = %d\n", irq_hits);
+-    
++
+     return (irq_hits != 1);
+ }
+@@ -540,7 +551,7 @@
+     /* Don't probe level-triggered interrupts -- reserved for PCI */
+     mask0 &= ~(inb(PIC) | (inb(PIC+1) << 8));
+ #endif
+-    
++
+     if (do_scan) {
+       set_bridge_state(sock);
+       i365_set(sock, I365_CSCINT, 0);
+@@ -551,7 +562,7 @@
+           if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
+               mask1 ^= (1 << i);
+     }
+-    
++
+     printk(KERN_INFO "    ISA irqs (");
+     if (mask1) {
+       printk("scanned");
+@@ -565,12 +576,12 @@
+       if (!cs_irq && (poll_interval == 0)) poll_interval = HZ;
+     }
+     printk(") = ");
+-    
++
+     for (i = 0; i < 16; i++)
+       if (mask1 & (1<<i))
+           printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
+     if (mask1 == 0) printk("none!");
+-    
++
+     return mask1;
+ }
+@@ -598,14 +609,14 @@
+     /* Use the next free entry in the socket table */
+     socket[sockets].ioaddr = port;
+     socket[sockets].psock = sock;
+-    
++
+     /* Wake up a sleepy Cirrus controller */
+     if (wakeup) {
+       i365_bclr(sockets, PD67_MISC_CTL_2, PD67_MC2_SUSPEND);
+       /* Pause at least 50 ms */
+       mdelay(50);
+     }
+-    
++
+     if ((val = i365_get(sockets, I365_IDENT)) & 0x70)
+       return -1;
+     switch (val) {
+@@ -618,7 +629,7 @@
+     case 0x88: case 0x89: case 0x8a:
+       type = IS_IBM; break;
+     }
+-    
++
+     /* Check for Vadem VG-468 chips */
+     outb(0x0e, port);
+     outb(0x37, port);
+@@ -633,7 +644,7 @@
+     val = i365_get(sockets, RF5C_CHIP_ID);
+     if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396))
+       type = IS_RF5Cx96;
+-    
++
+     /* Check for Cirrus CL-PD67xx chips */
+     i365_set(sockets, PD67_CHIP_INFO, 0);
+     val = i365_get(sockets, PD67_CHIP_INFO);
+@@ -655,14 +666,14 @@
+     bound to a (non PC Card) Linux driver.  We leave these alone.
+     We make an exception for cards that seem to be serial devices.
+-    
++
+ ======================================================================*/
+ static int __init is_alive(u_short sock)
+ {
+     u_char stat;
+     u_short start, stop;
+-    
++
+     stat = i365_get(sock, I365_STATUS);
+     start = i365_get_pair(sock, I365_IO(0)+I365_W_START);
+     stop = i365_get_pair(sock, I365_IO(0)+I365_W_STOP);
+@@ -697,7 +708,7 @@
+     base = sockets-ns;
+     if (t->ioaddr > 0) request_region(t->ioaddr, 2, "i82365");
+-    
++
+     if (base == 0) printk("\n");
+     printk(KERN_INFO "  %s", pcic[type].name);
+     printk(" ISA-to-PCMCIA at port %#x ofs 0x%02x",
+@@ -713,7 +724,7 @@
+     mask &= I365_MASK & set_bridge_opts(base, ns);
+     /* Scan for ISA interrupts */
+     mask = isa_scan(base, mask);
+-        
++
+     /* Poll if only two interrupts available */
+     if (!poll_interval) {
+       u_int tmp = (mask & 0xff20);
+@@ -735,15 +746,15 @@
+           printk(" status change on irq %d\n", cs_irq);
+       }
+     }
+-    
++
+     if (!isa_irq) {
+       if (poll_interval == 0)
+           poll_interval = HZ;
+       printk(" polling interval = %d ms\n",
+              poll_interval * 1000 / HZ);
+-      
++
+     }
+-    
++
+     /* Update socket interrupt information, capabilities */
+     for (i = 0; i < ns; i++) {
+       t[i].cap.features |= SS_CAP_PCCARD;
+@@ -866,12 +877,12 @@
+               events = pending_events[i];
+               pending_events[i] = 0;
+               spin_unlock_irq(&pending_event_lock);
+-              /* 
+-              SS_DETECT events need a small delay here. The reason for this is that 
++              /*
++              SS_DETECT events need a small delay here. The reason for this is that
+               the "is there a card" electronics need time to see the card after the
+-              "we have a card coming in" electronics have seen it. 
++              "we have a card coming in" electronics have seen it.
+               */
+-              if (events & SS_DETECT) 
++              if (events & SS_DETECT)
+                       mdelay(4);
+               if (socket[i].handler)
+                       socket[i].handler(socket[i].info, events);
+@@ -890,7 +901,7 @@
+     int i, j, csc;
+     u_int events, active;
+     u_long flags = 0;
+-    
++
+     DEBUG(4, "i82365: pcic_interrupt(%d)\n", irq);
+     for (j = 0; j < 20; j++) {
+@@ -906,20 +917,20 @@
+               continue;
+           }
+           events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+-          
+-          
++
++
+           /* Several sockets will send multiple "new card detected"
+-             events in rapid succession. However, the rest of the pcmcia expects 
++             events in rapid succession. However, the rest of the pcmcia expects
+              only one such event. We just ignore these events by having a
+                timeout */
+           if (events) {
+-              if ((jiffies - last_detect_jiffies)<(HZ/20)) 
++              if ((jiffies - last_detect_jiffies)<(HZ/20))
+                       events = 0;
+               last_detect_jiffies = jiffies;
+-              
++
+           }
+-      
++
+           if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
+               events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+           else {
+@@ -980,11 +991,11 @@
+ static int i365_get_status(u_short sock, u_int *value)
+ {
+     u_int status;
+-    
++
+     status = i365_get(sock, I365_STATUS);
+     *value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
+       ? SS_DETECT : 0;
+-      
++
+     if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
+       *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
+     else {
+@@ -1005,7 +1016,7 @@
+           *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
+       }
+     }
+-    
++
+     DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value);
+     return 0;
+ } /* i365_get_status */
+@@ -1016,7 +1027,7 @@
+ {
+     socket_info_t *t = &socket[sock];
+     u_char reg, vcc, vpp;
+-    
++
+     reg = i365_get(sock, I365_POWER);
+     state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0;
+     state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0;
+@@ -1057,14 +1068,14 @@
+     reg = i365_get(sock, I365_INTCTL);
+     state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET;
+     if (reg & I365_PC_IOCARD) state->flags |= SS_IOCARD;
+-    state->io_irq = reg & I365_IRQ_MASK;
+-    
++    state->io_irq = REG2SOCKIRQ(sock, reg & I365_IRQ_MASK);
++
+     /* speaker control */
+     if (t->flags & IS_CIRRUS) {
+       if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_SPKR_ENA)
+           state->flags |= SS_SPKR_ENA;
+     }
+-    
++
+     /* Card status change mask */
+     reg = i365_get(sock, I365_CSCINT);
+     state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0;
+@@ -1075,7 +1086,7 @@
+       state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0;
+       state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0;
+     }
+-    
++
+     DEBUG(1, "i82365: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+         "io_irq %d, csc_mask %#2.2x\n", sock, state->flags,
+         state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+@@ -1088,21 +1099,21 @@
+ {
+     socket_info_t *t = &socket[sock];
+     u_char reg;
+-    
++
+     DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+         "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+         state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+-    
++
+     /* First set global controller options */
+     set_bridge_state(sock);
+-    
++
+     /* IO card, RESET flag, IO interrupt */
+     reg = t->intr;
+-    reg |= state->io_irq;
++    reg |= SOCKIRQ2REG(sock, state->io_irq);
+     reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+     reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+     i365_set(sock, I365_INTCTL, reg);
+-    
++
+     reg = I365_PWR_NORESET;
+     if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
+     if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+@@ -1165,7 +1176,7 @@
+       default:        return -EINVAL;
+       }
+     }
+-    
++
+     if (reg != i365_get(sock, I365_POWER))
+       i365_set(sock, I365_POWER, reg);
+@@ -1175,9 +1186,9 @@
+       i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
+                  state->flags & SS_SPKR_ENA);
+     }
+-    
++
+     /* Card status change interrupt mask */
+-    reg = t->cs_irq << 4;
++    reg = SOCKIRQ2REG(sock, t->cs_irq) << 4;
+     if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
+     if (state->flags & SS_IOCARD) {
+       if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+@@ -1188,7 +1199,7 @@
+     }
+     i365_set(sock, I365_CSCINT, reg);
+     i365_get(sock, I365_CSC);
+-    
++
+     return 0;
+ } /* i365_set_socket */
+@@ -1197,7 +1208,7 @@
+ static int i365_get_io_map(u_short sock, struct pccard_io_map *io)
+ {
+     u_char map, ioctl, addr;
+-    
++
+     map = io->map;
+     if (map > 1) return -EINVAL;
+     io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START);
+@@ -1220,7 +1231,7 @@
+ static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
+ {
+     u_char map, ioctl;
+-    
++
+     DEBUG(1, "i82365: SetIOMap(%d, %d, %#2.2x, %d ns, "
+         "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+         io->speed, io->start, io->stop);
+@@ -1250,30 +1261,30 @@
+ {
+     u_short base, i;
+     u_char map, addr;
+-    
++
+     map = mem->map;
+     if (map > 4) return -EINVAL;
+     addr = i365_get(sock, I365_ADDRWIN);
+     mem->flags = (addr & I365_ENA_MEM(map)) ? MAP_ACTIVE : 0;
+     base = I365_MEM(map);
+-    
++
+     i = i365_get_pair(sock, base+I365_W_START);
+     mem->flags |= (i & I365_MEM_16BIT) ? MAP_16BIT : 0;
+     mem->flags |= (i & I365_MEM_0WS) ? MAP_0WS : 0;
+     mem->sys_start = ((u_long)(i & 0x0fff) << 12);
+-    
++
+     i = i365_get_pair(sock, base+I365_W_STOP);
+     mem->speed  = (i & I365_MEM_WS0) ? 1 : 0;
+     mem->speed += (i & I365_MEM_WS1) ? 2 : 0;
+     mem->speed = to_ns(mem->speed);
+     mem->sys_stop = ((u_long)(i & 0x0fff) << 12) + 0x0fff;
+-    
++
+     i = i365_get_pair(sock, base+I365_W_OFF);
+     mem->flags |= (i & I365_MEM_WRPROT) ? MAP_WRPROT : 0;
+     mem->flags |= (i & I365_MEM_REG) ? MAP_ATTRIB : 0;
+     mem->card_start = ((u_int)(i & 0x3fff) << 12) + mem->sys_start;
+     mem->card_start &= 0x3ffffff;
+-    
++
+     DEBUG(1, "i82365: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5."
+         "5lx, %#5.5x\n", sock, mem->map, mem->flags, mem->speed,
+         mem->sys_start, mem->sys_stop, mem->card_start);
+@@ -1281,12 +1292,12 @@
+ } /* i365_get_mem_map */
+ /*====================================================================*/
+-  
++
+ static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+ {
+     u_short base, i;
+     u_char map;
+-    
++
+     DEBUG(1, "i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
+         "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed,
+         mem->sys_start, mem->sys_stop, mem->card_start);
+@@ -1297,17 +1308,17 @@
+       return -EINVAL;
+     if ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff))
+       return -EINVAL;
+-      
++
+     /* Turn off the window before changing anything */
+     if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+       i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+-    
++
+     base = I365_MEM(map);
+     i = (mem->sys_start >> 12) & 0x0fff;
+     if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
+     if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
+     i365_set_pair(sock, base+I365_W_START, i);
+-    
++
+     i = (mem->sys_stop >> 12) & 0x0fff;
+     switch (to_cycles(mem->speed)) {
+     case 0:   break;
+@@ -1316,12 +1327,12 @@
+     default:  i |= I365_MEM_WS1 | I365_MEM_WS0; break;
+     }
+     i365_set_pair(sock, base+I365_W_STOP, i);
+-    
++
+     i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
+     if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
+     if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
+     i365_set_pair(sock, base+I365_W_OFF, i);
+-    
++
+     /* Turn on the window if necessary */
+     if (mem->flags & MAP_ACTIVE)
+       i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+@@ -1332,7 +1343,7 @@
+     Routines for accessing socket information and register dumps via
+     /proc/bus/pccard/...
+-    
++
+ ======================================================================*/
+ #ifdef CONFIG_PROC_FS
+@@ -1353,7 +1364,7 @@
+     u_short sock = (socket_info_t *)data - socket;
+     char *p = buf;
+     int i, top;
+-    
++
+     u_long flags = 0;
+     ISA_LOCK(sock, flags);
+     top = 0x40;
+@@ -1399,7 +1410,7 @@
+ /*====================================================================*/
+-/* this is horribly ugly... proper locking needs to be done here at 
++/* this is horribly ugly... proper locking needs to be done here at
+  * some time... */
+ #define LOCKED(x) do { \
+       int retval; \
+@@ -1532,7 +1543,7 @@
+     /* Set up interrupt handler(s) */
+     if (grab_irq != 0)
+       request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
+-    
++
+     if (register_ss_entry(sockets, &pcic_operations) != 0)
+       printk(KERN_NOTICE "i82365: register_ss_entry() failed\n");
+@@ -1544,9 +1555,9 @@
+       poll_timer.expires = jiffies + poll_interval;
+       add_timer(&poll_timer);
+     }
+-    
++
+     return 0;
+-    
++
+ } /* init_i82365 */
+ static void __exit exit_i82365(void)
+--- linux-2.4.27/drivers/pcmcia/rsrc_mgr.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/rsrc_mgr.c
+@@ -28,7 +28,7 @@
+     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 MPL or the GPL.
+-    
++
+ ======================================================================*/
+ #define __NO_VERSION__
+@@ -55,6 +55,10 @@
+ #include <pcmcia/cistpl.h>
+ #include "cs_internal.h"
++#ifndef ISAMEM_PHYS
++#define ISAMEM_PHYS 0
++#endif
++
+ /*====================================================================*/
+ /* Parameters that can be set with 'insmod' */
+@@ -62,7 +66,7 @@
+ #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
+ INT_MODULE_PARM(probe_mem,    1);             /* memory probe? */
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ INT_MODULE_PARM(probe_io,     1);             /* IO port probe? */
+ INT_MODULE_PARM(mem_limit,    0x10000);
+ #endif
+@@ -85,7 +89,7 @@
+ /* IO port resource database */
+ static resource_map_t io_db = { 0, 0, &io_db };
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ typedef struct irq_info_t {
+     u_int                     Attributes;
+@@ -133,6 +137,7 @@
+ static inline int check_mem_resource(unsigned long b, unsigned long n,
+                                    struct pci_dev *dev)
+ {
++      b += ISAMEM_PHYS;
+       return check_resource(resource_parent(b, n, IORESOURCE_MEM, dev), b, n);
+ }
+@@ -169,10 +174,15 @@
+ static int request_mem_resource(unsigned long b, unsigned long n,
+                               char *name, struct pci_dev *dev)
+ {
+-      struct resource *res = make_resource(b, n, IORESOURCE_MEM, name);
+-      struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev);
++      struct resource *res;
++      struct resource *pr;
+       int err = -ENOMEM;
++      b += ISAMEM_PHYS;
++
++      res = make_resource(b, n, IORESOURCE_MEM, name);
++      pr = resource_parent(b, n, IORESOURCE_MEM, dev);
++
+       if (res) {
+               err = request_resource(pr, res);
+               if (err)
+@@ -181,10 +191,16 @@
+       return err;
+ }
++void release_mem_resource(unsigned long b, unsigned long n)
++{
++      b += ISAMEM_PHYS;
++      release_mem_region(b, n);
++}
++
+ /*======================================================================
+     These manage the internal databases of available resources.
+-    
++
+ ======================================================================*/
+ static int add_interval(resource_map_t *map, u_long base, u_long num)
+@@ -248,25 +264,25 @@
+     These routines examine a region of IO or memory addresses to
+     determine what ranges might be genuinely available.
+-    
++
+ ======================================================================*/
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ static void do_io_probe(ioaddr_t base, ioaddr_t num)
+ {
+-    
++
+     ioaddr_t i, j, bad, any;
+     u_char *b, hole, most;
+-    
++
+     printk(KERN_INFO "cs: IO port probe 0x%04x-0x%04x:",
+          base, base+num-1);
+-    
++
+     /* First, what does a floating port look like? */
+     b = kmalloc(256, GFP_KERNEL);
+     if (!b) {
+             printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes");
+             return;
+-    }   
++    }
+     memset(b, 0, 256);
+     for (i = base, most = 0; i < base+num; i += 8) {
+       if (check_io_resource(i, 8, NULL))
+@@ -308,7 +324,7 @@
+           printk(" %#04x-%#04x", bad, i-1);
+       }
+     }
+-    
++
+     printk(any ? "\n" : " clean.\n");
+ }
+ #endif
+@@ -318,7 +334,7 @@
+     The memory probe.  If the memory list includes a 64K-aligned block
+     below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+     least mem_limit free space, we quit.
+-    
++
+ ======================================================================*/
+ static int do_mem_probe(u_long base, u_long num,
+@@ -332,7 +348,7 @@
+     bad = fail = 0;
+     step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
+     for (i = j = base; i < base+num; i = j + step) {
+-      if (!fail) {    
++      if (!fail) {
+           for (j = i; j < base+num; j += step)
+               if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) &&
+                   is_valid(j))
+@@ -356,7 +372,7 @@
+     return (num - bad);
+ }
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ static u_long inv_probe(int (*is_valid)(u_long),
+                       int (*do_cksum)(u_long),
+@@ -383,7 +399,7 @@
+     static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
+     static int hi = 0, lo = 0;
+     u_long b, i, ok = 0;
+-    
++
+     if (!probe_mem) return;
+     /* We do up to four passes through the list */
+     if (!force_low) {
+@@ -414,14 +430,14 @@
+     }
+ }
+-#else /* CONFIG_ISA */
++#else /* CONFIG_PCMCIA_PROBE */
+ void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
+                 int force_low, socket_info_t *s)
+ {
+     resource_map_t *m, *n;
+     static int done = 0;
+-    
++
+     if (!probe_mem || done++)
+       return;
+@@ -432,7 +448,7 @@
+     }
+ }
+-#endif /* CONFIG_ISA */
++#endif /* CONFIG_PCMCIA_PROBE */
+ /*======================================================================
+@@ -444,7 +460,7 @@
+     should be a power of two, greater than or equal to 'num'.  A value
+     of 0 means that all bits of *base are significant.  *base should
+     also be strictly less than 'align'.
+-    
++
+ ======================================================================*/
+ int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
+@@ -452,7 +468,7 @@
+ {
+     ioaddr_t try;
+     resource_map_t *m;
+-    
++
+     for (m = io_db.next; m != &io_db; m = m->next) {
+       try = (m->base & ~(align-1)) + *base;
+       for (try = (try >= m->base) ? try : try+align;
+@@ -500,10 +516,10 @@
+     This checks to see if an interrupt is available, with support
+     for interrupt sharing.  We don't support reserving interrupts
+     yet.  If the interrupt is available, we allocate it.
+-    
++
+ ======================================================================*/
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ static void fake_irq(int i, void *d, struct pt_regs *r) { }
+ static inline int check_irq(int irq)
+@@ -570,7 +586,7 @@
+ /*====================================================================*/
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+ void undo_irq(u_int Attributes, int irq)
+ {
+@@ -600,7 +616,7 @@
+     The various adjust_* calls form the external interface to the
+     resource database.
+-    
++
+ ======================================================================*/
+ static int adjust_memory(adjust_t *adj)
+@@ -632,7 +648,7 @@
+     default:
+       ret = CS_UNSUPPORTED_FUNCTION;
+     }
+-    
++
+     return ret;
+ }
+@@ -641,7 +657,7 @@
+ static int adjust_io(adjust_t *adj)
+ {
+     int base, num;
+-    
++
+     base = adj->resource.io.BasePort;
+     num = adj->resource.io.NumPorts;
+     if ((base < 0) || (base > 0xffff))
+@@ -653,7 +669,7 @@
+     case ADD_MANAGED_RESOURCE:
+       if (add_interval(&io_db, base, num) != 0)
+           return CS_IN_USE;
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+       if (probe_io)
+           do_io_probe(base, num);
+ #endif
+@@ -673,15 +689,15 @@
+ static int adjust_irq(adjust_t *adj)
+ {
+-#ifdef CONFIG_ISA
++#ifdef CONFIG_PCMCIA_PROBE
+     int irq;
+     irq_info_t *info;
+-    
++
+     irq = adj->resource.irq.IRQ;
+     if ((irq < 0) || (irq > 15))
+       return CS_BAD_IRQ;
+     info = &irq_table[irq];
+-    
++
+     switch (adj->Action) {
+     case ADD_MANAGED_RESOURCE:
+       if (info->Attributes & RES_REMOVED)
+@@ -716,7 +732,7 @@
+ {
+     if (CHECK_HANDLE(handle))
+       return CS_BAD_HANDLE;
+-    
++
+     switch (adj->Resource) {
+     case RES_MEMORY_RANGE:
+       return adjust_memory(adj);
+@@ -736,7 +752,7 @@
+ void release_resource_db(void)
+ {
+     resource_map_t *p, *q;
+-    
++
+     for (p = mem_db.next; p != &mem_db; p = q) {
+       q = p->next;
+       kfree(p);
+--- linux-2.4.27/drivers/pcmcia/sa1100.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100.h
+@@ -204,7 +204,9 @@
+ extern struct pcmcia_low_level flexanet_pcmcia_ops;
+ extern struct pcmcia_low_level simpad_pcmcia_ops;
+ extern struct pcmcia_low_level graphicsmaster_pcmcia_ops;
++extern struct pcmcia_low_level adsagc_pcmcia_ops;
+ extern struct pcmcia_low_level adsbitsy_pcmcia_ops;
++extern struct pcmcia_low_level adsbitsyplus_pcmcia_ops;
+ extern struct pcmcia_low_level stork_pcmcia_ops;
+ extern struct pcmcia_low_level badge4_pcmcia_ops;
+--- linux-2.4.27/drivers/pcmcia/sa1100_adsbitsy.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_adsbitsy.c
+@@ -11,28 +11,156 @@
+  */
+ #include <linux/kernel.h>
+ #include <linux/sched.h>
++#include <linux/ioport.h>
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
++#include <asm/irq.h>
+ #include "sa1100_generic.h"
+ #include "sa1111_generic.h"
++int adsbitsy_smc91111_present(void);
++
++#ifndef       CONFIG_SMC91111
++#define adsbitsy_smc91111_present() 0
++#endif
++
++static struct irqs {
++      int irq;
++      const char *str;
++} irqs[] = {
++      { S0_CD_VALID,    "SA1111 PCMCIA card detect" },
++      { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1"        },
++      { S1_CD_VALID,    "SA1111 CF card detect"     },
++      { S1_BVD1_STSCHG, "SA1111 CF BVD1"            },
++};
++
+ static int adsbitsy_pcmcia_init(struct pcmcia_init *init)
+ {
+-  /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
+-  PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
++  int ret=0;
++  int nirq = 0;
++  int slots = 0;
++  int i;
+-  /* Disable Power 3.3V/5V for PCMCIA/CF */
+-  PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
++  /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */
++  PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1);
+-  /* Why? */                   
+-  MECR = 0x09430943;
++  /* Disable Power 3.3V/5V for PCMCIA */
++  PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1;
+-  return sa1111_pcmcia_init(init);
++  if (!request_mem_region(_PCCR, 512, "PCMCIA"))
++        return -1;
++
++
++  INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) |
++             SA1111_IRQMASK_HI(S0_CD_VALID)   |
++             SA1111_IRQMASK_HI(S0_BVD1_STSCHG);
++
++  nirq = 2;
++  slots = 1;
++
++  if (!adsbitsy_smc91111_present()) {
++    /* If the SMC91111 is used CF cannot be used */
++    /* Set GPIO_A<3:2> to be outputs for CF power controller: */
++    PA_DDR &= ~(GPIO_GPIO2 | GPIO_GPIO3);
++
++    /* Disable Power 3.3V/5V for CF */
++    PA_DWR |= GPIO_GPIO2 | GPIO_GPIO3;
++
++    INTPOL1 |= SA1111_IRQMASK_HI(S1_READY_NINT) |
++               SA1111_IRQMASK_HI(S1_CD_VALID)   |
++               SA1111_IRQMASK_HI(S1_BVD1_STSCHG);
++
++    nirq = 4;
++    slots = 2;
++  }
++
++  for (i = ret = 0; i < nirq; i++) {
++        ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
++                          irqs[i].str, NULL);
++        if (ret)
++                break;
++  }
++
++  if (i < nirq) {
++        printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n",
++               irqs[i].irq, ret);
++        while (i--)
++                free_irq(irqs[i].irq, NULL);
++
++        release_mem_region(_PCCR, 16);
++  }
++
++  return ret ? -1 : slots;
+ }
+-static int
+-adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf)
++static int adsbitsy_pcmcia_shutdown(void)
++{
++
++  free_irq(S0_CD_VALID, NULL);
++  free_irq(S0_BVD1_STSCHG, NULL);
++  INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG));
++
++  if (!adsbitsy_smc91111_present()) {
++    free_irq(S1_CD_VALID, NULL);
++    free_irq(S1_BVD1_STSCHG, NULL);
++    INTPOL1 &= ~(SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG));
++  }
++
++  return 0;
++}
++
++
++static int adsbitsy_pcmcia_socket_state(struct pcmcia_state_array *state)
++{
++      unsigned long status;
++
++      if (adsbitsy_smc91111_present()) {
++              if(state->size<1) return -1;
++      }
++      else
++              if(state->size<2) return -1;
++
++      memset(state->state, 0,
++             (state->size)*sizeof(struct pcmcia_state));
++
++      status = PCSR;
++
++      state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1;
++      state->state[0].ready  = status & PCSR_S0_READY  ? 1 : 0;
++      state->state[0].bvd1   = status & PCSR_S0_BVD1   ? 1 : 0;
++      state->state[0].bvd2   = status & PCSR_S0_BVD2   ? 1 : 0;
++      state->state[0].wrprot = status & PCSR_S0_WP     ? 1 : 0;
++      state->state[0].vs_3v  = status & PCSR_S0_VS1    ? 0 : 1;
++      state->state[0].vs_Xv  = status & PCSR_S0_VS2    ? 0 : 1;
++
++      if (state->size > 1) {
++              if (adsbitsy_smc91111_present()) {
++                      // If there is SMC91111 on ADS Bitsy connector board
++                      // it returns not detect/ready/...
++                      state->state[1].detect = 0;
++                      state->state[1].ready = 0;
++                      state->state[1].bvd1 = 0;
++                      state->state[1].bvd2 = 0;
++                      state->state[1].wrprot = 0;
++                      state->state[1].vs_3v = 0;
++                      state->state[1].vs_Xv = 0;
++              }
++              else {
++                      state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1;
++                      state->state[1].ready  = status & PCSR_S1_READY  ? 1 : 0;
++                      state->state[1].bvd1   = status & PCSR_S1_BVD1   ? 1 : 0;
++                      state->state[1].bvd2   = status & PCSR_S1_BVD2   ? 1 : 0;
++                      state->state[1].wrprot = status & PCSR_S1_WP     ? 1 : 0;
++                      state->state[1].vs_3v  = status & PCSR_S1_VS1    ? 0 : 1;
++                      state->state[1].vs_Xv  = status & PCSR_S1_VS2    ? 0 : 1;
++              }
++      }
++      return 1;
++}
++
++static int adsbitsy_pcmcia_configure_socket(const struct pcmcia_configure *conf)
+ {
+   unsigned int pa_dwr_mask, pa_dwr_set;
+   int ret;
+@@ -54,10 +182,11 @@
+     switch (conf->vcc) {
+     default:
+-    case 0:   pa_dwr_set = 0;                         break;
++    case 0:   pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3;   break;
+     case 33:  pa_dwr_set = GPIO_GPIO2;                break;
+     case 50:  pa_dwr_set = GPIO_GPIO3;                break;
+     }
++    break;
+   default:
+     return -1;
+@@ -83,8 +212,8 @@
+ struct pcmcia_low_level adsbitsy_pcmcia_ops = {
+   init:                       adsbitsy_pcmcia_init,
+-  shutdown:           sa1111_pcmcia_shutdown,
+-  socket_state:               sa1111_pcmcia_socket_state,
++  shutdown:           adsbitsy_pcmcia_shutdown,
++  socket_state:               adsbitsy_pcmcia_socket_state,
+   get_irq_info:               sa1111_pcmcia_get_irq_info,
+   configure_socket:   adsbitsy_pcmcia_configure_socket,
+--- /dev/null
++++ linux-2.4.27/drivers/pcmcia/sa1100_adsbitsyplus.c
+@@ -0,0 +1,236 @@
++/*
++ * drivers/pcmcia/sa1100_adsbitsyplus.c
++ *
++ * PCMCIA implementation routines for ADS Bitsy Plus
++ *
++ * Created Feb 7, 2003 by Robert Whaley <rwhaley@applieddata.net>
++ *
++ * This file comes from sa1100_adsbitsy.c of Woojung Huh <whuh@applieddata.net>
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++
++#include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
++#include <asm/irq.h>
++
++#include "sa1100_generic.h"
++#include "sa1111_generic.h"
++
++int adsbitsy_smc91111_present(void);
++
++#ifndef       CONFIG_SMC91111
++#define adsbitsy_smc91111_present() 0
++#endif
++
++static struct irqs {
++      int irq;
++      const char *str;
++} irqs[] = {
++      { S0_CD_VALID,    "SA1111 PCMCIA card detect" },
++      { S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1"        },
++      { S1_CD_VALID,    "SA1111 CF card detect"     },
++      { S1_BVD1_STSCHG, "SA1111 CF BVD1"            },
++};
++
++#define sock0_3_3_reverse_logic() ((ADS_CPLD_IO2 & ADS_IO2_CPLD_REV_MASK) >= ADS_IO2_CPLD_REV_5_MAGIC)
++
++static int adsbitsyplus_pcmcia_init(struct pcmcia_init *init)
++{
++  int ret=0;
++  int nirq = 0;
++  int slots = 0;
++  int i;
++
++  /* Set GPIO_A<1:0> to be outputs for PCMCIA power controller: */
++  PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1);
++
++  /* Disable Power 3.3V/5V for PCMCIA */
++  if (sock0_3_3_reverse_logic())
++        PA_DWR = (PA_DWR & ~GPIO_GPIO0) | GPIO_GPIO1;
++  else
++        PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1;
++
++  if (!request_mem_region(_PCCR, 512, "PCMCIA"))
++        return -1;
++
++
++  INTPOL1 |= SA1111_IRQMASK_HI(S0_READY_NINT) |
++             SA1111_IRQMASK_HI(S0_CD_VALID)   |
++             SA1111_IRQMASK_HI(S0_BVD1_STSCHG);
++
++  nirq = 2;
++  slots = 1;
++
++  if (!adsbitsy_smc91111_present()) {
++    /* If the SMC91111 is used CF cannot be used */
++    /* Set GPIO_A<3:2> to be outputs for CF power controller: */
++    PA_DDR &= ~(GPIO_GPIO2 | GPIO_GPIO3);
++
++    /* Disable Power 3.3V/5V for CF */
++    PA_DWR |= GPIO_GPIO2 | GPIO_GPIO3;
++
++    INTPOL1 |= SA1111_IRQMASK_HI(S1_READY_NINT) |
++               SA1111_IRQMASK_HI(S1_CD_VALID)   |
++               SA1111_IRQMASK_HI(S1_BVD1_STSCHG);
++
++    nirq = 4;
++    slots = 2;
++  }
++
++  for (i = ret = 0; i < nirq; i++) {
++        ret = request_irq(irqs[i].irq, init->handler, SA_INTERRUPT,
++                          irqs[i].str, NULL);
++        if (ret)
++                break;
++  }
++
++  if (i < nirq) {
++        printk(KERN_ERR "sa1111_pcmcia: unable to grab IRQ%d (%d)\n",
++               irqs[i].irq, ret);
++        while (i--)
++                free_irq(irqs[i].irq, NULL);
++
++        release_mem_region(_PCCR, 16);
++  }
++
++  return ret ? -1 : slots;
++}
++
++static int adsbitsyplus_pcmcia_shutdown(void)
++{
++
++  free_irq(S0_CD_VALID, NULL);
++  free_irq(S0_BVD1_STSCHG, NULL);
++  INTPOL1 &= ~(SA1111_IRQMASK_HI(S0_CD_VALID) | SA1111_IRQMASK_HI(S0_BVD1_STSCHG));
++
++  if (!adsbitsy_smc91111_present()) {
++    free_irq(S1_CD_VALID, NULL);
++    free_irq(S1_BVD1_STSCHG, NULL);
++    INTPOL1 &= ~(SA1111_IRQMASK_HI(S1_CD_VALID) | SA1111_IRQMASK_HI(S1_BVD1_STSCHG));
++  }
++
++  return 0;
++}
++
++
++static int adsbitsyplus_pcmcia_socket_state(struct pcmcia_state_array *state)
++{
++      unsigned long status;
++
++      if (adsbitsy_smc91111_present()) {
++              if(state->size<1) return -1;
++      }
++      else
++              if(state->size<2) return -1;
++
++      memset(state->state, 0,
++             (state->size)*sizeof(struct pcmcia_state));
++
++      status = PCSR;
++
++      state->state[0].detect = status & PCSR_S0_DETECT ? 0 : 1;
++      state->state[0].ready  = status & PCSR_S0_READY  ? 1 : 0;
++      state->state[0].bvd1   = status & PCSR_S0_BVD1   ? 1 : 0;
++      state->state[0].bvd2   = status & PCSR_S0_BVD2   ? 1 : 0;
++      state->state[0].wrprot = status & PCSR_S0_WP     ? 1 : 0;
++      state->state[0].vs_3v  = status & PCSR_S0_VS1    ? 0 : 1;
++      state->state[0].vs_Xv  = status & PCSR_S0_VS2    ? 0 : 1;
++
++      if (state->size > 1) {
++              if (adsbitsy_smc91111_present()) {
++                      // If there is SMC91111 on ADS Bitsy connector board
++                      // it returns not detect/ready/...
++                      state->state[1].detect = 0;
++                      state->state[1].ready = 0;
++                      state->state[1].bvd1 = 0;
++                      state->state[1].bvd2 = 0;
++                      state->state[1].wrprot = 0;
++                      state->state[1].vs_3v = 0;
++                      state->state[1].vs_Xv = 0;
++              }
++              else {
++                      state->state[1].detect = status & PCSR_S1_DETECT ? 0 : 1;
++                      state->state[1].ready  = status & PCSR_S1_READY  ? 1 : 0;
++                      state->state[1].bvd1   = status & PCSR_S1_BVD1   ? 1 : 0;
++                      state->state[1].bvd2   = status & PCSR_S1_BVD2   ? 1 : 0;
++                      state->state[1].wrprot = status & PCSR_S1_WP     ? 1 : 0;
++                      state->state[1].vs_3v  = status & PCSR_S1_VS1    ? 0 : 1;
++                      state->state[1].vs_Xv  = status & PCSR_S1_VS2    ? 0 : 1;
++              }
++      }
++      return 1;
++}
++
++static int adsbitsyplus_pcmcia_configure_socket(const struct pcmcia_configure *conf)
++{
++  unsigned int pa_dwr_mask, pa_dwr_set;
++  int ret;
++
++  switch (conf->sock) {
++  case 0:
++    pa_dwr_mask = GPIO_GPIO0 | GPIO_GPIO1;
++
++    if (sock0_3_3_reverse_logic()) {
++          switch (conf->vcc) {
++          default:
++          case 0:     pa_dwr_set = GPIO_GPIO1;                break;
++          case 33:    pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1;   break;
++          case 50:    pa_dwr_set = 0;                         break;
++          }
++    } else {
++          switch (conf->vcc) {
++          default:
++          case 0:     pa_dwr_set = GPIO_GPIO0 | GPIO_GPIO1;   break;
++          case 33:    pa_dwr_set = GPIO_GPIO1;                break;
++          case 50:    pa_dwr_set = GPIO_GPIO0;                break;
++          }
++    }
++    break;
++
++  case 1:
++    pa_dwr_mask = GPIO_GPIO2 | GPIO_GPIO3;
++
++    switch (conf->vcc) {
++    default:
++    case 0:   pa_dwr_set = GPIO_GPIO2 | GPIO_GPIO3;   break;
++    case 33:  pa_dwr_set = GPIO_GPIO2;                break;
++    case 50:  pa_dwr_set = GPIO_GPIO3;                break;
++    }
++    break;
++
++  default:
++    return -1;
++  }
++
++  if (conf->vpp != conf->vcc && conf->vpp != 0) {
++    printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
++              __FUNCTION__, conf->vpp);
++    return -1;
++  }
++
++  ret = sa1111_pcmcia_configure_socket(conf);
++  if (ret == 0) {
++    unsigned long flags;
++
++    local_irq_save(flags);
++    PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
++    local_irq_restore(flags);
++  }
++
++  return ret;
++}
++
++struct pcmcia_low_level adsbitsyplus_pcmcia_ops = {
++  init:                       adsbitsyplus_pcmcia_init,
++  shutdown:           adsbitsyplus_pcmcia_shutdown,
++  socket_state:               adsbitsyplus_pcmcia_socket_state,
++  get_irq_info:               sa1111_pcmcia_get_irq_info,
++  configure_socket:   adsbitsyplus_pcmcia_configure_socket,
++
++  socket_init:                sa1111_pcmcia_socket_init,
++  socket_suspend:     sa1111_pcmcia_socket_suspend,
++};
++
+--- linux-2.4.27/drivers/pcmcia/sa1100_freebird.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_freebird.c
+@@ -67,9 +67,6 @@
+   if(state_array->size<2) return -1;
+-  memset(state_array->state, 0,
+-       (state_array->size)*sizeof(struct pcmcia_state));
+-
+   levels = LINKUP_PRS;
+ //printk("LINKUP_PRS=%x \n",levels);
+--- linux-2.4.27/drivers/pcmcia/sa1100_generic.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_generic.c
+@@ -992,10 +992,18 @@
+   if(machine_is_graphicsmaster())
+     pcmcia_low_level = &graphicsmaster_pcmcia_ops;
+ #endif
++#ifdef CONFIG_SA1100_ADSAGC
++  if(machine_is_adsagc())
++    pcmcia_low_level = &graphicsmaster_pcmcia_ops;
++#endif
+ #ifdef CONFIG_SA1100_ADSBITSY
+   if(machine_is_adsbitsy())
+     pcmcia_low_level = &adsbitsy_pcmcia_ops;
+ #endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++  if(machine_is_adsbitsyplus())
++    pcmcia_low_level = &adsbitsyplus_pcmcia_ops;
++#endif
+ #ifdef CONFIG_SA1100_STORK
+   if(machine_is_stork())
+     pcmcia_low_level = &stork_pcmcia_ops;
+@@ -1067,7 +1075,7 @@
+    * We initialize the MECR to default values here, because we are
+    * not guaranteed to see a SetIOMap operation at runtime.
+    */
+-  mecr = 0;
++  mecr = MECR;
+   clock = cpufreq_get(0);
+--- linux-2.4.27/drivers/pcmcia/sa1100_graphicsclient.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_graphicsclient.c
+@@ -3,6 +3,8 @@
+  *
+  * PCMCIA implementation routines for Graphics Client Plus
+  *
++ * Nov/14/01 Woojung
++ *    Set MECR at initializing time
+  * 9/12/01   Woojung
+  *    Turn power OFF at startup
+  * 1/31/2001 Woojung Huh
+@@ -19,11 +21,6 @@
+ #include <asm/irq.h>
+ #include "sa1100_generic.h"
+-#error This is broken!
+-
+-#define       S0_CD_IRQ               60                              // Socket 0 Card Detect IRQ
+-#define       S0_STS_IRQ              55                              // Socket 0 PCMCIA IRQ
+-
+ static volatile unsigned long *PCMCIA_Status = 
+               ((volatile unsigned long *) ADS_p2v(_ADS_CS_STATUS));
+@@ -46,20 +43,23 @@
+   *PCMCIA_Power &= ~0x03;
+   /* Register interrupts */
+-  irq = S0_CD_IRQ;
++  irq = IRQ_GRAPHICSCLIENT_S0_CD;
+   res = request_irq(irq, init->handler, SA_INTERRUPT, "PCMCIA 0 CD", NULL);
+   if (res < 0) {
+-        printk(KERN_ERR "%s: Request for IRQ %lu failed\n", __FUNCTION__, irq);
++        printk(KERN_ERR "%s: Request for IRQ %u failed\n", __FUNCTION__, irq);
+         return        -1;
+   }
++  // Nov/14/01 WH
++  MECR = 0x00000943;
++
+   return 1;                   // 1 PCMCIA Slot
+ }
+ static int gcplus_pcmcia_shutdown(void)
+ {
+   /* disable IRQs */
+-  free_irq( S0_CD_IRQ, NULL);
++  free_irq( IRQ_GRAPHICSCLIENT_S0_CD, NULL);
+   
+   /* Shutdown PCMCIA power */
+   mdelay(2);                                          // 2msec
+@@ -74,9 +74,6 @@
+   if(state_array->size<1) return -1;
+-  memset(state_array->state, 0, 
+-       (state_array->size)*sizeof(struct pcmcia_state));
+-
+   levels=*PCMCIA_Status;
+   state_array->state[0].detect=(levels & ADS_CS_ST_A_CD)?1:0;
+@@ -96,7 +93,7 @@
+               return -1;
+       if (info->sock == 0)
+-              info->irq = S0_STS_IRQ;
++              info->irq = IRQ_GRAPHICSCLIENT_S0_STS;
+       return 0;
+ }
+@@ -143,6 +140,11 @@
+   restore_flags(flags);
++  if (configure->irq)
++        enable_irq(IRQ_GRAPHICSCLIENT_S0_STS);
++  else
++        disable_irq(IRQ_GRAPHICSCLIENT_S0_STS);
++
+   return 0;
+ }
+--- linux-2.4.27/drivers/pcmcia/sa1100_graphicsmaster.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_graphicsmaster.c
+@@ -12,13 +12,13 @@
+ #include <linux/sched.h>
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
+ #include "sa1100_generic.h"
+ #include "sa1111_generic.h"
+ static int graphicsmaster_pcmcia_init(struct pcmcia_init *init)
+ {
+-  int return_val=0;
+   /* Set GPIO_A<3:0> to be outputs for PCMCIA/CF power controller: */
+   PA_DDR &= ~(GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
+@@ -26,9 +26,6 @@
+   /* Disable Power 3.3V/5V for PCMCIA/CF */
+   PA_DWR |= GPIO_GPIO0 | GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3;
+-  /* why? */
+-  MECR = 0x09430943;
+-
+   return sa1111_pcmcia_init(init);
+ }
+@@ -59,6 +56,10 @@
+     case 33:  pa_dwr_set = GPIO_GPIO3;                break;
+     case 50:  pa_dwr_set = GPIO_GPIO2;                break;
+     }
++    break;
++
++  default:
++    return -1;
+   }
+   if (conf->vpp != conf->vcc && conf->vpp != 0) {
+--- linux-2.4.27/drivers/pcmcia/sa1100_h3600.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_h3600.c
+@@ -29,6 +29,9 @@
+       set_GPIO_IRQ_edge(GPIO_H3600_PCMCIA_IRQ0 | GPIO_H3600_PCMCIA_IRQ1,
+                         GPIO_FALLING_EDGE);
++      set_GPIO_IRQ_edge(GPIO_H3600_PCMCIA_CD0 | GPIO_H3600_PCMCIA_CD1,
++                        GPIO_NO_EDGES);
++
+       /*
+        * Register interrupts
+        */
+--- linux-2.4.27/drivers/pcmcia/sa1100_jornada720.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_jornada720.c
+@@ -8,6 +8,7 @@
+ #include <linux/sched.h>
+ #include <asm/hardware.h>
++#include <asm/hardware/sa1111.h>
+ #include "sa1100_generic.h"
+ #include "sa1111_generic.h"
+@@ -88,7 +89,7 @@
+     local_irq_save(flags);
+     PA_DWR = (PA_DWR & ~pa_dwr_mask) | pa_dwr_set;
+-    locla_irq_restore(flags);
++    local_irq_restore(flags);
+   }
+   return ret;
+--- linux-2.4.27/drivers/pcmcia/sa1100_pangolin.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_pangolin.c
+@@ -53,9 +53,6 @@
+   if(state_array->size<2) return -1;
+-  memset(state_array->state, 0, 
+-       (state_array->size)*sizeof(struct pcmcia_state));
+-
+   levels=GPLR;
+ #ifndef CONFIG_SA1100_PANGOLIN_PCMCIA_IDE
+   state_array->state[1].detect=((levels & GPIO_PCMCIA_CD)==0)?1:0;
+--- linux-2.4.27/drivers/pcmcia/sa1100_shannon.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_shannon.c
+@@ -53,9 +53,6 @@
+ {
+       unsigned long levels;
+-      memset(state_array->state, 0,
+-             state_array->size * sizeof(struct pcmcia_state));
+-
+       levels = GPLR;
+       state_array->state[0].detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1;
+--- linux-2.4.27/drivers/pcmcia/sa1100_simpad.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_simpad.c
+@@ -63,9 +63,6 @@
+   if(state_array->size<2) return -1;
+-  memset(state_array->state, 0, 
+-       (state_array->size)*sizeof(struct pcmcia_state));
+-
+   levels=GPLR;
+   state_array->state[1].detect=((levels & GPIO_CF_CD)==0)?1:0;
+--- linux-2.4.27/drivers/pcmcia/sa1100_stork.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_stork.c
+@@ -79,9 +79,6 @@
+         if(state_array->size<2) return -1;
+-        memset(state_array->state, 0, 
+-               (state_array->size)*sizeof(struct pcmcia_state));
+-
+         levels=GPLR;
+       if (debug > 1)
+--- linux-2.4.27/drivers/pcmcia/sa1100_yopy.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/pcmcia/sa1100_yopy.c
+@@ -73,9 +73,6 @@
+       if (state_array->size != 1)
+               return -1;
+-      memset(state_array->state, 0,
+-             state_array->size * sizeof(struct pcmcia_state));
+-
+       levels = GPLR;
+       state_array->state[0].detect = (levels & GPIO_CF_CD)    ? 0 : 1;
+--- /dev/null
++++ linux-2.4.27/drivers/pld/Makefile
+@@ -0,0 +1,28 @@
++#
++# Makefile for the kernel pld 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: $
++#
++
++O_TARGET := pld.o
++
++export-objs   := pld_hotswap.o
++obj-y         :=
++obj-m         :=
++obj-n         :=
++obj-          :=
++
++obj-$(CONFIG_PLD)              += pld_epxa.o
++obj-$(CONFIG_PLD_HOTSWAP)      += pld_hotswap.o
++
++include $(TOPDIR)/Rules.make
++
++fastdep:
++
+--- /dev/null
++++ linux-2.4.27/drivers/pld/pld_epxa.c
+@@ -0,0 +1,375 @@
++
++/*
++ *  drivers/char/epxapld.c
++ *
++ *  Copyright (C) 2001 Altera Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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/errno.h>
++#include <linux/fs.h>
++#include <linux/slab.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/arch/excalibur.h>
++#include <asm/arch/hardware.h>
++#define PLD_CONF00_TYPE (volatile unsigned int *)
++#define MODE_CTRL00_TYPE (volatile unsigned int *)
++//#define DEBUG(x) x
++#define DEBUG(x)
++
++#include <asm/arch/mode_ctrl00.h>
++#include <asm/arch/pld_conf00.h>
++#ifdef CONFIG_PLD_HOTSWAP
++#include <linux/pld/pld_hotswap.h>
++#endif
++#include <linux/pld/pld_epxa.h>
++
++/*
++ *    Macros
++ */
++
++
++#define PLD_BASE (IO_ADDRESS(EXC_PLD_CONFIG00_BASE))
++#define CLOCK_DIV_RATIO ((1 + EXC_AHB2_CLK_FREQUENCY/32000000) & CONFIG_CONTROL_CLOCK_RATIO_MSK)
++/*
++ *    STRUCTURES
++ */
++
++
++struct  pld_sbihdr{
++      unsigned int fpos;
++      unsigned int temp;
++};
++
++static DECLARE_MUTEX(pld_sem);
++
++
++static void lock_pld (void)
++{
++      /* Lock the pld i/f  */
++      unsigned int tmp;
++
++      tmp = readl(CONFIG_CONTROL(PLD_BASE));
++      tmp |= CONFIG_CONTROL_LK_MSK;
++
++      writel(tmp,CONFIG_CONTROL(PLD_BASE));
++}
++
++static void unlock_excalibur_pld (void)
++{
++      /* Unlock the pld i/f */
++
++      if (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK ){
++              writel(CONFIG_UNLOCK_MAGIC, CONFIG_UNLOCK(PLD_BASE));
++              while (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK);
++        }
++}
++
++
++static int place_pld_into_configure_mode (void)
++{
++      unsigned int tmp;
++
++
++      if( readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK ){
++              /*
++               *      Already being configured!!!
++               */
++              printk(KERN_WARNING "pld0: Device already being configured!\n");
++              return -EBUSY;
++      }
++
++      /* Set up the config clock */
++
++      writel(CLOCK_DIV_RATIO,CONFIG_CONTROL_CLOCK(PLD_BASE));
++      while(readl(CONFIG_CONTROL_CLOCK(PLD_BASE))
++            !=CLOCK_DIV_RATIO);
++      /* Tell the device we wish to configure it */
++      tmp = readl(CONFIG_CONTROL(PLD_BASE));
++      tmp |= CONFIG_CONTROL_CO_MSK;
++      writel(tmp,CONFIG_CONTROL(PLD_BASE));
++
++
++      /*
++       *      Wait for the busy bit to clear then check for errors.
++       */
++
++      while((tmp=readl(CONFIG_CONTROL(PLD_BASE))&CONFIG_CONTROL_B_MSK ));
++
++      if ( tmp & CONFIG_CONTROL_E_MSK ){
++              if ( tmp & CONFIG_CONTROL_ES_0_MSK ){
++                      /* Already being programmed via JTAG */
++                      printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n");
++                      return -EBUSY;
++
++              }
++              if ( tmp & CONFIG_CONTROL_ES_1_MSK ){
++                      /* No config clock configured */
++                      printk(KERN_ERR "pld0:No config clock!\n");
++                      BUG();
++                      return -EBUSY;
++              }
++              if ( tmp & CONFIG_CONTROL_ES_2_MSK ){
++                      /* Already being programmed via External device */
++                      printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n");
++                      return -EBUSY;
++              }
++      }
++
++      return 0;
++}
++
++
++static int write_pld_data_word(unsigned int config_data)
++{
++      unsigned int tmp;
++
++      do{
++              tmp = readl(CONFIG_CONTROL(PLD_BASE));
++      }
++        while ( ( tmp & CONFIG_CONTROL_B_MSK ) &&
++              !( tmp & CONFIG_CONTROL_E_MSK ));
++
++        if ( tmp & CONFIG_CONTROL_E_MSK ){
++              printk("pld0: Error writing pld data, CONFIG_CONTROL=%#x\n",tmp);
++
++              return -EILSEQ;
++      }
++
++        writel(config_data,CONFIG_CONTROL_DATA(PLD_BASE));
++      return 0;
++
++}
++
++
++static int wait_for_device_to_configure (void)
++{
++      int i=0x10000;
++
++      while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK);
++
++      /*
++       * Wait for the config bit (CO) to go low, indicating that everything
++       * is Ok. If it doesn't, assume that is screwed up somehow and
++       * clear the CO bit to abort the configuration.
++       */
++
++      while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK);
++
++      while (i&&(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK)){
++              i--;
++      }
++
++      if (!i){
++              writel(0,CONFIG_CONTROL(PLD_BASE));
++              printk(KERN_WARNING "pld0: Invalid PLD config data\n");
++              return -EILSEQ;
++      }
++
++      return 0;
++}
++
++
++
++static int pld_open(struct inode* inode, struct file *filep)
++{
++
++      struct pld_sbihdr* sbihdr;
++
++      /* Check the device minor number */
++      if(minor(inode->i_rdev)){
++              DEBUG(printk("pld0: minor=%d",minor(inode->i_rdev));)
++                      return -ENODEV;
++      }
++
++      /* Create our private data and link it to the file structure */
++      sbihdr=kmalloc(sizeof(struct pld_sbihdr),GFP_KERNEL);
++
++      if(!sbihdr)
++              return -ENOMEM;
++
++      filep->private_data=sbihdr;
++
++      sbihdr->fpos=0;
++      sbihdr->temp=0;
++      return 0;
++}
++
++static int pld_release(struct inode* inode, struct file *filep){
++      int ret_code;
++
++      kfree(filep->private_data);
++              ret_code=wait_for_device_to_configure();
++      lock_pld();
++      return ret_code;
++}
++
++static ssize_t pld_write(struct file* filep, const char* data, size_t count, loff_t* ppos){
++
++      struct pld_sbihdr* sbihdr=filep->private_data;
++      int bytes_left=count;
++      int result;
++      DEBUG(int i=0);
++
++
++      /* Can't seek (pwrite) on pld.  */
++      if (ppos != &filep->f_pos)
++              return -ESPIPE;
++
++
++      /* Check access to the whole are in one go */
++      if(!access_ok(VERIFY_READ,(const void*)data, count)){
++              return -EFAULT;
++      }
++
++      /*
++       * We now lock against writes.
++       */
++      if (down_interruptible(&pld_sem))
++              return -ERESTARTSYS;
++
++      if(!sbihdr->fpos){
++              /*
++               * unlock the pld and place in configure mode
++               */
++              unlock_excalibur_pld();
++              result=place_pld_into_configure_mode();
++              if(result)
++                      return result;
++      }
++      DEBUG(printk("data= %#x count=%#x 0ffset=%#x\n",*data, count, *ppos));
++
++      while(bytes_left){
++              char tmp;
++              __get_user(tmp,data);
++              /* read our header ! */
++              sbihdr->temp|=tmp << (8*(sbihdr->fpos&3));
++              if((sbihdr->fpos&3)==3){
++                      if(write_pld_data_word(sbihdr->temp))
++                      {
++                              DEBUG(printk("pos=%d\n",sbihdr->fpos);)
++                                      return -EILSEQ;
++                      }
++                      DEBUG(if(i<10){)
++                            DEBUG(printk("fpos2 :%#x data=%#x\n",sbihdr->fpos,sbihdr->temp));
++                            DEBUG(i++);
++                            DEBUG(});
++                      sbihdr->temp=0;
++                      DEBUG(words_written++);
++              }
++              sbihdr->fpos++;
++              data++;
++              bytes_left--;
++      }
++
++      up(&pld_sem);
++      return (count);
++}
++
++int pld_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
++{
++
++      switch(cmd){
++
++#ifdef CONFIG_PLD_HOTSWAP
++      case PLD_IOC_ADD_PLD_DEV:
++      {
++              struct pldhs_dev_desc desc;
++              struct pldhs_dev_info info;
++              char *name;
++              void *data;
++              int result=0;
++
++              result=copy_from_user(&desc,(const void*)arg,sizeof(struct pldhs_dev_desc));
++              if(result)
++                      return -EFAULT;
++              result=copy_from_user(&info,(const void*)desc.info,sizeof(struct pldhs_dev_info));
++              if(result)
++                      return -EFAULT;
++              name=kmalloc(info.nsize,GFP_KERNEL);
++              if(!name)
++                      return -ENOMEM;
++
++              result=copy_from_user(name,(const void*)desc.name,info.nsize);
++              if(result){
++                      result=-EFAULT;
++                      goto ioctl_out;
++              }
++
++              data=kmalloc(info.pssize,GFP_KERNEL);
++              if(!data){
++                      result=-ENOMEM;
++                      goto ioctl_out;
++              }
++
++              result=copy_from_user(data,(const void*)desc.data,info.pssize);
++              if(result){
++                      result=-EFAULT;
++                      goto ioctl_out1;
++              }
++              result=pldhs_add_device(&info,name,data);
++
++      ioctl_out1:
++              kfree(data);
++      ioctl_out:
++              kfree(name);
++              return result;
++
++      }
++
++      case PLD_IOC_REMOVE_PLD_DEVS:
++              pldhs_remove_devices();
++              return 0;
++
++      case PLD_IOC_SET_INT_MODE:
++              if(cmd==3){
++                      printk(KERN_INFO "Interrupt mode set to 3 (Six individual interrupts)\n");
++                      return 0;
++              }else{
++                      printk(KERN_INFO "There is no interrupt handler available for this mode (%d). You will need to add one\n to implement whatever scheme you require\n");
++                      return -EACCES;
++              }
++#endif
++      default:
++              return -ENOTTY;
++      }
++}
++
++
++static struct file_operations pld_fops={
++      write:      pld_write,
++      ioctl:      pld_ioctl,
++      open:       pld_open,
++      release:    pld_release
++};
++
++static int __init pld_init(void){
++      int major;
++      major=register_chrdev(0,"pld",&pld_fops);
++      printk(KERN_INFO "Using PLD major num=%d\n",major);
++      if (major<0){
++              return major;
++      }
++      return 0;
++}
++
++__initcall(pld_init);
+--- /dev/null
++++ linux-2.4.27/drivers/pld/pld_hotswap.c
+@@ -0,0 +1,188 @@
++/*
++ *  linux/drivers/pld/pld_hotswap.c
++ *
++ *  Pld driver for Altera EPXA Excalibur devices
++ *
++ *
++ *  Copyright 2001 Altera Corporation (cdavies@altera.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 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: $
++ *
++ */
++
++/*
++ * pld_hotswap ops contains the basic operation required for adding
++ * and removing devices from the system.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/pagemap.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/init.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/list.h>
++#include <asm/uaccess.h>
++#include <linux/pld/pld_hotswap.h>
++
++
++static struct pld_hotswap_ops pldhs_driver_list={
++       list: LIST_HEAD_INIT(pldhs_driver_list.list),
++       name: "",
++};
++
++static spinlock_t list_lock=SPIN_LOCK_UNLOCKED;
++
++
++
++static struct pld_hotswap_ops *  pldhs_find_driver(char* name)
++{
++       struct pld_hotswap_ops * ptr;
++       struct list_head *list_pos;
++
++       spin_lock(&list_lock);
++       list_for_each(list_pos,&pldhs_driver_list.list){
++               ptr=(struct pld_hotswap_ops *)list_pos;
++               if(!strcmp(name, ptr->name)){
++                       spin_unlock(&list_lock);
++                       return ptr;
++
++               }
++       }
++       spin_unlock(&list_lock);
++       return 0;
++}
++
++
++
++int pldhs_register_driver(struct pld_hotswap_ops *drv)
++{
++
++       /* Check that the device is not already on the list
++        * if so, do nothing */
++       if(pldhs_find_driver(drv->name)){
++               return 0;
++       }
++
++       printk(KERN_INFO "PLD: Registering hotswap driver %s\n",drv->name);
++       /* Add this at the start of the list */
++       spin_lock(&list_lock);
++       list_add((struct list_head*)drv,&pldhs_driver_list.list);
++       spin_unlock(&list_lock);
++
++       return 0;
++}
++
++int pldhs_unregister_driver(char *name)
++{
++       struct pld_hotswap_ops *ptr;
++
++       ptr=pldhs_find_driver(name);
++       if(!ptr){
++               return -ENODEV;
++       }
++
++       printk(KERN_INFO "PLD: Unregistering hotswap driver%s\n",name);
++       spin_lock(&list_lock);
++       list_del((struct list_head*)ptr);
++       spin_unlock(&list_lock);
++
++       return 0;
++}
++
++int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data)
++{
++       struct pld_hotswap_ops * ptr;
++
++       ptr=pldhs_find_driver(drv_name);
++
++       if(!ptr){
++               /* try requesting this module*/
++               request_module(drv_name);
++               /* is the driver there now? */
++               ptr=pldhs_find_driver(drv_name);
++               if(!ptr){
++                       printk("pld hotswap: Failed to load a driver for %s\n",drv_name);
++                       return -ENODEV;
++               }
++       }
++
++       if(!ptr->add_device){
++               printk(KERN_WARNING "pldhs: no add_device() function for driver %s\n",drv_name);
++               return 0;
++       }
++
++       return ptr->add_device(dev_info,dev_ps_data);
++}
++
++int pldhs_remove_devices(void)
++{
++       struct list_head *list_pos;
++       struct pld_hotswap_ops * ptr;
++
++
++       spin_lock(&list_lock);
++       list_for_each(list_pos,&pldhs_driver_list.list){
++               ptr=(struct pld_hotswap_ops *)list_pos;
++               if(ptr->remove_devices)
++                       ptr->remove_devices();
++
++       }
++       spin_unlock(&list_lock);
++
++       return 0;
++}
++
++#ifdef CONFIG_PROC_FS
++int pldhs_read_proc(char* buf,char** start,off_t offset,int count,int *eof,void *data){
++
++
++       struct list_head *list_pos;
++       struct pld_hotswap_ops * ptr;
++       int i,len=0;
++
++       *start=buf;
++       spin_lock(&list_lock);
++       list_for_each(list_pos,&pldhs_driver_list.list){
++               ptr=(struct pld_hotswap_ops *)list_pos;
++               if(ptr->proc_read){
++                       i=ptr->proc_read(buf,start,offset,count,eof,data);
++                       count-=i;
++                       len+=i;
++                       *start+=i;
++               }
++       }
++       spin_unlock(&list_lock);
++
++       *start=NULL;
++       *eof=1;
++       return len;
++}
++
++void __init pldhs_init(void){
++       create_proc_read_entry("pld",0,NULL,pldhs_read_proc,NULL);
++}
++
++__initcall(pldhs_init);
++#endif
++
++EXPORT_SYMBOL(pldhs_register_driver);
++EXPORT_SYMBOL(pldhs_unregister_driver);
+--- linux-2.4.27/drivers/scsi/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/Makefile
+@@ -21,7 +21,7 @@
+ O_TARGET := scsidrv.o
+-export-objs   := scsi_syms.o 53c700.o libata-core.o
++export-objs   := scsi_syms.o 53c700.o scsi_error.o libata-core.o
+ mod-subdirs   := pcmcia ../acorn/scsi
+--- linux-2.4.27/drivers/scsi/scsi.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi.c
+@@ -1334,14 +1334,10 @@
+  */
+ int scsi_retry_command(Scsi_Cmnd * SCpnt)
+ {
+-      memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
+-             sizeof(SCpnt->data_cmnd));
+-      SCpnt->request_buffer = SCpnt->buffer;
+-      SCpnt->request_bufflen = SCpnt->bufflen;
+-      SCpnt->use_sg = SCpnt->old_use_sg;
+-      SCpnt->cmd_len = SCpnt->old_cmd_len;
+-      SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
+-      SCpnt->underflow = SCpnt->old_underflow;
++      /*
++       * Restore the SCSI command state.
++       */
++      scsi_setup_cmd_retry(SCpnt);
+         /*
+          * Zero the sense information from the last time we tried
+--- linux-2.4.27/drivers/scsi/scsi.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi.h
+@@ -465,6 +465,7 @@
+                                  int sectors);
+ extern struct Scsi_Device_Template *scsi_get_request_dev(struct request *);
+ extern int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt);
++extern void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt);
+ extern int scsi_insert_special_cmd(Scsi_Cmnd * SCpnt, int);
+ extern void scsi_io_completion(Scsi_Cmnd * SCpnt, int good_sectors,
+                              int block_sectors);
+@@ -588,6 +589,7 @@
+       unsigned changed:1;     /* Data invalid due to media change */
+       unsigned busy:1;        /* Used to prevent races */
+       unsigned lockable:1;    /* Able to prevent media removal */
++      unsigned locked:1;      /* Media removal disabled */
+       unsigned borken:1;      /* Tell the Seagate driver to be 
+                                * painfully slow on this device */
+       unsigned tagged_supported:1;    /* Supports SCSI-II tagged queuing */
+--- linux-2.4.27/drivers/scsi/scsi_dma.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi_dma.c
+@@ -30,6 +30,15 @@
+ typedef unsigned char FreeSectorBitmap;
+ #elif SECTORS_PER_PAGE <= 32
+ typedef unsigned int FreeSectorBitmap;
++#elif SECTORS_PER_PAGE <= 64
++#if 0
++typedef unsigned long long FreeSectorBitmap;
++#else
++typedef struct {
++      unsigned long l,h;
++} FreeSectorBitmap;
++#define LARGE_MALLOC
++#endif
+ #else
+ #error You lose.
+ #endif
+@@ -69,6 +78,7 @@
+  *              to allocate more memory in order to be able to write the
+  *              data to disk, you would wedge the system.
+  */
++#ifndef LARGE_MALLOC
+ void *scsi_malloc(unsigned int len)
+ {
+       unsigned int nbits, mask;
+@@ -167,6 +177,97 @@
+       panic("scsi_free:Bad offset");
+ }
++#else
++
++void *scsi_malloc(unsigned int len)
++{
++      unsigned int nbits;
++      unsigned long maskl, maskh, flags;
++      FreeSectorBitmap *fsb;
++      int i;
++
++      if (len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
++              return NULL;
++
++      save_flags_cli (flags);
++      nbits = len >> 9;
++      if (nbits < 32) {
++              maskl = (1 << nbits) - 1;
++              maskh = 0;
++      } else {
++              maskl = (unsigned long)-1;
++              maskh = (1 << (nbits - 32)) - 1;
++      }
++
++      fsb = dma_malloc_freelist;
++
++      for (i = 0; i < dma_sectors / SECTORS_PER_PAGE; i++) {
++              unsigned long mml, mmh;
++              int j;
++              mml = maskl;
++              mmh = maskh;
++              j = 0;
++              do {
++                      if ((fsb->l & mml) == 0 && (fsb->h & mmh) == 0) {
++                              fsb->h |= mmh;
++                              fsb->l |= mml;
++                              restore_flags (flags);
++                              scsi_dma_free_sectors -= nbits;
++#ifdef DEBUG
++                              printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9));
++#endif
++                              return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
++                      }
++                      mmh = (mmh << 1) | (mml >> 31);
++                      mml <<= 1;
++                      j++;
++              } while (!(mmh & (1 << 31)));
++              fsb ++;
++      }
++      return NULL;  /* Nope.  No more */
++}
++
++int scsi_free(void *obj, unsigned int len)
++{
++      unsigned int page, sector, nbits;
++      unsigned long maskl, maskh, flags;
++
++#ifdef DEBUG
++      printk("scsi_free %p %d\n",obj, len);
++#endif
++
++      for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
++              unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
++              if ((unsigned long) obj >= page_addr &&
++                  (unsigned long) obj < page_addr + PAGE_SIZE) {
++                      sector = (((unsigned long) obj) - page_addr) >> 9;
++                      nbits = len >> 9;
++                      if (nbits < 32) {
++                              maskl = (1 << nbits) - 1;
++                              maskh = 0;
++                      } else {
++                              maskl = (unsigned long)-1;
++                              maskh = (1 << (nbits - 32)) - 1;
++                      }
++                      if ((sector + nbits) > SECTORS_PER_PAGE)
++                              panic ("scsi_free:Bad memory alignment");
++                      maskh = (maskh << sector) | (maskl >> (32 - sector));
++                      maskl = maskl << sector;
++                      save_flags_cli(flags);
++                      if (((dma_malloc_freelist[page].l & maskl) != maskl) ||
++                          ((dma_malloc_freelist[page].h & maskh) != maskh))
++                              panic("scsi_free:Trying to free unused memory");
++                      scsi_dma_free_sectors += nbits;
++                      dma_malloc_freelist[page].l &= ~maskl;
++                      dma_malloc_freelist[page].h &= ~maskh;
++                      restore_flags(flags);
++                      return 0;
++              }
++      }
++      panic("scsi_free:Bad offset");
++}
++#endif
++
+ /*
+  * Function:    scsi_resize_dma_pool
+--- linux-2.4.27/drivers/scsi/scsi_error.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi_error.c
+@@ -35,6 +35,8 @@
+ #include "hosts.h"
+ #include "constants.h"
++#include <scsi/scsi_ioctl.h> /* grr */
++
+ /*
+  * We must always allow SHUTDOWN_SIGS.  Even if we are not a module,
+  * the host drivers that we are using may be loaded as modules, and
+@@ -49,6 +51,13 @@
+  */
+ #define SHUTDOWN_SIGS (sigmask(SIGHUP))
++/*
++ * The number of times we retry a REQUEST SENSE and TEST UNIT READY
++ * respectively.  This is arbitary.
++ */
++#define SENSE_RETRIES 5
++#define TUR_RETRIES   5
++
+ #ifdef DEBUG
+ #define SENSE_TIMEOUT SCSI_TIMEOUT
+ #define ABORT_TIMEOUT SCSI_TIMEOUT
+@@ -373,16 +382,12 @@
+  */
+ STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt)
+ {
+-      memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
+-             sizeof(SCpnt->data_cmnd));
+-      SCpnt->request_buffer = SCpnt->buffer;
+-      SCpnt->request_bufflen = SCpnt->bufflen;
+-      SCpnt->use_sg = SCpnt->old_use_sg;
+-      SCpnt->cmd_len = SCpnt->old_cmd_len;
+-      SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
+-      SCpnt->underflow = SCpnt->old_underflow;
++      int tries = 0;
+-      scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command);
++      do {
++              scsi_setup_cmd_retry(SCpnt);
++              scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command);
++      } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SCpnt->allowed);
+       /*
+        * Hey, we are done.  Let's look to see what happened.
+@@ -409,16 +414,10 @@
+       static unsigned char generic_sense[6] =
+       {REQUEST_SENSE, 0, 0, 0, 255, 0};
+       unsigned char scsi_result0[256], *scsi_result = NULL;
+-      int saved_result;
++      int saved_result, tries;
+       ASSERT_LOCK(&io_request_lock, 0);
+-      memcpy((void *) SCpnt->cmnd, (void *) generic_sense,
+-             sizeof(generic_sense));
+-
+-      if (SCpnt->device->scsi_level <= SCSI_2)
+-              SCpnt->cmnd[1] = SCpnt->lun << 5;
+-
+       scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma)
+           ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA);
+@@ -426,24 +425,41 @@
+               printk("cannot allocate scsi_result in scsi_request_sense.\n");
+               return FAILED;
+       }
+-      /*
+-       * Zero the sense buffer.  Some host adapters automatically always request
+-       * sense, so it is not a good idea that SCpnt->request_buffer and
+-       * SCpnt->sense_buffer point to the same address (DB).
+-       * 0 is not a valid sense code. 
+-       */
+-      memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+-      memset((void *) scsi_result, 0, 256);
+       saved_result = SCpnt->result;
+-      SCpnt->request_buffer = scsi_result;
+-      SCpnt->request_bufflen = 256;
+-      SCpnt->use_sg = 0;
+-      SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+-      SCpnt->sc_data_direction = SCSI_DATA_READ;
+-      SCpnt->underflow = 0;
+-      scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT);
++      tries = 0;
++      do {
++              memcpy((void *) SCpnt->cmnd, (void *) generic_sense,
++                     sizeof(generic_sense));
++
++              if (SCpnt->device->scsi_level <= SCSI_2)
++                      SCpnt->cmnd[1] = SCpnt->lun << 5;
++
++              /*
++               * Zero the sense buffer.  Some host adapters automatically
++               * always request sense, so it is not a good idea that
++               * SCpnt->request_buffer and SCpnt->sense_buffer point to
++               * the same address (DB).  0 is not a valid sense code. 
++               */
++              memset((void *) SCpnt->sense_buffer, 0,
++                     sizeof(SCpnt->sense_buffer));
++              memset((void *) scsi_result, 0, 256);
++
++              SCpnt->request_buffer = scsi_result;
++              SCpnt->request_bufflen = 256;
++              SCpnt->use_sg = 0;
++              SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
++              SCpnt->sc_data_direction = SCSI_DATA_READ;
++              SCpnt->underflow = 0;
++
++              scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT);
++              /*
++               * If the SCSI device responded with "logical unit
++               * is in process of becoming ready", we need to
++               * retry this command.
++               */
++      } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < SENSE_RETRIES);
+       /* Last chance to have valid sense data */
+       if (!scsi_sense_valid(SCpnt))
+@@ -458,15 +474,8 @@
+        * When we eventually call scsi_finish, we really wish to complete
+        * the original request, so let's restore the original data. (DB)
+        */
+-      memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
+-             sizeof(SCpnt->data_cmnd));
+       SCpnt->result = saved_result;
+-      SCpnt->request_buffer = SCpnt->buffer;
+-      SCpnt->request_bufflen = SCpnt->bufflen;
+-      SCpnt->use_sg = SCpnt->old_use_sg;
+-      SCpnt->cmd_len = SCpnt->old_cmd_len;
+-      SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
+-      SCpnt->underflow = SCpnt->old_underflow;
++      scsi_setup_cmd_retry(SCpnt);
+       /*
+        * Hey, we are done.  Let's look to see what happened.
+@@ -484,40 +493,42 @@
+ {
+       static unsigned char tur_command[6] =
+       {TEST_UNIT_READY, 0, 0, 0, 0, 0};
++      int tries = 0;
+-      memcpy((void *) SCpnt->cmnd, (void *) tur_command,
+-             sizeof(tur_command));
++      do {
++              memcpy((void *) SCpnt->cmnd, (void *) tur_command,
++                     sizeof(tur_command));
+-      if (SCpnt->device->scsi_level <= SCSI_2)
+-              SCpnt->cmnd[1] = SCpnt->lun << 5;
++              if (SCpnt->device->scsi_level <= SCSI_2)
++                      SCpnt->cmnd[1] = SCpnt->lun << 5;
+-      /*
+-       * Zero the sense buffer.  The SCSI spec mandates that any
+-       * untransferred sense data should be interpreted as being zero.
+-       */
+-      memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
++              /*
++               * Zero the sense buffer.  The SCSI spec mandates that any
++               * untransferred sense data should be interpreted as being zero.
++               */
++              memset((void *) SCpnt->sense_buffer, 0,
++                     sizeof(SCpnt->sense_buffer));
+-      SCpnt->request_buffer = NULL;
+-      SCpnt->request_bufflen = 0;
+-      SCpnt->use_sg = 0;
+-      SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+-      SCpnt->underflow = 0;
+-      SCpnt->sc_data_direction = SCSI_DATA_NONE;
++              SCpnt->request_buffer = NULL;
++              SCpnt->request_bufflen = 0;
++              SCpnt->use_sg = 0;
++              SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
++              SCpnt->underflow = 0;
++              SCpnt->sc_data_direction = SCSI_DATA_NONE;
+-      scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT);
++              scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT);
++              /*
++               * If the SCSI device responded with "logical unit
++               * is in process of becoming ready", we need to
++               * retry this command.
++               */
++      } while (SCpnt->eh_state == NEEDS_RETRY && tries++ < TUR_RETRIES);
+       /*
+        * When we eventually call scsi_finish, we really wish to complete
+        * the original request, so let's restore the original data. (DB)
+        */
+-      memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
+-             sizeof(SCpnt->data_cmnd));
+-      SCpnt->request_buffer = SCpnt->buffer;
+-      SCpnt->request_bufflen = SCpnt->bufflen;
+-      SCpnt->use_sg = SCpnt->old_use_sg;
+-      SCpnt->cmd_len = SCpnt->old_cmd_len;
+-      SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
+-      SCpnt->underflow = SCpnt->old_underflow;
++      scsi_setup_cmd_retry(SCpnt);
+       /*
+        * Hey, we are done.  Let's look to see what happened.
+@@ -577,7 +588,6 @@
+       host = SCpnt->host;
+-      retry:
+       /*
+        * We will use a queued command if possible, otherwise we will emulate the
+        * queuing and calling of completion function ourselves.
+@@ -660,14 +670,13 @@
+               SCSI_LOG_ERROR_RECOVERY(3,
+                       printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret));
+               switch (ret) {
+-              case SUCCESS:
+-                      SCpnt->eh_state = SUCCESS;
+-                      break;
+-              case NEEDS_RETRY:
+-                      goto retry;
+-              case FAILED:
+               default:
+-                      SCpnt->eh_state = FAILED;
++                      ret = FAILED;
++                      /*FALLTHROUGH*/
++              case FAILED:
++              case NEEDS_RETRY:
++              case SUCCESS:
++                      SCpnt->eh_state = ret;
+                       break;
+               }
+       } else {
+@@ -1208,6 +1217,82 @@
+ /*
++ * Function:  scsi_eh_lock_done
++ *
++ * Purpose:   free the command and request structures associated
++ *            with the error handlers door lock request
++ *
++ * Arguments: SCpnt - the SCSI command block for the door lock request.
++ *
++ * Returns:   Nothing
++ *
++ * Notes:     We completed the asynchronous door lock request, and
++ *            it has either locked the door or failed.  We must free
++ *            the command structures associated with this request.
++ */
++static void scsi_eh_lock_done(struct scsi_cmnd *SCpnt)
++{
++      struct scsi_request *SRpnt = SCpnt->sc_request;
++
++      SCpnt->sc_request = NULL;
++      SRpnt->sr_command = NULL;
++
++      scsi_release_command(SCpnt);
++      scsi_release_request(SRpnt);
++}
++
++
++/*
++ * Function:  scsi_eh_lock_door
++ *
++ * Purpose:   Prevent medium removal for the specified device
++ *
++ * Arguments: dev - SCSI device to prevent medium removal
++ *
++ * Locking:   We must be called from process context;
++ *            scsi_allocate_request() may sleep.
++ *
++ * Returns:   Nothing
++ *
++ * Notes:     We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request
++ *            on the head of the devices request queue, and continue.
++ *
++ * Bugs:      scsi_allocate_request() may sleep waiting for existing
++ *            requests to be processed.  However, since we haven't
++ *            kicked off any request processing for this host, this
++ *            may deadlock.
++ *
++ *            If scsi_allocate_request() fails for what ever reason,
++ *            we completely forget to lock the door.
++ */
++STATIC void scsi_eh_lock_door(struct scsi_device *dev)
++{
++      struct scsi_request *SRpnt = scsi_allocate_request(dev);
++
++      if (SRpnt == NULL) {
++              /* what now? */
++              return;
++      }
++
++      SRpnt->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL;
++      SRpnt->sr_cmnd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0;
++      SRpnt->sr_cmnd[2] = 0;
++      SRpnt->sr_cmnd[3] = 0;
++      SRpnt->sr_cmnd[4] = SCSI_REMOVAL_PREVENT;
++      SRpnt->sr_cmnd[5] = 0;
++      SRpnt->sr_data_direction = SCSI_DATA_NONE;
++      SRpnt->sr_bufflen = 0;
++      SRpnt->sr_buffer = NULL;
++      SRpnt->sr_allowed = 5;
++      SRpnt->sr_done = scsi_eh_lock_done;
++      SRpnt->sr_timeout_per_command = 10 * HZ;
++      SRpnt->sr_cmd_len = COMMAND_SIZE(SRpnt->sr_cmnd[0]);
++
++      scsi_insert_special_req(SRpnt, 1);
++}
++
++
++/*
+  * Function:  scsi_restart_operations
+  *
+  * Purpose:     Restart IO operations to the specified host.
+@@ -1229,6 +1314,15 @@
+       ASSERT_LOCK(&io_request_lock, 0);
+       /*
++       * If the door was locked, we need to insert a door lock request
++       * onto the head of the SCSI request queue for the device.  There
++       * is no point trying to lock the door of an off-line device.
++       */
++      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next)
++              if (SDpnt->online && SDpnt->locked)
++                      scsi_eh_lock_door(SDpnt);
++
++      /*
+        * Next free up anything directly waiting upon the host.  This will be
+        * requests for character device operations, and also for ioctls to queued
+        * block devices.
+@@ -1248,8 +1342,7 @@
+               request_queue_t *q;
+               if ((host->can_queue > 0 && (host->host_busy >= host->can_queue))
+                   || (host->host_blocked)
+-                  || (host->host_self_blocked)
+-                  || (SDpnt->device_blocked)) {
++                  || (host->host_self_blocked)) {
+                       break;
+               }
+               q = &SDpnt->request_queue;
+@@ -1259,136 +1352,202 @@
+ }
+ /*
+- * Function:  scsi_unjam_host
++ * Function:  scsi_eh_find_failed_command
+  *
+- * Purpose:     Attempt to fix a host which has a command that failed for
+- *              some reason.
++ * Purpose:   Find a failed Scsi_Cmnd structure on a device.
+  *
+- * Arguments:   host    - host that needs unjamming.
+- * 
+- * Returns:     Nothing
++ * Arguments: SDpnt   - Scsi_Device structure
+  *
+- * Notes:       When we come in here, we *know* that all commands on the
+- *              bus have either completed, failed or timed out.  We also
+- *              know that no further commands are being sent to the host,
+- *              so things are relatively quiet and we have freedom to
+- *              fiddle with things as we wish.
++ * Returns:   Pointer to Scsi_Cmnd structure, or NULL on failure
++ */
++STATIC Scsi_Cmnd *scsi_eh_find_failed_command(Scsi_Device *SDpnt)
++{
++      Scsi_Cmnd *SCpnt;
++
++      for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next)
++              if (SCpnt->state == SCSI_STATE_FAILED ||
++                  SCpnt->state == SCSI_STATE_TIMEOUT)
++                      return SCpnt;
++
++      return NULL;
++}
++
++
++/*
++ * Function:  scsi_eh_test_and_retry
+  *
+- * Additional note:  This is only the *default* implementation.  It is possible
+- *              for individual drivers to supply their own version of this
+- *              function, and if the maintainer wishes to do this, it is
+- *              strongly suggested that this function be taken as a template
+- *              and modified.  This function was designed to correctly handle
+- *              problems for about 95% of the different cases out there, and
+- *              it should always provide at least a reasonable amount of error
+- *              recovery.
++ * Purpose:   Try to retry a failed command.
+  *
+- * Note3:       Any command marked 'FAILED' or 'TIMEOUT' must eventually
+- *              have scsi_finish_command() called for it.  We do all of
+- *              the retry stuff here, so when we restart the host after we
+- *              return it should have an empty queue.
++ * Arguments: SCpnt   - scsi command structure
++ *            done    - list of commands that have been successfully
++ *                      completed.
++ *
++ * Returns:   SUCCESS or FAILED
++ *
++ * Note:      If the TEST UNIT READY command successfully executes,
++ *            but returns some form of "device not ready", we wait
++ *            a while, and retry 3 times.  The device could be still
++ *            re-initialising.
+  */
+-STATIC int scsi_unjam_host(struct Scsi_Host *host)
++STATIC int scsi_eh_test_and_retry(Scsi_Cmnd *SCpnt, Scsi_Cmnd **done)
+ {
+-      int devices_failed;
+-      int numfailed;
+-      int ourrtn;
+-      int rtn = FALSE;
+-      int result;
+-      Scsi_Cmnd *SCloop;
+-      Scsi_Cmnd *SCpnt;
+-      Scsi_Device *SDpnt;
+-      Scsi_Device *SDloop;
+-      Scsi_Cmnd *SCdone;
+-      int timed_out;
++      int rtn, tries = 3;
+-      ASSERT_LOCK(&io_request_lock, 0);
++      do {
++              rtn = scsi_test_unit_ready(SCpnt);
++              if (rtn != SUCCESS)
++                      return rtn;
+-      SCdone = NULL;
++              if (scsi_unit_is_ready(SCpnt))
++                      break;
++
++              if (tries-- == 0)
++                      return FAILED;
++
++              scsi_sleep(5 * HZ);
++      } while (1);
++
++      rtn = scsi_eh_retry_command(SCpnt);
++      if (rtn == SUCCESS) {
++              SCpnt->host->host_failed--;
++              scsi_eh_finish_command(done, SCpnt);
++      }
++
++      return rtn;
++}
+-      /*
+-       * First, protect against any sort of race condition.  If any of the outstanding
+-       * commands are in states that indicate that we are not yet blocked (i.e. we are
+-       * not in a quiet state) then we got woken up in error.  If we ever end up here,
+-       * we need to re-examine some of the assumptions.
+-       */
+-      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-              for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+-                      if (SCpnt->state == SCSI_STATE_FAILED
+-                          || SCpnt->state == SCSI_STATE_TIMEOUT
+-                          || SCpnt->state == SCSI_STATE_INITIALIZING
+-                          || SCpnt->state == SCSI_STATE_UNUSED) {
+-                              continue;
+-                      }
+-                      /*
+-                       * Rats.  Something is still floating around out there.  This could
+-                       * be the result of the fact that the upper level drivers are still frobbing
+-                       * commands that might have succeeded.  There are two outcomes.  One is that
+-                       * the command block will eventually be freed, and the other one is that
+-                       * the command will be queued and will be finished along the way.
+-                       */
+-                      SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target));
+ /*
+- *        panic("SCSI Error handler woken too early\n");
++ * Function:  scsi_eh_restart_device
+  *
+- * This is no longer a problem, since now the code cares only about
+- * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED.
+- * Other states are useful only to release active commands when devices are
+- * set offline. If (host->host_active == host->host_busy) we can safely assume
+- * that there are no commands in state other then TIMEOUT od FAILED. (DB)
++ * Purpose:   Retry all failed or timed out commands for a device
+  *
+- * FIXME:
+- * It is not easy to release correctly commands according to their state when 
+- * devices are set offline, when the state is neither TIMEOUT nor FAILED.
+- * When a device is set offline, we can have some command with
+- * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, 
+- * state=SCSI_STATE_INITIALIZING and the driver module cannot be released.
+- * (DB, 17 May 1998)
++ * Arguments: SDpnt   - SCSI device to retry
++ *            done    - list of commands that have been successfully
++ *                      completed.
++ *
++ * Returns:   SUCCESS or failure code
+  */
++STATIC int scsi_eh_restart_device(Scsi_Device *SDpnt, Scsi_Cmnd **done)
++{
++      Scsi_Cmnd *SCpnt, *SCnext;
++      int rtn = SUCCESS;
++
++      for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) {
++              SCnext = SCpnt->next;
++
++              if (SCpnt->state == SCSI_STATE_FAILED ||
++                  SCpnt->state == SCSI_STATE_TIMEOUT) {
++                      rtn = scsi_eh_test_and_retry(SCpnt, done);
++                      if (rtn != SUCCESS)
++                              break;
++              }
++      }
++
++      return rtn;
++}
++
++/*
++ * Function:  scsi_eh_set_device_offline
++ *
++ * Purpose:   set a device off line
++ *
++ * Arguments: SDpnt   - SCSI device to take off line
++ *            done    - list of commands that have been successfully
++ *                      completed.
++ *            reason  - text string describing why the device is off-line
++ *
++ * Returns:   Nothing
++ *
++ * Notes:     In addition, we complete each failed or timed out command
++ *            attached to this device.
++ */
++STATIC void scsi_eh_set_device_offline(Scsi_Device *SDpnt, Scsi_Cmnd **done,
++                                     const char *reason)
++{
++      Scsi_Cmnd *SCpnt, *SCnext;
++
++      printk(KERN_ERR "scsi: device set offline - %s: "
++              "host %d channel %d id %d lun %d\n",
++              reason, SDpnt->host->host_no, SDpnt->channel,
++              SDpnt->id, SDpnt->lun);
++
++      SDpnt->online = FALSE;
++
++      for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCnext) {
++              SCnext = SCpnt->next;
++
++              switch (SCpnt->state) {
++              case SCSI_STATE_TIMEOUT:
++                      SCpnt->result |= DRIVER_TIMEOUT;
++                      /*FALLTHROUGH*/
++
++              case SCSI_STATE_FAILED:
++                      SCSI_LOG_ERROR_RECOVERY(3,
++                              printk("Finishing command for device %d %x\n",
++                                      SDpnt->id, SCpnt->result));
++
++                      SDpnt->host->host_failed--;
++                      scsi_eh_finish_command(done, SCpnt);
++                      break;
++
++              default:
++                      break;
+               }
+       }
++}
++
++static void scsi_unjam_request_sense(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      int rtn;
++      int result;
++      Scsi_Cmnd *SCpnt;
++      Scsi_Device *SDpnt;
+       /*
+        * Next, see if we need to request sense information.  if so,
+        * then get it now, so we have a better idea of what to do.
+-       * FIXME(eric) this has the unfortunate side effect that if a host
+-       * adapter does not automatically request sense information, that we end
+-       * up shutting it down before we request it.  All hosts should be doing this
+-       * anyways, so for now all I have to say is tough noogies if you end up in here.
+-       * On second thought, this is probably a good idea.  We *really* want to give
+-       * authors an incentive to automatically request this.
++       * FIXME(eric) this has the unfortunate side effect that if a
++       * host adapter does not automatically request sense information,
++       * that we end up shutting it down before we request it.  All
++       * hosts should be doing this anyways, so for now all I have
++       * to say is tough noogies if you end up in here.  On second
++       * thought, this is probably a good idea.  We *really* want
++       * to give authors an incentive to automatically request this.
+        */
+-      SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we need to request sense\n"));
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Checking to see if we need to request sense\n"));
+       for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+               for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+-                      if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt)) {
++                      if (SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt))
+                               continue;
+-                      }
+-                      SCSI_LOG_ERROR_RECOVERY(2, printk("scsi_unjam_host: Requesting sense for %d\n",
+-                                                        SCpnt->target));
++
++                      SCSI_LOG_ERROR_RECOVERY(2,
++                              printk("scsi_unjam_host: Requesting sense for %d\n",
++                                        SCpnt->target));
+                       rtn = scsi_request_sense(SCpnt);
+-                      if (rtn != SUCCESS) {
++                      if (rtn != SUCCESS)
+                               continue;
+-                      }
+-                      SCSI_LOG_ERROR_RECOVERY(3, printk("Sense requested for %p - result %x\n",
+-                                                SCpnt, SCpnt->result));
++
++                      SCSI_LOG_ERROR_RECOVERY(3,
++                              printk("Sense requested for %p - result %x\n",
++                                        SCpnt, SCpnt->result));
+                       SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", SCpnt));
+                       result = scsi_decide_disposition(SCpnt);
+                       /*
+-                       * If the result was normal, then just pass it along to the
+-                       * upper level.
++                       * If the result was normal, then just pass
++                       * it along to the upper level.
+                        */
+                       if (result == SUCCESS) {
+                               SCpnt->host->host_failed--;
+-                              scsi_eh_finish_command(&SCdone, SCpnt);
++                              scsi_eh_finish_command(done, SCpnt);
+                       }
+-                      if (result != NEEDS_RETRY) {
++                      if (result != NEEDS_RETRY)
+                               continue;
+-                      }
++
+                       /* 
+                        * We only come in here if we want to retry a
+                        * command.  The test to see whether the command
+@@ -1398,20 +1557,29 @@
+                        */
+                       SCpnt->state = NEEDS_RETRY;
+                       rtn = scsi_eh_retry_command(SCpnt);
+-                      if (rtn != SUCCESS) {
++                      if (rtn != SUCCESS)
+                               continue;
+-                      }
++
+                       /*
+                        * We eventually hand this one back to the top level.
+                        */
+                       SCpnt->host->host_failed--;
+-                      scsi_eh_finish_command(&SCdone, SCpnt);
++                      scsi_eh_finish_command(done, SCpnt);
+               }
+       }
++}
++
++static void scsi_unjam_count(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++      Scsi_Cmnd *SCpnt;
++      int devices_failed;
++      int numfailed;
++      int timed_out;
+       /*
+-       * Go through the list of commands and figure out where we stand and how bad things
+-       * really are.
++       * Go through the list of commands and figure out where we
++       * stand and how bad things really are.
+        */
+       numfailed = 0;
+       timed_out = 0;
+@@ -1421,359 +1589,478 @@
+               for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+                       if (SCpnt->state == SCSI_STATE_FAILED) {
+-                              SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d failed\n",
+-                                                       SCpnt->target));
++                              SCSI_LOG_ERROR_RECOVERY(5,
++                                      printk("Command to ID %d failed\n",
++                                               SCpnt->target));
+                               numfailed++;
+                               device_error++;
+                       }
+                       if (SCpnt->state == SCSI_STATE_TIMEOUT) {
+-                              SCSI_LOG_ERROR_RECOVERY(5, printk("Command to ID %d timedout\n",
+-                                                       SCpnt->target));
++                              SCSI_LOG_ERROR_RECOVERY(5,
++                                      printk("Command to ID %d timedout\n",
++                                               SCpnt->target));
+                               timed_out++;
+                               device_error++;
+                       }
+               }
+-              if (device_error > 0) {
++              if (device_error > 0)
+                       devices_failed++;
+-              }
+       }
+-      SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d+%d commands on %d devices require eh work\n",
+-                                numfailed, timed_out, devices_failed));
++      SCSI_LOG_ERROR_RECOVERY(2,
++              printk("Total of %d+%d commands on %d devices require eh work\n",
++                        numfailed, timed_out, devices_failed));
++}
++
++static void scsi_unjam_abort(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++      Scsi_Cmnd *SCpnt;
++      int rtn;
+-      if (host->host_failed == 0) {
+-              ourrtn = TRUE;
+-              goto leave;
+-      }
+       /*
+-       * Next, try and see whether or not it makes sense to try and abort
+-       * the running command.  This only works out to be the case if we have
+-       * one command that has timed out.  If the command simply failed, it
+-       * makes no sense to try and abort the command, since as far as the
+-       * host adapter is concerned, it isn't running.
++       * Next, try and see whether or not it makes sense to try and
++       * abort the running command.  This only works out to be the
++       * case if we have one command that has timed out.  If the
++       * command simply failed, it makes no sense to try and abort
++       * the command, since as far as the host adapter is concerned,
++       * it isn't running.
+        */
+-      SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try abort\n"));
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Checking to see if we want to try abort\n"));
+       for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-              for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
+-                      if (SCloop->state != SCSI_STATE_TIMEOUT) {
++              for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
++                      if (SCpnt->state != SCSI_STATE_TIMEOUT)
+                               continue;
+-                      }
+-                      rtn = scsi_try_to_abort_command(SCloop, ABORT_TIMEOUT);
+-                      if (rtn == SUCCESS) {
+-                              rtn = scsi_test_unit_ready(SCloop);
+-                              if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
+-                                      rtn = scsi_eh_retry_command(SCloop);
+-
+-                                      if (rtn == SUCCESS) {
+-                                              SCloop->host->host_failed--;
+-                                              scsi_eh_finish_command(&SCdone, SCloop);
+-                                      }
+-                              }
+-                      }
++                      rtn = scsi_try_to_abort_command(SCpnt, ABORT_TIMEOUT);
++                      if (rtn == SUCCESS)
++                              scsi_eh_test_and_retry(SCpnt, done);
+               }
+       }
++}
++
++static void scsi_unjam_device_reset(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++      Scsi_Cmnd *SCpnt;
++      int rtn;
+-      /*
+-       * If we have corrected all of the problems, then we are done.
+-       */
+-      if (host->host_failed == 0) {
+-              ourrtn = TRUE;
+-              goto leave;
+-      }
+       /*
+        * Either the abort wasn't appropriate, or it didn't succeed.
+-       * Now try a bus device reset.  Still, look to see whether we have
+-       * multiple devices that are jammed or not - if we have multiple devices,
+-       * it makes no sense to try BUS_DEVICE_RESET - we really would need
+-       * to try a BUS_RESET instead.
++       * Now try a bus device reset.  Still, look to see whether we
++       * have multiple devices that are jammed or not - if we have
++       * multiple devices, it makes no sense to try BUS_DEVICE_RESET
++       * - we really would need to try a BUS_RESET instead.
+        *
+-       * Does this make sense - should we try BDR on each device individually?
+-       * Yes, definitely.
++       * Does this make sense - should we try BDR on each device
++       * individually?  Yes, definitely.
+        */
+-      SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Checking to see if we want to try BDR\n"));
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Checking to see if we want to try BDR\n"));
+       for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-              for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
+-                      if (SCloop->state == SCSI_STATE_FAILED
+-                          || SCloop->state == SCSI_STATE_TIMEOUT) {
+-                              break;
+-                      }
+-              }
+-
+-              if (SCloop == NULL) {
++              SCpnt = scsi_eh_find_failed_command(SDpnt);
++              if (SCpnt == NULL)
+                       continue;
+-              }
++
+               /*
+-               * OK, we have a device that is having problems.  Try and send
+-               * a bus device reset to it.
+-               *
+-               * FIXME(eric) - make sure we handle the case where multiple
+-               * commands to the same device have failed. They all must
+-               * get properly restarted.
++               * OK, we have a device that is having problems.
++               * Try and send a bus device reset to it.
+                */
+-              rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT);
++              rtn = scsi_try_bus_device_reset(SCpnt, RESET_TIMEOUT);
+-              if (rtn == SUCCESS) {
+-                      rtn = scsi_test_unit_ready(SCloop);
++              /*
++               * A successful bus device reset causes all commands
++               * currently executing on the device to terminate.
++               * We expect the HBA driver to "forget" all commands
++               * associated with this device.
++               *
++               * Retry each failed or timed out command currently
++               * outstanding for this device.
++               *
++               * If any command fails, bail out.  We will try a
++               * bus reset instead.
++               */
++              if (rtn == SUCCESS)
++                      scsi_eh_restart_device(SDpnt, done);
++      }
++}
+-                      if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
+-                              rtn = scsi_eh_retry_command(SCloop);
++static void scsi_unjam_bus_reset(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++      Scsi_Cmnd *SCpnt;
++      int rtn, channel, max_channel = 0;
+-                              if (rtn == SUCCESS) {
+-                                      SCloop->host->host_failed--;
+-                                      scsi_eh_finish_command(&SCdone, SCloop);
+-                              }
+-                      }
+-              }
+-      }
++      /*
++       * If we ended up here, we have serious problems.  The only thing
++       * left to try is a full bus reset.  If someone has grabbed the
++       * bus and isn't letting go, then perhaps this will help.
++       */
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Try hard bus reset\n"));
+-      if (host->host_failed == 0) {
+-              ourrtn = TRUE;
+-              goto leave;
+-      }
+       /*
+-       * If we ended up here, we have serious problems.  The only thing left
+-       * to try is a full bus reset.  If someone has grabbed the bus and isn't
+-       * letting go, then perhaps this will help.
++       * Find the maximum channel number for this host.
+        */
+-      SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard bus reset\n"));
++      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next)
++              if (SDpnt->channel > max_channel)
++                      max_channel = SDpnt->channel;
+-      /* 
+-       * We really want to loop over the various channels, and do this on
+-       * a channel by channel basis.  We should also check to see if any
+-       * of the failed commands are on soft_reset devices, and if so, skip
+-       * the reset.  
++      /*
++       * Loop over each channel, and see if it any device on
++       * each channel has failed.
+        */
+-      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-            next_device:
+-              for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+-                      if (SCpnt->state != SCSI_STATE_FAILED
+-                          && SCpnt->state != SCSI_STATE_TIMEOUT) {
++      for (channel = 0; channel <= max_channel; channel++) {
++              Scsi_Cmnd *failed_command;
++              int soft_reset;
++
++ try_again:
++              failed_command = NULL;
++              soft_reset = 0;
++
++              /*
++               * Loop over each device on this channel locating any
++               * failed command.  We need a Scsi_Cmnd structure to
++               * call the bus reset function.
++               *
++               * We also need to check if any of the failed commands
++               * are on soft_reset devices, and if so, skip the reset.  
++               */
++              for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
++                      if (SDpnt->channel != channel)
+                               continue;
+-                      }
+-                      /*
+-                       * We have a failed command.  Make sure there are no other failed
+-                       * commands on the same channel that are timed out and implement a
+-                       * soft reset.
+-                       */
+-                      for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
+-                              for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
+-                                      if (SCloop->channel != SCpnt->channel) {
+-                                              continue;
+-                                      }
+-                                      if (SCloop->state != SCSI_STATE_FAILED
+-                                          && SCloop->state != SCSI_STATE_TIMEOUT) {
+-                                              continue;
+-                                      }
+-                                      if (SDloop->soft_reset && SCloop->state == SCSI_STATE_TIMEOUT) {
+-                                              /* 
+-                                               * If this device uses the soft reset option, and this
+-                                               * is one of the devices acting up, then our only
+-                                               * option is to wait a bit, since the command is
+-                                               * supposedly still running.  
+-                                               *
+-                                               * FIXME(eric) - right now we will just end up falling
+-                                               * through to the 'take device offline' case.
+-                                               *
+-                                               * FIXME(eric) - It is possible that the command completed
+-                                               * *after* the error recovery procedure started, and if this
+-                                               * is the case, we are worrying about nothing here.
+-                                               */
+-                                              scsi_sleep(1 * HZ);
+-                                              goto next_device;
+-                                      }
+-                              }
+-                      }
++                      SCpnt = scsi_eh_find_failed_command(SDpnt);
++                      if (SCpnt)
++                              failed_command = SCpnt;
+                       /*
+-                       * We now know that we are able to perform a reset for the
+-                       * bus that SCpnt points to.  There are no soft-reset devices
+-                       * with outstanding timed out commands.
++                       * If this device has timed out or failed commands,
++                       * and uses the soft_reset option.
+                        */
+-                      rtn = scsi_try_bus_reset(SCpnt);
+-                      if (rtn == SUCCESS) {
+-                              for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
+-                                      for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
+-                                              if (SCloop->channel != SCpnt->channel) {
+-                                                      continue;
+-                                              }
+-                                              if (SCloop->state != SCSI_STATE_FAILED
+-                                                  && SCloop->state != SCSI_STATE_TIMEOUT) {
+-                                                      continue;
+-                                              }
+-                                              rtn = scsi_test_unit_ready(SCloop);
++                      if (SCpnt && SDpnt->soft_reset)
++                              soft_reset = 1;
++              }
+-                                              if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
+-                                                      rtn = scsi_eh_retry_command(SCloop);
++              /*
++               * If this channel hasn't failed, we
++               * don't need to reset it.
++               */
++              if (!failed_command)
++                      continue;
+-                                                      if (rtn == SUCCESS) {
+-                                                              SCpnt->host->host_failed--;
+-                                                              scsi_eh_finish_command(&SCdone, SCloop);
+-                                                      }
+-                                              }
+-                                              /*
+-                                               * If the bus reset worked, but we are still unable to
+-                                               * talk to the device, take it offline.
+-                                               * FIXME(eric) - is this really the correct thing to do?
+-                                               */
+-                                              if (rtn != SUCCESS) {
+-                                                      printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after bus reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
++              /* 
++               * If this device uses the soft reset option, and this
++               * is one of the devices acting up, then our only
++               * option is to wait a bit, since the command is
++               * supposedly still running.  
++               *
++               * FIXME(eric) - right now we will just end up falling
++               * through to the 'take device offline' case.
++               *
++               * FIXME(eric) - It is possible that the command completed
++               * *after* the error recovery procedure started, and if
++               * this is the case, we are worrying about nothing here.
++               *
++               * FIXME(rmk) - This should be bounded; we shouldn't wait
++               * for an infinite amount of time for any device.
++               */
++              if (soft_reset) {
++                      SCSI_LOG_ERROR_RECOVERY(3,
++                              printk("scsi_unjam_host: unable to try bus "
++                                      "reset for host %d channel %d\n",
++                                      host->host_no, channel));
++                      scsi_sleep(1 * HZ);
++                      goto try_again;
++              }
+-                                                      SDloop->online = FALSE;
+-                                                      SDloop->host->host_failed--;
+-                                                      scsi_eh_finish_command(&SCdone, SCloop);
+-                                              }
+-                                      }
+-                              }
++              /*
++               * We now know that we are able to perform a reset for the
++               * bus that SCpnt points to.  There are no soft-reset devices
++               * with outstanding timed out commands.
++               */
++              rtn = scsi_try_bus_reset(failed_command);
++
++              /*
++               * If we failed to reset the bus, move on to the next bus.
++               */
++              if (rtn != SUCCESS)
++                      continue;
++
++              /*
++               * We succeeded.  Retry each failed command.
++               */
++              for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
++                      if (SDpnt->channel != channel)
++                              continue;
++
++                      rtn = scsi_eh_restart_device(SDpnt, done);
++
++                      if (rtn != SUCCESS) {
++                              SCpnt = scsi_eh_find_failed_command(SDpnt);
++
++                              /*
++                               * This device failed again.  Since a bus
++                               * reset freed it up, chances are we've
++                               * hit the same problem, so try the same
++                               * solution.  We also need to ensure that
++                               * the SCSI bus is in the BUS FREE state
++                               * so we can try to talk to other devices.
++                               */
++                              scsi_try_bus_reset(SCpnt);
++                              scsi_eh_set_device_offline(SDpnt, done,
++                                      "not ready or command retry "
++                                      "failed after bus reset");
+                       }
+               }
+       }
++}
++
++static void scsi_unjam_host_reset(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++      Scsi_Cmnd *SCpnt;
++      Scsi_Cmnd *failed_command = NULL;
++      int rtn, soft_reset;
+-      if (host->host_failed == 0) {
+-              ourrtn = TRUE;
+-              goto leave;
+-      }
+       /*
+-       * If we ended up here, we have serious problems.  The only thing left
+-       * to try is a full host reset - perhaps the firmware on the device
+-       * crashed, or something like that.
++       * If we ended up here, we have serious problems.  The only thing
++       * left to try is a full host reset - perhaps the firmware on the
++       * device crashed, or something like that.
+        *
+-       * It is assumed that a succesful host reset will cause *all* information
+-       * about the command to be flushed from both the host adapter *and* the
+-       * device.
++       * It is assumed that a succesful host reset will cause *all*
++       * information about the command to be flushed from both the host
++       * adapter *and* the device.
+        *
+-       * FIXME(eric) - it isn't clear that devices that implement the soft reset
+-       * option can ever be cleared except via cycling the power.  The problem is
+-       * that sending the host reset command will cause the host to forget
+-       * about the pending command, but the device won't forget.  For now, we
+-       * skip the host reset option if any of the failed devices are configured
+-       * to use the soft reset option.
++       * FIXME(eric) - it isn't clear that devices that implement the
++       * soft reset option can ever be cleared except via cycling the
++       * power.  The problem is that sending the host reset command will
++       * cause the host to forget about the pending command, but the
++       * device won't forget.  For now, we skip the host reset option
++       * if any of the failed devices are configured to use the soft
++       * reset option.
+        */
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Try host reset\n"));
++
++ try_again:
++      failed_command = NULL;
++      soft_reset = 0;
++
+       for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-            next_device2:
+-              for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
+-                      if (SCpnt->state != SCSI_STATE_FAILED
+-                          && SCpnt->state != SCSI_STATE_TIMEOUT) {
+-                              continue;
+-                      }
+-                      if (SDpnt->soft_reset && SCpnt->state == SCSI_STATE_TIMEOUT) {
+-                              /* 
+-                               * If this device uses the soft reset option, and this
+-                               * is one of the devices acting up, then our only
+-                               * option is to wait a bit, since the command is
+-                               * supposedly still running.  
+-                               *
+-                               * FIXME(eric) - right now we will just end up falling
+-                               * through to the 'take device offline' case.
+-                               */
+-                              SCSI_LOG_ERROR_RECOVERY(3,
+-                                                      printk("scsi_unjam_host: Unable to try hard host reset\n"));
++              /*
++               * Locate any failed commands for this device.
++               */
++              SCpnt = scsi_eh_find_failed_command(SDpnt);
++              if (SCpnt)
++                      failed_command = SCpnt;
+-                              /*
+-                               * Due to the spinlock, we will never get out of this
+-                               * loop without a proper wait. (DB)
+-                               */
+-                              scsi_sleep(1 * HZ);
++              /*
++               * If this device has timed out or failed commands,
++               * and uses the soft_reset option.
++               */
++              if (SCpnt && SDpnt->soft_reset)
++                      soft_reset = 1;
++      }
+-                              goto next_device2;
+-                      }
+-                      SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Try hard host reset\n"));
++      /* 
++       * If this device uses the soft reset option, and this
++       * is one of the devices acting up, then our only
++       * option is to wait a bit, since the command is
++       * supposedly still running.  
++       *
++       * FIXME(eric) - right now we will just end up falling
++       * through to the 'take device offline' case.
++       *
++       * FIXME(rmk) - This should be bounded; we shouldn't wait
++       * for an infinite amount of time for any device.
++       */
++      if (soft_reset) {
++              SCSI_LOG_ERROR_RECOVERY(3,
++                      printk("scsi_unjam_host: unable to try "
++                              "hard host reset\n"));
+                       /*
+-                       * FIXME(eric) - we need to obtain a valid SCpnt to perform this call.
++                       * Due to the spinlock, we will never get out of this
++                       * loop without a proper wait. (DB)
+                        */
+-                      rtn = scsi_try_host_reset(SCpnt);
+-                      if (rtn == SUCCESS) {
+-                              /*
+-                               * FIXME(eric) we assume that all commands are flushed from the
+-                               * controller.  We should get a DID_RESET for all of the commands
+-                               * that were pending.  We should ignore these so that we can
+-                               * guarantee that we are in a consistent state.
+-                               *
+-                               * I believe this to be the case right now, but this needs to be
+-                               * tested.
+-                               */
+-                              for (SDloop = host->host_queue; SDloop; SDloop = SDloop->next) {
+-                                      for (SCloop = SDloop->device_queue; SCloop; SCloop = SCloop->next) {
+-                                              if (SCloop->state != SCSI_STATE_FAILED
+-                                                  && SCloop->state != SCSI_STATE_TIMEOUT) {
+-                                                      continue;
+-                                              }
+-                                              rtn = scsi_test_unit_ready(SCloop);
+-
+-                                              if (rtn == SUCCESS && scsi_unit_is_ready(SCloop)) {
+-                                                      rtn = scsi_eh_retry_command(SCloop);
++                      scsi_sleep(1 * HZ);
+-                                                      if (rtn == SUCCESS) {
+-                                                              SCpnt->host->host_failed--;
+-                                                              scsi_eh_finish_command(&SCdone, SCloop);
+-                                                      }
+-                                              }
+-                                              if (rtn != SUCCESS) {
+-                                                      printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after host reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
+-                                                      SDloop->online = FALSE;
+-                                                      SDloop->host->host_failed--;
+-                                                      scsi_eh_finish_command(&SCdone, SCloop);
+-                                              }
+-                                      }
+-                              }
+-                      }
+-              }
++                      goto try_again;
+       }
++      SCSI_LOG_ERROR_RECOVERY(3,
++              printk("scsi_unjam_host: Try hard host reset\n"));
++
+       /*
+-       * If we solved all of the problems, then let's rev up the engines again.
+-       */
+-      if (host->host_failed == 0) {
+-              ourrtn = TRUE;
+-              goto leave;
+-      }
+-      /*
+-       * If the HOST RESET failed, then for now we assume that the entire host
+-       * adapter is too hosed to be of any use.  For our purposes, however, it is
+-       * easier to simply take the devices offline that correspond to commands
+-       * that failed.
++       * FIXME(eric) - we need to obtain a valid SCpnt to perform this call.
+        */
+-      SCSI_LOG_ERROR_RECOVERY(1, printk("scsi_unjam_host: Take device offline\n"));
++      rtn = scsi_try_host_reset(failed_command);
++      if (rtn == SUCCESS) {
++              /*
++               * FIXME(eric) we assume that all commands are flushed from
++               * the controller.  We should get a DID_RESET for all of the
++               * commands that were pending.  We should ignore these so
++               * that we can guarantee that we are in a consistent state.
++               *
++               * I believe this to be the case right now, but this needs
++               * to be tested.
++               */
++              for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
++                      rtn = scsi_eh_restart_device(SDpnt, done);
+-      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+-              for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
+-                      if (SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT) {
+-                              SDloop = SCloop->device;
+-                              if (SDloop->online == TRUE) {
+-                                      printk(KERN_INFO "scsi: device set offline - command error recover failed: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
+-                                      SDloop->online = FALSE;
+-                              }
++                      if (rtn != SUCCESS) {
++                              SCpnt = scsi_eh_find_failed_command(SDpnt);
+                               /*
+-                               * This should pass the failure up to the top level driver, and
+-                               * it will have to try and do something intelligent with it.
++                               * This device failed again.  Since a host
++                               * reset freed it up, chances are we've
++                               * hit the same problem, so try the same
++                               * solution.  We also need to ensure that
++                               * the SCSI bus is in the BUS FREE state
++                               * so we can try to talk to other devices.
+                                */
+-                              SCloop->host->host_failed--;
+-
+-                              if (SCloop->state == SCSI_STATE_TIMEOUT) {
+-                                      SCloop->result |= (DRIVER_TIMEOUT << 24);
+-                              }
+-                              SCSI_LOG_ERROR_RECOVERY(3, printk("Finishing command for device %d %x\n",
+-                                  SDloop->id, SCloop->result));
+-
+-                              scsi_eh_finish_command(&SCdone, SCloop);
++                              scsi_try_host_reset(SCpnt);
++                              scsi_eh_set_device_offline(SDpnt, done,
++                                      "not ready or command retry "
++                                      "failed after host reset");
+                       }
+               }
+       }
++}
+-      if (host->host_failed != 0) {
++static void scsi_unjam_failure(struct Scsi_Host *host, Scsi_Cmnd **done)
++{
++      Scsi_Device *SDpnt;
++
++      /*
++       * If the HOST RESET failed, then for now we assume that the
++       * entire host adapter is too hosed to be of any use.  For our
++       * purposes, however, it is easier to simply take the devices
++       * offline that correspond to commands that failed.
++       */
++      SCSI_LOG_ERROR_RECOVERY(1,
++              printk("scsi_unjam_host: Take device offline\n"));
++
++      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next)
++              scsi_eh_set_device_offline(SDpnt, done,
++                      "command error recover failed");
++
++      if (host->host_failed != 0)
+               panic("scsi_unjam_host: Miscount of number of failed commands.\n");
+-      }
++
+       SCSI_LOG_ERROR_RECOVERY(3, printk("scsi_unjam_host: Returning\n"));
++}
+-      ourrtn = FALSE;
++static void (*unjam_method[])(struct Scsi_Host *, Scsi_Cmnd **) = {
++      scsi_unjam_request_sense,
++      scsi_unjam_count,
++      scsi_unjam_abort,
++      scsi_unjam_device_reset,
++      scsi_unjam_bus_reset,
++      scsi_unjam_host_reset,
++      scsi_unjam_failure,
++};
+-      leave:
++/*
++ * Function:  scsi_unjam_host
++ *
++ * Purpose:     Attempt to fix a host which has a command that failed for
++ *              some reason.
++ *
++ * Arguments:   host    - host that needs unjamming.
++ * 
++ * Returns:     Nothing
++ *
++ * Notes:       When we come in here, we *know* that all commands on the
++ *              bus have either completed, failed or timed out.  We also
++ *              know that no further commands are being sent to the host,
++ *              so things are relatively quiet and we have freedom to
++ *              fiddle with things as we wish.
++ *
++ * Additional note:  This is only the *default* implementation.  It is possible
++ *              for individual drivers to supply their own version of this
++ *              function, and if the maintainer wishes to do this, it is
++ *              strongly suggested that this function be taken as a template
++ *              and modified.  This function was designed to correctly handle
++ *              problems for about 95% of the different cases out there, and
++ *              it should always provide at least a reasonable amount of error
++ *              recovery.
++ *
++ * Note3:       Any command marked 'FAILED' or 'TIMEOUT' must eventually
++ *              have scsi_finish_command() called for it.  We do all of
++ *              the retry stuff here, so when we restart the host after we
++ *              return it should have an empty queue.
++ */
++STATIC int scsi_unjam_host(struct Scsi_Host *host)
++{
++      Scsi_Cmnd *SCdone = NULL;
++      Scsi_Cmnd *SCpnt;
++      Scsi_Device *SDpnt;
++      int ourrtn = FALSE;
++      int i;
++
++      ASSERT_LOCK(&io_request_lock, 0);
++
++      /*
++       * First, protect against any sort of race condition.  If any of the outstanding
++       * commands are in states that indicate that we are not yet blocked (i.e. we are
++       * not in a quiet state) then we got woken up in error.  If we ever end up here,
++       * we need to re-examine some of the assumptions.
++       */
++      for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
++              for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) {
++                      if (SCpnt->state == SCSI_STATE_FAILED
++                          || SCpnt->state == SCSI_STATE_TIMEOUT
++                          || SCpnt->state == SCSI_STATE_INITIALIZING
++                          || SCpnt->state == SCSI_STATE_UNUSED) {
++                              continue;
++                      }
++                      /*
++                       * Rats.  Something is still floating around out there.  This could
++                       * be the result of the fact that the upper level drivers are still frobbing
++                       * commands that might have succeeded.  There are two outcomes.  One is that
++                       * the command block will eventually be freed, and the other one is that
++                       * the command will be queued and will be finished along the way.
++                       */
++                      SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target));
++
++/*
++ *        panic("SCSI Error handler woken too early\n");
++ *
++ * This is no longer a problem, since now the code cares only about
++ * SCSI_STATE_TIMEOUT and SCSI_STATE_FAILED.
++ * Other states are useful only to release active commands when devices are
++ * set offline. If (host->host_active == host->host_busy) we can safely assume
++ * that there are no commands in state other then TIMEOUT od FAILED. (DB)
++ *
++ * FIXME:
++ * It is not easy to release correctly commands according to their state when 
++ * devices are set offline, when the state is neither TIMEOUT nor FAILED.
++ * When a device is set offline, we can have some command with
++ * rq_status=RQ_SCSY_BUSY, owner=SCSI_STATE_HIGHLEVEL, 
++ * state=SCSI_STATE_INITIALIZING and the driver module cannot be released.
++ * (DB, 17 May 1998)
++ */
++              }
++      }
++
++      for (i = 0; i < ARRAY_SIZE(unjam_method); i++) {
++              unjam_method[i](host, &SCdone);
++
++              /*
++               * If we solved all of the problems, then
++               * let's rev up the engines again.
++               */
++              if (host->host_failed == 0) {
++                      ourrtn = TRUE;
++                      break;
++              }
++      }
+       /*
+        * We should have a list of commands that we 'finished' during the course of
+@@ -2013,3 +2300,17 @@
+  * tab-width: 8
+  * End:
+  */
++
++EXPORT_SYMBOL(scsi_eh_times_out);
++EXPORT_SYMBOL(scsi_eh_retry_command);
++EXPORT_SYMBOL(scsi_request_sense);
++EXPORT_SYMBOL(scsi_test_unit_ready);
++EXPORT_SYMBOL(scsi_unit_is_ready);
++EXPORT_SYMBOL(scsi_eh_finish_command);
++EXPORT_SYMBOL(scsi_try_to_abort_command);
++EXPORT_SYMBOL(scsi_try_bus_device_reset);
++EXPORT_SYMBOL(scsi_try_bus_reset);
++EXPORT_SYMBOL(scsi_try_host_reset);
++EXPORT_SYMBOL(scsi_sense_valid);
++EXPORT_SYMBOL(scsi_done);
++EXPORT_SYMBOL(scsi_decide_disposition);
+--- linux-2.4.27/drivers/scsi/scsi_ioctl.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi_ioctl.c
+@@ -153,6 +153,29 @@
+       return result;
+ }
++int scsi_set_medium_removal(Scsi_Device *dev, char state)
++{
++      char scsi_cmd[MAX_COMMAND_SIZE];
++      int ret;
++
++      if (!dev->removable || !dev->lockable)
++              return 0;
++
++      scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
++      scsi_cmd[1] = (dev->scsi_level <= SCSI_2) ? (dev->lun << 5) : 0;
++      scsi_cmd[2] = 0;
++      scsi_cmd[3] = 0;
++      scsi_cmd[4] = state;
++      scsi_cmd[5] = 0;
++
++      ret = ioctl_internal_command(dev, scsi_cmd, IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
++
++      if (ret == 0)
++              dev->locked = state == SCSI_REMOVAL_PREVENT;
++
++      return ret;
++}
++
+ /*
+  * This interface is depreciated - users should use the scsi generic (sg)
+  * interface instead, as this is a more flexible approach to performing
+@@ -450,24 +473,9 @@
+               return scsi_ioctl_send_command((Scsi_Device *) dev,
+                                            (Scsi_Ioctl_Command *) arg);
+       case SCSI_IOCTL_DOORLOCK:
+-              if (!dev->removable || !dev->lockable)
+-                      return 0;
+-              scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+-              scsi_cmd[1] = cmd_byte1;
+-              scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+-              scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
+-              return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+-                                 IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
+-              break;
++              return scsi_set_medium_removal(dev, SCSI_REMOVAL_PREVENT);
+       case SCSI_IOCTL_DOORUNLOCK:
+-              if (!dev->removable || !dev->lockable)
+-                      return 0;
+-              scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+-              scsi_cmd[1] = cmd_byte1;
+-              scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+-              scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
+-              return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd,
+-                                 IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES);
++              return scsi_set_medium_removal(dev, SCSI_REMOVAL_ALLOW);
+       case SCSI_IOCTL_TEST_UNIT_READY:
+               scsi_cmd[0] = TEST_UNIT_READY;
+               scsi_cmd[1] = cmd_byte1;
+--- linux-2.4.27/drivers/scsi/scsi_lib.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi_lib.c
+@@ -208,6 +208,30 @@
+ }
+ /*
++ * Function:  scsi_setup_cmd_retry()
++ *
++ * Purpose:   Restore the command state for a retry
++ *
++ * Arguments: SCpnt   - command to be restored
++ *
++ * Returns:   Nothing
++ *
++ * Notes:     Immediately prior to retrying a command, we need
++ *            to restore certain fields that we saved above.
++ */
++void scsi_setup_cmd_retry(Scsi_Cmnd *SCpnt)
++{
++      memcpy((void *) SCpnt->cmnd, (void *) SCpnt->data_cmnd,
++             sizeof(SCpnt->data_cmnd));
++      SCpnt->request_buffer = SCpnt->buffer;
++      SCpnt->request_bufflen = SCpnt->bufflen;
++      SCpnt->use_sg = SCpnt->old_use_sg;
++      SCpnt->cmd_len = SCpnt->old_cmd_len;
++      SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;
++      SCpnt->underflow = SCpnt->old_underflow;
++}
++
++/*
+  * Function:    scsi_queue_next_request()
+  *
+  * Purpose:     Handle post-processing of completed commands.
+@@ -731,7 +755,7 @@
+                       printk("scsi%d: ERROR on channel %d, id %d, lun %d, CDB: ",
+                              SCpnt->host->host_no, (int) SCpnt->channel,
+                              (int) SCpnt->target, (int) SCpnt->lun);
+-                      print_command(SCpnt->cmnd);
++                      print_command(SCpnt->data_cmnd);
+                       print_sense("sd", SCpnt);
+                       SCpnt = scsi_end_request(SCpnt, 0, block_sectors);
+                       return;
+@@ -906,8 +930,17 @@
+                * space.   Technically the error handling thread should be
+                * doing this crap, but the error handler isn't used by
+                * most hosts.
++               *
++               * (rmk)
++               * Trying to lock the door can cause deadlocks.  We therefore
++               * only use this for old hosts; our door locking is now done
++               * by the error handler in scsi_restart_operations for new
++               * eh hosts.
++               *
++               * Note that we don't clear was_reset here; this is used by
++               * st.c, and either one or other has to die.
+                */
+-              if (SDpnt->was_reset) {
++              if (SHpnt->hostt->use_new_eh_code == 0 && SDpnt->was_reset) {
+                       /*
+                        * We need to relock the door, but we might
+                        * be in an interrupt handler.  Only do this
+@@ -918,7 +951,7 @@
+                        * this work.
+                        */
+                       SDpnt->was_reset = 0;
+-                      if (SDpnt->removable && !in_interrupt()) {
++                      if (SDpnt->removable && SDpnt->locked && !in_interrupt()) {
+                               spin_unlock_irq(&io_request_lock);
+                               scsi_ioctl(SDpnt, SCSI_IOCTL_DOORLOCK, 0);
+                               spin_lock_irq(&io_request_lock);
+--- linux-2.4.27/drivers/scsi/scsi_syms.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/scsi_syms.c
+@@ -104,3 +104,6 @@
+ extern int scsi_delete_timer(Scsi_Cmnd *);
+ EXPORT_SYMBOL(scsi_add_timer);
+ EXPORT_SYMBOL(scsi_delete_timer);
++
++extern int scsi_set_medium_removal(Scsi_Device *dev, char state);
++EXPORT_SYMBOL(scsi_set_medium_removal);
+--- linux-2.4.27/drivers/scsi/sd.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/sd.c
+@@ -399,6 +399,7 @@
+                       this_count = 0xffff;
+               SCpnt->cmnd[0] += READ_10 - READ_6;
++              SCpnt->cmnd[1] |= 1 << 3; /* Set FUA --rmk */
+               SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
+               SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
+               SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
+@@ -524,7 +525,7 @@
+       if (SDev->removable)
+               if (SDev->access_count==1)
+                       if (scsi_block_when_processing_errors(SDev))
+-                              scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL);
++                              scsi_set_medium_removal(SDev, SCSI_REMOVAL_PREVENT);
+       
+       return 0;
+@@ -553,7 +554,7 @@
+       if (SDev->removable) {
+               if (!SDev->access_count)
+                       if (scsi_block_when_processing_errors(SDev))
+-                              scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL);
++                              scsi_set_medium_removal(SDev, SCSI_REMOVAL_ALLOW);
+       }
+       if (SDev->host->hostt->module)
+               __MOD_DEC_USE_COUNT(SDev->host->hostt->module);
+--- linux-2.4.27/drivers/scsi/sr_ioctl.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/scsi/sr_ioctl.c
+@@ -214,9 +214,8 @@
+ int sr_lock_door(struct cdrom_device_info *cdi, int lock)
+ {
+-      return scsi_ioctl(scsi_CDs[MINOR(cdi->dev)].device,
+-                    lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK,
+-                        0);
++      return scsi_set_medium_removal(scsi_CDs[MINOR(cdi->dev)].device,
++                    lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
+ }
+ int sr_drive_status(struct cdrom_device_info *cdi, int slot)
+--- /dev/null
++++ linux-2.4.27/drivers/serial/21285.c
+@@ -0,0 +1,599 @@
++/*
++ * linux/drivers/char/serial_21285.c
++ *
++ * Driver for the serial port on the 21285 StrongArm-110 core logic chip.
++ *
++ * Based on drivers/char/serial.c
++ *
++ *  $Id: 21285.c,v 1.4.2.1 2002/10/24 09:53:23 rmk Exp $
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/serial.h>
++#include <linux/major.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/console.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/hardware/dec21285.h>
++#include <asm/hardware.h>
++
++#define BAUD_BASE             (mem_fclk_21285/64)
++
++#ifdef CONFIG_DEVFS_FS
++#define SERIAL_21285_NAME     "tts/FB%d"
++#define SERIAL_21285_AUXNAME  "cua/FB%d"
++#else
++#define SERIAL_21285_NAME     "ttyFB"
++#define SERIAL_21285_AUXNAME  "cuafb"
++#endif
++
++#define SERIAL_21285_MAJOR    204
++#define SERIAL_21285_MINOR    4
++
++#define SERIAL_21285_AUXMAJOR 205
++#define SERIAL_21285_AUXMINOR 4
++
++#ifdef CONFIG_SERIAL_21285_OLD
++#include <asm/mach-types.h>
++/*
++ * Compatability with a mistake made a long time ago.
++ * Note - the use of "ttyI", "/dev/ttyS0" and major/minor 5,64
++ * is HIGHLY DEPRECIATED, and will be removed in the 2.5
++ * kernel series.
++ *                                    -- rmk 15/04/2000 
++ */
++#define SERIAL_21285_OLD_NAME "ttyI"
++#define SERIAL_21285_OLD_MAJOR        TTY_MAJOR
++#define SERIAL_21285_OLD_MINOR        64
++
++static struct tty_driver rs285_old_driver;
++#endif
++
++static struct tty_driver rs285_driver, callout_driver;
++static int rs285_refcount;
++static struct tty_struct *rs285_table[1];
++
++static struct termios *rs285_termios[1];
++static struct termios *rs285_termios_locked[1];
++
++static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char;
++static struct tty_struct *rs285_tty;
++static DECLARE_MUTEX(rs285_sem);
++static int rs285_use_count;
++static unsigned long rs285_irq_enabled;
++
++#define TX_IRQ_BIT    (0)
++#define RX_IRQ_BIT    (1)
++
++static void rs285_stop_tx(void)
++{
++      if (test_and_clear_bit(TX_IRQ_BIT, &rs285_irq_enabled))
++              disable_irq(IRQ_CONTX);
++}
++
++static void rs285_start_tx(void)
++{
++      if (!test_and_set_bit(TX_IRQ_BIT, &rs285_irq_enabled))
++              enable_irq(IRQ_CONTX);
++}
++
++static void rs285_stop_rx(void)
++{
++      if (test_and_clear_bit(RX_IRQ_BIT, &rs285_irq_enabled))
++              disable_irq(IRQ_CONRX);
++}
++
++static void rs285_start_rx(void)
++{
++      if (!test_and_set_bit(RX_IRQ_BIT, &rs285_irq_enabled))
++              enable_irq(IRQ_CONRX);
++}
++
++static int rs285_write_room(struct tty_struct *tty)
++{
++      return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) : ((long) getp - (long) putp - 1);
++}
++
++static void rs285_rx_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      if (!rs285_tty) {
++              rs285_stop_rx();
++              return;
++      }
++      while (!(*CSR_UARTFLG & 0x10)) {
++              int ch, flag;
++              ch = *CSR_UARTDR;
++              flag = *CSR_RXSTAT;
++              if (flag & 4)
++                      tty_insert_flip_char(rs285_tty, 0, TTY_OVERRUN);
++              if (flag & 2)
++                      flag = TTY_PARITY;
++              else if (flag & 1)
++                      flag = TTY_FRAME;
++              tty_insert_flip_char(rs285_tty, ch, flag);
++      }
++      tty_flip_buffer_push(rs285_tty);
++}
++
++static void rs285_send_xchar(struct tty_struct *tty, char ch)
++{
++      x_char = ch;
++      rs285_start_tx();
++}
++
++static void rs285_throttle(struct tty_struct *tty)
++{
++      if (I_IXOFF(tty))
++              rs285_send_xchar(tty, STOP_CHAR(tty));
++}
++
++static void rs285_unthrottle(struct tty_struct *tty)
++{
++      if (I_IXOFF(tty)) {
++              if (x_char)
++                      x_char = 0;
++              else
++                      rs285_send_xchar(tty, START_CHAR(tty));
++      }
++}
++
++static void rs285_tx_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      while (!(*CSR_UARTFLG & 0x20)) {
++              if (x_char) {
++                      *CSR_UARTDR = x_char;
++                      x_char = 0;
++                      continue;
++              }
++              if (putp == getp) {
++                      rs285_stop_tx();
++                      break;
++              }
++              *CSR_UARTDR = *getp;
++              if (++getp >= wbuf + sizeof(wbuf))
++                      getp = wbuf;
++      }
++      if (rs285_tty)
++              wake_up_interruptible(&rs285_tty->write_wait);
++}
++
++static inline int rs285_xmit(int ch)
++{
++      if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp == wbuf))
++              return 0;
++      *putp = ch;
++      if (++putp >= wbuf + sizeof(wbuf))
++              putp = wbuf;
++      rs285_start_tx();
++      return 1;
++}
++
++static int rs285_write(struct tty_struct *tty, int from_user,
++                     const u_char * buf, int count)
++{
++      int i;
++
++      if (from_user && verify_area(VERIFY_READ, buf, count))
++              return -EINVAL;
++
++      for (i = 0; i < count; i++) {
++              char ch;
++              if (from_user)
++                      __get_user(ch, buf + i);
++              else
++                      ch = buf[i];
++              if (!rs285_xmit(ch))
++                      break;
++      }
++      return i;
++}
++
++static void rs285_put_char(struct tty_struct *tty, u_char ch)
++{
++      rs285_xmit(ch);
++}
++
++static int rs285_chars_in_buffer(struct tty_struct *tty)
++{
++      return sizeof(wbuf) - rs285_write_room(tty);
++}
++
++static void rs285_flush_buffer(struct tty_struct *tty)
++{
++      rs285_stop_tx();
++      putp = getp = wbuf;
++      if (x_char)
++              rs285_start_tx();
++}
++
++static inline void rs285_set_cflag(int cflag)
++{
++      int h_lcr, baud, quot;
++
++      switch (cflag & CSIZE) {
++      case CS5:
++              h_lcr = 0x10;
++              break;
++      case CS6:
++              h_lcr = 0x30;
++              break;
++      case CS7:
++              h_lcr = 0x50;
++              break;
++      default: /* CS8 */
++              h_lcr = 0x70;
++              break;
++
++      }
++      if (cflag & CSTOPB)
++              h_lcr |= 0x08;
++      if (cflag & PARENB)
++              h_lcr |= 0x02;
++      if (!(cflag & PARODD))
++              h_lcr |= 0x04;
++
++      switch (cflag & CBAUD) {
++      case B200:      baud = 200;             break;
++      case B300:      baud = 300;             break;
++      case B1200:     baud = 1200;            break;
++      case B1800:     baud = 1800;            break;
++      case B2400:     baud = 2400;            break;
++      case B4800:     baud = 4800;            break;
++      default:
++      case B9600:     baud = 9600;            break;
++      case B19200:    baud = 19200;           break;
++      case B38400:    baud = 38400;           break;
++      case B57600:    baud = 57600;           break;
++      case B115200:   baud = 115200;          break;
++      }
++
++      /*
++       * The documented expression for selecting the divisor is:
++       *  BAUD_BASE / baud - 1
++       * However, typically BAUD_BASE is not divisible by baud, so
++       * we want to select the divisor that gives us the minimum
++       * error.  Therefore, we want:
++       *  int(BAUD_BASE / baud - 0.5) ->
++       *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->
++       *  int((BAUD_BASE - (baud >> 1)) / baud)
++       */
++      quot = (BAUD_BASE - (baud >> 1)) / baud;
++
++      *CSR_UARTCON = 0;
++      *CSR_L_UBRLCR = quot & 0xff;
++      *CSR_M_UBRLCR = (quot >> 8) & 0x0f;
++      *CSR_H_UBRLCR = h_lcr;
++      *CSR_UARTCON = 1;
++}
++
++static void rs285_set_termios(struct tty_struct *tty, struct termios *old)
++{
++      if (old && tty->termios->c_cflag == old->c_cflag)
++              return;
++      rs285_set_cflag(tty->termios->c_cflag);
++}
++
++
++static void rs285_stop(struct tty_struct *tty)
++{
++      rs285_stop_tx();
++}
++
++static void rs285_start(struct tty_struct *tty)
++{
++      rs285_start_tx();
++}
++
++static void rs285_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++      int orig_jiffies = jiffies;
++      while (*CSR_UARTFLG & 8) {
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(1);
++              if (signal_pending(current))
++                      break;
++              if (timeout && time_after(jiffies, orig_jiffies + timeout))
++                      break;
++      }
++      set_current_state(TASK_RUNNING);
++}
++
++static int rs285_open(struct tty_struct *tty, struct file *filp)
++{
++      int line, ret;
++
++      MOD_INC_USE_COUNT;
++
++      line = MINOR(tty->device) - tty->driver.minor_start;
++      if (line)
++              return -ENODEV;
++
++      ret = down_interruptible(&rs285_sem);
++      if (ret)
++              return ret;
++
++      tty->driver_data = NULL;
++      rs285_tty = tty;
++
++      if (rs285_use_count == 0) {
++              rs285_irq_enabled = 3;
++              ret = request_irq(IRQ_CONRX, rs285_rx_int, 0, "rs285", NULL);
++              if (ret == 0) {
++                      ret = request_irq(IRQ_CONTX, rs285_tx_int, 0, "rs285",
++                                        NULL);
++                      if (ret)
++                              free_irq(IRQ_CONRX, NULL);
++              }
++      }
++
++      if (ret == 0)
++              rs285_use_count++;
++
++      up(&rs285_sem);
++
++      return ret;
++}
++
++static void rs285_close(struct tty_struct *tty, struct file *filp)
++{
++      down(&rs285_sem);
++      if (!--rs285_use_count) {
++              rs285_wait_until_sent(tty, 0);
++              rs285_stop_rx();
++              rs285_stop_tx();
++              rs285_tty = NULL;
++              free_irq(IRQ_CONTX, NULL);
++              free_irq(IRQ_CONRX, NULL);
++      }
++      up(&rs285_sem);
++      MOD_DEC_USE_COUNT;
++}
++
++static int __init rs285_init(void)
++{
++      int baud = B9600;
++
++      if (machine_is_personal_server())
++              baud = B57600;
++
++      rs285_driver.magic = TTY_DRIVER_MAGIC;
++      rs285_driver.driver_name = "serial_21285";
++      rs285_driver.name = SERIAL_21285_NAME;
++      rs285_driver.major = SERIAL_21285_MAJOR;
++      rs285_driver.minor_start = SERIAL_21285_MINOR;
++      rs285_driver.num = 1;
++      rs285_driver.type = TTY_DRIVER_TYPE_SERIAL;
++      rs285_driver.subtype = SERIAL_TYPE_NORMAL;
++      rs285_driver.init_termios = tty_std_termios;
++      rs285_driver.init_termios.c_cflag = baud | CS8 | CREAD | HUPCL | CLOCAL;
++      rs285_driver.flags = TTY_DRIVER_REAL_RAW;
++      rs285_driver.refcount = &rs285_refcount;
++      rs285_driver.table = rs285_table;
++      rs285_driver.termios = rs285_termios;
++      rs285_driver.termios_locked = rs285_termios_locked;
++
++      rs285_driver.open = rs285_open;
++      rs285_driver.close = rs285_close;
++      rs285_driver.write = rs285_write;
++      rs285_driver.put_char = rs285_put_char;
++      rs285_driver.write_room = rs285_write_room;
++      rs285_driver.chars_in_buffer = rs285_chars_in_buffer;
++      rs285_driver.flush_buffer = rs285_flush_buffer;
++      rs285_driver.throttle = rs285_throttle;
++      rs285_driver.unthrottle = rs285_unthrottle;
++      rs285_driver.send_xchar = rs285_send_xchar;
++      rs285_driver.set_termios = rs285_set_termios;
++      rs285_driver.stop = rs285_stop;
++      rs285_driver.start = rs285_start;
++      rs285_driver.wait_until_sent = rs285_wait_until_sent;
++
++      callout_driver = rs285_driver;
++      callout_driver.name = SERIAL_21285_AUXNAME;
++      callout_driver.major = SERIAL_21285_AUXMAJOR;
++      callout_driver.subtype = SERIAL_TYPE_CALLOUT;
++
++#ifdef CONFIG_SERIAL_21285_OLD
++      if (!machine_is_ebsa285() && !machine_is_netwinder()) {
++              rs285_old_driver = rs285_driver;
++              rs285_old_driver.name = SERIAL_21285_OLD_NAME;
++              rs285_old_driver.major = SERIAL_21285_OLD_MAJOR;
++              rs285_old_driver.minor_start = SERIAL_21285_OLD_MINOR;
++
++              if (tty_register_driver(&rs285_old_driver))
++                      printk(KERN_ERR "Couldn't register old 21285 serial driver\n");
++      }
++#endif
++
++      if (tty_register_driver(&rs285_driver))
++              printk(KERN_ERR "Couldn't register 21285 serial driver\n");
++      if (tty_register_driver(&callout_driver))
++              printk(KERN_ERR "Couldn't register 21285 callout driver\n");
++
++      return 0;
++}
++
++static void __exit rs285_fini(void)
++{
++      unsigned long flags;
++      int ret;
++
++      save_flags(flags);
++      cli();
++      ret = tty_unregister_driver(&callout_driver);
++      if (ret)
++              printk(KERN_ERR "Unable to unregister 21285 callout driver "
++                      "(%d)\n", ret);
++      ret = tty_unregister_driver(&rs285_driver);
++      if (ret)
++              printk(KERN_ERR "Unable to unregister 21285 driver (%d)\n",
++                      ret);
++#ifdef CONFIG_SERIAL_21285_OLD
++      if (!machine_is_ebsa285() && !machine_is_netwinder()) {
++              ret = tty_unregister_driver(&rs285_old_driver);
++              if (ret)
++                      printk(KERN_ERR "Unable to unregister old 21285 "
++                              "driver (%d)\n", ret);
++      }
++#endif
++      free_irq(IRQ_CONTX, NULL);
++      free_irq(IRQ_CONRX, NULL);
++      restore_flags(flags);
++}
++
++module_init(rs285_init);
++module_exit(rs285_fini);
++
++#ifdef CONFIG_SERIAL_21285_CONSOLE
++/************** console driver *****************/
++
++static void rs285_console_write(struct console *co, const char *s, u_int count)
++{
++      int i;
++
++      rs285_stop_tx();
++      for (i = 0; i < count; i++) {
++              while (*CSR_UARTFLG & 0x20);
++              *CSR_UARTDR = s[i];
++              if (s[i] == '\n') {
++                      while (*CSR_UARTFLG & 0x20);
++                      *CSR_UARTDR = '\r';
++              }
++      }
++      rs285_start_tx();
++}
++
++static kdev_t rs285_console_device(struct console *c)
++{
++      return MKDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
++}
++
++static int __init rs285_console_setup(struct console *co, char *options)
++{
++      int baud = 9600;
++      int bits = 8;
++      int parity = 'n';
++      int flow;
++      int cflag = CREAD | HUPCL | CLOCAL;
++
++      if (machine_is_personal_server())
++              baud = 57600;
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++
++      /*
++       *    Now construct a cflag setting.
++       */
++      switch (baud) {
++      case 1200:
++              cflag |= B1200;
++              break;
++      case 2400:
++              cflag |= B2400;
++              break;
++      case 4800:
++              cflag |= B4800;
++              break;
++      case 9600:
++              cflag |= B9600;
++              break;
++      case 19200:
++              cflag |= B19200;
++              break;
++      case 38400:
++              cflag |= B38400;
++              break;
++      case 57600:
++              cflag |= B57600;
++              break;
++      case 115200:
++              cflag |= B115200;
++              break;
++      default:
++              cflag |= B9600;
++              break;
++      }
++      switch (bits) {
++      case 7:
++              cflag |= CS7;
++              break;
++      default:
++              cflag |= CS8;
++              break;
++      }
++      switch (parity) {
++      case 'o':
++      case 'O':
++              cflag |= PARODD;
++              break;
++      case 'e':
++      case 'E':
++              cflag |= PARENB;
++              break;
++      }
++      co->cflag = cflag;
++      rs285_set_cflag(cflag);
++      rs285_console_write(NULL, "\e[2J\e[Hboot ", 12);
++      if (options)
++              rs285_console_write(NULL, options, strlen(options));
++      else
++              rs285_console_write(NULL, "no options", 10);
++      rs285_console_write(NULL, "\n", 1);
++
++      return 0;
++}
++
++#ifdef CONFIG_SERIAL_21285_OLD
++static struct console rs285_old_cons =
++{
++      SERIAL_21285_OLD_NAME,
++      rs285_console_write,
++      NULL,
++      rs285_console_device,
++      NULL,
++      rs285_console_setup,
++      CON_PRINTBUFFER,
++      -1,
++      0,
++      NULL
++};
++#endif
++
++static struct console rs285_cons =
++{
++      name:           SERIAL_21285_NAME,
++      write:          rs285_console_write,
++      device:         rs285_console_device,
++      setup:          rs285_console_setup,
++      flags:          CON_PRINTBUFFER,
++      index:          -1,
++};
++
++void __init rs285_console_init(void)
++{
++#ifdef CONFIG_SERIAL_21285_OLD
++      if (!machine_is_ebsa285() && !machine_is_netwinder())
++              register_console(&rs285_old_cons);
++#endif
++      register_console(&rs285_cons);
++}
++
++#endif /* CONFIG_SERIAL_21285_CONSOLE */
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver");
+--- /dev/null
++++ linux-2.4.27/drivers/serial/8250.c
+@@ -0,0 +1,2170 @@
++/*
++ *  linux/drivers/serial/8250.c
++ *
++ *  Driver for 8250/16550-type serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright (C) 2001 Russell King.
++ *
++ * 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: 8250.c,v 1.14.2.8 2002/10/24 14:31:31 rmk Exp $
++ *
++ * A note about mapbase / membase
++ *
++ *  mapbase is the physical address of the IO port.  Currently, we don't
++ *  support this very well, and it may well be dropped from this driver
++ *  in future.  As such, mapbase should be NULL.
++ *
++ *  membase is an 'ioremapped' cookie.  This is compatible with the old
++ *  serial.c driver, and is currently the preferred form.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/serial_reg.h>
++#include <linux/serialP.h>
++#include <linux/delay.h>
++#include <linux/serial_core.h>
++#include <linux/kmod.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#include "8250.h"
++
++/*
++ * Configuration:
++ *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
++ *                is unsafe when used on edge-triggered interrupts.
++ */
++unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
++
++/*
++ * Debugging.
++ */
++#if 0
++#define DEBUG_AUTOCONF(fmt...)        printk(fmt)
++#else
++#define DEBUG_AUTOCONF(fmt...)        do { } while (0)
++#endif
++
++#if 0
++#define DEBUG_INTR(fmt...)    printk(fmt)
++#else
++#define DEBUG_INTR(fmt...)    do { } while (0)
++#endif
++
++#define PASS_LIMIT    256
++
++/*
++ * We default to IRQ0 for the "no irq" hack.   Some
++ * machine types want others as well - they're free
++ * to redefine this in their header file.
++ */
++#define is_real_interrupt(irq)        ((irq) != 0)
++
++/*
++ * This converts from our new CONFIG_ symbols to the symbols
++ * that asm/serial.h expects.  You _NEED_ to comment out the
++ * linux/config.h include contained inside asm/serial.h for
++ * this to work.
++ */
++#undef CONFIG_SERIAL_MANY_PORTS
++#undef CONFIG_SERIAL_DETECT_IRQ
++#undef CONFIG_SERIAL_MULTIPORT
++#undef CONFIG_HUB6
++
++#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
++#define CONFIG_SERIAL_DETECT_IRQ 1
++#endif
++#ifdef CONFIG_SERIAL_8250_MULTIPORT
++#define CONFIG_SERIAL_MULTIPORT 1
++#endif
++#ifdef CONFIG_SERIAL_8250_HUB6
++#define CONFIG_HUB6 1
++#endif
++#ifdef CONFIG_SERIAL_8250_MANY_PORTS
++#define CONFIG_SERIAL_MANY_PORTS 1
++#endif
++
++#include <asm/serial.h>
++
++static struct old_serial_port old_serial_port[] = {
++      SERIAL_PORT_DFNS /* defined in asm/serial.h */
++};
++
++#define UART_NR       ARRAY_SIZE(old_serial_port)
++
++static struct tty_driver normal, callout;
++static struct tty_struct *serial8250_table[UART_NR];
++static struct termios *serial8250_termios[UART_NR], *serial8250_termios_locked[UART_NR];
++
++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
++
++#define PORT_RSA_MAX 4
++static int probe_rsa[PORT_RSA_MAX];
++static int force_rsa[PORT_RSA_MAX];
++#endif /* CONFIG_SERIAL_8250_RSA  */
++
++struct uart_8250_port {
++      struct uart_port        port;
++      struct timer_list       timer;          /* "no irq" timer */
++      struct list_head        list;           /* ports on this IRQ */
++      unsigned int            capabilities;   /* port capabilities */
++      unsigned char           acr;
++      unsigned char           ier;
++      unsigned short          rev;
++      unsigned char           lcr;
++      unsigned char           mcr;
++      unsigned char           mcr_mask;       /* mask of user bits */
++      unsigned char           mcr_force;      /* mask of forced bits */
++      unsigned char           efr;
++      unsigned int            lsr_break_flag;
++
++      /*
++       * We provide a per-port pm hook.
++       */
++      void                    (*pm)(struct uart_port *port,
++                                    unsigned int state, unsigned int old);
++};
++
++struct irq_info {
++      spinlock_t              lock;
++      struct list_head        *head;
++};
++
++static struct irq_info irq_lists[NR_IRQS];
++
++/*
++ * Here we define the default xmit fifo size used for each type of UART.
++ */
++static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {
++      { "unknown",    1,      0 },
++      { "8250",       1,      0 },
++      { "16450",      1,      0 },
++      { "16550",      1,      0 },
++      { "16550A",     16,     UART_CLEAR_FIFO | UART_USE_FIFO },
++      { "Cirrus",     1,      0 },
++      { "ST16650",    1,      UART_CLEAR_FIFO | UART_STARTECH },
++      { "ST16650V2",  32,     UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
++      { "TI16750",    64,     UART_CLEAR_FIFO | UART_USE_FIFO },
++      { "Startech",   1,      0 },
++      { "16C950/954", 128,    UART_CLEAR_FIFO | UART_USE_FIFO },
++      { "ST16654",    64,     UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
++      { "XR16850",    128,    UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
++      { "RSA",        2048,   UART_CLEAR_FIFO | UART_USE_FIFO }
++};
++
++static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
++{
++      offset <<= up->port.regshift;
++
++      switch (up->port.iotype) {
++#ifdef CONFIG_SERIAL_8250_HUB6
++      case SERIAL_IO_HUB6:
++              outb(up->port.hub6 - 1 + offset, up->port.iobase);
++              return inb(up->port.iobase + 1);
++#endif
++
++      case SERIAL_IO_MEM:
++              return readb(up->port.membase + offset);
++
++      default:
++              return inb(up->port.iobase + offset);
++      }
++}
++
++static _INLINE_ void
++serial_out(struct uart_8250_port *up, int offset, int value)
++{
++      offset <<= up->port.regshift;
++
++      switch (up->port.iotype) {
++#ifdef CONFIG_SERIAL_8250_HUB6
++      case SERIAL_IO_HUB6:
++              outb(up->port.hub6 - 1 + offset, up->port.iobase);
++              outb(value, up->port.iobase + 1);
++              break;
++#endif
++
++      case SERIAL_IO_MEM:
++              writeb(value, up->port.membase + offset);
++              break;
++
++      default:
++              outb(value, up->port.iobase + offset);
++      }
++}
++
++/*
++ * We used to support using pause I/O for certain machines.  We
++ * haven't supported this for a while, but just in case it's badly
++ * needed for certain old 386 machines, I've left these #define's
++ * in....
++ */
++#define serial_inp(up, offset)                serial_in(up, offset)
++#define serial_outp(up, offset, value)        serial_out(up, offset, value)
++
++
++/*
++ * For the 16C950
++ */
++static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
++{
++      serial_out(up, UART_SCR, offset);
++      serial_out(up, UART_ICR, value);
++}
++
++static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
++{
++      unsigned int value;
++
++      serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
++      serial_out(up, UART_SCR, offset);
++      value = serial_in(up, UART_ICR);
++      serial_icr_write(up, UART_ACR, up->acr);
++
++      return value;
++}
++
++#ifdef CONFIG_SERIAL_8250_RSA
++/*
++ * Attempts to turn on the RSA FIFO.  Returns zero on failure.
++ * We set the port uart clock rate if we succeed.
++ */
++static int __enable_rsa(struct uart_8250_port *up)
++{
++      unsigned char mode;
++      int result;
++
++      mode = serial_inp(up, UART_RSA_MSR);
++      result = mode & UART_RSA_MSR_FIFO;
++
++      if (!result) {
++              serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
++              mode = serial_inp(up, UART_RSA_MSR);
++              result = mode & UART_RSA_MSR_FIFO;
++      }
++
++      if (result)
++              up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
++
++      return result;
++}
++
++static void enable_rsa(struct uart_8250_port *up)
++{
++      if (up->port.type == PORT_RSA) {
++              if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
++                      spin_lock_irq(&up->port.lock);
++                      __enable_rsa(up);
++                      spin_unlock_irq(&up->port.lock);
++              }
++              if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
++                      serial_outp(up, UART_RSA_FRR, 0);
++      }
++}
++
++/*
++ * Attempts to turn off the RSA FIFO.  Returns zero on failure.
++ * It is unknown why interrupts were disabled in here.  However,
++ * the caller is expected to preserve this behaviour by grabbing
++ * the spinlock before calling this function.
++ */
++static void disable_rsa(struct uart_8250_port *up)
++{
++      unsigned char mode;
++      int result;
++
++      if (up->port.type == PORT_RSA &&
++          up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
++              spin_lock_irq(&up->port.lock);
++
++              mode = serial_inp(up, UART_RSA_MSR);
++              result = !(mode & UART_RSA_MSR_FIFO);
++
++              if (!result) {
++                      serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
++                      mode = serial_inp(up, UART_RSA_MSR);
++                      result = !(mode & UART_RSA_MSR_FIFO);
++              }
++
++              if (result)
++                      up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
++              spin_unlock_irq(&up->port.lock);
++      }
++}
++#endif /* CONFIG_SERIAL_8250_RSA */
++
++/*
++ * This is a quickie test to see how big the FIFO is.
++ * It doesn't work at all the time, more's the pity.
++ */
++static int size_fifo(struct uart_8250_port *up)
++{
++      unsigned char old_fcr, old_mcr, old_dll, old_dlm;
++      int count;
++
++      old_fcr = serial_inp(up, UART_FCR);
++      old_mcr = serial_inp(up, UART_MCR);
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
++                  UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++      serial_outp(up, UART_MCR, UART_MCR_LOOP);
++      serial_outp(up, UART_LCR, UART_LCR_DLAB);
++      old_dll = serial_inp(up, UART_DLL);
++      old_dlm = serial_inp(up, UART_DLM);
++      serial_outp(up, UART_DLL, 0x01);
++      serial_outp(up, UART_DLM, 0x00);
++      serial_outp(up, UART_LCR, 0x03);
++      for (count = 0; count < 256; count++)
++              serial_outp(up, UART_TX, count);
++      mdelay(20);/* FIXME - schedule_timeout */
++      for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
++           (count < 256); count++)
++              serial_inp(up, UART_RX);
++      serial_outp(up, UART_FCR, old_fcr);
++      serial_outp(up, UART_MCR, old_mcr);
++      serial_outp(up, UART_LCR, UART_LCR_DLAB);
++      serial_outp(up, UART_DLL, old_dll);
++      serial_outp(up, UART_DLM, old_dlm);
++
++      return count;
++}
++
++/*
++ * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
++ * When this function is called we know it is at least a StarTech
++ * 16650 V2, but it might be one of several StarTech UARTs, or one of
++ * its clones.  (We treat the broken original StarTech 16650 V1 as a
++ * 16550, and why not?  Startech doesn't seem to even acknowledge its
++ * existence.)
++ * 
++ * What evil have men's minds wrought...
++ */
++static void autoconfig_has_efr(struct uart_8250_port *up)
++{
++      unsigned char id1, id2, id3, rev, saved_dll, saved_dlm;
++
++      /*
++       * First we check to see if it's an Oxford Semiconductor UART.
++       *
++       * If we have to do this here because some non-National
++       * Semiconductor clone chips lock up if you try writing to the
++       * LSR register (which serial_icr_read does)
++       */
++
++      /*
++       * Check for Oxford Semiconductor 16C950.
++       *
++       * EFR [4] must be set else this test fails.
++       *
++       * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca)
++       * claims that it's needed for 952 dual UART's (which are not
++       * recommended for new designs).
++       */
++      up->acr = 0;
++      serial_out(up, UART_LCR, 0xBF);
++      serial_out(up, UART_EFR, 0x10);
++      serial_out(up, UART_LCR, 0x00);
++      id1 = serial_icr_read(up, UART_ID1);
++      id2 = serial_icr_read(up, UART_ID2);
++      id3 = serial_icr_read(up, UART_ID3);
++      rev = serial_icr_read(up, UART_REV);
++
++      DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);
++
++      if (id1 == 0x16 && id2 == 0xC9 &&
++          (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
++              up->port.type = PORT_16C950;
++              up->rev = rev | (id3 << 8);
++              return;
++      }
++      
++      /*
++       * We check for a XR16C850 by setting DLL and DLM to 0, and then
++       * reading back DLL and DLM.  The chip type depends on the DLM
++       * value read back:
++       *  0x10 - XR16C850 and the DLL contains the chip revision.
++       *  0x12 - XR16C2850.
++       *  0x14 - XR16C854.
++       */
++      serial_outp(up, UART_LCR, UART_LCR_DLAB);
++      saved_dll = serial_inp(up, UART_DLL);
++      saved_dlm = serial_inp(up, UART_DLM);
++      serial_outp(up, UART_DLL, 0);
++      serial_outp(up, UART_DLM, 0);
++      id2 = serial_inp(up, UART_DLL);
++      id1 = serial_inp(up, UART_DLM);
++      serial_outp(up, UART_DLL, saved_dll);
++      serial_outp(up, UART_DLM, saved_dlm);
++
++      DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2);
++
++      if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) {
++              if (id1 == 0x10)
++                      up->rev = id2;
++              up->port.type = PORT_16850;
++              return;
++      }
++
++      /*
++       * It wasn't an XR16C850.
++       *
++       * We distinguish between the '654 and the '650 by counting
++       * how many bytes are in the FIFO.  I'm using this for now,
++       * since that's the technique that was sent to me in the
++       * serial driver update, but I'm not convinced this works.
++       * I've had problems doing this in the past.  -TYT
++       */
++      if (size_fifo(up) == 64)
++              up->port.type = PORT_16654;
++      else
++              up->port.type = PORT_16650V2;
++}
++
++/*
++ * We detected a chip without a FIFO.  Only two fall into
++ * this category - the original 8250 and the 16450.  The
++ * 16450 has a scratch register (accessible with LCR=0)
++ */
++static void autoconfig_8250(struct uart_8250_port *up)
++{
++      unsigned char scratch, status1, status2;
++
++      up->port.type = PORT_8250;
++
++      scratch = serial_in(up, UART_SCR);
++      serial_outp(up, UART_SCR, 0xa5);
++      status1 = serial_in(up, UART_SCR);
++      serial_outp(up, UART_SCR, 0x5a);
++      status2 = serial_in(up, UART_SCR);
++      serial_outp(up, UART_SCR, scratch);
++
++      if (status1 == 0xa5 && status2 == 0x5a)
++              up->port.type = PORT_16450;
++}
++
++/*
++ * We know that the chip has FIFOs.  Does it have an EFR?  The
++ * EFR is located in the same register position as the IIR and
++ * we know the top two bits of the IIR are currently set.  The
++ * EFR should contain zero.  Try to read the EFR.
++ */
++static void autoconfig_16550a(struct uart_8250_port *up)
++{
++      unsigned char status1, status2;
++
++      up->port.type = PORT_16550A;
++
++      /*
++       * Check for presence of the EFR when DLAB is set.
++       * Only ST16C650V1 UARTs pass this test.
++       */
++      serial_outp(up, UART_LCR, UART_LCR_DLAB);
++      if (serial_in(up, UART_EFR) == 0) {
++              DEBUG_AUTOCONF("EFRv1 ");
++              up->port.type = PORT_16650;
++              return;
++      }
++
++      /*
++       * Maybe it requires 0xbf to be written to the LCR.
++       * (other ST16C650V2 UARTs, TI16C752A, etc)
++       */
++      serial_outp(up, UART_LCR, 0xBF);
++      if (serial_in(up, UART_EFR) == 0) {
++              DEBUG_AUTOCONF("EFRv2 ");
++              autoconfig_has_efr(up);
++              return;
++      }
++
++      /*
++       * Check for a National Semiconductor SuperIO chip.
++       * Attempt to switch to bank 2, read the value of the LOOP bit
++       * from EXCR1. Switch back to bank 0, change it in MCR. Then
++       * switch back to bank 2, read it from EXCR1 again and check
++       * it's changed. If so, set baud_base in EXCR2 to 921600.
++       */
++      serial_outp(up, UART_LCR, 0);
++      status1 = serial_in(up, UART_MCR);
++      serial_outp(up, UART_LCR, 0xE0);
++      status2 = serial_in(up, 0x02); /* EXCR1 */
++
++      if (!((status2 ^ status1) & UART_MCR_LOOP)) {
++              serial_outp(up, UART_LCR, 0);
++              serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP);
++              serial_outp(up, UART_LCR, 0xE0);
++              status2 = serial_in(up, 0x02); /* EXCR1 */
++              serial_outp(up, UART_LCR, 0);
++              serial_outp(up, UART_MCR, status1);
++
++              if ((status2 ^ status1) & UART_MCR_LOOP) {
++                      serial_outp(up, UART_LCR, 0xE0);
++                      status1 = serial_in(up, 0x04); /* EXCR1 */
++                      status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
++                      status1 |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
++                      serial_outp(up, 0x04, status1);
++                      serial_outp(up, UART_LCR, 0);
++
++                      up->port.type = PORT_NS16550A;
++                      up->port.uartclk = 921600*16;
++                      return;
++              }
++      }
++
++      /*
++       * No EFR.  Try to detect a TI16750, which only sets bit 5 of
++       * the IIR when 64 byte FIFO mode is enabled when DLAB is set.
++       * Try setting it with and without DLAB set.  Cheap clones
++       * set bit 5 without DLAB set.
++       */
++      serial_outp(up, UART_LCR, 0);
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
++      status1 = serial_in(up, UART_IIR) >> 5;
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
++      serial_outp(up, UART_LCR, UART_LCR_DLAB);
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
++      status2 = serial_in(up, UART_IIR) >> 5;
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
++
++      DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
++
++      if (status1 == 6 && status2 == 7) {
++              up->port.type = PORT_16750;
++              return;
++      }
++}
++
++/*
++ * This routine is called by rs_init() to initialize a specific serial
++ * port.  It determines what type of UART chip this serial port is
++ * using: 8250, 16450, 16550, 16550A.  The important question is
++ * whether or not this UART is a 16550A or not, since this will
++ * determine whether or not we can use its FIFO features or not.
++ */
++static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
++{
++      unsigned char status1, scratch, scratch2, scratch3;
++      unsigned char save_lcr, save_mcr;
++      unsigned long flags;
++
++      if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
++              return;
++
++      DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ",
++                      up->port.line, up->port.iobase, up->port.membase);
++
++      /*
++       * We really do need global IRQs disabled here - we're going to
++       * be frobbing the chips IRQ enable register to see if it exists.
++       */
++      spin_lock_irqsave(&up->port.lock, flags);
++
++      if (!(up->port.flags & UPF_BUGGY_UART)) {
++              /*
++               * Do a simple existence test first; if we fail this,
++               * there's no point trying anything else.
++               * 
++               * 0x80 is used as a nonsense port to prevent against
++               * false positives due to ISA bus float.  The
++               * assumption is that 0x80 is a non-existent port;
++               * which should be safe since include/asm/io.h also
++               * makes this assumption.
++               *
++               * Note: this is safe as long as MCR bit 4 is clear
++               * and the device is in "PC" mode.
++               */
++              scratch = serial_inp(up, UART_IER);
++              serial_outp(up, UART_IER, 0);
++#ifdef __i386__
++              outb(0xff, 0x080);
++#endif
++              scratch2 = serial_inp(up, UART_IER);
++              serial_outp(up, UART_IER, 0x0F);
++#ifdef __i386__
++              outb(0, 0x080);
++#endif
++              scratch3 = serial_inp(up, UART_IER);
++              serial_outp(up, UART_IER, scratch);
++              if (scratch2 != 0 || scratch3 != 0x0F) {
++                      /*
++                       * We failed; there's nothing here
++                       */
++                      DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
++                                     scratch2, scratch3);
++                      goto out;
++              }
++      }
++
++      save_mcr = serial_in(up, UART_MCR);
++      save_lcr = serial_in(up, UART_LCR);
++
++      /* 
++       * Check to see if a UART is really there.  Certain broken
++       * internal modems based on the Rockwell chipset fail this
++       * test, because they apparently don't implement the loopback
++       * test mode.  So this test is skipped on the COM 1 through
++       * COM 4 ports.  This *should* be safe, since no board
++       * manufacturer would be stupid enough to design a board
++       * that conflicts with COM 1-4 --- we hope!
++       */
++      if (!(up->port.flags & UPF_SKIP_TEST)) {
++              serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
++              status1 = serial_inp(up, UART_MSR) & 0xF0;
++              serial_outp(up, UART_MCR, save_mcr);
++              if (status1 != 0x90) {
++                      DEBUG_AUTOCONF("LOOP test failed (%02x) ",
++                                     status1);
++                      goto out;
++              }
++      }
++
++      /*
++       * We're pretty sure there's a port here.  Lets find out what
++       * type of port it is.  The IIR top two bits allows us to find
++       * out if its 8250 or 16450, 16550, 16550A or later.  This
++       * determines what we test for next.
++       *
++       * We also initialise the EFR (if any) to zero for later.  The
++       * EFR occupies the same register location as the FCR and IIR.
++       */
++      serial_outp(up, UART_LCR, 0xBF);
++      serial_outp(up, UART_EFR, 0);
++      serial_outp(up, UART_LCR, 0);
++
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
++      scratch = serial_in(up, UART_IIR) >> 6;
++
++      DEBUG_AUTOCONF("iir=%d ", scratch);
++
++      switch (scratch) {
++      case 0:
++              autoconfig_8250(up);
++              break;
++      case 1:
++              up->port.type = PORT_UNKNOWN;
++              break;
++      case 2:
++              up->port.type = PORT_16550;
++              break;
++      case 3:
++              autoconfig_16550a(up);
++              break;
++      }
++
++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
++      /*
++       * Only probe for RSA ports if we got the region.
++       */
++      if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
++              int i;
++
++              for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
++                      if (!probe_rsa[i] && !force_rsa[i])
++                              break;
++                      if (((probe_rsa[i] != up->port.iobase) ||
++                           check_region(up->port.iobase + UART_RSA_BASE, 16)) &&
++                          (force_rsa[i] != up->port.iobase))
++                              continue;
++                      if (__enable_rsa(up)) {
++                              up->port.type = PORT_RSA;
++                              break;
++                      }
++              }
++      }
++#endif
++      serial_outp(up, UART_LCR, save_lcr);
++
++      up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
++      up->capabilities = uart_config[up->port.type].flags;
++
++      if (up->port.type == PORT_UNKNOWN)
++              goto out;
++
++      /*
++       * Reset the UART.
++       */
++#ifdef CONFIG_SERIAL_8250_RSA
++      if (up->port.type == PORT_RSA)
++              serial_outp(up, UART_RSA_FRR, 0);
++#endif
++      serial_outp(up, UART_MCR, save_mcr);
++      serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
++                                   UART_FCR_CLEAR_RCVR |
++                                   UART_FCR_CLEAR_XMIT));
++      serial_outp(up, UART_FCR, 0);
++      (void)serial_in(up, UART_RX);
++      serial_outp(up, UART_IER, 0);
++
++ out:
++      spin_unlock_irqrestore(&up->port.lock, flags);
++
++#ifdef CONFIG_SERIAL_8250_RSA
++      if (up->port.iobase && up->port.type == PORT_RSA) {
++              release_region(up->port.iobase, 8);
++              request_region(up->port.iobase + UART_RSA_BASE, 16,
++                             "serial_rsa");
++      }
++#endif
++      DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
++}
++
++static void autoconfig_irq(struct uart_8250_port *up)
++{
++      unsigned char save_mcr, save_ier;
++      unsigned char save_ICP = 0;
++      unsigned int ICP = 0;
++      unsigned long irqs;
++      int irq;
++
++      if (up->port.flags & UPF_FOURPORT) {
++              ICP = (up->port.iobase & 0xfe0) | 0x1f;
++              save_ICP = inb_p(ICP);
++              outb_p(0x80, ICP);
++              (void) inb_p(ICP);
++      }
++
++      /* forget possible initially masked and pending IRQ */
++      probe_irq_off(probe_irq_on());
++      save_mcr = serial_inp(up, UART_MCR);
++      save_ier = serial_inp(up, UART_IER);
++      serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
++      
++      irqs = probe_irq_on();
++      serial_outp(up, UART_MCR, 0);
++      udelay (10);
++      if (up->port.flags & UPF_FOURPORT)  {
++              serial_outp(up, UART_MCR,
++                          UART_MCR_DTR | UART_MCR_RTS);
++      } else {
++              serial_outp(up, UART_MCR,
++                          UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
++      }
++      serial_outp(up, UART_IER, 0x0f);        /* enable all intrs */
++      (void)serial_inp(up, UART_LSR);
++      (void)serial_inp(up, UART_RX);
++      (void)serial_inp(up, UART_IIR);
++      (void)serial_inp(up, UART_MSR);
++      serial_outp(up, UART_TX, 0xFF);
++      udelay (20);
++      irq = probe_irq_off(irqs);
++
++      serial_outp(up, UART_MCR, save_mcr);
++      serial_outp(up, UART_IER, save_ier);
++
++      if (up->port.flags & UPF_FOURPORT)
++              outb_p(save_ICP, ICP);
++
++      up->port.irq = (irq > 0) ? irq : 0;
++}
++
++static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++
++      if (up->ier & UART_IER_THRI) {
++              up->ier &= ~UART_IER_THRI;
++              serial_out(up, UART_IER, up->ier);
++      }
++      if (up->port.type == PORT_16C950 && tty_stop) {
++              up->acr |= UART_ACR_TXDIS;
++              serial_icr_write(up, UART_ACR, up->acr);
++      }
++}
++
++static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++
++      if (!(up->ier & UART_IER_THRI)) {
++              up->ier |= UART_IER_THRI;
++              serial_out(up, UART_IER, up->ier);
++      }
++      /*
++       * We only do this from uart_start
++       */
++      if (tty_start && up->port.type == PORT_16C950) {
++              up->acr &= ~UART_ACR_TXDIS;
++              serial_icr_write(up, UART_ACR, up->acr);
++      }
++}
++
++static void serial8250_stop_rx(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++
++      up->ier &= ~UART_IER_RLSI;
++      up->port.read_status_mask &= ~UART_LSR_DR;
++      serial_out(up, UART_IER, up->ier);
++}
++
++static void serial8250_enable_ms(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++
++      up->ier |= UART_IER_MSI;
++      serial_out(up, UART_IER, up->ier);
++}
++
++static _INLINE_ void
++receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
++{
++      struct tty_struct *tty = up->port.info->tty;
++      unsigned char ch;
++      int max_count = 256;
++
++      do {
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++                      /*
++                       * FIXME: Deadlock can happen here if we're a
++                       * low-latency port.  We're holding the per-port
++                       * spinlock, and we call flush_to_ldisc->
++                       * n_tty_receive_buf->n_tty_receive_char->
++                       * opost->uart_put_char.
++                       */
++                      tty->flip.tqueue.routine((void *)tty);
++                      if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                              return; // if TTY_DONT_FLIP is set
++              }
++              ch = serial_inp(up, UART_RX);
++              *tty->flip.char_buf_ptr = ch;
++              *tty->flip.flag_buf_ptr = TTY_NORMAL;
++              up->port.icount.rx++;
++
++              if (*status & (UART_LSR_BI | UART_LSR_PE |
++                             UART_LSR_FE | UART_LSR_OE)) {
++                      /*
++                       * For statistics only
++                       */
++                      if (*status & UART_LSR_BI) {
++                              *status &= ~(UART_LSR_FE | UART_LSR_PE);
++                              up->port.icount.brk++;
++                              /*
++                               * We do the SysRQ and SAK checking
++                               * here because otherwise the break
++                               * may get masked by ignore_status_mask
++                               * or read_status_mask.
++                               */
++                              if (uart_handle_break(&up->port))
++                                      goto ignore_char;
++                      } else if (*status & UART_LSR_PE)
++                              up->port.icount.parity++;
++                      else if (*status & UART_LSR_FE)
++                              up->port.icount.frame++;
++                      if (*status & UART_LSR_OE)
++                              up->port.icount.overrun++;
++
++                      /*
++                       * Mask off conditions which should be ingored.
++                       */
++                      *status &= up->port.read_status_mask;
++
++#ifdef CONFIG_SERIAL_8250_CONSOLE
++                      if (up->port.line == up->port.cons->index) {
++                              /* Recover the break flag from console xmit */
++                              *status |= up->lsr_break_flag;
++                              up->lsr_break_flag = 0;
++                      }
++#endif
++                      if (*status & UART_LSR_BI) {
++                              DEBUG_INTR("handling break....");
++                              *tty->flip.flag_buf_ptr = TTY_BREAK;
++                      } else if (*status & UART_LSR_PE)
++                              *tty->flip.flag_buf_ptr = TTY_PARITY;
++                      else if (*status & UART_LSR_FE)
++                              *tty->flip.flag_buf_ptr = TTY_FRAME;
++              }
++              if (uart_handle_sysrq_char(&up->port, ch, regs))
++                      goto ignore_char;
++              if ((*status & up->port.ignore_status_mask) == 0) {
++                      tty->flip.flag_buf_ptr++;
++                      tty->flip.char_buf_ptr++;
++                      tty->flip.count++;
++              }
++              if ((*status & UART_LSR_OE) &&
++                  tty->flip.count < TTY_FLIPBUF_SIZE) {
++                      /*
++                       * Overrun is special, since it's reported
++                       * immediately, and doesn't affect the current
++                       * character.
++                       */
++                      *tty->flip.flag_buf_ptr = TTY_OVERRUN;
++                      tty->flip.flag_buf_ptr++;
++                      tty->flip.char_buf_ptr++;
++                      tty->flip.count++;
++              }
++      ignore_char:
++              *status = serial_inp(up, UART_LSR);
++      } while ((*status & UART_LSR_DR) && (max_count-- > 0));
++      tty_flip_buffer_push(tty);
++}
++
++static _INLINE_ void transmit_chars(struct uart_8250_port *up)
++{
++      struct circ_buf *xmit = &up->port.info->xmit;
++      int count;
++
++      if (up->port.x_char) {
++              serial_outp(up, UART_TX, up->port.x_char);
++              up->port.icount.tx++;
++              up->port.x_char = 0;
++              return;
++      }
++      if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
++              serial8250_stop_tx(&up->port, 0);
++              return;
++      }
++
++      count = up->port.fifosize;
++      do {
++              serial_out(up, UART_TX, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              up->port.icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      } while (--count > 0);
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(&up->port);
++
++      DEBUG_INTR("THRE...");
++
++      if (uart_circ_empty(xmit))
++              serial8250_stop_tx(&up->port, 0);
++}
++
++static _INLINE_ void check_modem_status(struct uart_8250_port *up)
++{
++      int status;
++
++      status = serial_in(up, UART_MSR);
++
++      if ((status & UART_MSR_ANY_DELTA) == 0)
++              return;
++
++      if (status & UART_MSR_TERI)
++              up->port.icount.rng++;
++      if (status & UART_MSR_DDSR)
++              up->port.icount.dsr++;
++      if (status & UART_MSR_DDCD)
++              uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
++      if (status & UART_MSR_DCTS)
++              uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
++
++      wake_up_interruptible(&up->port.info->delta_msr_wait);
++}
++
++/*
++ * This handles the interrupt from one port.
++ */
++static inline void
++serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
++{
++      unsigned int status = serial_inp(up, UART_LSR);
++
++      DEBUG_INTR("status = %x...", status);
++
++      if (status & UART_LSR_DR)
++              receive_chars(up, &status, regs);
++      check_modem_status(up);
++      if (status & UART_LSR_THRE)
++              transmit_chars(up);
++}
++
++/*
++ * This is the serial driver's interrupt routine.
++ *
++ * Arjan thinks the old way was overly complex, so it got simplified.
++ * Alan disagrees, saying that need the complexity to handle the weird
++ * nature of ISA shared interrupts.  (This is a special exception.)
++ *
++ * In order to handle ISA shared interrupts properly, we need to check
++ * that all ports have been serviced, and therefore the ISA interrupt
++ * line has been de-asserted.
++ *
++ * This means we need to loop through all ports. checking that they
++ * don't have an interrupt pending.
++ */
++static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct irq_info *i = dev_id;
++      struct list_head *l, *end = NULL;
++      int pass_counter = 0;
++
++      DEBUG_INTR("serial8250_interrupt(%d)...", irq);
++
++      spin_lock(&i->lock);
++
++      l = i->head;
++      do {
++              struct uart_8250_port *up;
++              unsigned int iir;
++
++              up = list_entry(l, struct uart_8250_port, list);
++
++              iir = serial_in(up, UART_IIR);
++              if (!(iir & UART_IIR_NO_INT)) {
++                      spin_lock(&up->port.lock);
++                      serial8250_handle_port(up, regs);
++                      spin_unlock(&up->port.lock);
++
++                      end = NULL;
++              } else if (end == NULL)
++                      end = l;
++
++              l = l->next;
++
++              if (l == i->head && pass_counter++ > PASS_LIMIT) {
++                      /* If we hit this, we're dead. */
++                      printk(KERN_ERR "serial8250: too much work for "
++                              "irq%d\n", irq);
++                      break;
++              }
++      } while (l != end);
++
++      spin_unlock(&i->lock);
++
++      DEBUG_INTR("end.\n");
++}
++
++/*
++ * To support ISA shared interrupts, we need to have one interrupt
++ * handler that ensures that the IRQ line has been deasserted
++ * before returning.  Failing to do this will result in the IRQ
++ * line being stuck active, and, since ISA irqs are edge triggered,
++ * no more IRQs will be seen.
++ */
++static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
++{
++      spin_lock_irq(&i->lock);
++
++      if (!list_empty(i->head)) {
++              if (i->head == &up->list)
++                      i->head = i->head->next;
++              list_del(&up->list);
++      } else {
++              BUG_ON(i->head != &up->list);
++              i->head = NULL;
++      }
++
++      spin_unlock_irq(&i->lock);
++}
++
++static int serial_link_irq_chain(struct uart_8250_port *up)
++{
++      struct irq_info *i = irq_lists + up->port.irq;
++      int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
++
++      spin_lock_irq(&i->lock);
++
++      if (i->head) {
++              list_add(&up->list, i->head);
++              spin_unlock_irq(&i->lock);
++
++              ret = 0;
++      } else {
++              INIT_LIST_HEAD(&up->list);
++              i->head = &up->list;
++              spin_unlock_irq(&i->lock);
++
++              ret = request_irq(up->port.irq, serial8250_interrupt,
++                                irq_flags, "serial", i);
++              if (ret < 0)
++                      serial_do_unlink(i, up);
++      }
++
++      return ret;
++}
++
++static void serial_unlink_irq_chain(struct uart_8250_port *up)
++{
++      struct irq_info *i = irq_lists + up->port.irq;
++
++      BUG_ON(i->head == NULL);
++
++      if (list_empty(i->head))
++              free_irq(up->port.irq, i);
++
++      serial_do_unlink(i, up);
++}
++
++/*
++ * This function is used to handle ports that do not have an
++ * interrupt.  This doesn't work very well for 16450's, but gives
++ * barely passable results for a 16550A.  (Although at the expense
++ * of much CPU overhead).
++ */
++static void serial8250_timeout(unsigned long data)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)data;
++      unsigned int timeout;
++      unsigned int iir;
++
++      iir = serial_in(up, UART_IIR);
++      if (!(iir & UART_IIR_NO_INT)) {
++              spin_lock(&up->port.lock);
++              serial8250_handle_port(up, NULL);
++              spin_unlock(&up->port.lock);
++      }
++
++      timeout = up->port.timeout;
++      timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
++      mod_timer(&up->timer, jiffies + timeout);
++}
++
++static unsigned int serial8250_tx_empty(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long flags;
++      unsigned int ret;
++
++      spin_lock_irqsave(&up->port.lock, flags);
++      ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
++      spin_unlock_irqrestore(&up->port.lock, flags);
++
++      return ret;
++}
++
++static unsigned int serial8250_get_mctrl(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long flags;
++      unsigned char status;
++      unsigned int ret;
++
++      spin_lock_irqsave(&up->port.lock, flags);
++      status = serial_in(up, UART_MSR);
++      spin_unlock_irqrestore(&up->port.lock, flags);
++
++      ret = 0;
++      if (status & UART_MSR_DCD)
++              ret |= TIOCM_CAR;
++      if (status & UART_MSR_RI)
++              ret |= TIOCM_RNG;
++      if (status & UART_MSR_DSR)
++              ret |= TIOCM_DSR;
++      if (status & UART_MSR_CTS)
++              ret |= TIOCM_CTS;
++      return ret;
++}
++
++static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned char mcr = 0;
++
++      if (mctrl & TIOCM_RTS)
++              mcr |= UART_MCR_RTS;
++      if (mctrl & TIOCM_DTR)
++              mcr |= UART_MCR_DTR;
++      if (mctrl & TIOCM_OUT1)
++              mcr |= UART_MCR_OUT1;
++      if (mctrl & TIOCM_OUT2)
++              mcr |= UART_MCR_OUT2;
++      if (mctrl & TIOCM_LOOP)
++              mcr |= UART_MCR_LOOP;
++
++      mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
++
++      serial_out(up, UART_MCR, mcr);
++}
++
++static void serial8250_break_ctl(struct uart_port *port, int break_state)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long flags;
++
++      spin_lock_irqsave(&up->port.lock, flags);
++      if (break_state == -1)
++              up->lcr |= UART_LCR_SBC;
++      else
++              up->lcr &= ~UART_LCR_SBC;
++      serial_out(up, UART_LCR, up->lcr);
++      spin_unlock_irqrestore(&up->port.lock, flags);
++}
++
++static int serial8250_startup(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long flags;
++      unsigned char lsr, iir;
++      int retval;
++
++      up->capabilities = uart_config[up->port.type].flags;
++      up->mcr = 0;
++      up->efr = 0;
++      up->ier = 0;
++
++      if (up->port.type == PORT_16C950) {
++              /* Wake up and initialize UART */
++              up->acr = 0;
++              serial_outp(up, UART_LCR, 0xBF);
++              serial_outp(up, UART_EFR, UART_EFR_ECB);
++              serial_outp(up, UART_IER, 0);
++              serial_outp(up, UART_LCR, 0);
++              serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
++              serial_outp(up, UART_LCR, 0xBF);
++              serial_outp(up, UART_EFR, UART_EFR_ECB);
++              serial_outp(up, UART_LCR, 0);
++      }
++
++#ifdef CONFIG_SERIAL_8250_RSA
++      /*
++       * If this is an RSA port, see if we can kick it up to the
++       * higher speed clock.
++       */
++      enable_rsa(up);
++#endif
++
++      /*
++       * Clear the FIFO buffers and disable them.
++       * (they will be reeanbled in change_speed())
++       */
++      if (up->capabilities & UART_CLEAR_FIFO) {
++              serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
++              serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
++                              UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++              serial_outp(up, UART_FCR, 0);
++      }
++
++      /*
++       * Clear the interrupt registers.
++       */
++      (void) serial_inp(up, UART_LSR);
++      (void) serial_inp(up, UART_RX);
++      (void) serial_inp(up, UART_IIR);
++      (void) serial_inp(up, UART_MSR);
++
++      /*
++       * At this point, there's no way the LSR could still be 0xff;
++       * if it is, then bail out, because there's likely no UART
++       * here.
++       */
++      if (!(up->port.flags & UPF_BUGGY_UART) &&
++          (serial_inp(up, UART_LSR) == 0xff)) {
++              printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
++              return -ENODEV;
++      }
++
++      /*
++       * If the "interrupt" for this port doesn't correspond with any
++       * hardware interrupt, we use a timer-based system.  The original
++       * driver used to do this with IRQ0.
++       */
++      if (!is_real_interrupt(up->port.irq)) {
++              unsigned int timeout = up->port.timeout;
++
++              timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
++
++              up->timer.data = (unsigned long)up;
++              mod_timer(&up->timer, jiffies + timeout);
++      } else {
++              retval = serial_link_irq_chain(up);
++              if (retval)
++                      return retval;
++      }
++
++      /*
++       * Now, initialize the UART
++       */
++      serial_outp(up, UART_LCR, UART_LCR_WLEN8);
++
++      spin_lock_irqsave(&up->port.lock, flags);
++      if (up->port.flags & UPF_FOURPORT) {
++              if (!is_real_interrupt(up->port.irq))
++                      up->port.mctrl |= TIOCM_OUT1;
++      } else
++              /*
++               * Most PC uarts need OUT2 raised to enable interrupts.
++               */
++              if (is_real_interrupt(up->port.irq))
++                      up->port.mctrl |= TIOCM_OUT2;
++
++      serial8250_set_mctrl(&up->port, up->port.mctrl);
++
++      /*
++       * Do a quick test to see if we receive an
++       * interrupt when we enable the TX irq.
++       */
++      serial_outp(up, UART_IER, UART_IER_THRI);
++      lsr = serial_in(up, UART_LSR);
++      iir = serial_in(up, UART_IIR);
++      serial_outp(up, UART_IER, 0);
++
++      if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
++              up->capabilities |= UART_BAD_TX_ENABLE;
++              printk("ttyS%d - enabling bad tx status workarounds\n",
++                      port->line);
++      }
++
++      spin_unlock_irqrestore(&up->port.lock, flags);
++
++      /*
++       * Finally, enable interrupts.  Note: Modem status interrupts
++       * are set via change_speed(), which will be occuring imminently
++       * anyway, so we don't enable them here.
++       */
++      up->ier = UART_IER_RLSI | UART_IER_RDI;
++      serial_outp(up, UART_IER, up->ier);
++
++      if (up->port.flags & UPF_FOURPORT) {
++              unsigned int icp;
++              /*
++               * Enable interrupts on the AST Fourport board
++               */
++              icp = (up->port.iobase & 0xfe0) | 0x01f;
++              outb_p(0x80, icp);
++              (void) inb_p(icp);
++      }
++
++      /*
++       * And clear the interrupt registers again for luck.
++       */
++      (void) serial_inp(up, UART_LSR);
++      (void) serial_inp(up, UART_RX);
++      (void) serial_inp(up, UART_IIR);
++      (void) serial_inp(up, UART_MSR);
++
++      return 0;
++}
++
++static void serial8250_shutdown(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long flags;
++
++      /*
++       * Disable interrupts from this port
++       */
++      up->ier = 0;
++      serial_outp(up, UART_IER, 0);
++
++      spin_lock_irqsave(&up->port.lock, flags);
++      if (up->port.flags & UPF_FOURPORT) {
++              /* reset interrupts on the AST Fourport board */
++              inb((up->port.iobase & 0xfe0) | 0x1f);
++              up->port.mctrl |= TIOCM_OUT1;
++      } else
++              up->port.mctrl &= ~TIOCM_OUT2;
++
++      serial8250_set_mctrl(&up->port, up->port.mctrl);
++      spin_unlock_irqrestore(&up->port.lock, flags);
++
++      /*
++       * Disable break condition and FIFOs
++       */
++      serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
++      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
++                                UART_FCR_CLEAR_RCVR |
++                                UART_FCR_CLEAR_XMIT);
++      serial_outp(up, UART_FCR, 0);
++
++#ifdef CONFIG_SERIAL_8250_RSA
++      /*
++       * Reset the RSA board back to 115kbps compat mode.
++       */
++      disable_rsa(up);
++#endif
++
++      /*
++       * Read data port to reset things, and then unlink from
++       * the IRQ chain.
++       */
++      (void) serial_in(up, UART_RX);
++
++      if (!is_real_interrupt(up->port.irq))
++              del_timer_sync(&up->timer);
++      else
++              serial_unlink_irq_chain(up);
++}
++
++static void serial8250_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned char cval, fcr = 0;
++      unsigned long flags;
++
++      switch (cflag & CSIZE) {
++      case CS5:
++              cval = 0x00;
++              break;
++      case CS6:
++              cval = 0x01;
++              break;
++      case CS7:
++              cval = 0x02;
++              break;
++      default:
++      case CS8:
++              cval = 0x03;
++              break;
++      }
++
++      if (cflag & CSTOPB)
++              cval |= 0x04;
++      if (cflag & PARENB)
++              cval |= UART_LCR_PARITY;
++      if (!(cflag & PARODD))
++              cval |= UART_LCR_EPAR;
++#ifdef CMSPAR
++      if (cflag & CMSPAR)
++              cval |= UART_LCR_SPAR;
++#endif
++
++      /*
++       * Work around a bug in the Oxford Semiconductor 952 rev B
++       * chip which causes it to seriously miscalculate baud rates
++       * when DLL is 0.
++       */
++      if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
++          up->rev == 0x5201)
++              quot ++;
++
++      if (up->capabilities & UART_USE_FIFO) {
++              if ((up->port.uartclk / quot) < (2400 * 16))
++                      fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
++#ifdef CONFIG_SERIAL_8250_RSA
++              else if (up->port.type == PORT_RSA)
++                      fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
++#endif
++              else
++                      fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
++      }
++      if (up->port.type == PORT_16750)
++              fcr |= UART_FCR7_64BYTE;
++
++      /*
++       * Ok, we're now changing the port state.  Do it with
++       * interrupts disabled.
++       */
++      spin_lock_irqsave(&up->port.lock, flags);
++
++      up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
++      if (iflag & IGNPAR)
++              up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
++      if (iflag & (BRKINT | PARMRK))
++              up->port.read_status_mask |= UART_LSR_BI;
++
++      /*
++       * Characteres to ignore
++       */
++      up->port.ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
++      if (iflag & IGNBRK) {
++              up->port.ignore_status_mask |= UART_LSR_BI;
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns too (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      up->port.ignore_status_mask |= UART_LSR_OE;
++      }
++
++      /*
++       * ignore all characters if CREAD is not set
++       */
++      if ((cflag & CREAD) == 0)
++              up->port.ignore_status_mask |= UART_LSR_DR;
++
++      /*
++       * CTS flow control flag and modem status interrupts
++       */
++      up->ier &= ~UART_IER_MSI;
++      if (UART_ENABLE_MS(&up->port, cflag))
++              up->ier |= UART_IER_MSI;
++
++      serial_out(up, UART_IER, up->ier);
++
++      if (up->capabilities & UART_MCRAFE) {
++              /*
++               * TI16C750 hardware flow control
++               */
++              up->mcr &= ~UART_MCR_AFE;
++              if (cflag & CRTSCTS)
++                      up->mcr |= UART_MCR_AFE;
++      }
++      if (up->capabilities & UART_EFRAFE) {
++              /*
++               * TI16C752/Startech hardware flow control
++               * FIXME:
++               * - TI16C752 requires control thresholds
++               *   to be set for auto-RTS.
++               * - We only enable auto-CTS here.
++               * Note: ST16C654 does not allow MCR bit 1
++               * to override RTS when UART_EFR_RTS is set.
++               */
++              up->efr &= ~UART_EFR_CTS;
++              if (cflag & CRTSCTS)
++                      up->efr |= UART_EFR_CTS;
++              serial_outp(up, UART_LCR, 0xBF);
++              serial_outp(up, UART_EFR, up->efr);
++      }
++
++      if (up->capabilities & UART_NATSEMI) {
++              /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
++              serial_outp(up, UART_LCR, 0xe0);
++      } else {
++              serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
++      }
++      serial_outp(up, UART_DLL, quot & 0xff);         /* LS of divisor */
++      serial_outp(up, UART_DLM, quot >> 8);           /* MS of divisor */
++      if (up->port.type == PORT_16750)
++              serial_outp(up, UART_FCR, fcr);         /* set fcr */
++      serial_outp(up, UART_LCR, cval);                /* reset DLAB */
++      up->lcr = cval;                                 /* Save LCR */
++      if (up->port.type != PORT_16750) {
++              if (fcr & UART_FCR_ENABLE_FIFO) {
++                      /* emulated UARTs (Lucent Venus 167x) need two steps */
++                      serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
++              }
++              serial_outp(up, UART_FCR, fcr);         /* set fcr */
++      }
++      serial8250_set_mctrl(&up->port, up->port.mctrl);
++      spin_unlock_irqrestore(&up->port.lock, flags);
++}
++
++static void
++serial8250_pm(struct uart_port *port, unsigned int state,
++            unsigned int oldstate)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      if (state) {
++              /* sleep */
++              if (up->capabilities & UART_STARTECH) {
++                      /* Arrange to enter sleep mode */
++                      serial_outp(up, UART_LCR, 0xBF);
++                      serial_outp(up, UART_EFR, UART_EFR_ECB);
++                      serial_outp(up, UART_LCR, 0);
++                      serial_outp(up, UART_IER, UART_IERX_SLEEP);
++                      serial_outp(up, UART_LCR, 0xBF);
++                      serial_outp(up, UART_EFR, 0);
++                      serial_outp(up, UART_LCR, 0);
++              }
++              if (up->port.type == PORT_16750) {
++                      /* Arrange to enter sleep mode */
++                      serial_outp(up, UART_IER, UART_IERX_SLEEP);
++              }
++      } else {
++              /* wake */
++              if (up->capabilities & UART_STARTECH) {
++                      /* Wake up UART */
++                      serial_outp(up, UART_LCR, 0xBF);
++                      serial_outp(up, UART_EFR, UART_EFR_ECB);
++                      /*
++                       * Turn off LCR == 0xBF so we actually set the IER
++                       * register on the XR16C850
++                       */
++                      serial_outp(up, UART_LCR, 0);
++                      serial_outp(up, UART_IER, 0);
++                      /*
++                       * Now reset LCR so we can turn off the ECB bit
++                       */
++                      serial_outp(up, UART_LCR, 0xBF);
++                      serial_outp(up, UART_EFR, 0);
++                      /*
++                       * For a XR16C850, we need to set the trigger levels
++                       */
++                      if (up->port.type == PORT_16850) {
++                              unsigned char fctr;
++
++                              fctr = serial_inp(up, UART_FCTR) &
++                                       ~(UART_FCTR_RX | UART_FCTR_TX);
++                              serial_outp(up, UART_FCTR, fctr |
++                                              UART_FCTR_TRGD |
++                                              UART_FCTR_RX);
++                              serial_outp(up, UART_TRG, UART_TRG_96);
++                              serial_outp(up, UART_FCTR, fctr |
++                                              UART_FCTR_TRGD |
++                                              UART_FCTR_TX);
++                              serial_outp(up, UART_TRG, UART_TRG_96);
++                      }
++                      serial_outp(up, UART_LCR, 0);
++              }
++
++              if (up->port.type == PORT_16750) {
++                      /* Wake up UART */
++                      serial_outp(up, UART_IER, 0);
++              }
++      }
++}
++
++/*
++ * Resource handling.  This is complicated by the fact that resources
++ * depend on the port type.  Maybe we should be claiming the standard
++ * 8250 ports, and then trying to get other resources as necessary?
++ */
++static int
++serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
++{
++      unsigned int size = 8 << up->port.regshift;
++      int ret = 0;
++
++      switch (up->port.iotype) {
++      case SERIAL_IO_MEM:
++              if (up->port.mapbase) {
++                      *res = request_mem_region(up->port.mapbase, size, "serial");
++                      if (!*res)
++                              ret = -EBUSY;
++              }
++              break;
++
++      case SERIAL_IO_HUB6:
++      case SERIAL_IO_PORT:
++              *res = request_region(up->port.iobase, size, "serial");
++              if (!*res)
++                      ret = -EBUSY;
++              break;
++      }
++      return ret;
++}
++
++static int
++serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
++{
++      unsigned int size = 8 << up->port.regshift;
++      unsigned long start;
++      int ret = 0;
++
++      switch (up->port.iotype) {
++      case SERIAL_IO_MEM:
++              if (up->port.mapbase) {
++                      start = up->port.mapbase;
++                      start += UART_RSA_BASE << up->port.regshift;
++                      *res = request_mem_region(start, size, "serial-rsa");
++                      if (!*res)
++                              ret = -EBUSY;
++              }
++              break;
++
++      case SERIAL_IO_HUB6:
++      case SERIAL_IO_PORT:
++              start = up->port.iobase;
++              start += UART_RSA_BASE << up->port.regshift;
++              *res = request_region(start, size, "serial-rsa");
++              if (!*res)
++                      ret = -EBUSY;
++              break;
++      }
++
++      return ret;
++}
++
++static void serial8250_release_port(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      unsigned long start, offset = 0, size = 0;
++
++      if (up->port.type == PORT_RSA) {
++              offset = UART_RSA_BASE << up->port.regshift;
++              size = 8;
++      }
++
++      size <<= up->port.regshift;
++
++      switch (up->port.iotype) {
++      case SERIAL_IO_MEM:
++              if (up->port.mapbase) {
++                      /*
++                       * Unmap the area.
++                       */
++                      if (up->port.flags & UPF_IOREMAP) {
++                              iounmap(up->port.membase);
++                              up->port.membase = NULL;
++                      }
++
++                      start = up->port.mapbase;
++
++                      if (size)
++                              release_mem_region(start + offset, size);
++                      release_mem_region(start, 8 << up->port.regshift);
++              }
++              break;
++
++      case SERIAL_IO_HUB6:
++      case SERIAL_IO_PORT:
++              start = up->port.iobase;
++
++              if (size)
++                      release_region(start + offset, size);
++              release_region(start + offset, 8 << up->port.regshift);
++              break;
++
++      default:
++              break;
++      }
++}
++
++static int serial8250_request_port(struct uart_port *port)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      struct resource *res = NULL, *res_rsa = NULL;
++      int ret = 0;
++
++      if (up->port.flags & UPF_RESOURCES) {
++              if (up->port.type == PORT_RSA) {
++                      ret = serial8250_request_rsa_resource(up, &res_rsa);
++                      if (ret < 0)
++                              return ret;
++              }
++
++              ret = serial8250_request_std_resource(up, &res);
++      }
++
++      /*
++       * If we have a mapbase, then request that as well.
++       */
++      if (ret == 0 && up->port.flags & UPF_IOREMAP) {
++              int size = res->end - res->start + 1;
++
++              up->port.membase = ioremap(up->port.mapbase, size);
++              if (!up->port.membase)
++                      ret = -ENOMEM;
++      }
++
++      if (ret < 0) {
++              if (res_rsa)
++                      release_resource(res_rsa);
++              if (res)
++                      release_resource(res);
++      }
++      return ret;
++}
++
++static void serial8250_config_port(struct uart_port *port, int flags)
++{
++      struct uart_8250_port *up = (struct uart_8250_port *)port;
++      struct resource *res_std = NULL, *res_rsa = NULL;
++      int probeflags = PROBE_ANY;
++      int ret;
++
++#ifdef CONFIG_MCA
++      /*
++       * Don't probe for MCA ports on non-MCA machines.
++       */
++      if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus)
++              return;
++#endif
++
++      /*
++       * Find the region that we can probe for.  This in turn
++       * tells us whether we can probe for the type of port.
++       */
++      if (up->port.flags & UPF_RESOURCES) {
++              ret = serial8250_request_std_resource(up, &res_std);
++              if (ret < 0)
++                      return;
++
++              ret = serial8250_request_rsa_resource(up, &res_rsa);
++              if (ret < 0)
++                      probeflags &= ~PROBE_RSA;
++      } else {
++              probeflags &= ~PROBE_RSA;
++      }
++
++      if (flags & UART_CONFIG_TYPE)
++              autoconfig(up, probeflags);
++      if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
++              autoconfig_irq(up);
++
++      /*
++       * If the port wasn't an RSA port, release the resource.
++       */
++      if (up->port.type != PORT_RSA && res_rsa)
++              release_resource(res_rsa);
++
++      if (up->port.type == PORT_UNKNOWN && res_std)
++              release_resource(res_std);
++}
++
++static int
++serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      if (ser->irq >= NR_IRQS || ser->irq < 0 ||
++          ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
++          ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS ||
++          ser->type == PORT_STARTECH)
++              return -EINVAL;
++      return 0;
++}
++
++static const char *
++serial8250_type(struct uart_port *port)
++{
++      int type = port->type;
++
++      if (type >= ARRAY_SIZE(uart_config))
++              type = 0;
++      return uart_config[type].name;
++}
++
++static struct uart_ops serial8250_pops = {
++      .tx_empty       = serial8250_tx_empty,
++      .set_mctrl      = serial8250_set_mctrl,
++      .get_mctrl      = serial8250_get_mctrl,
++      .stop_tx        = serial8250_stop_tx,
++      .start_tx       = serial8250_start_tx,
++      .stop_rx        = serial8250_stop_rx,
++      .enable_ms      = serial8250_enable_ms,
++      .break_ctl      = serial8250_break_ctl,
++      .startup        = serial8250_startup,
++      .shutdown       = serial8250_shutdown,
++      .change_speed   = serial8250_change_speed,
++      .pm             = serial8250_pm,
++      .type           = serial8250_type,
++      .release_port   = serial8250_release_port,
++      .request_port   = serial8250_request_port,
++      .config_port    = serial8250_config_port,
++      .verify_port    = serial8250_verify_port,
++};
++
++static struct uart_8250_port serial8250_ports[UART_NR];
++
++static void __init serial8250_isa_init_ports(void)
++{
++      struct uart_8250_port *up;
++      static int first = 1;
++      int i;
++
++      if (!first)
++              return;
++      first = 0;
++
++      for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port);
++           i++, up++) {
++              up->port.iobase   = old_serial_port[i].port;
++              up->port.irq      = irq_cannonicalize(old_serial_port[i].irq);
++              up->port.uartclk  = old_serial_port[i].baud_base * 16;
++              up->port.flags    = old_serial_port[i].flags |
++                                  UPF_RESOURCES;
++              up->port.hub6     = old_serial_port[i].hub6;
++              up->port.membase  = old_serial_port[i].iomem_base;
++              up->port.iotype   = old_serial_port[i].io_type;
++              up->port.regshift = old_serial_port[i].iomem_reg_shift;
++              up->port.ops      = &serial8250_pops;
++
++              if (up->port.iotype == UPIO_MEM && up->port.mapbase)
++                      up->port.flags |= UPF_IOREMAP;
++
++              if (share_irqs)
++                      up->port.flags |= UPF_SHARE_IRQ;
++      }
++}
++
++static void __init serial8250_register_ports(struct uart_driver *drv)
++{
++      int i;
++
++      serial8250_isa_init_ports();
++
++      for (i = 0; i < UART_NR; i++) {
++              struct uart_8250_port *up = &serial8250_ports[i];
++
++              up->port.line = i;
++              up->port.ops = &serial8250_pops;
++              init_timer(&up->timer);
++              up->timer.function = serial8250_timeout;
++
++              /*
++               * ALPHA_KLUDGE_MCR needs to be killed.
++               */
++              up->mcr_mask = ~ALPHA_KLUDGE_MCR;
++              up->mcr_force = ALPHA_KLUDGE_MCR;
++
++              uart_add_one_port(drv, &up->port);
++      }
++}
++
++#ifdef CONFIG_SERIAL_8250_CONSOLE
++
++#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
++
++/*
++ *    Wait for transmitter & holding register to empty
++ */
++static inline void wait_for_xmitr(struct uart_8250_port *up)
++{
++      unsigned int status, tmout = 10000;
++
++      /* Wait up to 10ms for the character(s) to be sent. */
++      do {
++              status = serial_in(up, UART_LSR);
++
++              if (status & UART_LSR_BI)
++                      up->lsr_break_flag = UART_LSR_BI;
++
++              if (--tmout == 0)
++                      break;
++              udelay(1);
++      } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
++
++      /* Wait up to 1s for flow control if necessary */
++      if (up->port.flags & UPF_CONS_FLOW) {
++              tmout = 1000000;
++              while (--tmout &&
++                     ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
++                      udelay(1);
++      }
++}
++
++/*
++ *    Print a string to the serial port trying not to disturb
++ *    any possible real use of the port...
++ *
++ *    The console_lock must be held when we get here.
++ */
++static void
++serial8250_console_write(struct console *co, const char *s, unsigned int count)
++{
++      struct uart_8250_port *up = &serial8250_ports[co->index];
++      unsigned int ier;
++      int i;
++
++      /*
++       *      First save the UER then disable the interrupts
++       */
++      ier = serial_in(up, UART_IER);
++      serial_out(up, UART_IER, 0);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++, s++) {
++              wait_for_xmitr(up);
++
++              /*
++               *      Send the character out.
++               *      If a LF, also do CR...
++               */
++              serial_out(up, UART_TX, *s);
++              if (*s == 10) {
++                      wait_for_xmitr(up);
++                      serial_out(up, UART_TX, 13);
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the IER
++       */
++      wait_for_xmitr(up);
++      serial_out(up, UART_IER, ier);
++}
++
++static kdev_t serial8250_console_device(struct console *co)
++{
++      return MKDEV(TTY_MAJOR, 64 + co->index);
++}
++
++static int __init serial8250_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = 9600;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      if (co->index >= UART_NR)
++              co->index = 0;
++      port = &serial8250_ports[co->index].port;
++
++      /*
++       * Temporary fix.
++       */
++      spin_lock_init(&port->lock);
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console serial8250_console = {
++      .name           = "ttyS",
++      .write          = serial8250_console_write,
++      .device         = serial8250_console_device,
++      .setup          = serial8250_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++};
++
++void __init serial8250_console_init(void)
++{
++      serial8250_isa_init_ports();
++      register_console(&serial8250_console);
++}
++
++#define SERIAL8250_CONSOLE    &serial8250_console
++#else
++#define SERIAL8250_CONSOLE    NULL
++#endif
++
++static struct uart_driver serial8250_reg = {
++      .owner                  = THIS_MODULE,
++#ifdef CONFIG_DEVFS_FS
++      .normal_name            = "tts/%d",
++      .callout_name           = "cua/%d",
++#else
++      .normal_name            = "ttyS",
++      .callout_name           = "cua",
++#endif
++      .normal_major           = TTY_MAJOR,
++      .callout_major          = TTYAUX_MAJOR,
++      .normal_driver          = &normal,
++      .callout_driver         = &callout,
++      .table                  = serial8250_table,
++      .termios                = serial8250_termios,
++      .termios_locked         = serial8250_termios_locked,
++      .minor                  = 64,
++      .nr                     = UART_NR,
++      .cons                   = SERIAL8250_CONSOLE,
++};
++
++/*
++ * register_serial and unregister_serial allows for 16x50 serial ports to be
++ * configured at run-time, to support PCMCIA modems.
++ */
++
++static int __register_serial(struct serial_struct *req, int line)
++{
++      struct uart_port port;
++
++      port.iobase   = req->port;
++      port.membase  = req->iomem_base;
++      port.irq      = req->irq;
++      port.uartclk  = req->baud_base * 16;
++      port.fifosize = req->xmit_fifo_size;
++      port.regshift = req->iomem_reg_shift;
++      port.iotype   = req->io_type;
++      port.flags    = req->flags | UPF_BOOT_AUTOCONF;
++      port.mapbase  = req->iomap_base;
++      port.line     = line;
++
++      if (share_irqs)
++              port.flags |= UPF_SHARE_IRQ;
++
++      if (HIGH_BITS_OFFSET)
++              port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET;
++
++      /*
++       * If a clock rate wasn't specified by the low level
++       * driver, then default to the standard clock rate.
++       */
++      if (port.uartclk == 0)
++              port.uartclk = BASE_BAUD * 16;
++
++      return uart_register_port(&serial8250_reg, &port);
++}
++
++/**
++ *    register_serial - configure a 16x50 serial port at runtime
++ *    @req: request structure
++ *
++ *    Configure the serial port specified by the request. If the
++ *    port exists and is in use an error is returned. If the port
++ *    is not currently in the table it is added.
++ *
++ *    The port is then probed and if necessary the IRQ is autodetected
++ *    If this fails an error is returned.
++ *
++ *    On success the port is ready to use and the line number is returned.
++ */
++int register_serial(struct serial_struct *req)
++{
++      return __register_serial(req, -1);
++}
++
++/**
++ *    unregister_serial - remove a 16x50 serial port at runtime
++ *    @line: serial line number
++ *
++ *    Remove one serial port.  This may be called from interrupt
++ *    context.
++ */
++void unregister_serial(int line)
++{
++      uart_unregister_port(&serial8250_reg, line);
++}
++
++/*
++ * This is for ISAPNP only.
++ */
++void serial8250_get_irq_map(unsigned int *map)
++{
++      int i;
++
++      for (i = 0; i < UART_NR; i++) {
++              if (serial8250_ports[i].port.type != PORT_UNKNOWN &&
++                  serial8250_ports[i].port.irq < 16)
++                      *map |= 1 << serial8250_ports[i].port.irq;
++      }
++}
++
++static int __init serial8250_init(void)
++{
++      int ret, i;
++
++      for (i = 0; i < NR_IRQS; i++)
++              spin_lock_init(&irq_lists[i].lock);
++
++      ret = uart_register_driver(&serial8250_reg);
++      if (ret >= 0)
++              serial8250_register_ports(&serial8250_reg);
++
++      return ret;
++}
++
++static void __exit serial8250_exit(void)
++{
++      int i;
++
++      for (i = 0; i < UART_NR; i++)
++              uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
++
++      uart_unregister_driver(&serial8250_reg);
++}
++
++module_init(serial8250_init);
++module_exit(serial8250_exit);
++
++EXPORT_SYMBOL(register_serial);
++EXPORT_SYMBOL(unregister_serial);
++EXPORT_SYMBOL(serial8250_get_irq_map);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Generic 8250/16x50 serial driver");
++
++MODULE_PARM(share_irqs, "i");
++MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
++      " (unsafe)");
++
++#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
++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_8250_RSA  */
++
+--- /dev/null
++++ linux-2.4.27/drivers/serial/8250.h
+@@ -0,0 +1,88 @@
++/*
++ *  linux/drivers/serial/8250.h
++ *
++ *  Driver for 8250/16550-type serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright (C) 2001 Russell King.
++ *
++ * 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: 8250.h,v 1.1.1.1.2.1 2002/10/24 09:53:24 rmk Exp $
++ */
++
++#include <linux/config.h>
++
++struct serial8250_probe {
++      struct module   *owner;
++      int             (*pci_init_one)(struct pci_dev *dev);
++      void            (*pci_remove_one)(struct pci_dev *dev);
++      void            (*pnp_init)(void);
++};
++
++int serial8250_register_probe(struct serial8250_probe *probe);
++void serial8250_unregister_probe(struct serial8250_probe *probe);
++void serial8250_get_irq_map(unsigned int *map);
++
++struct old_serial_port {
++      unsigned int uart;
++      unsigned int baud_base;
++      unsigned int port;
++      unsigned int irq;
++      unsigned int flags;
++      unsigned char hub6;
++      unsigned char io_type;
++      unsigned char *iomem_base;
++      unsigned short iomem_reg_shift;
++};
++
++struct serial8250_config {
++      const char      *name;
++      unsigned int    dfl_xmit_fifo_size;
++      unsigned int    flags;
++};
++
++#define UART_CLEAR_FIFO               0x01
++#define UART_USE_FIFO         0x02
++#define UART_STARTECH         0x04
++#define UART_NATSEMI          0x08
++#define UART_MCRAFE           0x10    /* TI16C750-style auto-flow */
++#define UART_EFRAFE           0x20    /* TI16C752/startech auto-flow */
++
++#define UART_BAD_TX_ENABLE    0x80000000
++
++#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
++#define SERIAL_INLINE
++#endif
++  
++#ifdef SERIAL_INLINE
++#define _INLINE_ inline
++#else
++#define _INLINE_
++#endif
++
++#define PROBE_RSA     (1 << 0)
++#define PROBE_ANY     (~0)
++
++#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
++
++#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
++#define SERIAL8250_SHARE_IRQS 1
++#else
++#define SERIAL8250_SHARE_IRQS 0
++#endif
++
++#if defined(__alpha__) && !defined(CONFIG_PCI)
++/*
++ * Digital did something really horribly wrong with the OUT1 and OUT2
++ * lines on at least some ALPHA's.  The failure mode is that if either
++ * is cleared, the machine locks up with endless interrupts.
++ */
++#define ALPHA_KLUDGE_MCR  (UART_MCR_OUT2 | UART_MCR_OUT1)
++#else
++#define ALPHA_KLUDGE_MCR 0
++#endif
+--- /dev/null
++++ linux-2.4.27/drivers/serial/8250_pci.c
+@@ -0,0 +1,1080 @@
++/*
++ *  linux/drivers/char/serial_8250_pci.c
++ *
++ *  Probe module for 8250/16550-type PCI serial ports.
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  $Id: 8250_pci.c,v 1.8.2.1 2002/10/24 09:53:24 rmk Exp $
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/serial.h>
++
++/* 2.4.6 compatibility cruft ;( */
++#define pci_board __pci_board
++#include <linux/serialP.h>
++#undef pci_board
++
++#include <asm/bitops.h>
++#include <asm/byteorder.h>
++#include <asm/serial.h>
++
++#include "8250.h"
++
++#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
++
++struct serial_private {
++      unsigned int nr;
++      struct pci_board *board;
++      int line[0];
++};
++
++struct pci_board {
++      int flags;
++      int num_ports;
++      int base_baud;
++      int uart_offset;
++      int reg_shift;
++      int (*init_fn)(struct pci_dev *dev, struct pci_board *board,
++                      int enable);
++      int first_uart_offset;
++};
++
++static 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);
++              }
++      }
++  
++      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);
++}
++
++/*
++ * 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.
++ */
++
++#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)
++
++static 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;
++}
++
++#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)
++
++static 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;
++}
++
++/* 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);
++   
++      /* 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;
++}
++
++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_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_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,
++#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_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, 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 },
++#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 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;
++}
++
++/*
++ * return -1 to refuse
++ */
++static int pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
++{
++      struct serial_private *priv;
++      struct pci_board *board, tmp;
++      struct serial_struct serial_req;
++      int base_baud, rc, k;
++
++      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 (%d,%d,%d,%d)\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));
++      }
++
++
++      priv = kmalloc(sizeof(struct serial_private) +
++                            sizeof(unsigned int) * board->num_ports,
++                            GFP_KERNEL);
++      if (!priv)
++              return -ENOMEM;
++
++      /*
++       * Run the initialization function, if any
++       */
++      if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0)) {
++              kfree(priv);
++              return -ENODEV;
++      }
++
++      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;
++#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
++              serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
++              serial_req.baud_base = base_baud;
++              priv->line[k] = register_serial(&serial_req);
++              if (priv->line[k] < 0)
++                      break;
++      }
++
++      priv->board = board;
++      priv->nr = k;
++
++      pci_set_drvdata(dev, priv);
++
++      return 0;
++}
++
++static void pci_remove_one(struct pci_dev *dev)
++{
++      struct serial_private *priv = pci_get_drvdata(dev);
++      int i;
++
++      pci_set_drvdata(dev, NULL);
++
++      for (i = 0; i < priv->nr; i++)
++              unregister_serial(priv->line[i]);
++
++      priv->board->init_fn(dev, priv->board, 0);
++
++      kfree(priv);
++}
++
++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_2_115200 },
++
++      /* 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_1S1P_10x_550,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig10x_1 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig10x_1 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig10x_1 },
++      {       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_2S1P_10x_550,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig10x_2 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig10x_2 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_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_1S1P_20x_550,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_0 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_0 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_0 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_0 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_0 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_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_2S1P_20x_550,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_2 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++              pbn_siig20x_2 },
++      {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_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 },
++
++#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, },
++      { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
++        PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, },
++      { 0, }
++};
++
++static struct pci_driver serial_pci_driver = {
++      name:           "serial",
++      probe:          pci_init_one,
++      remove:         pci_remove_one,
++      id_table:       serial_pci_tbl,
++};
++
++static int __init serial8250_pci_init(void)
++{
++      return pci_module_init(&serial_pci_driver);
++}
++
++static void __exit serial8250_pci_exit(void)
++{
++      pci_unregister_driver(&serial_pci_driver);
++}
++
++module_init(serial8250_pci_init);
++module_exit(serial8250_pci_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
++MODULE_GENERIC_TABLE(pci, serial_pci_tbl);
+--- /dev/null
++++ linux-2.4.27/drivers/serial/8250_pnp.c
+@@ -0,0 +1,553 @@
++/*
++ *  linux/drivers/char/serial_8250_pnp.c
++ *
++ *  Probe module for 8250/16550-type ISAPNP serial ports.
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ *  $Id: 8250_pnp.c,v 1.3.2.1 2002/10/24 09:53:25 rmk Exp $
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/isapnp.h>
++#include <linux/string.h>
++#include <linux/kernel.h>
++#include <linux/serial.h>
++#include <linux/serialP.h>
++
++#include <asm/bitops.h>
++#include <asm/byteorder.h>
++#include <asm/serial.h>
++
++#include "8250.h"
++
++static struct serial_state rs_table[] = { };
++#define NR_PORTS 0
++
++struct pnpbios_device_id
++{
++      char id[8];
++      unsigned long driver_data;
++};
++
++static const struct pnpbios_device_id pnp_dev_table[] = {
++      /* Archtek America Corp. */
++      /* Archtek SmartLink Modem 3334BT Plug & Play */
++      {       "AAC000F",              0       },
++      /* Anchor Datacomm BV */
++      /* SXPro 144 External Data Fax Modem Plug & Play */
++      {       "ADC0001",              0       },
++      /* SXPro 288 External Data Fax Modem Plug & Play */
++      {       "ADC0002",              0       },
++      /* Rockwell 56K ACF II Fax+Data+Voice Modem */
++      {       "AKY1021",              SPCI_FL_NO_SHIRQ        },
++      /* AZT3005 PnP SOUND DEVICE */
++      {       "AZT4001",              0       },
++      /* Best Data Products Inc. Smart One 336F PnP Modem */
++      {       "BDP3336",              0       },
++      /*  Boca Research */
++      /* Boca Complete Ofc Communicator 14.4 Data-FAX */
++      {       "BRI0A49",              0       },
++      /* Boca Research 33,600 ACF Modem */
++      {       "BRI1400",              0       },
++      /* Boca 33.6 Kbps Internal FD34FSVD */
++      {       "BRI3400",              0       },
++      /* Boca 33.6 Kbps Internal FD34FSVD */
++      {       "BRI0A49",              0       },
++      /* Best Data Products Inc. Smart One 336F PnP Modem */
++      {       "BDP3336",              0       },
++      /* Computer Peripherals Inc */
++      /* EuroViVa CommCenter-33.6 SP PnP */
++      {       "CPI4050",              0       },
++      /* Creative Labs */
++      /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
++      {       "CTL3001",              0       },
++      /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
++      {       "CTL3011",              0       },
++      /* Creative */
++      /* Creative Modem Blaster Flash56 DI5601-1 */
++      {       "DMB1032",              0       },
++      /* Creative Modem Blaster V.90 DI5660 */
++      {       "DMB2001",              0       },
++      /* FUJITSU */
++      /* Fujitsu 33600 PnP-I2 R Plug & Play */
++      {       "FUJ0202",              0       },
++      /* Fujitsu FMV-FX431 Plug & Play */
++      {       "FUJ0205",              0       },
++      /* Fujitsu 33600 PnP-I4 R Plug & Play */
++      {       "FUJ0206",              0       },
++      /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
++      {       "FUJ0209",              0       },
++      /* Archtek America Corp. */
++      /* Archtek SmartLink Modem 3334BT Plug & Play */
++      {       "GVC000F",              0       },
++      /* Hayes */
++      /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
++      {       "HAY0001",              0       },
++      /* Hayes Optima 336 V.34 + FAX + Voice PnP */
++      {       "HAY000C",              0       },
++      /* Hayes Optima 336B V.34 + FAX + Voice PnP */
++      {       "HAY000D",              0       },
++      /* Hayes Accura 56K Ext Fax Modem PnP */
++      {       "HAY5670",              0       },
++      /* Hayes Accura 56K Ext Fax Modem PnP */
++      {       "HAY5674",              0       },
++      /* Hayes Accura 56K Fax Modem PnP */
++      {       "HAY5675",              0       },
++      /* Hayes 288, V.34 + FAX */
++      {       "HAYF000",              0       },
++      /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
++      {       "HAYF001",              0       },
++      /* IBM */
++      /* IBM Thinkpad 701 Internal Modem Voice */
++      {       "IBM0033",              0       },
++      /* Intertex */
++      /* Intertex 28k8 33k6 Voice EXT PnP */
++      {       "IXDC801",              0       },
++      /* Intertex 33k6 56k Voice EXT PnP */
++      {       "IXDC901",              0       },
++      /* Intertex 28k8 33k6 Voice SP EXT PnP */
++      {       "IXDD801",              0       },
++      /* Intertex 33k6 56k Voice SP EXT PnP */
++      {       "IXDD901",              0       },
++      /* Intertex 28k8 33k6 Voice SP INT PnP */
++      {       "IXDF401",              0       },
++      /* Intertex 28k8 33k6 Voice SP EXT PnP */
++      {       "IXDF801",              0       },
++      /* Intertex 33k6 56k Voice SP EXT PnP */
++      {       "IXDF901",              0       },
++      /* Kortex International */
++      /* KORTEX 28800 Externe PnP */
++      {       "KOR4522",              0       },
++      /* KXPro 33.6 Vocal ASVD PnP */
++      {       "KORF661",              0       },
++      /* Lasat */
++      /* LASAT Internet 33600 PnP */
++      {       "LAS4040",              0       },
++      /* Lasat Safire 560 PnP */
++      {       "LAS4540",              0       },
++      /* Lasat Safire 336  PnP */
++      {       "LAS5440",              0       },
++      /* Microcom, Inc. */
++      /* Microcom TravelPorte FAST V.34 Plug & Play */
++      {       "MNP0281",              0       },
++      /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
++      {       "MNP0336",              0       },
++      /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
++      {       "MNP0339",              0       },
++      /* Microcom DeskPorte 28.8P Plug & Play */
++      {       "MNP0342",              0       },
++      /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
++      {       "MNP0500",              0       },
++      /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
++      {       "MNP0501",              0       },
++      /* Microcom DeskPorte 28.8S Internal Plug & Play */
++      {       "MNP0502",              0       },
++      /* Motorola */
++      /* Motorola BitSURFR Plug & Play */
++      {       "MOT1105",              0       },
++      /* Motorola TA210 Plug & Play */
++      {       "MOT1111",              0       },
++      /* Motorola HMTA 200 (ISDN) Plug & Play */
++      {       "MOT1114",              0       },
++      /* Motorola BitSURFR Plug & Play */
++      {       "MOT1115",              0       },
++      /* Motorola Lifestyle 28.8 Internal */
++      {       "MOT1190",              0       },
++      /* Motorola V.3400 Plug & Play */
++      {       "MOT1501",              0       },
++      /* Motorola Lifestyle 28.8 V.34 Plug & Play */
++      {       "MOT1502",              0       },
++      /* Motorola Power 28.8 V.34 Plug & Play */
++      {       "MOT1505",              0       },
++      /* Motorola ModemSURFR External 28.8 Plug & Play */
++      {       "MOT1509",              0       },
++      /* Motorola Premier 33.6 Desktop Plug & Play */
++      {       "MOT150A",              0       },
++      /* Motorola VoiceSURFR 56K External PnP */
++      {       "MOT150F",              0       },
++      /* Motorola ModemSURFR 56K External PnP */
++      {       "MOT1510",              0       },
++      /* Motorola ModemSURFR 56K Internal PnP */
++      {       "MOT1550",              0       },
++      /* Motorola ModemSURFR Internal 28.8 Plug & Play */
++      {       "MOT1560",              0       },
++      /* Motorola Premier 33.6 Internal Plug & Play */
++      {       "MOT1580",              0       },
++      /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
++      {       "MOT15B0",              0       },
++      /* Motorola VoiceSURFR 56K Internal PnP */
++      {       "MOT15F0",              0       },
++      /* Com 1 */
++      /*  Deskline K56 Phone System PnP */
++      {       "MVX00A1",              0       },
++      /* PC Rider K56 Phone System PnP */
++      {       "MVX00F2",              0       },
++      /* Pace 56 Voice Internal Plug & Play Modem */
++      {       "PMC2430",              0       },
++      /* Generic */
++      /* Generic standard PC COM port  */
++      {       "PNP0500",              0       },
++      /* Generic 16550A-compatible COM port */
++      {       "PNP0501",              0       },
++      /* Compaq 14400 Modem */
++      {       "PNPC000",              0       },
++      /* Compaq 2400/9600 Modem */
++      {       "PNPC001",              0       },
++      /* Dial-Up Networking Serial Cable between 2 PCs */
++      {       "PNPC031",              0       },
++      /* Dial-Up Networking Parallel Cable between 2 PCs */
++      {       "PNPC032",              0       },
++      /* Standard 9600 bps Modem */
++      {       "PNPC100",              0       },
++      /* Standard 14400 bps Modem */
++      {       "PNPC101",              0       },
++      /*  Standard 28800 bps Modem*/
++      {       "PNPC102",              0       },
++      /*  Standard Modem*/
++      {       "PNPC103",              0       },
++      /*  Standard 9600 bps Modem*/
++      {       "PNPC104",              0       },
++      /*  Standard 14400 bps Modem*/
++      {       "PNPC105",              0       },
++      /*  Standard 28800 bps Modem*/
++      {       "PNPC106",              0       },
++      /*  Standard Modem */
++      {       "PNPC107",              0       },
++      /* Standard 9600 bps Modem */
++      {       "PNPC108",              0       },
++      /* Standard 14400 bps Modem */
++      {       "PNPC109",              0       },
++      /* Standard 28800 bps Modem */
++      {       "PNPC10A",              0       },
++      /* Standard Modem */
++      {       "PNPC10B",              0       },
++      /* Standard 9600 bps Modem */
++      {       "PNPC10C",              0       },
++      /* Standard 14400 bps Modem */
++      {       "PNPC10D",              0       },
++      /* Standard 28800 bps Modem */
++      {       "PNPC10E",              0       },
++      /* Standard Modem */
++      {       "PNPC10F",              0       },
++      /* Standard PCMCIA Card Modem */
++      {       "PNP2000",              0       },
++      /* Rockwell */
++      /* Modular Technology */
++      /* Rockwell 33.6 DPF Internal PnP */
++      /* Modular Technology 33.6 Internal PnP */
++      {       "ROK0030",              0       },
++      /* Kortex International */
++      /* KORTEX 14400 Externe PnP */
++      {       "ROK0100",              0       },
++      /* Viking Components, Inc */
++      /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
++      {       "ROK4920",              0       },
++      /* Rockwell */
++      /* British Telecom */
++      /* Modular Technology */
++      /* Rockwell 33.6 DPF External PnP */
++      /* BT Prologue 33.6 External PnP */
++      /* Modular Technology 33.6 External PnP */
++      {       "RSS00A0",              0       },
++      /* Viking 56K FAX INT */
++      {       "RSS0262",              0       },
++      /* SupraExpress 28.8 Data/Fax PnP modem */
++      {       "SUP1310",              0       },
++      /* SupraExpress 33.6 Data/Fax PnP modem */
++      {       "SUP1421",              0       },
++      /* SupraExpress 33.6 Data/Fax PnP modem */
++      {       "SUP1590",              0       },
++      /* SupraExpress 33.6 Data/Fax PnP modem */
++      {       "SUP1760",              0       },
++      /* Phoebe Micro */
++      /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
++      {       "TEX0011",              0       },
++      /* Archtek America Corp. */
++      /* Archtek SmartLink Modem 3334BT Plug & Play */
++      {       "UAC000F",              0       },
++      /* 3Com Corp. */
++      /* Gateway Telepath IIvi 33.6 */
++      {       "USR0000",              0       },
++      /*  Sportster Vi 14.4 PnP FAX Voicemail */
++      {       "USR0004",              0       },
++      /* U.S. Robotics 33.6K Voice INT PnP */
++      {       "USR0006",              0       },
++      /* U.S. Robotics 33.6K Voice EXT PnP */
++      {       "USR0007",              0       },
++      /* U.S. Robotics 33.6K Voice INT PnP */
++      {       "USR2002",              0       },
++      /* U.S. Robotics 56K Voice INT PnP */
++      {       "USR2070",              0       },
++      /* U.S. Robotics 56K Voice EXT PnP */
++      {       "USR2080",              0       },
++      /* U.S. Robotics 56K FAX INT */
++      {       "USR3031",              0       },
++      /* U.S. Robotics 56K Voice INT PnP */
++      {       "USR3070",              0       },
++      /* U.S. Robotics 56K Voice EXT PnP */
++      {       "USR3080",              0       },
++      /* U.S. Robotics 56K Voice INT PnP */
++      {       "USR3090",              0       },
++      /* U.S. Robotics 56K Message  */
++      {       "USR9100",              0       },
++      /* U.S. Robotics 56K FAX EXT PnP*/
++      {       "USR9160",              0       },
++      /* U.S. Robotics 56K FAX INT PnP*/
++      {       "USR9170",              0       },
++      /* U.S. Robotics 56K Voice EXT PnP*/
++      {       "USR9180",              0       },
++      /* U.S. Robotics 56K Voice INT PnP*/
++      {       "USR9190",              0       },
++      {       "",                     0       }
++};
++
++static void inline 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;
++
++      for (tmp = modem_names; *tmp; tmp++)
++              if (strstr(name, *tmp))
++                      return 1;
++
++      return 0;
++}
++
++static int inline 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 serial_pnp_guess_board(struct pci_dev *dev, int *flags)
++{
++      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 -ENODEV;
++
++      if (!res || res->next)
++              return -ENODEV;
++
++      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 -ENODEV;
++}
++
++static int
++pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent,
++           char *slot_name)
++{
++      struct serial_struct serial_req;
++      int ret, line, flags = ent ? ent->driver_data : 0;
++
++      if (!ent) {
++              ret = serial_pnp_guess_board(dev, &flags);
++              if (ret)
++                      return ret;
++      }
++
++      if (dev->prepare(dev) < 0) {
++              printk("serial: PNP device '%s' prepare failed\n",
++                      slot_name);
++              return -ENODEV;
++      }
++
++      if (dev->active)
++              return -ENODEV;
++
++      if (flags & SPCI_FL_NO_SHIRQ)
++              avoid_irq_share(dev);
++
++      if (dev->activate(dev) < 0) {
++              printk("serial: PNP device '%s' activate failed\n",
++                      slot_name);
++              return -ENODEV;
++      }
++
++      memset(&serial_req, 0, sizeof(serial_req));
++      serial_req.irq = dev->irq_resource[0].start;
++      serial_req.port = pci_resource_start(dev, 0);
++      if (HIGH_BITS_OFFSET)
++              serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET;
++
++#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
++
++      serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
++      serial_req.baud_base = 115200;
++      line = register_serial(&serial_req);
++
++      if (line >= 0) {
++              pci_set_drvdata(dev, (void *)(line + 1));
++
++              /*
++               * Public health warning: remove this once the 2.5
++               * pnpbios_module_init() stuff is incorporated.
++               */
++              dev->driver = (void *)pnp_dev_table;
++      } else
++              dev->deactivate(dev);
++
++      return line >= 0 ? 0 : -ENODEV;
++}
++
++static void pnp_remove_one(struct pci_dev *dev)
++{
++      int line = (int)pci_get_drvdata(dev);
++
++      if (line) {
++              pci_set_drvdata(dev, NULL);
++
++              unregister_serial(line - 1);
++
++              dev->deactivate(dev);
++      }
++}
++
++static char hex[] = "0123456789ABCDEF";
++
++/*
++ * This function should vanish when 2.5 comes around and
++ * we have pnpbios_module_init()
++ */
++static void pnp_init(void)
++{
++      const struct pnpbios_device_id *id;
++      struct pci_dev *dev = NULL;
++
++#ifdef SERIAL_DEBUG_PNP
++      printk("Entered probe_serial_pnp()\n");
++#endif
++
++      isapnp_for_each_dev(dev) {
++              char slot_name[8];
++              u32 pnpid;
++
++              if (dev->active)
++                      continue;
++
++              pnpid = dev->vendor << 16 | dev->device;
++              pnpid = cpu_to_le32(pnpid);
++
++#define HEX(id,a) hex[((id)>>a) & 15]
++#define CHAR(id,a) (0x40 + (((id)>>a) & 31))
++              slot_name[0] = CHAR(pnpid, 26);
++              slot_name[1] = CHAR(pnpid, 21);
++              slot_name[2] = CHAR(pnpid, 16);
++              slot_name[3] = HEX(pnpid, 12);
++              slot_name[4] = HEX(pnpid, 8);
++              slot_name[5] = HEX(pnpid, 4);
++              slot_name[6] = HEX(pnpid, 0);
++              slot_name[7] = '\0';
++              
++              for (id = pnp_dev_table; id->id[0]; id++)
++                      if (memcmp(id->id, slot_name, 7) == 0)
++                              break;
++
++              if (id->id[0])
++                      pnp_init_one(dev, id, slot_name);
++              else
++                      pnp_init_one(dev, NULL, slot_name);
++      }
++
++#ifdef SERIAL_DEBUG_PNP
++      printk("Leaving probe_serial_pnp() (probe finished)\n");
++#endif
++}
++
++static int __init serial8250_pnp_init(void)
++{
++      if (!isapnp_present()) {
++#ifdef SERIAL_DEBUG_PNP
++              printk("Leaving probe_serial_pnp() (no isapnp)\n");
++#endif
++              return -ENODEV;
++      }
++      pnp_init();
++      return 0;
++}
++
++static void __exit serial8250_pnp_exit(void)
++{
++      struct pci_dev *dev = NULL;
++
++      isapnp_for_each_dev(dev) {
++              if (dev->driver != (void *)pnp_dev_table)
++                      continue;
++              pnp_remove_one(dev);
++      }
++}
++
++module_init(serial8250_pnp_init);
++module_exit(serial8250_pnp_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module");
++MODULE_GENERIC_TABLE(pnp, pnp_dev_table);
++
+--- /dev/null
++++ linux-2.4.27/drivers/serial/Config.in
+@@ -0,0 +1,91 @@
++#
++# Serial device configuration
++#
++# $Id: Config.in,v 1.4 2001/10/12 15:46:58 rmk Exp $
++#
++mainmenu_option next_comment
++comment 'Serial drivers'
++
++if [ "$CONFIG_ARM" = "y" ]; then
++  # I don't have this in my tree yet.
++  dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN
++  dep_bool '  Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN
++  if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then
++     int  '  Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600
++  fi
++
++  dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR
++  dep_bool '  Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA
++  if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then
++     define_bool CONFIG_SERIAL_INTEGRATOR y
++  fi
++
++  dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X
++  dep_bool '  Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X
++
++  dep_bool 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE
++  dep_bool '  Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE
++  dep_bool '  Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285
++
++  dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT
++  dep_bool '  Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00
++
++
++  dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100
++  dep_bool '  Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100
++  if [ "$CONFIG_SERIAL_SA1100" = "y" ]; then
++     int  '  Default SA1100 serial baudrate' CONFIG_SA1100_DEFAULT_BAUDRATE 9600
++  fi
++
++  dep_tristate 'ARM Omaha serial port support' CONFIG_SERIAL_OMAHA $CONFIG_ARCH_OMAHA
++  dep_bool '  Support for console on Omaha serial port' CONFIG_SERIAL_OMAHA_CONSOLE $CONFIG_SERIAL_OMAHA
++
++  dep_tristate 'AT91RM9200 serial port support' CONFIG_SERIAL_AT91 $CONFIG_ARCH_AT91RM9200
++  dep_bool '  Console on AT91RM9200 serial port' CONFIG_SERIAL_AT91_CONSOLE $CONFIG_SERIAL_AT91
++
++fi
++#
++# The new 8250/16550 serial drivers
++dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
++dep_bool '  Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
++
++dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250
++dep_bool '  Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED
++dep_bool '  Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED
++dep_bool '  Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED
++dep_bool '  Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED
++dep_bool '  Support Bell Technologies HUB6 card' CONFIG_SERIAL_8250_HUB6 $CONFIG_SERIAL_8250_EXTENDED
++
++if [ "$CONFIG_SERIAL_AMBA" = "y" -o \
++     "$CONFIG_SERIAL_CLPS711X" = "y" -o \
++     "$CONFIG_SERIAL_SA1100" = "y" -o \
++     "$CONFIG_SERIAL_ANAKIN" = "y" -o \
++     "$CONFIG_SERIAL_UART00" = "y" -o \
++     "$CONFIG_SERIAL_8250" = "y"  -o \
++     "$CONFIG_SERIAL_OMAHA" = "y" -o \
++     "$CONFIG_SERIAL_AT91" = "y" ]; then
++   define_bool CONFIG_SERIAL_CORE y
++else
++   if [ "$CONFIG_SERIAL_AMBA" = "m" -o \
++        "$CONFIG_SERIAL_CLPS711X" = "m" -o \
++        "$CONFIG_SERIAL_SA1100" = "m" -o \
++        "$CONFIG_SERIAL_ANAKIN" = "m" -o \
++        "$CONFIG_SERIAL_UART00" = "m" -o \
++        "$CONFIG_SERIAL_8250" = "m"  -o \
++        "$CONFIG_SERIAL_OMAHA" = "m" -o \
++        "$CONFIG_SERIAL_AT91" = "m" ]; then
++      define_bool CONFIG_SERIAL_CORE m
++   fi
++fi
++if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_8250_CONSOLE" = "y" -o \
++     "$CONFIG_SERIAL_OMAHA" = "y" -o \
++     "$CONFIG_SERIAL_AT91_CONSOLE" = "y" ]; then
++   define_bool CONFIG_SERIAL_CORE_CONSOLE y
++fi
++
++endmenu
+--- /dev/null
++++ linux-2.4.27/drivers/serial/Makefile
+@@ -0,0 +1,39 @@
++#
++# Makefile for the kernel serial 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.2 2001/10/12 15:46:58 rmk Exp $
++#
++
++O_TARGET := serial.o
++
++export-objs   := core.o 8250.o
++obj-y         :=
++obj-m         :=
++obj-n         :=
++obj-          :=
++
++serial-8250-y :=
++serial-8250-$(CONFIG_PCI) += 8250_pci.o
++serial-8250-$(CONFIG_ISAPNP) += 8250_pnp.o
++obj-$(CONFIG_SERIAL_CORE) += core.o
++obj-$(CONFIG_SERIAL_21285) += 21285.o
++obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y)
++obj-$(CONFIG_SERIAL_ANAKIN) += anakin.o
++obj-$(CONFIG_SERIAL_AMBA) += amba.o
++obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
++obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
++obj-$(CONFIG_SERIAL_UART00) += uart00.o
++obj-$(CONFIG_SERIAL_OMAHA) += omaha.o
++obj-$(CONFIG_SERIAL_AT91US3) += at91us3.o
++
++include $(TOPDIR)/Rules.make
++
++fastdep:
++
+--- /dev/null
++++ linux-2.4.27/drivers/serial/amba.c
+@@ -0,0 +1,770 @@
++/*
++ *  linux/drivers/char/serial_amba.c
++ *
++ *  Driver for AMBA serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright 1999 ARM Limited
++ *  Copyright (C) 2000 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
++ * 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: amba.c,v 1.9.2.2 2002/10/24 09:53:25 rmk Exp $
++ *
++ * This is a generic driver for ARM AMBA-type serial ports.  They
++ * have a lot of 16550-like features, but are not register compatable.
++ * Note that although they do have CTS, DCD and DSR inputs, they do
++ * not have an RI input, nor do they have DTR or RTS outputs.  If
++ * required, these have to be supplied via some other means (eg, GPIO)
++ * and hooked into this driver.
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/serial_amba.h>
++
++#define UART_NR               2
++
++#define SERIAL_AMBA_MAJOR     204
++#define SERIAL_AMBA_MINOR     16
++#define SERIAL_AMBA_NR                UART_NR
++
++#define CALLOUT_AMBA_NAME     "cuaam"
++#define CALLOUT_AMBA_MAJOR    205
++#define CALLOUT_AMBA_MINOR    16
++#define CALLOUT_AMBA_NR               UART_NR
++
++static struct tty_driver normal, callout;
++static struct tty_struct *amba_table[UART_NR];
++static struct termios *amba_termios[UART_NR], *amba_termios_locked[UART_NR];
++#ifdef SUPPORT_SYSRQ
++static struct console amba_console;
++#endif
++
++#define AMBA_ISR_PASS_LIMIT   256
++
++/*
++ * Access macros for the AMBA UARTs
++ */
++#define UART_GET_INT_STATUS(p)        readb((p)->membase + AMBA_UARTIIR)
++#define UART_PUT_ICR(p, c)    writel((c), (p)->membase + AMBA_UARTICR)
++#define UART_GET_FR(p)                readb((p)->membase + AMBA_UARTFR)
++#define UART_GET_CHAR(p)      readb((p)->membase + AMBA_UARTDR)
++#define UART_PUT_CHAR(p, c)   writel((c), (p)->membase + AMBA_UARTDR)
++#define UART_GET_RSR(p)               readb((p)->membase + AMBA_UARTRSR)
++#define UART_GET_CR(p)                readb((p)->membase + AMBA_UARTCR)
++#define UART_PUT_CR(p,c)      writel((c), (p)->membase + AMBA_UARTCR)
++#define UART_GET_LCRL(p)      readb((p)->membase + AMBA_UARTLCR_L)
++#define UART_PUT_LCRL(p,c)    writel((c), (p)->membase + AMBA_UARTLCR_L)
++#define UART_GET_LCRM(p)      readb((p)->membase + AMBA_UARTLCR_M)
++#define UART_PUT_LCRM(p,c)    writel((c), (p)->membase + AMBA_UARTLCR_M)
++#define UART_GET_LCRH(p)      readb((p)->membase + AMBA_UARTLCR_H)
++#define UART_PUT_LCRH(p,c)    writel((c), (p)->membase + AMBA_UARTLCR_H)
++#define UART_RX_DATA(s)               (((s) & AMBA_UARTFR_RXFE) == 0)
++#define UART_TX_READY(s)      (((s) & AMBA_UARTFR_TXFF) == 0)
++#define UART_TX_EMPTY(p)      ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
++
++#define UART_DUMMY_RSR_RX     256
++#define UART_PORT_SIZE                64
++
++/*
++ * On the Integrator platform, the port RTS and DTR are provided by
++ * bits in the following SC_CTRLS register bits:
++ *        RTS  DTR
++ *  UART0  7    6
++ *  UART1  5    4
++ */
++#define SC_CTRLC      (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
++#define SC_CTRLS      (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
++
++/*
++ * We wrap our port structure around the generic uart_port.
++ */
++struct uart_amba_port {
++      struct uart_port        port;
++      unsigned int            dtr_mask;
++      unsigned int            rts_mask;
++      unsigned int            old_status;
++};
++
++static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop)
++{
++      unsigned int cr;
++
++      cr = UART_GET_CR(port);
++      cr &= ~AMBA_UARTCR_TIE;
++      UART_PUT_CR(port, cr);
++}
++
++static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start)
++{
++      unsigned int cr;
++
++      cr = UART_GET_CR(port);
++      cr |= AMBA_UARTCR_TIE;
++      UART_PUT_CR(port, cr);
++}
++
++static void ambauart_stop_rx(struct uart_port *port)
++{
++      unsigned int cr;
++
++      cr = UART_GET_CR(port);
++      cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE);
++      UART_PUT_CR(port, cr);
++}
++
++static void ambauart_enable_ms(struct uart_port *port)
++{
++      unsigned int cr;
++
++      cr = UART_GET_CR(port);
++      cr |= AMBA_UARTCR_MSIE;
++      UART_PUT_CR(port, cr);
++}
++
++static void
++#ifdef SUPPORT_SYSRQ
++ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs)
++#else
++ambauart_rx_chars(struct uart_port *port)
++#endif
++{
++      struct tty_struct *tty = port->info->tty;
++      unsigned int status, ch, rsr, max_count = 256;
++
++      status = UART_GET_FR(port);
++      while (UART_RX_DATA(status) && max_count--) {
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++                      tty->flip.tqueue.routine((void *)tty);
++                      if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++                              printk(KERN_WARNING "TTY_DONT_FLIP set\n");
++                              return;
++                      }
++              }
++
++              ch = UART_GET_CHAR(port);
++
++              *tty->flip.char_buf_ptr = ch;
++              *tty->flip.flag_buf_ptr = TTY_NORMAL;
++              port->icount.rx++;
++
++              /*
++               * Note that the error handling code is
++               * out of the main execution path
++               */
++              rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX;
++              if (rsr & AMBA_UARTRSR_ANY) {
++                      if (rsr & AMBA_UARTRSR_BE) {
++                              rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE);
++                              port->icount.brk++;
++                              if (uart_handle_break(port))
++                                      goto ignore_char;
++                      } else if (rsr & AMBA_UARTRSR_PE)
++                              port->icount.parity++;
++                      else if (rsr & AMBA_UARTRSR_FE)
++                              port->icount.frame++;
++                      if (rsr & AMBA_UARTRSR_OE)
++                              port->icount.overrun++;
++
++                      rsr &= port->read_status_mask;
++
++                      if (rsr & AMBA_UARTRSR_BE)
++                              *tty->flip.flag_buf_ptr = TTY_BREAK;
++                      else if (rsr & AMBA_UARTRSR_PE)
++                              *tty->flip.flag_buf_ptr = TTY_PARITY;
++                      else if (rsr & AMBA_UARTRSR_FE)
++                              *tty->flip.flag_buf_ptr = TTY_FRAME;
++              }
++
++              if (uart_handle_sysrq_char(port, ch, regs))
++                      goto ignore_char;
++
++              if ((rsr & port->ignore_status_mask) == 0) {
++                      tty->flip.flag_buf_ptr++;
++                      tty->flip.char_buf_ptr++;
++                      tty->flip.count++;
++              }
++              if ((rsr & AMBA_UARTRSR_OE) &&
++                  tty->flip.count < TTY_FLIPBUF_SIZE) {
++                      /*
++                       * Overrun is special, since it's reported
++                       * immediately, and doesn't affect the current
++                       * character
++                       */
++                      *tty->flip.char_buf_ptr++ = 0;
++                      *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
++                      tty->flip.count++;
++              }
++      ignore_char:
++              status = UART_GET_FR(port);
++      }
++      tty_flip_buffer_push(tty);
++      return;
++}
++
++static void ambauart_tx_chars(struct uart_port *port)
++{
++      struct circ_buf *xmit = &port->info->xmit;
++      int count;
++
++      if (port->x_char) {
++              UART_PUT_CHAR(port, port->x_char);
++              port->icount.tx++;
++              port->x_char = 0;
++              return;
++      }
++      if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
++              ambauart_stop_tx(port, 0);
++              return;
++      }
++
++      count = port->fifosize >> 1;
++      do {
++              UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      } while (--count > 0);
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(port);
++
++      if (uart_circ_empty(xmit))
++              ambauart_stop_tx(port, 0);
++}
++
++static void ambauart_modem_status(struct uart_port *port)
++{
++      struct uart_amba_port *uap = (struct uart_amba_port *)port;
++      unsigned int status, delta;
++
++      UART_PUT_ICR(&uap->port, 0);
++
++      status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY;
++
++      delta = status ^ uap->old_status;
++      uap->old_status = status;
++
++      if (!delta)
++              return;
++
++      if (delta & AMBA_UARTFR_DCD)
++              uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD);
++
++      if (delta & AMBA_UARTFR_DSR)
++              uap->port.icount.dsr++;
++
++      if (delta & AMBA_UARTFR_CTS)
++              uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS);
++
++      wake_up_interruptible(&uap->port.info->delta_msr_wait);
++}
++
++static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
++
++      status = UART_GET_INT_STATUS(port);
++      do {
++              if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS))
++#ifdef SUPPORT_SYSRQ
++                      ambauart_rx_chars(port, regs);
++#else
++                      ambauart_rx_chars(port);
++#endif
++              if (status & AMBA_UARTIIR_TIS)
++                      ambauart_tx_chars(port);
++              if (status & AMBA_UARTIIR_MIS)
++                      ambauart_modem_status(port);
++
++              if (pass_counter-- == 0)
++                      break;
++
++              status = UART_GET_INT_STATUS(port);
++      } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS |
++                         AMBA_UARTIIR_TIS));
++}
++
++static unsigned int ambauart_tx_empty(struct uart_port *port)
++{
++      return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT;
++}
++
++static unsigned int ambauart_get_mctrl(struct uart_port *port)
++{
++      unsigned int result = 0;
++      unsigned int status;
++
++      status = UART_GET_FR(port);
++      if (status & AMBA_UARTFR_DCD)
++              result |= TIOCM_CAR;
++      if (status & AMBA_UARTFR_DSR)
++              result |= TIOCM_DSR;
++      if (status & AMBA_UARTFR_CTS)
++              result |= TIOCM_CTS;
++
++      return result;
++}
++
++static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++      struct uart_amba_port *uap = (struct uart_amba_port *)port;
++      unsigned int ctrls = 0, ctrlc = 0;
++
++      if (mctrl & TIOCM_RTS)
++              ctrlc |= uap->rts_mask;
++      else
++              ctrls |= uap->rts_mask;
++
++      if (mctrl & TIOCM_DTR)
++              ctrlc |= uap->dtr_mask;
++      else
++              ctrls |= uap->dtr_mask;
++
++      __raw_writel(ctrls, SC_CTRLS);
++      __raw_writel(ctrlc, SC_CTRLC);
++}
++
++static void ambauart_break_ctl(struct uart_port *port, int break_state)
++{
++      unsigned long flags;
++      unsigned int lcr_h;
++
++      spin_lock_irqsave(&port->lock, flags);
++      lcr_h = UART_GET_LCRH(port);
++      if (break_state == -1)
++              lcr_h |= AMBA_UARTLCR_H_BRK;
++      else
++              lcr_h &= ~AMBA_UARTLCR_H_BRK;
++      UART_PUT_LCRH(port, lcr_h);
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static int ambauart_startup(struct uart_port *port)
++{
++      struct uart_amba_port *uap = (struct uart_amba_port *)port;
++      int retval;
++
++      /*
++       * Allocate the IRQ
++       */
++      retval = request_irq(port->irq, ambauart_int, 0, "amba", port);
++      if (retval)
++              return retval;
++
++      /*
++       * initialise the old status of the modem signals
++       */
++      uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY;
++
++      /*
++       * Finally, enable interrupts
++       */
++      UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE |
++                        AMBA_UARTCR_RTIE);
++
++      return 0;
++}
++
++static void ambauart_shutdown(struct uart_port *port)
++{
++      /*
++       * Free the interrupt
++       */
++      free_irq(port->irq, port);
++
++      /*
++       * disable all interrupts, disable the port
++       */
++      UART_PUT_CR(port, 0);
++
++      /* disable break condition and fifos */
++      UART_PUT_LCRH(port, UART_GET_LCRH(port) &
++              ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));
++}
++
++static void ambauart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
++{
++      unsigned int lcr_h, old_cr;
++      unsigned long flags;
++
++#if DEBUG
++      printk("ambauart_set_cflag(0x%x) called\n", cflag);
++#endif
++      /* byte size and parity */
++      switch (cflag & CSIZE) {
++      case CS5:
++              lcr_h = AMBA_UARTLCR_H_WLEN_5;
++              break;
++      case CS6:
++              lcr_h = AMBA_UARTLCR_H_WLEN_6;
++              break;
++      case CS7:
++              lcr_h = AMBA_UARTLCR_H_WLEN_7;
++              break;
++      default: // CS8
++              lcr_h = AMBA_UARTLCR_H_WLEN_8;
++              break;
++      }
++      if (cflag & CSTOPB)
++              lcr_h |= AMBA_UARTLCR_H_STP2;
++      if (cflag & PARENB) {
++              lcr_h |= AMBA_UARTLCR_H_PEN;
++              if (!(cflag & PARODD))
++                      lcr_h |= AMBA_UARTLCR_H_EPS;
++      }
++      if (port->fifosize > 1)
++              lcr_h |= AMBA_UARTLCR_H_FEN;
++
++      spin_lock_irqsave(&port->lock, flags);
++
++      port->read_status_mask = AMBA_UARTRSR_OE;
++      if (iflag & INPCK)
++              port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
++      if (iflag & (BRKINT | PARMRK))
++              port->read_status_mask |= AMBA_UARTRSR_BE;
++
++      /*
++       * Characters to ignore
++       */
++      port->ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
++      if (iflag & IGNBRK) {
++              port->ignore_status_mask |= AMBA_UARTRSR_BE;
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns too (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      port->ignore_status_mask |= AMBA_UARTRSR_OE;
++      }
++
++      /*
++       * Ignore all characters if CREAD is not set.
++       */
++      if ((cflag & CREAD) == 0)
++              port->ignore_status_mask |= UART_DUMMY_RSR_RX;
++
++      old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE;
++
++      if (UART_ENABLE_MS(port, cflag))
++              old_cr |= AMBA_UARTCR_MSIE;
++
++      UART_PUT_CR(port, 0);
++
++      /* Set baud rate */
++      quot -= 1;
++      UART_PUT_LCRM(port, ((quot & 0xf00) >> 8));
++      UART_PUT_LCRL(port, (quot & 0xff));
++
++      /*
++       * ----------v----------v----------v----------v-----
++       * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
++       * ----------^----------^----------^----------^-----
++       */
++      UART_PUT_LCRH(port, lcr_h);
++      UART_PUT_CR(port, old_cr);
++
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static const char *ambauart_type(struct uart_port *port)
++{
++      return port->type == PORT_AMBA ? "AMBA" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'
++ */
++static void ambauart_release_port(struct uart_port *port)
++{
++      release_mem_region(port->mapbase, UART_PORT_SIZE);
++}
++
++/*
++ * Request the memory region(s) being used by 'port'
++ */
++static int ambauart_request_port(struct uart_port *port)
++{
++      return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba")
++                      != NULL ? 0 : -EBUSY;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void ambauart_config_port(struct uart_port *port, int flags)
++{
++      if (flags & UART_CONFIG_TYPE) {
++              port->type = PORT_AMBA;
++              ambauart_request_port(port);
++      }
++}
++
++/*
++ * verify the new serial_struct (for TIOCSSERIAL).
++ */
++static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      int ret = 0;
++      if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
++              ret = -EINVAL;
++      if (ser->irq < 0 || ser->irq >= NR_IRQS)
++              ret = -EINVAL;
++      if (ser->baud_base < 9600)
++              ret = -EINVAL;
++      return ret;
++}
++
++static struct uart_ops amba_pops = {
++      .tx_empty       = ambauart_tx_empty,
++      .set_mctrl      = ambauart_set_mctrl,
++      .get_mctrl      = ambauart_get_mctrl,
++      .stop_tx        = ambauart_stop_tx,
++      .start_tx       = ambauart_start_tx,
++      .stop_rx        = ambauart_stop_rx,
++      .enable_ms      = ambauart_enable_ms,
++      .break_ctl      = ambauart_break_ctl,
++      .startup        = ambauart_startup,
++      .shutdown       = ambauart_shutdown,
++      .change_speed   = ambauart_change_speed,
++      .type           = ambauart_type,
++      .release_port   = ambauart_release_port,
++      .request_port   = ambauart_request_port,
++      .config_port    = ambauart_config_port,
++      .verify_port    = ambauart_verify_port,
++};
++
++static struct uart_amba_port amba_ports[UART_NR] = {
++      {
++              .port   = {
++                      .membase        = (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE),
++                      .mapbase        = INTEGRATOR_UART0_BASE,
++                      .iotype         = SERIAL_IO_MEM,
++                      .irq            = IRQ_UARTINT0,
++                      .uartclk        = 14745600,
++                      .fifosize       = 16,
++                      .ops            = &amba_pops,
++                      .flags          = ASYNC_BOOT_AUTOCONF,
++                      .line           = 0,
++              },
++              .dtr_mask       = 1 << 5,
++              .rts_mask       = 1 << 4,
++      },
++      {
++              .port   = {
++                      .membase        = (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE),
++                      .mapbase        = INTEGRATOR_UART1_BASE,
++                      .iotype         = SERIAL_IO_MEM,
++                      .irq            = IRQ_UARTINT1,
++                      .uartclk        = 14745600,
++                      .fifosize       = 16,
++                      .ops            = &amba_pops,
++                      .flags          = ASYNC_BOOT_AUTOCONF,
++                      .line           = 1,
++              },
++              .dtr_mask       = 1 << 7,
++              .rts_mask       = 1 << 6,
++      }
++};
++
++#ifdef CONFIG_SERIAL_AMBA_CONSOLE
++
++static void ambauart_console_write(struct console *co, const char *s, unsigned int count)
++{
++      struct uart_port *port = &amba_ports[co->index].port;
++      unsigned int status, old_cr;
++      int i;
++
++      /*
++       *      First save the CR then disable the interrupts
++       */
++      old_cr = UART_GET_CR(port);
++      UART_PUT_CR(port, AMBA_UARTCR_UARTEN);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = UART_GET_FR(port);
++              } while (!UART_TX_READY(status));
++              UART_PUT_CHAR(port, s[i]);
++              if (s[i] == '\n') {
++                      do {
++                              status = UART_GET_FR(port);
++                      } while (!UART_TX_READY(status));
++                      UART_PUT_CHAR(port, '\r');
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the TCR
++       */
++      do {
++              status = UART_GET_FR(port);
++      } while (status & AMBA_UARTFR_BUSY);
++      UART_PUT_CR(port, old_cr);
++}
++
++static kdev_t ambauart_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index);
++}
++
++static void __init
++ambauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
++{
++      if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) {
++              unsigned int lcr_h, quot;
++              lcr_h = UART_GET_LCRH(port);
++
++              *parity = 'n';
++              if (lcr_h & AMBA_UARTLCR_H_PEN) {
++                      if (lcr_h & AMBA_UARTLCR_H_EPS)
++                              *parity = 'e';
++                      else
++                              *parity = 'o';
++              }
++
++              if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7)
++                      *bits = 7;
++              else
++                      *bits = 8;
++
++              quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8;
++              *baud = port->uartclk / (16 * (quot + 1));
++      }
++}
++
++static int __init ambauart_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = 38400;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      if (co->index >= UART_NR)
++              co->index = 0;
++      port = &amba_ports[co->index].port;
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              ambauart_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console amba_console = {
++      .name           = "ttyAM",
++      .write          = ambauart_console_write,
++      .device         = ambauart_console_device,
++      .setup          = ambauart_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++};
++
++void __init ambauart_console_init(void)
++{
++      register_console(&amba_console);
++}
++
++#define AMBA_CONSOLE  &amba_console
++#else
++#define AMBA_CONSOLE  NULL
++#endif
++
++static struct uart_driver amba_reg = {
++      .owner                  = THIS_MODULE,
++      .normal_major           = SERIAL_AMBA_MAJOR,
++#ifdef CONFIG_DEVFS_FS
++      .normal_name            = "ttyAM%d",
++      .callout_name           = "cuaam%d",
++#else
++      .normal_name            = "ttyAM",
++      .callout_name           = "cuaam",
++#endif
++      .normal_driver          = &normal,
++      .callout_major          = CALLOUT_AMBA_MAJOR,
++      .callout_driver         = &callout,
++      .table                  = amba_table,
++      .termios                = amba_termios,
++      .termios_locked         = amba_termios_locked,
++      .minor                  = SERIAL_AMBA_MINOR,
++      .nr                     = UART_NR,
++      .cons                   = AMBA_CONSOLE,
++};
++
++static int __init ambauart_init(void)
++{
++      int ret;
++
++      ret = uart_register_driver(&amba_reg);
++      if (ret == 0) {
++              int i;
++
++              for (i = 0; i < UART_NR; i++)
++                      uart_add_one_port(&amba_reg, &amba_ports[i].port);
++      }
++      return ret;
++}
++
++static void __exit ambauart_exit(void)
++{
++      int i;
++
++      for (i = 0; i < UART_NR; i++)
++              uart_remove_one_port(&amba_reg, &amba_ports[i].port);
++
++      uart_unregister_driver(&amba_reg);
++}
++
++module_init(ambauart_init);
++module_exit(ambauart_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
++MODULE_DESCRIPTION("ARM AMBA serial port driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/serial/anakin.c
+@@ -0,0 +1,545 @@
++/*
++ *  linux/drivers/char/serial_anakin.c
++ *
++ *  Based on driver for AMBA serial ports, by ARM Limited,
++ *  Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o.
++ *
++ *  Copyright (C) 2001 Aleph One Ltd. for Acunia N.V.
++ *
++ *  Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V.
++ *
++ * 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.
++ *
++ *  Changelog:
++ *   20-Apr-2001 TTC  Created
++ *   05-May-2001 W/TTC        Updated for serial_core.c
++ *   27-Jun-2001 jonm Minor changes; add mctrl support, switch to 
++ *                    SA_INTERRUPT. Works reliably now. No longer requires
++ *                    changes to the serial_core API.
++ *
++ *  $Id: anakin.c,v 1.5.2.2 2002/10/24 09:53:25 rmk Exp $
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#include <linux/serial_core.h>
++
++#include <asm/arch/serial_reg.h>
++
++#define UART_NR                       5
++
++#define SERIAL_ANAKIN_NAME    "ttyAN"
++#define SERIAL_ANAKIN_MAJOR   204
++#define SERIAL_ANAKIN_MINOR   32
++
++#define CALLOUT_ANAKIN_NAME   "cuaan"
++#define CALLOUT_ANAKIN_MAJOR  205
++#define CALLOUT_ANAKIN_MINOR  32
++
++static struct tty_driver normal, callout;
++static struct tty_struct *anakin_table[UART_NR];
++static struct termios *anakin_termios[UART_NR], *anakin_termios_locked[UART_NR];
++static struct uart_state anakin_state[UART_NR];
++static u_int txenable[NR_IRQS];               /* Software interrupt register */
++
++static inline unsigned int
++anakin_in(struct uart_port *port, u_int offset)
++{
++      return __raw_readl(port->base + offset);
++}
++
++static inline void
++anakin_out(struct uart_port *port, u_int offset, unsigned int value)
++{
++      __raw_writel(value, port->base + offset);
++}
++
++static void
++anakin_stop_tx(struct uart_port *port, u_int from_tty)
++{
++      txenable[port->irq] = 0;
++}
++
++static inline void
++anakin_transmit_buffer(struct uart_info *info)
++{
++      struct uart_port *port = info->port;
++
++      while (!(anakin_in(port, 0x10) & TXEMPTY));
++      anakin_out(port, 0x14, info->xmit.buf[info->xmit.tail]);
++      anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
++      info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE-1);
++        info->state->icount.tx++;
++
++      if (info->xmit.head == info->xmit.tail)
++              anakin_stop_tx(port, 0); 
++}
++
++static inline void
++anakin_transmit_x_char(struct uart_info *info)
++{
++      struct uart_port *port = info->port;
++
++      anakin_out(port, 0x14, info->x_char);
++      anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
++      info->state->icount.tx++;
++      info->x_char = 0;
++}
++
++static void
++anakin_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty)
++{
++      unsigned int flags;
++
++      save_flags_cli(flags);
++
++      // is it this... or below: if (nonempty
++      if (!txenable[port->irq]) {
++              txenable[port->irq] = TXENABLE;
++
++              if ((anakin_in(port, 0x10) & TXEMPTY) && nonempty) {
++                  anakin_transmit_buffer((struct uart_info*)port->unused);
++              }
++      }
++
++      restore_flags(flags);
++}
++
++static void
++anakin_stop_rx(struct uart_port *port)
++{
++      unsigned long flags;
++
++      save_flags_cli(flags);
++      while (anakin_in(port, 0x10) & RXRELEASE) 
++          anakin_in(port, 0x14);
++      anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX);
++      restore_flags(flags);
++}
++
++static void
++anakin_enable_ms(struct uart_port *port)
++{
++}
++
++static inline void
++anakin_rx_chars(struct uart_info *info)
++{
++      unsigned int ch;
++      struct tty_struct *tty = info->tty;
++
++      if (!(anakin_in(info->port, 0x10) & RXRELEASE))
++              return;
++
++      ch = anakin_in(info->port, 0x14) & 0xff;
++
++      if (tty->flip.count < TTY_FLIPBUF_SIZE) {
++              *tty->flip.char_buf_ptr++ = ch;
++              *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
++              info->state->icount.rx++;
++              tty->flip.count++;
++      } 
++      tty_flip_buffer_push(tty);
++}
++
++static inline void
++anakin_overrun_chars(struct uart_info *info)
++{
++      unsigned int ch;
++
++      ch = anakin_in(info->port, 0x14);
++      info->state->icount.overrun++;
++}
++
++static inline void
++anakin_tx_chars(struct uart_info *info)
++{
++      if (info->x_char) {
++              anakin_transmit_x_char(info);
++              return; 
++      }
++
++      if (info->xmit.head == info->xmit.tail
++          || info->tty->stopped
++          || info->tty->hw_stopped) {
++              anakin_stop_tx(info->port, 0);
++              return;
++      }
++
++      anakin_transmit_buffer(info);
++
++      if (CIRC_CNT(info->xmit.head,
++                   info->xmit.tail,
++                   UART_XMIT_SIZE) < WAKEUP_CHARS)
++              uart_event(info, EVT_WRITE_WAKEUP);
++}
++
++static void
++anakin_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int status;
++      struct uart_info *info = dev_id;
++
++      status = anakin_in(info->port, 0x1c);
++
++      if (status & RX) 
++              anakin_rx_chars(info);
++
++      if (status & OVERRUN) 
++              anakin_overrun_chars(info);
++
++      if (txenable[info->port->irq] && (status & TX)) 
++              anakin_tx_chars(info);
++}
++
++static u_int
++anakin_tx_empty(struct uart_port *port)
++{
++      return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0;
++}
++
++static u_int
++anakin_get_mctrl(struct uart_port *port)
++{
++      unsigned int status = 0;
++
++      status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0);
++      status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0);
++      status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0);
++      status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0);
++      
++      return status;
++}
++
++static void
++anakin_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++      unsigned int status;
++
++      status = anakin_in(port, 0x18);
++
++      if (mctrl & TIOCM_RTS) 
++              status |= RTS;
++      else 
++              status &= ~RTS;
++
++      if (mctrl & TIOCM_CAR)
++              status |= DCD;
++      else 
++              status &= ~DCD;
++
++      anakin_out(port, 0x18, status);
++}
++
++static void
++anakin_break_ctl(struct uart_port *port, int break_state)
++{
++      unsigned int status;
++
++      status = anakin_in(port, 0x20);
++
++      if (break_state == -1)
++              status |= SETBREAK;
++      else
++              status &= ~SETBREAK;
++
++      anakin_out(port, 0x20, status);
++}
++
++static int
++anakin_startup(struct uart_port *port, struct uart_info *info)
++{
++      int retval;
++      unsigned int read,write;
++
++      /*
++       * Allocate the IRQ
++       */
++      retval = request_irq(port->irq, anakin_int, SA_INTERRUPT, "serial_anakin", info);
++      if (retval)
++              return retval;
++
++      port->ops->set_mctrl(port, info->mctrl);
++
++      /*
++       * initialise the old status of the modem signals
++       */
++      port->old_status = 0;
++
++      /*
++       * Finally, disable IRQ and softIRQs for first byte)
++       */
++      txenable[port->irq] = 0;
++      read = anakin_in(port, 0x18);
++      write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE;
++      anakin_out(port, 0x18, write);
++
++      /* Store the uart_info pointer so we can reference it in 
++       * anakin_start_tx() */
++      port->unused = (u_int)info;
++      
++      return 0;
++}
++
++static void
++anakin_shutdown(struct uart_port *port, struct uart_info *info)
++{
++      /*
++       * Free the interrupt
++       */
++      free_irq(port->irq, info);
++
++      /*
++       * disable all interrupts, disable the port
++       */
++      anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE);
++}
++
++static void
++anakin_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)
++{
++      unsigned int flags;
++
++      save_flags_cli(flags);
++      while (!(anakin_in(port, 0x10) & TXEMPTY));
++      anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER)
++                      | (quot << 3));
++
++      //parity always set to none
++      anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY);
++      restore_flags(flags);
++}
++
++static const char *anakin_type(struct port *port)
++{
++      return port->type == PORT_ANAKIN ? "ANAKIN" : NULL;
++}
++
++static struct uart_ops anakin_pops = {
++      tx_empty:       anakin_tx_empty,
++      set_mctrl:      anakin_set_mctrl,
++      get_mctrl:      anakin_get_mctrl,
++      stop_tx:        anakin_stop_tx,
++      start_tx:       anakin_start_tx,
++      stop_rx:        anakin_stop_rx,
++      enable_ms:      anakin_enable_ms,
++      break_ctl:      anakin_break_ctl,
++      startup:        anakin_startup,
++      shutdown:       anakin_shutdown,
++      change_speed:   anakin_change_speed,
++      type:           anakin_type,
++};
++
++static struct uart_port anakin_ports[UART_NR] = {
++      {
++              base:           IO_BASE + UART0,
++              irq:            IRQ_UART0,
++              uartclk:        3686400,
++              fifosize:       0,
++              ops:            &anakin_pops,
++      },
++      {
++              base:           IO_BASE + UART1,
++              irq:            IRQ_UART1,
++              uartclk:        3686400,
++              fifosize:       0,
++              ops:            &anakin_pops,
++      },
++      {
++              base:           IO_BASE + UART2,
++              irq:            IRQ_UART2,
++              uartclk:        3686400,
++              fifosize:       0,
++              ops:            &anakin_pops,
++      },
++      {
++              base:           IO_BASE + UART3,
++              irq:            IRQ_UART3,
++              uartclk:        3686400,
++              fifosize:       0,
++              ops:            &anakin_pops,
++      },
++      {
++              base:           IO_BASE + UART4,
++              irq:            IRQ_UART4,
++              uartclk:        3686400,
++              fifosize:       0,
++              ops:            &anakin_pops,
++      },
++};
++
++
++#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
++
++static void
++anakin_console_write(struct console *co, const char *s, u_int count)
++{
++      struct uart_port *port = anakin_ports + co->index;
++      unsigned int flags, status, i;
++
++      /*
++       *      First save the status then disable the interrupts
++       */
++      save_flags_cli(flags);
++      status = anakin_in(port, 0x18);
++      anakin_out(port, 0x18, status & ~IRQENABLE);
++      restore_flags(flags);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++, s++) {
++              while (!(anakin_in(port, 0x10) & TXEMPTY));
++
++              /*
++               *      Send the character out.
++               *      If a LF, also do CR...
++               */
++              anakin_out(port, 0x14, *s);
++              anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
++
++              if (*s == 10) {
++                      while (!(anakin_in(port, 0x10) & TXEMPTY));
++                      anakin_out(port, 0x14, 13);
++                      anakin_out(port, 0x18, anakin_in(port, 0x18)
++                                      | SENDREQUEST);
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the interrupts
++       */
++      while (!(anakin_in(port, 0x10) & TXEMPTY));
++
++      if (status & IRQENABLE)
++              save_flags_cli(flags);
++              anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE);
++              restore_flags(flags);
++}
++
++static kdev_t
++anakin_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index);
++}
++
++/*
++ * Read the current UART setup.
++ */
++static void __init
++anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
++{
++      int paritycode;
++
++      *baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER);
++      paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY);
++      switch (paritycode) {
++        case NONEPARITY: *parity = 'n'; break;
++        case ODDPARITY: *parity = 'o'; break;
++        case EVENPARITY: *parity = 'e'; break;
++      }
++      *bits = 8;
++}
++
++static int __init
++anakin_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE;
++      int bits = 8;
++      int parity = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      port = uart_get_console(anakin_ports, UART_NR, co);
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits);
++      else
++              anakin_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits);
++}
++
++static struct console anakin_console = {
++      name:           SERIAL_ANAKIN_NAME,
++      write:          anakin_console_write,
++      device:         anakin_console_device,
++      setup:          anakin_console_setup,
++      flags:          CON_PRINTBUFFER,
++      index:          -1,
++};
++
++void __init
++anakin_console_init(void)
++{
++      register_console(&anakin_console);
++}
++
++#define ANAKIN_CONSOLE                &anakin_console
++#else
++#define ANAKIN_CONSOLE                NULL
++#endif
++
++static struct uart_register anakin_reg = {
++      normal_major:           SERIAL_ANAKIN_MAJOR,
++      normal_name:            SERIAL_ANAKIN_NAME,
++      normal_driver:          &normal,
++      callout_major:          CALLOUT_ANAKIN_MAJOR,
++      callout_name:           CALLOUT_ANAKIN_NAME,
++      callout_driver:         &callout,
++      table:                  anakin_table,
++      termios:                anakin_termios,
++      termios_locked:         anakin_termios_locked,
++      minor:                  SERIAL_ANAKIN_MINOR,
++      nr:                     UART_NR,
++      state:                  anakin_state,
++      port:                   anakin_ports,
++      cons:                   ANAKIN_CONSOLE,
++};
++
++static int __init
++anakin_init(void)
++{
++      return uart_register_port(&anakin_reg);
++}
++
++__initcall(anakin_init);
++
++MODULE_DESCRIPTION("Anakin serial driver");
++MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>");
++MODULE_SUPPORTED_DEVICE("ttyAN");
++MODULE_LICENSE("GPL");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/serial/clps711x.c
+@@ -0,0 +1,635 @@
++/*
++ *  linux/drivers/char/serial_clps711x.c
++ *
++ *  Driver for CLPS711x serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright 1999 ARM Limited
++ *  Copyright (C) 2000 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
++ * 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: clps711x.c,v 1.12.2.2 2002/10/24 09:53:25 rmk Exp $
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/bitops.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/clps7111.h>
++
++#define UART_NR               2
++
++#define SERIAL_CLPS711X_NAME  "ttyAM"
++#define SERIAL_CLPS711X_MAJOR 204
++#define SERIAL_CLPS711X_MINOR 16
++#define SERIAL_CLPS711X_NR    UART_NR
++
++#define CALLOUT_CLPS711X_NAME "cuaam"
++#define CALLOUT_CLPS711X_MAJOR        205
++#define CALLOUT_CLPS711X_MINOR        16
++#define CALLOUT_CLPS711X_NR   UART_NR
++
++static struct tty_driver normal, callout;
++static struct tty_struct *clps711x_table[UART_NR];
++static struct termios *clps711x_termios[UART_NR], *clps711x_termios_locked[UART_NR];
++
++/*
++ * We use the relevant SYSCON register as a base address for these ports.
++ */
++#define UBRLCR(port)          ((port)->iobase + UBRLCR1 - SYSCON1)
++#define UARTDR(port)          ((port)->iobase + UARTDR1 - SYSCON1)
++#define SYSFLG(port)          ((port)->iobase + SYSFLG1 - SYSCON1)
++#define SYSCON(port)          ((port)->iobase + SYSCON1 - SYSCON1)
++
++#define TX_IRQ(port)          ((port)->irq)
++#define RX_IRQ(port)          ((port)->irq + 1)
++
++#define UART_ANY_ERR          (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
++
++#define tx_enabled(port)      ((port)->unused[0])
++
++static void
++clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop)
++{
++      if (tx_enabled(port)) {
++              disable_irq(TX_IRQ(port));
++              tx_enabled(port) = 0;
++      }
++}
++
++static void
++clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start)
++{
++      if (!tx_enabled(port)) {
++              enable_irq(TX_IRQ(port));
++              tx_enabled(port) = 1;
++      }
++}
++
++static void clps711xuart_stop_rx(struct uart_port *port)
++{
++      disable_irq(RX_IRQ(port));
++}
++
++static void clps711xuart_enable_ms(struct uart_port *port)
++{
++}
++
++static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      struct tty_struct *tty = port->info->tty;
++      unsigned int status, ch, flg, ignored = 0;
++
++      status = clps_readl(SYSFLG(port));
++      while (!(status & SYSFLG_URXFE)) {
++              ch = clps_readl(UARTDR(port));
++
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              port->icount.rx++;
++
++              flg = TTY_NORMAL;
++
++              /*
++               * Note that the error handling code is
++               * out of the main execution path
++               */
++              if (ch & UART_ANY_ERR)
++                      goto handle_error;
++
++              if (uart_handle_sysrq_char(port, ch, regs))
++                      goto ignore_char;
++
++      error_return:
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++      ignore_char:
++              status = clps_readl(SYSFLG(port));
++      }
++ out:
++      tty_flip_buffer_push(tty);
++      return;
++
++ handle_error:
++      if (ch & UARTDR_PARERR)
++              port->icount.parity++;
++      else if (ch & UARTDR_FRMERR)
++              port->icount.frame++;
++      if (ch & UARTDR_OVERR)
++              port->icount.overrun++;
++
++      if (ch & port->ignore_status_mask) {
++              if (++ignored > 100)
++                      goto out;
++              goto ignore_char;
++      }
++      ch &= port->read_status_mask;
++
++      if (ch & UARTDR_PARERR)
++              flg = TTY_PARITY;
++      else if (ch & UARTDR_FRMERR)
++              flg = TTY_FRAME;
++
++      if (ch & UARTDR_OVERR) {
++              /*
++               * CHECK: does overrun affect the current character?
++               * ASSUMPTION: it does not.
++               */
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              ch = 0;
++              flg = TTY_OVERRUN;
++      }
++#ifdef SUPPORT_SYSRQ
++      port->sysrq = 0;
++#endif
++      goto error_return;
++}
++
++static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      struct circ_buf *xmit = &port->info->xmit;
++      int count;
++
++      if (port->x_char) {
++              clps_writel(port->x_char, UARTDR(port));
++              port->icount.tx++;
++              port->x_char = 0;
++              return;
++      }
++      if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
++              clps711xuart_stop_tx(port, 0);
++              return;
++      }
++
++      count = port->fifosize >> 1;
++      do {
++              clps_writel(xmit->buf[xmit->tail], UARTDR(port));
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      } while (--count > 0);
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(port);
++
++      if (uart_circ_empty(xmit))
++              clps711xuart_stop_tx(port, 0);
++}
++
++static unsigned int clps711xuart_tx_empty(struct uart_port *port)
++{
++      unsigned int status = clps_readl(SYSFLG(port));
++      return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
++}
++
++static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
++{
++      unsigned int port_addr;
++      unsigned int result = 0;
++      unsigned int status;
++
++      port_addr = SYSFLG(port);
++      if (port_addr == SYSFLG1) {
++              status = clps_readl(SYSFLG1);
++              if (status & SYSFLG1_DCD)
++                      result |= TIOCM_CAR;
++              if (status & SYSFLG1_DSR)
++                      result |= TIOCM_DSR;
++              if (status & SYSFLG1_CTS)
++                      result |= TIOCM_CTS;
++      }
++
++      return result;
++}
++
++static void
++clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
++{
++}
++
++static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
++{
++      unsigned long flags;
++      unsigned int ubrlcr;
++
++      spin_lock_irqsave(&port->lock, flags);
++      ubrlcr = clps_readl(UBRLCR(port));
++      if (break_state == -1)
++              ubrlcr |= UBRLCR_BREAK;
++      else
++              ubrlcr &= ~UBRLCR_BREAK;
++      clps_writel(ubrlcr, UBRLCR(port));
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static int clps711xuart_startup(struct uart_port *port)
++{
++      unsigned int syscon;
++      int retval;
++
++      tx_enabled(port) = 1;
++
++      /*
++       * Allocate the IRQs
++       */
++      retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
++                           "clps711xuart_tx", port);
++      if (retval)
++              return retval;
++
++      retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
++                           "clps711xuart_rx", port);
++      if (retval) {
++              free_irq(TX_IRQ(port), port);
++              return retval;
++      }
++
++      /*
++       * enable the port
++       */
++      syscon = clps_readl(SYSCON(port));
++      syscon |= SYSCON_UARTEN;
++      clps_writel(syscon, SYSCON(port));
++
++      return 0;
++}
++
++static void clps711xuart_shutdown(struct uart_port *port)
++{
++      unsigned int ubrlcr, syscon;
++
++      /*
++       * Free the interrupt
++       */
++      free_irq(TX_IRQ(port), port);   /* TX interrupt */
++      free_irq(RX_IRQ(port), port);   /* RX interrupt */
++
++      /*
++       * disable the port
++       */
++      syscon = clps_readl(SYSCON(port));
++      syscon &= ~SYSCON_UARTEN;
++      clps_writel(syscon, SYSCON(port));
++
++      /*
++       * disable break condition and fifos
++       */
++      ubrlcr = clps_readl(UBRLCR(port));
++      ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
++      clps_writel(ubrlcr, UBRLCR(port));
++}
++
++static void clps711xuart_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
++{
++      unsigned int ubrlcr;
++      unsigned long flags;
++
++#if DEBUG
++      printk("clps711xuart_change_speed(cflag=0x%x, iflag=0x%x, quot=%d) called\n",
++              cflag, iflag, quot);
++#endif
++      /* byte size and parity */
++      switch (cflag & CSIZE) {
++      case CS5:
++              ubrlcr = UBRLCR_WRDLEN5;
++              break;
++      case CS6:
++              ubrlcr = UBRLCR_WRDLEN6;
++              break;
++      case CS7:
++              ubrlcr = UBRLCR_WRDLEN7;
++              break;
++      default: // CS8
++              ubrlcr = UBRLCR_WRDLEN8;
++              break;
++      }
++      if (cflag & CSTOPB)
++              ubrlcr |= UBRLCR_XSTOP;
++      if (cflag & PARENB) {
++              ubrlcr |= UBRLCR_PRTEN;
++              if (!(cflag & PARODD))
++                      ubrlcr |= UBRLCR_EVENPRT;
++      }
++      if (port->fifosize > 1)
++              ubrlcr |= UBRLCR_FIFOEN;
++
++      spin_lock_irqsave(&port->lock, flags);
++
++      port->read_status_mask = UARTDR_OVERR;
++      if (iflag & INPCK)
++              port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
++
++      /*
++       * Characters to ignore
++       */
++      port->ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
++      if (iflag & IGNBRK) {
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns to (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      port->ignore_status_mask |= UARTDR_OVERR;
++      }
++
++      quot -= 1;
++
++      clps_writel(ubrlcr | quot, UBRLCR(port));
++
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static const char *clps711xuart_type(struct uart_port *port)
++{
++      return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void clps711xuart_config_port(struct uart_port *port, int flags)
++{
++      if (flags & UART_CONFIG_TYPE)
++              port->type = PORT_CLPS711X;
++}
++
++static void clps711xuart_release_port(struct uart_port *port)
++{
++}
++
++static int clps711xuart_request_port(struct uart_port *port)
++{
++      return 0;
++}
++
++static struct uart_ops clps711x_pops = {
++      .tx_empty       = clps711xuart_tx_empty,
++      .set_mctrl      = clps711xuart_set_mctrl_null,
++      .get_mctrl      = clps711xuart_get_mctrl,
++      .stop_tx        = clps711xuart_stop_tx,
++      .start_tx       = clps711xuart_start_tx,
++      .stop_rx        = clps711xuart_stop_rx,
++      .enable_ms      = clps711xuart_enable_ms,
++      .break_ctl      = clps711xuart_break_ctl,
++      .startup        = clps711xuart_startup,
++      .shutdown       = clps711xuart_shutdown,
++      .change_speed   = clps711xuart_change_speed,
++      .type           = clps711xuart_type,
++      .config_port    = clps711xuart_config_port,
++      .release_port   = clps711xuart_release_port,
++      .request_port   = clps711xuart_request_port,
++};
++
++static struct uart_port clps711x_ports[UART_NR] = {
++      {
++              .iobase         = SYSCON1,
++              .irq            = IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
++              .uartclk        = 3686400,
++              .fifosize       = 16,
++              .ops            = &clps711x_pops,
++              .line           = 0,
++              .flags          = ASYNC_BOOT_AUTOCONF,
++      },
++      {
++              .iobase         = SYSCON2,
++              .irq            = IRQ_UTXINT2, /* IRQ_URXINT2 */
++              .uartclk        = 3686400,
++              .fifosize       = 16,
++              .ops            = &clps711x_pops,
++              .line           = 1,
++              .flags          = ASYNC_BOOT_AUTOCONF,
++      }
++};
++
++#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
++/*
++ *    Print a string to the serial port trying not to disturb
++ *    any possible real use of the port...
++ *
++ *    The console_lock must be held when we get here.
++ *
++ *    Note that this is called with interrupts already disabled
++ */
++static void
++clps711xuart_console_write(struct console *co, const char *s,
++                         unsigned int count)
++{
++      struct uart_port *port = clps711x_ports + co->index;
++      unsigned int status, syscon;
++      int i;
++
++      /*
++       *      Ensure that the port is enabled.
++       */
++      syscon = clps_readl(SYSCON(port));
++      clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = clps_readl(SYSFLG(port));
++              } while (status & SYSFLG_UTXFF);
++              clps_writel(s[i], UARTDR(port));
++              if (s[i] == '\n') {
++                      do {
++                              status = clps_readl(SYSFLG(port));
++                      } while (status & SYSFLG_UTXFF);
++                      clps_writel('\r', UARTDR(port));
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the uart state.
++       */
++      do {
++              status = clps_readl(SYSFLG(port));
++      } while (status & SYSFLG_UBUSY);
++
++      clps_writel(syscon, SYSCON(port));
++}
++
++static kdev_t clps711xuart_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index);
++}
++
++static void __init
++clps711xuart_console_get_options(struct uart_port *port, int *baud,
++                               int *parity, int *bits)
++{
++      if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
++              unsigned int ubrlcr, quot;
++
++              ubrlcr = clps_readl(UBRLCR(port));
++
++              *parity = 'n';
++              if (ubrlcr & UBRLCR_PRTEN) {
++                      if (ubrlcr & UBRLCR_EVENPRT)
++                              *parity = 'e';
++                      else
++                              *parity = 'o';
++              }
++
++              if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
++                      *bits = 7;
++              else
++                      *bits = 8;
++
++              quot = ubrlcr & UBRLCR_BAUD_MASK;
++              *baud = port->uartclk / (16 * (quot + 1));
++      }
++}
++
++static int __init clps711xuart_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = 38400;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      port = uart_get_console(clps711x_ports, UART_NR, co);
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              clps711xuart_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console clps711x_console = {
++      .name           = SERIAL_CLPS711X_NAME,
++      .write          = clps711xuart_console_write,
++      .device         = clps711xuart_console_device,
++      .setup          = clps711xuart_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++};
++
++void __init clps711xuart_console_init(void)
++{
++      register_console(&clps711x_console);
++}
++
++#define CLPS711X_CONSOLE      &clps711x_console
++#else
++#define CLPS711X_CONSOLE      NULL
++#endif
++
++static struct uart_driver clps711x_reg = {
++#ifdef CONFIG_DEVFS_FS
++      .normal_name            = SERIAL_CLPS711X_NAME,
++      .callout_name           = CALLOUT_CLPS711X_NAME,
++#else
++      .normal_name            = SERIAL_CLPS711X_NAME,
++      .callout_name           = CALLOUT_CLPS711X_NAME,
++#endif
++
++      .normal_major           = SERIAL_CLPS711X_MAJOR,
++      .normal_driver          = &normal,
++      .callout_major          = CALLOUT_CLPS711X_MAJOR,
++      .callout_driver         = &callout,
++
++      .table                  = clps711x_table,
++      .termios                = clps711x_termios,
++      .termios_locked         = clps711x_termios_locked,
++
++      .minor                  = SERIAL_CLPS711X_MINOR,
++      .nr                     = UART_NR,
++
++      .cons                   = CLPS711X_CONSOLE,
++};
++
++static int __init clps711xuart_init(void)
++{
++      int ret, i;
++
++      printk(KERN_INFO "Serial: CLPS711x driver\n");
++
++      ret = uart_register_driver(&clps711x_reg);
++      if (ret)
++              return ret;
++
++      for (i = 0; i < UART_NR; i++)
++              uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
++
++      return 0;
++}
++
++static void __exit clps711xuart_exit(void)
++{
++      int i;
++
++      for (i = 0; i < UART_NR; i++)
++              uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
++
++      uart_unregister_driver(&clps711x_reg);
++}
++
++module_init(clps711xuart_init);
++module_exit(clps711xuart_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("Deep Blue Solutions Ltd");
++MODULE_DESCRIPTION("CLPS-711x generic serial driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/serial/core.c
+@@ -0,0 +1,2584 @@
++/*
++ *  linux/drivers/serial/core.c
++ *
++ *  Driver core for serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright 1999 ARM Limited
++ *  Copyright (C) 2000-2001 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
++ * 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: core.c,v 1.20.2.5 2002/03/13 15:22:26 rmk Exp $
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/pm.h>
++#include <linux/serial_core.h>
++#include <linux/smp_lock.h>
++
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++#undef        DEBUG
++#ifdef DEBUG
++#define DPRINTK(x...) printk(x)
++#else
++#define DPRINTK(x...) do { } while (0)
++#endif
++
++#ifndef CONFIG_PM
++#define pm_access(pm)         do { } while (0)
++#define pm_unregister(pm)     do { } while (0)
++#endif
++
++/*
++ * This is used to lock changes in serial line configuration.
++ */
++static DECLARE_MUTEX(port_sem);
++
++#define HIGH_BITS_OFFSET      ((sizeof(long)-sizeof(int))*8)
++
++#define uart_users(state)     ((state)->count + ((state)->info ? (state)->info->blocked_open : 0))
++
++#ifdef CONFIG_SERIAL_CORE_CONSOLE
++#define uart_console(port)    ((port)->cons && (port)->cons->index == (port)->line)
++#else
++#define uart_console(port)    (0)
++#endif
++
++static void uart_change_speed(struct uart_state *state, struct termios *old_termios);
++static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
++static void uart_change_pm(struct uart_state *state, int pm_state);
++
++/*
++ * This routine is used by the interrupt handler to schedule processing in
++ * the software interrupt portion of the driver.
++ */
++void uart_write_wakeup(struct uart_port *port)
++{
++      struct uart_info *info = port->info;
++      tasklet_schedule(&info->tlet);
++}
++
++static void uart_stop(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      unsigned long flags;
++
++      spin_lock_irqsave(&port->lock, flags);
++      port->ops->stop_tx(port, 1);
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void __uart_start(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++
++      if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
++          !tty->stopped && !tty->hw_stopped)
++              port->ops->start_tx(port, 1);
++}
++
++static void uart_start(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      unsigned long flags;
++
++      pm_access(state->pm);
++
++      spin_lock_irqsave(&port->lock, flags);
++      __uart_start(tty);
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static void uart_tasklet_action(unsigned long data)
++{
++      struct uart_state *state = (struct uart_state *)data;
++      struct tty_struct *tty;
++
++      tty = state->info->tty;
++      if (tty) {
++              if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++                  tty->ldisc.write_wakeup)
++                      tty->ldisc.write_wakeup(tty);
++              wake_up_interruptible(&tty->write_wait);
++      }
++}
++
++static inline void
++uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
++{
++      unsigned long flags;
++      unsigned int old;
++
++      spin_lock_irqsave(&port->lock, flags);
++      old = port->mctrl;
++      port->mctrl = (old & ~clear) | set;
++      if (old != port->mctrl)
++              port->ops->set_mctrl(port, port->mctrl);
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++#define uart_set_mctrl(port,set)      uart_update_mctrl(port,set,0)
++#define uart_clear_mctrl(port,clear)  uart_update_mctrl(port,0,clear)
++
++static inline unsigned int uart_get_altspeed(struct uart_port *port)
++{
++      unsigned int flags = port->flags & UPF_SPD_MASK;
++      unsigned int altbaud = 0;
++
++      if (flags == ASYNC_SPD_HI)
++              altbaud = 57600;
++      if (flags == ASYNC_SPD_VHI)
++              altbaud = 115200;
++      if (flags == ASYNC_SPD_SHI)
++              altbaud = 230400;
++      if (flags == ASYNC_SPD_WARP)
++              altbaud = 460800;
++
++      return altbaud;
++}
++
++/*
++ * Startup the port.  This will be called once per open.  All calls
++ * will be serialised by the per-port semaphore.
++ */
++static int uart_startup(struct uart_state *state, int init_hw)
++{
++      struct uart_info *info = state->info;
++      struct uart_port *port = state->port;
++      unsigned long page;
++      int retval = 0;
++
++      if (info->flags & UIF_INITIALIZED)
++              return 0;
++
++      /*
++       * Set the TTY IO error marker - we will only clear this
++       * once we have successfully opened the port.  Also set
++       * up the tty->alt_speed kludge
++       */
++      if (info->tty)
++              set_bit(TTY_IO_ERROR, &info->tty->flags);
++
++      if (port->type == PORT_UNKNOWN)
++              return 0;
++
++      /*
++       * Initialise and allocate the transmit and temporary
++       * buffer.
++       */
++      if (!info->xmit.buf) {
++              page = get_zeroed_page(GFP_KERNEL);
++              if (!page)
++                      return -ENOMEM;
++
++              info->xmit.buf = (unsigned char *) page;
++              info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE;
++              init_MUTEX(&info->tmpbuf_sem);
++              uart_circ_clear(&info->xmit);
++      }
++
++      port->mctrl = 0;
++
++      retval = port->ops->startup(port);
++      if (retval == 0) {
++              if (init_hw) {
++                      /*
++                       * Initialise the hardware port settings.
++                       */
++                      uart_change_speed(state, NULL);
++
++                      /*
++                       * Setup the RTS and DTR signals once the
++                       * port is open and ready to respond.
++                       */
++                      if (info->tty->termios->c_cflag & CBAUD)
++                              uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++              }
++
++              info->flags |= UIF_INITIALIZED;
++
++              clear_bit(TTY_IO_ERROR, &info->tty->flags);
++      }
++
++      if (retval && capable(CAP_SYS_ADMIN))
++              retval = 0;
++
++      return retval;
++}
++
++/*
++ * This routine will shutdown a serial port; interrupts are disabled, and
++ * DTR is dropped if the hangup on close termio flag is on.  Calls to
++ * uart_shutdown are serialised by the per-port semaphore.
++ */
++static void uart_shutdown(struct uart_state *state)
++{
++      struct uart_info *info = state->info;
++      struct uart_port *port = state->port;
++
++      if (!(info->flags & UIF_INITIALIZED))
++              return;
++
++      /*
++       * Turn off DTR and RTS early.
++       */
++      if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
++              uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++
++      /*
++       * clear delta_msr_wait queue to avoid mem leaks: we may free
++       * the irq here so the queue might never be woken up.  Note
++       * that we won't end up waiting on delta_msr_wait again since
++       * any outstanding file descriptors should be pointing at
++       * hung_up_tty_fops now.
++       */
++      wake_up_interruptible(&info->delta_msr_wait);
++
++      /*
++       * Free the IRQ and disable the port.
++       */
++      port->ops->shutdown(port);
++
++      /*
++       * Free the transmit buffer page.
++       */
++      if (info->xmit.buf) {
++              free_page((unsigned long)info->xmit.buf);
++              info->xmit.buf = NULL;
++              info->tmpbuf = NULL;
++      }
++
++      /*
++       * kill off our tasklet
++       */
++      tasklet_kill(&info->tlet);
++      if (info->tty)
++              set_bit(TTY_IO_ERROR, &info->tty->flags);
++
++      info->flags &= ~UIF_INITIALIZED;
++}
++
++/**
++ *    uart_update_timeout - update per-port FIFO timeout.
++ *    @port: uart_port structure describing the port.
++ *    @cflag: termios cflag value
++ *    @quot: uart clock divisor quotient
++ *
++ *    Set the port FIFO timeout value.  The @cflag value should
++ *    reflect the actual hardware settings.
++ */
++void
++uart_update_timeout(struct uart_port *port, unsigned int cflag,
++                  unsigned int baud)
++{
++      unsigned int bits;
++
++      /* byte size and parity */
++      switch (cflag & CSIZE) {
++      case CS5:
++              bits = 7;
++              break;
++      case CS6:
++              bits = 8;
++              break;
++      case CS7:
++              bits = 9;
++              break;
++      default:
++              bits = 10;
++              break; // CS8
++      }
++
++      if (cflag & CSTOPB)
++              bits++;
++      if (cflag & PARENB)
++              bits++;
++
++      /*
++       * The total number of bits to be transmitted in the fifo.
++       */
++      bits = bits * port->fifosize;
++
++      /*
++       * Figure the timeout to send the above number of bits.
++       * Add .02 seconds of slop
++       */
++      port->timeout = (HZ * bits) / baud + HZ/50;
++}
++
++EXPORT_SYMBOL(uart_update_timeout);
++
++static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud)
++{
++      u_int quot;
++
++      /* Special case: B0 rate */
++      if (!baud)
++              baud = 9600;
++
++      /* Old HI/VHI/custom speed handling */
++      if (baud == 38400 &&
++          ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
++              quot = port->custom_divisor;
++      else
++              quot = port->uartclk / (16 * baud);
++
++      return quot;
++}
++
++static void
++uart_change_speed(struct uart_state *state, struct termios *old_termios)
++{
++      struct tty_struct *tty = state->info->tty;
++      struct uart_port *port = state->port;
++      struct termios *termios;
++      unsigned int quot, baud, cflag, try;
++
++      /*
++       * If we have no tty, termios, or the port does not exist,
++       * then we can't set the parameters for this port.
++       */
++      if (!tty || !tty->termios || port->type == PORT_UNKNOWN)
++              return;
++
++      termios = tty->termios;
++
++      cflag = termios->c_cflag;
++
++      for (try = 0; try < 2; try ++) {
++              /* Determine divisor based on baud rate */
++              baud = tty_get_baud_rate(tty);
++              quot = uart_calculate_quot(port, baud);
++              if (quot)
++                      break;
++
++              /*
++               * Oops, the quotient was zero.  Try again with
++               * the old baud rate if possible.
++               */
++              termios->c_cflag &= ~CBAUD;
++              if (old_termios) {
++                      termios->c_cflag |=
++                               (old_termios->c_cflag & CBAUD);
++                      old_termios = NULL;
++                      continue;
++              }
++
++              /*
++               * As a last resort, if the quotient is zero,
++               * default to 9600 bps
++               */
++              termios->c_cflag |= B9600;
++      }
++
++      uart_update_timeout(port, cflag, port->uartclk / (16 * quot));
++
++      if (termios->c_cflag & CRTSCTS)
++              state->info->flags |= UIF_CTS_FLOW;
++      else
++              state->info->flags &= ~UIF_CTS_FLOW;
++      if (termios->c_cflag & CLOCAL)
++              state->info->flags &= ~UIF_CHECK_CD;
++      else
++              state->info->flags |= UIF_CHECK_CD;
++
++      /*
++       * Set up parity check flag
++       */
++      pm_access(state->pm);
++
++      port->ops->change_speed(port, cflag, termios->c_iflag, quot);
++}
++
++static inline void
++__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
++{
++      unsigned long flags;
++
++      if (!circ->buf)
++              return;
++
++      spin_lock_irqsave(&port->lock, flags);
++      if (uart_circ_chars_free(circ) != 0) {
++              circ->buf[circ->head] = c;
++              circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
++      }
++      spin_unlock_irqrestore(&port->lock, flags);
++}
++
++static inline int
++__uart_user_write(struct uart_port *port, struct circ_buf *circ,
++                const unsigned char *buf, int count)
++{
++      unsigned long flags;
++      int c, ret = 0;
++
++      if (down_interruptible(&port->info->tmpbuf_sem))
++              return -EINTR;
++
++      while (1) {
++              int c1;
++              c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++              if (count < c)
++                      c = count;
++              if (c <= 0)
++                      break;
++
++              c -= copy_from_user(port->info->tmpbuf, buf, c);
++              if (!c) {
++                      if (!ret)
++                              ret = -EFAULT;
++                      break;
++              }
++              spin_lock_irqsave(&port->lock, flags);
++              c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++              if (c1 < c)
++                      c = c1;
++              memcpy(circ->buf + circ->head, port->info->tmpbuf, c);
++              circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
++              spin_unlock_irqrestore(&port->lock, flags);
++              buf += c;
++              count -= c;
++              ret += c;
++      }
++      up(&port->info->tmpbuf_sem);
++
++      return ret;
++}
++
++static inline int
++__uart_kern_write(struct uart_port *port, struct circ_buf *circ,
++                const unsigned char *buf, int count)
++{
++      unsigned long flags;
++      int c, ret = 0;
++
++      spin_lock_irqsave(&port->lock, flags);
++      while (1) {
++              c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++              if (count < c)
++                      c = count;
++              if (c <= 0)
++                      break;
++              memcpy(circ->buf + circ->head, buf, c);
++              circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
++              buf += c;
++              count -= c;
++              ret += c;
++      }
++      spin_unlock_irqrestore(&port->lock, flags);
++
++      return ret;
++}
++
++static void uart_put_char(struct tty_struct *tty, unsigned char ch)
++{
++      struct uart_state *state = tty->driver_data;
++
++      __uart_put_char(state->port, &state->info->xmit, ch);
++}
++
++static void uart_flush_chars(struct tty_struct *tty)
++{
++      uart_start(tty);
++}
++
++static int
++uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf,
++         int count)
++{
++      struct uart_state *state = tty->driver_data;
++      int ret;
++
++      if (!state->info->xmit.buf)
++              return 0;
++
++      if (from_user)
++              ret = __uart_user_write(state->port, &state->info->xmit, buf, count);
++      else
++              ret = __uart_kern_write(state->port, &state->info->xmit, buf, count);
++
++      uart_start(tty);
++      return ret;
++}
++
++static int uart_write_room(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++
++      return uart_circ_chars_free(&state->info->xmit);
++}
++
++static int uart_chars_in_buffer(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++
++      return uart_circ_chars_pending(&state->info->xmit);
++}
++
++static void uart_flush_buffer(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      unsigned long flags;
++
++      DPRINTK("uart_flush_buffer(%d) called\n",
++              MINOR(tty->device) - tty->driver.minor_start);
++
++      spin_lock_irqsave(&port->lock, flags);
++      uart_circ_clear(&state->info->xmit);
++      spin_unlock_irqrestore(&port->lock, flags);
++      wake_up_interruptible(&tty->write_wait);
++      if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++          tty->ldisc.write_wakeup)
++              (tty->ldisc.write_wakeup)(tty);
++}
++
++/*
++ * This function is used to send a high-priority XON/XOFF character to
++ * the device
++ */
++static void uart_send_xchar(struct tty_struct *tty, char ch)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      unsigned long flags;
++
++      if (port->ops->send_xchar)
++              port->ops->send_xchar(port, ch);
++      else {
++              port->x_char = ch;
++              if (ch) {
++                      spin_lock_irqsave(&port->lock, flags);
++                      port->ops->start_tx(port, 0);
++                      spin_unlock_irqrestore(&port->lock, flags);
++              }
++      }
++}
++
++static void uart_throttle(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++
++      if (I_IXOFF(tty))
++              uart_send_xchar(tty, STOP_CHAR(tty));
++
++      if (tty->termios->c_cflag & CRTSCTS)
++              uart_clear_mctrl(state->port, TIOCM_RTS);
++}
++
++static void uart_unthrottle(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++
++      if (I_IXOFF(tty)) {
++              if (port->x_char)
++                      port->x_char = 0;
++              else
++                      uart_send_xchar(tty, START_CHAR(tty));
++      }
++
++      if (tty->termios->c_cflag & CRTSCTS)
++              uart_set_mctrl(port, TIOCM_RTS);
++}
++
++static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo)
++{
++      struct uart_port *port = state->port;
++      struct serial_struct tmp;
++
++      memset(&tmp, 0, sizeof(tmp));
++      tmp.type            = port->type;
++      tmp.line            = port->line;
++      tmp.port            = port->iobase;
++      if (HIGH_BITS_OFFSET)
++              tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET;
++      tmp.irq             = port->irq;
++      tmp.flags           = port->flags;
++      tmp.xmit_fifo_size  = port->fifosize;
++      tmp.baud_base       = port->uartclk / 16;
++      tmp.close_delay     = state->close_delay;
++      tmp.closing_wait    = state->closing_wait;
++      tmp.custom_divisor  = port->custom_divisor;
++      tmp.hub6            = port->hub6;
++      tmp.io_type         = port->iotype;
++      tmp.iomem_reg_shift = port->regshift;
++      tmp.iomem_base      = (void *)port->mapbase;
++
++      if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
++              return -EFAULT;
++      return 0;
++}
++
++static int
++uart_set_info(struct uart_state *state, struct serial_struct *newinfo)
++{
++      struct serial_struct new_serial;
++      struct uart_port *port = state->port;
++      unsigned long new_port;
++      unsigned int change_irq, change_port, old_flags;
++      unsigned int old_custom_divisor;
++      int retval = 0;
++
++      if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
++              return -EFAULT;
++
++      new_port = new_serial.port;
++      if (HIGH_BITS_OFFSET)
++              new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
++
++      new_serial.irq = irq_cannonicalize(new_serial.irq);
++
++      /*
++       * This semaphore protects state->count.  It is also
++       * very useful to prevent opens.  Also, take the
++       * port configuration semaphore to make sure that a
++       * module insertion/removal doesn't change anything
++       * under us.
++       */
++      down(&state->sem);
++
++      change_irq  = new_serial.irq != port->irq;
++
++      /*
++       * Since changing the 'type' of the port changes its resource
++       * allocations, we should treat type changes the same as
++       * IO port changes.
++       */
++      change_port = new_port != port->iobase ||
++                    (unsigned long)new_serial.iomem_base != port->mapbase ||
++                    new_serial.hub6 != port->hub6 ||
++                    new_serial.io_type != port->iotype ||
++                    new_serial.iomem_reg_shift != port->regshift ||
++                    new_serial.type != port->type;
++
++      old_flags = port->flags;
++      old_custom_divisor = port->custom_divisor;
++
++      if (!capable(CAP_SYS_ADMIN)) {
++              retval = -EPERM;
++              if (change_irq || change_port ||
++                  (new_serial.baud_base != port->uartclk / 16) ||
++                  (new_serial.close_delay != state->close_delay) ||
++                  (new_serial.closing_wait != state->closing_wait) ||
++                  (new_serial.xmit_fifo_size != port->fifosize) ||
++                  (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0))
++                      goto exit;
++              port->flags = ((port->flags & ~UPF_USR_MASK) |
++                             (new_serial.flags & UPF_USR_MASK));
++              port->custom_divisor = new_serial.custom_divisor;
++              goto check_and_exit;
++      }
++
++      /*
++       * Ask the low level driver to verify the settings.
++       */
++      if (port->ops->verify_port)
++              retval = port->ops->verify_port(port, &new_serial);
++
++      if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
++          (new_serial.baud_base < 9600))
++              retval = -EINVAL;
++
++      if (retval)
++              goto exit;
++
++      if (change_port || change_irq) {
++              retval = -EBUSY;
++
++              /*
++               * Make sure that we are the sole user of this port.
++               */
++              if (uart_users(state) > 1)
++                      goto exit;
++
++              /*
++               * We need to shutdown the serial port at the old
++               * port/type/irq combination.
++               */
++              uart_shutdown(state);
++      }
++
++      if (change_port) {
++              unsigned long old_iobase, old_mapbase;
++              unsigned int old_type, old_iotype, old_hub6, old_shift;
++
++              old_iobase = port->iobase;
++              old_mapbase = port->mapbase;
++              old_type = port->type;
++              old_hub6 = port->hub6;
++              old_iotype = port->iotype;
++              old_shift = port->regshift;
++
++              /*
++               * Free and release old regions
++               */
++              if (old_type != PORT_UNKNOWN)
++                      port->ops->release_port(port);
++
++              port->iobase = new_port;
++              port->type = new_serial.type;
++              port->hub6 = new_serial.hub6;
++              port->iotype = new_serial.io_type;
++              port->regshift = new_serial.iomem_reg_shift;
++              port->mapbase = (unsigned long)new_serial.iomem_base;
++
++              /*
++               * Claim and map the new regions
++               */
++              if (port->type != PORT_UNKNOWN) {
++                      retval = port->ops->request_port(port);
++              } else {
++                      /* Always success - Jean II */
++                      retval = 0;
++              }
++
++              /*
++               * If we fail to request resources for the
++               * new port, try to restore the old settings.
++               */
++              if (retval && old_type != PORT_UNKNOWN) {
++                      port->iobase = old_iobase;
++                      port->type = old_type;
++                      port->hub6 = old_hub6;
++                      port->iotype = old_iotype;
++                      port->regshift = old_shift;
++                      port->mapbase = old_mapbase;
++                      retval = port->ops->request_port(port);
++                      /*
++                       * If we failed to restore the old settings,
++                       * we fail like this.
++                       */
++                      if (retval)
++                              port->type = PORT_UNKNOWN;
++
++                      /*
++                       * We failed anyway.
++                       */
++                      retval = -EBUSY;
++              }
++      }
++
++      port->irq              = new_serial.irq;
++      port->uartclk          = new_serial.baud_base * 16;
++      port->flags            = (port->flags & ~UPF_CHANGE_MASK) |
++                               (new_serial.flags & UPF_CHANGE_MASK);
++      port->custom_divisor   = new_serial.custom_divisor;
++      state->close_delay     = new_serial.close_delay * HZ / 100;
++      state->closing_wait    = new_serial.closing_wait * HZ / 100;
++      port->fifosize         = new_serial.xmit_fifo_size;
++      if (state->info->tty)
++              state->info->tty->low_latency =
++                      (port->flags & UPF_LOW_LATENCY) ? 1 : 0;
++
++ check_and_exit:
++      retval = 0;
++      if (port->type == PORT_UNKNOWN)
++              goto exit;
++      if (state->info->flags & UIF_INITIALIZED) {
++              if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
++                  old_custom_divisor != port->custom_divisor) {
++                      state->info->tty->alt_speed = uart_get_altspeed(port);
++                      uart_change_speed(state, NULL);
++              }
++      } else
++              retval = uart_startup(state, 1);
++ exit:
++      up(&state->sem);
++      return retval;
++}
++
++
++/*
++ * uart_get_lsr_info - get line status register info.
++ * Note: uart_ioctl protects us against hangups.
++ */
++static int uart_get_lsr_info(struct uart_state *state, unsigned int *value)
++{
++      struct uart_port *port = state->port;
++      unsigned int result;
++
++      result = port->ops->tx_empty(port);
++
++      /*
++       * If we're about to load something into the transmit
++       * register, we'll pretend the transmitter isn't empty to
++       * avoid a race condition (depending on when the transmit
++       * interrupt happens).
++       */
++      if (port->x_char ||
++          ((uart_circ_chars_pending(&state->info->xmit) > 0) &&
++           !state->info->tty->stopped && !state->info->tty->hw_stopped))
++              result &= ~TIOCSER_TEMT;
++      
++      return put_user(result, value);
++}
++
++static int uart_tiocmget(struct tty_struct *tty, struct file *file)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      int result = -EIO;
++
++      down(&state->sem);
++      if ((!file || !tty_hung_up_p(file)) &&
++          !(tty->flags & (1 << TTY_IO_ERROR))) {
++              result = port->mctrl;
++              result |= port->ops->get_mctrl(port);
++      }
++      up(&state->sem);
++
++      return result;
++}
++
++static int
++uart_tiocmset(struct tty_struct *tty, struct file *file,
++            unsigned int set, unsigned int clear)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      int ret = -EIO;
++
++      down(&state->sem);
++      if ((!file || !tty_hung_up_p(file)) &&
++          !(tty->flags & (1 << TTY_IO_ERROR))) {
++              uart_update_mctrl(port, set, clear);
++              ret = 0;
++      }
++      up(&state->sem);
++      return ret;
++}
++
++static void uart_break_ctl(struct tty_struct *tty, int break_state)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++
++      BUG_ON(!kernel_locked());
++
++      down(&state->sem);
++
++      if (port->type != PORT_UNKNOWN)
++              port->ops->break_ctl(port, break_state);
++
++      up(&state->sem);
++}
++
++static int uart_do_autoconfig(struct uart_state *state)
++{
++      struct uart_port *port = state->port;
++      int flags, ret;
++
++      if (!capable(CAP_SYS_ADMIN))
++              return -EPERM;
++
++      /*
++       * Take the per-port semaphore.  This prevents count from
++       * changing, and hence any extra opens of the port while
++       * we're auto-configuring.
++       */
++      if (down_interruptible(&state->sem))
++              return -ERESTARTSYS;
++
++      ret = -EBUSY;
++      if (uart_users(state) == 1) {
++              uart_shutdown(state);
++
++              /*
++               * If we already have a port type configured,
++               * we must release its resources.
++               */
++              if (port->type != PORT_UNKNOWN)
++                      port->ops->release_port(port);
++
++              flags = UART_CONFIG_TYPE;
++              if (port->flags & UPF_AUTO_IRQ)
++                      flags |= UART_CONFIG_IRQ;
++
++              /*
++               * This will claim the ports resources if
++               * a port is found.
++               */
++              port->ops->config_port(port, flags);
++
++              ret = uart_startup(state, 1);
++      }
++      up(&state->sem);
++      return ret;
++}
++
++/*
++ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
++ * - mask passed in arg for lines of interest
++ *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
++ * Caller should use TIOCGICOUNT to see which one it was
++ */
++static int
++uart_wait_modem_status(struct uart_state *state, unsigned long arg)
++{
++      struct uart_port *port = state->port;
++      DECLARE_WAITQUEUE(wait, current);
++      struct uart_icount cprev, cnow;
++      int ret;
++
++      /*
++       * note the counters on entry
++       */
++      spin_lock_irq(&port->lock);
++      memcpy(&cprev, &port->icount, sizeof(struct uart_icount));
++
++      /*
++       * Force modem status interrupts on
++       */
++      port->ops->enable_ms(port);
++      spin_unlock_irq(&port->lock);
++
++      add_wait_queue(&state->info->delta_msr_wait, &wait);
++      for (;;) {
++              spin_lock_irq(&port->lock);
++              memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
++              spin_unlock_irq(&port->lock);
++
++              set_current_state(TASK_INTERRUPTIBLE);
++
++              if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
++                  ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
++                  ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
++                  ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
++                      ret = 0;
++                      break;
++              }
++
++              schedule();
++
++              /* see if a signal did it */
++              if (signal_pending(current)) {
++                      ret = -ERESTARTSYS;
++                      break;
++              }
++
++              cprev = cnow;
++      }
++
++      current->state = TASK_RUNNING;
++      remove_wait_queue(&state->info->delta_msr_wait, &wait);
++
++      return ret;
++}
++
++/*
++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
++ * Return: write counters to the user passed counter struct
++ * NB: both 1->0 and 0->1 transitions are counted except for
++ *     RI where only 0->1 is counted.
++ */
++static int
++uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt)
++{
++      struct serial_icounter_struct icount;
++      struct uart_icount cnow;
++      struct uart_port *port = state->port;
++
++      spin_lock_irq(&port->lock);
++      memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
++      spin_unlock_irq(&port->lock);
++
++      icount.cts         = cnow.cts;
++      icount.dsr         = cnow.dsr;
++      icount.rng         = cnow.rng;
++      icount.dcd         = cnow.dcd;
++      icount.rx          = cnow.rx;
++      icount.tx          = cnow.tx;
++      icount.frame       = cnow.frame;
++      icount.overrun     = cnow.overrun;
++      icount.parity      = cnow.parity;
++      icount.brk         = cnow.brk;
++      icount.buf_overrun = cnow.buf_overrun;
++
++      return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
++}
++
++/*
++ * Called via sys_ioctl under the BKL.  We can use spin_lock_irq() here.
++ */
++static int
++uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
++         unsigned long arg)
++{
++      struct uart_state *state = tty->driver_data;
++      int ret = -ENOIOCTLCMD;
++
++      BUG_ON(!kernel_locked());
++
++      /*
++       * These ioctls don't rely on the hardware to be present.
++       */
++      switch (cmd) {
++      case TIOCGSERIAL:
++              ret = uart_get_info(state, (struct serial_struct *)arg);
++              break;
++
++      case TIOCSSERIAL:
++              ret = uart_set_info(state, (struct serial_struct *)arg);
++              break;
++
++      case TIOCSERCONFIG:
++              ret = uart_do_autoconfig(state);
++              break;
++
++      case TIOCSERGWILD: /* obsolete */
++      case TIOCSERSWILD: /* obsolete */
++              ret = 0;
++              break;
++      }
++
++      if (ret != -ENOIOCTLCMD)
++              goto out;
++
++      if (tty->flags & (1 << TTY_IO_ERROR)) {
++              ret = -EIO;
++              goto out;
++      }
++
++      /*
++       * The following should only be used when hardware is present.
++       */
++      switch (cmd) {
++      case TIOCMIWAIT:
++              ret = uart_wait_modem_status(state, arg);
++              break;
++
++      case TIOCGICOUNT:
++              ret = uart_get_count(state, (struct serial_icounter_struct *)arg);
++              break;
++
++      case TIOCMGET:
++              {
++                      int val;
++                      val = uart_tiocmget(tty, filp);
++                      if (val >= 0) {
++                              ret = put_user(val, (int *)arg);
++                      } else {
++                              ret = val;
++                      }
++              }
++              break;
++
++      case TIOCMBIS:
++      case TIOCMBIC:
++      case TIOCMSET:
++              {
++                      int val, set = 0, clear = 0;
++                      ret = get_user(val, (int *)arg);
++                      if (ret)
++                              break;
++
++                      switch (cmd) {
++                      case TIOCMBIS:
++                              set = val;
++                              break;
++                      case TIOCMBIC:
++                              clear = val;
++                              break;
++                      case TIOCMSET:
++                              set = val;
++                              clear = ~val;
++                              break;
++                      }
++
++                      set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
++                      clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
++
++                      ret = uart_tiocmset(tty, filp, set, clear);
++              }
++              break;
++      }
++
++      if (ret != -ENOIOCTLCMD)
++              goto out;
++
++      down(&state->sem);
++
++      if (tty_hung_up_p(filp)) {
++              ret = -EIO;
++              goto out_up;
++      }
++
++      /*
++       * All these rely on hardware being present and need to be
++       * protected against the tty being hung up.
++       */
++      switch (cmd) {
++      case TIOCSERGETLSR: /* Get line status register */
++              ret = uart_get_lsr_info(state, (unsigned int *)arg);
++              break;
++
++      default: {
++              struct uart_port *port = state->port;
++              if (port->ops->ioctl)
++                      ret = port->ops->ioctl(port, cmd, arg);
++              break;
++      }
++      }
++ out_up:
++      up(&state->sem);
++ out:
++      return ret;
++}
++
++static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios)
++{
++      struct uart_state *state = tty->driver_data;
++      unsigned long flags;
++      unsigned int cflag = tty->termios->c_cflag;
++
++      BUG_ON(!kernel_locked());
++
++      /*
++       * These are the bits that are used to setup various
++       * flags in the low level driver.
++       */
++#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++      if ((cflag ^ old_termios->c_cflag) == 0 &&
++          RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
++              return;
++
++      uart_change_speed(state, old_termios);
++
++      /* Handle transition to B0 status */
++      if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
++              uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
++
++      /* Handle transition away from B0 status */
++      if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
++              unsigned int mask = TIOCM_DTR;
++              if (!(cflag & CRTSCTS) ||
++                  !test_bit(TTY_THROTTLED, &tty->flags))
++                      mask |= TIOCM_RTS;
++              uart_set_mctrl(state->port, mask);
++      }
++
++      /* Handle turning off CRTSCTS */
++      if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
++              spin_lock_irqsave(&state->port->lock, flags);
++              tty->hw_stopped = 0;
++              __uart_start(tty);
++              spin_unlock_irqrestore(&state->port->lock, flags);
++      }
++
++#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(&state->info->open_wait);
++#endif
++}
++
++/*
++ * In 2.4.5, calls to this will be serialized via the BKL in
++ *  linux/drivers/char/tty_io.c:tty_release()
++ *  linux/drivers/char/tty_io.c:do_tty_handup()
++ */
++static void uart_close(struct tty_struct *tty, struct file *filp)
++{
++      struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port;
++
++      BUG_ON(!kernel_locked());
++
++      if (!state || !state->port)
++              return;
++
++      port = state->port;
++
++      DPRINTK("uart_close(%d) called\n", port->line);
++
++      down(&state->sem);
++
++      if (tty_hung_up_p(filp))
++              goto done;
++
++      if ((tty->count == 1) && (state->count != 1)) {
++              /*
++               * Uh, oh.  tty->count is 1, which means that the tty
++               * structure will be freed.  state->count should always
++               * be one in these conditions.  If it's greater than
++               * one, we've got real problems, since it means the
++               * serial port won't be shutdown.
++               */
++              printk("uart_close: bad serial port count; tty->count is 1, "
++                     "state->count is %d\n", state->count);
++              state->count = 1;
++      }
++      if (--state->count < 0) {
++              printk("rs_close: bad serial port count for %s%d: %d\n",
++                     tty->driver.name, port->line, state->count);
++              state->count = 0;
++      }
++      if (state->count)
++              goto done;
++
++      /*
++       * Save the termios structure, since this port may have
++       * separate termios for callout and dialin.
++       */
++      if (state->info->flags & UIF_NORMAL_ACTIVE)
++              state->normal_termios = *tty->termios;
++      if (state->info->flags & UIF_CALLOUT_ACTIVE)
++              state->callout_termios = *tty->termios;
++
++      /*
++       * Now we wait for the transmit buffer to clear; and we notify
++       * the line discipline to only process XON/XOFF characters by
++       * setting tty->closing.
++       */
++      tty->closing = 1;
++
++      if (state->closing_wait != USF_CLOSING_WAIT_NONE)
++              tty_wait_until_sent(tty, state->closing_wait);
++
++      /*
++       * At this point, we stop accepting input.  To do this, we
++       * disable the receive line status interrupts.
++       */
++      if (state->info->flags & UIF_INITIALIZED) {
++              unsigned long flags;
++              spin_lock_irqsave(&port->lock, flags);
++              port->ops->stop_rx(port);
++              spin_unlock_irqrestore(&port->lock, flags);
++              /*
++               * Before we drop DTR, make sure the UART transmitter
++               * has completely drained; this is especially
++               * important if there is a transmit FIFO!
++               */
++              uart_wait_until_sent(tty, port->timeout);
++      }
++
++      uart_shutdown(state);
++      uart_flush_buffer(tty);
++      if (tty->ldisc.flush_buffer)
++              tty->ldisc.flush_buffer(tty);
++      tty->closing = 0;
++      state->info->tty = NULL;
++
++      if (state->info->blocked_open) {
++              if (state->close_delay) {
++                      set_current_state(TASK_INTERRUPTIBLE);
++                      schedule_timeout(state->close_delay);
++                      set_current_state(TASK_RUNNING);
++              }
++      } else if (!uart_console(port)) {
++              uart_change_pm(state, 3);
++      }
++
++      /*
++       * Wake up anyone trying to open this port.
++       */
++      state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE);
++      wake_up_interruptible(&state->info->open_wait);
++
++ done:
++      up(&state->sem);
++      if (drv->owner)
++              __MOD_DEC_USE_COUNT(drv->owner);
++}
++
++static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
++{
++      struct uart_state *state = tty->driver_data;
++      struct uart_port *port = state->port;
++      unsigned long char_time, expire;
++
++      BUG_ON(!kernel_locked());
++
++      if (port->type == PORT_UNKNOWN || port->fifosize == 0)
++              return;
++
++      /*
++       * Set the check interval to be 1/5 of the estimated time to
++       * send a single character, and make it at least 1.  The check
++       * interval should also be less than the timeout.
++       *
++       * Note: we have to use pretty tight timings here to satisfy
++       * the NIST-PCTS.
++       */
++      char_time = (port->timeout - HZ/50) / port->fifosize;
++      char_time = char_time / 5;
++      if (char_time == 0)
++              char_time = 1;
++      if (timeout && timeout < char_time)
++              char_time = timeout;
++
++      /*
++       * If the transmitter hasn't cleared in twice the approximate
++       * amount of time to send the entire FIFO, it probably won't
++       * ever clear.  This assumes the UART isn't doing flow
++       * control, which is currently the case.  Hence, if it ever
++       * takes longer than port->timeout, this is probably due to a
++       * UART bug of some kind.  So, we clamp the timeout parameter at
++       * 2*port->timeout.
++       */
++      if (timeout == 0 || timeout > 2 * port->timeout)
++              timeout = 2 * port->timeout;
++
++      expire = jiffies + timeout;
++
++      DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",
++              port->line, jiffies, expire);
++
++      /*
++       * Check whether the transmitter is empty every 'char_time'.
++       * 'timeout' / 'expire' give us the maximum amount of time
++       * we wait.
++       */
++      while (!port->ops->tx_empty(port)) {
++              set_current_state(TASK_INTERRUPTIBLE);
++              schedule_timeout(char_time);
++              if (signal_pending(current))
++                      break;
++              if (time_after(jiffies, expire))
++                      break;
++      }
++      set_current_state(TASK_RUNNING); /* might not be needed */
++}
++
++/*
++ * This is called with the BKL held in
++ *  linux/drivers/char/tty_io.c:do_tty_hangup()
++ * We're called from the eventd thread, so we can sleep for
++ * a _short_ time only.
++ */
++static void uart_hangup(struct tty_struct *tty)
++{
++      struct uart_state *state = tty->driver_data;
++
++      BUG_ON(!kernel_locked());
++      DPRINTK("uart_hangup(%d)\n", state->port->line);
++
++      down(&state->sem);
++      if (state->info && state->info->flags & (UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE)) {
++              uart_flush_buffer(tty);
++              uart_shutdown(state);
++              state->count = 0;
++              state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CALLOUT_ACTIVE);
++              state->info->tty = NULL;
++              wake_up_interruptible(&state->info->open_wait);
++              wake_up_interruptible(&state->info->delta_msr_wait);
++      }
++      up(&state->sem);
++}
++
++/*
++ * Copy across the serial console cflag setting into the termios settings
++ * for the initial open of the port.  This allows continuity between the
++ * kernel settings, and the settings init adopts when it opens the port
++ * for the first time.
++ */
++static void uart_update_termios(struct uart_state *state)
++{
++      struct tty_struct *tty = state->info->tty;
++      struct uart_port *port = state->port;
++
++      if (uart_console(port) && port->cons->cflag) {
++              tty->termios->c_cflag = port->cons->cflag;
++              port->cons->cflag = 0;
++      }
++
++      /*
++       * If the device failed to grab its irq resources,
++       * or some other error occurred, don't try to talk
++       * to the port hardware.
++       */
++      if (!(tty->flags & (1 << TTY_IO_ERROR))) {
++              /*
++               * Make termios settings take effect.
++               */
++              uart_change_speed(state, NULL);
++
++              /*
++               * And finally enable the RTS and DTR signals.
++               */
++              if (tty->termios->c_cflag & CBAUD)
++                      uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++      }
++}
++
++/*
++ * Block the open until the port is ready.  We must be called with
++ * the per-port semaphore held.
++ */
++static int
++uart_block_til_ready(struct file *filp, struct uart_state *state)
++{
++      DECLARE_WAITQUEUE(wait, current);
++      struct uart_info *info = state->info;
++      struct uart_port *port = state->port;
++      struct termios *termios;
++
++      /*
++       * If this is a callout device, then just make sure the normal
++       * device isn't being used.
++       */
++      if (info->tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
++              if (info->flags & UIF_NORMAL_ACTIVE)
++                      return -EBUSY;
++              if ((info->flags & UIF_CALLOUT_ACTIVE) &&
++                  (info->flags & ASYNC_SESSION_LOCKOUT) &&
++                  (info->session != current->session))
++                      return -EBUSY;
++              if ((info->flags & UIF_CALLOUT_ACTIVE) &&
++                  (info->flags & ASYNC_PGRP_LOCKOUT) &&
++                  (info->pgrp != current->pgrp))
++                      return -EBUSY;
++              info->flags |= UIF_CALLOUT_ACTIVE;
++              return 0;
++      }
++
++      if (info->flags & UIF_CALLOUT_ACTIVE) {
++              termios = &state->normal_termios;
++      } else {
++              termios = state->info->tty->termios;
++      }
++
++      info->blocked_open++;
++      state->count--;
++
++      add_wait_queue(&info->open_wait, &wait);
++      while (1) {
++              set_current_state(TASK_INTERRUPTIBLE);
++
++              /*
++               * If we have been hung up, tell userspace/restart open.
++               */
++              if (tty_hung_up_p(filp) || info->tty == NULL)
++                      break;
++
++              /*
++               * If the port has been closed, tell userspace/restart open.
++               */
++              if (!(info->flags & UIF_INITIALIZED))
++                      break;
++
++              /*
++               * If non-blocking mode is set, or CLOCAL mode is set,
++               * we don't want to wait for the modem status lines to
++               * indicate that the port is ready.
++               *
++               * Also, if the port is not enabled/configured, we want
++               * to allow the open to succeed here.  Note that we will
++               * have set TTY_IO_ERROR for a non-existant port.
++               */
++              if ((filp->f_flags & O_NONBLOCK) ||
++                  (termios->c_cflag & CLOCAL) ||
++                  (info->tty->flags & (1 << TTY_IO_ERROR))) {
++                      break;
++              }
++
++              if (!(info->flags & UIF_CALLOUT_ACTIVE)) {
++                      /*
++                       * Set DTR to allow modem to know we're waiting.  Do
++                       * not set RTS here - we want to make sure we catch
++                       * the data from the modem.
++                       */
++                      if (info->tty->termios->c_cflag & CBAUD)
++                              uart_set_mctrl(port, TIOCM_DTR);
++
++                      /*
++                       * and wait for the carrier to indicate that the
++                       * modem is ready for us.
++                       */
++                      if (port->ops->get_mctrl(port) & TIOCM_CAR)
++                              break;
++              }
++
++              up(&state->sem);
++              schedule();
++              down(&state->sem);
++
++              if (signal_pending(current))
++                      break;
++      }
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&info->open_wait, &wait);
++
++      state->count++;
++      info->blocked_open--;
++
++      info->flags |= UIF_NORMAL_ACTIVE;
++
++      if (signal_pending(current))
++              return -ERESTARTSYS;
++
++      if (!info->tty || tty_hung_up_p(filp))
++              return -EAGAIN;
++
++      return 0;
++}
++
++static struct uart_state *uart_get(struct uart_driver *drv, int line)
++{
++      struct uart_state *state;
++
++      down(&port_sem);
++      state = drv->state + line;
++      if (down_interruptible(&state->sem)) {
++              state = ERR_PTR(-ERESTARTSYS);
++              goto out;
++      }
++
++      state->count++;
++      if (!state->port) {
++              state->count--;
++              up(&state->sem);
++              state = ERR_PTR(-ENXIO);
++              goto out;
++      }
++
++      if (!state->info) {
++              state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL);
++              if (state->info) {
++                      memset(state->info, 0, sizeof(struct uart_info));
++                      init_waitqueue_head(&state->info->open_wait);
++                      init_waitqueue_head(&state->info->delta_msr_wait);
++
++                      /*
++                       * Link the info into the other structures.
++                       */
++                      state->port->info = state->info;
++
++                      tasklet_init(&state->info->tlet, uart_tasklet_action,
++                                   (unsigned long)state);
++              } else {
++                      state->count--;
++                      up(&state->sem);
++                      state = ERR_PTR(-ENOMEM);
++              }
++      }
++
++ out:
++      up(&port_sem);
++      return state;
++}
++
++/*
++ * In 2.4.5, calls to uart_open are serialised by the BKL in
++ *   linux/fs/devices.c:chrdev_open()
++ * Note that if this fails, then uart_close() _will_ be called.
++ */
++static int uart_open(struct tty_struct *tty, struct file *filp)
++{
++      struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
++      struct uart_state *state;
++      int retval, line = MINOR(tty->device) - tty->driver.minor_start;
++
++      BUG_ON(!kernel_locked());
++      DPRINTK("uart_open(%d) called\n", line);
++
++      /*
++       * tty->driver->num won't change, so we won't fail here with
++       * tty->driver_data set to something non-NULL (and therefore
++       * we won't get caught by uart_close()).
++       */
++      retval = -ENODEV;
++      if (line >= tty->driver.num)
++              goto fail;
++
++      if (!try_inc_mod_count(drv->owner))
++              goto fail;
++
++      /*
++       * We take the semaphore inside uart_get to guarantee that we won't
++       * be re-entered while allocating the info structure, or while we
++       * request any IRQs that the driver may need.  This also has the nice
++       * side-effect that it delays the action of uart_hangup, so we can
++       * guarantee that info->tty will always contain something reasonable.
++       */
++      state = uart_get(drv, line);
++      if (IS_ERR(state)) {
++              retval = PTR_ERR(state);
++              if (!tty->driver_data)
++                      goto out;
++              else
++                      goto fail;
++      }
++
++      /*
++       * Once we set tty->driver_data here, we are guaranteed that
++       * uart_close() will decrement the driver module use count.
++       * Any failures from here onwards should not touch the count.
++       */
++      tty->driver_data = state;
++      tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
++      tty->alt_speed = uart_get_altspeed(state->port);
++      state->info->tty = tty;
++
++      /*
++       * If the port is in the middle of closing, bail out now.
++       */
++      if (tty_hung_up_p(filp)) {
++              retval = -EAGAIN;
++              state->count--;
++              up(&state->sem);
++              goto fail;
++      }
++
++      /*
++       * Make sure the device is in D0 state.
++       */
++      if (state->count == 1)
++              uart_change_pm(state, 0);
++
++      /*
++       * Start up the serial port.
++       */
++      retval = uart_startup(state, 0);
++
++      /*
++       * If we succeeded, wait until the port is ready.
++       */
++      if (retval == 0)
++              retval = uart_block_til_ready(filp, state);
++
++      /*
++       * If this is the first open to succeed, adjust things to suit.
++       */
++      if (retval == 0 && state->count == 1) {
++              if (state->port->flags & UPF_SPLIT_TERMIOS) {
++                      if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
++                              *tty->termios = state->normal_termios;
++                      else
++                              *tty->termios = state->callout_termios;
++              }
++
++              uart_update_termios(state);
++
++              state->info->session = current->session;
++              state->info->pgrp = current->pgrp;
++      }
++      up(&state->sem);
++
++      return 0;
++
++ out:
++      if (drv->owner)
++              __MOD_DEC_USE_COUNT(drv->owner);
++ fail:
++      return retval;
++}
++
++static const char *uart_type(struct uart_port *port)
++{
++      const char *str = NULL;
++
++      if (port->ops->type)
++              str = port->ops->type(port);
++
++      if (!str)
++              str = "unknown";
++
++      return str;
++}
++
++#ifdef CONFIG_PROC_FS
++
++static int uart_line_info(char *buf, struct uart_driver *drv, int i)
++{
++      struct uart_state *state = drv->state + i;
++      struct uart_port *port = state->port;
++      char stat_buf[32];
++      unsigned int status;
++      int ret;
++
++      if (!port)
++              return 0;
++
++      ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d",
++                      port->line, uart_type(port),
++                      port->iobase, port->irq);
++
++      if (port->type == PORT_UNKNOWN) {
++              strcat(buf, "\n");
++              return ret + 1;
++      }
++
++      status = port->ops->get_mctrl(port);
++
++      ret += sprintf(buf + ret, " tx:%d rx:%d",
++                      port->icount.tx, port->icount.rx);
++      if (port->icount.frame)
++              ret += sprintf(buf + ret, " fe:%d",
++                      port->icount.frame);
++      if (port->icount.parity)
++              ret += sprintf(buf + ret, " pe:%d",
++                      port->icount.parity);
++      if (port->icount.brk)
++              ret += sprintf(buf + ret, " brk:%d",
++                      port->icount.brk);
++      if (port->icount.overrun)
++              ret += sprintf(buf + ret, " oe:%d",
++                      port->icount.overrun);
++
++#define INFOBIT(bit,str) \
++      if (port->mctrl & (bit)) \
++              strcat(stat_buf, (str))
++#define STATBIT(bit,str) \
++      if (status & (bit)) \
++              strcat(stat_buf, (str))
++
++      stat_buf[0] = '\0';
++      stat_buf[1] = '\0';
++      INFOBIT(TIOCM_RTS, "|RTS");
++      STATBIT(TIOCM_CTS, "|CTS");
++      INFOBIT(TIOCM_DTR, "|DTR");
++      STATBIT(TIOCM_DSR, "|DSR");
++      STATBIT(TIOCM_CAR, "|CD");
++      STATBIT(TIOCM_RNG, "|RI");
++      if (stat_buf[0])
++              stat_buf[0] = ' ';
++      strcat(stat_buf, "\n");
++
++      ret += sprintf(buf + ret, stat_buf);
++      return ret;
++}
++
++static int uart_read_proc(char *page, char **start, off_t off,
++                        int count, int *eof, void *data)
++{
++      struct tty_driver *ttydrv = data;
++      struct uart_driver *drv = ttydrv->driver_state;
++      int i, len = 0, l;
++      off_t begin = 0;
++
++      len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
++                      "", "", "");
++      for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) {
++              l = uart_line_info(page + len, drv, i);
++              len += l;
++              if (len + begin > off + count)
++                      goto done;
++              if (len + begin < off) {
++                      begin += len;
++                      len = 0;
++              }
++      }
++      *eof = 1;
++ done:
++      if (off >= len + begin)
++              return 0;
++      *start = page + (off - begin);
++      return (count < begin + len - off) ? count : (begin + len - off);
++}
++#endif
++
++#ifdef CONFIG_SERIAL_CORE_CONSOLE
++/*
++ *    Check whether an invalid uart number has been specified, and
++ *    if so, search for the first available port that does have
++ *    console support.
++ */
++struct uart_port * __init
++uart_get_console(struct uart_port *ports, int nr, struct console *co)
++{
++      int idx = co->index;
++
++      if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
++                                   ports[idx].membase == NULL))
++              for (idx = 0; idx < nr; idx++)
++                      if (ports[idx].iobase != 0 ||
++                          ports[idx].membase != NULL)
++                              break;
++
++      co->index = idx;
++
++      return ports + idx;
++}
++
++/**
++ *    uart_parse_options - Parse serial port baud/parity/bits/flow contro.
++ *    @options: pointer to option string
++ *    @baud: pointer to an 'int' variable for the baud rate.
++ *    @parity: pointer to an 'int' variable for the parity.
++ *    @bits: pointer to an 'int' variable for the number of data bits.
++ *    @flow: pointer to an 'int' variable for the flow control character.
++ *
++ *    uart_parse_options decodes a string containing the serial console
++ *    options.  The format of the string is <baud><parity><bits><flow>,
++ *    eg: 115200n8r
++ */
++void __init
++uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
++{
++      char *s = options;
++
++      *baud = simple_strtoul(s, NULL, 10);
++      while (*s >= '0' && *s <= '9')
++              s++;
++      if (*s)
++              *parity = *s++;
++      if (*s)
++              *bits = *s++ - '0';
++      if (*s)
++              *flow = *s;
++}
++
++struct baud_rates {
++      unsigned int rate;
++      unsigned int cflag;
++};
++
++static struct baud_rates baud_rates[] = {
++      { 921600, B921600 },
++      { 460800, B460800 },
++      { 230400, B230400 },
++      { 115200, B115200 },
++      {  57600, B57600  },
++      {  38400, B38400  },
++      {  19200, B19200  },
++      {   9600, B9600   },
++      {   4800, B4800   },
++      {   2400, B2400   },
++      {   1200, B1200   },
++      {      0, B38400  }
++};
++
++/**
++ *    uart_set_options - setup the serial console parameters
++ *    @port: pointer to the serial ports uart_port structure
++ *    @co: console pointer
++ *    @baud: baud rate
++ *    @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
++ *    @bits: number of data bits
++ *    @flow: flow control character - 'r' (rts)
++ */
++int __init
++uart_set_options(struct uart_port *port, struct console *co,
++               int baud, int parity, int bits, int flow)
++{
++      struct termios termios;
++      unsigned int quot;
++      int i;
++
++      memset(&termios, 0, sizeof(struct termios));
++
++      termios.c_cflag = CREAD | HUPCL | CLOCAL;
++
++      /*
++       * Construct a cflag setting.
++       */
++      for (i = 0; baud_rates[i].rate; i++)
++              if (baud_rates[i].rate <= baud)
++                      break;
++
++      termios.c_cflag |= baud_rates[i].cflag;
++      baud = baud_rates[i].rate;
++      if (baud == 0)
++              baud = 38400;
++
++      if (bits == 7)
++              termios.c_cflag |= CS7;
++      else
++              termios.c_cflag |= CS8;
++
++      switch (parity) {
++      case 'o': case 'O':
++              termios.c_cflag |= PARODD;
++              /*fall through*/
++      case 'e': case 'E':
++              termios.c_cflag |= PARENB;
++              break;
++      }
++
++      if (flow == 'r')
++              termios.c_cflag |= CRTSCTS;
++
++      quot = (port->uartclk / (16 * baud));
++      port->ops->change_speed(port, termios.c_cflag, 0, quot);
++      co->cflag = termios.c_cflag;
++
++      return 0;
++}
++
++extern void ambauart_console_init(void);
++extern void anakin_console_init(void);
++extern void clps711xuart_console_init(void);
++extern void sa1100_rs_console_init(void);
++extern void serial8250_console_init(void);
++extern void at91_console_init(void);
++
++/*
++ * Central "initialise all serial consoles" container.  Needs to be killed.
++ */
++void __init uart_console_init(void)
++{
++#ifdef CONFIG_SERIAL_AMBA_CONSOLE
++      ambauart_console_init();
++#endif
++#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
++      anakin_console_init();
++#endif
++#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
++      clps711xuart_console_init();
++#endif
++#ifdef CONFIG_SERIAL_SA1100_CONSOLE
++      sa1100_rs_console_init();
++#endif
++#ifdef CONFIG_SERIAL_AT91_CONSOLE
++      at91_console_init();
++#endif
++#ifdef CONFIG_SERIAL_8250_CONSOLE
++      serial8250_console_init();
++#endif
++#ifdef CONFIG_SERIAL_UART00_CONSOLE
++      uart00_console_init();
++#endif
++}
++#endif /* CONFIG_SERIAL_CORE_CONSOLE */
++
++static void uart_change_pm(struct uart_state *state, int pm_state)
++{
++      struct uart_port *port = state->port;
++      if (port->ops->pm)
++              port->ops->pm(port, pm_state, 0);
++}
++
++#ifdef CONFIG_PM
++int uart_suspend_port(struct uart_state *state)
++{
++      struct uart_port *port = state->port;
++
++      down(&state->sem);
++      if (port) {
++              /*
++               * Disable the console device before suspending.
++               */
++              if (uart_console(port))
++                      port->cons->flags &= ~CON_ENABLED;
++
++              if (state->info && state->info->flags & UIF_INITIALIZED) {
++                      struct uart_ops *ops = port->ops;
++
++                      spin_lock_irq(&port->lock);
++                      ops->stop_tx(port, 0);
++                      ops->set_mctrl(port, 0);
++                      ops->stop_rx(port);
++                      spin_unlock_irq(&port->lock);
++
++                      /*
++                       * Wait for the transmitter to empty.
++                       */
++                      while (!ops->tx_empty(port)) {
++                              set_current_state(TASK_UNINTERRUPTIBLE);
++                              schedule_timeout(10*HZ/1000);
++                      }
++                      set_current_state(TASK_RUNNING);
++
++                      ops->shutdown(port);
++              }
++
++              uart_change_pm(state, 3);
++      }
++
++      up(&state->sem);
++
++      return 0;
++}
++
++int uart_resume_port(struct uart_state *state)
++{
++      struct uart_port *port = state->port;
++
++      down(&state->sem);
++      if (port) {
++              uart_change_pm(state, 0);
++
++              /*
++               * Re-enable the console device after suspending.
++               */
++              if (uart_console(port)) {
++                      uart_change_speed(state, NULL);
++                      port->cons->flags |= CON_ENABLED;
++              }
++
++              if (state->info && state->info->flags & UIF_INITIALIZED) {
++                      struct uart_ops *ops = port->ops;
++
++                      ops->set_mctrl(port, 0);
++                      ops->startup(port);
++                      uart_change_speed(state, NULL);
++                      spin_lock_irq(&port->lock);
++                      ops->set_mctrl(port, port->mctrl);
++                      ops->start_tx(port, 0);
++                      spin_unlock_irq(&port->lock);
++              }
++      }
++
++      up(&state->sem);
++
++      return 0;
++}
++
++/*
++ *  Wakeup support.
++ */
++static int uart_pm_set_wakeup(struct uart_state *state, int data)
++{
++      int err = 0;
++
++      if (state->port->ops->set_wake)
++              err = state->port->ops->set_wake(state->port, data);
++
++      return err;
++}
++
++static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      struct uart_state *state = dev->data;
++      int err = 0;
++
++      if (state->port && state->port->type == PORT_UNKNOWN)
++              return 0;
++
++      switch (rqst) {
++      case PM_SUSPEND:
++              err = uart_suspend_port(state);
++              break;
++
++      case PM_RESUME:
++              err = uart_resume_port(state);
++              break;
++
++      case PM_SET_WAKEUP:
++              err = uart_pm_set_wakeup(state, (int)data);
++              break;
++      }
++      return err;
++}
++#endif
++
++static inline void
++uart_report_port(struct uart_driver *drv, struct uart_port *port)
++{
++      printk("%s%d at ", drv->normal_name, port->line);
++      switch (port->iotype) {
++      case UPIO_PORT:
++              printk("I/O 0x%x", port->iobase);
++              break;
++      case UPIO_HUB6:
++              printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6);
++              break;
++      case UPIO_MEM:
++              printk("MMIO 0x%lx", port->mapbase);
++              break;
++      }
++      printk(" (irq = %d) is a %s\n", port->irq, uart_type(port));
++}
++
++static void
++uart_configure_port(struct uart_driver *drv, struct uart_state *state,
++                  struct uart_port *port)
++{
++      unsigned int flags;
++
++      /*
++       * If there isn't a port here, don't do anything further.
++       */
++      if (!port->iobase && !port->mapbase && !port->membase)
++              return;
++
++      /*
++       * Now do the auto configuration stuff.  Note that config_port
++       * is expected to claim the resources and map the port for us.
++       */
++      flags = UART_CONFIG_TYPE;
++      if (port->flags & UPF_AUTO_IRQ)
++              flags |= UART_CONFIG_IRQ;
++      if (port->flags & UPF_BOOT_AUTOCONF) {
++              port->type = PORT_UNKNOWN;
++              port->ops->config_port(port, flags);
++      }
++
++      if (port->type != PORT_UNKNOWN) {
++              unsigned long flags;
++
++              uart_report_port(drv, port);
++
++              /*
++               * Ensure that the modem control lines are de-activated.
++               * We probably don't need a spinlock around this, but
++               */
++              spin_lock_irqsave(&port->lock, flags);
++              port->ops->set_mctrl(port, 0);
++              spin_unlock_irqrestore(&port->lock, flags);
++
++              /*
++               * Power down all ports by default, except the
++               * console if we have one.
++               */
++              if (!uart_console(port))
++                      uart_change_pm(state, 3);
++      }
++}
++
++/*
++ * This reverses the effects of uart_configure_port, hanging up the
++ * port before removal.
++ */
++static void
++uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state)
++{
++      struct uart_port *port = state->port;
++      struct uart_info *info = state->info;
++
++      if (info && info->tty)
++              tty_vhangup(info->tty);
++
++      down(&state->sem);
++
++      state->info = NULL;
++
++      /*
++       * Free the port IO and memory resources, if any.
++       */
++      if (port->type != PORT_UNKNOWN)
++              port->ops->release_port(port);
++
++      /*
++       * Indicate that there isn't a port here anymore.
++       */
++      port->type = PORT_UNKNOWN;
++
++      /*
++       * Kill the tasklet, and free resources.
++       */
++      if (info) {
++              tasklet_kill(&info->tlet);
++              kfree(info);
++      }
++
++      up(&state->sem);
++}
++
++/**
++ *    uart_register_driver - register a driver with the uart core layer
++ *    @drv: low level driver structure
++ *
++ *    Register a uart driver with the core driver.  We in turn register
++ *    with the tty layer, and initialise the core driver per-port state.
++ *
++ *    We have a proc file in /proc/tty/driver which is named after the
++ *    normal driver.
++ *
++ *    drv->port should be NULL, and the per-port structures should be
++ *    registered using uart_add_one_port after this call has succeeded.
++ */
++int uart_register_driver(struct uart_driver *drv)
++{
++      struct tty_driver *normal, *callout;
++      int i, retval;
++
++      BUG_ON(drv->state);
++
++      /*
++       * Maybe we should be using a slab cache for this, especially if
++       * we have a large number of ports to handle.  Note that we also
++       * allocate space for an integer for reference counting.
++       */
++      drv->state = kmalloc(sizeof(struct uart_state) * drv->nr +
++                           sizeof(int), GFP_KERNEL);
++      retval = -ENOMEM;
++      if (!drv->state)
++              goto out;
++
++      memset(drv->state, 0, sizeof(struct uart_state) * drv->nr +
++                      sizeof(int));
++
++      normal  = drv->normal_driver;
++      callout = drv->callout_driver;
++
++      normal->magic           = TTY_DRIVER_MAGIC;
++      normal->driver_name     = drv->normal_name;
++      normal->name            = drv->normal_name;
++      normal->major           = drv->normal_major;
++      normal->minor_start     = drv->minor;
++      normal->num             = drv->nr;
++      normal->type            = TTY_DRIVER_TYPE_SERIAL;
++      normal->subtype         = SERIAL_TYPE_NORMAL;
++      normal->init_termios    = tty_std_termios;
++      normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++      normal->flags           = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
++      normal->refcount        = (int *)(drv->state + drv->nr);
++      normal->table           = drv->table;
++      normal->termios         = drv->termios;
++      normal->termios_locked  = drv->termios_locked;
++      normal->driver_state    = drv;
++
++      normal->open            = uart_open;
++      normal->close           = uart_close;
++      normal->write           = uart_write;
++      normal->put_char        = uart_put_char;
++      normal->flush_chars     = uart_flush_chars;
++      normal->write_room      = uart_write_room;
++      normal->chars_in_buffer = uart_chars_in_buffer;
++      normal->flush_buffer    = uart_flush_buffer;
++      normal->ioctl           = uart_ioctl;
++      normal->throttle        = uart_throttle;
++      normal->unthrottle      = uart_unthrottle;
++      normal->send_xchar      = uart_send_xchar;
++      normal->set_termios     = uart_set_termios;
++      normal->stop            = uart_stop;
++      normal->start           = uart_start;
++      normal->hangup          = uart_hangup;
++      normal->break_ctl       = uart_break_ctl;
++      normal->wait_until_sent = uart_wait_until_sent;
++#ifdef CONFIG_PROC_FS
++      normal->read_proc       = uart_read_proc;
++#endif
++
++      /*
++       * The callout device is just like the normal device except for
++       * the major number and the subtype code.
++       */
++      *callout                = *normal;
++      callout->name           = drv->callout_name;
++      callout->major          = drv->callout_major;
++      callout->subtype        = SERIAL_TYPE_CALLOUT;
++      callout->read_proc      = NULL;
++      callout->proc_entry     = NULL;
++
++      /*
++       * Initialise the UART state(s).
++       */
++      for (i = 0; i < drv->nr; i++) {
++              struct uart_state *state = drv->state + i;
++
++              state->callout_termios = callout->init_termios;
++              state->normal_termios  = normal->init_termios;
++              state->close_delay     = 5 * HZ / 10;
++              state->closing_wait    = 30 * HZ;
++
++              init_MUTEX(&state->sem);
++      }
++
++      retval = tty_register_driver(normal);
++      if (retval)
++              goto out;
++
++      retval = tty_register_driver(callout);
++      if (retval)
++              tty_unregister_driver(normal);
++
++ out:
++      if (retval < 0) {
++              kfree(drv->state);
++      }
++      return retval;
++}
++
++/**
++ *    uart_unregister_driver - remove a driver from the uart core layer
++ *    @drv: low level driver structure
++ *
++ *    Remove all references to a driver from the core driver.  The low
++ *    level driver must have removed all its ports via the
++ *    uart_remove_one_port() if it registered them with uart_add_one_port().
++ *    (ie, drv->port == NULL)
++ */
++void uart_unregister_driver(struct uart_driver *drv)
++{
++      tty_unregister_driver(drv->normal_driver);
++      tty_unregister_driver(drv->callout_driver);
++
++      kfree(drv->state);
++      drv->state = NULL;
++}
++
++/**
++ *    uart_add_one_port - attach a driver-defined port structure
++ *    @drv: pointer to the uart low level driver structure for this port
++ *    @port: uart port structure to use for this port.
++ *
++ *    This allows the driver to register its own uart_port structure
++ *    with the core driver.  The main purpose is to allow the low
++ *    level uart drivers to expand uart_port, rather than having yet
++ *    more levels of structures.
++ */
++int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
++{
++      struct uart_state *state;
++      int ret = 0;
++
++      BUG_ON(in_interrupt());
++
++      if (port->line >= drv->nr)
++              return -EINVAL;
++
++      state = drv->state + port->line;
++
++      down(&port_sem);
++      if (state->port) {
++              ret = -EINVAL;
++              goto out;
++      }
++
++      state->port = port;
++
++      spin_lock_init(&port->lock);
++      port->cons = drv->cons;
++      port->info = state->info;
++
++      uart_configure_port(drv, state, port);
++
++      /*
++       * Register the port whether it's detected or not.  This allows
++       * setserial to be used to alter this ports parameters.
++       */
++      tty_register_devfs(drv->normal_driver, 0, drv->minor + port->line);
++      tty_register_devfs(drv->callout_driver, 0, drv->minor + port->line);
++
++#ifdef CONFIG_PM
++      port->cons = drv->cons;
++      state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm);
++      if (state->pm)
++              state->pm->data = state;
++#endif
++
++ out:
++      up(&port_sem);
++
++      return ret;
++}
++
++/**
++ *    uart_remove_one_port - detach a driver defined port structure
++ *    @drv: pointer to the uart low level driver structure for this port
++ *    @port: uart port structure for this port
++ *
++ *    This unhooks (and hangs up) the specified port structure from the
++ *    core driver.  No further calls will be made to the low-level code
++ *    for this port.
++ */
++int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
++{
++      struct uart_state *state = drv->state + port->line;
++
++      BUG_ON(in_interrupt());
++
++      if (state->port != port)
++              printk(KERN_ALERT "Removing wrong port: %p != %p\n",
++                      state->port, port);
++
++      down(&port_sem);
++
++      pm_unregister(state->pm);
++
++      /*
++       * Remove the devices from devfs
++       */
++      tty_unregister_devfs(drv->normal_driver, drv->minor + port->line);
++      tty_unregister_devfs(drv->callout_driver, drv->minor + port->line);
++
++      uart_unconfigure_port(drv, state);
++      state->port = NULL;
++      up(&port_sem);
++
++      return 0;
++}
++
++/*
++ *    Are the two ports equivalent?
++ */
++static int uart_match_port(struct uart_port *port1, struct uart_port *port2)
++{
++      if (port1->iotype != port2->iotype)
++              return 0;
++
++      switch (port1->iotype) {
++      case UPIO_PORT:
++              return (port1->iobase == port2->iobase);
++      case UPIO_HUB6:
++              return (port1->iobase == port2->iobase) &&
++                     (port1->hub6   == port2->hub6);
++      case UPIO_MEM:
++              return (port1->membase == port2->membase);
++      }
++      return 0;
++}
++
++/*
++ *    Try to find an unused uart_state slot for a port.
++ */
++static struct uart_state *
++uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port)
++{
++      int i;
++
++      /*
++       * First, find a port entry which matches.  Note: if we do
++       * find a matching entry, and it has a non-zero use count,
++       * then we can't register the port.
++       */
++      for (i = 0; i < drv->nr; i++)
++              if (uart_match_port(drv->state[i].port, port))
++                      return &drv->state[i];
++
++      /*
++       * We didn't find a matching entry, so look for the first
++       * free entry.  We look for one which hasn't been previously
++       * used (indicated by zero iobase).
++       */
++      for (i = 0; i < drv->nr; i++)
++              if (drv->state[i].port->type == PORT_UNKNOWN &&
++                  drv->state[i].port->iobase == 0 &&
++                  drv->state[i].count == 0)
++                      return &drv->state[i];
++
++      /*
++       * That also failed.  Last resort is to find any currently
++       * entry which doesn't have a real port associated with it.
++       */
++      for (i = 0; i < drv->nr; i++)
++              if (drv->state[i].port->type == PORT_UNKNOWN &&
++                  drv->state[i].count == 0)
++                      return &drv->state[i];
++
++      return NULL;
++}
++
++/**
++ *    uart_register_port: register uart settings with a port
++ *    @drv: pointer to the uart low level driver structure for this port
++ *    @port: uart port structure describing the port
++ *
++ *    Register UART settings with the specified low level driver.  Detect
++ *    the type of the port if UPF_BOOT_AUTOCONF is set, and detect the
++ *    IRQ if UPF_AUTO_IRQ is set.
++ *
++ *    We try to pick the same port for the same IO base address, so that
++ *    when a modem is plugged in, unplugged and plugged back in, it gets
++ *    allocated the same port.
++ *
++ *    Returns negative error, or positive line number.
++ */
++int uart_register_port(struct uart_driver *drv, struct uart_port *port)
++{
++      struct uart_state *state;
++      int ret;
++
++      down(&port_sem);
++
++      state = uart_find_match_or_unused(drv, port);
++
++      if (state) {
++              /*
++               * Ok, we've found a line that we can use.
++               *
++               * If we find a port that matches this one, and it appears
++               * to be in-use (even if it doesn't have a type) we shouldn't
++               * alter it underneath itself - the port may be open and
++               * trying to do useful work.
++               */
++              if (uart_users(state) != 0) {
++                      ret = -EBUSY;
++                      goto out;
++              }
++
++              /*
++               * If the port is already initialised, don't touch it.
++               */
++              if (state->port->type == PORT_UNKNOWN) {
++                      state->port->iobase   = port->iobase;
++                      state->port->membase  = port->membase;
++                      state->port->irq      = port->irq;
++                      state->port->uartclk  = port->uartclk;
++                      state->port->fifosize = port->fifosize;
++                      state->port->regshift = port->regshift;
++                      state->port->iotype   = port->iotype;
++                      state->port->flags    = port->flags;
++                      state->port->line     = state - drv->state;
++                      state->port->mapbase  = port->mapbase;
++
++                      uart_configure_port(drv, state, state->port);
++              }
++
++              ret = state->port->line;
++      } else
++              ret = -ENOSPC;
++ out:
++      up(&port_sem);
++      return ret;
++}
++
++/**
++ *    uart_unregister_port - de-allocate a port
++ *    @drv: pointer to the uart low level driver structure for this port
++ *    @line: line index previously returned from uart_register_port()
++ *
++ *    Hang up the specified line associated with the low level driver,
++ *    and mark the port as unused.
++ */
++void uart_unregister_port(struct uart_driver *drv, int line)
++{
++      struct uart_state *state;
++
++      if (line < 0 || line >= drv->nr) {
++              printk(KERN_ERR "Attempt to unregister %s%d\n",
++                      drv->normal_name, line);
++              return;
++      }
++
++      state = drv->state + line;
++
++      down(&port_sem);
++      uart_unconfigure_port(drv, state);
++      up(&port_sem);
++}
++
++EXPORT_SYMBOL(uart_write_wakeup);
++EXPORT_SYMBOL(uart_register_driver);
++EXPORT_SYMBOL(uart_unregister_driver);
++EXPORT_SYMBOL(uart_register_port);
++EXPORT_SYMBOL(uart_unregister_port);
++EXPORT_SYMBOL(uart_add_one_port);
++EXPORT_SYMBOL(uart_remove_one_port);
++
++MODULE_DESCRIPTION("Serial driver core");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/serial/omaha.c
+@@ -0,0 +1,584 @@
++/*
++ *  linux/drivers/char/omaha.c
++ *
++ *  Driver for Omaha serial port
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright 1999-2002 ARM Limited
++ *  Copyright (C) 2000 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
++ * 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: serial_amba.c,v 1.4 2001/07/17 20:34:27 rmk Exp $
++ *
++ * This is a generic driver for ARM AMBA-type serial ports.  They
++ * have a lot of 16550-like features, but are not register compatable.
++ * Note that although they do have CTS, DCD and DSR inputs, they do
++ * not have an RI input, nor do they have DTR or RTS outputs.  If
++ * required, these have to be supplied via some other means (eg, GPIO)
++ * and hooked into this driver.
++ */
++  
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++
++#if defined(CONFIG_SERIAL_OMAHA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++#include <asm/hardware/serial_omaha.h>
++
++#define UART_NR               1
++
++#define SERIAL_OMAHA_MAJOR    204
++#define SERIAL_OMAHA_MINOR    32
++#define SERIAL_OMAHA_NR               UART_NR
++
++#define CALLOUT_OMAHA_NAME    "cuaom"
++#define CALLOUT_OMAHA_MAJOR   205
++#define CALLOUT_OMAHA_MINOR   32
++#define CALLOUT_OMAHA_NR      UART_NR
++
++static struct tty_driver normal, callout;
++static struct tty_struct *omaha_table[UART_NR];
++static struct termios *omaha_termios[UART_NR], *omaha_termios_locked[UART_NR];
++#ifdef SUPPORT_SYSRQ
++static struct console omaha_console;
++#endif
++
++#define OMAHA_ISR_PASS_LIMIT  256
++
++/*
++ * Access macros for the Omaha UARTs
++ */
++
++#define UART_GET_FR(p)                readb((p)->membase + OMAHA_UTRSTAT)
++#define UART_GET_CHAR(p)      readb((p)->membase + OMAHA_URXH)
++#define UART_PUT_CHAR(p, c)   writel((c), (p)->membase + OMAHA_UTXH)
++#define UART_GET_RSR(p)               readb((p)->membase + OMAHA_UERSTAT)
++#define UART_FIFO_STATUS(p)   (readl((p)->membase + OMAHA_UFSTAT))
++#define UART_RX_DATA(s)               (((s) & OMAHA_RXFF_CNT) != 0)
++#define UART_TX_DATA(s)               (!((s) & OMAHA_TXFF))
++#define UART_TX_READY(s)      (((s) & OMAHA_UTX_EMPTY))
++#define UART_TX_EMPTY(p)      ((UART_GET_FR(p) & OMAHA_UTXEMPTY) != 0)
++                                    
++#define UART_DUMMY_RSR_RX     256
++#define UART_PORT_SIZE                64
++
++#define RX_IRQ(port)          ((port)->irq)
++#define TX_IRQ(port)          ((port)->irq + 5)
++
++/*
++ * Our private driver data mappings.
++ */
++#define drv_old_status        driver_priv
++
++static void omahauart_stop_tx(struct uart_port *port, u_int from_tty)
++{
++      disable_irq(TX_IRQ(port));
++}
++
++static void omahauart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty)
++{
++      if (nonempty)
++              enable_irq(TX_IRQ(port));
++}
++
++static void omahauart_stop_rx(struct uart_port *port)
++{
++      disable_irq(RX_IRQ(port));
++}
++
++static void omahauart_enable_ms(struct uart_port *port)
++{
++      // Do nothing...
++}
++
++static void
++#ifdef SUPPORT_SYSRQ
++omahauart_rx_chars(struct uart_info *info, struct pt_regs *regs)
++#else
++omahauart_rx_chars(struct uart_info *info)
++#endif
++{
++      struct tty_struct *tty = info->tty;
++      volatile unsigned int status, data, ch, rsr, max_count = 256;
++      struct uart_port *port = info->port;
++
++      status = UART_FIFO_STATUS(port);
++      while (UART_RX_DATA(status) && max_count--) {
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++                      tty->flip.tqueue.routine((void *)tty);
++                      if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
++                              printk(KERN_WARNING "TTY_DONT_FLIP set\n");
++                              return;
++                      }
++              }
++
++              ch = UART_GET_CHAR(port);
++
++              *tty->flip.char_buf_ptr = ch;
++              *tty->flip.flag_buf_ptr = TTY_NORMAL;
++              port->icount.rx++;
++
++              /*
++               * Note that the error handling code is
++               * out of the main execution path
++               */
++              rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX;
++              if (rsr & 0xf) {
++                      if (rsr & OMAHA_UART_BREAK) {
++                              rsr &= ~(OMAHA_UART_FRAME | OMAHA_UART_PARITY);
++                              port->icount.brk++;
++                              if (uart_handle_break(info, &omaha_console))
++                                      goto ignore_char;
++                      } else if (rsr & OMAHA_UART_PARITY)
++                              port->icount.parity++;
++                      else if (rsr & OMAHA_UART_FRAME)
++                              port->icount.frame++;
++                      if (rsr & OMAHA_UART_OVERRUN)
++                              port->icount.overrun++;
++
++                      rsr &= port->read_status_mask;
++
++                      if (rsr & OMAHA_UART_BREAK)
++                              *tty->flip.flag_buf_ptr = TTY_BREAK;
++                      else if (rsr & OMAHA_UART_PARITY)
++                              *tty->flip.flag_buf_ptr = TTY_PARITY;
++                      else if (rsr & OMAHA_UART_FRAME)
++                              *tty->flip.flag_buf_ptr = TTY_FRAME;
++              }
++
++              if (uart_handle_sysrq_char(info, ch, regs))
++                      goto ignore_char;
++
++              if ((rsr & port->ignore_status_mask) == 0) {
++                      tty->flip.flag_buf_ptr++;
++                      tty->flip.char_buf_ptr++;
++                      tty->flip.count++;
++              }
++              if ((rsr & OMAHA_UART_OVERRUN) &&
++                  tty->flip.count < TTY_FLIPBUF_SIZE) {
++                      /*
++                       * Overrun is special, since it's reported
++                       * immediately, and doesn't affect the current
++                       * character
++                       */
++                      *tty->flip.char_buf_ptr++ = 0;
++                      *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
++                      tty->flip.count++;
++              }
++      ignore_char:
++              status = UART_FIFO_STATUS(port);
++      }
++      tty_flip_buffer_push(tty);
++      return;
++}
++
++static void omahauart_tx_chars(struct uart_info *info)
++{
++      struct uart_port *port = info->port;
++      volatile unsigned int status;
++
++      if (port->x_char) {
++              UART_PUT_CHAR(port, port->x_char);
++              port->icount.tx++;
++              port->x_char = 0;
++              return;
++      }
++      if (info->xmit.head == info->xmit.tail
++          || info->tty->stopped
++          || info->tty->hw_stopped) {
++              omahauart_stop_tx(port, 0);
++              return;
++      }
++
++      status = UART_FIFO_STATUS(info->port);
++      
++      // FIll FIFO as far as possible
++      while(UART_TX_DATA(UART_FIFO_STATUS(info->port)))
++      {
++              UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
++              info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (info->xmit.head == info->xmit.tail)
++                      break;
++      }
++
++      if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) <
++                      WAKEUP_CHARS)
++              uart_event(info, EVT_WRITE_WAKEUP);
++
++      if (info->xmit.head == info->xmit.tail)
++              omahauart_stop_tx(info->port, 0);
++}
++
++static void omahauart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_info *info = dev_id;
++      volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT;
++
++      status = UART_FIFO_STATUS(info->port);
++      do {
++              // TX if FIFO not full
++              if (UART_TX_DATA(status))
++                      omahauart_tx_chars(info);
++              
++              if (pass_counter-- == 0)
++                      break;
++
++              status = UART_FIFO_STATUS(info->port);
++      } while (UART_TX_DATA(status));
++}
++
++static void omahauart_int_rx(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_info *info = dev_id;
++      volatile unsigned int status, pass_counter = OMAHA_ISR_PASS_LIMIT;
++
++      status = UART_FIFO_STATUS(info->port);
++      do {
++              if (UART_RX_DATA(status))
++#ifdef SUPPORT_SYSRQ
++                      omahauart_rx_chars(info, regs);
++#else
++                      omahauart_rx_chars(info);
++#endif
++              
++              if (pass_counter-- == 0)
++                      break;
++
++              status = UART_FIFO_STATUS(info->port);
++      } while (UART_RX_DATA(status));
++}
++
++static u_int omahauart_tx_empty(struct uart_port *port)
++{
++      return UART_FIFO_STATUS(port) ? 0 : TIOCSER_TEMT;
++}
++
++static int omahauart_get_mctrl(struct uart_port *port)
++{
++      // Report no errors.
++
++      return 0;
++}
++
++static void omahauart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++      // Do nothing.
++}
++
++static void omahauart_break_ctl(struct uart_port *port, int break_state)
++{
++      // Do nothing.
++}
++
++static int omahauart_startup(struct uart_port *port, struct uart_info *info)
++{
++      unsigned int tmp;
++      int retval;
++
++      /*
++       * Allocate the IRQs
++       */
++      retval = request_irq(TX_IRQ(port), omahauart_int_tx, 0, "omaha_uart_tx", info);
++      if (retval)
++              return retval;
++
++      retval = request_irq(RX_IRQ(port), omahauart_int_rx, 0, "omaha_uart_rx", info);
++      
++      if (retval)
++      {
++              free_irq(TX_IRQ(port), info);
++              return retval;
++      }
++      
++      /*
++       * initialise the old status of the modem signals
++       */
++      info->drv_old_status = 0;
++
++      // Clear all errors
++      writel(0, port->membase + OMAHA_UERSTAT);
++      
++      // Enable FIFO, 16-byte watermark, also do reset (auto-clearing)
++      writel(0xF7, port->membase + OMAHA_UFCON);
++
++      // Level driven TX/RX ints, with rx timeout enabled
++      tmp = readl(port->membase + OMAHA_UCON);
++      tmp |= 0x280; // rx is pulse driven...
++      writel(tmp, port->membase + OMAHA_UCON);
++
++      return 0;
++}
++
++static void omahauart_shutdown(struct uart_port *port, struct uart_info *info)
++{
++      /*
++       * Free the interrupt
++       */
++      free_irq(TX_IRQ(port), info);   /* TX interrupt */
++      free_irq(RX_IRQ(port), info);   /* RX interrupt */
++
++}
++
++static void omahauart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)
++{
++      // Do nothing.
++}
++
++static const char *omahauart_type(struct uart_port *port)
++{
++      return port->type == PORT_OMAHA ? "OMAHA" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'
++ */
++static void omahauart_release_port(struct uart_port *port)
++{
++      release_mem_region(port->mapbase, UART_PORT_SIZE);
++}
++
++/*
++ * Request the memory region(s) being used by 'port'
++ */
++static int omahauart_request_port(struct uart_port *port)
++{
++      return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_omaha")
++                      != NULL ? 0 : -EBUSY;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void omahauart_config_port(struct uart_port *port, int flags)
++{
++      if (flags & UART_CONFIG_TYPE) {
++              port->type = PORT_OMAHA;
++              omahauart_request_port(port);
++      }
++}
++
++/*
++ * verify the new serial_struct (for TIOCSSERIAL).
++ */
++static int omahauart_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      int ret = 0;
++      if (ser->type != PORT_UNKNOWN && ser->type != PORT_OMAHA)
++              ret = -EINVAL;
++      if (ser->irq < 0 || ser->irq >= NR_IRQS)
++              ret = -EINVAL;
++      if (ser->baud_base < 9600)
++              ret = -EINVAL;
++      return ret;
++}
++
++static struct uart_ops omaha_pops = {
++      .tx_empty       = omahauart_tx_empty,
++      .set_mctrl      = omahauart_set_mctrl,
++      .get_mctrl      = omahauart_get_mctrl,
++      .stop_tx        = omahauart_stop_tx,
++      .start_tx       = omahauart_start_tx,
++      .stop_rx        = omahauart_stop_rx,
++      .enable_ms      = omahauart_enable_ms,
++      .break_ctl      = omahauart_break_ctl,
++      .startup        = omahauart_startup,
++      .shutdown       = omahauart_shutdown,
++      .change_speed   = omahauart_change_speed,
++      .type           = omahauart_type,
++      .release_port   = omahauart_release_port,
++      .request_port   = omahauart_request_port,
++      .config_port    = omahauart_config_port,
++      .verify_port    = omahauart_verify_port,
++};
++
++static struct uart_port omaha_ports[UART_NR] = {
++      {
++              .membase        = (void *)IO_ADDRESS(OMAHA_UART0_BASE),
++              .mapbase        = OMAHA_UART0_BASE,
++              .iotype         = SERIAL_IO_MEM,
++              .irq            = OMAHA_INT_URXD0,
++              .uartclk        = 10000000,
++              .fifosize       = 8,
++              .unused         = { 4, 5 }, /*Udriver_priv: PORT_CTRLS(5, 4), */
++              .ops            = &omaha_pops,
++              .flags          = ASYNC_BOOT_AUTOCONF,
++      }
++};
++
++#ifdef CONFIG_SERIAL_OMAHA_CONSOLE
++static void omahauart_console_write(struct console *co, const char *s, u_int count)
++{
++      struct uart_port *port = omaha_ports + co->index;
++      unsigned int status;
++      int i;
++
++      /*
++       *      First save the CR then disable the interrupts
++       */
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = UART_GET_FR(port);
++              } while ((status & OMAHA_UTX_EMPTY) == 0);
++              UART_PUT_CHAR(port, s[i]);
++              if (s[i] == '\n') {
++                      do {
++                              status = UART_GET_FR(port);
++                      } while ((status & OMAHA_UTX_EMPTY) == 0);
++                      UART_PUT_CHAR(port, '\r');
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the TCR
++       */
++      do {
++              status = UART_GET_FR(port);
++      } while ((status & OMAHA_UTX_EMPTY) == 0);
++}
++
++static kdev_t omahauart_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_OMAHA_MAJOR, SERIAL_OMAHA_MINOR + co->index);
++}
++
++static int omahauart_console_wait_key(struct console *co)
++{
++      struct uart_port *port = omaha_ports + co->index;
++      unsigned int status;
++
++      do {
++              status = UART_FIFO_STATUS(port);
++      } while (!UART_RX_DATA(status));
++      return UART_GET_CHAR(port);
++}
++
++static void __init
++omahauart_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
++{
++      // Do nothing.
++}
++
++static int __init omahauart_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = 38400;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      port = uart_get_console(omaha_ports, UART_NR, co);
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              omahauart_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console omaha_console = {
++      .write          = omahauart_console_write,
++      .device         = omahauart_console_device,
++      .wait_key       = omahauart_console_wait_key,
++      .setup          = omahauart_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++};
++
++void __init omahauart_console_init(void)
++{
++      register_console(&omaha_console);
++}
++
++#define OMAHA_CONSOLE &omaha_console
++#else
++#define OMAHA_CONSOLE NULL
++#endif
++
++static struct uart_driver omaha_reg = {
++      .owner                  = THIS_MODULE,
++      .normal_major           = SERIAL_OMAHA_MAJOR,
++#ifdef CONFIG_DEVFS_FS
++      .normal_name            = "ttyOM%d",
++      .callout_name           = "cuaom%d",
++#else
++      .normal_name            = "ttyOM",
++      .callout_name           = "cuaom",
++#endif
++      .normal_driver          = &normal,
++      .callout_major          = CALLOUT_OMAHA_MAJOR,
++      .callout_driver         = &callout,
++      .table                  = omaha_table,
++      .termios                = omaha_termios,
++      .termios_locked         = omaha_termios_locked,
++      .minor                  = SERIAL_OMAHA_MINOR,
++      .nr                     = UART_NR,
++      .port                   = omaha_ports,
++      .cons                   = OMAHA_CONSOLE,
++};
++
++static int __init omahauart_init(void)
++{
++      return uart_register_driver(&omaha_reg);
++}
++
++static void __exit omahauart_exit(void)
++{
++      uart_unregister_driver(&omaha_reg);
++}
++
++module_init(omahauart_init);
++module_exit(omahauart_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/serial/sa1100.c
+@@ -0,0 +1,904 @@
++/*
++ *  linux/drivers/char/serial_sa1100.c
++ *
++ *  Driver for SA11x0 serial ports
++ *
++ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
++ *
++ *  Copyright (C) 2000 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
++ * 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: sa1100.c,v 1.14.2.4 2002/10/24 09:53:25 rmk Exp $
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/ioport.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/mach/serial_sa1100.h>
++
++#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++
++/* We've been assigned a range on the "Low-density serial ports" major */
++#define SERIAL_SA1100_MAJOR   204
++#define CALLOUT_SA1100_MAJOR  205
++#define MINOR_START           5
++
++#define NR_PORTS              3
++
++#define SA1100_ISR_PASS_LIMIT 256
++
++/*
++ * Convert from ignore_status_mask or read_status_mask to UTSR[01]
++ */
++#define SM_TO_UTSR0(x)        ((x) & 0xff)
++#define SM_TO_UTSR1(x)        ((x) >> 8)
++#define UTSR0_TO_SM(x)        ((x))
++#define UTSR1_TO_SM(x)        ((x) << 8)
++
++#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0)
++#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1)
++#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2)
++#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3)
++#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0)
++#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1)
++#define UART_GET_CHAR(sport)  __raw_readl((sport)->port.membase + UTDR)
++
++#define UART_PUT_UTCR0(sport,v)       __raw_writel((v),(sport)->port.membase + UTCR0)
++#define UART_PUT_UTCR1(sport,v)       __raw_writel((v),(sport)->port.membase + UTCR1)
++#define UART_PUT_UTCR2(sport,v)       __raw_writel((v),(sport)->port.membase + UTCR2)
++#define UART_PUT_UTCR3(sport,v)       __raw_writel((v),(sport)->port.membase + UTCR3)
++#define UART_PUT_UTSR0(sport,v)       __raw_writel((v),(sport)->port.membase + UTSR0)
++#define UART_PUT_UTSR1(sport,v)       __raw_writel((v),(sport)->port.membase + UTSR1)
++#define UART_PUT_CHAR(sport,v)        __raw_writel((v),(sport)->port.membase + UTDR)
++
++/*
++ * This is the size of our serial port register set.
++ */
++#define UART_PORT_SIZE        0x24
++
++static struct tty_driver normal, callout;
++static struct tty_struct *sa1100_table[NR_PORTS];
++static struct termios *sa1100_termios[NR_PORTS], *sa1100_termios_locked[NR_PORTS];
++static int (*sa1100_open)(struct uart_port *);
++static void (*sa1100_close)(struct uart_port *);
++#ifdef SUPPORT_SYSRQ
++static struct console sa1100_console;
++#endif
++
++/*
++ * This determines how often we check the modem status signals
++ * for any change.  They generally aren't connected to an IRQ
++ * so we have to poll them.  We also check immediately before
++ * filling the TX fifo incase CTS has been dropped.
++ */
++#define MCTRL_TIMEOUT (250*HZ/1000)
++
++struct sa1100_port {
++      struct uart_port        port;
++      struct timer_list       timer;
++      unsigned int            old_status;
++};
++
++/*
++ * Handle any change of modem status signal since we were last called.
++ */
++static void sa1100_mctrl_check(struct sa1100_port *sport)
++{
++      unsigned int status, changed;
++
++      status = sport->port.ops->get_mctrl(&sport->port);
++      changed = status ^ sport->old_status;
++
++      if (changed == 0)
++              return;
++
++      sport->old_status = status;
++
++      if (changed & TIOCM_RI)
++              sport->port.icount.rng++;
++      if (changed & TIOCM_DSR)
++              sport->port.icount.dsr++;
++      if (changed & TIOCM_CAR)
++              uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
++      if (changed & TIOCM_CTS)
++              uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
++
++      wake_up_interruptible(&sport->port.info->delta_msr_wait);
++}
++
++/*
++ * This is our per-port timeout handler, for checking the
++ * modem status signals.
++ */
++static void sa1100_timeout(unsigned long data)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)data;
++      unsigned long flags;
++
++      if (sport->port.info) {
++              spin_lock_irqsave(&sport->port.lock, flags);
++              sa1100_mctrl_check(sport);
++              spin_unlock_irqrestore(&sport->port.lock, flags);
++
++              mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
++      }
++}
++
++/*
++ * interrupts disabled on entry
++ */
++static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      u32 utcr3;
++
++      utcr3 = UART_GET_UTCR3(sport);
++      UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
++      sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
++}
++
++/*
++ * interrupts may not be disabled on entry
++ */
++static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      unsigned long flags;
++      u32 utcr3;
++
++      spin_lock_irqsave(&sport->port.lock, flags);
++      utcr3 = UART_GET_UTCR3(sport);
++      sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
++      UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
++      spin_unlock_irqrestore(&sport->port.lock, flags);
++}
++
++/*
++ * Interrupts enabled
++ */
++static void sa1100_stop_rx(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      u32 utcr3;
++
++      utcr3 = UART_GET_UTCR3(sport);
++      UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE);
++}
++
++/*
++ * Set the modem control timer to fire immediately.
++ */
++static void sa1100_enable_ms(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      mod_timer(&sport->timer, jiffies);
++}
++
++static void
++sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs)
++{
++      struct tty_struct *tty = sport->port.info->tty;
++      unsigned int status, ch, flg, ignored = 0;
++
++      status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
++               UTSR0_TO_SM(UART_GET_UTSR0(sport));
++      while (status & UTSR1_TO_SM(UTSR1_RNE)) {
++              ch = UART_GET_CHAR(sport);
++
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              sport->port.icount.rx++;
++
++              flg = TTY_NORMAL;
++
++              /*
++               * note that the error handling code is
++               * out of the main execution path
++               */
++              if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR))
++                      goto handle_error;
++
++              if (uart_handle_sysrq_char(&sport->port, ch, regs))
++                      goto ignore_char;
++
++      error_return:
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++      ignore_char:
++              status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
++                       UTSR0_TO_SM(UART_GET_UTSR0(sport));
++      }
++ out:
++      tty_flip_buffer_push(tty);
++      return;
++
++ handle_error:
++      if (status & UTSR1_TO_SM(UTSR1_PRE))
++              sport->port.icount.parity++;
++      else if (status & UTSR1_TO_SM(UTSR1_FRE))
++              sport->port.icount.frame++;
++      if (status & UTSR1_TO_SM(UTSR1_ROR))
++              sport->port.icount.overrun++;
++
++      if (status & sport->port.ignore_status_mask) {
++              if (++ignored > 100)
++                      goto out;
++              goto ignore_char;
++      }
++
++      status &= sport->port.read_status_mask;
++
++      if (status & UTSR1_TO_SM(UTSR1_PRE))
++              flg = TTY_PARITY;
++      else if (status & UTSR1_TO_SM(UTSR1_FRE))
++              flg = TTY_FRAME;
++
++      if (status & UTSR1_TO_SM(UTSR1_ROR)) {
++              /*
++               * overrun does *not* affect the character
++               * we read from the FIFO
++               */
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              ch = 0;
++              flg = TTY_OVERRUN;
++      }
++#ifdef SUPPORT_SYSRQ
++      sport->port.sysrq = 0;
++#endif
++      goto error_return;
++}
++
++static void sa1100_tx_chars(struct sa1100_port *sport)
++{
++      struct circ_buf *xmit = &sport->port.info->xmit;
++
++      if (sport->port.x_char) {
++              UART_PUT_CHAR(sport, sport->port.x_char);
++              sport->port.icount.tx++;
++              sport->port.x_char = 0;
++              return;
++      }
++
++      /*
++       * Check the modem control lines before
++       * transmitting anything.
++       */
++      sa1100_mctrl_check(sport);
++
++      if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
++              sa1100_stop_tx(&sport->port, 0);
++              return;
++      }
++
++      /*
++       * Tried using FIFO (not checking TNF) for fifo fill:
++       * still had the '4 bytes repeated' problem.
++       */
++      while (UART_GET_UTSR1(sport) & UTSR1_TNF) {
++              UART_PUT_CHAR(sport, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              sport->port.icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      }
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(&sport->port);
++
++      if (uart_circ_empty(xmit))
++              sa1100_stop_tx(&sport->port, 0);
++}
++
++static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct sa1100_port *sport = dev_id;
++      unsigned int status, pass_counter = 0;
++
++      spin_lock(&sport->port.lock);
++      status = UART_GET_UTSR0(sport);
++      status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS;
++      do {
++              if (status & (UTSR0_RFS | UTSR0_RID)) {
++                      /* Clear the receiver idle bit, if set */
++                      if (status & UTSR0_RID)
++                              UART_PUT_UTSR0(sport, UTSR0_RID);
++                      sa1100_rx_chars(sport, regs);
++              }
++
++              /* Clear the relevant break bits */
++              if (status & (UTSR0_RBB | UTSR0_REB))
++                      UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB));
++
++              if (status & UTSR0_RBB)
++                      sport->port.icount.brk++;
++
++              if (status & UTSR0_REB)
++                      uart_handle_break(&sport->port);
++
++              if (status & UTSR0_TFS)
++                      sa1100_tx_chars(sport);
++              if (pass_counter++ > SA1100_ISR_PASS_LIMIT)
++                      break;
++              status = UART_GET_UTSR0(sport);
++              status &= SM_TO_UTSR0(sport->port.read_status_mask) |
++                        ~UTSR0_TFS;
++      } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));
++      spin_unlock(&sport->port.lock);
++}
++
++/*
++ * Return TIOCSER_TEMT when transmitter is not busy.
++ */
++static unsigned int sa1100_tx_empty(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT;
++}
++
++static unsigned int sa1100_get_mctrl(struct uart_port *port)
++{
++      return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
++}
++
++static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++}
++
++/*
++ * Interrupts always disabled.
++ */
++static void sa1100_break_ctl(struct uart_port *port, int break_state)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      unsigned long flags;
++      unsigned int utcr3;
++
++      spin_lock_irqsave(&sport->port.lock, flags);
++      utcr3 = UART_GET_UTCR3(sport);
++      if (break_state == -1)
++              utcr3 |= UTCR3_BRK;
++      else
++              utcr3 &= ~UTCR3_BRK;
++      UART_PUT_UTCR3(sport, utcr3);
++      spin_unlock_irqrestore(&sport->port.lock, flags);
++}
++
++static int sa1100_startup(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      int retval;
++
++      /*
++       * Allocate the IRQ
++       */
++      retval = request_irq(sport->port.irq, sa1100_int, 0,
++                           "sa11x0-uart", sport);
++      if (retval)
++              return retval;
++
++      /*
++       * If there is a specific "open" function
++       * (to register control line interrupts)
++       */
++      if (sa1100_open) {
++              retval = sa1100_open(port);
++              if (retval) {
++                      free_irq(sport->port.irq, sport);
++                      return retval;
++              }
++      }
++
++      /*
++       * Finally, clear and enable interrupts
++       */
++      UART_PUT_UTSR0(sport, -1);
++      UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE);
++
++      return 0;
++}
++
++static void sa1100_shutdown(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      /*
++       * Stop our timer.
++       */
++      del_timer_sync(&sport->timer);
++
++      /*
++       * Free the interrupt
++       */
++      free_irq(sport->port.irq, sport);
++
++      /*
++       * If there is a specific "close" function (to unregister
++       * control line interrupts)
++       */
++      if (sa1100_close)
++              sa1100_close(port);
++
++      /*
++       * Disable all interrupts, port and break condition.
++       */
++      UART_PUT_UTCR3(sport, 0);
++}
++
++static void sa1100_change_speed(struct uart_port *port, unsigned int cflag, unsigned int iflag, unsigned int quot)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      unsigned long flags;
++      unsigned int utcr0, old_utcr3;
++
++      if ((cflag & CSIZE) == CS8)
++              utcr0 = UTCR0_DSS;
++      else
++              utcr0 = 0;
++
++      if (cflag & CSTOPB)
++              utcr0 |= UTCR0_SBS;
++      if (cflag & PARENB) {
++              utcr0 |= UTCR0_PE;
++              if (!(cflag & PARODD))
++                      utcr0 |= UTCR0_OES;
++      }
++
++      spin_lock_irqsave(&sport->port.lock, flags);
++
++      sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS);
++      sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR);
++      if (iflag & INPCK)
++              sport->port.read_status_mask |=
++                              UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
++      if (iflag & (BRKINT | PARMRK))
++              sport->port.read_status_mask |=
++                              UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
++
++      /*
++       * Characters to ignore
++       */
++      sport->port.ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              sport->port.ignore_status_mask |=
++                              UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
++      if (iflag & IGNBRK) {
++              sport->port.ignore_status_mask |=
++                              UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns too (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      sport->port.ignore_status_mask |=
++                              UTSR1_TO_SM(UTSR1_ROR);
++      }
++
++      del_timer_sync(&sport->timer);
++
++      /*
++       * disable interrupts and drain transmitter
++       */
++      old_utcr3 = UART_GET_UTCR3(sport);
++      UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE));
++
++      while (UART_GET_UTSR1(sport) & UTSR1_TBY)
++              barrier();
++
++      /* then, disable everything */
++      UART_PUT_UTCR3(sport, 0);
++
++      /* set the parity, stop bits and data size */
++      UART_PUT_UTCR0(sport, utcr0);
++
++      /* set the baud rate */
++      quot -= 1;
++      UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8));
++      UART_PUT_UTCR2(sport, (quot & 0xff));
++
++      UART_PUT_UTSR0(sport, -1);
++
++      UART_PUT_UTCR3(sport, old_utcr3);
++
++      if (UART_ENABLE_MS(&sport->port, cflag))
++              sa1100_enable_ms(&sport->port);
++
++      spin_unlock_irqrestore(&sport->port.lock, flags);
++}
++
++static const char *sa1100_type(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      return sport->port.type == PORT_SA1100 ? "SA1100" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'.
++ */
++static void sa1100_release_port(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
++}
++
++/*
++ * Request the memory region(s) being used by 'port'.
++ */
++static int sa1100_request_port(struct uart_port *port)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
++                      "sa11x0-uart") != NULL ? 0 : -EBUSY;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void sa1100_config_port(struct uart_port *port, int flags)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++
++      if (flags & UART_CONFIG_TYPE &&
++          sa1100_request_port(&sport->port) == 0)
++              sport->port.type = PORT_SA1100;
++}
++
++/*
++ * Verify the new serial_struct (for TIOCSSERIAL).
++ * The only change we allow are to the flags and type, and
++ * even then only between PORT_SA1100 and PORT_UNKNOWN
++ */
++static int sa1100_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      struct sa1100_port *sport = (struct sa1100_port *)port;
++      int ret = 0;
++
++      if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
++              ret = -EINVAL;
++      if (sport->port.irq != ser->irq)
++              ret = -EINVAL;
++      if (ser->io_type != SERIAL_IO_MEM)
++              ret = -EINVAL;
++      if (sport->port.uartclk / 16 != ser->baud_base)
++              ret = -EINVAL;
++      if ((void *)sport->port.mapbase != ser->iomem_base)
++              ret = -EINVAL;
++      if (sport->port.iobase != ser->port)
++              ret = -EINVAL;
++      if (ser->hub6 != 0)
++              ret = -EINVAL;
++      return ret;
++}
++
++static struct uart_ops sa1100_pops = {
++      .tx_empty       = sa1100_tx_empty,
++      .set_mctrl      = sa1100_set_mctrl,
++      .get_mctrl      = sa1100_get_mctrl,
++      .stop_tx        = sa1100_stop_tx,
++      .start_tx       = sa1100_start_tx,
++      .stop_rx        = sa1100_stop_rx,
++      .enable_ms      = sa1100_enable_ms,
++      .break_ctl      = sa1100_break_ctl,
++      .startup        = sa1100_startup,
++      .shutdown       = sa1100_shutdown,
++      .change_speed   = sa1100_change_speed,
++      .type           = sa1100_type,
++      .release_port   = sa1100_release_port,
++      .request_port   = sa1100_request_port,
++      .config_port    = sa1100_config_port,
++      .verify_port    = sa1100_verify_port,
++};
++
++static struct sa1100_port sa1100_ports[NR_PORTS];
++
++/*
++ * Setup the SA1100 serial ports.  Note that we don't include the IrDA
++ * port here since we have our own SIR/FIR driver (see drivers/net/irda)
++ *
++ * Note also that we support "console=ttySAx" where "x" is either 0 or 1.
++ * Which serial port this ends up being depends on the machine you're
++ * running this kernel on.  I'm not convinced that this is a good idea,
++ * but that's the way it traditionally works.
++ *
++ * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer
++ * used here.
++ */
++static void __init sa1100_init_ports(void)
++{
++      static int first = 1;
++      int i;
++
++      if (!first)
++              return;
++      first = 0;
++
++      for (i = 0; i < NR_PORTS; i++) {
++              sa1100_ports[i].port.uartclk   = 3686400;
++              sa1100_ports[i].port.ops       = &sa1100_pops;
++              sa1100_ports[i].port.fifosize  = 8;
++              sa1100_ports[i].port.line      = i;
++              sa1100_ports[i].port.iotype    = SERIAL_IO_MEM;
++              init_timer(&sa1100_ports[i].timer);
++              sa1100_ports[i].timer.function = sa1100_timeout;
++              sa1100_ports[i].timer.data     = (unsigned long)&sa1100_ports[i];
++      }
++
++      /*
++       * make transmit lines outputs, so that when the port
++       * is closed, the output is in the MARK state.
++       */
++      PPDR |= PPC_TXD1 | PPC_TXD3;
++      PPSR |= PPC_TXD1 | PPC_TXD3;
++}
++
++void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns)
++{
++      if (fns->enable_ms)
++              sa1100_pops.enable_ms = fns->enable_ms;
++      if (fns->get_mctrl)
++              sa1100_pops.get_mctrl = fns->get_mctrl;
++      if (fns->set_mctrl)
++              sa1100_pops.set_mctrl = fns->set_mctrl;
++      sa1100_open          = fns->open;
++      sa1100_close         = fns->close;
++      sa1100_pops.pm       = fns->pm;
++      sa1100_pops.set_wake = fns->set_wake;
++}
++
++void __init sa1100_register_uart(int idx, int port)
++{
++      if (idx >= NR_PORTS) {
++              printk(KERN_ERR "%s: bad index number %d\n", __FUNCTION__, idx);
++              return;
++      }
++
++      switch (port) {
++      case 1:
++              sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0;
++              sa1100_ports[idx].port.mapbase = _Ser1UTCR0;
++              sa1100_ports[idx].port.irq     = IRQ_Ser1UART;
++              sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
++              break;
++
++      case 2:
++              sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0;
++              sa1100_ports[idx].port.mapbase = _Ser2UTCR0;
++              sa1100_ports[idx].port.irq     = IRQ_Ser2ICP;
++              sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
++              break;
++
++      case 3:
++              sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0;
++              sa1100_ports[idx].port.mapbase = _Ser3UTCR0;
++              sa1100_ports[idx].port.irq     = IRQ_Ser3UART;
++              sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
++              break;
++
++      default:
++              printk(KERN_ERR "%s: bad port number %d\n", __FUNCTION__, port);
++      }
++}
++
++
++#ifdef CONFIG_SERIAL_SA1100_CONSOLE
++
++/*
++ * Interrupts are disabled on entering
++ */
++static void
++sa1100_console_write(struct console *co, const char *s, unsigned int count)
++{
++      struct sa1100_port *sport = &sa1100_ports[co->index];
++      unsigned int old_utcr3, status, i;
++
++      /*
++       *      First, save UTCR3 and then disable interrupts
++       */
++      old_utcr3 = UART_GET_UTCR3(sport);
++      UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) |
++                              UTCR3_TXE);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = UART_GET_UTSR1(sport);
++              } while (!(status & UTSR1_TNF));
++              UART_PUT_CHAR(sport, s[i]);
++              if (s[i] == '\n') {
++                      do {
++                              status = UART_GET_UTSR1(sport);
++                      } while (!(status & UTSR1_TNF));
++                      UART_PUT_CHAR(sport, '\r');
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore UTCR3
++       */
++      do {
++              status = UART_GET_UTSR1(sport);
++      } while (status & UTSR1_TBY);
++      UART_PUT_UTCR3(sport, old_utcr3);
++}
++
++static kdev_t sa1100_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_SA1100_MAJOR, MINOR_START + co->index);
++}
++
++/*
++ * If the port was already initialised (eg, by a boot loader), try to determine
++ * the current setup.
++ */
++static void __init
++sa1100_console_get_options(struct sa1100_port *sport, int *baud,
++                         int *parity, int *bits)
++{
++      unsigned int utcr3;
++
++      utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE);
++      if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) {
++              /* ok, the port was enabled */
++              unsigned int utcr0, quot;
++
++              utcr0 = UART_GET_UTCR0(sport);
++
++              *parity = 'n';
++              if (utcr0 & UTCR0_PE) {
++                      if (utcr0 & UTCR0_OES)
++                              *parity = 'e';
++                      else
++                              *parity = 'o';
++              }
++
++              if (utcr0 & UTCR0_DSS)
++                      *bits = 8;
++              else
++                      *bits = 7;
++
++              quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8;
++              quot &= 0xfff;
++              *baud = sport->port.uartclk / (16 * (quot + 1));
++      }
++}
++
++static int __init
++sa1100_console_setup(struct console *co, char *options)
++{
++      struct sa1100_port *sport;
++      int baud = CONFIG_SA1100_DEFAULT_BAUDRATE;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      if (co->index == -1 || co->index >= NR_PORTS)
++              co->index = 0;
++      sport = &sa1100_ports[co->index];
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              sa1100_console_get_options(sport, &baud, &parity, &bits);
++
++      return uart_set_options(&sport->port, co, baud, parity, bits, flow);
++}
++
++static struct console sa1100_console = {
++      .name           = "ttySA",
++      .write          = sa1100_console_write,
++      .device         = sa1100_console_device,
++      .setup          = sa1100_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++};
++
++void __init sa1100_rs_console_init(void)
++{
++      sa1100_init_ports();
++      register_console(&sa1100_console);
++}
++
++#define SA1100_CONSOLE        &sa1100_console
++#else
++#define SA1100_CONSOLE        NULL
++#endif
++
++static struct uart_driver sa1100_reg = {
++      .owner                  = THIS_MODULE,
++      .normal_major           = SERIAL_SA1100_MAJOR,
++#ifdef CONFIG_DEVFS_FS
++      .normal_name            = "ttySA%d",
++      .callout_name           = "cusa%d",
++#else
++      .normal_name            = "ttySA",
++      .callout_name           = "cusa",
++#endif
++      .normal_driver          = &normal,
++      .callout_major          = CALLOUT_SA1100_MAJOR,
++      .callout_driver         = &callout,
++      .table                  = sa1100_table,
++      .termios                = sa1100_termios,
++      .termios_locked         = sa1100_termios_locked,
++      .minor                  = MINOR_START,
++      .nr                     = NR_PORTS,
++      .cons                   = SA1100_CONSOLE,
++};
++
++static int __init sa1100_serial_init(void)
++{
++      int i, ret;
++
++      sa1100_init_ports();
++
++      ret = uart_register_driver(&sa1100_reg);
++      if (ret)
++              return ret;
++
++      for (i = 0; i < NR_PORTS; i++)
++              uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
++
++      return 0;
++}
++
++static void __exit sa1100_serial_exit(void)
++{
++      int i;
++
++      for (i = 0; i < NR_PORTS; i++)
++              uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port);
++
++      uart_unregister_driver(&sa1100_reg);
++}
++
++module_init(sa1100_serial_init);
++module_exit(sa1100_serial_exit);
++
++EXPORT_NO_SYMBOLS;
++
++MODULE_AUTHOR("Deep Blue Solutions Ltd");
++MODULE_DESCRIPTION("SA1100 generic serial port driver");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/serial/uart00.c
+@@ -0,0 +1,903 @@
++/*
++ *  linux/drivers/serial/uart00.c
++ *
++ *  Driver for UART00 serial ports
++ *
++ *  Based on drivers/char/serial_amba.c, by ARM Limited & 
++ *                                          Deep Blue Solutions Ltd.
++ *  Copyright 2001 Altera Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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: uart00.c,v 1.3.2.5 2002/10/24 09:53:26 rmk Exp $
++ *
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/major.h>
++#include <linux/string.h>
++#include <linux/fcntl.h>
++#include <linux/ptrace.h>
++#include <linux/ioport.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/console.h>
++#include <linux/sysrq.h>
++#include <linux/pld/pld_hotswap.h>
++#include <linux/proc_fs.h>
++
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/bitops.h>
++#include <asm/sizes.h>
++
++#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
++#define SUPPORT_SYSRQ
++#endif
++
++#include <linux/serial_core.h>
++#include <asm/arch/excalibur.h>
++#define UART00_TYPE (volatile unsigned int*)
++#include <asm/arch/uart00.h>
++#include <asm/arch/int_ctrl00.h>
++
++#undef DEBUG
++#define UART_NR               2
++
++#define SERIAL_UART00_NAME    "ttyUA"
++#define SERIAL_UART00_MAJOR   204
++#define SERIAL_UART00_MINOR   16 /* Temporary - will change in future */
++#define SERIAL_UART00_NR      UART_NR
++#define UART_PORT_SIZE 0x50
++
++#define CALLOUT_UART00_NAME   "cuaua"
++#define CALLOUT_UART00_MAJOR  205
++#define CALLOUT_UART00_MINOR  16      /* Temporary - will change in future */
++#define CALLOUT_UART00_NR     UART_NR
++
++static struct tty_driver normal, callout;
++static struct tty_struct *uart00_table[UART_NR];
++static struct termios *uart00_termios[UART_NR], *uart00_termios_locked[UART_NR];
++static struct console uart00_console;
++static struct uart_driver uart00_reg;
++
++
++#define UART00_ISR_PASS_LIMIT 256
++
++/*
++ * Access macros for the UART00 UARTs
++ */
++#define UART_GET_INT_STATUS(p)        inl(UART_ISR((p)->membase))
++#define UART_PUT_IES(p, c)      outl(c,UART_IES((p)->membase))
++#define UART_GET_IES(p)         inl(UART_IES((p)->membase))
++#define UART_PUT_IEC(p, c)      outl(c,UART_IEC((p)->membase))
++#define UART_GET_IEC(p)         inl(UART_IEC((p)->membase))
++#define UART_PUT_CHAR(p, c)     outl(c,UART_TD((p)->membase))
++#define UART_GET_CHAR(p)        inl(UART_RD((p)->membase))
++#define UART_GET_RSR(p)         inl(UART_RSR((p)->membase))
++#define UART_GET_RDS(p)         inl(UART_RDS((p)->membase))
++#define UART_GET_MSR(p)         inl(UART_MSR((p)->membase))
++#define UART_GET_MCR(p)         inl(UART_MCR((p)->membase))
++#define UART_PUT_MCR(p, c)      outl(c,UART_MCR((p)->membase))
++#define UART_GET_MC(p)          inl(UART_MC((p)->membase))
++#define UART_PUT_MC(p, c)       outl(c,UART_MC((p)->membase))
++#define UART_GET_TSR(p)         inl(UART_TSR((p)->membase))
++#define UART_GET_DIV_HI(p)    inl(UART_DIV_HI((p)->membase))
++#define UART_PUT_DIV_HI(p,c)  outl(c,UART_DIV_HI((p)->membase))
++#define UART_GET_DIV_LO(p)    inl(UART_DIV_LO((p)->membase))
++#define UART_PUT_DIV_LO(p,c)  outl(c,UART_DIV_LO((p)->membase))
++#define UART_RX_DATA(s)               ((s) & UART_RSR_RX_LEVEL_MSK)
++#define UART_TX_READY(s)      (((s) & UART_TSR_TX_LEVEL_MSK) < 15)
++//#define UART_TX_EMPTY(p)    ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0)
++
++static void uart00_stop_tx(struct uart_port *port, u_int from_tty)
++{
++
++      UART_PUT_IEC(port, UART_IEC_TIE_MSK);
++}
++
++static void uart00_stop_rx(struct uart_port *port)
++{
++
++      UART_PUT_IEC(port, UART_IEC_RE_MSK);
++}
++
++static void uart00_enable_ms(struct uart_port *port)
++{
++
++      UART_PUT_IES(port, UART_IES_ME_MSK);
++}
++
++static void
++uart00_rx_chars(struct uart_port *port, struct pt_regs *regs)
++{
++      struct uart_info *info = port->info;
++      struct tty_struct *tty = info->tty;
++      unsigned int status, ch, rds, flg, ignored = 0;
++      
++
++      status = UART_GET_RSR(port);
++      while (UART_RX_DATA(status)) {
++
++              /* 
++               * We need to read rds before reading the 
++               * character from the fifo
++               */
++              rds = UART_GET_RDS(port);
++              ch = UART_GET_CHAR(port);
++              port->icount.rx++;
++
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++
++              flg = TTY_NORMAL;
++
++              /*
++               * Note that the error handling code is
++               * out of the main execution path
++               */
++              if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|UART_RDS_PE_MSK))
++                      goto handle_error;
++              if (uart_handle_sysrq_char(port, ch, regs))
++                      goto ignore_char;
++
++      error_return:
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++      ignore_char:
++              status = UART_GET_RSR(port);
++      }
++out:
++      tty_flip_buffer_push(tty);
++      return;
++
++handle_error:
++      if (rds & UART_RDS_BI_MSK) {
++              status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK);
++              port->icount.brk++;
++
++#ifdef SUPPORT_SYSRQ
++              if (uart_handle_break(port))
++                      goto ignore_char;
++#endif
++      } else if (rds & UART_RDS_PE_MSK)
++              port->icount.parity++;
++      else if (rds & UART_RDS_FE_MSK)
++              port->icount.frame++;
++      if (rds & UART_RDS_OE_MSK)
++              port->icount.overrun++;
++
++      if (rds & port->ignore_status_mask) {
++              if (++ignored > 100)
++                      goto out;
++              goto ignore_char;
++      }
++      rds &= port->read_status_mask;
++
++      if (rds & UART_RDS_BI_MSK)
++              flg = TTY_BREAK;
++      else if (rds & UART_RDS_PE_MSK)
++              flg = TTY_PARITY;
++      else if (rds & UART_RDS_FE_MSK)
++              flg = TTY_FRAME;
++
++      if (rds & UART_RDS_OE_MSK) {
++              /*
++               * CHECK: does overrun affect the current character?
++               * ASSUMPTION: it does not.
++               */
++              *tty->flip.flag_buf_ptr++ = flg;
++              *tty->flip.char_buf_ptr++ = ch;
++              tty->flip.count++;
++              if (tty->flip.count >= TTY_FLIPBUF_SIZE)
++                      goto ignore_char;
++              ch = 0;
++              flg = TTY_OVERRUN;
++      }
++#ifdef SUPPORT_SYSRQ
++      port->sysrq = 0;
++#endif
++      goto error_return;
++}
++
++static void uart00_tx_chars(struct uart_port *port)
++{
++      int count;
++      struct uart_info *info = port->info;
++
++      if (port->x_char) {
++              while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15);
++              UART_PUT_CHAR(port, port->x_char);
++              port->icount.tx++;
++              port->x_char = 0;
++              
++              return;
++      }
++      if (info->xmit.head == info->xmit.tail
++          || info->tty->stopped
++          || info->tty->hw_stopped) {
++              uart00_stop_tx(port, 0);
++              return;
++      }
++
++      count = port->fifosize >> 1;
++      do {
++              while((UART_GET_TSR(port)& UART_TSR_TX_LEVEL_MSK)==15);
++              UART_PUT_CHAR(port, info->xmit.buf[info->xmit.tail]);
++              info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (info->xmit.head == info->xmit.tail)
++                      break;
++      } while (--count > 0);
++
++      if (CIRC_CNT(info->xmit.head,
++                   info->xmit.tail,
++                   UART_XMIT_SIZE) < WAKEUP_CHARS)
++              uart_write_wakeup(port);
++
++      if (info->xmit.head == info->xmit.tail)
++              uart00_stop_tx(port, 0);
++}
++
++static void uart00_start_tx(struct uart_port *port, u_int from_tty)
++{
++      UART_PUT_IES(port,UART_IES_TIE_MSK );           
++      uart00_tx_chars(port);
++}
++
++static void uart00_modem_status(struct uart_port *port)
++{
++      unsigned int status;
++      struct uart_icount *icount = &port->icount;
++      struct uart_info *info = port->info;
++
++      status = UART_GET_MSR(port);
++
++      if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | 
++                     UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK))
++              return;
++
++      if (status & UART_MSR_DDCD_MSK) {
++              icount->dcd++;
++#ifdef CONFIG_HARD_PPS
++              if ((port->flags & ASYNC_HARDPPS_CD) &&
++                  (status & UART_MSR_DCD_MSK))
++                      hardpps();
++#endif
++              if (info->flags & ASYNC_CHECK_CD) {
++                      if (status & UART_MSR_DCD_MSK)
++                              wake_up_interruptible(&info->open_wait);
++                      else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
++                                 (port->flags & ASYNC_CALLOUT_NOHUP))) {
++                              if (info->tty)
++                                      tty_hangup(info->tty);
++                      }
++              }
++      }
++
++      if (status & UART_MSR_DDSR_MSK)
++              icount->dsr++;
++
++      if (status & UART_MSR_DCTS_MSK) {
++              icount->cts++;
++
++              if (info->flags & ASYNC_CTS_FLOW) {
++                      status &= UART_MSR_CTS_MSK;
++
++                      if (info->tty->hw_stopped) {
++                              if (status) {
++                                      info->tty->hw_stopped = 0;
++                                      port->ops->start_tx(port, 0);
++                                      uart_write_wakeup(port);
++                              }
++                      } else {
++                              if (!status) {
++                                      info->tty->hw_stopped = 1;
++                                      port->ops->stop_tx(port, 0);
++                              }
++                      }
++              }
++      }
++      wake_up_interruptible(&info->delta_msr_wait);
++
++}
++
++static void uart00_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      unsigned int status, pass_counter = 0;
++
++      status = UART_GET_INT_STATUS(port);
++      do {
++
++              if (status & UART_ISR_RI_MSK)
++                      uart00_rx_chars(port, regs);
++              if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK))
++                      uart00_tx_chars(port);
++              if (status & UART_ISR_MI_MSK)
++                      uart00_modem_status(port);
++              if (pass_counter++ > UART00_ISR_PASS_LIMIT)
++                      break;
++
++              status = UART_GET_INT_STATUS(port);
++      } while (status);
++}
++
++static u_int uart00_tx_empty(struct uart_port *port)
++{
++      return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT;
++}
++
++static u_int uart00_get_mctrl(struct uart_port *port)
++{
++      unsigned int result = 0;
++      unsigned int status;
++
++      status = UART_GET_MSR(port);
++      if (status & UART_MSR_DCD_MSK)
++              result |= TIOCM_CAR;
++      if (status & UART_MSR_DSR_MSK)
++              result |= TIOCM_DSR;
++      if (status & UART_MSR_CTS_MSK)
++              result |= TIOCM_CTS;
++      if (status & UART_MCR_RI_MSK)
++              result |= TIOCM_RNG;
++
++      return result;
++}
++
++static void uart00_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++      unsigned char mcr = 0;
++
++      if (mctrl & TIOCM_RTS)
++              mcr |= UART_MCR_RTS_MSK;
++      if (mctrl & TIOCM_DTR)
++              mcr |= UART_MCR_DTR_MSK;
++      if (mctrl & TIOCM_LOOP)
++              mcr |= UART_MCR_LB_MSK;
++
++      UART_PUT_MCR(port, mcr);
++}
++
++static void uart00_break_ctl(struct uart_port *port, int break_state)
++{
++      unsigned int mcr;
++
++      mcr = UART_GET_MCR(port);
++      if (break_state == -1)
++              mcr |= UART_MCR_BR_MSK;
++      else
++              mcr &= ~UART_MCR_BR_MSK;
++      UART_PUT_MCR(port, mcr);
++}
++
++static inline u_int uart_calculate_quot(struct uart_port *port, u_int baud)
++{
++      u_int quot;
++
++      /* Special case: B0 rate */
++      if (!baud)
++              baud = 9600;
++
++      quot = (port->uartclk / (16 * baud)-1)  ;
++
++      return quot;
++}
++static void uart00_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)
++{
++      u_int uart_mc=0, old_ies;
++      unsigned long flags;
++
++#ifdef DEBUG
++      printk("uart00_set_cflag(0x%x) called\n", cflag);
++#endif
++      /* byte size and parity */
++      switch (cflag & CSIZE) {
++      case CS5: uart_mc = UART_MC_CLS_CHARLEN_5; break;
++      case CS6: uart_mc = UART_MC_CLS_CHARLEN_6; break;
++      case CS7: uart_mc = UART_MC_CLS_CHARLEN_7; break;
++      default:  uart_mc = UART_MC_CLS_CHARLEN_8; break; // CS8
++      }
++      if (cflag & CSTOPB)
++              uart_mc|= UART_MC_ST_TWO;
++      if (cflag & PARENB) {
++              uart_mc |= UART_MC_PE_MSK;
++              if (!(cflag & PARODD))
++                      uart_mc |= UART_MC_EP_MSK;
++      }
++
++      port->read_status_mask = UART_RDS_OE_MSK;
++      if (iflag & INPCK)
++              port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
++      if (iflag & (BRKINT | PARMRK))
++              port->read_status_mask |= UART_RDS_BI_MSK;
++
++      /*
++       * Characters to ignore
++       */
++      port->ignore_status_mask = 0;
++      if (iflag & IGNPAR)
++              port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
++      if (iflag & IGNBRK) {
++              port->ignore_status_mask |= UART_RDS_BI_MSK;
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns to (for real raw support).
++               */
++              if (iflag & IGNPAR)
++                      port->ignore_status_mask |= UART_RDS_OE_MSK;
++      }
++
++      /* first, disable everything */
++      save_flags(flags); cli();
++      old_ies = UART_GET_IES(port); 
++
++      if ((port->flags & ASYNC_HARDPPS_CD) ||
++          (cflag & CRTSCTS) || !(cflag & CLOCAL))
++              old_ies |= UART_IES_ME_MSK;
++
++
++      /* Set baud rate */
++      UART_PUT_DIV_LO(port, (quot & 0xff));
++      UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8));
++   
++
++      UART_PUT_MC(port, uart_mc);
++      UART_PUT_IES(port, old_ies);
++
++      restore_flags(flags);
++}
++
++static int uart00_startup(struct uart_port *port)
++{
++      int retval;
++
++      /*
++       * Allocate the IRQ
++       */
++      retval = request_irq(port->irq, uart00_int, 0, "uart00", port);
++      if (retval)
++              return retval;
++
++      /*
++       * Finally, enable interrupts. Use the TII interrupt to minimise 
++       * the number of interrupts generated. If higher performance is 
++       * needed, consider using the TI interrupt with a suitable FIFO
++       * threshold
++       */
++      UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK);
++
++      return 0;
++}
++
++static void uart00_shutdown(struct uart_port *port)
++{
++      /*
++       * disable all interrupts, disable the port
++       */
++      UART_PUT_IEC(port, 0xff);
++
++      /* disable break condition and fifos */
++      UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK);
++
++      /*
++       * Free the interrupt
++       */
++      free_irq(port->irq, port);
++}
++
++static const char *uart00_type(struct uart_port *port)
++{
++      return port->type == PORT_UART00 ? "Altera UART00" : NULL;
++}
++
++/*
++ * Release the memory region(s) being used by 'port'
++ */
++static void uart00_release_port(struct uart_port *port)
++{
++      release_mem_region(port->mapbase, UART_PORT_SIZE);
++
++#ifdef CONFIG_ARCH_CAMELOT
++      if(port->membase!=(void*)IO_ADDRESS(EXC_UART00_BASE)){
++              iounmap(port->membase);
++      }
++#endif
++}
++
++/*
++ * Request the memory region(s) being used by 'port'
++ */
++static int uart00_request_port(struct uart_port *port)
++{
++      int result;
++
++      result = request_mem_region(port->mapbase, UART_PORT_SIZE,
++                                  "serial_uart00") != NULL ? 0 : -EBUSY;
++      if (result)
++              return result;
++
++      port->membase = ioremap(port->mapbase, SZ_4K);
++      if (!port->membase) {
++              printk(KERN_ERR "serial00: cannot map io memory\n");
++              release_mem_region(port->mapbase, UART_PORT_SIZE);
++      }
++
++      return port->membase ? 0 : -ENOMEM;
++}
++
++/*
++ * Configure/autoconfigure the port.
++ */
++static void uart00_config_port(struct uart_port *port, int flags)
++{
++      if (flags & UART_CONFIG_TYPE) {
++              if (uart00_request_port(port) == 0)
++                      port->type = PORT_UART00;
++      }
++}
++
++/*
++ * verify the new serial_struct (for TIOCSSERIAL).
++ */
++static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser)
++{
++      int ret = 0;
++      if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00)
++              ret = -EINVAL;
++      if (ser->irq < 0 || ser->irq >= NR_IRQS)
++              ret = -EINVAL;
++      if (ser->baud_base < 9600)
++              ret = -EINVAL;
++      return ret;
++}
++
++static struct uart_ops uart00_pops = {
++      tx_empty:       uart00_tx_empty,
++      set_mctrl:      uart00_set_mctrl,
++      get_mctrl:      uart00_get_mctrl,
++      stop_tx:        uart00_stop_tx,
++      start_tx:       uart00_start_tx,
++      stop_rx:        uart00_stop_rx,
++      enable_ms:      uart00_enable_ms,
++      break_ctl:      uart00_break_ctl,
++      startup:        uart00_startup,
++      shutdown:       uart00_shutdown,
++      change_speed:   uart00_change_speed,
++      type:           uart00_type,
++      release_port:   uart00_release_port,
++      request_port:   uart00_request_port,
++      config_port:    uart00_config_port,
++      verify_port:    uart00_verify_port,
++};
++
++static struct uart_port uart00_ports[UART_NR] = {
++
++#ifdef CONFIG_ARCH_CAMELOT
++{
++      .membase        = (void*)IO_ADDRESS(EXC_UART00_BASE),
++      .mapbase        =  EXC_UART00_BASE,
++      .iotype         = SERIAL_IO_MEM,
++      .irq            = IRQ_UART,
++      .uartclk        = EXC_AHB2_CLK_FREQUENCY,
++      .fifosize       = 16,
++      .ops            = &uart00_pops,
++      .flags          = ASYNC_BOOT_AUTOCONF,
++}
++#endif
++};
++
++#ifdef CONFIG_SERIAL_UART00_CONSOLE
++static void uart00_console_write(struct console *co, const char *s, unsigned count)
++{
++#ifdef CONFIG_ARCH_CAMELOT
++      struct uart_port *port = &uart00_ports[0];
++      unsigned int status, old_ies;
++      int i;
++
++      /*
++       *      First save the CR then disable the interrupts
++       */
++      old_ies = UART_GET_IES(port);
++      UART_PUT_IEC(port,0xff);
++
++      /*
++       *      Now, do each character
++       */
++      for (i = 0; i < count; i++) {
++              do {
++                      status = UART_GET_TSR(port);
++              } while (!UART_TX_READY(status));
++              UART_PUT_CHAR(port, s[i]);
++              if (s[i] == '\n') {
++                      do {
++                              status = UART_GET_TSR(port);
++                      } while (!UART_TX_READY(status));
++                      UART_PUT_CHAR(port, '\r');
++              }
++      }
++
++      /*
++       *      Finally, wait for transmitter to become empty
++       *      and restore the IES
++       */
++      do {
++              status = UART_GET_TSR(port);
++      } while (status & UART_TSR_TX_LEVEL_MSK);
++      UART_PUT_IES(port, old_ies);
++#endif
++}
++
++static kdev_t uart00_console_device(struct console *co)
++{
++      return MKDEV(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index);
++}
++
++static void /*__init*/ uart00_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
++{
++      u_int uart_mc, quot;
++      uart_mc= UART_GET_MC(port);
++
++      *parity = 'n';
++      if (uart_mc & UART_MC_PE_MSK) {
++              if (uart_mc & UART_MC_EP_MSK)
++                      *parity = 'e';
++              else
++                      *parity = 'o';
++      }
++
++      switch (uart_mc & UART_MC_CLS_MSK){
++
++      case UART_MC_CLS_CHARLEN_5:
++              *bits = 5;
++              break;
++      case UART_MC_CLS_CHARLEN_6:
++              *bits = 6;
++              break;
++      case UART_MC_CLS_CHARLEN_7:
++              *bits = 7;
++              break;
++      case UART_MC_CLS_CHARLEN_8:
++              *bits = 8;
++              break;
++      }
++      quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8);
++      *baud = port->uartclk / (16 *quot );
++}
++
++static int __init uart00_console_setup(struct console *co, char *options)
++{
++      struct uart_port *port;
++      int baud = 38400;
++      int bits = 8;
++      int parity = 'n';
++      int flow= 'n';
++
++#ifdef CONFIG_ARCH_CAMELOT
++      /*
++       * Check whether an invalid uart number has been specified, and
++       * if so, search for the first available port that does have
++       * console support.
++       */
++      port = &uart00_ports[0];
++      co->index = 0;
++#else
++      return -ENODEV;
++#endif
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++      else
++              uart00_console_get_options(port, &baud, &parity, &bits);
++
++      return uart_set_options(port, co, baud, parity, bits, flow);
++}
++
++static struct console uart00_console = {
++      .name   = SERIAL_UART00_NAME,
++      .write  = uart00_console_write,
++      .device = uart00_console_device,
++      .setup  = uart00_console_setup,
++      .flags  = CON_PRINTBUFFER,
++      .index  = 0,
++};
++
++void __init uart00_console_init(void)
++{
++      register_console(&uart00_console);
++}
++
++#define UART00_CONSOLE        &uart00_console
++#else
++#define UART00_CONSOLE        NULL
++#endif
++
++static struct uart_driver uart00_reg = {
++      .owner          = NULL,
++      .normal_major   = SERIAL_UART00_MAJOR,
++      .normal_name    = SERIAL_UART00_NAME,
++      .normal_driver  = &normal,
++      .callout_major  = CALLOUT_UART00_MAJOR,
++      .callout_name   = CALLOUT_UART00_NAME,
++      .callout_driver = &callout,
++      .table          = uart00_table,
++      .termios        = uart00_termios,
++      .termios_locked = uart00_termios_locked,
++      .minor          = SERIAL_UART00_MINOR,
++      .nr             = UART_NR,
++      .state          = NULL,
++      .cons           = UART00_CONSOLE,
++};
++
++struct dev_port_entry{
++      struct uart_port *port;
++};
++
++static struct dev_port_entry dev_port_map[UART_NR];
++
++#ifdef CONFIG_PLD_HOTSWAP
++/*
++ * Keep a mapping of dev_info addresses -> port lines to use when
++ * removing ports dev==NULL indicates unused entry
++ */
++
++struct uart00_ps_data{
++      unsigned int clk;
++      unsigned int fifosize;
++};
++
++int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data)
++{
++      struct uart00_ps_data* dev_ps=dev_ps_data;
++      struct uart_port * port;
++      int i,result;
++
++      i=0;
++      while(dev_port_map[i].port)
++              i++;
++
++      if(i==UART_NR){
++              printk(KERN_WARNING "uart00: Maximum number of ports reached\n");
++              return 0;
++      }
++
++      port=&uart00_ports[i];
++
++      printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize);
++      port->membase=0;
++      port->mapbase=dev_info->base_addr;
++      port->iotype=SERIAL_IO_MEM;
++      port->irq=dev_info->irq;
++      port->uartclk=dev_ps->clk;
++      port->fifosize=dev_ps->fifosize;
++      port->ops=&uart00_pops;
++      port->line=i;
++      port->flags=ASYNC_BOOT_AUTOCONF;
++
++      result=uart_register_port(&uart00_reg, port);
++      if(result<0){
++              printk("uart_register_port returned %d\n",result);
++              return result;
++      }
++      dev_port_map[i].port=port;
++      printk("uart00: added device at %lx as ttyUA%d\n",dev_port_map[i].port->mapbase,i);
++      return 0;
++
++}
++
++int uart00_remove_devices(void)
++{
++      int i,result;
++
++
++      result=0;
++      for(i=1;i<UART_NR;i++){
++              if(dev_port_map[i].port){
++                      uart_unregister_port(&uart00_reg,i);
++                      dev_port_map[i].port=NULL;
++              }
++      }
++      return 0;
++
++}
++
++#ifdef CONFIG_PROC_FS
++
++
++int uart00_proc_read(char* buf,char** start,off_t offset,int count,int *eof,void *data){
++
++      int i,len=0;
++      struct uart_port *port;
++      int limit = count - 80;
++      char ps_data[80];
++      if(*start)
++              buf=*start;
++      for(i=0;(i<UART_NR)&&(len<limit);i++){
++              if(dev_port_map[i].port){
++                      port=dev_port_map[i].port;
++                      sprintf(ps_data,"clk, %dHz, fifo size, %dbytes",
++                              port->uartclk,port->fifosize);
++                      len+=PLDHS_READ_PROC_DATA(buf+len,"uart00",i,
++                                       port->mapbase,port->irq,ps_data);
++
++              }
++      }
++      *eof=1;
++      return len;
++}
++
++
++
++#endif
++struct pld_hotswap_ops uart00_pldhs_ops={
++      .name           = "uart00",
++      .add_device     = uart00_add_device,
++      .remove_devices = uart00_remove_devices,
++      .proc_read      = uart00_proc_read
++};
++
++#endif
++
++static int __init uart00_init(void)
++{
++      int ret;
++      int i;
++
++      ret = uart_register_driver(&uart00_reg);
++      if (ret) {
++              printk(KERN_ERR "uart00: Couldn't register driver\n");
++              return ret;
++      }
++
++      unregister_console(&uart00_console);
++
++      for(i=0;i<UART_NR;i++){
++              uart00_ports[i].ops=&uart00_pops;
++      }
++
++      printk(KERN_WARNING "uart00:Using temporary major/minor pairs - these WILL change in the future\n");
++
++#ifdef CONFIG_PLD_HOTSWAP
++      pldhs_register_driver(&uart00_pldhs_ops);
++#endif
++      for (i=0; i<UART_NR; i++)
++              uart_add_one_port(&uart00_reg,&uart00_ports[i]);
++
++      uart00_console.flags = 0;
++      register_console(&uart00_console);
++#ifdef CONFIG_ARCH_CAMELOT
++      dev_port_map[0].port=uart00_ports;
++#endif
++      return ret;
++}
++
++
++__initcall(uart00_init);
++
++
+--- linux-2.4.27/drivers/sound/.version
++++ /dev/null
+@@ -1,2 +0,0 @@
+-3.8s
+-0x030804
+--- linux-2.4.27/drivers/sound/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/sound/Config.in
+@@ -131,6 +131,17 @@
+ dep_tristate '  VIA 82C686 Audio Codec' CONFIG_SOUND_VIA82CXXX $CONFIG_PCI
+ dep_mbool    '  VIA 82C686 MIDI' CONFIG_MIDI_VIA82CXXX $CONFIG_SOUND_VIA82CXXX
++if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
++   dep_tristate '  StrongARM-11x0 Sound Drivers' CONFIG_SOUND_SA1100 $CONFIG_ARCH_SA1100 $CONFIG_SOUND
++   dep_tristate '    UDA1341 Stereo Codec' CONFIG_SOUND_UDA1341 $CONFIG_L3 $CONFIG_SOUND_SA1100 $CONFIG_SOUND
++   dep_tristate '      Assabet audio support' CONFIG_SOUND_ASSABET_UDA1341 $CONFIG_SA1100_ASSABET $CONFIG_SOUND_UDA1341
++   dep_tristate '      Compaq iPAQ audio support' CONFIG_SOUND_H3600_UDA1341 $CONFIG_SA1100_H3600 $CONFIG_SOUND_UDA1341
++   dep_tristate '      Pangolin audio support' CONFIG_SOUND_PANGOLIN_UDA1341 $CONFIG_SA1100_PANGOLIN $CONFIG_SOUND_UDA1341
++   dep_tristate '      SA1111 audio support' CONFIG_SOUND_SA1111_UDA1341 $CONFIG_SA1111 $CONFIG_SOUND_UDA1341
++   dep_tristate '    SA1111 AC97 Sound' CONFIG_SOUND_SA1111_AC97 $CONFIG_SA1111 $CONFIG_SOUND_SA1100
++   dep_tristate '    Generic DAC on the SA11x0 SSP port' CONFIG_SOUND_SA1100SSP $CONFIG_SOUND_SA1100
++fi
++
+ dep_tristate '  OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
+ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
+@@ -220,14 +231,14 @@
+          bool '      Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+       fi
+    fi
++       
++fi
+-   if [ "$CONFIG_ARM" = "y" ]; then
+-      if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" ]; then
+-         dep_tristate '    VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS
+-      fi
+-      dep_tristate '    Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER
++if [ "$CONFIG_ARM" = "y" ]; then
++   if [ "$CONFIG_ARCH_ACORN" = "y" -o "$CONFIG_ARCH_CLPS7500" = "y" -o "$CONFIG_ARCH_RISCSTATION" ]; then
++      dep_tristate '    VIDC 16-bit sound' CONFIG_SOUND_VIDC $CONFIG_SOUND_OSS
+    fi
+-
++   dep_tristate '    Netwinder WaveArtist' CONFIG_SOUND_WAVEARTIST $CONFIG_SOUND_OSS $CONFIG_ARCH_NETWINDER
+ fi
+ dep_tristate '  TV card (bt848) mixer support' CONFIG_SOUND_TVMIXER $CONFIG_SOUND $CONFIG_I2C
+--- linux-2.4.27/drivers/sound/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/sound/Makefile
+@@ -10,7 +10,8 @@
+ export-objs   :=  ad1848.o audio_syms.o midi_syms.o mpu401.o \
+                   msnd.o opl3.o sb_common.o sequencer_syms.o \
+                   sound_core.o sound_syms.o uart401.o \
+-                  nm256_audio.o ac97.o ac97_codec.o aci.o
++                  nm256_audio.o ac97.o ac97_codec.o aci.o \
++                  sa1100-audio.o
+ # Each configuration option enables a list of files.
+@@ -76,6 +77,14 @@
+ obj-$(CONFIG_SOUND_FORTE)     += forte.o ac97_codec.o
+ obj-$(CONFIG_SOUND_TRIDENT)   += trident.o ac97_codec.o
+ obj-$(CONFIG_SOUND_HARMONY)   += harmony.o
++obj-$(CONFIG_SOUND_SA1100)    += sa1100-audio.o
++obj-$(CONFIG_SOUND_UDA1341)   += uda1341.o
++obj-$(CONFIG_SOUND_ASSABET_UDA1341) += assabet-uda1341.o
++obj-$(CONFIG_SOUND_PANGOLIN_UDA1341) += pangolin-uda1341.o
++obj-$(CONFIG_SOUND_H3600_UDA1341) += h3600-uda1341.o
++obj-$(CONFIG_SOUND_SA1111_UDA1341) += sa1111-uda1341.o
++obj-$(CONFIG_SOUND_SA1111_AC97)       += sa1111-ac97.o ac97_codec.o
++obj-$(CONFIG_SOUND_SA1100SSP) += sa1100ssp.o
+ obj-$(CONFIG_SOUND_EMU10K1)   += ac97_codec.o
+ obj-$(CONFIG_SOUND_BCM_CS4297A)       += swarm_cs4297a.o
+ obj-$(CONFIG_SOUND_RME96XX)     += rme96xx.o
+@@ -105,7 +114,6 @@
+   obj-y += dmasound/dmasound.o
+ endif
+-
+ # Declare multi-part drivers.
+ list-multi    := sound.o gus.o pas2.o sb.o sb_lib.o vidc_mod.o \
+--- /dev/null
++++ linux-2.4.27/drivers/sound/assabet-uda1341.c
+@@ -0,0 +1,404 @@
++/*
++ * Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * This is the machine specific part of the Assabet/UDA1341 support.
++ * This driver makes use of the UDA1341 and the sa1100-audio modules.
++ *
++ * History:
++ *
++ * 2000-05-21 Nicolas Pitre   Initial release.
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-07-17 Nicolas Pitre   Supports 44100Hz and 22050Hz samplerate now.
++ *
++ * 2001-08-03 Russell King    Fix left/right channel swap.
++ *                            Attempt to reduce power consumption when idle.
++ *
++ * 2001-09-23 Russell King    Remove old L3 bus driver.
++ *
++ * Please note that fiddling too much with MDREFR results in oopses, so we don't
++ * touch MDREFR unnecessarily, which means we don't touch it on close.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/cpufreq.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/arch/assabet.h>
++
++#include "sa1100-audio.h"
++
++/*
++ * Define this to fix the power drain on early Assabets
++ */
++#define FIX_POWER_DRAIN
++
++/*
++ * Debugging?
++ */
++#undef DEBUG
++
++
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++#define AUDIO_RATE_DEFAULT    44100
++
++/*
++ * Mixer (UDA1341) interface
++ */
++
++static struct l3_client uda1341;
++
++static int
++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      /*
++       * We only accept mixer (type 'M') ioctls.
++       */
++      if (_IOC_TYPE(cmd) != 'M')
++              return -EINVAL;
++
++      return l3_command(&uda1341, cmd, (void *)arg);
++}
++
++static struct file_operations assabet_mixer_fops = {
++      ioctl:          mixer_ioctl,
++      owner:          THIS_MODULE
++};
++
++
++/*
++ * Audio interface
++ */
++static long audio_samplerate = AUDIO_RATE_DEFAULT;
++
++/*
++ * FIXME: what about SFRM going high when SSP is disabled?
++ */
++static void assabet_set_samplerate(long val)
++{
++      struct uda1341_cfg cfg;
++      u_int clk_ref, clk_div;
++
++      /* We don't want to mess with clocks when frames are in flight */
++      Ser4SSCR0 &= ~SSCR0_SSE;
++      /* wait for any frame to complete */
++      udelay(125);
++
++      /*
++       * Our clock source is derived from the CPLD on which we don't have
++       * much control unfortunately.  This was intended for a fixed 48000 Hz
++       * samplerate assuming a core clock of 221.2 MHz.  The CPLD appears
++       * to divide the memory clock so there is a ratio of 4608 between
++       * the core clock and the resulting samplerate (obtained by
++       * measurements, the CPLD equations should confirm that).
++       *
++       * Still we can play with the SA1110's clock divisor for the SSP port
++       * to get half the samplerate as well.
++       *
++       * Apparently the clock sent to the SA1110 for the SSP port is further
++       * more divided from the clock sent to the UDA1341 (some people tried
++       * to be too clever in their design, or simply failed to read the
++       * SA1110 manual).  If it was the same clock we would have been able
++       * to support a third samplerate with the UDA1341's 384FS mode.
++       *
++       * At least it would have been a minimum acceptable solution to be
++       * able to set the CPLD divisor by software.  The iPAQ design is
++       * certainly a better example to follow for a new design.
++       */
++      clk_ref = cpufreq_get(0) * 1000 / 4608;
++      if (val > clk_ref*4/7) {
++              audio_samplerate = clk_ref;
++              cfg.fs = 256;
++              clk_div = SSCR0_SerClkDiv(2);
++      } else {
++              audio_samplerate = clk_ref/2;
++              cfg.fs = 512;
++              clk_div = SSCR0_SerClkDiv(4);
++      }
++
++      cfg.format = FMT_LSB16;
++      l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
++
++      Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
++}
++
++/*
++ * Initialise the Assabet audio driver.
++ *
++ * Note that we have to be careful with the order that we do things here;
++ * there is a D-type flip flop which is clocked from the SFRM line which
++ * indicates whether the same is for the left or right channel to the
++ * UDA1341.
++ *
++ * When you disable the SSP (by clearing SSCR0_SSE) it appears that the
++ * SFRM signal can float high.  When you re-enable the SSP, you clock the
++ * flip flop once, and end up swapping the left and right channels.
++ *
++ * The ASSABET_BCR_CODEC_RST line will force this flip flop into a known
++ * state, but this line resets other devices as well!
++ *
++ * In addition to the above, it appears that powering down the UDA1341 on
++ * early Assabets leaves the UDA_WS actively driving a logic '1' into the
++ * chip, wasting power!  (you can tell this by D11 being half-on).  We
++ * attempt to correct this by kicking the flip flop on init/open/close.
++ * We should probably do this on PM resume as well.
++ *
++ * (Note the ordering of ASSABET_BCR_AUDIO_ON, SFRM and ASSABET_BCR_CODEC_RST
++ * is important).
++ */
++static void assabet_audio_init(void *dummy)
++{
++      unsigned long flags;
++      unsigned int mdrefr;
++
++      local_irq_save(flags);
++
++      /*
++       * Enable the power for the UDA1341 before driving any signals.
++       * We leave the audio amp (LM4880) disabled for now.
++       */
++      ASSABET_BCR_set(ASSABET_BCR_AUDIO_ON);
++
++#ifdef FIX_POWER_DRAIN
++      GPSR = GPIO_SSP_SFRM;
++      GPCR = GPIO_SSP_SFRM;
++#endif
++
++      ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
++      ASSABET_BCR_clear(ASSABET_BCR_STEREO_LB);
++
++      /*
++       * Setup the SSP uart.
++       */
++      PPAR |= PPAR_SPR;
++      Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2);
++      Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
++      GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK;
++      GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;
++      GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK);
++      Ser4SSCR0 |= SSCR0_SSE;
++
++      /*
++       * Only give SFRM to the SSP after it has been enabled.
++       */
++      GAFR |= GPIO_SSP_SFRM;
++
++      /*
++       * The assabet board uses the SDRAM clock as the source clock for
++       * audio. This is supplied to the SA11x0 from the CPLD on pin 19.
++       * At 206MHz we need to run the audio clock (SDRAM bank 2)
++       * at half speed. This clock will scale with core frequency so
++       * the audio sample rate will also scale. The CPLD on Assabet
++       * will need to be programmed to match the core frequency.
++       */
++      mdrefr = MDREFR;
++      if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD |
++                     MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) {
++              mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN;
++              mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD);
++              MDREFR = mdrefr;
++              (void) MDREFR;
++      }
++      local_irq_restore(flags);
++
++      /* Wait for the UDA1341 to wake up */
++      mdelay(1);
++
++      l3_open(&uda1341);
++
++      assabet_set_samplerate(audio_samplerate);
++
++      /* Enable the audio power */
++      ASSABET_BCR_clear(ASSABET_BCR_QMUTE | ASSABET_BCR_SPK_OFF);
++}
++
++/*
++ * Shutdown the Assabet audio driver.
++ *
++ * We have to be careful about the SFRM line here for the same reasons
++ * described in the initialisation comments above.  This basically means
++ * that we must hand the SSP pins back to the GPIO module before disabling
++ * the SSP.
++ *
++ * In addition, to reduce power drain, we toggle the SFRM line once so
++ * that the UDA_WS line is at logic 0.
++ *
++ * We can't clear ASSABET_BCR_CODEC_RST without knowing if the UCB1300 or
++ * ADV7171 driver is still active.  If it is, then we still need to play
++ * games, so we might as well leave ASSABET_BCR_CODEC_RST set.
++ */
++static void assabet_audio_shutdown(void *dummy)
++{
++      ASSABET_BCR_set(ASSABET_BCR_STEREO_LB | ASSABET_BCR_QMUTE |
++                      ASSABET_BCR_SPK_OFF);
++
++      l3_close(&uda1341);
++
++      GAFR &= ~(GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM);
++      Ser4SSCR0 = 0;
++
++#ifdef FIX_POWER_DRAIN
++      GPSR = GPIO_SSP_SFRM;
++      GPCR = GPIO_SSP_SFRM;
++#endif
++
++      /* disable the audio power */
++      ASSABET_BCR_clear(ASSABET_BCR_AUDIO_ON);
++}
++
++static int assabet_audio_ioctl( struct inode *inode, struct file *file,
++                              uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      /*
++       * These are platform dependent ioctls which are not handled by the
++       * generic sa1100-audio module.
++       */
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* the UDA1341 is stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* the UDA1341 is stereo only */
++              return put_user(2, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret) break;
++              assabet_set_samplerate(val);
++              /* fall through */
++
++      case SOUND_PCM_READ_RATE:
++              return put_user(audio_samplerate, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* we can do signed 16-bit only */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (As per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return ret;
++}
++
++static audio_stream_t output_stream, input_stream;
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      output_dma:     DMA_Ser4SSPWr,
++      output_id:      "Assabet UDA1341 out",
++      input_stream:   &input_stream,
++      input_dma:      DMA_Ser4SSPRd,
++      input_id:       "Assabet UDA1341 in",
++      need_tx_for_rx: 1,
++      hw_init:        assabet_audio_init,
++      hw_shutdown:    assabet_audio_shutdown,
++      client_ioctl:   assabet_audio_ioctl,
++      sem:            __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++static int assabet_audio_open(struct inode *inode, struct file *file)
++{
++      return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations assabet_audio_fops = {
++      open:           assabet_audio_open,
++      owner:          THIS_MODULE
++};
++
++
++static int audio_dev_id, mixer_dev_id;
++
++static int __init assabet_uda1341_init(void)
++{
++      int ret;
++
++      if (!machine_is_assabet())
++              return -ENODEV;
++
++      ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341");
++      if (ret)
++              goto out;
++
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&assabet_audio_fops, -1);
++      mixer_dev_id = register_sound_mixer(&assabet_mixer_fops, -1);
++
++#ifdef FIX_POWER_DRAIN
++      {
++              unsigned long flags;
++              local_irq_save(flags);
++              ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
++              GPSR = GPIO_SSP_SFRM;
++              GPDR |= GPIO_SSP_SFRM;
++              GPCR = GPIO_SSP_SFRM;
++              local_irq_restore(flags);
++      }
++#endif
++
++      printk(KERN_INFO "Sound: Assabet UDA1341: dsp id %d mixer id %d\n",
++              audio_dev_id, mixer_dev_id);
++      return 0;
++
++release_l3:
++      l3_detach_client(&uda1341);
++out:
++      return ret;
++}
++
++static void __exit assabet_uda1341_exit(void)
++{
++      unregister_sound_dsp(audio_dev_id);
++      unregister_sound_mixer(mixer_dev_id);
++      l3_detach_client(&uda1341);
++}
++
++module_init(assabet_uda1341_init);
++module_exit(assabet_uda1341_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_DESCRIPTION("Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec.");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/h3600-uda1341.c
+@@ -0,0 +1,352 @@
++/*
++ * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * This is the machine specific part of the Compaq iPAQ (aka Bitsy) support.
++ * This driver makes use of the UDA1341 and the sa1100-audio modules.
++ *
++ * History:
++ *
++ * 2000-05-21 Nicolas Pitre   Initial UDA1341 driver release.
++ *
++ * 2000-07-?? George France   Bitsy support.
++ *
++ * 2000-12-13 Deborah Wallach Fixed power handling for iPAQ/h3600
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-07-13 Nicolas Pitre   Fixes for all supported samplerates.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++//#include <asm/arch/h3600_hal.h>
++
++#include "sa1100-audio.h"
++
++
++#undef DEBUG
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++#define AUDIO_NAME            "Bitsy_UDA1341"
++
++#define AUDIO_RATE_DEFAULT    44100
++
++
++static struct l3_client uda1341;
++
++static int
++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      /*
++       * We only accept mixer (type 'M') ioctls.
++       */
++      if (_IOC_TYPE(cmd) != 'M')
++              return -EINVAL;
++
++      return l3_command(&uda1341, cmd, (void *)arg);
++}
++
++static struct file_operations h3600_mixer_fops = {
++      ioctl:          mixer_ioctl,
++      owner:          THIS_MODULE
++};
++
++
++/*
++ * Audio interface
++ */
++
++static long audio_samplerate = AUDIO_RATE_DEFAULT;
++
++/*
++ * Stop-gap solution until rest of hh.org HAL stuff is merged.
++ */
++#define GPIO_H3600_CLK_SET0           GPIO_GPIO (12)
++#define GPIO_H3600_CLK_SET1           GPIO_GPIO (13)
++static void h3600_set_audio_clock(long val)
++{
++      switch (val) {
++      case 24000: case 32000: case 48000:     /* 00: 12.288 MHz */
++              GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
++              break;
++
++      case 22050: case 29400: case 44100:     /* 01: 11.2896 MHz */
++              GPSR = GPIO_H3600_CLK_SET0;
++              GPCR = GPIO_H3600_CLK_SET1;
++              break;
++
++      case 8000: case 10666: case 16000:      /* 10: 4.096 MHz */
++              GPCR = GPIO_H3600_CLK_SET0;
++              GPSR = GPIO_H3600_CLK_SET1;
++              break;
++
++      case 10985: case 14647: case 21970:     /* 11: 5.6245 MHz */
++              GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1;
++              break;
++      }
++}
++
++static void h3600_set_samplerate(long val)
++{
++      struct uda1341_cfg cfg;
++      int clk_div = 0;
++
++      /* We don't want to mess with clocks when frames are in flight */
++      Ser4SSCR0 &= ~SSCR0_SSE;
++      /* wait for any frame to complete */
++      udelay(125);
++
++      /*
++       * We have the following clock sources:
++       * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz
++       * Those can be divided either by 256, 384 or 512.
++       * This makes up 12 combinations for the following samplerates...
++       */
++      if (val >= 48000)
++              val = 48000;
++      else if (val >= 44100)
++              val = 44100;
++      else if (val >= 32000)
++              val = 32000;
++      else if (val >= 29400)
++              val = 29400;
++      else if (val >= 24000)
++              val = 24000;
++      else if (val >= 22050)
++              val = 22050;
++      else if (val >= 21970)
++              val = 21970;
++      else if (val >= 16000)
++              val = 16000;
++      else if (val >= 14647)
++              val = 14647;
++      else if (val >= 10985)
++              val = 10985;
++      else if (val >= 10666)
++              val = 10666;
++      else
++              val = 8000;
++
++      /* Set the external clock generator */
++      h3600_set_audio_clock(val);
++
++      /* Select the clock divisor */
++      switch (val) {
++      case 8000:
++      case 10985:
++      case 22050:
++      case 24000:
++              cfg.fs = 512;
++              clk_div = SSCR0_SerClkDiv(16);
++              break;
++      case 16000:
++      case 21970:
++      case 44100:
++      case 48000:
++              cfg.fs = 256;
++              clk_div = SSCR0_SerClkDiv(8);
++              break;
++      case 10666:
++      case 14647:
++      case 29400:
++      case 32000:
++              cfg.fs = 384;
++              clk_div = SSCR0_SerClkDiv(12);
++              break;
++      }
++
++      cfg.format = FMT_LSB16;
++      l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
++      Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
++      audio_samplerate = val;
++}
++
++static void h3600_audio_init(void *dummy)
++{
++      unsigned long flags;
++
++      /* Setup the uarts */
++      local_irq_save(flags);
++      GAFR |= (GPIO_SSP_CLK);
++      GPDR &= ~(GPIO_SSP_CLK);
++      Ser4SSCR0 = 0;
++      Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8);
++      Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
++      Ser4SSCR0 |= SSCR0_SSE;
++
++      /* Enable the audio power */
++
++      clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
++      set_h3600_egpio(IPAQ_EGPIO_AUDIO_ON);
++      set_h3600_egpio(IPAQ_EGPIO_QMUTE);
++      local_irq_restore(flags);
++
++      /* external clock configuration */
++      h3600_set_samplerate(audio_samplerate);
++
++      /* Wait for the UDA1341 to wake up */
++      set_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
++      mdelay(1);
++
++      /* make the left and right channels unswapped (flip the WS latch ) */
++      Ser4SSDR = 0;
++
++      /* Initialize the UDA1341 internal state */
++      l3_open(&uda1341);
++
++      clr_h3600_egpio(IPAQ_EGPIO_QMUTE);
++}
++
++static void h3600_audio_shutdown(void *dummy)
++{
++      /* disable the audio power and all signals leading to the audio chip */
++      l3_close(&uda1341);
++      Ser4SSCR0 = 0;
++      clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET);
++      clr_h3600_egpio(IPAQ_EGPIO_AUDIO_ON);
++      clr_h3600_egpio(IPAQ_EGPIO_QMUTE);
++}
++
++static int h3600_audio_ioctl(struct inode *inode, struct file *file,
++                           uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      /*
++       * These are platform dependent ioctls which are not handled by the
++       * generic sa1100-audio module.
++       */
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* the UDA1341 is stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* the UDA1341 is stereo only */
++              return put_user(2, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret) break;
++              h3600_set_samplerate(val);
++              /* fall through */
++
++      case SOUND_PCM_READ_RATE:
++              return put_user(audio_samplerate, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* we can do 16-bit only */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (As per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return ret;
++}
++
++static audio_stream_t output_stream, input_stream;
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      output_dma:     DMA_Ser4SSPWr,
++      output_id:      "UDA1341 out",
++      input_stream:   &input_stream,
++      input_dma:      DMA_Ser4SSPRd,
++      input_id:       "UDA1341 in",
++      need_tx_for_rx: 1,
++      hw_init:        h3600_audio_init,
++      hw_shutdown:    h3600_audio_shutdown,
++      client_ioctl:   h3600_audio_ioctl,
++      sem:            __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++static int h3600_audio_open(struct inode *inode, struct file *file)
++{
++      return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations h3600_audio_fops = {
++      open:           h3600_audio_open,
++      owner:          THIS_MODULE
++};
++
++
++static int audio_dev_id, mixer_dev_id;
++
++static int __init h3600_uda1341_init(void)
++{
++      int ret;
++
++      if (!machine_is_h3xxx())
++              return -ENODEV;
++
++      ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341");
++      if (ret)
++              goto out;
++
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1);
++      mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1);
++
++      printk( KERN_INFO "iPAQ audio support initialized\n" );
++      return 0;
++
++release_l3:
++      l3_detach_client(&uda1341);
++out:
++      return ret;
++}
++
++static void __exit h3600_uda1341_exit(void)
++{
++      unregister_sound_dsp(audio_dev_id);
++      unregister_sound_mixer(mixer_dev_id);
++      l3_detach_client(&uda1341);
++}
++
++module_init(h3600_uda1341_init);
++module_exit(h3600_uda1341_exit);
++
++MODULE_AUTHOR("Nicolas Pitre, George France");
++MODULE_DESCRIPTION("Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec.");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/pangolin-uda1341.c
+@@ -0,0 +1,322 @@
++/*
++ * Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * This is the machine specific part of the Pangolin/UDA1341 support.
++ * This driver makes use of the UDA1341 and the sa1100-audio modules.
++ *
++ * History:
++ *
++ * 2000-05-21 Nicolas Pitre   Initial release.
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-07-17 Nicolas Pitre   Supports 44100Hz and 22050Hz samplerate now.
++ *
++ * 2001-08-06 Richard Fan             Pangolin Support
++ *
++ * 2001-09-23 Russell King    Update inline with Assabet driver
++ *                            Remove old L3 bus driver
++ *
++ * Note: this should probably be merged with the Assabet audio driver,
++ * and become the "SDRAM-clock driven" SA1100 audio driver.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++#include "sa1100-audio.h"
++
++/*
++ * Debugging?
++ */
++#undef DEBUG
++
++
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++#define AUDIO_RATE_DEFAULT    44100
++
++#define QmutePin             GPIO_GPIO(4)
++#define SpeakerOffPin        GPIO_GPIO(5)
++
++/*
++ * Mixer (UDA1341) interface
++ */
++
++static struct l3_client uda1341;
++
++static int
++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      /*
++       * We only accept mixer (type 'M') ioctls.
++       */
++      if (_IOC_TYPE(cmd) != 'M')
++              return -EINVAL;
++
++      return l3_command(&uda1341, cmd, (void *)arg);
++}
++
++static struct file_operations pangolin_mixer_fops = {
++      ioctl:          mixer_ioctl,
++      owner:          THIS_MODULE
++};
++
++
++/*
++ * Audio interface
++ */
++static long audio_samplerate = AUDIO_RATE_DEFAULT;
++
++static void pangolin_set_samplerate(long val)
++{
++      struct uda1341_cfg cfg;
++      int clk_div;
++
++      /* We don't want to mess with clocks when frames are in flight */
++      Ser4SSCR0 &= ~SSCR0_SSE;
++      /* wait for any frame to complete */
++      udelay(125);
++
++      /*
++       * Our clock source is derived from the CPLD on which we don't have
++       * much control unfortunately.  This was intended for a fixed 44100Hz
++       * samplerate assuming a core clock of 206 MHz.  Still we can play
++       * with the SA1110's clock divisor for the SSP port to get a 22050Hz
++       * samplerate.
++       *
++       * Apparently the clock sent to the SA1110 for the SSP port is
++       * divided from the clock sent to the UDA1341 (some people tried to
++       * be too clever in their design, or simply failed to read the SA1110
++       * manual).  If it was the same source we would have been able to
++       * support a third samplerate.
++       *
++       * At least it would have been a minimum acceptable solution to be
++       * able to set the CPLD divisor by software.  The iPAQ design is
++       * certainly a better example to follow for a new design.
++       */
++      if (val >= 44100) {
++              audio_samplerate = 44100;
++              cfg.fs = 256;
++              clk_div = SSCR0_SerClkDiv(2);
++      } else {
++              audio_samplerate = 22050;
++              cfg.fs = 512;
++              clk_div = SSCR0_SerClkDiv(4);
++      }
++
++      cfg.format = FMT_LSB16;
++      l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
++
++      Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE;
++}
++
++static void pangolin_audio_init(void *dummy)
++{
++      unsigned long flags;
++      unsigned int mdrefr;
++
++      local_irq_save(flags);
++
++      /*
++       * Setup the SSP uart.
++       */
++      PPAR |= PPAR_SPR;
++      Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2);
++      Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk;
++      GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK |
++              GPIO_SSP_SFRM;
++      GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM;
++      GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK);
++      Ser4SSCR0 |= SSCR0_SSE;
++
++      GAFR &= ~(SpeakerOffPin | QmutePin);
++      GPDR |=  (SpeakerOffPin | QmutePin);
++      GPCR = SpeakerOffPin;
++
++      /*
++       * The assabet board uses the SDRAM clock as the source clock for
++       * audio. This is supplied to the SA11x0 from the CPLD on pin 19.
++       * At 206MHz we need to run the audio clock (SDRAM bank 2)
++       * at half speed. This clock will scale with core frequency so
++       * the audio sample rate will also scale. The CPLD on Assabet
++       * will need to be programmed to match the core frequency.
++       */
++      mdrefr = MDREFR;
++      if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD |
++                     MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) {
++              mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN;
++              mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD);
++              MDREFR = mdrefr;
++              (void) MDREFR;
++      }
++      local_irq_restore(flags);
++
++      /* Wait for the UDA1341 to wake up */
++      mdelay(100);
++
++      l3_open(&uda1341);
++
++      pangolin_set_samplerate(audio_samplerate);
++
++      GPCR = QmutePin;
++}
++
++static void pangolin_audio_shutdown(void *dummy)
++{
++      GPSR = QmutePin;
++
++      l3_close(&uda1341);
++
++      Ser4SSCR0 = 0;
++      MDREFR &= ~(MDREFR_K2DB2 | MDREFR_K2RUN);
++}
++
++static int pangolin_audio_ioctl( struct inode *inode, struct file *file,
++                              uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      /*
++       * These are platform dependent ioctls which are not handled by the
++       * generic sa1100-audio module.
++       */
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* the UDA1341 is stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* the UDA1341 is stereo only */
++              return put_user(2, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret) break;
++              pangolin_set_samplerate(val);
++              /* fall through */
++
++      case SOUND_PCM_READ_RATE:
++              return put_user(audio_samplerate, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* we can do signed 16-bit only */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (As per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return ret;
++}
++
++static audio_stream_t output_stream, input_stream;
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      output_dma:     DMA_Ser4SSPWr,
++      output_id:      "Pangolin UDA1341 out",
++      input_stream:   &input_stream,
++      input_dma:      DMA_Ser4SSPRd,
++      input_id:       "Pangolin UDA1341 in",
++      need_tx_for_rx: 1,
++      hw_init:        pangolin_audio_init,
++      hw_shutdown:    pangolin_audio_shutdown,
++      client_ioctl:   pangolin_audio_ioctl,
++      sem:            __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++static int pangolin_audio_open(struct inode *inode, struct file *file)
++{
++      return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations pangolin_audio_fops = {
++      open:           pangolin_audio_open,
++      owner:          THIS_MODULE
++};
++
++
++static int audio_dev_id, mixer_dev_id;
++
++static int __init pangolin_uda1341_init(void)
++{
++      unsigned long flags;
++      int ret;
++
++      if (!machine_is_pangolin())
++              return -ENODEV;
++
++      ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341");
++      if (ret)
++              goto out;
++
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&pangolin_audio_fops, -1);
++      mixer_dev_id = register_sound_mixer(&pangolin_mixer_fops, -1);
++
++      local_irq_save(flags);
++      GAFR &= ~(SpeakerOffPin | QmutePin);
++      GPDR |=  (SpeakerOffPin | QmutePin);
++      local_irq_restore(flags);
++
++      printk(KERN_INFO "Pangolin UDA1341 audio driver initialized\n");
++      return 0;
++
++release_l3:
++      l3_detach_client(&uda1341);
++out:
++      return ret;
++}
++
++static void __exit pangolin_uda1341_exit(void)
++{
++      unregister_sound_dsp(audio_dev_id);
++      unregister_sound_mixer(mixer_dev_id);
++      l3_detach_client(&uda1341);
++}
++
++module_init(pangolin_uda1341_init);
++module_exit(pangolin_uda1341_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_DESCRIPTION("Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec.");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/sa1100-audio.c
+@@ -0,0 +1,976 @@
++/*
++ * Common audio handling for the SA11x0 processor
++ *
++ * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ *
++ * This module handles the generic buffering/DMA/mmap audio interface for
++ * codecs connected to the SA1100 chip.  All features depending on specific
++ * hardware implementations like supported audio formats or samplerates are
++ * relegated to separate specific modules.
++ *
++ *
++ * History:
++ *
++ * 2000-05-21 Nicolas Pitre   Initial release.
++ *
++ * 2000-06-10 Erik Bunce      Add initial poll support.
++ *
++ * 2000-08-22 Nicolas Pitre   Removed all DMA stuff. Now using the
++ *                            generic SA1100 DMA interface.
++ *
++ * 2000-11-30 Nicolas Pitre   - Validation of opened instances;
++ *                            - Power handling at open/release time instead
++ *                              of driver load/unload;
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-07-22 Nicolas Pitre   - added mmap() and realtime support
++ *                            - corrected many details to better comply
++ *                              with the OSS API
++ *
++ * 2001-10-19 Nicolas Pitre   - brought DMA registration processing
++ *                              into this module for better ressource
++ *                              management.  This also fixes a bug
++ *                              with the suspend/resume logic.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/sched.h>
++#include <linux/poll.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/sysrq.h>
++
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <asm/hardware.h>
++#include <asm/semaphore.h>
++#include <asm/dma.h>
++
++#include "sa1100-audio.h"
++
++
++#undef DEBUG 
++/* #define DEBUG 1 */
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++#define AUDIO_NAME            "sa1100-audio"
++#define AUDIO_NBFRAGS_DEFAULT 8
++#define AUDIO_FRAGSIZE_DEFAULT        8192
++
++#define NEXT_BUF(_s_,_b_) { \
++      (_s_)->_b_##_idx++; \
++      (_s_)->_b_##_idx %= (_s_)->nbfrags; \
++      (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }
++
++#define AUDIO_ACTIVE(state)   ((state)->rd_ref || (state)->wr_ref)
++
++/*
++ * This function frees all buffers
++ */
++
++static void audio_clear_buf(audio_stream_t * s)
++{
++      DPRINTK("audio_clear_buf\n");
++
++      /* ensure DMA won't run anymore */
++      s->active = 0;
++      s->stopped = 0;
++      sa1100_dma_flush_all(s->dma_ch);
++
++      if (s->buffers) {
++              int frag;
++              for (frag = 0; frag < s->nbfrags; frag++) {
++                      if (!s->buffers[frag].master)
++                              continue;
++                      consistent_free(s->buffers[frag].start,
++                                      s->buffers[frag].master,
++                                      s->buffers[frag].dma_addr);
++              }
++              kfree(s->buffers);
++              s->buffers = NULL;
++      }
++
++      s->buf_idx = 0;
++      s->buf = NULL;
++}
++
++
++/*
++ * This function allocates the buffer structure array and buffer data space
++ * according to the current number of fragments and fragment size.
++ */
++
++static int audio_setup_buf(audio_stream_t * s)
++{
++      int frag;
++      int dmasize = 0;
++      char *dmabuf = NULL;
++      dma_addr_t dmaphys = 0;
++
++      if (s->buffers)
++              return -EBUSY;
++
++      s->buffers = (audio_buf_t *)
++              kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
++      if (!s->buffers)
++              goto err;
++      memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);
++
++      for (frag = 0; frag < s->nbfrags; frag++) {
++              audio_buf_t *b = &s->buffers[frag];
++
++              /*
++               * Let's allocate non-cached memory for DMA buffers.
++               * We try to allocate all memory at once.
++               * If this fails (a common reason is memory fragmentation),
++               * then we allocate more smaller buffers.
++               */
++              if (!dmasize) {
++                      dmasize = (s->nbfrags - frag) * s->fragsize;
++                      do {
++                              dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,
++                                                        dmasize,
++                                                        &dmaphys);
++                              if (!dmabuf)
++                                      dmasize -= s->fragsize;
++                      } while (!dmabuf && dmasize);
++                      if (!dmabuf)
++                              goto err;
++                      b->master = dmasize;
++                      memzero(dmabuf, dmasize);
++              }
++
++              b->start = dmabuf;
++              b->dma_addr = dmaphys;
++              b->stream = s;
++              sema_init(&b->sem, 1);
++              DPRINTK("buf %d: start %p dma %p\n", frag, b->start,
++                      b->dma_addr);
++
++              dmabuf += s->fragsize;
++              dmaphys += s->fragsize;
++              dmasize -= s->fragsize;
++      }
++
++      s->buf_idx = 0;
++      s->buf = &s->buffers[0];
++      s->bytecount = 0;
++      s->getptrCount = 0;
++      s->fragcount = 0;
++
++      return 0;
++
++err:
++      printk(AUDIO_NAME ": unable to allocate audio memory\n ");
++      audio_clear_buf(s);
++      return -ENOMEM;
++}
++
++
++/*
++ * This function yanks all buffers from the DMA code's control and
++ * resets them ready to be used again.
++ */
++
++static void audio_reset_buf(audio_stream_t * s)
++{
++      int frag;
++
++      s->active = 0;
++      s->stopped = 0;
++      sa1100_dma_flush_all(s->dma_ch);
++      if (s->buffers) {
++              for (frag = 0; frag < s->nbfrags; frag++) {
++                      audio_buf_t *b = &s->buffers[frag];
++                      b->size = 0;
++                      sema_init(&b->sem, 1);
++              }
++      }
++      s->bytecount = 0;
++      s->getptrCount = 0;
++      s->fragcount = 0;
++}
++
++
++/*
++ * DMA callback functions
++ */
++
++static void audio_dmaout_done_callback(void *buf_id, int size)
++{
++      audio_buf_t *b = (audio_buf_t *) buf_id;
++      audio_stream_t *s = b->stream;
++
++      /* Accounting */
++      s->bytecount += size;
++      s->fragcount++;
++
++      /* Recycle buffer */
++      if (s->mapped)
++              sa1100_dma_queue_buffer(s->dma_ch, buf_id,
++                                      b->dma_addr, s->fragsize);
++      else
++              up(&b->sem);
++
++      /* And any process polling on write. */
++      wake_up(&s->wq);
++}
++
++static void audio_dmain_done_callback(void *buf_id, int size)
++{
++      audio_buf_t *b = (audio_buf_t *) buf_id;
++      audio_stream_t *s = b->stream;
++
++      /* Accounting */
++      s->bytecount += size;
++      s->fragcount++;
++
++      /* Recycle buffer */
++      if (s->mapped) {
++              sa1100_dma_queue_buffer(s->dma_ch, buf_id,
++                                      b->dma_addr, s->fragsize);
++      } else {
++              b->size = size;
++              up(&b->sem);
++      }
++
++      /* And any process polling on write. */
++      wake_up(&s->wq);
++}
++
++static int audio_sync(struct file *file)
++{
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *s = state->output_stream;
++      audio_buf_t *b;
++
++      DPRINTK("audio_sync\n");
++
++      if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
++              return 0;
++
++      /*
++       * Send current buffer if it contains data.  Be sure to send
++       * a full sample count.
++       */
++      b = s->buf;
++      if (b->size &= ~3) {
++              down(&b->sem);
++              sa1100_dma_queue_buffer(s->dma_ch, (void *) b,
++                                      b->dma_addr, b->size);
++              b->size = 0;
++              NEXT_BUF(s, buf);
++      }
++
++      /*
++       * Let's wait for the last buffer we sent i.e. the one before the
++       * current buf_idx.  When we acquire the semaphore, this means either:
++       * - DMA on the buffer completed or
++       * - the buffer was already free thus nothing else to sync.
++       */
++      b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);
++      if (down_interruptible(&b->sem))
++              return -EINTR;
++      up(&b->sem);
++      return 0;
++}
++
++
++static int audio_write(struct file *file, const char *buffer,
++                     size_t count, loff_t * ppos)
++{
++      const char *buffer0 = buffer;
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *s = state->output_stream;
++      int chunksize, ret = 0;
++
++      DPRINTK("audio_write: count=%d\n", count);
++
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++      if (s->mapped)
++              return -ENXIO;
++      if (!s->buffers && audio_setup_buf(s))
++              return -ENOMEM;
++
++      while (count > 0) {
++              audio_buf_t *b = s->buf;
++
++              /* Wait for a buffer to become free */
++              if (file->f_flags & O_NONBLOCK) {
++                      ret = -EAGAIN;
++                      if (down_trylock(&b->sem))
++                              break;
++              } else {
++                      ret = -ERESTARTSYS;
++                      if (down_interruptible(&b->sem))
++                              break;
++              }
++
++              /* Feed the current buffer */
++              chunksize = s->fragsize - b->size;
++              if (chunksize > count)
++                      chunksize = count;
++              DPRINTK("write %d to %d\n", chunksize, s->buf_idx);
++              if (copy_from_user(b->start + b->size, buffer, chunksize)) {
++                      up(&b->sem);
++                      return -EFAULT;
++              }
++              b->size += chunksize;
++              buffer += chunksize;
++              count -= chunksize;
++              if (b->size < s->fragsize) {
++                      up(&b->sem);
++                      break;
++              }
++
++              /* Send current buffer to dma */
++              s->active = 1;
++              sa1100_dma_queue_buffer(s->dma_ch, (void *) b,
++                                      b->dma_addr, b->size);
++              b->size = 0;    /* indicate that the buffer has been sent */
++              NEXT_BUF(s, buf);
++      }
++
++      if ((buffer - buffer0))
++              ret = buffer - buffer0;
++      DPRINTK("audio_write: return=%d\n", ret);
++      return ret;
++}
++
++
++static inline void audio_check_tx_spin(audio_state_t *state)
++{
++      /*
++       * With some codecs like the Philips UDA1341 we must ensure
++       * there is an output stream at any time while recording since
++       * this is how the UDA1341 gets its clock from the SA1100.
++       * So while there is no playback data to send, the output DMA
++       * will spin with all zeroes.  We use the cache flush special
++       * area for that.
++       */
++      if (state->need_tx_for_rx && !state->tx_spinning) {
++              sa1100_dma_set_spin(state->output_stream->dma_ch,
++                                  (dma_addr_t)FLUSH_BASE_PHYS, 2048);
++              state->tx_spinning = 1;
++      }
++}
++
++
++static void audio_prime_dma(audio_stream_t *s)
++{
++      int i;
++
++      s->active = 1;
++      for (i = 0; i < s->nbfrags; i++) {
++              audio_buf_t *b = s->buf;
++              down(&b->sem);
++              sa1100_dma_queue_buffer(s->dma_ch, (void *) b,
++                                      b->dma_addr, s->fragsize);
++              NEXT_BUF(s, buf);
++      }
++}
++
++
++static int audio_read(struct file *file, char *buffer,
++                    size_t count, loff_t * ppos)
++{
++      char *buffer0 = buffer;
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *s = state->input_stream;
++      int chunksize, ret = 0;
++
++      DPRINTK("audio_read: count=%d\n", count);
++
++      if (ppos != &file->f_pos)
++              return -ESPIPE;
++      if (s->mapped)
++              return -ENXIO;
++
++      if (!s->active) {
++              if (!s->buffers && audio_setup_buf(s))
++                      return -ENOMEM;
++              audio_check_tx_spin(state);
++              audio_prime_dma(s);
++      }
++
++      while (count > 0) {
++              audio_buf_t *b = s->buf;
++
++              /* Wait for a buffer to become full */
++              if (file->f_flags & O_NONBLOCK) {
++                      ret = -EAGAIN;
++                      if (down_trylock(&b->sem))
++                              break;
++              } else {
++                      ret = -ERESTARTSYS;
++                      if (down_interruptible(&b->sem))
++                              break;
++              }
++
++              /* Grab data from the current buffer */
++              chunksize = b->size;
++              if (chunksize > count)
++                      chunksize = count;
++              DPRINTK("read %d from %d\n", chunksize, s->buf_idx);
++              if (copy_to_user(buffer,
++                               b->start + s->fragsize - b->size,
++                               chunksize)) {
++                      up(&b->sem);
++                      return -EFAULT;
++              }
++              b->size -= chunksize;
++              buffer += chunksize;
++              count -= chunksize;
++              if (b->size > 0) {
++                      up(&b->sem);
++                      break;
++              }
++
++              /* Make current buffer available for DMA again */
++              sa1100_dma_queue_buffer(s->dma_ch, (void *) b,
++                                      b->dma_addr, s->fragsize);
++              NEXT_BUF(s, buf);
++      }
++
++      if ((buffer - buffer0))
++              ret = buffer - buffer0;
++      DPRINTK("audio_read: return=%d\n", ret);
++      return ret;
++}
++
++
++static int audio_mmap(struct file *file, struct vm_area_struct *vma)
++{
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *s;
++      unsigned long size, vma_addr;
++      int i, ret;
++
++      if (vma->vm_pgoff != 0)
++              return -EINVAL;
++
++      if (vma->vm_flags & VM_WRITE) {
++              if (!state->wr_ref)
++                      return -EINVAL;;
++              s = state->output_stream;
++      } else if (vma->vm_flags & VM_READ) {
++              if (!state->rd_ref)
++                      return -EINVAL;
++              s = state->input_stream;
++      } else return -EINVAL;
++
++      if (s->mapped)
++              return -EINVAL;
++      size = vma->vm_end - vma->vm_start;
++      if (size != s->fragsize * s->nbfrags)
++              return -EINVAL;
++      if (!s->buffers && audio_setup_buf(s))
++              return -ENOMEM;
++      vma_addr = vma->vm_start;
++      for (i = 0; i < s->nbfrags; i++) {
++              audio_buf_t *buf = &s->buffers[i];
++              if (!buf->master)
++                      continue;
++              ret = remap_page_range(vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot);
++              if (ret)
++                      return ret;
++              vma_addr += buf->master;
++      }
++      s->mapped = 1;
++
++      return 0;
++}
++
++
++static unsigned int audio_poll(struct file *file,
++                             struct poll_table_struct *wait)
++{
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *is = state->input_stream;
++      audio_stream_t *os = state->output_stream;
++      unsigned int mask = 0;
++      int i;
++
++      DPRINTK("audio_poll(): mode=%s%s\n",
++              (file->f_mode & FMODE_READ) ? "r" : "",
++              (file->f_mode & FMODE_WRITE) ? "w" : "");
++
++      if (file->f_mode & FMODE_READ) {
++              /* Start audio input if not already active */
++              if (!is->active) {
++                      if (!is->buffers && audio_setup_buf(is))
++                              return -ENOMEM;
++                      audio_check_tx_spin(state);
++                      audio_prime_dma(is);
++              }
++              poll_wait(file, &is->wq, wait);
++      }
++
++      if (file->f_mode & FMODE_WRITE) {
++              if (!os->buffers && audio_setup_buf(os))
++                      return -ENOMEM;
++              poll_wait(file, &os->wq, wait);
++      }
++
++      if (file->f_mode & FMODE_READ) {
++              if (is->mapped) {
++/* if the buffer is mapped assume we care that there are more bytes available than 
++   when we last asked using SNDCTL_DSP_GETxPTR */
++                      if (is->bytecount != is->getptrCount)
++                              mask |= POLLIN | POLLRDNORM;
++              } else {
++                      for (i = 0; i < is->nbfrags; i++) {
++                              if (atomic_read(&is->buffers[i].sem.count) > 0) {
++                                      mask |= POLLIN | POLLRDNORM;
++                                      break;
++                              }
++                      }
++              }
++      }
++      if (file->f_mode & FMODE_WRITE) {
++              if (os->mapped) {
++                      if (os->bytecount != os->getptrCount)
++                              mask |= POLLOUT | POLLWRNORM;
++              } else {
++                      for (i = 0; i < os->nbfrags; i++) {
++                              if (atomic_read(&os->buffers[i].sem.count) > 0) {
++                                      mask |= POLLOUT | POLLWRNORM;
++                                      break;
++                              }
++                      }
++              }
++      }
++
++      DPRINTK("audio_poll() returned mask of %s%s\n",
++              (mask & POLLIN) ? "r" : "",
++              (mask & POLLOUT) ? "w" : "");
++
++      return mask;
++}
++
++
++static loff_t audio_llseek(struct file *file, loff_t offset, int origin)
++{
++      return -ESPIPE;
++}
++
++
++static int audio_set_fragments(audio_stream_t *s, int val)
++{
++      if (s->active)
++              return -EBUSY;
++      if (s->buffers)
++              audio_clear_buf(s);
++      s->nbfrags = (val >> 16) & 0x7FFF;
++      val &= 0xffff;
++      if (val < 4)
++              val = 4;
++      if (val > 15)
++              val = 15;
++      s->fragsize = 1 << val;
++      if (s->nbfrags < 2)
++              s->nbfrags = 2;
++      if (s->nbfrags * s->fragsize > 128 * 1024)
++              s->nbfrags = 128 * 1024 / s->fragsize;
++      if (audio_setup_buf(s))
++              return -ENOMEM;
++      return val|(s->nbfrags << 16);
++}
++
++static int audio_ioctl(struct inode *inode, struct file *file,
++                     uint cmd, ulong arg)
++{
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      audio_stream_t *os = state->output_stream;
++      audio_stream_t *is = state->input_stream;
++      long val;
++
++      /* dispatch based on command */
++      switch (cmd) {
++      case OSS_GETVERSION:
++              return put_user(SOUND_VERSION, (int *)arg);
++
++      case SNDCTL_DSP_GETBLKSIZE:
++              if (file->f_mode & FMODE_WRITE)
++                      return put_user(os->fragsize, (int *)arg);
++              else
++                      return put_user(is->fragsize, (int *)arg);
++
++      case SNDCTL_DSP_GETCAPS:
++              val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP;
++              if (is && os)
++                      val |= DSP_CAP_DUPLEX;
++              return put_user(val, (int *)arg);
++
++      case SNDCTL_DSP_SETFRAGMENT:
++              if (get_user(val, (long *) arg))
++                      return -EFAULT;
++              if (file->f_mode & FMODE_READ) {
++                      int ret = audio_set_fragments(is, val);
++                      if (ret < 0)
++                              return ret;
++                      ret = put_user(ret, (int *)arg);
++                      if (ret)
++                              return ret;
++              }
++              if (file->f_mode & FMODE_WRITE) {
++                      int ret = audio_set_fragments(os, val);
++                      if (ret < 0)
++                              return ret;
++                      ret = put_user(ret, (int *)arg);
++                      if (ret)
++                              return ret;
++              }
++              return 0;
++
++      case SNDCTL_DSP_SYNC:
++              return audio_sync(file);
++
++      case SNDCTL_DSP_SETDUPLEX:
++              return 0;
++
++      case SNDCTL_DSP_POST:
++              return 0;
++
++      case SNDCTL_DSP_GETTRIGGER:
++              val = 0;
++              if (file->f_mode & FMODE_READ && is->active && !is->stopped)
++                      val |= PCM_ENABLE_INPUT;
++              if (file->f_mode & FMODE_WRITE && os->active && !os->stopped)
++                      val |= PCM_ENABLE_OUTPUT;
++              return put_user(val, (int *)arg);
++
++      case SNDCTL_DSP_SETTRIGGER:
++              if (get_user(val, (int *)arg))
++                      return -EFAULT;
++              if (file->f_mode & FMODE_READ) {
++                      if (val & PCM_ENABLE_INPUT) {
++                              if (!is->active) {
++                                      if (!is->buffers && audio_setup_buf(is))
++                                              return -ENOMEM;
++                                      audio_prime_dma(is);
++                              }
++                              audio_check_tx_spin(state);
++                              if (is->stopped) {
++                                      is->stopped = 0;
++                                      sa1100_dma_resume(is->dma_ch);
++                              }
++                      } else {
++                              sa1100_dma_stop(is->dma_ch);
++                              is->stopped = 1;
++                      }
++              }
++              if (file->f_mode & FMODE_WRITE) {
++                      if (val & PCM_ENABLE_OUTPUT) {
++                              if (!os->active) {
++                                      if (!os->buffers && audio_setup_buf(os))
++                                              return -ENOMEM;
++                                      if (os->mapped)
++                                              audio_prime_dma(os);
++                              }
++                              if (os->stopped) {
++                                      os->stopped = 0;
++                                      sa1100_dma_resume(os->dma_ch);
++                              }
++                      } else {
++                              sa1100_dma_stop(os->dma_ch);
++                              os->stopped = 1;
++                      }
++              }
++              return 0;
++
++      case SNDCTL_DSP_GETOPTR:
++      case SNDCTL_DSP_GETIPTR:
++          {
++              count_info inf = { 0, };
++              audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is;
++              audio_buf_t *b;
++              dma_addr_t ptr;
++              int bytecount, offset, flags;
++
++              if ((s == is && !(file->f_mode & FMODE_READ)) ||
++                  (s == os && !(file->f_mode & FMODE_WRITE)))
++                      return -EINVAL;
++              if (s->active) {
++                      save_flags_cli(flags);
++                      if (sa1100_dma_get_current(s->dma_ch, (void *)&b, &ptr) == 0) {
++                              offset = ptr - b->dma_addr;
++                              inf.ptr = (b - s->buffers) * s->fragsize + offset;
++                      } else offset = 0;
++                      bytecount = s->bytecount + offset;
++                      s->getptrCount = s->bytecount;  /* so poll can tell if it changes */
++                      inf.blocks = s->fragcount;
++                      s->fragcount = 0;
++                      restore_flags(flags);
++                      if (bytecount < 0)
++                              bytecount = 0;
++                      inf.bytes = bytecount;
++              }
++              return copy_to_user((void *)arg, &inf, sizeof(inf));
++          }
++
++      case SNDCTL_DSP_GETOSPACE:
++          {
++              audio_buf_info inf = { 0, };
++              int i;
++
++              if (!(file->f_mode & FMODE_WRITE))
++                      return -EINVAL;
++              if (!os->buffers && audio_setup_buf(os))
++                      return -ENOMEM;
++              for (i = 0; i < os->nbfrags; i++) {
++                      if (atomic_read(&os->buffers[i].sem.count) > 0) {
++                              if (os->buffers[i].size == 0)
++                                      inf.fragments++;
++                              inf.bytes += os->fragsize - os->buffers[i].size;
++                      }
++              }
++              inf.fragstotal = os->nbfrags;
++              inf.fragsize = os->fragsize;
++              return copy_to_user((void *)arg, &inf, sizeof(inf));
++          }
++
++      case SNDCTL_DSP_GETISPACE:
++          {
++              audio_buf_info inf = { 0, };
++              int i;
++
++              if (!(file->f_mode & FMODE_READ))
++                      return -EINVAL;
++              if (!is->buffers && audio_setup_buf(is))
++                      return -ENOMEM;
++              for (i = 0; i < is->nbfrags; i++) {
++                      if (atomic_read(&is->buffers[i].sem.count) > 0) {
++                              if (is->buffers[i].size == is->fragsize)
++                                      inf.fragments++;
++                              inf.bytes += is->buffers[i].size;
++                      }
++              }
++              inf.fragstotal = is->nbfrags;
++              inf.fragsize = is->fragsize;
++              return copy_to_user((void *)arg, &inf, sizeof(inf));
++          }
++
++      case SNDCTL_DSP_NONBLOCK:
++              file->f_flags |= O_NONBLOCK;
++              return 0;
++
++      case SNDCTL_DSP_RESET:
++              if (file->f_mode & FMODE_READ) {
++                      if (state->tx_spinning) {
++                              sa1100_dma_set_spin(os->dma_ch, 0, 0);
++                              state->tx_spinning = 0;
++                      }
++                      audio_reset_buf(is);
++              }
++              if (file->f_mode & FMODE_WRITE) {
++                      audio_reset_buf(os);
++              }
++              return 0;
++
++      default:
++              /*
++               * Let the client of this module handle the
++               * non generic ioctls
++               */
++              return state->client_ioctl(inode, file, cmd, arg);
++      }
++
++      return 0;
++}
++
++
++static int audio_release(struct inode *inode, struct file *file)
++{
++      audio_state_t *state = (audio_state_t *)file->private_data;
++      DPRINTK("audio_release\n");
++
++      down(&state->sem);
++
++      if (file->f_mode & FMODE_READ) {
++              if (state->tx_spinning) {
++                      sa1100_dma_set_spin(state->output_stream->dma_ch, 0, 0);
++                      state->tx_spinning = 0;
++              }
++              audio_clear_buf(state->input_stream);
++              if (!state->skip_dma_init) {
++                      sa1100_free_dma(state->input_stream->dma_ch);
++                      if (state->need_tx_for_rx && !state->wr_ref)
++                              sa1100_free_dma(state->output_stream->dma_ch);
++              }
++              state->rd_ref = 0;
++      }
++
++      if (file->f_mode & FMODE_WRITE) {
++              audio_sync(file);
++              audio_clear_buf(state->output_stream);
++              if (!state->skip_dma_init)
++                      if (!state->need_tx_for_rx || !state->rd_ref)
++                              sa1100_free_dma(state->output_stream->dma_ch);
++              state->wr_ref = 0;
++      }
++
++      if (!AUDIO_ACTIVE(state)) {
++             if (state->hw_shutdown)
++                     state->hw_shutdown(state->data);
++#ifdef CONFIG_PM
++             pm_unregister(state->pm_dev);
++#endif
++      }
++
++      up(&state->sem);
++      return 0;
++}
++
++
++#ifdef CONFIG_PM
++
++static int audio_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
++{
++      audio_state_t *state = (audio_state_t *)pm_dev->data;
++
++      switch (req) {
++      case PM_SUSPEND: /* enter D1-D3 */
++              if (state->output_stream)
++                      sa1100_dma_sleep(state->output_stream->dma_ch);
++              if (state->input_stream)
++                      sa1100_dma_sleep(state->input_stream->dma_ch);
++              if (AUDIO_ACTIVE(state) && state->hw_shutdown)
++                      state->hw_shutdown(state->data);
++              break;
++      case PM_RESUME:  /* enter D0 */
++              if (AUDIO_ACTIVE(state) && state->hw_init)
++                      state->hw_init(state->data);
++              if (state->input_stream)
++                      sa1100_dma_wakeup(state->input_stream->dma_ch);
++              if (state->output_stream)
++                      sa1100_dma_wakeup(state->output_stream->dma_ch);
++              break;
++      }
++      return 0;
++}
++
++#endif
++
++
++int sa1100_audio_attach(struct inode *inode, struct file *file,
++                        audio_state_t *state)
++{
++      int err, need_tx_dma;
++
++      DPRINTK("audio_open\n");
++
++      down(&state->sem);
++
++      /* access control */
++      err = -ENODEV;
++      if ((file->f_mode & FMODE_WRITE) && !state->output_stream)
++              goto out;
++      if ((file->f_mode & FMODE_READ) && !state->input_stream)
++              goto out;
++      err = -EBUSY;
++      if ((file->f_mode & FMODE_WRITE) && state->wr_ref)
++              goto out;
++      if ((file->f_mode & FMODE_READ) && state->rd_ref)
++              goto out;
++      err = -EINVAL;
++      if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !state->output_stream)
++              goto out;
++
++      /* request DMA channels */
++      if (state->skip_dma_init)
++              goto skip_dma;
++      need_tx_dma = ((file->f_mode & FMODE_WRITE) ||
++                     ((file->f_mode & FMODE_READ) && state->need_tx_for_rx));
++      if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx))
++              need_tx_dma = 0;
++      if (need_tx_dma) {
++              err = sa1100_request_dma(&state->output_stream->dma_ch,
++                                       state->output_id,
++                                       state->output_dma);
++              if (err)
++                      goto out;
++      }
++      if (file->f_mode & FMODE_READ) {
++              err = sa1100_request_dma(&state->input_stream->dma_ch,
++                                       state->input_id,
++                                       state->input_dma);
++              if (err) {
++                      if (need_tx_dma)
++                              sa1100_free_dma(state->output_stream->dma_ch);
++                      goto out;
++              }
++      }
++skip_dma:
++
++      /* now complete initialisation */
++      if (!AUDIO_ACTIVE(state)) {
++              if (state->hw_init)
++                      state->hw_init(state->data);
++#ifdef CONFIG_PM
++              state->pm_dev = pm_register(PM_SYS_DEV, 0, audio_pm_callback);
++              if (state->pm_dev)
++                      state->pm_dev->data = state;
++#endif
++      }
++
++      if ((file->f_mode & FMODE_WRITE)) {
++              state->wr_ref = 1;
++              audio_clear_buf(state->output_stream);
++              state->output_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT;
++              state->output_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT;
++              state->output_stream->mapped = 0;
++              sa1100_dma_set_callback(state->output_stream->dma_ch,
++                                      audio_dmaout_done_callback);
++              init_waitqueue_head(&state->output_stream->wq);
++      }
++      if (file->f_mode & FMODE_READ) {
++              state->rd_ref = 1;
++              audio_clear_buf(state->input_stream);
++              state->input_stream->fragsize = AUDIO_FRAGSIZE_DEFAULT;
++              state->input_stream->nbfrags = AUDIO_NBFRAGS_DEFAULT;
++              state->input_stream->mapped = 0;
++              sa1100_dma_set_callback(state->input_stream->dma_ch,
++                                      audio_dmain_done_callback);
++              init_waitqueue_head(&state->input_stream->wq);
++      }
++
++      file->private_data      = state;
++      file->f_op->release     = audio_release;
++      file->f_op->write       = audio_write;
++      file->f_op->read        = audio_read;
++      file->f_op->mmap        = audio_mmap;
++      file->f_op->poll        = audio_poll;
++      file->f_op->ioctl       = audio_ioctl;
++      file->f_op->llseek      = audio_llseek;
++      err = 0;
++
++out:
++      up(&state->sem);
++      return err;
++}
++
++EXPORT_SYMBOL(sa1100_audio_attach);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_DESCRIPTION("Common audio handling for the SA11x0 processor");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.27/drivers/sound/sa1100-audio.h
+@@ -0,0 +1,68 @@
++/*
++ * Common audio handling for the SA11x0
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++
++/*
++ * Buffer Management
++ */
++
++typedef struct {
++      int size;               /* buffer size */
++      char *start;            /* points to actual buffer */
++      dma_addr_t dma_addr;    /* physical buffer address */
++      struct semaphore sem;   /* down before touching the buffer */
++      int master;             /* owner for buffer allocation, contain size when true */
++      struct audio_stream_s *stream;  /* owning stream */
++} audio_buf_t;
++
++typedef struct audio_stream_s {
++      audio_buf_t *buffers;   /* pointer to audio buffer structures */
++      audio_buf_t *buf;       /* current buffer used by read/write */
++      u_int buf_idx;          /* index for the pointer above... */
++      u_int fragsize;         /* fragment i.e. buffer size */
++      u_int nbfrags;          /* nbr of fragments i.e. buffers */
++      int bytecount;          /* nbr of processed bytes */
++      int getptrCount;        /* value of bytecount last time anyone asked via GETxPTR */
++      int fragcount;          /* nbr of fragment transitions */
++      dmach_t dma_ch;         /* DMA channel ID */
++      wait_queue_head_t wq;   /* for poll */
++      int mapped:1;           /* mmap()'ed buffers */
++      int active:1;           /* actually in progress */
++      int stopped:1;          /* might be active but stopped */
++} audio_stream_t;
++
++/*
++ * State structure for one instance
++ */
++
++typedef struct {
++      audio_stream_t *output_stream;
++      audio_stream_t *input_stream;
++      dma_device_t output_dma;
++      dma_device_t input_dma;
++      char *output_id;
++      char *input_id;
++      int rd_ref:1;           /* open reference for recording */
++      int wr_ref:1;           /* open reference for playback */
++      int need_tx_for_rx:1;   /* if data must be sent while receiving */
++      int tx_spinning:1;      /* tx spinning active */
++      int skip_dma_init:1;    /* hack for the SA1111 */
++      void *data;
++      void (*hw_init)(void *);
++      void (*hw_shutdown)(void *);
++      int (*client_ioctl)(struct inode *, struct file *, uint, ulong);
++      struct pm_dev *pm_dev;
++      struct semaphore sem;   /* to protect against races in attach() */
++} audio_state_t;
++
++/*
++ * Functions exported by this module
++ */
++extern int sa1100_audio_attach( struct inode *inode, struct file *file,
++                              audio_state_t *state);
+--- /dev/null
++++ linux-2.4.27/drivers/sound/sa1100ssp.c
+@@ -0,0 +1,182 @@
++/*
++ * Glue audio driver for a simple DAC on the SA1100's SSP port
++ *
++ * Copyright (c) 2001 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * History:
++ *
++ * 2001-06-04 Nicolas Pitre   Initial release.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++
++#include "sa1100-audio.h"
++
++
++#undef DEBUG
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++#define AUDIO_NAME            "SA1100 SSP audio"
++
++#define AUDIO_FMT             AFMT_S16_LE
++#define AUDIO_CHANNELS                2
++
++static int sample_rate = 44100;
++
++
++static void ssp_audio_init(void)
++{
++      if (machine_is_lart()) {
++              unsigned long flags;
++              local_irq_save(flags);
++
++              /* LART has the SSP port rewired to GPIO 10-13, 19 */
++              /* alternate functions for the GPIOs */
++              GAFR |= ( GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK |
++                        GPIO_SSP_SFRM | GPIO_SSP_CLK );
++
++              /* Set the direction: 10, 12, 13 output; 11, 19 input */
++              GPDR |= ( GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM );
++              GPDR &= ~( GPIO_SSP_RXD | GPIO_SSP_CLK );
++
++              /* enable SSP pin swap */
++              PPAR |= PPAR_SPR;
++
++              local_irq_restore(flags);
++      }
++
++      /* turn on the SSP */
++      Ser4SSCR0 = 0;
++      Ser4SSCR0 = (SSCR0_DataSize(16) | SSCR0_TI | SSCR0_SerClkDiv(2) |
++                   SSCR0_SSE);
++      Ser4SSCR1 = (SSCR1_SClkIactL | SSCR1_SClk1P | SSCR1_ExtClk);
++}
++
++static void ssp_audio_shutdown(void)
++{
++      Ser4SSCR0 = 0;
++}
++
++static int ssp_audio_ioctl( struct inode *inode, struct file *file,
++                          uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      /*
++       * These are platform dependent ioctls which are not handled by the
++       * generic sa1100-audio module.
++       */
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* Simple standard DACs are stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* Simple standard DACs are stereo only */
++              return put_user(AUDIO_CHANNELS, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++      case SOUND_PCM_READ_RATE:
++              /* We assume the clock doesn't change */
++              return put_user(sample_rate, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* Simple standard DACs are 16-bit only */
++              return put_user(AUDIO_FMT, (long *) arg);
++
++      default:
++              return -EINVAL;
++      }
++
++      return ret;
++}
++
++static audio_stream_t output_stream;
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      output_dma:     DMA_Ser4SSPWr,
++      output_id:      "Generic SSP sound",
++      hw_init:        ssp_audio_init,
++      hw_shutdown:    ssp_audio_shutdown,
++      client_ioctl:   ssp_audio_ioctl,
++      sem:            __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++static int ssp_audio_open(struct inode *inode, struct file *file)
++{
++      return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations ssp_audio_fops = {
++      open:           ssp_audio_open,
++      owner:          THIS_MODULE
++};
++
++static int audio_dev_id;
++
++static int __init sa1100ssp_audio_init(void)
++{
++      int ret;
++
++      if (!machine_is_lart()) {
++              printk(KERN_ERR AUDIO_NAME ": no support for this SA-1100 design!\n");
++              /* look at ssp_audio_init() for specific initialisations */
++              return -ENODEV;
++      }
++
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&ssp_audio_fops, -1);
++
++      printk( KERN_INFO AUDIO_NAME " initialized\n" );
++      return 0;
++}
++
++static void __exit sa1100ssp_audio_exit(void)
++{
++      unregister_sound_dsp(audio_dev_id);
++}
++
++module_init(sa1100ssp_audio_init);
++module_exit(sa1100ssp_audio_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_DESCRIPTION("Glue audio driver for a simple DAC on the SA1100's SSP port");
++
++MODULE_PARM(sample_rate, "i");
++MODULE_PARM_DESC(sample_rate, "Sample rate of the audio DAC, default is 44100");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/sa1111-ac97.c
+@@ -0,0 +1,518 @@
++/*
++ * Glue audio driver for the CS4205 and CS4201 AC'97 codecs.
++ * largely based on the framework provided by sa1111-uda1341.c.
++ *
++ * Copyright (c) 2002 Bertrik Sikken (bertrik.sikken@technolution.nl)
++ * Copyright (c) 2002 Robert Whaley (rwhaley@applieddata.net)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * This driver makes use of the ac97_codec module (for mixer registers)
++ * and the sa1100-audio module (for DMA).
++ *
++ * History:
++ *
++ * 2002-04-04 Initial version.
++ * 2002-04-10 Updated mtd_audio_init to improve choppy sound
++ *              and hanging sound issue.
++ * 2002-05-16   Updated for ADS Bitsy+ Robert Whaley
++ * 2002-06-28   Cleanup and added retry for read register timeouts
++ * 2002-08-14   Updated for ADS AGC Robert Whaley
++ * 2002-12-26   Cleanup, remove CONFIG_PM (it's handled by sa1100-audio.c)
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/errno.h>
++#include <linux/proc_fs.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/ac97_codec.h>
++
++#include <asm/semaphore.h>
++#include <asm/uaccess.h>
++#include <asm/dma.h>
++#include <asm/hardware/sa1111.h>
++
++#include "sa1100-audio.h"
++
++/* SAC FIFO depth, low nibble is transmit fifo, high nibble is receive FIFO */
++#define SAC_FIFO_DEPTH                0x77
++
++// #define DEBUG
++
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++/*
++      Our codec data
++*/
++static struct ac97_codec ac97codec;
++static int audio_dev_id, mixer_dev_id;
++static audio_stream_t output_stream, input_stream;
++
++/* proc info */
++
++struct proc_dir_entry *ac97_ps;
++
++static int sa1111_ac97_set_adc_rate(long rate);
++static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val);
++static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg);
++
++static int
++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      /*
++       * We only accept mixer (type 'M') ioctls.
++       */
++      if (_IOC_TYPE(cmd) != 'M') {
++              return -EINVAL;
++      }
++
++      /* pass the ioctl to the ac97 mixer */
++      return ac97codec.mixer_ioctl(&ac97codec, cmd, arg);
++}
++
++
++static struct file_operations sa1111_ac97_mixer_fops = {
++      ioctl:          mixer_ioctl,
++      owner:          THIS_MODULE
++};
++
++static void sa1111_ac97_power_off(void *dummy)
++{
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++      /* turn off audio and audio amp */
++      ADS_CPLD_PCON |= (ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON);
++
++      /* make GPIO11 high impeadence */
++      GPDR &= ~GPIO_GPIO11;
++
++      /* disable SACR0 so we can make these pins high impeadence */
++      SACR0 &= ~SACR0_ENB;
++
++      /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */
++      PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
++
++#endif
++
++#ifdef CONFIG_SA1100_ADSAGC
++      /* turn off audio and audio amp */
++      ADS_CR1 &= ~(ADS_CR1_CODEC | ADS_CR1_AMP);
++
++      /* disable SACR0 so we can make these pins high impeadence */
++      SACR0 &= ~SACR0_ENB;
++
++      /* make BIT_CLK, SDATA_OUT, and SYNC high impeadence */
++      PC_DDR |= (GPIO_GPIO1 | GPIO_GPIO2 | GPIO_GPIO3);
++
++#endif
++}
++
++
++static void sa1111_ac97_power_on(void *dummy)
++{
++      int ret, i;
++
++      /* disable L3 */
++      SACR1 = 0;
++
++      SKPCR |= (SKPCR_ACCLKEN);       /* enable ac97 clock */
++      udelay(50);
++
++      /* BIT_CLK is input to SA1111, DMA thresholds 9 (both dirs) */
++      SACR0 |= SACR0_BCKD | (SAC_FIFO_DEPTH << 8);
++
++      /* reset SAC registers */
++      SACR0 &= ~SACR0_RST;
++      udelay(50);
++      SACR0 |= SACR0_RST;
++      udelay(50);
++      SACR0 &= ~SACR0_RST;
++
++      /* setup SA1111 to use AC'97 */
++      SBI_SKCR |= SKCR_SELAC;         /* select ac97 */
++      udelay(50);
++
++      /* issue a cold AC97 reset */
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++
++      /* initialize reset line */
++      GAFR &= ~GPIO_GPIO11;
++      GPDR |= GPIO_GPIO11;
++      GPSR = GPIO_GPIO11;
++
++      /* turn on audio and audio amp */
++      ADS_CPLD_PCON &= ~(ADS_PCON_AUDIO_ON | ADS_PCON_AUDIOPA_ON);
++      mdelay(5);
++
++      /* reset by lowering the reset pin momentarily */
++      DPRINTK("reseting codec via GPIO11\n");
++      GPCR = GPIO_GPIO11;
++      udelay(5);
++      GPSR = GPIO_GPIO11;
++      udelay(10);
++
++#endif
++#ifdef CONFIG_SA1100_ADSAGC
++
++      /* turn on audio and audio amp */
++      DPRINTK("before turning on power.  ADS_CR1: %x\n", ADS_CR1);
++      ADS_CR1 |= (ADS_CR1_AMP | ADS_CR1_CODEC);
++      DPRINTK("after turnning on power.  ADS_CR1: %x\n", ADS_CR1);
++      mdelay(5);
++
++      /* reset by lowering the reset pin momentarily */
++      DPRINTK("reseting codec via CPLD\n");
++      ADS_CR1 |= ADS_CR1_AUDIO_RST;
++      DPRINTK("after reset1.  ADS_CR1: %x\n", ADS_CR1);
++      udelay(5);
++      ADS_CR1 &= ~ADS_CR1_AUDIO_RST;
++      DPRINTK("after reset2.  ADS_CR1: %x\n", ADS_CR1);
++      udelay(10);
++
++#endif
++      SACR2 = 0;
++      udelay(50);
++
++      DPRINTK("before SW reset:  SACR2: %x\n", SACR2);
++      SACR2 = SACR2_RESET;
++      DPRINTK("after SW reset:  SACR2: %x\n", SACR2);
++      udelay(50);
++
++      /* set AC97 slot 3 and 4 (PCM out) to valid */
++      SACR2 = (SACR2_RESET | SACR2_TS3V | SACR2_TS4V);
++
++      /* enable SAC */
++      SACR0 |= SACR0_ENB;
++
++      i = 100;
++      while (!(SASR1 & SASR1_CRDY)) {
++              if (!i--) {
++                      printk("Didn't get CRDY.  SASR1=%x SKID=%x\n", SASR1, SBI_SKID);
++                      break;
++              }
++              udelay(50);
++      }
++
++      if (!(ret = ac97_probe_codec(&ac97codec))) {
++              printk("ac97_probe_codec failed  (%d)\n", ret);
++              return;
++      }
++
++      /* mic ADC on, disable VRA, disable VRM */
++      sa1111_ac97_write_reg(&ac97codec, AC97_EXTENDED_STATUS, 0x0200);
++}
++
++
++/*
++ * Audio interface
++ */
++
++
++static int sa1111_ac97_audio_ioctl(struct inode *inode, struct file *file,
++                           uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      DPRINTK("sa1111_ac97_audio_ioctl\n");
++
++      /*
++       * These are platform dependent ioctls which are not handled by the
++       * generic sa1100-audio module.
++       */
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret) {
++                      return ret;
++              }
++              /* the cs42xx is stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* the cs42xx is stereo only */
++              return put_user(2, (long *) arg);
++
++#define SA1100_AC97_IOCTL_EXTRAS
++
++#ifdef SA1100_AC97_IOCTL_EXTRAS
++
++#define SNDCTL_DSP_AC97_CMD _SIOWR('P', 99, int)
++#define SNDCTL_DSP_INPUT_SPEED _SIOWR('P', 98, int)
++#define SOUND_PCM_READ_INPUT_RATE _SIOWR('P', 97, int)
++
++      case SNDCTL_DSP_AC97_CMD:
++
++              ret = get_user(val, (long *) arg);
++              if (ret) {
++                      break;
++              }
++              sa1111_ac97_write_reg(&ac97codec, (u8) ((val & 0xff000000) >> 24), (u16) (val & 0xffff));
++              return 0;
++
++
++      case SNDCTL_DSP_INPUT_SPEED:
++              ret = get_user(val, (long *) arg);
++              // acc code here to set the speed
++              if (ret) {
++                      break;
++              }
++              // note that this only changes the ADC rate, not the
++              // rate of the DAC.
++              ret = sa1111_ac97_set_adc_rate(val);
++              if (ret)
++                break;
++              return put_user(val, (long *) arg);
++
++      case SOUND_PCM_READ_INPUT_RATE:
++
++              return put_user((long) sa1111_ac97_read_reg(&ac97codec, 0x32), (long *) arg);
++
++
++#endif
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret) {
++                      break;
++              }
++
++      case SOUND_PCM_READ_RATE:
++              /* only 48 kHz playback is supported by the SA1111 */
++              return put_user(48000L, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* we can do 16-bit only */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (As per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return ret;
++}
++
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      input_stream:   &input_stream,
++      skip_dma_init:  1,  /* done locally */
++      hw_init:        sa1111_ac97_power_on,
++      hw_shutdown:    sa1111_ac97_power_off,
++      client_ioctl:   sa1111_ac97_audio_ioctl,
++      sem:                    __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++
++static int sa1111_ac97_audio_open(struct inode *inode, struct file *file)
++{
++      return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations sa1111_ac97_audio_fops = {
++      open:           sa1111_ac97_audio_open,
++      owner:          THIS_MODULE
++};
++
++
++static void sa1111_ac97_write_reg(struct ac97_codec *dev, u8 reg, u16 val)
++{
++      int i;
++
++      /* reset status bits */
++      SASCR = SASCR_DTS;
++
++      /* write command and data registers */
++      ACCAR = reg << 12;
++      ACCDR = val << 4;
++
++      /* wait for data to be transmitted */
++      i = 0;
++      while ((SASR1 & SASR1_CADT) == 0) {
++              udelay(50);
++              if (++i > 10) {
++                      DPRINTK("sa1111_ac97_write_reg failed (data not transmitted. SASR1: %x)\n", SASR1);
++                      break;
++              }
++      }
++
++      DPRINTK("<%03d> sa1111_ac97_write_reg, [%02X]=%04X\n", i, reg, val);
++}
++
++
++static u16 sa1111_ac97_read_reg(struct ac97_codec *dev, u8 reg)
++{
++      u16             val;
++      int             i;
++      int             retry = 10;
++
++      do {
++              /* reset status bits */
++              SASCR = SASCR_RDD | SASCR_STO;
++
++              /* write command register */
++              ACCAR = (reg | 0x80) << 12;
++              ACCDR = 0;
++
++              /* wait for SADR bit in SASR1 */
++              i = 0;
++              while ((SASR1 & SASR1_SADR) == 0) {
++                      udelay(50);
++                      if (++i > 10) {
++                              DPRINTK("<---> sa1111_ac97_read_reg failed\n");
++                              retry--;
++                              break;
++                      }
++                      if ((SASR1 & SASR1_RSTO) != 0) {
++                              DPRINTK("sa1111_ac97_read_reg *timeout*\n");
++                              retry--;
++                              break;
++                      }
++              }
++
++      } while ((SASR1 & SASR1_SADR) == 0 && retry > 0);
++
++      val = ACSDR >> 4;
++
++      DPRINTK("<%03d> sa1111_ac97_read_reg, [%02X]=%04X\n", i, reg, val);
++      return val;
++}
++
++
++/* wait for codec ready */
++static void sa1111_ac97_ready(struct ac97_codec *dev)
++{
++      int i;
++      u16     val;
++
++      i = 0;
++      while ((SASR1 & SASR1_CRDY) == 0) {
++              udelay(50);
++              if (++i > 10) {
++                      DPRINTK("sa1111_ac97_ready failed\n");
++                      return;
++              }
++      }
++      DPRINTK("codec_ready bit took %d cycles\n", i);
++
++      /* Wait for analog parts of codec to initialise */
++      i = 0;
++      do {
++              val = sa1111_ac97_read_reg(&ac97codec, AC97_POWER_CONTROL);
++              if (++i > 100) {
++                      break;
++              }
++              mdelay(10);
++      } while ((val & 0xF) != 0xF || val == 0xFFFF);
++
++      /* the cs42xx typically takes 150 ms to initialise */
++
++      DPRINTK("analog init took %d cycles\n", i);
++}
++
++
++static int __init sa1111_ac97_init(void)
++{
++      int ret;
++
++      // SBI_SKCR |= SKCR_RCLKEN;
++
++      DPRINTK("sa1111_ac97_init\n");
++
++      /* install the ac97 mixer module */
++      ac97codec.codec_read    = sa1111_ac97_read_reg;
++      ac97codec.codec_write   = sa1111_ac97_write_reg;
++      ac97codec.codec_wait    = sa1111_ac97_ready;
++
++      /* Acquire and initialize DMA */
++      ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out",
++                                   SA1111_SAC_XMT_CHANNEL);
++      if (ret < 0) {
++              printk("DMA request for SAC output failed\n");
++              return ret;
++      }
++
++      ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in",
++                                   SA1111_SAC_RCV_CHANNEL);
++      if (ret < 0) {
++              printk("DMA request for SAC input failed\n");
++              sa1100_free_dma(output_stream.dma_ch);
++              return ret;
++      }
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&sa1111_ac97_audio_fops, -1);
++      mixer_dev_id = register_sound_mixer(&sa1111_ac97_mixer_fops, -1);
++
++
++      /* setup proc entry */
++      ac97_ps = create_proc_read_entry ("driver/sa1111-ac97", 0, NULL,
++                                        ac97_read_proc, &ac97codec);
++
++      return 0;
++}
++
++
++static void __exit sa1111_ac97_exit(void)
++{
++      SKPCR &= ~SKPCR_ACCLKEN;                /* disable ac97 clock */
++      SBI_SKCR &= ~SKCR_SELAC;                /* deselect ac97 */
++
++      unregister_sound_dsp(audio_dev_id);
++      unregister_sound_mixer(mixer_dev_id);
++      sa1100_free_dma(output_stream.dma_ch);
++      sa1100_free_dma(input_stream.dma_ch);
++}
++
++static int sa1111_ac97_set_adc_rate(long rate)
++{
++
++  // note this only changes the rate of the ADC, the DAC is fixed at 48K.
++  // this is due to limitations of the SA1111 chip
++
++  u16 code = rate;
++
++  switch (rate) {
++  case  8000:
++  case 11025:
++  case 16000:
++  case 22050:
++  case 32000:
++  case 44100:
++  case 48000:
++    break;
++  default:
++    return -1;
++  }
++  sa1111_ac97_write_reg(&ac97codec, 0x2A, 0x0001);
++  sa1111_ac97_write_reg(&ac97codec, 0x32, code);
++  return 0;
++}
++
++module_init(sa1111_ac97_init);
++module_exit(sa1111_ac97_exit);
++
++MODULE_AUTHOR("Bertrik Sikken, Technolution B.V., Netherlands");
++MODULE_DESCRIPTION("Glue audio driver for AC'97 codec");
++MODULE_LICENSE("GPL");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/sa1111-uda1341.c
+@@ -0,0 +1,282 @@
++/*
++ * Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec.
++ *
++ * Copyright (c) 2000 John Dorsey
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * History:
++ *
++ * 2000-09-04 John Dorsey     SA-1111 Serial Audio Controller support
++ *                            was initially added to the sa1100-uda1341.c
++ *                            driver.
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-09-23 Russell King    Remove old L3 bus driver.
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/sound.h>
++#include <linux/soundcard.h>
++#include <linux/ioport.h>
++#include <linux/pm.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/semaphore.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/dma.h>
++#include <asm/arch/assabet.h>
++#include <asm/hardware/sa1111.h>
++
++#include "sa1100-audio.h"
++
++#undef DEBUG
++#ifdef DEBUG
++#define DPRINTK( x... )  printk( ##x )
++#else
++#define DPRINTK( x... )
++#endif
++
++
++/*
++ * Definitions
++ */
++
++#define AUDIO_RATE_DEFAULT    22050
++
++#define AUDIO_CLK_BASE                561600
++
++
++
++/*
++ * Mixer interface
++ */
++
++static struct l3_client uda1341;
++
++static int
++mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
++{
++      /*
++       * We only accept mixer (type 'M') ioctls.
++       */
++      if (_IOC_TYPE(cmd) != 'M')
++              return -EINVAL;
++
++      return l3_command(&uda1341, cmd, (void *)arg);
++}
++
++static struct file_operations uda1341_mixer_fops = {
++      ioctl:          mixer_ioctl,
++      owner:          THIS_MODULE
++};
++
++
++/*
++ * Audio interface
++ */
++
++static int audio_clk_div = (AUDIO_CLK_BASE + AUDIO_RATE_DEFAULT/2)/AUDIO_RATE_DEFAULT;
++
++static void sa1111_audio_init(void *dummy)
++{
++#ifdef CONFIG_ASSABET_NEPONSET
++      if (machine_is_assabet()) {
++              /* Select I2S audio (instead of AC-Link) */
++              AUD_CTL = AUD_SEL_1341;
++      }
++#endif
++#ifdef CONFIG_SA1100_JORNADA720
++      if (machine_is_jornada720()) {
++              /* LDD4 is speaker, LDD3 is microphone */
++              PPSR &= ~(PPC_LDD3 | PPC_LDD4);
++              PPDR |= PPC_LDD3 | PPC_LDD4;
++              PPSR |= PPC_LDD4; /* enable speaker */
++              PPSR |= PPC_LDD3; /* enable microphone */
++      }
++#endif
++
++      SBI_SKCR &= ~SKCR_SELAC;
++
++      /* Enable the I2S clock and L3 bus clock: */
++      SKPCR |= (SKPCR_I2SCLKEN | SKPCR_L3CLKEN);
++
++      /* Activate and reset the Serial Audio Controller */
++      SACR0 |= (SACR0_ENB | SACR0_RST);
++      mdelay(5);
++      SACR0 &= ~SACR0_RST;
++
++      /* For I2S, BIT_CLK is supplied internally. The "SA-1111
++       * Specification Update" mentions that the BCKD bit should
++       * be interpreted as "0 = output". Default clock divider
++       * is 22.05kHz.
++       *
++       * Select I2S, L3 bus. "Recording" and "Replaying"
++       * (receive and transmit) are enabled.
++       */
++      SACR1 = SACR1_L3EN;
++      SKAUD = audio_clk_div - 1;
++
++      /* Initialize the UDA1341 internal state */
++      l3_open(&uda1341);
++}
++
++static void sa1111_audio_shutdown(void *dummy)
++{
++      l3_close(&uda1341);
++      SACR0 &= ~SACR0_ENB;
++}
++
++static int sa1111_audio_ioctl( struct inode *inode, struct file *file,
++                              uint cmd, ulong arg)
++{
++      long val;
++      int ret = 0;
++
++      switch (cmd) {
++      case SNDCTL_DSP_STEREO:
++              ret = get_user(val, (int *) arg);
++              if (ret)
++                      return ret;
++              /* the UDA1341 is stereo only */
++              ret = (val == 0) ? -EINVAL : 1;
++              return put_user(ret, (int *) arg);
++
++      case SNDCTL_DSP_CHANNELS:
++      case SOUND_PCM_READ_CHANNELS:
++              /* the UDA1341 is stereo only */
++              return put_user(2, (long *) arg);
++
++      case SNDCTL_DSP_SPEED:
++              ret = get_user(val, (long *) arg);
++              if (ret) break;
++              if (val < 8000) val = 8000;
++              if (val > 48000) val = 48000;
++              audio_clk_div = (AUDIO_CLK_BASE + val/2)/val;
++              SKAUD = audio_clk_div - 1;
++              /* fall through */
++
++      case SOUND_PCM_READ_RATE:
++              return put_user(AUDIO_CLK_BASE/audio_clk_div, (long *) arg);
++
++      case SNDCTL_DSP_SETFMT:
++      case SNDCTL_DSP_GETFMTS:
++              /* we can do 16-bit only */
++              return put_user(AFMT_S16_LE, (long *) arg);
++
++      default:
++              /* Maybe this is meant for the mixer (as per OSS Docs) */
++              return mixer_ioctl(inode, file, cmd, arg);
++      }
++
++      return ret;
++}
++
++static audio_stream_t output_stream, input_stream;
++
++static audio_state_t audio_state = {
++      output_stream:  &output_stream,
++      input_stream:   &input_stream,
++      skip_dma_init:  1,  /* done locally */
++      hw_init:        sa1111_audio_init,
++      hw_shutdown:    sa1111_audio_shutdown,
++      client_ioctl:   sa1111_audio_ioctl,
++      sem:            __MUTEX_INITIALIZER(audio_state.sem),
++};
++
++static int sa1111_audio_open(struct inode *inode, struct file *file)
++{
++        return sa1100_audio_attach(inode, file, &audio_state);
++}
++
++/*
++ * Missing fields of this structure will be patched with the call
++ * to sa1100_audio_attach().
++ */
++static struct file_operations sa1111_audio_fops = {
++      open:           sa1111_audio_open,
++      owner:          THIS_MODULE
++};
++
++static int audio_dev_id, mixer_dev_id;
++
++static int __init sa1111_uda1341_init(void)
++{
++      struct uda1341_cfg cfg;
++      int ret;
++
++      if ( !( (machine_is_assabet() && machine_has_neponset()) ||
++              machine_is_jornada720() ||
++              machine_is_badge4() ))
++              return -ENODEV;
++
++      if (!request_mem_region(_SACR0, 512, "sound"))
++              return -EBUSY;
++
++      ret = l3_attach_client(&uda1341, "l3-sa1111", "uda1341");
++      if (ret)
++              goto out;
++
++      /* Acquire and initialize DMA */
++      ret = sa1111_sac_request_dma(&output_stream.dma_ch, "SA1111 audio out",
++                                   SA1111_SAC_XMT_CHANNEL);
++      if (ret < 0)
++              goto release_l3;
++      
++      ret = sa1111_sac_request_dma(&input_stream.dma_ch, "SA1111 audio in",
++                                   SA1111_SAC_RCV_CHANNEL);
++      if (ret < 0)
++              goto release_dma;
++
++      cfg.fs     = 256;
++      cfg.format = FMT_I2S;
++      l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg);
++
++      /* register devices */
++      audio_dev_id = register_sound_dsp(&sa1111_audio_fops, -1);
++      mixer_dev_id = register_sound_mixer(&uda1341_mixer_fops, -1);
++
++      printk(KERN_INFO "Sound: SA1111 UDA1341: dsp id %d mixer id %d\n",
++              audio_dev_id, mixer_dev_id);
++      return 0;
++
++release_dma:
++      sa1100_free_dma(output_stream.dma_ch);
++release_l3:
++      l3_detach_client(&uda1341);
++out:
++      release_mem_region(_SACR0, 512);
++      return ret;
++}
++
++static void __exit sa1111_uda1341_exit(void)
++{
++      unregister_sound_dsp(audio_dev_id);
++      unregister_sound_mixer(mixer_dev_id);
++      sa1100_free_dma(output_stream.dma_ch);
++      sa1100_free_dma(input_stream.dma_ch);
++      l3_detach_client(&uda1341);
++
++      release_mem_region(_SACR0, 512);
++}
++
++module_init(sa1111_uda1341_init);
++module_exit(sa1111_uda1341_exit);
++
++MODULE_AUTHOR("John Dorsey, Nicolas Pitre");
++MODULE_DESCRIPTION("Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec.");
++
++EXPORT_NO_SYMBOLS;
+--- /dev/null
++++ linux-2.4.27/drivers/sound/uda1341.c
+@@ -0,0 +1,511 @@
++/*
++ * Philips UDA1341 mixer device driver
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ *
++ * History:
++ *
++ * 2000-05-21 Nicolas Pitre   Initial release.
++ *
++ * 2000-08-19 Erik Bunce      More inline w/ OSS API and UDA1341 docs
++ *                            including fixed AGC and audio source handling
++ *
++ * 2000-11-30 Nicolas Pitre   - More mixer functionalities.
++ *
++ * 2001-06-03 Nicolas Pitre   Made this file a separate module, based on
++ *                            the former sa1100-uda1341.c driver.
++ *
++ * 2001-08-13 Russell King    Re-written as part of the L3 interface
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/soundcard.h>
++#include <linux/l3/l3.h>
++#include <linux/l3/uda1341.h>
++
++#include <asm/uaccess.h>
++
++#define DEF_VOLUME    65
++
++/*
++ * UDA1341 L3 address and command types
++ */
++#define UDA1341_L3ADDR                5
++#define UDA1341_DATA0         (UDA1341_L3ADDR << 2 | 0)
++#define UDA1341_DATA1         (UDA1341_L3ADDR << 2 | 1)
++#define UDA1341_STATUS                (UDA1341_L3ADDR << 2 | 2)
++
++struct uda1341_regs {
++      unsigned char   stat0;
++#define STAT0                 0x00
++#define STAT0_RST             (1 << 6)
++#define STAT0_SC_MASK         (3 << 4)
++#define STAT0_SC_512FS                (0 << 4)
++#define STAT0_SC_384FS                (1 << 4)
++#define STAT0_SC_256FS                (2 << 4)
++#define STAT0_IF_MASK         (7 << 1)
++#define STAT0_IF_I2S          (0 << 1)
++#define STAT0_IF_LSB16                (1 << 1)
++#define STAT0_IF_LSB18                (2 << 1)
++#define STAT0_IF_LSB20                (3 << 1)
++#define STAT0_IF_MSB          (4 << 1)
++#define STAT0_IF_LSB16MSB     (5 << 1)
++#define STAT0_IF_LSB18MSB     (6 << 1)
++#define STAT0_IF_LSB20MSB     (7 << 1)
++#define STAT0_DC_FILTER               (1 << 0)
++
++      unsigned char   stat1;
++#define STAT1                 0x80
++#define STAT1_DAC_GAIN                (1 << 6)        /* gain of DAC */
++#define STAT1_ADC_GAIN                (1 << 5)        /* gain of ADC */
++#define STAT1_ADC_POL         (1 << 4)        /* polarity of ADC */
++#define STAT1_DAC_POL         (1 << 3)        /* polarity of DAC */
++#define STAT1_DBL_SPD         (1 << 2)        /* double speed playback */
++#define STAT1_ADC_ON          (1 << 1)        /* ADC powered */
++#define STAT1_DAC_ON          (1 << 0)        /* DAC powered */
++
++      unsigned char   data0_0;
++#define DATA0                 0x00
++#define DATA0_VOLUME_MASK     0x3f
++#define DATA0_VOLUME(x)               (x)
++
++      unsigned char   data0_1;
++#define DATA1                 0x40
++#define DATA1_BASS(x)         ((x) << 2)
++#define DATA1_BASS_MASK               (15 << 2)
++#define DATA1_TREBLE(x)               ((x))
++#define DATA1_TREBLE_MASK     (3)
++
++      unsigned char   data0_2;
++#define DATA2                 0x80
++#define DATA2_PEAKAFTER               (1 << 5)
++#define DATA2_DEEMP_NONE      (0 << 3)
++#define DATA2_DEEMP_32KHz     (1 << 3)
++#define DATA2_DEEMP_44KHz     (2 << 3)
++#define DATA2_DEEMP_48KHz     (3 << 3)
++#define DATA2_MUTE            (1 << 2)
++#define DATA2_FILTER_FLAT     (0 << 0)
++#define DATA2_FILTER_MIN      (1 << 0)
++#define DATA2_FILTER_MAX      (3 << 0)
++
++#define EXTADDR(n)            (0xc0 | (n))
++#define EXTDATA(d)            (0xe0 | (d))
++
++      unsigned char   ext0;
++#define EXT0                  0
++#define EXT0_CH1_GAIN(x)      (x)
++
++      unsigned char   ext1;
++#define EXT1                  1
++#define EXT1_CH2_GAIN(x)      (x)
++
++      unsigned char   ext2;
++#define EXT2                  2
++#define EXT2_MIC_GAIN_MASK    (7 << 2)
++#define EXT2_MIC_GAIN(x)      ((x) << 2)
++#define EXT2_MIXMODE_DOUBLEDIFF       (0)
++#define EXT2_MIXMODE_CH1      (1)
++#define EXT2_MIXMODE_CH2      (2)
++#define EXT2_MIXMODE_MIX      (3)
++
++      unsigned char   ext4;
++#define EXT4                  4
++#define EXT4_AGC_ENABLE               (1 << 4)
++#define EXT4_INPUT_GAIN_MASK  (3)
++#define EXT4_INPUT_GAIN(x)    ((x) & 3)
++
++      unsigned char   ext5;
++#define EXT5                  5
++#define EXT5_INPUT_GAIN(x)    ((x) >> 2)
++
++      unsigned char   ext6;
++#define EXT6                  6
++#define EXT6_AGC_CONSTANT_MASK        (7 << 2)
++#define EXT6_AGC_CONSTANT(x)  ((x) << 2)
++#define EXT6_AGC_LEVEL_MASK   (3)
++#define EXT6_AGC_LEVEL(x)     (x)
++};
++
++#define REC_MASK      (SOUND_MASK_LINE | SOUND_MASK_MIC)
++#define DEV_MASK      (REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE)
++
++struct uda1341 {
++      struct uda1341_regs regs;
++      int             active;
++      unsigned short  volume;
++      unsigned short  bass;
++      unsigned short  treble;
++      unsigned short  line;
++      unsigned short  mic;
++      int             mod_cnt;
++};
++
++#define ADD_FIELD(reg,field)                          \
++              *p++ = reg | uda->regs.field
++
++#define ADD_EXTFIELD(reg,field)                               \
++              *p++ = EXTADDR(reg);                    \
++              *p++ = EXTDATA(uda->regs.field);
++
++static void uda1341_sync(struct l3_client *clnt)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      char buf[24], *p = buf;
++
++      ADD_FIELD(STAT0, stat0);
++      ADD_FIELD(STAT1, stat1);
++
++      if (p != buf)
++              l3_write(clnt, UDA1341_STATUS, buf, p - buf);
++
++      p = buf;
++      ADD_FIELD(DATA0, data0_0);
++      ADD_FIELD(DATA1, data0_1);
++      ADD_FIELD(DATA2, data0_2);
++      ADD_EXTFIELD(EXT0, ext0);
++      ADD_EXTFIELD(EXT1, ext1);
++      ADD_EXTFIELD(EXT2, ext2);
++      ADD_EXTFIELD(EXT4, ext4);
++      ADD_EXTFIELD(EXT5, ext5);
++      ADD_EXTFIELD(EXT6, ext6);
++
++      if (p != buf)
++              l3_write(clnt, UDA1341_DATA0, buf, p - buf);
++}
++
++static void uda1341_cmd_init(struct l3_client *clnt)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      char buf[2];
++
++      uda->active = 1;
++
++      buf[0] = uda->regs.stat0 | STAT0_RST;
++      buf[1] = uda->regs.stat0;
++
++      l3_write(clnt, UDA1341_STATUS, buf, 2);
++
++      /* resend all parameters */
++      uda1341_sync(clnt);
++}
++
++static int uda1341_configure(struct l3_client *clnt, struct uda1341_cfg *conf)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      int ret = 0;
++
++      uda->regs.stat0 &= ~(STAT0_SC_MASK | STAT0_IF_MASK);
++
++      switch (conf->fs) {
++      case 512: uda->regs.stat0 |= STAT0_SC_512FS;    break;
++      case 384: uda->regs.stat0 |= STAT0_SC_384FS;    break;
++      case 256: uda->regs.stat0 |= STAT0_SC_256FS;    break;
++      default:  ret = -EINVAL;                        break;
++      }
++
++      switch (conf->format) {
++      case FMT_I2S:           uda->regs.stat0 |= STAT0_IF_I2S;        break;
++      case FMT_LSB16:         uda->regs.stat0 |= STAT0_IF_LSB16;      break;
++      case FMT_LSB18:         uda->regs.stat0 |= STAT0_IF_LSB18;      break;
++      case FMT_LSB20:         uda->regs.stat0 |= STAT0_IF_LSB20;      break;
++      case FMT_MSB:           uda->regs.stat0 |= STAT0_IF_MSB;        break;
++      case FMT_LSB16MSB:      uda->regs.stat0 |= STAT0_IF_LSB16MSB;   break;
++      case FMT_LSB18MSB:      uda->regs.stat0 |= STAT0_IF_LSB18MSB;   break;
++      case FMT_LSB20MSB:      uda->regs.stat0 |= STAT0_IF_LSB20MSB;   break;
++      }
++
++      if (ret == 0 && uda->active) {
++              char buf = uda->regs.stat0 | STAT0;
++              l3_write(clnt, UDA1341_STATUS, &buf, 1);
++      }
++      return ret;
++}
++
++static int uda1341_update_direct(struct l3_client *clnt, int cmd, void *arg)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      struct l3_gain *v = arg;
++      char newreg;
++      int val;
++
++      switch (cmd) {
++      case L3_SET_VOLUME: /* set volume.  val =  0 to 100 => 62 to 1 */
++              uda->regs.data0_0 = DATA0_VOLUME(62 - ((v->left * 61) / 100));
++              newreg = uda->regs.data0_0 | DATA0;
++              break;
++
++      case L3_SET_BASS:   /* set bass.    val = 50 to 100 => 0 to 12 */
++              val = v->left - 50;
++              if (val < 0)
++                      val = 0;
++              uda->regs.data0_1 &= ~DATA1_BASS_MASK;
++              uda->regs.data0_1 |= DATA1_BASS((val * 12) / 50);
++              newreg = uda->regs.data0_1 | DATA1;
++              break;
++
++      case L3_SET_TREBLE: /* set treble.  val = 50 to 100 => 0 to 3 */
++              val = v->left - 50;
++              if (val < 0)
++                      val = 0;
++              uda->regs.data0_1 &= ~DATA1_TREBLE_MASK;
++              uda->regs.data0_1 |= DATA1_TREBLE((val * 3) / 50);
++              newreg = uda->regs.data0_1 | DATA1;
++              break;
++
++      default:
++              return -EINVAL;
++      }               
++
++      if (uda->active)
++              l3_write(clnt, UDA1341_DATA0, &newreg, 1);
++      return 0;
++}
++
++static int uda1341_update_indirect(struct l3_client *clnt, int cmd, void *arg)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      struct l3_gain *gain = arg;
++      struct l3_agc *agc = arg;
++      char buf[8], *p = buf;
++      int val, ret = 0;
++
++      switch (cmd) {
++      case L3_SET_GAIN:
++              val = 31 - (gain->left * 31 / 100);
++              switch (gain->channel) {
++              case 1:
++                      uda->regs.ext0 = EXT0_CH1_GAIN(val);
++                      ADD_EXTFIELD(EXT0, ext0);
++                      break;
++
++              case 2:
++                      uda->regs.ext1 = EXT1_CH2_GAIN(val);
++                      ADD_EXTFIELD(EXT1, ext1);
++                      break;
++
++              default:
++                      ret = -EINVAL;
++              }
++              break;
++
++      case L3_INPUT_AGC:
++              if (agc->channel == 2) {
++                      if (agc->enable)
++                              uda->regs.ext4 |= EXT4_AGC_ENABLE;
++                      else
++                              uda->regs.ext4 &= ~EXT4_AGC_ENABLE;
++#if 0
++                      agc->level
++                      agc->attack
++                      agc->decay
++#endif
++                      ADD_EXTFIELD(EXT4, ext4);
++              } else
++                      ret = -EINVAL;
++              break;
++
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      if (ret == 0 && uda->active)
++              l3_write(clnt, UDA1341_DATA0, buf, p - buf);
++
++      return ret;
++}
++
++static int uda1341_mixer_ioctl(struct l3_client *clnt, int cmd, void *arg)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      struct l3_gain gain;
++      int val, nr = _IOC_NR(cmd), ret = 0;
++
++      if (cmd == SOUND_MIXER_INFO) {
++              struct mixer_info mi;
++
++              strncpy(mi.id, "UDA1341", sizeof(mi.id));
++              strncpy(mi.name, "Philips UDA1341", sizeof(mi.name));
++              mi.modify_counter = uda->mod_cnt;
++              return copy_to_user(arg, &mi, sizeof(mi));
++      }
++
++      if (_IOC_DIR(cmd) & _IOC_WRITE) {
++              ret = get_user(val, (int *)arg);
++              if (ret)
++                      goto out;
++
++              gain.left    = val & 255;
++              gain.right   = val >> 8;
++              gain.channel = 0;
++
++              switch (nr) {
++              case SOUND_MIXER_VOLUME:
++                      uda->volume = val;
++                      uda->mod_cnt++;
++                      uda1341_update_direct(clnt, L3_SET_VOLUME, &gain);
++                      break;
++
++              case SOUND_MIXER_BASS:
++                      uda->bass = val;
++                      uda->mod_cnt++;
++                      uda1341_update_direct(clnt, L3_SET_BASS, &gain);
++                      break;
++
++              case SOUND_MIXER_TREBLE:
++                      uda->treble = val;
++                      uda->mod_cnt++;
++                      uda1341_update_direct(clnt, L3_SET_TREBLE, &gain);
++                      break;
++
++              case SOUND_MIXER_LINE:
++                      uda->line = val;
++                      gain.channel = 1;
++                      uda->mod_cnt++;
++                      uda1341_update_indirect(clnt, L3_SET_GAIN, &gain);
++                      break;
++
++              case SOUND_MIXER_MIC:
++                      uda->mic = val;
++                      gain.channel = 2;
++                      uda->mod_cnt++;
++                      uda1341_update_indirect(clnt, L3_SET_GAIN, &gain);
++                      break;
++
++              case SOUND_MIXER_RECSRC:
++                      break;
++
++              default:
++                      ret = -EINVAL;
++              }
++      }
++
++      if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
++              int nr = _IOC_NR(cmd);
++              ret = 0;
++
++              switch (nr) {
++              case SOUND_MIXER_VOLUME:     val = uda->volume; break;
++              case SOUND_MIXER_BASS:       val = uda->bass;   break;
++              case SOUND_MIXER_TREBLE:     val = uda->treble; break;
++              case SOUND_MIXER_LINE:       val = uda->line;   break;
++              case SOUND_MIXER_MIC:        val = uda->mic;    break;
++              case SOUND_MIXER_RECSRC:     val = REC_MASK;    break;
++              case SOUND_MIXER_RECMASK:    val = REC_MASK;    break;
++              case SOUND_MIXER_DEVMASK:    val = DEV_MASK;    break;
++              case SOUND_MIXER_CAPS:       val = 0;           break;
++              case SOUND_MIXER_STEREODEVS: val = 0;           break;
++              default:        val = 0;     ret = -EINVAL;     break;
++              }
++
++              if (ret == 0)
++                      ret = put_user(val, (int *)arg);
++      }
++out:
++      return ret;
++}
++
++static int uda1341_attach(struct l3_client *clnt)
++{
++      struct uda1341 *uda;
++
++      uda = kmalloc(sizeof(*uda), GFP_KERNEL);
++      if (!uda)
++              return -ENOMEM;
++
++      memset(uda, 0, sizeof(*uda));
++
++      uda->volume = DEF_VOLUME | DEF_VOLUME << 8;
++      uda->bass   = 50 | 50 << 8;
++      uda->treble = 50 | 50 << 8;
++      uda->line   = 88 | 88 << 8;
++      uda->mic    = 88 | 88 << 8;
++
++      uda->regs.stat0   = STAT0_SC_256FS | STAT0_IF_LSB16;
++      uda->regs.stat1   = STAT1_DAC_GAIN | STAT1_ADC_GAIN |
++                          STAT1_ADC_ON | STAT1_DAC_ON;
++      uda->regs.data0_0 = DATA0_VOLUME(62 - ((DEF_VOLUME * 61) / 100));
++      uda->regs.data0_1 = DATA1_BASS(0) | DATA1_TREBLE(0);
++      uda->regs.data0_2 = DATA2_PEAKAFTER | DATA2_DEEMP_NONE |
++                          DATA2_FILTER_MAX;
++      uda->regs.ext0    = EXT0_CH1_GAIN(4);
++      uda->regs.ext1    = EXT1_CH2_GAIN(4);
++      uda->regs.ext2    = EXT2_MIXMODE_MIX | EXT2_MIC_GAIN(4);
++      uda->regs.ext4    = EXT4_AGC_ENABLE | EXT4_INPUT_GAIN(0);
++      uda->regs.ext5    = EXT5_INPUT_GAIN(0);
++      uda->regs.ext6    = EXT6_AGC_CONSTANT(3) | EXT6_AGC_LEVEL(0);
++
++      clnt->driver_data = uda;
++
++      return 0;
++}
++
++static void uda1341_detach(struct l3_client *clnt)
++{
++      kfree(clnt->driver_data);
++}
++
++static int
++uda1341_command(struct l3_client *clnt, int cmd, void *arg)
++{
++      int ret = -EINVAL;
++
++      if (_IOC_TYPE(cmd) == 'M')
++              ret = uda1341_mixer_ioctl(clnt, cmd, arg);
++      else if (cmd == L3_UDA1341_CONFIGURE)
++              ret = uda1341_configure(clnt, arg);
++
++      return ret;
++}
++
++static int uda1341_open(struct l3_client *clnt)
++{
++      uda1341_cmd_init(clnt);
++      return 0;
++}
++
++static void uda1341_close(struct l3_client *clnt)
++{
++      struct uda1341 *uda = clnt->driver_data;
++      uda->active = 0;
++}
++
++static struct l3_ops uda1341_ops = {
++      open:           uda1341_open,
++      command:        uda1341_command,
++      close:          uda1341_close,
++};
++
++static struct l3_driver uda1341 = {
++      name:           UDA1341_NAME,
++      attach_client:  uda1341_attach,
++      detach_client:  uda1341_detach,
++      ops:            &uda1341_ops,
++      owner:          THIS_MODULE,
++};
++
++static int __init uda1341_init(void)
++{
++      return l3_add_driver(&uda1341);
++}
++
++static void __exit uda1341_exit(void)
++{
++      l3_del_driver(&uda1341);
++}
++
++module_init(uda1341_init);
++module_exit(uda1341_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_DESCRIPTION("Philips UDA1341 CODEC driver");
+--- linux-2.4.27/drivers/sound/vidc.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/sound/vidc.c
+@@ -40,6 +40,7 @@
+ #endif
+ #define VIDC_SOUND_CLOCK      (250000)
++#define VIDC_SOUND_CLOCK_EXT  (176400)
+ /*
+  * When using SERIAL SOUND mode (external DAC), the number of physical
+@@ -192,28 +193,43 @@
+       return vidc_audio_format;
+ }
++#define my_abs(i) ((i)<0 ? -(i) : (i))
++
+ static int vidc_audio_set_speed(int dev, int rate)
+ {
+       if (rate) {
+-              unsigned int hwctrl, hwrate;
++              unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
+               unsigned int newsize, new2size;
+-              /*
+-               * If we have selected 44.1kHz, use the DAC clock.
+-               */
+-              if (0 && rate == 44100) {
+-                      hwctrl = 0x00000002;
++              hwctrl = 0x00000003;
++
++              /* Using internal clock */
++              hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
++              if (hwrate < 3)
+                       hwrate = 3;
+-              } else {
+-                      hwctrl = 0x00000003;
++              if (hwrate > 255)
++                      hwrate = 255;
+-                      hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
+-                      if (hwrate < 3)
+-                              hwrate = 3;
+-                      if (hwrate > 255)
+-                              hwrate = 255;
++              /* Using exernal clock */
++              hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
++              if (hwrate_ext < 3)
++                      hwrate_ext = 3;
++              if (hwrate_ext > 255)
++                      hwrate_ext = 255;
+-                      rate = VIDC_SOUND_CLOCK / hwrate;
++              rate_int = VIDC_SOUND_CLOCK / hwrate;
++              rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
++
++              /* Chose between external and internal clock */
++              if (my_abs(rate_ext-rate) < my_abs(rate_int-rate)) {
++                      /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
++                      hwrate=hwrate_ext;
++                      hwctrl=0x00000002;
++                      rate=rate_ext;
++              } else {
++                      /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
++                      hwctrl=0x00000003;
++                      rate=rate_int;
+               }
+               vidc_writel(0xb0000000 | (hwrate - 2));
+@@ -225,13 +241,14 @@
+               if (newsize > 4096)
+                       newsize = 4096;
+               for (new2size = 128; new2size < newsize; new2size <<= 1);
+-                      if (new2size - newsize > newsize - (new2size >> 1))
+-                              new2size >>= 1;
++              if (new2size - newsize > newsize - (new2size >> 1))
++                      new2size >>= 1;
+               if (new2size > 4096) {
+                       printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
+                               newsize, new2size);
+                       new2size = 4096;
+               }
++              /*printk("VIDC: dma size %d\n", new2size);*/
+               dma_bufsize = new2size;
+               vidc_audio_rate = rate;
+       }
+--- linux-2.4.27/drivers/sound/waveartist.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/sound/waveartist.c
+@@ -247,17 +247,15 @@
+               printk("\n");
+       }
+-      if (inb(io_base + STATR) & CMD_RF) {
+-              int old_data;
+-
+-              /* flush the port
+-               */
++      /*
++       * flush any stale command data from the port.
++       */
++      while (inb(io_base + STATR) & CMD_RF) {
++              unsigned int old_data;
+               old_data = inw(io_base + CMDR);
+-
+-              if (debug_flg & DEBUG_CMD)
+-                      printk("flushed %04X...", old_data);
+-
++              printk("waveartist: flushing stale command data: 0x%04x pc=%p\n",
++                     old_data, __builtin_return_address(0));
+               udelay(10);
+       }
+@@ -287,16 +285,19 @@
+                       resp[i] = inw(io_base + CMDR);
+       }
+-      if (debug_flg & DEBUG_CMD) {
+-              if (!timed_out) {
+-                      printk("waveartist_cmd: resp=");
++      if (debug_flg & DEBUG_CMD && !timed_out) {
++              printk("waveartist_cmd: resp=");
+-                      for (i = 0; i < nr_resp; i++)
+-                              printk("%04X ", resp[i]);
++              for (i = 0; i < nr_resp; i++)
++                      printk("%04X ", resp[i]);
++              printk("\n");
++      }
+-                      printk("\n");
+-              } else
+-                      printk("waveartist_cmd: timed out\n");
++      if (timed_out) {
++              printk(KERN_ERR "waveartist_cmd: command timed out:");
++              for (i = 0; i < nr_cmd; i++)
++                      printk(" %04x", cmd[i]);
++              printk("\n");
+       }
+       return timed_out ? 1 : 0;
+@@ -495,7 +496,6 @@
+           audio_devs[dev]->flags & DMA_AUTOMODE &&
+           intrflag &&
+           count == devc->xfer_count) {
+-              devc->audio_mode |= PCM_ENABLE_INPUT;
+               return; /*
+                        * Auto DMA mode on. No need to react
+                        */
+@@ -571,9 +571,6 @@
+       wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
+       unsigned int    speed, bits;
+-      if (devc->audio_mode)
+-              return 0;
+-
+       speed = waveartist_get_speed(portc);
+       bits  = waveartist_get_bits(portc);
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/Config.in
+@@ -0,0 +1,11 @@
++
++mainmenu_option next_comment
++comment 'Synchronous Serial Interface'
++tristate 'Synchronous Serial Interface Support' CONFIG_SSI
++
++comment 'SSI Bus Drivers'
++dep_tristate '  CLPS711X SSI support' CONFIG_SSI_CLPS711X $CONFIG_SSI $CONFIG_ARCH_CLPS711X
++
++comment 'SSI Device Drivers'
++dep_tristate '  JUNO keyboard support' CONFIG_SSI_JUNO $CONFIG_SSI
++endmenu
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/Makefile
+@@ -0,0 +1,50 @@
++#
++# Makefile for the SSI 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 definition is now inherited from the
++# parent makefile.
++#
++
++O_TARGET      := ssi.o
++
++obj-y         :=
++obj-m         :=
++obj-n         :=
++obj-          :=
++
++export-objs   :=
++list-multi    :=
++
++obj-$(CONFIG_SSI)             += ssi_core.o
++obj-$(CONFIG_SSI_CLPS711X)    += clps711x_ssi1.o
++obj-y                         += juno.o
++
++# Extract lists of the multi-part drivers.
++# The 'int-*' lists are intermediate files used to build the multi's.
++
++multi-y               := $(filter $(list-multi), $(obj-y))
++multi-m               := $(filter $(list-multi), $(obj-m))
++int-y         := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
++int-m         := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
++
++# Files that are both resident and modular; remove from modular.
++
++obj-m         := $(filter-out $(obj-y), $(obj-m))
++int-m         := $(filter-out $(int-y), $(int-m))
++
++# Take multi-part drivers out of obj-y and put components in.
++
++obj-y         := $(filter-out $(list-multi), $(obj-y)) $(int-y)
++
++# Translate to Rules.make lists.
++
++O_OBJS                := $(filter-out $(export-objs), $(obj-y))
++OX_OBJS               := $(filter     $(export-objs), $(obj-y))
++M_OBJS                := $(sort $(filter-out $(export-objs), $(obj-m)))
++MX_OBJS               := $(sort $(filter     $(export-objs), $(obj-m)))
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/README
+@@ -0,0 +1,86 @@
++              Synchronous Serial Interface bus driver
++              ---------------------------------------
++
++   EEEEE X   X PPPP  EEEEE RRRR  IIIII M   M EEEEE N   N TTTTT  AAA  L
++   E      X X  P   P E     R   R   I   MM MM E     NN  N   T   A   A L
++   EEEE    X   PPPP  EEEE  RRRR    I   M M M EEEE  N N N   T   AAAAA L
++   E      X X  P     E     R R     I   M   M E     N  NN   T   A   A L
++   EEEEE X   X P     EEEEE R  R  IIIII M   M EEEEE N   N   T   A   A LLLLL
++
++This directory holds the SSI bus drivers.  Basically, a SSI bus consists
++of the following signals:
++
++      stxd    Transmit data
++      srxd    Receive data
++      sclk    Clock
++      sfrm    Frame
++              Chip selects (1 - n)
++
++There may be more than one device on a SSI bus, and each device can
++have different timing requirements.  There are several frame formats:
++
++1. Texas Instruments Synchronous Serial Frame format
++
++   sclk ____~_~_~_~_~_~_~_~____
++   sfrm ____~~_________________
++   stxd ------bn..........b0---
++   srxd ------bn..........b0---
++
++   - data latched in on the falling edge of the clock
++   - data shifted out on the rising edge of the clock
++
++2. Motorola SPI frame format
++
++   sclk ______~_~_~_~_~_~_~____
++   sfrm ~~~~________________~~~
++   stxd -----bn..........b0----
++   srxd ----.bn..........b0----
++
++   - data latched in on the rising edge of the clock
++   - data shifted out on the falling edge of the clock, or falling edge
++     of sfrm
++
++3. National Microwire format
++
++   sclk ______~_~_~_~_~_~_~_~_~_~_~_~_~_____
++   sfrm ~~~~_____________________________~~~
++   stxd -----bn......b0---------------------
++   srxd -----------------bn..........b0.----
++
++   - data latched in on the rising edge of the clock
++   - data shifted out on the falling edge of the clock
++   - half duplex, one clock between transmission and reception
++
++Types of devices
++----------------
++
++The following types of devices can be found on a SSP bus:
++
++      Sound chips
++      Keyboard devices
++      Touch screen devices
++
++Keyboard
++--------
++
++TX:
++0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz
++1. select device
++2. keyboard responds asserting key_atn
++3. wait 0.1ms to 5ms
++4. transmit data byte
++5. wait >= 150us
++6. repeat step 4 until all data sent
++7. deselect device
++8. keyboard responds de-asserting key_atn
++9. wait >= 120us
++
++RX:
++0. Format: cfglen = 8, framelen = 8, clkpol = 1, clk < 250kHz
++1. keyboard asserts key_atn
++2. select device after 0.1ms < 5ms
++3. read data byte
++4. wait 150us
++5. if key_atn still asserted, goto 3
++6. deselect device
++7. wait >= 120us
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/clps711x_ssi1.c
+@@ -0,0 +1,237 @@
++/*
++ *  linux/drivers/ssi/clps711x_ssi1.c
++ *
++ * SSI bus driver for the CLPS711x SSI1 bus.  We support EP7212
++ * extensions as well.
++ *
++ * Frame sizes can be between 4 and 24 bits.
++ * Config sizes can be between 4 and 16 bits.
++ */
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++
++#include <asm/mach-types.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/hardware/ep7212.h>
++
++#include "ssi_bus.h"
++#include "ssi_dev.h"
++
++#define EP7212
++
++/*
++ * Port E on the P720T is used for the SPI bus chip selects
++ *  0 - Keyboard
++ *  1 - Touch screen
++ *  2 - CS4224 ADC/DAC
++ *  7 - idle
++ */
++
++#if 0
++/*
++ * we place in the transmit buffer:
++ *  <control>
++ * received data (binary):
++ *  0xxxxxxxxxxxx000
++ * where 'x' is the value
++ */
++struct ssi_dev ads7846_dev = {
++      name:           "ADS7846",
++      id:             1,
++      proto:          SSI_SPI,
++      cfglen:         8,
++      framelen:       24,
++      clkpol:         0,
++      clkfreq:        2500000,
++};
++
++/*
++ * we place in the transmit buffer:
++ *  write: <20> <map> <data>...
++ * received data discarded
++ */
++struct ssi_dev cs4224_dev = {
++      name:           "CS4224",
++      id:             2,
++      proto:          SSI_SPI,
++      cfglen:         8,
++      framelen:       8,
++      clkpol:         0,
++      clkfreq:        6000000,
++};
++#endif
++
++/*
++ * Supplement with whatever method your board requires
++ */
++static void ssi1_select_id(int id)
++{
++      if (machine_is_p720t()) {
++              clps_writel(7, PEDDR);
++              clps_writel(id, PEDR);
++      }
++}
++
++/*
++ * Select the specified device.  The upper layers will have already
++ * checked that the bus transmit queue is idle.  We need to make sure
++ * that the interface itself is idle.
++ */
++static int ssi1_select(struct ssi_bus *bus, struct ssi_dev *dev)
++{
++      u_int id = dev ? dev->id : 7;
++      u_int val;
++
++      /*
++       * Make sure that the interface is idle
++       */
++      do {
++              val = clps_readl(SYSFLG1);
++      } while (val & SYSFLG1_SSIBUSY);
++
++      ssi1_select_id(7);
++
++      if (dev) {
++              /*
++               * Select clock frequency.  This is very rough,
++               * and assumes that we're operating in PLL mode.
++               */
++              val = clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK;
++//            if (dev->clkfreq <= 16000)              /* <16kHz */
++//                    val |= SYSCON1_ADCKSEL(0);
++//            else if (dev->clkfreq < 64000)          /* <64kHz */
++//                    val |= SYSCON1_ADCKSEL(1);
++//            else if (dev->clkfreq < 128000)         /* <128kHz */
++                      val |= SYSCON1_ADCKSEL(2);
++//            else                                    /* >= 128kHz */
++//                    val |= SYSCON1_ADCKSEL(3);
++              clps_writel(val, SYSCON1);
++
++              bus->cfglen     = dev->cfglen;
++              bus->framelen   = dev->framelen;
++              bus->clkpol     = dev->clkpol;
++              bus->proto      = dev->proto;
++
++#ifdef EP7212
++              /*
++               * Set the clock edge according to the device.
++               * (set clkpol if the device reads data on the
++               * falling edge of the clock signal).
++               */
++              val = ep_readl(SYSCON3) & ~SYSCON3_ADCCKNSEN;
++              if (bus->clkpol && dev->proto != SSI_USAR)
++                      val |= SYSCON3_ADCCKNSEN;
++              ep_writel(val, SYSCON3);
++#endif
++
++              /*
++               * Select the device
++               */
++              ssi1_select_id(id);
++
++#ifdef EP7212
++              /*
++               * If we are doing USAR, wait 30us, then set
++               * the clock line low.
++               */
++              if (dev->proto == SSI_USAR) {
++                      udelay(150);
++
++                      val |= SYSCON3_ADCCKNSEN;
++                      ep_writel(val, SYSCON3);
++              }
++#endif
++      }
++
++      return 0;
++}
++
++static void ssi1_int(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct ssi_bus *bus = (struct ssi_bus *)dev_id;
++
++      /*
++       * Read the data word and queue it.
++       */
++      ssi_core_rcv(bus, clps_readl(SYNCIO));
++}
++
++/*
++ * Enable transmission and/or of some bytes
++ */
++static int ssi1_trans(struct ssi_bus *bus, u_int data)
++{
++      u_int syncio;
++
++#ifdef EP7212
++      data <<= 32 - bus->cfglen;
++      syncio = bus->cfglen | bus->framelen << 7 | data;
++#else
++      syncio = data | bus->framelen << 8;
++#endif
++
++      clps_writel(syncio, SYNCIO);
++      clps_writel(syncio | SYNCIO_TXFRMEN, SYNCIO);
++      return 0;
++}
++
++/*
++ * Initialise the SSI bus.
++ */
++static int ssi1_bus_init(struct ssi_bus *bus)
++{
++      int retval, val;
++
++      retval = request_irq(IRQ_SSEOTI, ssi1_int, 0, "ssi1", bus);
++      if (retval)
++              return retval;
++
++#ifdef EP7212
++      /*
++       * EP7212 only!  Set the configuration command length.
++       * On the CLPS711x chips, it is fixed at 8 bits.
++       */
++      val = ep_readl(SYSCON3);
++      val |= SYSCON3_ADCCON;
++      ep_writel(val, SYSCON3);
++#endif
++
++      ssi1_select(bus, NULL);
++
++      PLD_SPI |= PLD_SPI_EN;
++
++      return 0;
++}
++
++static void ssi1_bus_exit(struct ssi_bus *bus)
++{
++      ssi1_select(bus, NULL);
++
++      PLD_SPI &= ~PLD_SPI_EN;
++
++      free_irq(IRQ_SSEOTI, bus);
++}
++
++struct ssi_bus clps711x_ssi1_bus = {
++      name:   "clps711x_ssi1",
++      init:   ssi1_bus_init,
++      exit:   ssi1_bus_exit,
++      select: ssi1_select,
++      trans:  ssi1_trans,
++};
++
++static int __init clps711x_ssi1_init(void)
++{
++      return ssi_register_bus(&clps711x_ssi1_bus);
++}
++
++static void __exit clps711x_ssi1_exit(void)
++{
++      ssi_unregister_bus(&clps711x_ssi1_bus);
++}
++
++module_init(clps711x_ssi1_init);
++module_exit(clps711x_ssi1_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/juno.c
+@@ -0,0 +1,131 @@
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/arch/syspld.h>
++
++#include "ssi_bus.h"
++#include "ssi_dev.h"
++
++extern struct ssi_bus clps711x_ssi1_bus;
++
++static u_int recvbuf[16];
++static volatile u_int ptr, rxed;
++
++static inline void juno_enable_irq(void)
++{
++      enable_irq(IRQ_EINT1);
++}
++
++static inline void juno_disable_irq(void)
++{
++      disable_irq(IRQ_EINT1);
++}
++
++static void juno_rcv(struct ssi_dev *dev, u_int data)
++{
++      if (ptr < 16) {
++              recvbuf[ptr] = data;
++              ptr++;
++      } else
++              printk("juno_rcv: %04x\n", data);
++      rxed = 1;
++}
++
++static void juno_irq(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct ssi_dev *dev = dev_id;
++
++      printk("juno_irq\n");
++
++      ssi_select_device(dev->bus, dev);
++
++      ptr = 0;
++      do {
++              rxed = 0;
++              ssi_transmit_data(dev, 0xff);
++              while (rxed == 0);
++              udelay(150);
++      } while (PLD_INT & PLD_INT_KBD_ATN);
++
++      ssi_select_device(dev->bus, NULL);
++
++      { int i;
++        printk("juno_rcv: ");
++        for (i = 0; i < ptr; i++)
++              printk("%04x ", recvbuf[i]);
++        printk("\n");
++      }
++}
++
++static void juno_command(struct ssi_dev *dev, int cmd, int data)
++{
++      ssi_transmit_data(dev, cmd);
++      mdelay(1);
++      ssi_transmit_data(dev, data);
++      mdelay(1);
++      ssi_transmit_data(dev, 0xa0 ^ 0xc0);
++      mdelay(1);
++}
++
++static int juno_dev_init(struct ssi_dev *dev)
++{
++      int retval;
++
++      PLD_KBD |= PLD_KBD_EN;
++      ptr = 16;
++
++      mdelay(20);
++
++      retval = request_irq(IRQ_EINT1, juno_irq, 0, dev->name, dev);
++      if (retval)
++              return retval;
++
++      juno_disable_irq();
++
++      if (ssi_select_device(dev->bus, dev) != 0) {
++              printk("juno: ssi_select_dev failed\n");
++              return -EBUSY;
++      }
++
++      mdelay(1);
++
++      juno_command(dev, 0x80, 0x20);
++
++      ssi_select_device(dev->bus, NULL);
++
++      juno_enable_irq();
++
++      return 0;
++}
++
++static struct ssi_dev juno_dev = {
++      name:           "Juno",
++      id:             0,
++      proto:          SSI_USAR,
++      cfglen:         8,
++      framelen:       8,
++      clkpol:         1,
++      clkfreq:        250000,
++      rcv:            juno_rcv,
++      init:           juno_dev_init,
++};
++
++static int __init juno_init(void)
++{
++      return ssi_register_device(&clps711x_ssi1_bus, &juno_dev);
++}
++
++static void __exit juno_exit(void)
++{
++      ssi_unregister_device(&juno_dev);
++}
++
++module_init(juno_init);
++module_exit(juno_exit);
++
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/ssi_bus.h
+@@ -0,0 +1,21 @@
++#include <linux/circ_buf.h>
++
++struct ssi_dev;
++
++struct ssi_bus {
++      u_char          cfglen;
++      u_char          framelen;
++      u_char          clkpol;
++      u_char          proto;
++      struct ssi_dev  *dev;           /* current device */
++      int             (*select)(struct ssi_bus *, struct ssi_dev *);
++      int             (*trans)(struct ssi_bus *, u_int data);
++      int             (*init)(struct ssi_bus *);
++      void            (*exit)(struct ssi_bus *);
++      char            *name;
++      u_int           devices;
++};
++
++extern int ssi_core_rcv(struct ssi_bus *bus, u_int data);
++extern int ssi_register_bus(struct ssi_bus *bus);
++extern int ssi_unregister_bus(struct ssi_bus *bus);
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/ssi_core.c
+@@ -0,0 +1,175 @@
++/*
++ *  linux/drivers/ssi/ssi_core.c
++ *
++ * This file provides a common framework to allow multiple SSI devices
++ * to work together on a single SSI bus.
++ *
++ * You can use this in two ways:
++ *  1. select the device, queue up data, flush the data to the device,
++ *     (optionally) purge the received data, deselect the device.
++ *  2. select the device, queue up one data word, flush to the device
++ *     read data word, queue up next data word, flush to the device...
++ *     deselect the device.
++ */
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/kernel.h>
++#include <linux/malloc.h>
++#include <linux/init.h>
++
++#include <asm/errno.h>
++
++#include "ssi_bus.h"
++#include "ssi_dev.h"
++
++#define DEBUG
++
++/**
++ *    ssi_core_rcv - pass received SSI data to the device
++ *    @bus: the bus that the data was received from
++ *    @data: the data word that was received
++ *
++ *    This function is intended to be called by SSI bus device
++ *    drivers to pass received data to the device driver.
++ */
++int ssi_core_rcv(struct ssi_bus *bus, u_int data)
++{
++      struct ssi_dev *dev = bus->dev;
++
++      if (dev && dev->rcv)
++              dev->rcv(dev, data);
++
++      return 0;
++}
++
++/**
++ *    ssi_transmit_data - queue SSI data for later transmission
++ *    @dev: device requesting data to be transmitted
++ *    @data: data word to be transmitted.
++ *
++ *    Queue one data word of SSI data for later transmission.
++ */
++int ssi_transmit_data(struct ssi_dev *dev, u_int data)
++{
++      struct ssi_bus *bus = dev->bus;
++
++      /*
++       * Make sure that we currently own the bus
++       */
++      if (bus->dev != dev)
++              BUG();
++
++      bus->trans(bus, data);
++      return 0;
++}
++
++/**
++ *    ssi_select_device - select a SSI device for later transactions
++ *    @dev: device to be selected
++ */
++int ssi_select_device(struct ssi_bus *bus, struct ssi_dev *dev)
++{
++      int retval;
++
++#ifdef DEBUG
++      printk("SSI: selecting device %s on bus %s\n",
++              dev ? dev->name : "<none>", bus->name);
++#endif
++
++      /*
++       * Select the device if it wasn't already selected.
++       */
++      retval = 0;
++      if (bus->dev != dev) {
++              retval = bus->select(bus, dev);
++              bus->dev = dev;
++      }
++
++      return retval;
++}
++
++/**
++ *    ssi_register_device - register a SSI device with a SSI bus
++ *    @bus: bus
++ *    @dev: SSI device
++ */
++int ssi_register_device(struct ssi_bus *bus, struct ssi_dev *dev)
++{
++      int retval;
++
++      dev->bus = bus;
++      bus->devices++;
++      retval = dev->init(dev);
++      if (retval != 0) {
++              dev->bus = NULL;
++              bus->devices--;
++      } else {
++#ifdef DEBUG
++              printk("SSI: registered new device %s on bus %s\n", dev->name, bus->name);
++#endif
++      }
++      return retval;
++}
++
++/**
++ *    ssi_unregister_device - unregister a SSI device from a SSI bus
++ *    @dev: SSI device
++ */
++int ssi_unregister_device(struct ssi_dev *dev)
++{
++      struct ssi_bus *bus = dev->bus;
++
++      if (bus->dev == dev)
++              bus->dev = NULL;
++
++      dev->bus = NULL;
++      bus->devices--;
++#ifdef DEBUG
++      printk("SSI: unregistered device %s on bus %s\n", dev->name, bus->name);
++#endif
++      return 0;
++}
++
++/**
++ *    ssi_register_bus - register a SSI bus driver
++ *    @bus: bus
++ */
++int ssi_register_bus(struct ssi_bus *bus)
++{
++      int retval;
++
++      retval = bus->init(bus);
++      if (retval == 0) {
++              bus->devices = 0;
++#ifdef DEBUG
++              printk("SSI: registered new bus %s\n", bus->name);
++#endif
++      }
++
++      return retval;
++}
++
++/**
++ *    ssi_unregister_bus - unregister a SSI bus driver
++ *    @bus: bus
++ */
++int ssi_unregister_bus(struct ssi_bus *bus)
++{
++      int retval = -EBUSY;
++      if (bus->devices == 0) {
++              retval = 0;
++      }
++      return retval;
++}
++
++static int __init ssi_init(void)
++{
++      return 0;
++}
++
++static void __exit ssi_exit(void)
++{
++}
++
++module_init(ssi_init);
++module_exit(ssi_exit);
+--- /dev/null
++++ linux-2.4.27/drivers/ssi/ssi_dev.h
+@@ -0,0 +1,21 @@
++struct ssi_bus;
++
++#define SSI_SPI               1
++#define SSI_MICROWIRE 2
++#define SSI_TISSF     3
++#define SSI_USAR      4
++
++struct ssi_dev {
++      char            *name;
++      u_int           id;
++      u_int           clkfreq;
++      u_char          cfglen;
++      u_char          framelen;
++      u_char          clkpol;
++      u_char          proto;
++      void            (*rcv)(struct ssi_dev *, u_int);
++      int             (*init)(struct ssi_dev *);
++      struct ssi_bus  *bus;
++};
++
++
+--- linux-2.4.27/drivers/usb/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/Config.in
+@@ -4,7 +4,13 @@
+ mainmenu_option next_comment
+ comment 'USB support'
+-dep_tristate 'Support for USB' CONFIG_USB $CONFIG_PCI
++# 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
++   tristate 'Support for USB' CONFIG_USB
++else
++   define_bool CONFIG_USB n
++fi
++
+ if [ "$CONFIG_USB" = "y" -o  "$CONFIG_USB" = "m" ]; then
+    bool '  USB verbose debug messages' CONFIG_USB_DEBUG
+--- linux-2.4.27/drivers/usb/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/Makefile
+@@ -74,6 +74,14 @@
+ subdir-$(CONFIG_USB_OHCI)     += host
+ ifeq ($(CONFIG_USB_OHCI),y)
++      obj-y += host/usb-ohci.o host/usb-ohci-pci.o
++endif
++subdir-$(CONFIG_USB_OHCI_SA1111)+= host
++ifeq ($(CONFIG_USB_OHCI),y)
++      obj-y += host/usb-ohci.o host/usb-ohci-sa1111.o
++endif
++subdir-$(CONFIG_USB_OHCI_AT91)        += host
++ifeq ($(CONFIG_USB_OHCI_AT91),y)
+       obj-y += host/usb-ohci.o
+ endif
+@@ -85,8 +93,6 @@
+ obj-$(CONFIG_USB_KBD)         += usbkbd.o
+ obj-$(CONFIG_USB_AIPTEK)      += aiptek.o
+ obj-$(CONFIG_USB_WACOM)               += wacom.o
+-obj-$(CONFIG_USB_KBTAB)               += kbtab.o
+-obj-$(CONFIG_USB_POWERMATE)   += powermate.o
+ obj-$(CONFIG_USB_SCANNER)     += scanner.o
+ obj-$(CONFIG_USB_ACM)         += acm.o
+--- linux-2.4.27/drivers/usb/hcd.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/hcd.c
+@@ -96,7 +96,7 @@
+ /* used when updating hcd data */
+ static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+-static struct usb_operations hcd_operations;
++/*static*/ struct usb_operations hcd_operations;
+ /*-------------------------------------------------------------------------*/
+@@ -1208,13 +1208,21 @@
+       } else {
+               if (usb_pipecontrol (urb->pipe))
+                       urb->setup_dma = pci_map_single (
++#ifdef CONFIG_PCI
+                                       hcd->pdev,
++#else
++                                      NULL,
++#endif
+                                       urb->setup_packet,
+                                       sizeof (struct usb_ctrlrequest),
+                                       PCI_DMA_TODEVICE);
+               if (urb->transfer_buffer_length != 0)
+                       urb->transfer_dma = pci_map_single (
++#ifdef CONFIG_PCI
+                                       hcd->pdev,
++#else
++                                      NULL,
++#endif
+                                       urb->transfer_buffer,
+                                       urb->transfer_buffer_length,
+                                       usb_pipein (urb->pipe)
+@@ -1424,7 +1432,7 @@
+       return 0;
+ }
+-static struct usb_operations hcd_operations = {
++/*static*/ struct usb_operations hcd_operations = {
+       allocate:               hcd_alloc_dev,
+       get_frame_number:       hcd_get_frame_number,
+       submit_urb:             hcd_submit_urb,
+@@ -1434,7 +1442,7 @@
+ /*-------------------------------------------------------------------------*/
+-static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
++/*static*/ void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+ {
+       struct usb_hcd          *hcd = __hcd;
+       int                     start = hcd->state;
+@@ -1490,12 +1498,24 @@
+       
+       /* For 2.4, don't unmap bounce buffer if it's a root hub operation. */
+       if (usb_pipecontrol (urb->pipe) && !is_root_hub_operation)
+-              pci_unmap_single (hcd->pdev, urb->setup_dma,
++              pci_unmap_single (
++#ifdef CONFIG_PCI
++                              hcd->pdev,
++#else
++                              NULL,
++#endif
++                              urb->setup_dma,
+                               sizeof (struct usb_ctrlrequest),
+                               PCI_DMA_TODEVICE);
+       if ((urb->transfer_buffer_length != 0) && !is_root_hub_operation)
+-              pci_unmap_single (hcd->pdev, urb->transfer_dma,
++              pci_unmap_single (
++#ifdef CONFIG_PCI
++                              hcd->pdev,
++#else
++                              NULL,
++#endif
++                              urb->transfer_dma,
+                               urb->transfer_buffer_length,
+                               usb_pipein (urb->pipe)
+                                   ? PCI_DMA_FROMDEVICE
+--- linux-2.4.27/drivers/usb/host/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/host/Config.in
+@@ -12,8 +12,13 @@
+    define_bool CONFIG_USB_UHCI_ALT n
+ 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" -a "$CONFIG_X86_64" != "y" ]; then
+    # Cypress embedded USB controller on StrongARM or on x86 in PC/104
+    dep_tristate '  SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL
+    dep_tristate '  SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $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.27/drivers/usb/host/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/host/Makefile
+@@ -8,9 +8,11 @@
+ obj-$(CONFIG_USB_EHCI_HCD)                    += ehci-hcd.o
+ obj-$(CONFIG_USB_UHCI_ALT)                    += uhci.o
+ obj-$(CONFIG_USB_UHCI)                                += usb-uhci.o
+-obj-$(CONFIG_USB_OHCI)                                += usb-ohci.o
++obj-$(CONFIG_USB_OHCI)                                += usb-ohci.o usb-ohci-pci.o
+ obj-$(CONFIG_USB_SL811HS_ALT)                         += sl811.o
+ obj-$(CONFIG_USB_SL811HS)                     += hc_sl811.o
++obj-$(CONFIG_USB_OHCI_SA1111)                 += usb-ohci.o usb-ohci-sa1111.o
++obj-$(CONFIG_USB_OHCI_AT91)                   += usb-ohci.o
+ # Extract lists of the multi-part drivers.
+ # The 'int-*' lists are the intermediate files used to build the multi's.
+--- /dev/null
++++ linux-2.4.27/drivers/usb/host/usb-ohci-pci.c
+@@ -0,0 +1,436 @@
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>  /* for in_interrupt() */
++#undef DEBUG
++#include <linux/usb.h>
++
++#include "usb-ohci.h"
++
++#ifdef CONFIG_PMAC_PBOOK
++#include <asm/machdep.h>
++#include <asm/pmac_feature.h>
++#include <asm/pci-bridge.h>
++#ifndef CONFIG_PM
++#define CONFIG_PM
++#endif
++#endif
++
++
++/*-------------------------------------------------------------------------*/
++
++/* Increment the module usage count, start the control thread and
++ * return success. */
++
++static struct pci_driver ohci_pci_driver;
++extern spinlock_t usb_ed_lock;
++int __devinit
++hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
++          const char *name, const char *slot_name);
++extern void hc_remove_ohci(ohci_t *ohci);
++
++static int __devinit
++hc_found_ohci (struct pci_dev *dev, int irq,
++      void *mem_base, const struct pci_device_id *id)
++{
++      unsigned long flags = id->driver_data;
++      
++      printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
++      
++      /* Check for NSC87560. We have to look at the bridge (fn1) to identify
++         the USB (fn2). This quirk might apply to more or even all NSC stuff
++         I don't know.. */
++         
++      if(dev->vendor == PCI_VENDOR_ID_NS)
++      {
++              struct pci_dev *fn1  = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
++              if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO)
++                      flags |= OHCI_QUIRK_SUCKYIO;
++              
++      }
++      
++      if (flags & OHCI_QUIRK_SUCKYIO)
++              printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
++      if (flags & OHCI_QUIRK_AMD756)
++              printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
++
++      return hc_add_ohci(dev, irq, mem_base, flags,
++                         ohci_pci_driver.name, dev->slot_name);
++}
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef        CONFIG_PM
++
++/* controller died; cleanup debris, then restart */
++/* must not be called from interrupt context */
++
++static void hc_restart (ohci_t *ohci)
++{
++      int temp;
++      int i;
++
++      if (ohci->pci_latency)
++              pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
++
++      ohci->disabled = 1;
++      ohci->sleeping = 0;
++      if (ohci->bus->root_hub)
++              usb_disconnect (&ohci->bus->root_hub);
++      
++      /* empty the interrupt branches */
++      for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
++      for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
++      
++      /* no EDs to remove */
++      ohci->ed_rm_list [0] = NULL;
++      ohci->ed_rm_list [1] = NULL;
++
++      /* empty control and bulk lists */       
++      ohci->ed_isotail     = NULL;
++      ohci->ed_controltail = NULL;
++      ohci->ed_bulktail    = NULL;
++
++      if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
++              err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
++      } else
++              dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
++}
++
++#endif        /* CONFIG_PM */
++
++/*-------------------------------------------------------------------------*/
++
++/* configured so that an OHCI device is always provided */
++/* always called with process context; sleeping is OK */
++
++static int __devinit
++ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
++{
++      unsigned long mem_resource, mem_len;
++      void *mem_base;
++      int status;
++
++      if (pci_enable_device(dev) < 0)
++              return -ENODEV;
++
++        if (!dev->irq) {
++              err("found OHCI device with no IRQ assigned. check BIOS settings!");
++              pci_disable_device (dev);
++              return -ENODEV;
++        }
++      
++      /* we read its hardware registers as memory */
++      mem_resource = pci_resource_start(dev, 0);
++      mem_len = pci_resource_len(dev, 0);
++      if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
++              dbg ("controller already in use");
++              pci_disable_device (dev);
++              return -EBUSY;
++      }
++
++      mem_base = ioremap_nocache (mem_resource, mem_len);
++      if (!mem_base) {
++              err("Error mapping OHCI memory");
++              release_mem_region (mem_resource, mem_len);
++              pci_disable_device (dev);
++              return -EFAULT;
++      }
++
++      /* controller writes into our memory */
++      pci_set_master (dev);
++
++      status = hc_found_ohci (dev, dev->irq, mem_base, id);
++      if (status < 0) {
++              iounmap (mem_base);
++              release_mem_region (mem_resource, mem_len);
++              pci_disable_device (dev);
++      }
++      return status;
++} 
++
++/*-------------------------------------------------------------------------*/
++
++/* may be called from interrupt context [interface spec] */
++/* may be called without controller present */
++/* may be called with controller, bus, and devices active */
++
++static void __devexit
++ohci_pci_remove (struct pci_dev *dev)
++{
++      ohci_t          *ohci = (ohci_t *) pci_get_drvdata(dev);
++
++      dbg ("remove %s controller usb-%s%s%s",
++              hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
++              dev->slot_name,
++              ohci->disabled ? " (disabled)" : "",
++              in_interrupt () ? " in interrupt" : ""
++              );
++
++      hc_remove_ohci(ohci);
++
++      release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
++      pci_disable_device (dev);
++}
++
++
++#ifdef        CONFIG_PM
++
++/*-------------------------------------------------------------------------*/
++
++static int
++ohci_pci_suspend (struct pci_dev *dev, u32 state)
++{
++      ohci_t                  *ohci = (ohci_t *) pci_get_drvdata(dev);
++      unsigned long           flags;
++      u16 cmd;
++
++      if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
++              dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
++                      hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
++              return -EIO;
++      }
++
++      /* act as if usb suspend can always be used */
++      info ("USB suspend: usb-%s", dev->slot_name);
++      ohci->sleeping = 1;
++
++      /* First stop processing */
++      spin_lock_irqsave (&usb_ed_lock, flags);
++      ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
++      writel (ohci->hc_control, &ohci->regs->control);
++      writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
++      (void) readl (&ohci->regs->intrstatus);
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
++
++      /* Wait a frame or two */
++      mdelay(1);
++      if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
++              mdelay (1);
++              
++#ifdef CONFIG_PMAC_PBOOK
++      if (_machine == _MACH_Pmac)
++              disable_irq (ohci->irq);
++      /* else, 2.4 assumes shared irqs -- don't disable */
++#endif
++      /* Enable remote wakeup */
++      writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
++
++      /* Suspend chip and let things settle down a bit */
++      ohci->hc_control = OHCI_USB_SUSPEND;
++      writel (ohci->hc_control, &ohci->regs->control);
++      (void) readl (&ohci->regs->control);
++      mdelay (500); /* No schedule here ! */
++      switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
++              case OHCI_USB_RESET:
++                      dbg("Bus in reset phase ???");
++                      break;
++              case OHCI_USB_RESUME:
++                      dbg("Bus in resume phase ???");
++                      break;
++              case OHCI_USB_OPER:
++                      dbg("Bus in operational phase ???");
++                      break;
++              case OHCI_USB_SUSPEND:
++                      dbg("Bus suspended");
++                      break;
++      }
++      /* In some rare situations, Apple's OHCI have happily trashed
++       * memory during sleep. We disable it's bus master bit during
++       * suspend
++       */
++      pci_read_config_word (dev, PCI_COMMAND, &cmd);
++      cmd &= ~PCI_COMMAND_MASTER;
++      pci_write_config_word (dev, PCI_COMMAND, cmd);
++#ifdef CONFIG_PMAC_PBOOK
++      {
++              struct device_node      *of_node;
++
++              /* Disable USB PAD & cell clock */
++              of_node = pci_device_to_OF_node (ohci->ohci_dev);
++              if (of_node)
++                      pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
++      }
++#endif
++      return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++ohci_pci_resume (struct pci_dev *dev)
++{
++      ohci_t          *ohci = (ohci_t *) pci_get_drvdata(dev);
++      int             temp;
++      unsigned long   flags;
++
++      /* guard against multiple resumes */
++      atomic_inc (&ohci->resume_count);
++      if (atomic_read (&ohci->resume_count) != 1) {
++              err ("concurrent PCI resumes for usb-%s", dev->slot_name);
++              atomic_dec (&ohci->resume_count);
++              return 0;
++      }
++
++#ifdef CONFIG_PMAC_PBOOK
++      {
++              struct device_node *of_node;
++
++              /* Re-enable USB PAD & cell clock */
++              of_node = pci_device_to_OF_node (ohci->ohci_dev);
++              if (of_node)
++                      pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
++      }
++#endif
++
++      /* did we suspend, or were we powered off? */
++      ohci->hc_control = readl (&ohci->regs->control);
++      temp = ohci->hc_control & OHCI_CTRL_HCFS;
++
++#ifdef DEBUG
++      /* the registers may look crazy here */
++      ohci_dump_status (ohci);
++#endif
++
++      /* Re-enable bus mastering */
++      pci_set_master(ohci->ohci_dev);
++      
++      switch (temp) {
++
++      case OHCI_USB_RESET:    // lost power
++              info ("USB restart: usb-%s", dev->slot_name);
++              hc_restart (ohci);
++              break;
++
++      case OHCI_USB_SUSPEND:  // host wakeup
++      case OHCI_USB_RESUME:   // remote wakeup
++              info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
++                      (temp == OHCI_USB_SUSPEND)
++                              ? "host" : "remote");
++              ohci->hc_control = OHCI_USB_RESUME;
++              writel (ohci->hc_control, &ohci->regs->control);
++              (void) readl (&ohci->regs->control);
++              mdelay (20); /* no schedule here ! */
++              /* Some controllers (lucent) need a longer delay here */
++              mdelay (15);
++              temp = readl (&ohci->regs->control);
++              temp = ohci->hc_control & OHCI_CTRL_HCFS;
++              if (temp != OHCI_USB_RESUME) {
++                      err ("controller usb-%s won't resume", dev->slot_name);
++                      ohci->disabled = 1;
++                      return -EIO;
++              }
++
++              /* Some chips likes being resumed first */
++              writel (OHCI_USB_OPER, &ohci->regs->control);
++              (void) readl (&ohci->regs->control);
++              mdelay (3);
++
++              /* Then re-enable operations */
++              spin_lock_irqsave (&usb_ed_lock, flags);
++              ohci->disabled = 0;
++              ohci->sleeping = 0;
++              ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
++              if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
++                      if (ohci->ed_controltail)
++                              ohci->hc_control |= OHCI_CTRL_CLE;
++                      if (ohci->ed_bulktail)
++                              ohci->hc_control |= OHCI_CTRL_BLE;
++              }
++              writel (ohci->hc_control, &ohci->regs->control);
++              writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
++              writel (OHCI_INTR_SF, &ohci->regs->intrenable);
++              /* Check for a pending done list */
++              writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);       
++              (void) readl (&ohci->regs->intrdisable);
++              spin_unlock_irqrestore (&usb_ed_lock, flags);
++#ifdef CONFIG_PMAC_PBOOK
++              if (_machine == _MACH_Pmac)
++                      enable_irq (ohci->irq);
++#endif
++              if (ohci->hcca->done_head)
++                      dl_done_list (ohci, dl_reverse_done_list (ohci));
++              writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
++              writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
++              writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
++              break;
++
++      default:
++              warn ("odd PCI resume for usb-%s", dev->slot_name);
++      }
++
++      /* controller is operational, extra resumes are harmless */
++      atomic_dec (&ohci->resume_count);
++
++      return 0;
++}
++
++#endif        /* CONFIG_PM */
++
++
++/*-------------------------------------------------------------------------*/
++
++static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
++
++      /*
++       * AMD-756 [Viper] USB has a serious erratum when used with
++       * lowspeed devices like mice.
++       */
++      vendor:         0x1022,
++      device:         0x740c,
++      subvendor:      PCI_ANY_ID,
++      subdevice:      PCI_ANY_ID,
++
++      driver_data:    OHCI_QUIRK_AMD756,
++
++} , {
++
++      /* handle any USB OHCI controller */
++      class:          ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
++      class_mask:     ~0,
++
++      /* no matter who makes it */
++      vendor:         PCI_ANY_ID,
++      device:         PCI_ANY_ID,
++      subvendor:      PCI_ANY_ID,
++      subdevice:      PCI_ANY_ID,
++
++      }, { /* end: all zeroes */ }
++};
++
++MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
++
++static struct pci_driver ohci_pci_driver = {
++      name:           "usb-ohci",
++      id_table:       &ohci_pci_ids [0],
++
++      probe:          ohci_pci_probe,
++      remove:         __devexit_p(ohci_pci_remove),
++
++#ifdef        CONFIG_PM
++      suspend:        ohci_pci_suspend,
++      resume:         ohci_pci_resume,
++#endif        /* PM */
++};
++
++ 
++/*-------------------------------------------------------------------------*/
++
++static int __init ohci_hcd_init (void) 
++{
++      return pci_module_init (&ohci_pci_driver);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void __exit ohci_hcd_cleanup (void) 
++{     
++      pci_unregister_driver (&ohci_pci_driver);
++}
++
++module_init (ohci_hcd_init);
++module_exit (ohci_hcd_cleanup);
++
+--- /dev/null
++++ linux-2.4.27/drivers/usb/host/usb-ohci-sa1111.c
+@@ -0,0 +1,118 @@
++/*
++ *  linux/drivers/usb/usb-ohci-sa1111.c
++ *
++ *  The outline of this code was taken from Brad Parkers <brad@heeltoe.com>
++ *  original OHCI driver modifications, and reworked into a cleaner form
++ *  by Russell King <rmk@arm.linux.org.uk>.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/pci.h>
++#include <asm/arch/assabet.h>
++#include <asm/arch/badge4.h>
++#include <asm/hardware/sa1111.h>
++
++#include "usb-ohci.h"
++
++int __devinit
++hc_add_ohci(struct pci_dev *dev, int irq, void *membase, unsigned long flags,
++          const char *name, const char *slot_name);
++extern void hc_remove_ohci(ohci_t *ohci);
++
++static ohci_t *sa1111_ohci;
++
++static void __init sa1111_ohci_configure(void)
++{
++      unsigned int usb_rst = 0;
++
++      if (machine_is_xp860() ||
++          machine_has_neponset() ||
++          machine_is_accelent_sa() ||
++          machine_is_pfs168() ||
++          machine_is_badge4())
++              usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
++
++      /*
++       * Configure the power sense and control lines.  Place the USB
++       * host controller in reset.
++       */
++      USB_RESET = usb_rst | USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
++
++      /*
++       * Now, carefully enable the USB clock, and take
++       * the USB host controller out of reset.
++       */
++      SKPCR |= SKPCR_UCLKEN;
++      udelay(11);
++      USB_RESET = usb_rst;
++}
++
++static int __init sa1111_ohci_init(void)
++{
++      int ret;
++
++      /*
++       * Request memory resources.
++       */
++//    if (!request_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT, "usb-ohci"))
++//            return -EBUSY;
++
++      sa1111_ohci_configure();
++
++      /*
++       * Initialise the generic OHCI driver.
++       */
++      ret = hc_add_ohci(SA1111_FAKE_PCIDEV, NIRQHCIM,
++                        (void *)&USB_OHCI_OP_BASE, 0, &sa1111_ohci,
++                        "usb-ohci", "sa1111");
++
++//    if (ret)
++//            release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
++
++#ifdef CONFIG_SA1100_BADGE4
++      if (machine_is_badge4() && (!ret)) {
++              /* found the controller, so now power the bus */
++              badge4_set_5V(BADGE4_5V_USB, 1);
++      }
++#endif
++
++      return ret;
++}
++
++static void __exit sa1111_ohci_exit(void)
++{
++      hc_remove_ohci(sa1111_ohci);
++
++      /*
++       * Put the USB host controller into reset.
++       */
++      USB_RESET |= USB_RESET_FORCEIFRESET | USB_RESET_FORCEHCRESET;
++
++      /*
++       * Stop the USB clock.
++       */
++      SKPCR &= ~SKPCR_UCLKEN;
++
++      /*
++       * Release memory resources.
++       */
++//    release_mem_region(_USB_OHCI_OP_BASE, _USB_EXTENT);
++
++#ifdef CONFIG_SA1100_BADGE4
++      if (machine_is_badge4()) {
++              badge4_set_5V(BADGE4_5V_USB, 0);
++      }
++#endif
++}
++
++module_init(sa1111_ohci_init);
++module_exit(sa1111_ohci_exit);
+--- linux-2.4.27/drivers/usb/host/usb-ohci.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/host/usb-ohci.c
+@@ -12,7 +12,6 @@
+  * 
+  * History:
+  *
+- * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou@ali>)
+  * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on
+  *    load failure (Matthew Frederickson)
+  * 2002/01/20 async unlink fixes:  return -EINPROGRESS (per spec) and
+@@ -81,16 +80,6 @@
+ #include "../hcd.h"
+-#ifdef CONFIG_PMAC_PBOOK
+-#include <asm/machdep.h>
+-#include <asm/pmac_feature.h>
+-#include <asm/pci-bridge.h>
+-#ifndef CONFIG_PM
+-#define CONFIG_PM
+-#endif
+-#endif
+-
+-
+ /*
+  * Version Information
+  */
+@@ -98,12 +87,12 @@
+ #define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
+ #define DRIVER_DESC "USB OHCI Host Controller Driver"
+-/* For initializing controller (mask in an HCFS mode too) */
+-#define       OHCI_CONTROL_INIT \
+-      (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+-
+ #define OHCI_UNLINK_TIMEOUT   (HZ / 10)
++static LIST_HEAD (ohci_hcd_list);
++spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
++
++
+ /*-------------------------------------------------------------------------*/
+ /* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+@@ -130,57 +119,6 @@
+ /*-------------------------------------------------------------------------*
+  * URB support functions 
+  *-------------------------------------------------------------------------*/ 
+-
+-static void ohci_complete_add(struct ohci *ohci, struct urb *urb)
+-{
+-
+-      if (urb->hcpriv != NULL) {
+-              printk("completing with non-null priv!\n");
+-              return;
+-      }
+-
+-      if (ohci->complete_tail == NULL) {
+-              ohci->complete_head = urb;
+-              ohci->complete_tail = urb;
+-      } else {
+-              ohci->complete_head->hcpriv = urb;
+-              ohci->complete_tail = urb;
+-      }
+-}
+-
+-static inline struct urb *ohci_complete_get(struct ohci *ohci)
+-{
+-      struct urb *urb;
+-
+-      if ((urb = ohci->complete_head) == NULL)
+-              return NULL;
+-      if (urb == ohci->complete_tail) {
+-              ohci->complete_tail = NULL;
+-              ohci->complete_head = NULL;
+-      } else {
+-              ohci->complete_head = urb->hcpriv;
+-      }
+-      urb->hcpriv = NULL;
+-      return urb;
+-}
+-
+-static inline void ohci_complete(struct ohci *ohci)
+-{
+-      struct urb *urb;
+-
+-      spin_lock(&ohci->ohci_lock);
+-      while ((urb = ohci_complete_get(ohci)) != NULL) {
+-              spin_unlock(&ohci->ohci_lock);
+-              if (urb->dev) {
+-                      usb_dec_dev_use (urb->dev);
+-                      urb->dev = NULL;
+-              }
+-              if (urb->complete)
+-                      (*urb->complete)(urb);
+-              spin_lock(&ohci->ohci_lock);
+-      }
+-      spin_unlock(&ohci->ohci_lock);
+-}
+  
+ /* free HCD-private data associated with this URB */
+@@ -256,14 +194,20 @@
+               }
+               urb_free_priv ((struct ohci *)urb->dev->bus->hcpriv, urb_priv);
+-      } else {
+-              if (urb->dev != NULL) {
+-                      err ("Non-null dev at rm_priv time");
+-                      // urb->dev = NULL;
+-              }
++              usb_dec_dev_use (urb->dev);
++              urb->dev = NULL;
+       }
+ }
++static void urb_rm_priv (struct urb * urb)
++{
++      unsigned long flags;
++
++      spin_lock_irqsave (&usb_ed_lock, flags);
++      urb_rm_priv_locked (urb);
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
++}
++
+ /*-------------------------------------------------------------------------*/
+  
+ #ifdef DEBUG
+@@ -484,7 +428,7 @@
+ static void ohci_dump (ohci_t *controller, int verbose)
+ {
+-      dbg ("OHCI controller usb-%s state", controller->ohci_dev->slot_name);
++      dbg ("OHCI controller usb-%s state", controller->slot_name);
+       // dumps some of the state we know about
+       ohci_dump_status (controller);
+@@ -507,6 +451,7 @@
+ {
+       urb_priv_t * urb_priv = urb->hcpriv;
+       struct urb * urbt;
++      unsigned long flags;
+       int i;
+       
+       if (!urb_priv)
+@@ -514,8 +459,7 @@
+       /* just to be sure */
+       if (!urb->complete) {
+-              urb_rm_priv_locked (urb);
+-              ohci_complete_add(hc, urb);     /* Just usb_dec_dev_use */
++              urb_rm_priv (urb);
+               return -1;
+       }
+       
+@@ -531,18 +475,20 @@
+                               usb_pipeout (urb->pipe)
+                                       ? PCI_DMA_TODEVICE
+                                       : PCI_DMA_FROMDEVICE);
++
+                       if (urb->interval) {
+                               urb->complete (urb);
+-
++ 
+                               /* implicitly requeued */
+                               urb->actual_length = 0;
+                               urb->status = -EINPROGRESS;
+                               td_submit_urb (urb);
+                       } else {
+-                              urb_rm_priv_locked (urb);
+-                              ohci_complete_add(hc, urb);
++                              urb_rm_priv(urb);
++                              urb->complete (urb);
+                       }
+-                      break;
++                      break;
++
+                       
+               case PIPE_ISOCHRONOUS:
+                       for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next);
+@@ -554,6 +500,7 @@
+                                               ? PCI_DMA_TODEVICE
+                                               : PCI_DMA_FROMDEVICE);
+                               urb->complete (urb);
++                              spin_lock_irqsave (&usb_ed_lock, flags);
+                               urb->actual_length = 0;
+                               urb->status = USB_ST_URB_PENDING;
+                               urb->start_frame = urb_priv->ed->last_iso + 1;
+@@ -564,17 +511,18 @@
+                                       }
+                                       td_submit_urb (urb);
+                               }
+-
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
++                              
+                       } else { /* unlink URB, call complete */
+-                              urb_rm_priv_locked (urb);
+-                              ohci_complete_add(hc, urb);
++                              urb_rm_priv (urb);
++                              urb->complete (urb);    
+                       }               
+                       break;
+                               
+               case PIPE_BULK:
+               case PIPE_CONTROL: /* unlink URB, call complete */
+-                      urb_rm_priv_locked (urb);
+-                      ohci_complete_add(hc, urb);
++                      urb_rm_priv (urb);
++                      urb->complete (urb);    
+                       break;
+       }
+       return 0;
+@@ -594,7 +542,7 @@
+       int i, size = 0;
+       unsigned long flags;
+       int bustime = 0;
+-      int mem_flags = GFP_ATOMIC;
++      int mem_flags = ALLOC_FLAGS;
+       
+       if (!urb->dev || !urb->dev->bus)
+               return -ENODEV;
+@@ -611,24 +559,20 @@
+ #ifdef DEBUG
+       urb_print (urb, "SUB", usb_pipein (pipe));
+ #endif
+-
++      
+       /* handle a request to the virtual root hub */
+       if (usb_pipedevice (pipe) == ohci->rh.devnum) 
+               return rh_submit_urb (urb);
+-
+-      spin_lock_irqsave(&ohci->ohci_lock, flags);
+-
++      
+       /* when controller's hung, permit only roothub cleanup attempts
+        * such as powering down ports */
+       if (ohci->disabled) {
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+               usb_dec_dev_use (urb->dev);     
+               return -ESHUTDOWN;
+       }
+       /* every endpoint has a ed, locate and fill it */
+       if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) {
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+               usb_dec_dev_use (urb->dev);     
+               return -ENOMEM;
+       }
+@@ -650,7 +594,6 @@
+               case PIPE_ISOCHRONOUS: /* number of packets from URB */
+                       size = urb->number_of_packets;
+                       if (size <= 0) {
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+                               usb_dec_dev_use (urb->dev);     
+                               return -EINVAL;
+                       }
+@@ -670,9 +613,8 @@
+       /* allocate the private part of the URB */
+       urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), 
+-                                                      GFP_ATOMIC);
++                                                      in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
+       if (!urb_priv) {
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+               usb_dec_dev_use (urb->dev);     
+               return -ENOMEM;
+       }
+@@ -683,12 +625,13 @@
+       urb_priv->ed = ed;      
+       /* allocate the TDs (updating hash chains) */
++      spin_lock_irqsave (&usb_ed_lock, flags);
+       for (i = 0; i < size; i++) { 
+               urb_priv->td[i] = td_alloc (ohci, SLAB_ATOMIC);
+               if (!urb_priv->td[i]) {
+                       urb_priv->length = i;
+                       urb_free_priv (ohci, urb_priv);
+-                      spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++                      spin_unlock_irqrestore (&usb_ed_lock, flags);
+                       usb_dec_dev_use (urb->dev);     
+                       return -ENOMEM;
+               }
+@@ -696,7 +639,7 @@
+       if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+               urb_free_priv (ohci, urb_priv);
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++              spin_unlock_irqrestore (&usb_ed_lock, flags);
+               usb_dec_dev_use (urb->dev);     
+               return -EINVAL;
+       }
+@@ -718,7 +661,7 @@
+                       }
+                       if (bustime < 0) {
+                               urb_free_priv (ohci, urb_priv);
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
+                               usb_dec_dev_use (urb->dev);     
+                               return bustime;
+                       }
+@@ -744,6 +687,9 @@
+       if (urb->timeout) {
+               struct list_head        *entry;
++              // FIXME:  usb-uhci uses relative timeouts (like this),
++              // while uhci uses absolute ones (probably better).
++              // Pick one solution and change the affected drivers.
+               urb->timeout += jiffies;
+               list_for_each (entry, &ohci->timeout_list) {
+@@ -757,11 +703,10 @@
+               /* drive timeouts by SF (messy, but works) */
+               writel (OHCI_INTR_SF, &ohci->regs->intrenable); 
+-              (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+       }
+ #endif
+-      spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
+       return 0;       
+ }
+@@ -792,7 +737,6 @@
+       if (usb_pipedevice (urb->pipe) == ohci->rh.devnum)
+               return rh_unlink_urb (urb);
+-      spin_lock_irqsave(&ohci->ohci_lock, flags);
+       if (urb->hcpriv && (urb->status == USB_ST_URB_PENDING)) { 
+               if (!ohci->disabled) {
+                       urb_priv_t  * urb_priv;
+@@ -802,7 +746,6 @@
+                        */
+                       if (!(urb->transfer_flags & USB_ASYNC_UNLINK)
+                                       && in_interrupt ()) {
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+                               err ("bug in call from %p; use async!",
+                                       __builtin_return_address(0));
+                               return -EWOULDBLOCK;
+@@ -811,10 +754,11 @@
+                       /* flag the urb and its TDs for deletion in some
+                        * upcoming SF interrupt delete list processing
+                        */
++                      spin_lock_irqsave (&usb_ed_lock, flags);
+                       urb_priv = urb->hcpriv;
+                       if (!urb_priv || (urb_priv->state == URB_DEL)) {
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
+                               return 0;
+                       }
+                               
+@@ -829,36 +773,26 @@
+                               add_wait_queue (&unlink_wakeup, &wait);
+                               urb_priv->wait = &unlink_wakeup;
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
+                               /* wait until all TDs are deleted */
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+-                              while (timeout && (urb->status == USB_ST_URB_PENDING)) {
++                              while (timeout && (urb->status == USB_ST_URB_PENDING))
+                                       timeout = schedule_timeout (timeout);
+-                                      set_current_state(TASK_UNINTERRUPTIBLE);
+-                              }
+                               set_current_state(TASK_RUNNING);
+                               remove_wait_queue (&unlink_wakeup, &wait); 
+                               if (urb->status == USB_ST_URB_PENDING) {
+                                       err ("unlink URB timeout");
+                                       return -ETIMEDOUT;
+                               }
+-
+-                              usb_dec_dev_use (urb->dev);
+-                              urb->dev = NULL;
+-                              if (urb->complete)
+-                                      urb->complete (urb); 
+                       } else {
+                               /* usb_dec_dev_use done in dl_del_list() */
+                               urb->status = -EINPROGRESS;
+-                              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
+                               return -EINPROGRESS;
+                       }
+               } else {
+-                      urb_rm_priv_locked (urb);
+-                      spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+-                      usb_dec_dev_use (urb->dev);
+-                      urb->dev = NULL;
++                      urb_rm_priv (urb);
+                       if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+                               urb->status = -ECONNRESET;
+                               if (urb->complete)
+@@ -866,8 +800,6 @@
+                       } else 
+                               urb->status = -ENOENT;
+               }       
+-      } else {
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+       }       
+       return 0;
+ }
+@@ -910,14 +842,14 @@
+                * (freeing all the TDs, unlinking EDs) but we need
+                * to defend against bugs that prevent that.
+                */
+-              spin_lock_irqsave(&ohci->ohci_lock, flags);     
++              spin_lock_irqsave (&usb_ed_lock, flags);        
+               for(i = 0; i < NUM_EDS; i++) {
+                       ed = &(dev->ed[i]);
+                       if (ed->state != ED_NEW) {
+                               if (ed->state == ED_OPER) {
+                                       /* driver on that interface didn't unlink an urb */
+                                       dbg ("driver usb-%s dev %d ed 0x%x unfreed URB",
+-                                              ohci->ohci_dev->slot_name, usb_dev->devnum, i);
++                                              ohci->slot_name, usb_dev->devnum, i);
+                                       ep_unlink (ohci, ed);
+                               }
+                               ep_rm_ed (usb_dev, ed);
+@@ -925,7 +857,7 @@
+                               cnt++;
+                       }
+               }
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++              spin_unlock_irqrestore (&usb_ed_lock, flags);
+               
+               /* if the controller is running, tds for those unlinked
+                * urbs get freed by dl_del_list at the next SF interrupt
+@@ -962,7 +894,7 @@
+                       } else {
+                               /* likely some interface's driver has a refcount bug */
+                               err ("bus %s devnum %d deletion in interrupt",
+-                                      ohci->ohci_dev->slot_name, usb_dev->devnum);
++                                      ohci->slot_name, usb_dev->devnum);
+                               BUG ();
+                       }
+               }
+@@ -1259,12 +1191,17 @@
+       td_t * td;
+       ed_t * ed_ret;
+       volatile ed_t * ed; 
++      unsigned long flags;
++      
++      
++      spin_lock_irqsave (&usb_ed_lock, flags);
+       ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | 
+                       (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]);
+       if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
+               /* pending delete request */
++              spin_unlock_irqrestore (&usb_ed_lock, flags);
+               return NULL;
+       }
+       
+@@ -1277,6 +1214,7 @@
+                       /* out of memory */
+                       if (td)
+                           td_free(ohci, td);
++                      spin_unlock_irqrestore (&usb_ed_lock, flags);
+                       return NULL;
+               }
+               ed->hwTailP = cpu_to_le32 (td->td_dma);
+@@ -1299,7 +1237,8 @@
+               ed->int_period = interval;
+               ed->int_load = load;
+       }
+-
++      
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
+       return ed_ret; 
+ }
+@@ -1340,7 +1279,6 @@
+               /* enable SOF interrupt */
+               writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+               writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+-              (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+       }
+ }
+@@ -1362,7 +1300,11 @@
+               err("internal OHCI error: TD index > length");
+               return;
+       }
+-      
++#ifdef CONFIG_SA1111
++      if (data & (1 << 20))
++              panic("td_fill: A20 = 1: %08x\n", data);
++#endif
++
+       /* use this td as the next dummy */
+       td_pt = urb_priv->td [index];
+       td_pt->hwNextTD = 0;
+@@ -1461,7 +1403,6 @@
+                       if (!ohci->sleeping) {
+                               wmb();
+                               writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+-                              (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+                       }
+                       break;
+@@ -1490,7 +1431,6 @@
+                       if (!ohci->sleeping) {
+                               wmb();
+                               writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+-                              (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+                       }
+                       break;
+@@ -1558,7 +1498,7 @@
+ /* handle an urb that is being unlinked */
+-static void dl_del_urb (ohci_t *ohci, struct urb * urb)
++static void dl_del_urb (struct urb * urb)
+ {
+       wait_queue_head_t * wait_head = ((urb_priv_t *)(urb->hcpriv))->wait;
+@@ -1566,9 +1506,12 @@
+       if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+               urb->status = -ECONNRESET;
+-              ohci_complete_add(ohci, urb);
++              if (urb->complete)
++                      urb->complete (urb);
+       } else {
+               urb->status = -ENOENT;
++              if (urb->complete)
++                      urb->complete (urb);
+               /* unblock sohci_unlink_urb */
+               if (wait_head)
+@@ -1587,7 +1530,10 @@
+       td_t * td_rev = NULL;
+       td_t * td_list = NULL;
+       urb_priv_t * urb_priv = NULL;
+-
++      unsigned long flags;
++      
++      spin_lock_irqsave (&usb_ed_lock, flags);
++      
+       td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0;
+       ohci->hcca->done_head = 0;
+       
+@@ -1613,6 +1559,7 @@
+               td_rev = td_list;
+               td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0;    
+       }       
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
+       return td_list;
+ }
+@@ -1624,6 +1571,7 @@
+  
+ static void dl_del_list (ohci_t  * ohci, unsigned int frame)
+ {
++      unsigned long flags;
+       ed_t * ed;
+       __u32 edINFO;
+       __u32 tdINFO;
+@@ -1631,6 +1579,8 @@
+       __u32 * td_p;
+       int ctrl = 0, bulk = 0;
++      spin_lock_irqsave (&usb_ed_lock, flags);
++
+       for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) {
+               tdTailP = dma_to_td (ohci, le32_to_cpup (&ed->hwTailP) & 0xfffffff0);
+@@ -1651,7 +1601,7 @@
+                               /* URB is done; clean up */
+                               if (++(urb_priv->td_cnt) == urb_priv->length)
+-                                      dl_del_urb (ohci, urb);
++                                      dl_del_urb (urb);
+                       } else {
+                               td_p = &td->hwNextTD;
+                       }
+@@ -1708,6 +1658,7 @@
+       }
+       ohci->ed_rm_list[frame] = NULL;
++      spin_unlock_irqrestore (&usb_ed_lock, flags);
+ }
+@@ -1724,7 +1675,9 @@
+       struct urb * urb;
+       urb_priv_t * urb_priv;
+       __u32 tdINFO, edHeadP, edTailP;
+- 
++      
++      unsigned long flags;
++      
+       while (td_list) {
+               td_list_next = td_list->next_dl_td;
+               
+@@ -1753,10 +1706,13 @@
+                               urb->status = cc_to_error[cc];
+                               sohci_return_urb (ohci, urb);
+                       } else {
+-                              dl_del_urb (ohci, urb);
++                              spin_lock_irqsave (&usb_ed_lock, flags);
++                              dl_del_urb (urb);
++                              spin_unlock_irqrestore (&usb_ed_lock, flags);
+                       }
+               }
+               
++              spin_lock_irqsave (&usb_ed_lock, flags);
+               if (ed->state != ED_NEW) { 
+                       edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0;
+                       edTailP = le32_to_cpup (&ed->hwTailP);
+@@ -1765,6 +1721,7 @@
+                       if ((edHeadP == edTailP) && (ed->state == ED_OPER)) 
+                               ep_unlink (ohci, ed);
+               }       
++              spin_unlock_irqrestore (&usb_ed_lock, flags);
+       
+               td_list = td_list_next;
+       }  
+@@ -1855,7 +1812,7 @@
+       num_ports = roothub_a (ohci) & RH_A_NDP; 
+       if (num_ports > MAX_ROOT_PORTS) {
+               err ("bogus NDP=%d for OHCI usb-%s", num_ports,
+-                      ohci->ohci_dev->slot_name);
++                      ohci->slot_name);
+               err ("rereads as NDP=%d",
+                       readl (&ohci->regs->roothub.a) & RH_A_NDP);
+               /* retry later; "should not happen" */
+@@ -1955,8 +1912,7 @@
+       int leni = urb->transfer_buffer_length;
+       int len = 0;
+       int status = TD_CC_NOERROR;
+-      unsigned long flags;
+-
++      
+       __u32 datab[4];
+       __u8  * data_buf = (__u8 *) datab;
+       
+@@ -1965,16 +1921,13 @@
+       __u16 wIndex;
+       __u16 wLength;
+-      spin_lock_irqsave(&ohci->ohci_lock, flags);
+-
+       if (usb_pipeint(pipe)) {
+               ohci->rh.urb =  urb;
+               ohci->rh.send = 1;
+               ohci->rh.interval = urb->interval;
+               rh_init_int_timer(urb);
+               urb->status = cc_to_error [TD_CC_NOERROR];
+-
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
++              
+               return 0;
+       }
+@@ -2146,7 +2099,6 @@
+ #endif
+       urb->hcpriv = NULL;
+-      spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+       usb_dec_dev_use (usb_dev);
+       urb->dev = NULL;
+       if (urb->complete)
+@@ -2159,16 +2111,13 @@
+ static int rh_unlink_urb (struct urb * urb)
+ {
+       ohci_t * ohci = urb->dev->bus->hcpriv;
+-      unsigned int flags;
+  
+-      spin_lock_irqsave(&ohci->ohci_lock, flags);
+       if (ohci->rh.urb == urb) {
+               ohci->rh.send = 0;
+               del_timer (&ohci->rh.rh_int_timer);
+               ohci->rh.urb = NULL;
+               urb->hcpriv = NULL;
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+               usb_dec_dev_use(urb->dev);
+               urb->dev = NULL;
+               if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+@@ -2177,8 +2126,6 @@
+                               urb->complete (urb);
+               } else
+                       urb->status = -ENOENT;
+-      } else {
+-              spin_unlock_irqrestore(&ohci->ohci_lock, flags);
+       }
+       return 0;
+ }
+@@ -2213,16 +2160,12 @@
+       writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+       dbg("USB HC reset_hc usb-%s: ctrl = 0x%x ;",
+-              ohci->ohci_dev->slot_name,
++              ohci->slot_name,
+               readl (&ohci->regs->control));
+       /* Reset USB (needed by some controllers) */
+       writel (0, &ohci->regs->control);
+-
+-      /* Force a state change from USBRESET to USBOPERATIONAL for ALi */
+-      (void) readl (&ohci->regs->control);    /* PCI posting */
+-      writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control);
+-
++              
+       /* HC Reset requires max 10 ms delay */
+       writel (OHCI_HCR,  &ohci->regs->cmdstatus);
+       while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+@@ -2291,8 +2234,6 @@
+       writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+ #endif        /* OHCI_USE_NPS */
+-      (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+-
+       // POTPGT delay is bits 24-31, in 2 ms units.
+       mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+  
+@@ -2322,7 +2263,7 @@
+ static void check_timeouts (struct ohci *ohci)
+ {
+-      spin_lock (&ohci->ohci_lock);
++      spin_lock (&usb_ed_lock);
+       while (!list_empty (&ohci->timeout_list)) {
+               struct urb      *urb;
+@@ -2335,15 +2276,15 @@
+                       continue;
+               urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+-              spin_unlock (&ohci->ohci_lock);
++              spin_unlock (&usb_ed_lock);
+               // outside the interrupt handler (in a timer...)
+               // this reference would race interrupts
+               sohci_unlink_urb (urb);
+-              spin_lock (&ohci->ohci_lock);
++              spin_lock (&usb_ed_lock);
+       }
+-      spin_unlock (&ohci->ohci_lock);
++      spin_unlock (&usb_ed_lock);
+ }
+@@ -2357,8 +2298,6 @@
+       struct ohci_regs * regs = ohci->regs;
+       int ints; 
+-      spin_lock (&ohci->ohci_lock);
+-
+       /* avoid (slow) readl if only WDH happened */
+       if ((ohci->hcca->done_head != 0)
+                       && !(le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+@@ -2367,22 +2306,20 @@
+       /* cardbus/... hardware gone before remove() */
+       } else if ((ints = readl (&regs->intrstatus)) == ~(u32)0) {
+               ohci->disabled++;
+-              spin_unlock (&ohci->ohci_lock);
+               err ("%s device removed!", ohci->ohci_dev->slot_name);
+               return;
+       /* interrupt for some other device? */
+       } else if ((ints &= readl (&regs->intrenable)) == 0) {
+-              spin_unlock (&ohci->ohci_lock);
+               return;
+-      } 
++      }
+       // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no));
+       if (ints & OHCI_INTR_UE) {
+               ohci->disabled++;
+               err ("OHCI Unrecoverable Error, controller usb-%s disabled",
+-                      ohci->ohci_dev->slot_name);
++                      ohci->slot_name);
+               // e.g. due to PCI Master/Target Abort
+ #ifdef        DEBUG
+@@ -2398,39 +2335,26 @@
+   
+       if (ints & OHCI_INTR_WDH) {
+               writel (OHCI_INTR_WDH, &regs->intrdisable);     
+-              (void)readl (&regs->intrdisable); /* PCI posting flush */
+               dl_done_list (ohci, dl_reverse_done_list (ohci));
+               writel (OHCI_INTR_WDH, &regs->intrenable); 
+-              (void)readl (&regs->intrdisable); /* PCI posting flush */
+       }
+   
+       if (ints & OHCI_INTR_SO) {
+               dbg("USB Schedule overrun");
+               writel (OHCI_INTR_SO, &regs->intrenable);        
+-              (void)readl (&regs->intrdisable); /* PCI posting flush */
+       }
+       // FIXME:  this assumes SOF (1/ms) interrupts don't get lost...
+       if (ints & OHCI_INTR_SF) { 
+               unsigned int frame = le16_to_cpu (ohci->hcca->frame_no) & 1;
+               writel (OHCI_INTR_SF, &regs->intrdisable);      
+-              (void)readl (&regs->intrdisable); /* PCI posting flush */
+               if (ohci->ed_rm_list[!frame] != NULL) {
+                       dl_del_list (ohci, !frame);
+               }
+-              if (ohci->ed_rm_list[frame] != NULL) {
++              if (ohci->ed_rm_list[frame] != NULL)
+                       writel (OHCI_INTR_SF, &regs->intrenable);       
+-                      (void)readl (&regs->intrdisable); /* PCI posting flush */
+-              }
+       }
+-      /*
+-       * Finally, we are done with trashing about our hardware lists
+-       * and other CPUs are allowed in. The festive flipping of the lock
+-       * ensues as we struggle with the check_timeouts disaster.
+-       */
+-      spin_unlock (&ohci->ohci_lock);
+-
+       if (!list_empty (&ohci->timeout_list)) {
+               check_timeouts (ohci);
+ // FIXME:  enable SF as needed in a timer;
+@@ -2442,9 +2366,6 @@
+       writel (ints, &regs->intrstatus);
+       writel (OHCI_INTR_MIE, &regs->intrenable);      
+-      (void)readl (&regs->intrdisable); /* PCI posting flush */
+-
+-      ohci_complete(ohci);
+ }
+ /*-------------------------------------------------------------------------*/
+@@ -2475,10 +2396,14 @@
+       ohci->regs = mem_base;   
+       ohci->ohci_dev = dev;
++#ifdef CONFIG_PCI
+       pci_set_drvdata(dev, ohci);
++#endif
+  
++      INIT_LIST_HEAD (&ohci->ohci_hcd_list);
++      list_add (&ohci->ohci_hcd_list, &ohci_hcd_list);
++
+       INIT_LIST_HEAD (&ohci->timeout_list);
+-      spin_lock_init(&ohci->ohci_lock);
+       ohci->bus = usb_alloc_bus (&sohci_device_operations);
+       if (!ohci->bus) {
+@@ -2501,7 +2426,7 @@
+ static void hc_release_ohci (ohci_t * ohci)
+ {     
+-      dbg ("USB HC release ohci usb-%s", ohci->ohci_dev->slot_name);
++      dbg ("USB HC release ohci usb-%s", ohci->slot_name);
+       /* disconnect all devices */    
+       if (ohci->bus->root_hub)
+@@ -2514,7 +2439,9 @@
+               free_irq (ohci->irq, ohci);
+               ohci->irq = -1;
+       }
+-      pci_set_drvdata(ohci->ohci_dev, NULL);
++#ifdef CONFIG_PCI
++      pci_set_drvdata(ohci->ohci_dev, 0);
++#endif
+       if (ohci->bus) {
+               if (ohci->bus->busnum != -1)
+                       usb_deregister_bus (ohci->bus);
+@@ -2522,6 +2449,9 @@
+               usb_free_bus (ohci->bus);
+       }
++      list_del (&ohci->ohci_hcd_list);
++      INIT_LIST_HEAD (&ohci->ohci_hcd_list);
++
+       ohci_mem_cleanup (ohci);
+     
+       /* unmap the IO address space */
+@@ -2534,17 +2464,15 @@
+ /*-------------------------------------------------------------------------*/
+-/* Increment the module usage count, start the control thread and
+- * return success. */
+-
+-static struct pci_driver ohci_pci_driver;
+- 
+-static int __devinit
+-hc_found_ohci (struct pci_dev *dev, int irq,
+-      void *mem_base, const struct pci_device_id *id)
++/*
++ * Host bus independent add one OHCI host controller.
++ */
++int __devinit
++hc_add_ohci(struct pci_dev *dev, int irq, void *mem_base, unsigned long flags,
++          const char *name, const char *slot_name)
+ {
+-      ohci_t * ohci;
+       char buf[8], *bufp = buf;
++      ohci_t * ohci;
+       int ret;
+ #ifndef __sparc__
+@@ -2554,34 +2482,17 @@
+ #endif
+       printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n",
+               (unsigned long) mem_base, bufp);
+-      printk(KERN_INFO __FILE__ ": usb-%s, %s\n", dev->slot_name, dev->name);
+-    
++
+       ohci = hc_alloc_ohci (dev, mem_base);
+       if (!ohci) {
+               return -ENOMEM;
+       }
++      ohci->slot_name = slot_name;
+       if ((ret = ohci_mem_init (ohci)) < 0) {
+               hc_release_ohci (ohci);
+               return ret;
+       }
+-      ohci->flags = id->driver_data;
+-      
+-      /* Check for NSC87560. We have to look at the bridge (fn1) to identify
+-         the USB (fn2). This quirk might apply to more or even all NSC stuff
+-         I don't know.. */
+-         
+-      if(dev->vendor == PCI_VENDOR_ID_NS)
+-      {
+-              struct pci_dev *fn1  = pci_find_slot(dev->bus->number, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
+-              if(fn1 && fn1->vendor == PCI_VENDOR_ID_NS && fn1->device == PCI_DEVICE_ID_NS_87560_LIO)
+-                      ohci->flags |= OHCI_QUIRK_SUCKYIO;
+-              
+-      }
+-      
+-      if (ohci->flags & OHCI_QUIRK_SUCKYIO)
+-              printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
+-      if (ohci->flags & OHCI_QUIRK_AMD756)
+-              printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
++      ohci->flags = flags;
+       if (hc_reset (ohci) < 0) {
+               hc_release_ohci (ohci);
+@@ -2590,13 +2501,11 @@
+       /* FIXME this is a second HC reset; why?? */
+       writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control);
+-      (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */
+       wait_ms (10);
+       usb_register_bus (ohci->bus);
+       
+-      if (request_irq (irq, hc_interrupt, SA_SHIRQ,
+-                      ohci_pci_driver.name, ohci) != 0) {
++      if (request_irq (irq, hc_interrupt, SA_SHIRQ, name, ohci) != 0) {
+               err ("request interrupt %s failed", bufp);
+               hc_release_ohci (ohci);
+               return -EBUSY;
+@@ -2604,7 +2513,7 @@
+       ohci->irq = irq;     
+       if (hc_start (ohci) < 0) {
+-              err ("can't start usb-%s", dev->slot_name);
++              err ("can't start usb-%s", ohci->slot_name);
+               hc_release_ohci (ohci);
+               return -EBUSY;
+       }
+@@ -2615,114 +2524,11 @@
+       return 0;
+ }
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef        CONFIG_PM
+-
+-/* controller died; cleanup debris, then restart */
+-/* must not be called from interrupt context */
+-
+-static void hc_restart (ohci_t *ohci)
+-{
+-      int temp;
+-      int i;
+-
+-      if (ohci->pci_latency)
+-              pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
+-
+-      ohci->disabled = 1;
+-      ohci->sleeping = 0;
+-      if (ohci->bus->root_hub)
+-              usb_disconnect (&ohci->bus->root_hub);
+-      
+-      /* empty the interrupt branches */
+-      for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
+-      for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table[i] = 0;
+-      
+-      /* no EDs to remove */
+-      ohci->ed_rm_list [0] = NULL;
+-      ohci->ed_rm_list [1] = NULL;
+-
+-      /* empty control and bulk lists */       
+-      ohci->ed_isotail     = NULL;
+-      ohci->ed_controltail = NULL;
+-      ohci->ed_bulktail    = NULL;
+-
+-      if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+-              err ("can't restart usb-%s, %d", ohci->ohci_dev->slot_name, temp);
+-      } else
+-              dbg ("restart usb-%s completed", ohci->ohci_dev->slot_name);
+-}
+-
+-#endif        /* CONFIG_PM */
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* configured so that an OHCI device is always provided */
+-/* always called with process context; sleeping is OK */
+-
+-static int __devinit
+-ohci_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+-{
+-      unsigned long mem_resource, mem_len;
+-      void *mem_base;
+-      int status;
+-
+-      if (pci_enable_device(dev) < 0)
+-              return -ENODEV;
+-
+-        if (!dev->irq) {
+-              err("found OHCI device with no IRQ assigned. check BIOS settings!");
+-              pci_disable_device (dev);
+-              return -ENODEV;
+-        }
+-      
+-      /* we read its hardware registers as memory */
+-      mem_resource = pci_resource_start(dev, 0);
+-      mem_len = pci_resource_len(dev, 0);
+-      if (!request_mem_region (mem_resource, mem_len, ohci_pci_driver.name)) {
+-              dbg ("controller already in use");
+-              pci_disable_device (dev);
+-              return -EBUSY;
+-      }
+-
+-      mem_base = ioremap_nocache (mem_resource, mem_len);
+-      if (!mem_base) {
+-              err("Error mapping OHCI memory");
+-              release_mem_region (mem_resource, mem_len);
+-              pci_disable_device (dev);
+-              return -EFAULT;
+-      }
+-
+-      /* controller writes into our memory */
+-      pci_set_master (dev);
+-
+-      status = hc_found_ohci (dev, dev->irq, mem_base, id);
+-      if (status < 0) {
+-              iounmap (mem_base);
+-              release_mem_region (mem_resource, mem_len);
+-              pci_disable_device (dev);
+-      }
+-      return status;
+-} 
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* may be called from interrupt context [interface spec] */
+-/* may be called without controller present */
+-/* may be called with controller, bus, and devices active */
+-
+-static void __devexit
+-ohci_pci_remove (struct pci_dev *dev)
++/*
++ * Host bus independent remove one OHCI host controller.
++ */
++void __devexit hc_remove_ohci(ohci_t *ohci)
+ {
+-      ohci_t          *ohci = pci_get_drvdata(dev);
+-
+-      dbg ("remove %s controller usb-%s%s%s",
+-              hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+-              dev->slot_name,
+-              ohci->disabled ? " (disabled)" : "",
+-              in_interrupt () ? " in interrupt" : ""
+-              );
+ #ifdef        DEBUG
+       ohci_dump (ohci, 1);
+ #endif
+@@ -2739,270 +2545,8 @@
+                       &ohci->regs->control);
+       hc_release_ohci (ohci);
+-
+-      release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
+-      pci_disable_device (dev);
+ }
+-
+-#ifdef        CONFIG_PM
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static int
+-ohci_pci_suspend (struct pci_dev *dev, u32 state)
+-{
+-      ohci_t                  *ohci = pci_get_drvdata(dev);
+-      unsigned long           flags;
+-      u16 cmd;
+-
+-      if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
+-              dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
+-                      hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
+-              return -EIO;
+-      }
+-
+-      /* act as if usb suspend can always be used */
+-      info ("USB suspend: usb-%s", dev->slot_name);
+-      ohci->sleeping = 1;
+-
+-      /* First stop processing */
+-      spin_lock_irqsave (&ohci->ohci_lock, flags);
+-      ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+-      writel (ohci->hc_control, &ohci->regs->control);
+-      writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+-      (void) readl (&ohci->regs->intrstatus);
+-      spin_unlock_irqrestore (&ohci->ohci_lock, flags);
+-
+-      /* Wait a frame or two */
+-      mdelay(1);
+-      if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+-              mdelay (1);
+-              
+-#ifdef CONFIG_PMAC_PBOOK
+-      if (_machine == _MACH_Pmac)
+-              disable_irq (ohci->irq);
+-      /* else, 2.4 assumes shared irqs -- don't disable */
+-#endif
+-      /* Enable remote wakeup */
+-      writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
+-
+-      /* Suspend chip and let things settle down a bit */
+-      ohci->hc_control = OHCI_USB_SUSPEND;
+-      writel (ohci->hc_control, &ohci->regs->control);
+-      (void) readl (&ohci->regs->control);
+-      mdelay (500); /* No schedule here ! */
+-      switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+-              case OHCI_USB_RESET:
+-                      dbg("Bus in reset phase ???");
+-                      break;
+-              case OHCI_USB_RESUME:
+-                      dbg("Bus in resume phase ???");
+-                      break;
+-              case OHCI_USB_OPER:
+-                      dbg("Bus in operational phase ???");
+-                      break;
+-              case OHCI_USB_SUSPEND:
+-                      dbg("Bus suspended");
+-                      break;
+-      }
+-      /* In some rare situations, Apple's OHCI have happily trashed
+-       * memory during sleep. We disable it's bus master bit during
+-       * suspend
+-       */
+-      pci_read_config_word (dev, PCI_COMMAND, &cmd);
+-      cmd &= ~PCI_COMMAND_MASTER;
+-      pci_write_config_word (dev, PCI_COMMAND, cmd);
+-#ifdef CONFIG_PMAC_PBOOK
+-      {
+-              struct device_node      *of_node;
+-
+-              /* Disable USB PAD & cell clock */
+-              of_node = pci_device_to_OF_node (ohci->ohci_dev);
+-              if (of_node)
+-                      pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+-      }
+-#endif
+-      return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static int
+-ohci_pci_resume (struct pci_dev *dev)
+-{
+-      ohci_t          *ohci = pci_get_drvdata(dev);
+-      int             temp;
+-      unsigned long   flags;
+-
+-      /* guard against multiple resumes */
+-      atomic_inc (&ohci->resume_count);
+-      if (atomic_read (&ohci->resume_count) != 1) {
+-              err ("concurrent PCI resumes for usb-%s", dev->slot_name);
+-              atomic_dec (&ohci->resume_count);
+-              return 0;
+-      }
+-
+-#ifdef CONFIG_PMAC_PBOOK
+-      {
+-              struct device_node *of_node;
+-
+-              /* Re-enable USB PAD & cell clock */
+-              of_node = pci_device_to_OF_node (ohci->ohci_dev);
+-              if (of_node)
+-                      pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+-      }
+-#endif
+-
+-      /* did we suspend, or were we powered off? */
+-      ohci->hc_control = readl (&ohci->regs->control);
+-      temp = ohci->hc_control & OHCI_CTRL_HCFS;
+-
+-#ifdef DEBUG
+-      /* the registers may look crazy here */
+-      ohci_dump_status (ohci);
+-#endif
+-
+-      /* Re-enable bus mastering */
+-      pci_set_master(ohci->ohci_dev);
+-      
+-      switch (temp) {
+-
+-      case OHCI_USB_RESET:    // lost power
+-              info ("USB restart: usb-%s", dev->slot_name);
+-              hc_restart (ohci);
+-              break;
+-
+-      case OHCI_USB_SUSPEND:  // host wakeup
+-      case OHCI_USB_RESUME:   // remote wakeup
+-              info ("USB continue: usb-%s from %s wakeup", dev->slot_name,
+-                      (temp == OHCI_USB_SUSPEND)
+-                              ? "host" : "remote");
+-              ohci->hc_control = OHCI_USB_RESUME;
+-              writel (ohci->hc_control, &ohci->regs->control);
+-              (void) readl (&ohci->regs->control);
+-              mdelay (20); /* no schedule here ! */
+-              /* Some controllers (lucent) need a longer delay here */
+-              mdelay (15);
+-              temp = readl (&ohci->regs->control);
+-              temp = ohci->hc_control & OHCI_CTRL_HCFS;
+-              if (temp != OHCI_USB_RESUME) {
+-                      err ("controller usb-%s won't resume", dev->slot_name);
+-                      ohci->disabled = 1;
+-                      return -EIO;
+-              }
+-
+-              /* Some chips likes being resumed first */
+-              writel (OHCI_USB_OPER, &ohci->regs->control);
+-              (void) readl (&ohci->regs->control);
+-              mdelay (3);
+-
+-              /* Then re-enable operations */
+-              spin_lock_irqsave (&ohci->ohci_lock, flags);
+-              ohci->disabled = 0;
+-              ohci->sleeping = 0;
+-              ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+-              if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
+-                      if (ohci->ed_controltail)
+-                              ohci->hc_control |= OHCI_CTRL_CLE;
+-                      if (ohci->ed_bulktail)
+-                              ohci->hc_control |= OHCI_CTRL_BLE;
+-              }
+-              writel (ohci->hc_control, &ohci->regs->control);
+-              writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+-              writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+-              /* Check for a pending done list */
+-              writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);       
+-              (void) readl (&ohci->regs->intrdisable);
+-#ifdef CONFIG_PMAC_PBOOK
+-              if (_machine == _MACH_Pmac)
+-                      enable_irq (ohci->irq);
+-#endif
+-              if (ohci->hcca->done_head)
+-                      dl_done_list (ohci, dl_reverse_done_list (ohci));
+-              writel (OHCI_INTR_WDH, &ohci->regs->intrenable); 
+-              writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+-              writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+-              spin_unlock_irqrestore (&ohci->ohci_lock, flags);
+-              break;
+-
+-      default:
+-              warn ("odd PCI resume for usb-%s", dev->slot_name);
+-      }
+-
+-      /* controller is operational, extra resumes are harmless */
+-      atomic_dec (&ohci->resume_count);
+-
+-      return 0;
+-}
+-
+-#endif        /* CONFIG_PM */
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static const struct pci_device_id __devinitdata ohci_pci_ids [] = { {
+-
+-      /*
+-       * AMD-756 [Viper] USB has a serious erratum when used with
+-       * lowspeed devices like mice.
+-       */
+-      vendor:         0x1022,
+-      device:         0x740c,
+-      subvendor:      PCI_ANY_ID,
+-      subdevice:      PCI_ANY_ID,
+-
+-      driver_data:    OHCI_QUIRK_AMD756,
+-
+-} , {
+-
+-      /* handle any USB OHCI controller */
+-      class:          ((PCI_CLASS_SERIAL_USB << 8) | 0x10),
+-      class_mask:     ~0,
+-
+-      /* no matter who makes it */
+-      vendor:         PCI_ANY_ID,
+-      device:         PCI_ANY_ID,
+-      subvendor:      PCI_ANY_ID,
+-      subdevice:      PCI_ANY_ID,
+-
+-      }, { /* end: all zeroes */ }
+-};
+-
+-MODULE_DEVICE_TABLE (pci, ohci_pci_ids);
+-
+-static struct pci_driver ohci_pci_driver = {
+-      name:           "usb-ohci",
+-      id_table:       &ohci_pci_ids [0],
+-
+-      probe:          ohci_pci_probe,
+-      remove:         __devexit_p(ohci_pci_remove),
+-
+-#ifdef        CONFIG_PM
+-      suspend:        ohci_pci_suspend,
+-      resume:         ohci_pci_resume,
+-#endif        /* PM */
+-};
+-
+- 
+-/*-------------------------------------------------------------------------*/
+-
+-static int __init ohci_hcd_init (void) 
+-{
+-      return pci_module_init (&ohci_pci_driver);
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void __exit ohci_hcd_cleanup (void) 
+-{     
+-      pci_unregister_driver (&ohci_pci_driver);
+-}
+-
+-module_init (ohci_hcd_init);
+-module_exit (ohci_hcd_cleanup);
+-
+-
+ MODULE_AUTHOR( DRIVER_AUTHOR );
+ MODULE_DESCRIPTION( DRIVER_DESC );
+ MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/usb/host/usb-ohci.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/usb/host/usb-ohci.h
+@@ -384,12 +384,13 @@
+ #define OHCI_QUIRK_SUCKYIO    0x02            /* NSC superio */
+       struct ohci_regs * regs;        /* OHCI controller's memory */
++      struct list_head ohci_hcd_list; /* list of all ohci_hcd */
++      struct ohci * next;             // chain of ohci device contexts
+       struct list_head timeout_list;
+       // struct list_head urb_list;   // list of all pending urbs
+-      spinlock_t ohci_lock;           /* Covers all fields up & down */
+-      struct urb *complete_head, *complete_tail;
+-
++      // spinlock_t urb_list_lock;    // lock to keep consistency 
++  
+       int ohci_int_load[32];          /* load of the 32 Interrupt Chains (for load balancing)*/
+       ed_t * ed_rm_list[2];     /* lists of all endpoints to be removed */
+       ed_t * ed_bulktail;       /* last endpoint of bulk list */
+@@ -403,6 +404,7 @@
+       /* PCI device handle, settings, ... */
+       struct pci_dev  *ohci_dev;
++      const char      *slot_name;
+       u8              pci_latency;
+       struct pci_pool *td_cache;
+       struct pci_pool *dev_cache;
+@@ -448,7 +450,7 @@
+ #endif
+  
+ #ifndef CONFIG_PCI
+-#     error "usb-ohci currently requires PCI-based controllers"
++//#   error "usb-ohci currently requires PCI-based controllers"
+       /* to support non-PCI OHCIs, you need custom bus/mem/... glue */
+ #endif
+@@ -641,3 +643,6 @@
+       pci_pool_free (hc->dev_cache, dev, dev->dma);
+ }
++/* For initializing controller (mask in an HCFS mode too) */
++#define       OHCI_CONTROL_INIT \
++      (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+--- linux-2.4.27/drivers/video/Config.in~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/Config.in
+@@ -30,13 +30,28 @@
+          tristate '  Permedia3 support (EXPERIMENTAL)' CONFIG_FB_PM3
+       fi
+    fi
+-   if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+-      bool '  Acorn VIDC support' CONFIG_FB_ACORN
+-   fi
+-   dep_tristate '  Cyber2000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI
+-   if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+-      bool '  SA-1100 LCD support' CONFIG_FB_SA1100
++   if [ "$CONFIG_ARM" = "y" ]; then
++     if [ "$CONFIG_ARCH_ACORN" -o "$CONFIG_ARCH_RISCSTATION" ]; then
++      bool '  Acorn VIDC support' CONFIG_FB_ACORN
++     else
++      define_bool CONFIG_FB_ACORN n
++     fi
++     dep_bool '  Anakin LCD support' CONFIG_FB_ANAKIN $CONFIG_ARCH_ANAKIN
++     dep_bool '  CLPS711X LCD support' CONFIG_FB_CLPS711X $CONFIG_ARCH_CLPS711X
++     dep_bool '  SA-1100 LCD support' CONFIG_FB_SA1100 $CONFIG_ARCH_SA1100
++     dep_bool '  MX1ADS LCD support' CONFIG_FB_DBMX1 $CONFIG_ARCH_MX1ADS
++     if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF" = "y" ]; then
++        choice 'CerfBoard LCD Display Size' \
++              "3.8_Color              CONFIG_CERF_LCD_38_A \
++               3.8_Mono               CONFIG_CERF_LCD_38_B \
++               5.7            CONFIG_CERF_LCD_57_A \
++               7.2            CONFIG_CERF_LCD_72_A" 5.7
++     fi
++     if [ "$CONFIG_FB_SA1100" = "y" -a "$CONFIG_SA1100_CERF_CPLD" = "y" ]; then
++       bool 'Cerfboard Backlight (CerfPDA)' CONFIG_SA1100_CERF_LCD_BACKLIGHT
++     fi
+    fi
++   dep_tristate '  CyberPro 2000/2010/5000 support' CONFIG_FB_CYBER2000 $CONFIG_PCI
+    if [ "$CONFIG_APOLLO" = "y" ]; then
+       define_bool CONFIG_FB_APOLLO y
+    fi
+@@ -265,7 +280,7 @@
+          "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \
+          "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \
+          "$CONFIG_FB_BWTWO" = "y" -o "$CONFIG_FB_CLGEN" = "y"  -o \
+-         "$CONFIG_FB_TX3912" = "y" ]; then
++         "$CONFIG_FB_TX3912" = "y" -o "$CONFIG_FB_CLPS711X" = "y" ]; then
+        define_tristate CONFIG_FBCON_MFB y
+       else
+        if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \
+@@ -273,19 +288,20 @@
+             "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \
+             "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \
+             "$CONFIG_FB_BWTWO" = "m" -o "$CONFIG_FB_CLGEN" = "m" -o \
+-            "$CONFIG_FB_TX3912" = "m" ]; then
++            "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m"  ]; then
+           define_tristate CONFIG_FBCON_MFB m
+        fi
+       fi
+       if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \
+          "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" -o \
+-         "$CONFIG_FB_TX3912" = "y" ]; then
++         "$CONFIG_FB_TX3912" = "y"  -o "$CONFIG_FB_CLPS711X" = "y" -o \
++           "$CONFIG_FB_DBMX1" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB2 y
+        define_tristate CONFIG_FBCON_CFB4 y
+       else
+        if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \
+             "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" -o \
+-            "$CONFIG_FB_TX3912" = "m" ]; then
++            "$CONFIG_FB_TX3912" = "m" -o "$CONFIG_FB_CLPS711X" = "m" ]; then
+           define_tristate CONFIG_FBCON_CFB2 m
+           define_tristate CONFIG_FBCON_CFB4 m
+        fi
+@@ -312,7 +328,8 @@
+          "$CONFIG_FB_TX3912" = "y" -o \
+          "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_NEOMAGIC" = "y" -o \
+          "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_HP300" = "y" -o \
+-         "$CONFIG_FB_INTEL" = "y" ]; then
++         "$CONFIG_FB_INTEL" = "y" -o \
++           "$CONFIG_FB_DBMX1" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB8 y
+       else
+        if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
+@@ -354,7 +371,9 @@
+          "$CONFIG_FB_CYBER2000" = "y" -o "$CONFIG_FB_3DFX" = "y"  -o \
+          "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_SA1100" = "y" -o \
+          "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_VOODOO1" = "y" -o \
+-         "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" ]; then
++         "$CONFIG_FB_NEOMAGIC" = "y" -o "$CONFIG_FB_INTEL" = "y" -o \
++         "$CONFIG_FB_ANAKIN" = "y" -o \
++           "$CONFIG_FB_DBMX1" = "y" ]; then
+        define_tristate CONFIG_FBCON_CFB16 y
+       else
+        if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
+@@ -509,6 +528,9 @@
+        if [ "$CONFIG_AMIGA" = "y" ]; then
+           define_bool CONFIG_FONT_PEARL_8x8 y
+        fi
++       if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_RISCSTATION" = "y" ]; then
++          define_bool CONFIG_FONT_ACORN_8x8 y
++       fi
+        if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_ACORN" = "y" ]; then
+           define_bool CONFIG_FONT_ACORN_8x8 y
+        fi
+--- linux-2.4.27/drivers/video/Makefile~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/Makefile
+@@ -55,6 +55,7 @@
+ obj-$(CONFIG_FB_PLATINUM)         += platinumfb.o
+ obj-$(CONFIG_FB_VALKYRIE)         += valkyriefb.o
+ obj-$(CONFIG_FB_CT65550)          += chipsfb.o
++obj-$(CONFIG_FB_CLPS711X)         += clps711xfb.o
+ obj-$(CONFIG_FB_CYBER)            += cyberfb.o
+ obj-$(CONFIG_FB_CYBER2000)        += cyber2000fb.o
+ obj-$(CONFIG_FB_SGIVW)            += sgivwfb.o
+@@ -68,7 +69,7 @@
+ obj-$(CONFIG_FB_TRIDENT)          += tridentfb.o fbgen.o
+ obj-$(CONFIG_FB_S3TRIO)           += S3triofb.o
+ obj-$(CONFIG_FB_TGA)              += tgafb.o fbgen.o
+-obj-$(CONFIG_FB_VESA)             += vesafb.o 
++obj-$(CONFIG_FB_VESA)             += vesafb.o
+ obj-$(CONFIG_FB_VGA16)            += vga16fb.o fbcon-vga-planes.o
+ obj-$(CONFIG_FB_VIRGE)            += virgefb.o
+ obj-$(CONFIG_FB_G364)             += g364fb.o
+@@ -126,14 +127,16 @@
+ obj-$(CONFIG_FB_SUN3)             += sun3fb.o
+ obj-$(CONFIG_FB_BWTWO)            += bwtwofb.o
+-obj-$(CONFIG_FB_HGA)              += hgafb.o  
++obj-$(CONFIG_FB_HGA)              += hgafb.o
+ obj-$(CONFIG_FB_SA1100)           += sa1100fb.o
+-obj-$(CONFIG_FB_VIRTUAL)          += vfb.o  
++obj-$(CONFIG_FB_DBMX1)            += dbmx1fb.o
++obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
+ obj-$(CONFIG_FB_HIT)              += hitfb.o fbgen.o
+ obj-$(CONFIG_FB_E1355)            += epson1355fb.o fbgen.o
+ obj-$(CONFIG_FB_E1356)            += epson1356fb.o
+ obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
+ obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
++obj-$(CONFIG_FB_ANAKIN)           += anakinfb.o
+ # Generic Low Level Drivers
+@@ -169,4 +172,3 @@
+           -e 's/dfont\(_uni.*\]\)/promfont\1 __initdata/' > promcon_tbl.c
+ promcon_tbl.o: promcon_tbl.c $(TOPDIR)/include/linux/types.h
+-
+--- linux-2.4.27/drivers/video/acornfb.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/acornfb.c
+@@ -752,11 +752,12 @@
+       }
+ #endif
+ #ifdef FBCON_HAS_CFB16
+-      if (bpp == 16 && regno < 16) {
++      if (bpp == 16) {
+               int i;
+-              current_par.cmap.cfb16[regno] =
+-                              regno | regno << 5 | regno << 10;
++              if (regno < 16)
++                      current_par.cmap.cfb16[regno] =
++                                      regno | regno << 5 | regno << 10;
+               pal.p = 0;
+               vidc_writel(0x10000000);
+@@ -1215,7 +1216,7 @@
+                               p.vidc20.green = current_par.palette[(i >> 1) & 31].vidc20.green;
+                               p.vidc20.blue  = current_par.palette[(i >> 2) & 31].vidc20.blue;
+                       }
+-                      acornfb_palette_write(i, current_par.palette[i]);
++                      acornfb_palette_write(i, p);
+               }
+       } else
+ #endif
+--- /dev/null
++++ linux-2.4.27/drivers/video/anakinfb.c
+@@ -0,0 +1,221 @@
++/*
++ *  linux/drivers/video/anakinfb.c
++ *
++ *  Copyright (C) 2001 Aleph One Ltd. for Acunia N.V.
++ *
++ * 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.
++ *
++ *  Changelog:
++ *   23-Apr-2001 TTC  Created
++ */
++
++#include <linux/types.h>
++#include <linux/fb.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <asm/io.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-cfb16.h>
++
++static u16 colreg[16];
++static int currcon = 0;
++static struct fb_info fb_info;
++static struct display display;
++
++static int
++anakinfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
++                      u_int *transp, struct fb_info *info)
++{
++      if (regno > 15)
++              return 1;
++
++      *red = colreg[regno] & 0xf800;
++      *green = colreg[regno] & 0x7e0 << 5;
++      *blue = colreg[regno] & 0x1f << 11;
++      *transp = 0;
++      return 0;
++}
++
++static int
++anakinfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                      u_int transp, struct fb_info *info)
++{
++      if (regno > 15)
++              return 1;
++
++      colreg[regno] = (red & 0xf800) | (green & 0xfc00 >> 5) |
++                      (blue & 0xf800 >> 11);
++      return 0;
++}
++
++static int
++anakinfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++      memset(fix, 0, sizeof(struct fb_fix_screeninfo));
++      strcpy(fix->id, "AnakinFB");
++      fix->smem_start = VGA_START;
++      fix->smem_len = VGA_SIZE;
++      fix->type = FB_TYPE_PACKED_PIXELS;
++      fix->type_aux = 0;
++      fix->visual = FB_VISUAL_TRUECOLOR;
++      fix->xpanstep = 0;
++      fix->ypanstep = 0;
++      fix->ywrapstep = 0;
++        fix->line_length = 400 * 2;
++      fix->accel = FB_ACCEL_NONE;
++      return 0;
++}
++        
++static int
++anakinfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      memset(var, 0, sizeof(struct fb_var_screeninfo));
++      var->xres = 400;
++      var->yres = 234;
++      var->xres_virtual = 400;
++      var->yres_virtual = 234;
++      var->xoffset = 0;
++      var->yoffset = 0;
++      var->bits_per_pixel = 16;
++      var->grayscale = 0;
++      var->red.offset = 11;
++      var->red.length = 5;
++      var->green.offset = 5;
++      var->green.length = 6;
++      var->blue.offset = 0;
++      var->blue.length = 5;
++      var->transp.offset = 0;
++      var->transp.length = 0;
++      var->nonstd = 0;
++      var->activate = FB_ACTIVATE_NOW;
++      var->height = -1;
++      var->width = -1;
++      var->pixclock = 0;
++      var->left_margin = 0;
++      var->right_margin = 0;
++      var->upper_margin = 0;
++      var->lower_margin = 0;
++      var->hsync_len = 0;
++      var->vsync_len = 0;
++      var->sync = 0;
++      var->vmode = FB_VMODE_NONINTERLACED;
++      return 0;
++}
++
++static int
++anakinfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      return -EINVAL;
++}
++
++static int
++anakinfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++                      struct fb_info *info)
++{
++      if (con == currcon)
++              return fb_get_cmap(cmap, kspc, anakinfb_getcolreg, info);
++      else if (fb_display[con].cmap.len)
++              fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
++      else
++              fb_copy_cmap(fb_default_cmap(16), cmap, kspc ? 0 : 2);
++      return 0;
++}
++
++static int
++anakinfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++                      struct fb_info *info)
++{
++      int err;
++
++      if (!fb_display[con].cmap.len) {
++              if ((err = fb_alloc_cmap(&fb_display[con].cmap, 16, 0)))
++                      return err;
++      }
++      if (con == currcon)
++              return fb_set_cmap(cmap, kspc, anakinfb_setcolreg, info);
++      else
++              fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
++      return 0;
++}
++
++static int
++anakinfb_switch_con(int con, struct fb_info *info)
++{ 
++      currcon = con;
++      return 0;
++
++}
++
++static int
++anakinfb_updatevar(int con, struct fb_info *info)
++{
++      return 0;
++}
++
++static void
++anakinfb_blank(int blank, struct fb_info *info)
++{
++      /*
++       * TODO: use I2C to blank/unblank the screen
++       */
++}
++
++static struct fb_ops anakinfb_ops = {
++      owner:          THIS_MODULE,
++      fb_get_fix:     anakinfb_get_fix,
++      fb_get_var:     anakinfb_get_var,
++      fb_set_var:     anakinfb_set_var,
++      fb_get_cmap:    anakinfb_get_cmap,
++      fb_set_cmap:    anakinfb_set_cmap,
++};
++
++int __init
++anakinfb_init(void)
++{
++      memset(&fb_info, 0, sizeof(struct fb_info));
++      strcpy(fb_info.modename, "AnakinFB");
++      fb_info.node = -1;
++      fb_info.flags = FBINFO_FLAG_DEFAULT;
++      fb_info.fbops = &anakinfb_ops;
++      fb_info.disp = &display;
++      strcpy(fb_info.fontname, "VGA8x16");
++      fb_info.changevar = NULL;
++      fb_info.switch_con = &anakinfb_switch_con;
++      fb_info.updatevar = &anakinfb_updatevar;
++      fb_info.blank = &anakinfb_blank;
++
++      memset(&display, 0, sizeof(struct display));
++      anakinfb_get_var(&display.var, 0, &fb_info);
++      display.screen_base = ioremap(VGA_START, VGA_SIZE);
++      display.visual = FB_VISUAL_TRUECOLOR;
++      display.type = FB_TYPE_PACKED_PIXELS;
++      display.type_aux = 0;
++      display.ypanstep = 0;
++      display.ywrapstep = 0;
++      display.line_length = 400 * 2;
++      display.can_soft_blank = 1;
++      display.inverse = 0;
++
++#ifdef FBCON_HAS_CFB16
++      display.dispsw = &fbcon_cfb16;
++      display.dispsw_data = colreg;
++#else
++      display.dispsw = &fbcon_dummy;
++#endif
++
++      if (register_framebuffer(&fb_info) < 0)
++              return -EINVAL;
++
++      MOD_INC_USE_COUNT;
++      return 0;
++}
++
++MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>");
++MODULE_DESCRIPTION("Anakin framebuffer driver");
++MODULE_SUPPORTED_DEVICE("fb");
+--- /dev/null
++++ linux-2.4.27/drivers/video/clps711xfb.c
+@@ -0,0 +1,677 @@
++/*
++ *  linux/drivers/video/clps711xfb.c
++ *
++ *  Copyright (C) 2000-2001 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
++ * 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
++ *
++ *  Framebuffer driver for the CLPS7111 and EP7212 processors.
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/delay.h>
++
++#include <video/fbcon.h>
++#include <video/fbcon-cfb4.h>
++#include <video/fbcon-cfb2.h>
++#include <video/fbcon-mfb.h>
++
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++
++#include <asm/hardware/clps7111.h>
++#include <asm/arch/syspld.h>
++
++static struct clps7111fb_info {
++      struct fb_info          fb;
++      int                     currcon;
++} *cfb;
++
++#define CMAP_MAX_SIZE 16
++
++/* The /proc entry for the backlight. */
++static struct proc_dir_entry *clps7111fb_backlight_proc_entry = NULL;
++
++static int clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
++              int count, int *eof, void *data);
++static int clps7111fb_proc_backlight_write(struct file *file, 
++              const char *buffer, unsigned long count, void *data);
++
++/*
++ * LCD AC Prescale.  This comes from the LCD panel manufacturers specifications.
++ * This determines how many clocks + 1 of CL1 before the M signal toggles.
++ * The number of lines on the display must not be divisible by this number.
++ */
++static unsigned int lcd_ac_prescale = 13;
++
++/*
++ *    Set a single color register. Return != 0 for invalid regno.
++ */
++static int
++clps7111fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                   u_int transp, struct fb_info *info)
++{
++      unsigned int level, mask, shift, pal;
++
++      if (regno >= (1 << info->var.bits_per_pixel))
++              return 1;
++
++      /* gray = 0.30*R + 0.58*G + 0.11*B */
++      level = (red * 77 + green * 151 + blue * 28) >> 20;
++
++      /*
++       * On an LCD, a high value is dark, while a low value is light. 
++       * So we invert the level.
++       *
++       * This isn't true on all machines, so we only do it on EDB7211.
++       *  --rmk
++       */
++      if (machine_is_edb7211() || machine_is_guidea07()) {
++              level = 15 - level;
++      }
++
++      shift = 4 * (regno & 7);
++      level <<= shift;
++      mask  = 15 << shift;
++      level &= mask;
++
++      regno = regno < 8 ? PALLSW : PALMSW;
++
++      pal = clps_readl(regno);
++      pal = (pal & ~mask) | level;
++      clps_writel(pal, regno);
++
++      return 0;
++}
++                  
++/*
++ * Set the colormap
++ */
++static int
++clps7111fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++                  struct fb_info *info)
++{
++      struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
++      struct fb_cmap *dcmap = &fb_display[con].cmap;
++      int err = 0;
++
++      /* no colormap allocated? */
++      if (!dcmap->len)
++              err = fb_alloc_cmap(dcmap, CMAP_MAX_SIZE, 0);
++
++      if (!err && con == cfb->currcon) {
++              err = fb_set_cmap(cmap, kspc, clps7111fb_setcolreg, &cfb->fb);
++              dcmap = &cfb->fb.cmap;
++      }
++
++      if (!err)
++              fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
++
++      return err;
++}
++
++/*
++ *    Set the User Defined Part of the Display
++ */
++static int
++clps7111fb_set_var(struct fb_var_screeninfo *var, int con,
++                 struct fb_info *info)
++{
++      struct display *display;
++      unsigned int lcdcon, syscon, pixclock;
++      int chgvar = 0;
++
++      if (var->activate & FB_ACTIVATE_TEST)
++              return 0;
++
++      if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
++              return -EINVAL;
++
++      if (cfb->fb.var.xres != var->xres)
++              chgvar = 1;
++      if (cfb->fb.var.yres != var->yres)
++              chgvar = 1;
++      if (cfb->fb.var.xres_virtual != var->xres_virtual)
++              chgvar = 1;
++      if (cfb->fb.var.yres_virtual != var->yres_virtual)
++              chgvar = 1;
++      if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
++              chgvar = 1;
++
++      if (con < 0) {
++              display = cfb->fb.disp;
++              chgvar = 0;
++      } else {
++              display = fb_display + con;
++      }
++
++      var->transp.msb_right   = 0;
++      var->transp.offset      = 0;
++      var->transp.length      = 0;
++      var->red.msb_right      = 0;
++      var->red.offset         = 0;
++      var->red.length         = var->bits_per_pixel;
++      var->green              = var->red;
++      var->blue               = var->red;
++
++      switch (var->bits_per_pixel) {
++#ifdef FBCON_HAS_MFB
++      case 1:
++              cfb->fb.fix.visual      = FB_VISUAL_MONO01;
++              display->dispsw         = &fbcon_mfb;
++              display->dispsw_data    = NULL;
++              break;
++#endif
++#ifdef FBCON_HAS_CFB2
++      case 2:
++              cfb->fb.fix.visual      = FB_VISUAL_PSEUDOCOLOR;
++              display->dispsw         = &fbcon_cfb2;
++              display->dispsw_data    = NULL;
++              break;
++#endif
++#ifdef FBCON_HAS_CFB4
++      case 4:
++              cfb->fb.fix.visual      = FB_VISUAL_PSEUDOCOLOR;
++              display->dispsw         = &fbcon_cfb4;
++              display->dispsw_data    = NULL;
++              break;
++#endif
++      default:
++              return -EINVAL;
++      }
++
++      display->next_line      = var->xres_virtual * var->bits_per_pixel / 8;
++
++      cfb->fb.fix.line_length = display->next_line;
++
++      display->screen_base    = cfb->fb.screen_base;
++      display->line_length    = cfb->fb.fix.line_length;
++      display->visual         = cfb->fb.fix.visual;
++      display->type           = cfb->fb.fix.type;
++      display->type_aux       = cfb->fb.fix.type_aux;
++      display->ypanstep       = cfb->fb.fix.ypanstep;
++      display->ywrapstep      = cfb->fb.fix.ywrapstep;
++      display->can_soft_blank = 1;
++      display->inverse        = 0;
++
++      cfb->fb.var             = *var;
++      cfb->fb.var.activate    &= ~FB_ACTIVATE_ALL;
++
++      /*
++       * Update the old var.  The fbcon drivers still use this.
++       * Once they are using cfb->fb.var, this can be dropped.
++       *                                      --rmk
++       */
++      display->var            = cfb->fb.var;
++
++      /*
++       * If we are setting all the virtual consoles, also set the
++       * defaults used to create new consoles.
++       */
++      if (var->activate & FB_ACTIVATE_ALL)
++              cfb->fb.disp->var = cfb->fb.var;
++
++      if (chgvar && info && cfb->fb.changevar)
++              cfb->fb.changevar(con);
++
++      lcdcon = (var->xres_virtual * var->yres_virtual * var->bits_per_pixel) / 128 - 1;
++      lcdcon |= ((var->xres_virtual / 16) - 1) << 13;
++      lcdcon |= lcd_ac_prescale << 25;
++
++      /*
++       * Calculate pixel prescale value from the pixclock.  This is:
++       *  36.864MHz / pixclock_mhz - 1.
++       * However, pixclock is in picoseconds, so this ends up being:
++       *  36864000 * pixclock_ps / 10^12 - 1
++       * and this will overflow the 32-bit math.  We perform this as
++       * (9 * 4096000 == 36864000):
++       *  pixclock_ps * 9 * (4096000 / 10^12) - 1
++       */
++      pixclock = 9 * info->var.pixclock / 244140 - 1;
++      lcdcon |= pixclock << 19;
++
++      if (info->var.bits_per_pixel == 4)
++              lcdcon |= LCDCON_GSMD;
++      if (info->var.bits_per_pixel >= 2)
++              lcdcon |= LCDCON_GSEN;
++
++      /*
++       * LCDCON must only be changed while the LCD is disabled
++       */
++      syscon = clps_readl(SYSCON1);
++      clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
++      clps_writel(lcdcon, LCDCON);
++      clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
++
++      fb_set_cmap(&cfb->fb.cmap, 1, clps7111fb_setcolreg, &cfb->fb);
++
++      return 0;
++}
++
++/*
++ * Get the currently displayed virtual consoles colormap.
++ */
++static int
++gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++      fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
++      return 0;
++}
++
++/*
++ * Get the currently displayed virtual consoles fixed part of the display.
++ */
++static int
++gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++      *fix = info->fix;
++      return 0;
++}
++
++/*
++ * Get the current user defined part of the display.
++ */
++static int
++gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      *var = info->var;
++      return 0;
++}
++
++static struct fb_ops clps7111fb_ops = {
++      owner:          THIS_MODULE,
++      fb_set_var:     clps7111fb_set_var,
++      fb_set_cmap:    clps7111fb_set_cmap,
++      fb_get_fix:     gen_get_fix,
++      fb_get_var:     gen_get_var,
++      fb_get_cmap:    gen_get_cmap,
++};
++
++static int clps7111fb_switch(int con, struct fb_info *info)
++{
++      struct clps7111fb_info *cfb = (struct clps7111fb_info *)info;
++      struct display *disp;
++      struct fb_cmap *cmap;
++
++      if (cfb->currcon >= 0) {
++              disp = fb_display + cfb->currcon;
++
++              /*
++               * Save the old colormap and video mode.
++               */
++              disp->var = cfb->fb.var;
++              if (disp->cmap.len)
++                      fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
++      }
++
++      cfb->currcon = con;
++      disp = fb_display + con;
++
++      /*
++       * Install the new colormap and change the video mode.  By default,
++       * fbcon sets all the colormaps and video modes to the default
++       * values at bootup.
++       */
++      if (disp->cmap.len)
++              cmap = &disp->cmap;
++      else
++              cmap = fb_default_cmap(CMAP_MAX_SIZE);
++
++      fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
++
++      cfb->fb.var = disp->var;
++      cfb->fb.var.activate = FB_ACTIVATE_NOW;
++
++      clps7111fb_set_var(&cfb->fb.var, con, &cfb->fb);
++
++      return 0;
++}
++
++static int clps7111fb_updatevar(int con, struct fb_info *info)
++{
++      return -EINVAL;
++}
++
++static void clps7111fb_blank(int blank, struct fb_info *info)
++{
++      if (blank) {
++              if (machine_is_edb7211()) {
++                      /* Turn off the LCD backlight. */
++                      clps_writeb(clps_readb(PDDR) & ~EDB_PD3_LCDBL, PDDR);
++
++                      /* Power off the LCD DC-DC converter. */
++                      clps_writeb(clps_readb(PDDR) & ~EDB_PD1_LCD_DC_DC_EN, PDDR);
++
++                      /* Delay for a little while (half a second). */
++                      udelay(100);
++
++                      /* Power off the LCD panel. */
++                      clps_writeb(clps_readb(PDDR) & ~EDB_PD2_LCDEN, PDDR);
++
++                      /* Power off the LCD controller. */
++                      clps_writel(clps_readl(SYSCON1) & ~SYSCON1_LCDEN, 
++                                      SYSCON1);
++              }
++      } else {
++              if (machine_is_edb7211()) {
++                      /* Power up the LCD controller. */
++                      clps_writel(clps_readl(SYSCON1) | SYSCON1_LCDEN,
++                                      SYSCON1);
++
++                      /* Power up the LCD panel. */
++                      clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
++
++                      /* Delay for a little while. */
++                      udelay(100);
++
++                      /* Power up the LCD DC-DC converter. */
++                      clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN,
++                                              PDDR);
++
++                      /* Turn on the LCD backlight. */
++                      clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
++              }
++      }
++}
++
++static int 
++clps7111fb_proc_backlight_read(char *page, char **start, off_t off,
++              int count, int *eof, void *data)
++{
++      /* We need at least two characters, one for the digit, and one for
++       * the terminating NULL. */
++      if (count < 2) 
++              return -EINVAL;
++
++      if (machine_is_edb7211()) {
++              return sprintf(page, "%d\n", 
++                              (clps_readb(PDDR) & EDB_PD3_LCDBL) ? 1 : 0);
++      }
++
++      return 0;
++}
++
++static int 
++clps7111fb_proc_backlight_write(struct file *file, const char *buffer, 
++              unsigned long count, void *data)
++{
++      unsigned char char_value;
++      int value;
++
++      if (count < 1) {
++              return -EINVAL;
++      }
++
++      if (copy_from_user(&char_value, buffer, 1)) 
++              return -EFAULT;
++
++      value = char_value - '0';
++
++      if (machine_is_edb7211()) {
++              unsigned char port_d;
++
++              port_d = clps_readb(PDDR);
++
++              if (value) {
++                      port_d |= EDB_PD3_LCDBL;
++              } else {
++                      port_d &= ~EDB_PD3_LCDBL;
++              }
++
++              clps_writeb(port_d, PDDR);
++      }
++
++      return count;
++}
++
++static void __init clps711x_guess_lcd_params(struct fb_info *info)
++{
++      unsigned int lcdcon, syscon, size;
++      unsigned long phys_base = PHYS_OFFSET;
++      void *virt_base = (void *)PAGE_OFFSET;
++
++      info->var.xres_virtual   = 640;
++      info->var.yres_virtual   = 240;
++      info->var.bits_per_pixel = 4;
++      info->var.activate       = FB_ACTIVATE_NOW;
++      info->var.height         = -1;
++      info->var.width          = -1;
++      info->var.pixclock       = 93006; /* 10.752MHz pixel clock */
++
++      /*
++       * If the LCD controller is already running, decode the values
++       * in LCDCON to xres/yres/bpp/pixclock/acprescale
++       */
++      syscon = clps_readl(SYSCON1);
++      if (syscon & SYSCON1_LCDEN) {
++              lcdcon = clps_readl(LCDCON);
++
++              /*
++               * Decode GSMD and GSEN bits to bits per pixel
++               * (in cs89712 docs, GSEN is GSMD1, GSMD is GSMD2)
++               */
++              switch (lcdcon & (LCDCON_GSMD | LCDCON_GSEN)) {
++              case LCDCON_GSMD | LCDCON_GSEN:
++                      info->var.bits_per_pixel = 4;
++                      break;
++
++              case LCDCON_GSEN:
++                      info->var.bits_per_pixel = 2;
++                      break;
++
++              default:
++                      info->var.bits_per_pixel = 1;
++                      break;
++              }
++
++              /*
++               * Decode xres/yres
++               */
++              info->var.xres_virtual = (((lcdcon >> 13) & 0x3f) + 1) * 16;
++              info->var.yres_virtual = (((lcdcon & 0x1fff) + 1) * 128) /
++                                        (info->var.xres_virtual *
++                                         info->var.bits_per_pixel);
++
++              /*
++               * Calculate pixclock
++               */
++              info->var.pixclock = (((lcdcon >> 19) & 0x3f) + 1) * 244140 / 9;
++
++              /*
++               * Grab AC prescale
++               */
++              lcd_ac_prescale = (lcdcon >> 25) & 0x1f;
++      }
++
++      info->var.xres = info->var.xres_virtual;
++      info->var.yres = info->var.yres_virtual;
++      info->var.grayscale = info->var.bits_per_pixel > 1;
++
++      size = info->var.xres * info->var.yres * info->var.bits_per_pixel / 8;
++
++      /*
++       * Might be worth checking to see if we can use the on-board
++       * RAM if size here...
++       * CLPS7110 - no on-board SRAM
++       * EP7212   - 38400 bytes
++       */
++      if (size <= 38400) {
++              printk(KERN_INFO "CLPS711xFB: could use on-board SRAM?\n");
++      }
++
++      if ((syscon & SYSCON1_LCDEN) == 0) {
++              /*
++               * The display isn't running.  Ensure that
++               * the display memory is empty.
++               */
++              memset(virt_base, 0, size);
++      }
++
++      info->screen_base    = virt_base;
++      info->fix.smem_start = phys_base;
++      info->fix.smem_len   = PAGE_ALIGN(size);
++      info->fix.type       = FB_TYPE_PACKED_PIXELS;
++}
++
++/* this function initializes the LCD registers as required by the Guide A07
++*/
++int __init clps711xfb_guidea07_init(void)
++{
++      unsigned long videomem, videosize;
++      unsigned int lcdcon, syscon;
++
++      //LCDCON must only be changed while the LCD is disabled
++      syscon = clps_readl(SYSCON1);
++      clps_writel(syscon & ~SYSCON1_LCDEN, SYSCON1);
++
++      printk(KERN_INFO "CLPS711xFB: setting up cs89712 SRAM as QVGA video framebuffer\n");
++      clps_writel(0x6, FBADDR); //internal SRAM location
++      videomem = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
++      if (!videomem) {
++              printk("ioremap failed!\n");
++              kfree(cfb);
++              return -EIO;
++      }
++
++      videosize = (320*240 * 4 / 8);  //ie. 38400 or 0x9600
++      memset((void *)videomem, 0, videosize); //clear the vid memory
++
++      cfb->fb.screen_base     = (void *)videomem;
++      cfb->fb.fix.smem_start  = SRAM_START; //must be physical address
++      cfb->fb.fix.smem_len    = videosize;
++
++      cfb->fb.var.grayscale   = 1;
++        cfb->fb.var.bits_per_pixel = 4;
++      cfb->fb.var.xres_virtual = 320;
++      cfb->fb.var.activate     = FB_ACTIVATE_NOW;
++
++      // change to 4bpp mode
++      lcdcon = clps_readl(LCDCON);
++      lcdcon |= LCDCON_GSMD | LCDCON_GSEN; //4bpp
++        //lcdcon |= 5 << 19;  //pixel prescale value (pixclock)
++      //lcdcon |= 13 << 25;   //AC prescale value
++      clps_writel(lcdcon, LCDCON);
++
++      //reenable LCD
++      clps_writel(syscon | SYSCON1_LCDEN, SYSCON1);
++
++      return 0;
++}
++
++int __init clps711xfb_init(void)
++{
++      int err = -ENOMEM;
++
++      cfb = kmalloc(sizeof(*cfb) + sizeof(struct display), GFP_KERNEL);
++      if (!cfb)
++              goto out;
++
++      memset(cfb, 0, sizeof(*cfb) + sizeof(struct display));
++      strcpy(cfb->fb.fix.id, "clps7111");
++
++      cfb->currcon            = -1;
++      cfb->fb.screen_base     = (void *)PAGE_OFFSET;
++      cfb->fb.fix.smem_start  = PAGE_OFFSET;
++      cfb->fb.fix.smem_len    = 0x14000;
++      cfb->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
++
++      cfb->fb.fbops           = &clps7111fb_ops;
++      cfb->fb.changevar       = NULL;
++      cfb->fb.switch_con      = clps7111fb_switch;
++      cfb->fb.updatevar       = clps7111fb_updatevar;
++      cfb->fb.blank           = clps7111fb_blank;
++      cfb->fb.flags           = FBINFO_FLAG_DEFAULT;
++      cfb->fb.disp            = (struct display *)(cfb + 1);
++
++      clps711x_guess_lcd_params(&cfb->fb);
++
++      if (machine_is_guidea07()) {
++              clps711xfb_guidea07_init();
++      }
++
++      if (machine_is_fortunet()) {
++              cfb->fb.fix.smem_len = 0x20000;
++      }
++
++      fb_alloc_cmap(&cfb->fb.cmap, CMAP_MAX_SIZE, 0);
++
++      /* Register the /proc entries. */
++      clps7111fb_backlight_proc_entry = create_proc_entry("backlight", 0444,
++              &proc_root);
++      if (clps7111fb_backlight_proc_entry == NULL) {
++              printk("Couldn't create the /proc entry for the backlight.\n");
++              return -EINVAL;
++      }
++
++      clps7111fb_backlight_proc_entry->read_proc = 
++              &clps7111fb_proc_backlight_read;
++      clps7111fb_backlight_proc_entry->write_proc = 
++              &clps7111fb_proc_backlight_write;
++
++      /*
++       * Power up the LCD
++       */
++      if (machine_is_p720t()) {
++              PLD_LCDEN = PLD_LCDEN_EN;
++              PLD_PWR |= (PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
++      }
++
++      if (machine_is_edb7211()) {
++              /* Power up the LCD panel. */
++              clps_writeb(clps_readb(PDDR) | EDB_PD2_LCDEN, PDDR);
++
++              /* Delay for a little while. */
++              udelay(100);
++
++              /* Power up the LCD DC-DC converter. */
++              clps_writeb(clps_readb(PDDR) | EDB_PD1_LCD_DC_DC_EN, PDDR);
++
++              /* Turn on the LCD backlight. */
++              clps_writeb(clps_readb(PDDR) | EDB_PD3_LCDBL, PDDR);
++      }
++
++      clps7111fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++      err = register_framebuffer(&cfb->fb);
++
++out:  return err;
++}
++
++static void __exit clps711xfb_exit(void)
++{
++      unregister_framebuffer(&cfb->fb);
++      kfree(cfb);
++
++      /*
++       * Power down the LCD
++       */
++      if (machine_is_p720t()) {
++              PLD_LCDEN = 0;
++              PLD_PWR &= ~(PLD_S4_ON|PLD_S3_ON|PLD_S2_ON|PLD_S1_ON);
++      }
++}
++
++#ifdef MODULE
++module_init(clps711xfb_init);
++#endif
++module_exit(clps711xfb_exit);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("CLPS711x framebuffer driver");
++MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/video/cyber2000fb.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/cyber2000fb.c
+@@ -1,7 +1,7 @@
+ /*
+  *  linux/drivers/video/cyber2000fb.c
+  *
+- *  Copyright (C) 1998-2000 Russell King
++ *  Copyright (C) 1998-2002 Russell King
+  *
+  *  MIPS and 50xx clock support
+  *  Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com>
+@@ -13,22 +13,28 @@
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+- * Intergraphics CyberPro 2000, 2010 and 5000 frame buffer device
++ * Integraphics CyberPro 2000, 2010 and 5000 frame buffer device
+  *
+  * Based on cyberfb.c.
+  *
+- * Note that we now use the new fbcon fix, var and cmap scheme.  We do still
+- * have to check which console is the currently displayed one however, since
+- * especially for the colourmap stuff.  Once fbcon has been fully migrated,
+- * we can kill the last 5 references to cfb->currcon.
++ * Note that we now use the new fbcon fix, var and cmap scheme.  We do
++ * still have to check which console is the currently displayed one
++ * however, especially for the colourmap stuff.
+  *
+- * We also use the new hotplug PCI subsystem.  I'm not sure if there are any
+- * such cards, but I'm erring on the side of caution.  We don't want to go
+- * pop just because someone does have one.
++ * We also use the new hotplug PCI subsystem.  I'm not sure if there
++ * are any such cards, but I'm erring on the side of caution.  We don't
++ * want to go pop just because someone does have one.
+  *
+- * Note that this doesn't work fully in the case of multiple CyberPro cards
+- * with grabbers.  We currently can only attach to the first CyberPro card
+- * found.
++ * Note that this doesn't work fully in the case of multiple CyberPro
++ * cards with grabbers.  We currently can only attach to the first
++ * CyberPro card found.
++ *
++ * When we're in truecolour mode, we power down the LUT RAM as a power
++ * saving feature.  Also, when we enter any of the powersaving modes
++ * (except soft blanking) we power down the RAMDACs.  This saves about
++ * 1W, which is roughly 8% of the power consumption of a NetWinder
++ * (which, incidentally, is about the same saving as a 2.5in hard disk
++ * entering standby mode.)
+  */
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -55,20 +61,16 @@
+ #include <video/fbcon-cfb24.h>
+ #include <video/fbcon-cfb32.h>
+-/*
+- * Define this if you don't want RGB565, but RGB555 for 16bpp displays.
+- */
+-/*#define CFB16_IS_CFB15*/
+-
+ #include "cyber2000fb.h"
+ struct cfb_info {
+       struct fb_info          fb;
+       struct display_switch   *dispsw;
++      struct display          *display;
+       struct pci_dev          *dev;
+       unsigned char           *region;
+       unsigned char           *regs;
+-      signed int              currcon;
++      u_int                   id;
+       int                     func_use_count;
+       u_long                  ref_ps;
+@@ -81,10 +83,16 @@
+               u8 red, green, blue;
+       } palette[NR_PALETTE];
++      u_char                  mem_ctl0;
+       u_char                  mem_ctl1;
+       u_char                  mem_ctl2;
+       u_char                  mclk_mult;
+       u_char                  mclk_div;
++      /*
++       * RAMDAC control register is both of these or'ed together
++       */
++      u_char                  ramdac_ctrl;
++      u_char                  ramdac_powerdown;
+ };
+ static char default_font_storage[40];
+@@ -144,7 +152,7 @@
+ {
+       int count = 100000;
+-      while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & 0x80) {
++      while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & CO_CTRL_BUSY) {
+               if (!count--) {
+                       debug_printf("accel_wait timed out\n");
+                       cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);
+@@ -154,24 +162,23 @@
+       }
+ }
+-static void cyber2000_accel_setup(struct display *p)
++static void cyber2000_accel_setup(struct display *display)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+-      cfb->dispsw->setup(p);
++      cfb->dispsw->setup(display);
+ }
+ static void
+-cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
++cyber2000_accel_bmove(struct display *display, int sy, int sx, int dy, int dx,
+                     int height, int width)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
+-      struct fb_var_screeninfo *var = &p->fb_info->var;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
++      struct fb_var_screeninfo *var = &display->var;
+       u_long src, dst;
+-      u_int fh, fw;
+-      int cmd = CO_CMD_L_PATTERN_FGCOL;
++      u_int fh, fw, cmd = CO_CMD_L_PATTERN_FGCOL;
+-      fw    = fontwidth(p);
++      fw    = fontwidth(display);
+       sx    *= fw;
+       dx    *= fw;
+       width *= fw;
+@@ -183,7 +190,7 @@
+               cmd |= CO_CMD_L_INC_LEFT;
+       }
+-      fh     = fontheight(p);
++      fh     = fontheight(display);
+       sy     *= fh;
+       dy     *= fh;
+       height *= fh;
+@@ -199,227 +206,265 @@
+       dst    = dx + dy * var->xres_virtual;
+       cyber2000_accel_wait(cfb);
+-      cyber2000fb_writeb(0x00,  CO_REG_CONTROL, cfb);
+-      cyber2000fb_writeb(0x03,  CO_REG_FORE_MIX, cfb);
+-      cyber2000fb_writew(width, CO_REG_WIDTH, cfb);
++      cyber2000fb_writeb(0x00, CO_REG_CONTROL, cfb);
++      cyber2000fb_writew(width, CO_REG_PIXWIDTH, cfb);
++      cyber2000fb_writew(height, CO_REG_PIXHEIGHT, cfb);
+-      if (var->bits_per_pixel != 24) {
+-              cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+-              cyber2000fb_writel(src, CO_REG_SRC_PTR, cfb);
+-      } else {
+-              cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
+-              cyber2000fb_writeb(dst,     CO_REG_X_PHASE, cfb);
+-              cyber2000fb_writel(src * 3, CO_REG_SRC_PTR, cfb);
++      if (var->bits_per_pixel == 24) {
++              cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
++              dst *= 3;
++              src *= 3;
+       }
+-      cyber2000fb_writew(height, CO_REG_HEIGHT, cfb);
+-      cyber2000fb_writew(cmd,    CO_REG_CMD_L, cfb);
+-      cyber2000fb_writew(0x2800, CO_REG_CMD_H, cfb);
++      cyber2000fb_writel(src, CO_REG_SRC1_PTR, cfb);
++      cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
++      cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);
++      cyber2000fb_writew(cmd, CO_REG_CMD_L, cfb);
++      cyber2000fb_writew(CO_CMD_H_FGSRCMAP|CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);
+ }
+ static void
+-cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+-                    int height, int width)
++cyber2000_accel_clear(struct vc_data *conp, struct display *display, int sy,
++                    int sx, int height, int width)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
+-      struct fb_var_screeninfo *var = &p->fb_info->var;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
++      struct fb_var_screeninfo *var = &display->var;
+       u_long dst;
+       u_int fw, fh;
+-      u32 bgx = attr_bgcol_ec(p, conp);
++      u32 bgx = attr_bgcol_ec(display, conp);
+-      fw = fontwidth(p);
+-      fh = fontheight(p);
++      fw = fontwidth(display);
++      fh = fontheight(display);
+       dst    = sx * fw + sy * var->xres_virtual * fh;
+       width  = width * fw - 1;
+       height = height * fh - 1;
+       cyber2000_accel_wait(cfb);
+-      cyber2000fb_writeb(0x00,   CO_REG_CONTROL,  cfb);
+-      cyber2000fb_writeb(0x03,   CO_REG_FORE_MIX, cfb);
+-      cyber2000fb_writew(width,  CO_REG_WIDTH,    cfb);
+-      cyber2000fb_writew(height, CO_REG_HEIGHT,   cfb);
+-
+-      switch (var->bits_per_pixel) {
+-      case 15:
+-      case 16:
+-              bgx = ((u16 *)p->dispsw_data)[bgx];
+-      case 8:
+-              cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+-              break;
++      cyber2000fb_writeb(0x00, CO_REG_CONTROL, cfb);
++      cyber2000fb_writew(width, CO_REG_PIXWIDTH, cfb);
++      cyber2000fb_writew(height, CO_REG_PIXHEIGHT, cfb);
+-      case 24:
+-              cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
++      if (var->bits_per_pixel == 24) {
+               cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
+-              bgx = ((u32 *)p->dispsw_data)[bgx];
+-              break;
+-
+-      case 32:
+-              bgx = ((u32 *)p->dispsw_data)[bgx];
+-              cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
+-              break;
++              dst *= 3;
+       }
+-      cyber2000fb_writel(bgx, CO_REG_FOREGROUND, cfb);
++      if (var->bits_per_pixel == 16)
++              bgx = ((u16 *)display->dispsw_data)[bgx];
++      else if (var->bits_per_pixel >= 24)
++              bgx = ((u32 *)display->dispsw_data)[bgx];
++
++      cyber2000fb_writel(bgx, CO_REG_FGCOLOUR, cfb);
++      cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
++      cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);
+       cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb);
+-      cyber2000fb_writew(0x0800, CO_REG_CMD_H, cfb);
++      cyber2000fb_writew(CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);
+ }
+ static void
+-cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c,
++cyber2000_accel_putc(struct vc_data *conp, struct display *display, int c,
+                    int yy, int xx)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+       cyber2000_accel_wait(cfb);
+-      cfb->dispsw->putc(conp, p, c, yy, xx);
++      cfb->dispsw->putc(conp, display, c, yy, xx);
+ }
+ static void
+-cyber2000_accel_putcs(struct vc_data *conp, struct display *p,
++cyber2000_accel_putcs(struct vc_data *conp, struct display *display,
+                     const unsigned short *s, int count, int yy, int xx)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+       cyber2000_accel_wait(cfb);
+-      cfb->dispsw->putcs(conp, p, s, count, yy, xx);
++      cfb->dispsw->putcs(conp, display, s, count, yy, xx);
+ }
+-static void cyber2000_accel_revc(struct display *p, int xx, int yy)
++static void cyber2000_accel_revc(struct display *display, int xx, int yy)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+       cyber2000_accel_wait(cfb);
+-      cfb->dispsw->revc(p, xx, yy);
++      cfb->dispsw->revc(display, xx, yy);
+ }
+ static void
+-cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p,
++cyber2000_accel_clear_margins(struct vc_data *conp, struct display *display,
+                             int bottom_only)
+ {
+-      struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
++      struct cfb_info *cfb = (struct cfb_info *)display->fb_info;
+-      cfb->dispsw->clear_margins(conp, p, bottom_only);
++      cfb->dispsw->clear_margins(conp, display, bottom_only);
+ }
+ static struct display_switch fbcon_cyber_accel = {
+-      setup:          cyber2000_accel_setup,
+-      bmove:          cyber2000_accel_bmove,
+-      clear:          cyber2000_accel_clear,
+-      putc:           cyber2000_accel_putc,
+-      putcs:          cyber2000_accel_putcs,
+-      revc:           cyber2000_accel_revc,
+-      clear_margins:  cyber2000_accel_clear_margins,
+-      fontwidthmask:  FONTWIDTH(8)|FONTWIDTH(16)
++      .setup          = cyber2000_accel_setup,
++      .bmove          = cyber2000_accel_bmove,
++      .clear          = cyber2000_accel_clear,
++      .putc           = cyber2000_accel_putc,
++      .putcs          = cyber2000_accel_putcs,
++      .revc           = cyber2000_accel_revc,
++      .clear_margins  = cyber2000_accel_clear_margins,
++      .fontwidthmask  = FONTWIDTH(8)|FONTWIDTH(16)
+ };
++static inline u32 convert_bitfield(u_int val, struct fb_bitfield *bf)
++{
++      u_int mask = (1 << bf->length) - 1;
++
++      return (val >> (16 - bf->length) & mask) << bf->offset;
++}
++
+ /*
+  *    Set a single color register. Return != 0 for invalid regno.
+  */
+ static int
+-cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+-                  u_int transp, struct fb_info *info)
++cyber2000fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++                    u_int transp, struct fb_info *info)
+ {
+       struct cfb_info *cfb = (struct cfb_info *)info;
++      struct fb_var_screeninfo *var = &cfb->display->var;
++      u32 pseudo_val;
++      int ret = 1;
+-      u_int alpha = transp ^ 0xFFFF;
+-
+-      if (regno >= NR_PALETTE)
++      switch (cfb->fb.fix.visual) {
++      default:
+               return 1;
+-      red   >>= 8;
+-      green >>= 8;
+-      blue  >>= 8;
+-      alpha >>= 8;
++#ifdef FBCON_HAS_CFB8
++      /*
++       * Pseudocolour:
++       *         8     8
++       * pixel --/--+--/-->  red lut  --> red dac
++       *            |  8
++       *            +--/--> green lut --> green dac
++       *            |  8
++       *            +--/-->  blue lut --> blue dac
++       */
++      case FB_VISUAL_PSEUDOCOLOR:
++              if (regno >= NR_PALETTE)
++                      return 1;
+-      cfb->palette[regno].red   = red;
+-      cfb->palette[regno].green = green;
+-      cfb->palette[regno].blue  = blue;
++              red >>= 8;
++              green >>= 8;
++              blue >>= 8;
++
++              cfb->palette[regno].red   = red;
++              cfb->palette[regno].green = green;
++              cfb->palette[regno].blue  = blue;
+-      switch (cfb->fb.var.bits_per_pixel) {
+-#ifdef FBCON_HAS_CFB8
+-      case 8:
+               cyber2000fb_writeb(regno, 0x3c8, cfb);
+-              cyber2000fb_writeb(red,   0x3c9, cfb);
++              cyber2000fb_writeb(red, 0x3c9, cfb);
+               cyber2000fb_writeb(green, 0x3c9, cfb);
+-              cyber2000fb_writeb(blue,  0x3c9, cfb);
+-              break;
++              cyber2000fb_writeb(blue, 0x3c9, cfb);
++              return 0;
+ #endif
+-#ifdef FBCON_HAS_CFB16
+-      case 16:
+-#ifndef CFB16_IS_CFB15
+-              if (regno < 64) {
+-                      /* write green */
++      /*
++       * Direct colour:
++       *          n     rl
++       *  pixel --/--+--/-->  red lut  --> red dac
++       *             |  gl
++       *             +--/--> green lut --> green dac
++       *             |  bl
++       *             +--/-->  blue lut --> blue dac
++       * n = bpp, rl = red length, gl = green length, bl = blue length
++       */
++      case FB_VISUAL_DIRECTCOLOR:
++              red >>= 8;
++              green >>= 8;
++              blue >>= 8;
++
++              if (var->green.length == 6 && regno < 64) {
++                      cfb->palette[regno << 2].green = green;
++
++                      /*
++                       * The 6 bits of the green component are applied
++                       * to the high 6 bits of the LUT.
++                       */
+                       cyber2000fb_writeb(regno << 2, 0x3c8, cfb);
+                       cyber2000fb_writeb(cfb->palette[regno >> 1].red, 0x3c9, cfb);
+                       cyber2000fb_writeb(green, 0x3c9, cfb);
+                       cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 0x3c9, cfb);
++
++                      green = cfb->palette[regno << 3].green;
++
++                      ret = 0;
+               }
+-              if (regno < 32) {
+-                      /* write red,blue */
++              if (var->green.length >= 5 && regno < 32) {
++                      cfb->palette[regno << 3].red   = red;
++                      cfb->palette[regno << 3].green = green;
++                      cfb->palette[regno << 3].blue  = blue;
++
++                      /*
++                       * The 5 bits of each colour component are
++                       * applied to the high 5 bits of the LUT.
++                       */
+                       cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
+                       cyber2000fb_writeb(red, 0x3c9, cfb);
+-                      cyber2000fb_writeb(cfb->palette[regno << 1].green, 0x3c9, cfb);
++                      cyber2000fb_writeb(green, 0x3c9, cfb);
+                       cyber2000fb_writeb(blue, 0x3c9, cfb);
++                      ret = 0;
+               }
+-              if (regno < 16)
+-                      ((u16 *)cfb->fb.pseudo_palette)[regno] =
+-                              ((red   << 8) & 0xf800) |
+-                              ((green << 3) & 0x07e0) |
+-                              ((blue  >> 3));
+-              break;
+-#endif
++              if (var->green.length == 4 && regno < 16) {
++                      cfb->palette[regno << 4].red   = red;
++                      cfb->palette[regno << 4].green = green;
++                      cfb->palette[regno << 4].blue  = blue;
+-      case 15:
+-              if (regno < 32) {
+-                      cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
++                      /*
++                       * The 5 bits of each colour component are
++                       * applied to the high 5 bits of the LUT.
++                       */
++                      cyber2000fb_writeb(regno << 4, 0x3c8, cfb);
+                       cyber2000fb_writeb(red, 0x3c9, cfb);
+                       cyber2000fb_writeb(green, 0x3c9, cfb);
+                       cyber2000fb_writeb(blue, 0x3c9, cfb);
++                      ret = 0;
+               }
+-              if (regno < 16)
+-                      ((u16 *)cfb->fb.pseudo_palette)[regno] =
+-                              ((red   << 7) & 0x7c00) |
+-                              ((green << 2) & 0x03e0) |
+-                              ((blue  >> 3));
+-              break;
+-
+-#endif
+-
+-#ifdef FBCON_HAS_CFB24
+-      case 24:
+-              cyber2000fb_writeb(regno, 0x3c8, cfb);
+-              cyber2000fb_writeb(red,   0x3c9, cfb);
+-              cyber2000fb_writeb(green, 0x3c9, cfb);
+-              cyber2000fb_writeb(blue,  0x3c9, cfb);
+-              if (regno < 16)
+-                      ((u32 *)cfb->fb.pseudo_palette)[regno] =
+-                              (red << 16) | (green << 8) | blue;
++              /*
++               * Since this is only used for the first 16 colours, we
++               * don't have to care about overflowing for regno >= 32
++               */
++              pseudo_val = regno << var->red.offset |
++                           regno << var->green.offset |
++                           regno << var->blue.offset;
+               break;
+-#endif
+-#ifdef FBCON_HAS_CFB32
+-      case 32:
+-              cyber2000fb_writeb(regno, 0x3c8, cfb);
+-              cyber2000fb_writeb(red,   0x3c9, cfb);
+-              cyber2000fb_writeb(green, 0x3c9, cfb);
+-              cyber2000fb_writeb(blue,  0x3c9, cfb);
+-
+-              if (regno < 16)
+-                      ((u32 *)cfb->fb.pseudo_palette)[regno] =
+-                              (alpha << 24) | (red << 16) | (green << 8) | blue;
++      /*
++       * True colour:
++       *          n     rl
++       *  pixel --/--+--/--> red dac
++       *             |  gl
++       *             +--/--> green dac
++       *             |  bl
++       *             +--/--> blue dac
++       * n = bpp, rl = red length, gl = green length, bl = blue length
++       */
++      case FB_VISUAL_TRUECOLOR:
++              pseudo_val = convert_bitfield(transp ^ 0xffff, &var->transp);
++              pseudo_val |= convert_bitfield(red, &var->red);
++              pseudo_val |= convert_bitfield(green, &var->green);
++              pseudo_val |= convert_bitfield(blue, &var->blue);
+               break;
+-#endif
++      }
+-      default:
+-              return 1;
++      /*
++       * Now set our pseudo palette for the CFB16/24/32 drivers.
++       */
++      if (regno < 16) {
++              if (var->bits_per_pixel == 16)
++                      ((u16 *)cfb->fb.pseudo_palette)[regno] = pseudo_val;
++              else
++                      ((u32 *)cfb->fb.pseudo_palette)[regno] = pseudo_val;
++              ret = 0;
+       }
+-      return 0;
++      return ret;
+ }
+ struct par_info {
+@@ -428,8 +473,8 @@
+        */
+       u_char  clock_mult;
+       u_char  clock_div;
+-      u_char  visualid;
+-      u_char  pixformat;
++      u_char  extseqmisc;
++      u_char  co_pixfmt;
+       u_char  crtc_ofl;
+       u_char  crtc[19];
+       u_int   width;
+@@ -439,8 +484,7 @@
+       /*
+        * Other
+        */
+-      u_char  palette_ctrl;
+-      u_int   vmode;
++      u_char  ramdac;
+ };
+ static const u_char crtc_idx[] = {
+@@ -449,6 +493,18 @@
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+ };
++static void cyber2000fb_write_ramdac_ctrl(struct cfb_info *cfb)
++{
++      unsigned int i;
++      unsigned int val = cfb->ramdac_ctrl | cfb->ramdac_powerdown;
++
++      cyber2000fb_writeb(0x56, 0x3ce, cfb);
++      i = cyber2000fb_readb(0x3cf, cfb);
++      cyber2000fb_writeb(i | 4, 0x3cf, cfb);
++      cyber2000fb_writeb(val, 0x3c6, cfb);
++      cyber2000fb_writeb(i, 0x3cf, cfb);
++}
++
+ static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw)
+ {
+       u_int i;
+@@ -480,7 +536,7 @@
+       for (i = 0x0a; i < 0x10; i++)
+               cyber2000_crtcw(i, 0, cfb);
+-      cyber2000_grphw(0x11, hw->crtc_ofl, cfb);
++      cyber2000_grphw(EXT_CRT_VRTOFL, hw->crtc_ofl, cfb);
+       cyber2000_grphw(0x00, 0x00, cfb);
+       cyber2000_grphw(0x01, 0x00, cfb);
+       cyber2000_grphw(0x02, 0x00, cfb);
+@@ -501,30 +557,17 @@
+       cyber2000_attrw(0x13, 0x00, cfb);
+       cyber2000_attrw(0x14, 0x00, cfb);
+-      /* woody: set the interlaced bit... */
+-      /* FIXME: what about doublescan? */
+-      cyber2000fb_writeb(0x11, 0x3ce, cfb);
+-      i = cyber2000fb_readb(0x3cf, cfb);
+-      if (hw->vmode == FB_VMODE_INTERLACED)
+-              i |= 0x20;
+-      else
+-              i &= ~0x20;
+-      cyber2000fb_writeb(i, 0x3cf, cfb);
+-
+       /* PLL registers */
+-      cyber2000_grphw(DCLK_MULT, hw->clock_mult, cfb);
+-      cyber2000_grphw(DCLK_DIV,  hw->clock_div, cfb);
+-      cyber2000_grphw(MCLK_MULT, cfb->mclk_mult, cfb);
+-      cyber2000_grphw(MCLK_DIV,  cfb->mclk_div, cfb);
++      cyber2000_grphw(EXT_DCLK_MULT, hw->clock_mult, cfb);
++      cyber2000_grphw(EXT_DCLK_DIV,  hw->clock_div, cfb);
++      cyber2000_grphw(EXT_MCLK_MULT, cfb->mclk_mult, cfb);
++      cyber2000_grphw(EXT_MCLK_DIV,  cfb->mclk_div, cfb);
+       cyber2000_grphw(0x90, 0x01, cfb);
+       cyber2000_grphw(0xb9, 0x80, cfb);
+       cyber2000_grphw(0xb9, 0x00, cfb);
+-      cyber2000fb_writeb(0x56, 0x3ce, cfb);
+-      i = cyber2000fb_readb(0x3cf, cfb);
+-      cyber2000fb_writeb(i | 4, 0x3cf, cfb);
+-      cyber2000fb_writeb(hw->palette_ctrl, 0x3c6, cfb);
+-      cyber2000fb_writeb(i,    0x3cf, cfb);
++      cfb->ramdac_ctrl = hw->ramdac;
++      cyber2000fb_write_ramdac_ctrl(cfb);
+       cyber2000fb_writeb(0x20, 0x3c0, cfb);
+       cyber2000fb_writeb(0xff, 0x3c6, cfb);
+@@ -532,31 +575,32 @@
+       cyber2000_grphw(0x14, hw->fetch, cfb);
+       cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) |
+                             ((hw->pitch >> 4) & 0x30), cfb);
+-      cyber2000_grphw(0x77, hw->visualid, cfb);
++      cyber2000_grphw(EXT_SEQ_MISC, hw->extseqmisc, cfb);
+-      /* make sure we stay in linear mode */
+-      cyber2000_grphw(0x33, 0x0d, cfb);
++//    cyber2000_grphw(EXT_BIU_MISC, EXT_BIU_MISC_LIN_ENABLE |
++//                                  EXT_BIU_MISC_COP_ENABLE |
++//                                  EXT_BIU_MISC_COP_BFC, cfb);
+       /*
+        * Set up accelerator registers
+        */
+       cyber2000fb_writew(hw->width,     CO_REG_SRC_WIDTH,  cfb);
+       cyber2000fb_writew(hw->width,     CO_REG_DEST_WIDTH, cfb);
+-      cyber2000fb_writeb(hw->pixformat, CO_REG_PIX_FORMAT, cfb);
++      cyber2000fb_writeb(hw->co_pixfmt, CO_REG_PIXFMT, cfb);
+ }
+ static inline int
+ cyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var)
+ {
+-      u_int base;
+-
+-      base = var->yoffset * var->xres_virtual + var->xoffset;
++      u_int base = var->yoffset * var->xres_virtual + var->xoffset;
+-      /* have to be careful, because bits_per_pixel might be 15
+-         in this version of the driver -- dok@directfb.org 2002/06/13 */
+-      base *= (var->bits_per_pixel + 7) >> 3;
++      base *= var->bits_per_pixel;
+-      base >>= 2;
++      /*
++       * Convert to bytes and shift two extra bits because DAC
++       * can only start on 4 byte aligned data.
++       */
++      base >>= 5;
+       if (base >= 1 << 20)
+               return -EINVAL;
+@@ -576,27 +620,20 @@
+                    struct fb_info *info)
+ {
+       struct cfb_info *cfb = (struct cfb_info *)info;
+-      struct fb_cmap *dcmap = &fb_display[con].cmap;
++      struct display *display = fb_display + con;
++      struct fb_cmap *dcmap = &display->cmap;
+       int err = 0;
+       /* no colormap allocated? */
+-      if (!dcmap->len) {
+-              int size;
+-
+-              if (cfb->fb.var.bits_per_pixel == 16)
+-                      size = 32;
+-              else
+-                      size = 256;
+-
+-              err = fb_alloc_cmap(dcmap, size, 0);
+-      }
++      if (!dcmap->len)
++              err = fb_alloc_cmap(dcmap, 256, 0);
+       /*
+        * we should be able to remove this test once fbcon has been
+        * "improved" --rmk
+        */
+-      if (!err && con == cfb->currcon) {
+-              err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb);
++      if (!err && display == cfb->display) {
++              err = fb_set_cmap(cmap, kspc, cyber2000fb_setcolreg, &cfb->fb);
+               dcmap = &cfb->fb.cmap;
+       }
+@@ -672,8 +709,9 @@
+       hw->crtc[16] = Vblankend;
+       hw->crtc[18] = 0xff;
+-      /* overflow - graphics reg 0x11 */
+-      /* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
++      /*
++       * overflow - graphics reg 0x11
++       * 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
+        * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT
+        */
+       hw->crtc_ofl =
+@@ -681,7 +719,12 @@
+               BIT(Vdispend,   10, 0x01,  1) |
+               BIT(Vsyncstart, 10, 0x01,  2) |
+               BIT(Vblankstart,10, 0x01,  3) |
+-              1 << 4;
++              EXT_CRT_VRTOFL_LINECOMP10;
++
++      /* woody: set the interlaced bit... */
++      /* FIXME: what about doublescan? */
++      if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
++              hw->crtc_ofl |= EXT_CRT_VRTOFL_INTERLACE;
+       return 0;
+ }
+@@ -787,7 +830,7 @@
+       vco = ref_ps * best_div1 / best_mult;
+       if ((ref_ps == 40690) && (vco < 5556))
+               /* Set VFSEL when VCO > 180MHz (5.556 ps). */
+-              hw->clock_div |= DCLK_DIV_VFSEL;
++              hw->clock_div |= EXT_DCLK_DIV_VFSEL;
+       return 0;
+ }
+@@ -801,58 +844,131 @@
+ cyber2000fb_decode_var(struct fb_var_screeninfo *var, struct cfb_info *cfb,
+                      struct par_info *hw)
+ {
++      unsigned int mem;
+       int err;
+       hw->width = var->xres_virtual;
+-      hw->palette_ctrl = 0x06;
+-      hw->vmode = var->vmode;
++      hw->ramdac = RAMDAC_VREFEN | RAMDAC_DAC8BIT;
++
++      var->transp.msb_right   = 0;
++      var->red.msb_right      = 0;
++      var->green.msb_right    = 0;
++      var->blue.msb_right     = 0;
+       switch (var->bits_per_pixel) {
+ #ifdef FBCON_HAS_CFB8
+       case 8: /* PSEUDOCOLOUR, 256 */
+-              hw->pixformat           = PIXFORMAT_8BPP;
+-              hw->visualid            = VISUALID_256;
++              hw->co_pixfmt           = CO_PIXFMT_8BPP;
+               hw->pitch               = hw->width >> 3;
++              hw->extseqmisc          = EXT_SEQ_MISC_8;
++
++              var->transp.offset      = 0;
++              var->transp.length      = 0;
++              var->red.offset         = 0;
++              var->red.length         = 8;
++              var->green.offset       = 0;
++              var->green.length       = 8;
++              var->blue.offset        = 0;
++              var->blue.length        = 8;
+               break;
+ #endif
+ #ifdef FBCON_HAS_CFB16
+-      case 16:/* DIRECTCOLOUR, 64k */
+-#ifndef CFB16_IS_CFB15
+-              hw->pixformat           = PIXFORMAT_16BPP;
+-              hw->visualid            = VISUALID_64K;
+-              hw->pitch               = hw->width >> 2;
+-              hw->palette_ctrl        |= 0x10;
+-              break;
+-#endif
+-      case 15:/* DIRECTCOLOUR, 32k */
+-              hw->pixformat           = PIXFORMAT_16BPP;
+-              hw->visualid            = VISUALID_32K;
++      case 16:/* DIRECTCOLOUR, 64k or 32k */
++              hw->co_pixfmt           = CO_PIXFMT_16BPP;
+               hw->pitch               = hw->width >> 2;
+-              hw->palette_ctrl        |= 0x10;
+-              break;
++              switch (var->green.length) {
++              case 6: /* RGB565, 64k */
++                      hw->extseqmisc          = EXT_SEQ_MISC_16_RGB565;
++
++                      var->transp.offset      = 0;
++                      var->transp.length      = 0;
++                      var->red.offset         = 11;
++                      var->red.length         = 5;
++                      var->green.offset       = 5;
++                      var->green.length       = 6;
++                      var->blue.offset        = 0;
++                      var->blue.length        = 5;
++                      break;
++
++              default:
++              case 5: /* RGB555, 32k */
++                      hw->extseqmisc          = EXT_SEQ_MISC_16_RGB555;
++
++                      var->transp.offset      = 0;
++                      var->transp.length      = 0;
++                      var->red.offset         = 10;
++                      var->red.length         = 5;
++                      var->green.offset       = 5;
++                      var->green.length       = 5;
++                      var->blue.offset        = 0;
++                      var->blue.length        = 5;
++                      break;
++
++              case 4: /* RGB444, 4k + transparency? */
++                      hw->extseqmisc          = EXT_SEQ_MISC_16_RGB444;
++
++                      var->transp.offset      = 12;
++                      var->transp.length      = 4;
++                      var->red.offset         = 8;
++                      var->red.length         = 4;
++                      var->green.offset       = 4;
++                      var->green.length       = 4;
++                      var->blue.offset        = 0;
++                      var->blue.length        = 4;
++                      break;
++              }
++              break;
+ #endif
+ #ifdef FBCON_HAS_CFB24
+       case 24:/* TRUECOLOUR, 16m */
+-              hw->pixformat           = PIXFORMAT_24BPP;
+-              hw->visualid            = VISUALID_16M;
++              hw->co_pixfmt           = CO_PIXFMT_24BPP;
+               hw->width               *= 3;
+               hw->pitch               = hw->width >> 3;
+-              hw->palette_ctrl        |= 0x10;
++              hw->ramdac              |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++              hw->extseqmisc          = EXT_SEQ_MISC_24_RGB888;
++
++              var->transp.offset      = 0;
++              var->transp.length      = 0;
++              var->red.offset         = 16;
++              var->red.length         = 8;
++              var->green.offset       = 8;
++              var->green.length       = 8;
++              var->blue.offset        = 0;
++              var->blue.length        = 8;
+               break;
+ #endif
+ #ifdef FBCON_HAS_CFB32
+       case 32:/* TRUECOLOUR, 16m */
+-              hw->pixformat           = PIXFORMAT_32BPP;
+-              hw->visualid            = VISUALID_16M_32;
++              hw->co_pixfmt           = CO_PIXFMT_32BPP;
+               hw->pitch               = hw->width >> 1;
+-              hw->palette_ctrl        |= 0x10;
++              hw->ramdac              |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++              hw->extseqmisc          = EXT_SEQ_MISC_32;
++
++              var->transp.offset      = 24;
++              var->transp.length      = 8;
++              var->red.offset         = 16;
++              var->red.length         = 8;
++              var->green.offset       = 8;
++              var->green.length       = 8;
++              var->blue.offset        = 0;
++              var->blue.length        = 8;
+               break;
+ #endif
+       default:
+               return -EINVAL;
+       }
++      mem = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8);
++      if (mem > cfb->fb.fix.smem_len)
++              var->yres_virtual = cfb->fb.fix.smem_len * 8 /
++                      (var->bits_per_pixel * var->xres_virtual);
++
++      if (var->yres > var->yres_virtual)
++              var->yres = var->yres_virtual;
++      if (var->xres > var->xres_virtual)
++              var->xres = var->xres_virtual;
++
+       err = cyber2000fb_decode_clock(hw, cfb, var);
+       if (err)
+               return err;
+@@ -880,7 +996,7 @@
+       struct cfb_info *cfb = (struct cfb_info *)info;
+       struct display *display;
+       struct par_info hw;
+-      int err, chgvar = 0;
++      int err, chgvar;
+       /*
+        * CONUPDATE and SMOOTH_XPAN are equal.  However,
+@@ -888,11 +1004,11 @@
+        */
+       if (var->vmode & FB_VMODE_CONUPDATE) {
+               var->vmode |= FB_VMODE_YWRAP;
+-              var->xoffset = cfb->fb.var.xoffset;
+-              var->yoffset = cfb->fb.var.yoffset;
++              var->xoffset = cfb->display->var.xoffset;
++              var->yoffset = cfb->display->var.yoffset;
+       }
+-      err = cyber2000fb_decode_var(var, (struct cfb_info *)info, &hw);
++      err = cyber2000fb_decode_var(var, cfb, &hw);
+       if (err)
+               return err;
+@@ -902,105 +1018,61 @@
+       if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
+               return -EINVAL;
+-      if (cfb->fb.var.xres != var->xres)
+-              chgvar = 1;
+-      if (cfb->fb.var.yres != var->yres)
+-              chgvar = 1;
+-      if (cfb->fb.var.xres_virtual != var->xres_virtual)
+-              chgvar = 1;
+-      if (cfb->fb.var.yres_virtual != var->yres_virtual)
+-              chgvar = 1;
+-      if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
+-              chgvar = 1;
+-
+       if (con < 0) {
+               display = cfb->fb.disp;
+-              chgvar = 0;
+       } else {
+               display = fb_display + con;
+       }
+-      var->red.msb_right      = 0;
+-      var->green.msb_right    = 0;
+-      var->blue.msb_right     = 0;
++      chgvar = cfb->fb.var.xres != var->xres ||
++               cfb->fb.var.yres != var->yres ||
++               cfb->fb.var.xres_virtual != var->xres_virtual ||
++               cfb->fb.var.yres_virtual != var->yres_virtual ||
++               cfb->fb.var.bits_per_pixel != var->bits_per_pixel;
++
++      if (memcmp(&cfb->fb.var.red, &var->red, sizeof(var->red)) ||
++          memcmp(&cfb->fb.var.green, &var->green, sizeof(var->green)) ||
++          memcmp(&cfb->fb.var.blue, &var->blue, sizeof(var->blue)))
++              chgvar = 1;
++
++      if (con < 0)
++              chgvar = 0;
++
++      /*
++       * If we are setting all the virtual consoles, also set the
++       * defaults used to create new consoles.
++       */
++      err = var->activate;
++      var->activate = FB_ACTIVATE_NOW;
++      if (err & FB_ACTIVATE_ALL)
++              cfb->fb.disp->var = *var;
++
++      cfb->fb.var = *var;
++      cfb->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+       switch (var->bits_per_pixel) {
+ #ifdef FBCON_HAS_CFB8
+       case 8: /* PSEUDOCOLOUR, 256 */
+-              var->red.offset         = 0;
+-              var->red.length         = 8;
+-              var->green.offset       = 0;
+-              var->green.length       = 8;
+-              var->blue.offset        = 0;
+-              var->blue.length        = 8;
+-
+-              cfb->fb.fix.visual      = FB_VISUAL_PSEUDOCOLOR;
+               cfb->dispsw             = &fbcon_cfb8;
+               display->dispsw_data    = NULL;
+-              display->next_line      = var->xres_virtual;
+               break;
+ #endif
+ #ifdef FBCON_HAS_CFB16
+-      case 16:/* DIRECTCOLOUR, 64k */
+-#ifndef CFB16_IS_CFB15
+-              var->red.offset         = 11;
+-              var->red.length         = 5;
+-              var->green.offset       = 5;
+-              var->green.length       = 6;
+-              var->blue.offset        = 0;
+-              var->blue.length        = 5;
+-
+-              cfb->fb.fix.visual      = FB_VISUAL_DIRECTCOLOR;
++      case 16:/* DIRECTCOLOUR */
+               cfb->dispsw             = &fbcon_cfb16;
+               display->dispsw_data    = cfb->fb.pseudo_palette;
+-              display->next_line      = var->xres_virtual * 2;
+-              break;
+-#endif
+-      case 15:/* DIRECTCOLOUR, 32k */
+-              var->bits_per_pixel     = 15;
+-              var->red.offset         = 10;
+-              var->red.length         = 5;
+-              var->green.offset       = 5;
+-              var->green.length       = 5;
+-              var->blue.offset        = 0;
+-              var->blue.length        = 5;
+-
+-              cfb->fb.fix.visual      = FB_VISUAL_DIRECTCOLOR;
+-              cfb->dispsw             = &fbcon_cfb16;
+-              display->dispsw_data    = cfb->fb.pseudo_palette;
+-              display->next_line      = var->xres_virtual * 2;
+               break;
+ #endif
+ #ifdef FBCON_HAS_CFB24
+       case 24:/* TRUECOLOUR, 16m */
+-              var->red.offset         = 16;
+-              var->red.length         = 8;
+-              var->green.offset       = 8;
+-              var->green.length       = 8;
+-              var->blue.offset        = 0;
+-              var->blue.length        = 8;
+-
+-              cfb->fb.fix.visual      = FB_VISUAL_TRUECOLOR;
+               cfb->dispsw             = &fbcon_cfb24;
+               display->dispsw_data    = cfb->fb.pseudo_palette;
+-              display->next_line      = var->xres_virtual * 3;
+               break;
+ #endif
+ #ifdef FBCON_HAS_CFB32
+       case 32:/* TRUECOLOUR, 16m */
+-              var->transp.offset      = 24;
+-              var->transp.length      = 8;
+-              var->red.offset         = 16;
+-              var->red.length         = 8;
+-              var->green.offset       = 8;
+-              var->green.length       = 8;
+-              var->blue.offset        = 0;
+-              var->blue.length        = 8;
+-
+-              cfb->fb.fix.visual      = FB_VISUAL_TRUECOLOR;
+               cfb->dispsw             = &fbcon_cfb32;
+               display->dispsw_data    = cfb->fb.pseudo_palette;
+-              display->next_line      = var->xres_virtual * 4;
+               break;
+ #endif
+       default:/* in theory this should never happen */
+@@ -1010,15 +1082,27 @@
+               break;
+       }
++      /*
++       * 8bpp displays are always pseudo colour.
++       * 16bpp and above are direct colour or true colour, depending
++       * on whether the RAMDAC palettes are bypassed.  (Direct colour
++       * has palettes, true colour does not.)
++       */
++      if (var->bits_per_pixel == 8)
++              cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++      else if (hw.ramdac & RAMDAC_BYPASS)
++              cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
++      else
++              cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR;
++
+       if (var->accel_flags & FB_ACCELF_TEXT && cfb->dispsw != &fbcon_dummy)
+               display->dispsw = &fbcon_cyber_accel;
+       else
+               display->dispsw = cfb->dispsw;
+-      cfb->fb.fix.line_length = display->next_line;
+-
+       display->screen_base    = cfb->fb.screen_base;
+       display->line_length    = cfb->fb.fix.line_length;
++      display->next_line      = cfb->fb.fix.line_length;
+       display->visual         = cfb->fb.fix.visual;
+       display->type           = cfb->fb.fix.type;
+       display->type_aux       = cfb->fb.fix.type_aux;
+@@ -1026,31 +1110,15 @@
+       display->ywrapstep      = cfb->fb.fix.ywrapstep;
+       display->can_soft_blank = 1;
+       display->inverse        = 0;
++      display->var            = *var;
+-      cfb->fb.var = *var;
+-      cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
+-
+-      /*
+-       * Update the old var.  The fbcon drivers still use this.
+-       * Once they are using cfb->fb.var, this can be dropped.
+-       *                                      --rmk
+-       */
+-      display->var = cfb->fb.var;
+-
+-      /*
+-       * If we are setting all the virtual consoles, also set the
+-       * defaults used to create new consoles.
+-       */
+-      if (var->activate & FB_ACTIVATE_ALL)
+-              cfb->fb.disp->var = cfb->fb.var;
++      cyber2000fb_set_timing(cfb, &hw);
++      cyber2000fb_update_start(cfb, var);
++      fb_set_cmap(&cfb->fb.cmap, 1, cyber2000fb_setcolreg, &cfb->fb);
+-      if (chgvar && info && cfb->fb.changevar)
++      if (chgvar && cfb->fb.changevar)
+               cfb->fb.changevar(con);
+-      cyber2000fb_update_start(cfb, var);
+-      cyber2000fb_set_timing(cfb, &hw);
+-      fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb);
+-
+       return 0;
+ }
+@@ -1072,18 +1140,18 @@
+       if (var->xoffset > (var->xres_virtual - var->xres))
+               return -EINVAL;
+-      if (y_bottom > cfb->fb.var.yres_virtual)
++      if (y_bottom > cfb->display->var.yres_virtual)
+               return -EINVAL;
+       if (cyber2000fb_update_start(cfb, var))
+               return -EINVAL;
+-      cfb->fb.var.xoffset = var->xoffset;
+-      cfb->fb.var.yoffset = var->yoffset;
++      cfb->display->var.xoffset = var->xoffset;
++      cfb->display->var.yoffset = var->yoffset;
+       if (var->vmode & FB_VMODE_YWRAP) {
+-              cfb->fb.var.vmode |= FB_VMODE_YWRAP;
++              cfb->display->var.vmode |= FB_VMODE_YWRAP;
+       } else {
+-              cfb->fb.var.vmode &= ~FB_VMODE_YWRAP;
++              cfb->display->var.vmode &= ~FB_VMODE_YWRAP;
+       }
+       return 0;
+@@ -1106,22 +1174,18 @@
+ static int cyber2000fb_switch(int con, struct fb_info *info)
+ {
+       struct cfb_info *cfb = (struct cfb_info *)info;
+-      struct display *disp;
++      struct display *display = cfb->display;
+       struct fb_cmap *cmap;
+-      if (cfb->currcon >= 0) {
+-              disp = fb_display + cfb->currcon;
+-
++      if (display) {
+               /*
+                * Save the old colormap and video mode.
+                */
+-              disp->var = cfb->fb.var;
+-              if (disp->cmap.len)
+-                      fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
++              if (display->cmap.len)
++                      fb_copy_cmap(&cfb->fb.cmap, &display->cmap, 0);
+       }
+-      cfb->currcon = con;
+-      disp = fb_display + con;
++      cfb->display = display = fb_display + con;
+       /*
+        * Install the new colormap and change the video mode.  By default,
+@@ -1132,73 +1196,88 @@
+        * depth of the new video mode.  For now, we leave it at its
+        * default 256 entry.
+        */
+-      if (disp->cmap.len)
+-              cmap = &disp->cmap;
++      if (display->cmap.len)
++              cmap = &display->cmap;
+       else
+-              cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
++              cmap = fb_default_cmap(1 << display->var.bits_per_pixel);
+       fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
+-      cfb->fb.var = disp->var;
+-      cfb->fb.var.activate = FB_ACTIVATE_NOW;
+-
+-      cyber2000fb_set_var(&cfb->fb.var, con, &cfb->fb);
++      display->var.activate = FB_ACTIVATE_NOW;
++      cyber2000fb_set_var(&display->var, con, &cfb->fb);
+       return 0;
+ }
+ /*
+  *    (Un)Blank the display.
++ *
++ *  Blank the screen if blank_mode != 0, else unblank. If
++ *  blank == NULL then the caller blanks by setting the CLUT
++ *  (Color Look Up Table) to all black. Return 0 if blanking
++ *  succeeded, != 0 if un-/blanking failed due to e.g. a
++ *  video mode which doesn't support it. Implements VESA
++ *  suspend and powerdown modes on hardware that supports
++ *  disabling hsync/vsync:
++ *    blank_mode == 2: suspend vsync
++ *    blank_mode == 3: suspend hsync
++ *    blank_mode == 4: powerdown
++ *
++ *  wms...Enable VESA DMPS compatible powerdown mode
++ *  run "setterm -powersave powerdown" to take advantage
+  */
+ static void cyber2000fb_blank(int blank, struct fb_info *info)
+ {
+       struct cfb_info *cfb = (struct cfb_info *)info;
++      unsigned int sync = 0;
+       int i;
+-      /*
+-       *  Blank the screen if blank_mode != 0, else unblank. If
+-       *  blank == NULL then the caller blanks by setting the CLUT
+-       *  (Color Look Up Table) to all black. Return 0 if blanking
+-       *  succeeded, != 0 if un-/blanking failed due to e.g. a
+-       *  video mode which doesn't support it. Implements VESA
+-       *  suspend and powerdown modes on hardware that supports
+-       *  disabling hsync/vsync:
+-       *    blank_mode == 2: suspend vsync
+-       *    blank_mode == 3: suspend hsync
+-       *    blank_mode == 4: powerdown
+-       *
+-       *  wms...Enable VESA DMPS compatible powerdown mode
+-       *  run "setterm -powersave powerdown" to take advantage
+-       */
+-     
+       switch (blank) {
+       case 4: /* powerdown - both sync lines down */
+-              cyber2000_grphw(0x16, 0x05, cfb);
++              sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_0;
+               break;  
+       case 3: /* hsync off */
+-              cyber2000_grphw(0x16, 0x01, cfb);
++              sync = EXT_SYNC_CTL_VS_NORMAL | EXT_SYNC_CTL_HS_0;
+               break;  
+       case 2: /* vsync off */
+-              cyber2000_grphw(0x16, 0x04, cfb);
+-              break;  
++              sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_NORMAL;
++              break;
+       case 1: /* soft blank */
+-              cyber2000_grphw(0x16, 0x00, cfb);
++      default: /* unblank */
++              break;
++      }
++
++      cyber2000_grphw(EXT_SYNC_CTL, sync, cfb);
++
++      if (blank <= 1) {
++              /* turn on ramdacs */
++              cfb->ramdac_powerdown &= ~(RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN);
++              cyber2000fb_write_ramdac_ctrl(cfb);
++      }
++
++      /*
++       * Soft blank/unblank the display.
++       */
++      if (blank) {    /* soft blank */
+               for (i = 0; i < NR_PALETTE; i++) {
+                       cyber2000fb_writeb(i, 0x3c8, cfb);
+                       cyber2000fb_writeb(0, 0x3c9, cfb);
+                       cyber2000fb_writeb(0, 0x3c9, cfb);
+                       cyber2000fb_writeb(0, 0x3c9, cfb);
+               }
+-              break;
+-      default: /* unblank */
+-              cyber2000_grphw(0x16, 0x00, cfb);
++      } else {        /* unblank */
+               for (i = 0; i < NR_PALETTE; i++) {
+                       cyber2000fb_writeb(i, 0x3c8, cfb);
+                       cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb);
+                       cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb);
+                       cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb);
+               }
+-              break;
++      }
++
++      if (blank >= 2) {
++              /* turn off ramdacs */
++              cfb->ramdac_powerdown |= RAMDAC_DACPWRDN | RAMDAC_BYPASS | RAMDAC_RAMPWRDN;
++              cyber2000fb_write_ramdac_ctrl(cfb);
+       }
+ }
+@@ -1233,51 +1312,61 @@
+ }
+ static struct fb_ops cyber2000fb_ops = {
+-      owner:          THIS_MODULE,
+-      fb_set_var:     cyber2000fb_set_var,
+-      fb_set_cmap:    cyber2000fb_set_cmap,
+-      fb_pan_display: cyber2000fb_pan_display,
+-      fb_get_fix:     gen_get_fix,
+-      fb_get_var:     gen_get_var,
+-      fb_get_cmap:    gen_get_cmap,
++      .owner          = THIS_MODULE,
++      .fb_set_var     = cyber2000fb_set_var,
++      .fb_set_cmap    = cyber2000fb_set_cmap,
++      .fb_pan_display = cyber2000fb_pan_display,
++      .fb_get_fix     = gen_get_fix,
++      .fb_get_var     = gen_get_var,
++      .fb_get_cmap    = gen_get_cmap,
+ };
+ /*
++ * This is the only "static" reference to the internal data structures
++ * of this driver.  It is here solely at the moment to support the other
++ * CyberPro modules external to this driver.
++ */
++static struct cfb_info                *int_cfb_info;
++
++/*
+  * Enable access to the extended registers
+  */
+-static void cyber2000fb_enable_extregs(struct cfb_info *cfb)
++void cyber2000fb_enable_extregs(struct cfb_info *cfb)
+ {
+       cfb->func_use_count += 1;
+       if (cfb->func_use_count == 1) {
+               int old;
+-              old = cyber2000_grphr(FUNC_CTL, cfb);
+-              cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL, cfb);
++              old = cyber2000_grphr(EXT_FUNC_CTL, cfb);
++              old |= EXT_FUNC_CTL_EXTREGENBL;
++              cyber2000_grphw(EXT_FUNC_CTL, old, cfb);
+       }
+ }
+ /*
+  * Disable access to the extended registers
+  */
+-static void cyber2000fb_disable_extregs(struct cfb_info *cfb)
++void cyber2000fb_disable_extregs(struct cfb_info *cfb)
+ {
+       if (cfb->func_use_count == 1) {
+               int old;
+-              old = cyber2000_grphr(FUNC_CTL, cfb);
+-              cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL, cfb);
++              old = cyber2000_grphr(EXT_FUNC_CTL, cfb);
++              old &= ~EXT_FUNC_CTL_EXTREGENBL;
++              cyber2000_grphw(EXT_FUNC_CTL, old, cfb);
+       }
+-      cfb->func_use_count -= 1;
++      if (cfb->func_use_count == 0)
++              printk(KERN_ERR "disable_extregs: count = 0\n");
++      else
++              cfb->func_use_count -= 1;
+ }
+-/*
+- * This is the only "static" reference to the internal data structures
+- * of this driver.  It is here solely at the moment to support the other
+- * CyberPro modules external to this driver.
+- */
+-static struct cfb_info                *int_cfb_info;
++void cyber2000fb_get_fb_var(struct cfb_info *cfb, struct fb_var_screeninfo *var)
++{
++      memcpy(var, &cfb->display->var, sizeof(struct fb_var_screeninfo));
++}
+ /*
+  * Attach a capture/tv driver to the core CyberX0X0 driver.
+@@ -1311,164 +1400,102 @@
+ EXPORT_SYMBOL(cyber2000fb_attach);
+ EXPORT_SYMBOL(cyber2000fb_detach);
++EXPORT_SYMBOL(cyber2000fb_enable_extregs);
++EXPORT_SYMBOL(cyber2000fb_disable_extregs);
++EXPORT_SYMBOL(cyber2000fb_get_fb_var);
+ /*
+  * These parameters give
+  * 640x480, hsync 31.5kHz, vsync 60Hz
+  */
+ static struct fb_videomode __devinitdata cyber2000fb_default_mode = {
+-      refresh:        60,
+-      xres:           640,
+-      yres:           480,
+-      pixclock:       39722,
+-      left_margin:    56,
+-      right_margin:   16,
+-      upper_margin:   34,
+-      lower_margin:   9,
+-      hsync_len:      88,
+-      vsync_len:      2,
+-      sync:           FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+-      vmode:          FB_VMODE_NONINTERLACED
++      .refresh        = 60,
++      .xres           = 640,
++      .yres           = 480,
++      .pixclock       = 39722,
++      .left_margin    = 56,
++      .right_margin   = 16,
++      .upper_margin   = 34,
++      .lower_margin   = 9,
++      .hsync_len      = 88,
++      .vsync_len      = 2,
++      .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++      .vmode          = FB_VMODE_NONINTERLACED
+ };
++/* static register programming for all chips */
+ static char igs_regs[] __devinitdata = {
+-                                      0x12, 0x00,     0x13, 0x00,
+-                                      0x16, 0x00,
+-                      0x31, 0x00,     0x32, 0x00,
+-      0x50, 0x00,     0x51, 0x00,     0x52, 0x00,     0x53, 0x00,
+-      0x54, 0x00,     0x55, 0x00,     0x56, 0x00,     0x57, 0x01,
+-      0x58, 0x00,     0x59, 0x00,     0x5a, 0x00,
+-      0x70, 0x0b,                                     0x73, 0x30,
+-      0x74, 0x0b,     0x75, 0x17,     0x76, 0x00,     0x7a, 0xc8
++      EXT_CRT_IRQ,            0,
++      EXT_CRT_TEST,           0,
++      EXT_SYNC_CTL,           0,
++      EXT_SEG_WRITE_PTR,      0,
++      EXT_SEG_READ_PTR,       0,
++      EXT_BIU_MISC,           EXT_BIU_MISC_LIN_ENABLE |
++                              EXT_BIU_MISC_COP_ENABLE |
++                              EXT_BIU_MISC_COP_BFC,
++      EXT_FUNC_CTL,           0,
++      CURS_H_START,           0,
++      CURS_H_START + 1,       0,
++      CURS_H_PRESET,          0,
++      CURS_V_START,           0,
++      CURS_V_START + 1,       0,
++      CURS_V_PRESET,          0,
++      CURS_CTL,               0,
++      EXT_ATTRIB_CTL,         EXT_ATTRIB_CTL_EXT,
++      EXT_OVERSCAN_RED,       0,
++      EXT_OVERSCAN_GREEN,     0,
++      EXT_OVERSCAN_BLUE,      0,
++};
++
++/* specific register setting for the 2000 series */
++static char igs_2000_regs[] __devinitdata = {
++      /* some of these are questionable when we have a BIOS */
++      EXT_MEM_CTL0,           EXT_MEM_CTL0_7CLK |
++                              EXT_MEM_CTL0_RAS_1 |
++                              EXT_MEM_CTL0_MULTCAS,
++      EXT_HIDDEN_CTL1,        0x30,
++      EXT_FIFO_CTL,           0x0b,
++      EXT_FIFO_CTL + 1,       0x17,
++      0x76,                   0x00,
++      EXT_HIDDEN_CTL4,        0xc8
+ };
+ /*
+- * We need to wake up the CyberPro, and make sure its in linear memory
+- * mode.  Unfortunately, this is specific to the platform and card that
+- * we are running on.
+- *
+- * On x86 and ARM, should we be initialising the CyberPro first via the
+- * IO registers, and then the MMIO registers to catch all cases?  Can we
+- * end up in the situation where the chip is in MMIO mode, but not awake
+- * on an x86 system?
+- *
+- * Note that on the NetWinder, the firmware automatically detects the
+- * type, width and size, and leaves this in extended registers 0x71 and
+- * 0x72 for us.
++ * Initialise the CyberPro hardware.
+  */
+-static inline void cyberpro_init_hw(struct cfb_info *cfb, int at_boot)
++static void cyberpro_init_hw(struct cfb_info *cfb)
+ {
+       int i;
+-      /*
+-       * Wake up the CyberPro.
+-       */
+-#ifdef __sparc__
+-#ifdef __sparc_v9__
+-#error "You loose, consult DaveM."
+-#else
+-      /*
+-       * SPARC does not have an "outb" instruction, so we generate
+-       * I/O cycles storing into a reserved memory space at
+-       * physical address 0x3000000
+-       */
+-      {
+-              unsigned char *iop;
+-
+-              iop = ioremap(0x3000000, 0x5000);
+-              if (iop == NULL) {
+-                      prom_printf("iga5000: cannot map I/O\n");
+-                      return -ENOMEM;
+-              }
+-
+-              writeb(0x18, iop + 0x46e8);
+-              writeb(0x01, iop + 0x102);
+-              writeb(0x08, iop + 0x46e8);
+-              writeb(0x33, iop + 0x3ce);
+-              writeb(0x01, iop + 0x3cf);
+-
+-              iounmap((void *)iop);
+-      }
+-#endif
+-
+-      if (at_boot) {
+-              /*
+-               * Use mclk from BIOS.  Only read this if we're
+-               * initialising this card for the first time.
+-               * FIXME: what about hotplug?
+-               */
+-              cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
+-              cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
+-      }
+-#endif
+-#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+-      /*
+-       * x86 and MIPS are simple, we just do regular
+-       * outb's instead of cyber2000fb_writeb.
+-       */
+-      outb(0x18, 0x46e8);
+-      outb(0x01, 0x102);
+-      outb(0x08, 0x46e8);
+-      outb(0x33, 0x3ce);
+-      outb(0x01, 0x3cf);
+-
+-      if (at_boot) {
+-              /*
+-               * Use mclk from BIOS.  Only read this if we're
+-               * initialising this card for the first time.
+-               * FIXME: what about hotplug?
+-               */
+-              cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
+-              cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
+-      }
+-#endif
+-#ifdef __arm__
+-      cyber2000fb_writeb(0x18, 0x46e8, cfb);
+-      cyber2000fb_writeb(0x01, 0x102, cfb);
+-      cyber2000fb_writeb(0x08, 0x46e8, cfb);
+-      cyber2000fb_writeb(0x33, 0x3ce, cfb);
+-      cyber2000fb_writeb(0x01, 0x3cf, cfb);
+-
+-      /*
+-       * MCLK on the NetWinder and the Shark is fixed at 75MHz
+-       */
+-      cfb->mclk_mult = 0xdb;
+-      cfb->mclk_div  = 0x54;
+-#endif
+-
+-      /*
+-       * Initialise the CyberPro
+-       */
+       for (i = 0; i < sizeof(igs_regs); i += 2)
+               cyber2000_grphw(igs_regs[i], igs_regs[i+1], cfb);
+-      if (at_boot) {
++      if (cfb->id == ID_CYBERPRO_5000) {
+               /*
+-               * get the video RAM size and width from the VGA register.
+-               * This should have been already initialised by the BIOS,
+-               * but if it's garbage, claim default 1MB VRAM (woody)
++               * On the CyberPro5XXXX, ensure that we're using the correct
++               * PLL (5XXX's may be programmed to use an additional set of
++               * PLLs.)
+                */
+-              cfb->mem_ctl1 = cyber2000_grphr(MEM_CTL1, cfb);
+-              cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2, cfb);
++              unsigned char val;
++              cyber2000fb_writeb(0xba, 0x3ce, cfb);
++              val = cyber2000fb_readb(0x3cf, cfb) & 0x80;
++              cyber2000fb_writeb(val, 0x3cf, cfb);
++              cyber2000fb_ops.fb_pan_display = NULL; /* FIXME: panning broken */
+       } else {
+               /*
+-               * Reprogram the MEM_CTL1 and MEM_CTL2 registers
++               * Other supported chips (2000 series) appear to need
++               * these registers programming
+                */
+-              cyber2000_grphw(MEM_CTL1, cfb->mem_ctl1, cfb);
+-              cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2, cfb);
++              for (i = 0; i < sizeof(igs_2000_regs); i += 2)
++                      cyber2000_grphw(igs_2000_regs[i],
++                                      igs_2000_regs[i+1],
++                                      cfb);
+       }
+-      /*
+-       * Ensure thatwe are using the correct PLL.
+-       * (CyberPro 5000's may be programmed to use
+-       * an additional set of PLLs.
+-       */
+-      cyber2000fb_writeb(0xba, 0x3ce, cfb);
+-      cyber2000fb_writeb(cyber2000fb_readb(0x3cf, cfb) & 0x80, 0x3cf, cfb);
+ }
+ static struct cfb_info * __devinit
+-cyberpro_alloc_fb_info(struct pci_dev *dev, const struct pci_device_id *id, char *name)
++cyberpro_alloc_fb_info(unsigned int id, char *name)
+ {
+       struct cfb_info *cfb;
+@@ -1480,10 +1507,9 @@
+       memset(cfb, 0, sizeof(struct cfb_info) + sizeof(struct display));
+-      cfb->currcon            = -1;
+-      cfb->dev                = dev;
++      cfb->id                 = id;
+-      if (id->driver_data == FB_ACCEL_IGS_CYBER5000)
++      if (id == ID_CYBERPRO_5000)
+               cfb->ref_ps     = 40690; // 24.576 MHz
+       else
+               cfb->ref_ps     = 69842; // 14.31818 MHz (69841?)
+@@ -1492,7 +1518,7 @@
+       cfb->divisors[1]        = 2;
+       cfb->divisors[2]        = 4;
+-      if (id->driver_data == FB_ACCEL_IGS_CYBER2000)
++      if (id == ID_CYBERPRO_2000)
+               cfb->divisors[3] = 8;
+       else
+               cfb->divisors[3] = 6;
+@@ -1504,7 +1530,24 @@
+       cfb->fb.fix.xpanstep    = 0;
+       cfb->fb.fix.ypanstep    = 1;
+       cfb->fb.fix.ywrapstep   = 0;
+-      cfb->fb.fix.accel       = id->driver_data;
++
++      switch (id) {
++      case ID_IGA_1682:
++              cfb->fb.fix.accel = 0;
++              break;
++
++      case ID_CYBERPRO_2000:
++              cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2000;
++              break;
++
++      case ID_CYBERPRO_2010:
++              cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2010;
++              break;
++
++      case ID_CYBERPRO_5000:
++              cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER5000;
++              break;
++      }
+       cfb->fb.var.nonstd      = 0;
+       cfb->fb.var.activate    = FB_ACTIVATE_NOW;
+@@ -1569,55 +1612,46 @@
+       return 0;
+ }
+-static int __devinit
+-cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id)
++/*
++ * The CyberPro chips can be placed on many different bus types.
++ * This probe function is common to all bus types.  The bus-specific
++ * probe function is expected to have:
++ *  - enabled access to the linear memory region
++ *  - memory mapped access to the registers
++ *  - initialised mem_ctl1 and mem_ctl2 appropriately.
++ */
++static int __devinit cyberpro_common_probe(struct cfb_info *cfb)
+ {
+-      struct cfb_info *cfb;
+-      u_int h_sync, v_sync;
+       u_long smem_size;
+-      char name[16];
++      u_int h_sync, v_sync;
+       int err;
+-      sprintf(name, "CyberPro%4X", id->device);
+-
+-      err = pci_enable_device(dev);
+-      if (err)
+-              return err;
+-
+-      err = pci_request_regions(dev, name);
+-      if (err)
+-              return err;
+-
+-      err = -ENOMEM;
+-      cfb = cyberpro_alloc_fb_info(dev, id, name);
+-      if (!cfb)
+-              goto failed_release;
+-
+-      cfb->region = ioremap(pci_resource_start(dev, 0),
+-                            pci_resource_len(dev, 0));
+-      if (!cfb->region)
+-              goto failed_ioremap;
+-
+-      cfb->regs = cfb->region + MMIO_OFFSET;
++      cyberpro_init_hw(cfb);
+-      cyberpro_init_hw(cfb, 1);
++      /*
++       * Get the video RAM size and width from the VGA register.
++       * This should have been already initialised by the BIOS,
++       * but if it's garbage, claim default 1MB VRAM (woody)
++       */
++      cfb->mem_ctl0 = cyber2000_grphr(EXT_MEM_CTL0, cfb);
++      cfb->mem_ctl1 = cyber2000_grphr(EXT_MEM_CTL1, cfb);
++      cfb->mem_ctl2 = cyber2000_grphr(EXT_MEM_CTL2, cfb);
++      /*
++       * Determine the size of the memory.
++       */
+       switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) {
+       case MEM_CTL2_SIZE_4MB: smem_size = 0x00400000; break;
+       case MEM_CTL2_SIZE_2MB: smem_size = 0x00200000; break;
++      case MEM_CTL2_SIZE_1MB: smem_size = 0x00100000; break;
+       default:                smem_size = 0x00100000; break;
+       }
+-      /*
+-       * Hmm, we _need_ a portable way of finding the address for
+-       * the remap stuff, both for mmio and for smem.
+-       */
+-      cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
+-      cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
+-      cfb->fb.fix.mmio_len   = MMIO_SIZE;
+       cfb->fb.fix.smem_len   = smem_size;
++      cfb->fb.fix.mmio_len   = MMIO_SIZE;
+       cfb->fb.screen_base    = cfb->region;
++      err = -EINVAL;
+       if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
+                         &cyber2000fb_default_mode, 8)) {
+               printk("%s: no valid mode found\n", cfb->fb.fix.id);
+@@ -1644,13 +1678,181 @@
+       v_sync = h_sync / (cfb->fb.var.yres + cfb->fb.var.upper_margin +
+                cfb->fb.var.lower_margin + cfb->fb.var.vsync_len);
+-      printk(KERN_INFO "%s: %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
++      printk(KERN_INFO "%s: %dKiB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+               cfb->fb.fix.id, cfb->fb.fix.smem_len >> 10,
+               cfb->fb.var.xres, cfb->fb.var.yres,
+               h_sync / 1000, h_sync % 1000, v_sync);
+       err = register_framebuffer(&cfb->fb);
+-      if (err < 0)
++
++failed:
++      return err;
++}
++
++static void cyberpro_common_resume(struct cfb_info *cfb)
++{
++      cyberpro_init_hw(cfb);
++
++      /*
++       * Reprogram the MEM_CTL0, 1 and 2 registers
++       */
++      cyber2000_grphw(EXT_MEM_CTL0, cfb->mem_ctl0, cfb);
++      cyber2000_grphw(EXT_MEM_CTL1, cfb->mem_ctl1, cfb);
++      cyber2000_grphw(EXT_MEM_CTL2, cfb->mem_ctl2, cfb);
++
++      /*
++       * Restore the old video mode and the palette.
++       * We also need to tell fbcon to redraw the console.
++       */
++      cfb->fb.var.activate = FB_ACTIVATE_NOW;
++      cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++}
++
++
++
++
++/*
++ * PCI specific support.
++ */
++
++/*
++ * We need to wake up the CyberPro, and make sure its in linear memory
++ * mode.  Unfortunately, this is specific to the platform and card that
++ * we are running on.
++ *
++ * On x86 and ARM, should we be initialising the CyberPro first via the
++ * IO registers, and then the MMIO registers to catch all cases?  Can we
++ * end up in the situation where the chip is in MMIO mode, but not awake
++ * on an x86 system?
++ */
++static int cyberpro_pci_enable_mmio(struct cfb_info *cfb)
++{
++      unsigned char val;
++
++#if defined(__sparc_v9__)
++#error "You loose, consult DaveM."
++#elif defined(__sparc__)
++      /*
++       * SPARC does not have an "outb" instruction, so we generate
++       * I/O cycles storing into a reserved memory space at
++       * physical address 0x3000000
++       */
++      unsigned char *iop;
++
++      iop = ioremap(0x3000000, 0x5000);
++      if (iop == NULL) {
++              prom_printf("iga5000: cannot map I/O\n");
++              return -ENOMEM;
++      }
++
++      writeb(0x18, iop + 0x46e8);
++      writeb(0x01, iop + 0x102);
++      writeb(0x08, iop + 0x46e8);
++      writeb(EXT_BIU_MISC, iop + 0x3ce);
++      writeb(EXT_BIU_MISC_LIN_ENABLE, iop + 0x3cf);
++
++      iounmap((void *)iop);
++#elif defined(CONFIG_ARCH_SHARK)
++      /*
++       * Shark probably needs to do it this way rather than use the
++       * IO method below.  Since the CyberPro on the Shark isn't a
++       * PCI device, we probably want to move this to a bus-specific
++       * probe function.  Do we even need to do this?
++       */
++      cyber2000fb_writeb(0x18, 0x46e8, cfb);
++      cyber2000fb_writeb(0x01, 0x102, cfb);
++      cyber2000fb_writeb(0x08, 0x46e8, cfb);
++      cyber2000fb_writeb(EXT_BIU_MISC, 0x3ce, cfb);
++      cyber2000fb_writeb(EXT_BIU_MISC_LIN_ENABLE, 0x3cf, cfb);
++#else
++      /*
++       * Most other machine types are "normal", so
++       * we use the standard IO-based wakeup.
++       */
++      outb(0x18, 0x46e8);
++      outb(0x01, 0x102);
++      outb(0x08, 0x46e8);
++      outb(EXT_BIU_MISC, 0x3ce);
++      outb(EXT_BIU_MISC_LIN_ENABLE, 0x3cf);
++#endif
++
++      /*
++       * Allow the CyberPro to accept PCI burst accesses
++       */
++      val = cyber2000_grphr(EXT_BUS_CTL, cfb);
++      if (!(val & EXT_BUS_CTL_PCIBURST_WRITE)) {
++              printk(KERN_INFO "%s: enabling PCI bursts\n", cfb->fb.fix.id);
++
++              val |= EXT_BUS_CTL_PCIBURST_WRITE;
++
++              if (cfb->id == ID_CYBERPRO_5000)
++                      val |= EXT_BUS_CTL_PCIBURST_READ;
++
++              cyber2000_grphw(EXT_BUS_CTL, val, cfb);
++      }
++
++      return 0;
++}
++
++static int __devinit
++cyberpro_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
++{
++      struct cfb_info *cfb;
++      char name[16];
++      int err;
++
++      sprintf(name, "CyberPro%4X", id->device);
++
++      err = pci_enable_device(dev);
++      if (err)
++              return err;
++
++      err = pci_request_regions(dev, name);
++      if (err)
++              return err;
++
++      err = -ENOMEM;
++      cfb = cyberpro_alloc_fb_info(id->driver_data, name);
++      if (!cfb)
++              goto failed_release;
++
++      cfb->dev = dev;
++      cfb->region = ioremap(pci_resource_start(dev, 0),
++                            pci_resource_len(dev, 0));
++      if (!cfb->region)
++              goto failed_ioremap;
++
++      cfb->regs = cfb->region + MMIO_OFFSET;
++      cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
++      cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
++
++      /*
++       * Bring up the hardware.  This is expected to enable access
++       * to the linear memory region, and allow access to the memory
++       * mapped registers.  Also, mem_ctl1 and mem_ctl2 must be
++       * initialised.
++       */
++      err = cyberpro_pci_enable_mmio(cfb);
++      if (err)
++              goto failed;
++
++      /*
++       * Use MCLK from BIOS. FIXME: what about hotplug?
++       */
++      cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb);
++      cfb->mclk_div  = cyber2000_grphr(EXT_MCLK_DIV, cfb);
++#ifdef __arm__
++      if (machine_is_netwinder() || machine_is_shark()) {
++              /*
++               * MCLK on the NetWinder and the Shark is fixed at 75MHz
++               */
++              cfb->mclk_mult = 0xdb;
++              cfb->mclk_div  = 0x54;
++      }
++#endif
++
++      err = cyberpro_common_probe(cfb);
++      if (err)
+               goto failed;
+       /*
+@@ -1672,7 +1874,7 @@
+       return err;
+ }
+-static void __devexit cyberpro_remove(struct pci_dev *dev)
++static void __devexit cyberpro_pci_remove(struct pci_dev *dev)
+ {
+       struct cfb_info *cfb = pci_get_drvdata(dev);
+@@ -1701,7 +1903,7 @@
+       }
+ }
+-static int cyberpro_suspend(struct pci_dev *dev, u32 state)
++static int cyberpro_pci_suspend(struct pci_dev *dev, u32 state)
+ {
+       return 0;
+ }
+@@ -1709,41 +1911,44 @@
+ /*
+  * Re-initialise the CyberPro hardware
+  */
+-static int cyberpro_resume(struct pci_dev *dev)
++static int cyberpro_pci_resume(struct pci_dev *dev)
+ {
+       struct cfb_info *cfb = pci_get_drvdata(dev);
+       if (cfb) {
+-              cyberpro_init_hw(cfb, 0);
+-
+-              /*
+-               * Restore the old video mode and the palette.
+-               * We also need to tell fbcon to redraw the console.
+-               */
+-              cfb->fb.var.activate = FB_ACTIVATE_NOW;
+-              cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
++              cyberpro_pci_enable_mmio(cfb);
++              cyberpro_common_resume(cfb);
+       }
+       return 0;
+ }
+ static struct pci_device_id cyberpro_pci_table[] __devinitdata = {
++//    Not yet
++//    { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682,
++//            PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_IGA_1682 },
+       { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000,
+-              PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2000 },
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2000 },
+       { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010,
+-              PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2010 },
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2010 },
+       { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000,
+-              PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER5000 },
++              PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_5000 },
+       { 0, }
+ };
++MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
++
++#ifndef __devexit_p
++#define __devexit_p(x) (x)
++#endif
++
+ static struct pci_driver cyberpro_driver = {
+-      name:           "CyberPro",
+-      probe:          cyberpro_probe,
+-      remove:         __devexit_p(cyberpro_remove),
+-      suspend:        cyberpro_suspend,
+-      resume:         cyberpro_resume,
+-      id_table:       cyberpro_pci_table
++      .name           = "CyberPro",
++      .probe          = cyberpro_pci_probe,
++      .remove         = __devexit_p(cyberpro_pci_remove),
++      .suspend        = cyberpro_pci_suspend,
++      .resume         = cyberpro_pci_resume,
++      .id_table       = cyberpro_pci_table
+ };
+ /*
+@@ -1768,5 +1973,4 @@
+ MODULE_AUTHOR("Russell King");
+ MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
+-MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
+ MODULE_LICENSE("GPL");
+--- linux-2.4.27/drivers/video/cyber2000fb.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/cyber2000fb.h
+@@ -36,24 +36,55 @@
+ #define debug_printf(x...) do { } while (0)
+ #endif
+-#define PIXFORMAT_8BPP                0
+-#define PIXFORMAT_16BPP               1
+-#define PIXFORMAT_24BPP               2
+-#define PIXFORMAT_32BPP               3
++#define RAMDAC_RAMPWRDN               0x01
++#define RAMDAC_DAC8BIT                0x02
++#define RAMDAC_VREFEN         0x04
++#define RAMDAC_BYPASS         0x10
++#define RAMDAC_DACPWRDN               0x40
+-#define VISUALID_256          1
+-#define VISUALID_64K          2
+-#define VISUALID_16M_32               3
+-#define VISUALID_16M          4
+-#define VISUALID_32K          6
++#define EXT_CRT_VRTOFL                0x11
++#define EXT_CRT_VRTOFL_LINECOMP10     0x10
++#define EXT_CRT_VRTOFL_INTERLACE      0x20
+-#define FUNC_CTL              0x3c
+-#define FUNC_CTL_EXTREGENBL           0x80    /* enable access to 0xbcxxx             */
++#define EXT_CRT_IRQ           0x12
++#define EXT_CRT_IRQ_ENABLE            0x01
++#define EXT_CRT_IRQ_ACT_HIGH          0x04
+-#define BIU_BM_CONTROL                0x3e
+-#define BIU_BM_CONTROL_ENABLE         0x01    /* enable bus-master                    */
+-#define BIU_BM_CONTROL_BURST          0x02    /* enable burst                         */
+-#define BIU_BM_CONTROL_BACK2BACK      0x04    /* enable back to back                  */
++#define EXT_CRT_TEST          0x13
++
++#define EXT_SYNC_CTL          0x16
++#define EXT_SYNC_CTL_HS_NORMAL                0x00
++#define EXT_SYNC_CTL_HS_0             0x01
++#define EXT_SYNC_CTL_HS_1             0x02
++#define EXT_SYNC_CTL_HS_HSVS          0x03
++#define EXT_SYNC_CTL_VS_NORMAL                0x00
++#define EXT_SYNC_CTL_VS_0             0x04
++#define EXT_SYNC_CTL_VS_1             0x08
++#define EXT_SYNC_CTL_VS_COMP          0x0c
++
++#define EXT_BUS_CTL           0x30
++#define EXT_BUS_CTL_LIN_1MB           0x00
++#define EXT_BUS_CTL_LIN_2MB           0x01
++#define EXT_BUS_CTL_LIN_4MB           0x02
++#define EXT_BUS_CTL_ZEROWAIT          0x04
++#define EXT_BUS_CTL_PCIBURST_WRITE    0x20
++#define EXT_BUS_CTL_PCIBURST_READ     0x80    /* CyberPro 5000 only */
++
++#define EXT_SEG_WRITE_PTR     0x31
++#define EXT_SEG_READ_PTR      0x32
++#define EXT_BIU_MISC          0x33
++#define EXT_BIU_MISC_LIN_ENABLE               0x01
++#define EXT_BIU_MISC_COP_ENABLE               0x04
++#define EXT_BIU_MISC_COP_BFC          0x08
++
++#define EXT_FUNC_CTL          0x3c
++#define EXT_FUNC_CTL_EXTREGENBL               0x80    /* enable access to 0xbcxxx             */
++
++#define PCI_BM_CTL            0x3e
++#define PCI_BM_CTL_ENABLE             0x01    /* enable bus-master                    */
++#define PCI_BM_CTL_BURST              0x02    /* enable burst                         */
++#define PCI_BM_CTL_BACK2BACK          0x04    /* enable back to back                  */
++#define PCI_BM_CTL_DUMMY              0x08    /* insert dummy cycle                   */
+ #define X_V2_VID_MEM_START    0x40
+ #define X_V2_VID_SRC_WIDTH    0x43
+@@ -87,6 +118,19 @@
+ #define K_CAP_X2_CTL1         0x49
++#define CURS_H_START          0x50
++#define CURS_H_PRESET         0x52
++#define CURS_V_START          0x53
++#define CURS_V_PRESET         0x55
++#define CURS_CTL              0x56
++
++#define EXT_ATTRIB_CTL                0x57
++#define EXT_ATTRIB_CTL_EXT            0x01
++
++#define EXT_OVERSCAN_RED      0x58
++#define EXT_OVERSCAN_GREEN    0x59
++#define EXT_OVERSCAN_BLUE     0x5a
++
+ #define CAP_X_START           0x60
+ #define CAP_X_END             0x62
+ #define CAP_Y_START           0x64
+@@ -96,46 +140,112 @@
+ #define CAP_DDA_Y_INIT                0x6c
+ #define CAP_DDA_Y_INC         0x6e
+-#define MEM_CTL1              0x71
++#define EXT_MEM_CTL0          0x70
++#define EXT_MEM_CTL0_7CLK             0x01
++#define EXT_MEM_CTL0_RAS_1            0x02
++#define EXT_MEM_CTL0_RAS2CAS_1                0x04
++#define EXT_MEM_CTL0_MULTCAS          0x08
++#define EXT_MEM_CTL0_ASYM             0x10
++#define EXT_MEM_CTL0_CAS1ON           0x20
++#define EXT_MEM_CTL0_FIFOFLUSH                0x40
++#define EXT_MEM_CTL0_SEQRESET         0x80
+-#define MEM_CTL2              0x72
++#define EXT_MEM_CTL1          0x71
++#define EXT_MEM_CTL1_PAR              0x00
++#define EXT_MEM_CTL1_SERPAR           0x01
++#define EXT_MEM_CTL1_SER              0x03
++#define EXT_MEM_CTL1_SYNC             0x04
++#define EXT_MEM_CTL1_VRAM             0x08
++#define EXT_MEM_CTL1_4K_REFRESH               0x10
++#define EXT_MEM_CTL1_256Kx4           0x00
++#define EXT_MEM_CTL1_512Kx8           0x40
++#define EXT_MEM_CTL1_1Mx16            0x60
++
++#define EXT_MEM_CTL2          0x72
++#define MEM_CTL2_SIZE_1MB             0x00
+ #define MEM_CTL2_SIZE_2MB             0x01
+ #define MEM_CTL2_SIZE_4MB             0x02
+ #define MEM_CTL2_SIZE_MASK            0x03
+ #define MEM_CTL2_64BIT                        0x04
++#define EXT_HIDDEN_CTL1               0x73
++
+ #define EXT_FIFO_CTL          0x74
++#define EXT_SEQ_MISC          0x77
++#define EXT_SEQ_MISC_8                        0x01
++#define EXT_SEQ_MISC_16_RGB565                0x02
++#define EXT_SEQ_MISC_32                       0x03
++#define EXT_SEQ_MISC_24_RGB888                0x04
++#define EXT_SEQ_MISC_16_RGB555                0x06
++#define EXT_SEQ_MISC_8_RGB332         0x09
++#define EXT_SEQ_MISC_16_RGB444                0x0a
++
++#define EXT_HIDDEN_CTL4               0x7a
++
++#define CURS_MEM_START                0x7e            /* bits 23..12 */
++
+ #define CAP_PIP_X_START               0x80
+ #define CAP_PIP_X_END         0x82
+ #define CAP_PIP_Y_START               0x84
+ #define CAP_PIP_Y_END         0x86
+-#define CAP_NEW_CTL1          0x88
++#define EXT_CAP_CTL1          0x88
+-#define CAP_NEW_CTL2          0x89
++#define EXT_CAP_CTL2          0x89
++#define EXT_CAP_CTL2_ODDFRAMEIRQ      0x01
++#define EXT_CAP_CTL2_ANYFRAMEIRQ      0x02
+ #define BM_CTRL0              0x9c
+ #define BM_CTRL1              0x9d
+-#define CAP_MODE1             0xa4
+-#define CAP_MODE1_8BIT                        0x01    /* enable 8bit capture mode             */
+-#define CAP_MODE1_CCIR656             0x02    /* CCIR656 mode                         */
+-#define CAP_MODE1_IGNOREVGT           0x04    /* ignore VGT                           */
+-#define CAP_MODE1_ALTFIFO             0x10    /* use alternate FIFO for capture       */
+-#define CAP_MODE1_SWAPUV              0x20    /* swap UV bytes                        */
+-#define CAP_MODE1_MIRRORY             0x40    /* mirror vertically                    */
+-#define CAP_MODE1_MIRRORX             0x80    /* mirror horizontally                  */
++#define EXT_CAP_MODE1         0xa4
++#define EXT_CAP_MODE1_8BIT            0x01    /* enable 8bit capture mode             */
++#define EXT_CAP_MODE1_CCIR656         0x02    /* CCIR656 mode                         */
++#define EXT_CAP_MODE1_IGNOREVGT               0x04    /* ignore VGT                           */
++#define EXT_CAP_MODE1_ALTFIFO         0x10    /* use alternate FIFO for capture       */
++#define EXT_CAP_MODE1_SWAPUV          0x20    /* swap UV bytes                        */
++#define EXT_CAP_MODE1_MIRRORY         0x40    /* mirror vertically                    */
++#define EXT_CAP_MODE1_MIRRORX         0x80    /* mirror horizontally                  */
+-#define DCLK_MULT             0xb0
+-#define DCLK_DIV              0xb1
+-#define DCLK_DIV_VFSEL                        0x20
+-#define MCLK_MULT             0xb2
+-#define MCLK_DIV              0xb3
++#define EXT_CAP_MODE2         0xa5
++#define EXT_CAP_MODE2_CCIRINVOE               0x01
++#define EXT_CAP_MODE2_CCIRINVVGT      0x02
++#define EXT_CAP_MODE2_CCIRINVHGT      0x04
++#define EXT_CAP_MODE2_CCIRINVDG               0x08
++#define EXT_CAP_MODE2_DATEND          0x10
++#define EXT_CAP_MODE2_CCIRDGH         0x20
++#define EXT_CAP_MODE2_FIXSONY         0x40
++#define EXT_CAP_MODE2_SYNCFREEZE      0x80
+-#define CAP_MODE2             0xa5
++#define EXT_TV_CTL            0xae
+-#define Y_TV_CTL              0xae
++#define EXT_DCLK_MULT         0xb0
++#define EXT_DCLK_DIV          0xb1
++#define EXT_DCLK_DIV_VFSEL            0x20
++#define EXT_MCLK_MULT         0xb2
++#define EXT_MCLK_DIV          0xb3
++
++#define EXT_LATCH1            0xb5
++#define EXT_LATCH1_VAFC_EN            0x01    /* enable VAFC                          */
++
++#define EXT_FEATURE           0xb7
++#define EXT_FEATURE_BUS_MASK          0x07    /* host bus mask                        */
++#define EXT_FEATURE_BUS_PCI           0x00
++#define EXT_FEATURE_BUS_VL_STD                0x04
++#define EXT_FEATURE_BUS_VL_LINEAR     0x05
++#define EXT_FEATURE_1682              0x20    /* IGS 1682 compatibility               */
++
++#define EXT_LATCH2            0xb6
++#define EXT_LATCH2_I2C_CLKEN          0x10
++#define EXT_LATCH2_I2C_CLK            0x20
++#define EXT_LATCH2_I2C_DATEN          0x40
++#define EXT_LATCH2_I2C_DAT            0x80
++
++#define EXT_XT_CTL            0xbe
++#define EXT_XT_CAP16                  0x04
++#define EXT_XT_LINEARFB                       0x08
++#define EXT_XT_PAL                    0x10
+ #define EXT_MEM_START         0xc0            /* ext start address 21 bits            */
+ #define HOR_PHASE_SHIFT               0xc2            /* high 3 bits                          */
+@@ -160,25 +270,37 @@
+ #define EXT_VID_FMT_RGB565            0x02
+ #define EXT_VID_FMT_RGB888_24         0x03
+ #define EXT_VID_FMT_RGB888_32         0x04
++#define EXT_VID_FMT_RGB8              0x05
++#define EXT_VID_FMT_RGB4444           0x06
++#define EXT_VID_FMT_RGB8T             0x07
+ #define EXT_VID_FMT_DUP_PIX_ZOON      0x08    /* duplicate pixel zoom                 */
+ #define EXT_VID_FMT_MOD_3RD_PIX               0x20    /* modify 3rd duplicated pixel          */
+ #define EXT_VID_FMT_DBL_H_PIX         0x40    /* double horiz pixels                  */
+-#define EXT_VID_FMT_UV128             0x80    /* UV data offset by 128                */
++#define EXT_VID_FMT_YUV128            0x80    /* YUV data offset by 128               */
+ #define EXT_VID_DISP_CTL1     0xdc
+ #define EXT_VID_DISP_CTL1_INTRAM      0x01    /* video pixels go to internal RAM      */
+ #define EXT_VID_DISP_CTL1_IGNORE_CCOMP        0x02    /* ignore colour compare registers      */
+ #define EXT_VID_DISP_CTL1_NOCLIP      0x04    /* do not clip to 16235,16240           */
+ #define EXT_VID_DISP_CTL1_UV_AVG      0x08    /* U/V data is averaged                 */
+-#define EXT_VID_DISP_CTL1_Y128                0x10    /* Y data offset by 128                 */
+-#define EXT_VID_DISP_CTL1_VINTERPOL_OFF       0x20    /* vertical interpolation off           */
++#define EXT_VID_DISP_CTL1_Y128                0x10    /* Y data offset by 128 (if YUV128 set) */
++#define EXT_VID_DISP_CTL1_VINTERPOL_OFF       0x20    /* disable vertical interpolation       */
+ #define EXT_VID_DISP_CTL1_FULL_WIN    0x40    /* video out window full                */
+ #define EXT_VID_DISP_CTL1_ENABLE_WINDOW       0x80    /* enable video window                  */
+ #define EXT_VID_FIFO_CTL1     0xdd
++#define EXT_VID_FIFO_CTL1_OE_HIGH     0x02
++#define EXT_VID_FIFO_CTL1_INTERLEAVE  0x04    /* enable interleaved memory read       */
++
++#define EXT_ROM_UCB4GH                0xe5
++#define EXT_ROM_UCB4GH_FREEZE         0x02    /* capture frozen                       */
++#define EXT_ROM_UCB4GH_ODDFRAME               0x04    /* 1 = odd frame captured               */
++#define EXT_ROM_UCB4GH_1HL            0x08    /* first horizonal line after VGT falling edge */
++#define EXT_ROM_UCB4GH_ODD            0x10    /* odd frame indicator                  */
++#define EXT_ROM_UCB4GH_INTSTAT                0x20    /* video interrupt                      */
+ #define VFAC_CTL1             0xe8
+-#define VFAC_CTL1_CAPTURE             0x01    /* capture enable                       */
++#define VFAC_CTL1_CAPTURE             0x01    /* capture enable (only when VSYNC high)*/
+ #define VFAC_CTL1_VFAC_ENABLE         0x02    /* vfac enable                          */
+ #define VFAC_CTL1_FREEZE_CAPTURE      0x04    /* freeze capture                       */
+ #define VFAC_CTL1_FREEZE_CAPTURE_SYNC 0x08    /* sync freeze capture                  */
+@@ -197,6 +319,13 @@
+ #define VFAC_CTL2_INVERT_OVSYNC               0x80    /* invert other vsync input             */
+ #define VFAC_CTL3             0xea
++#define VFAC_CTL3_CAP_LARGE_FIFO      0x01    /* large capture fifo                   */
++#define VFAC_CTL3_CAP_INTERLACE               0x02    /* capture odd and even fields          */
++#define VFAC_CTL3_CAP_HOLD_4NS                0x00    /* hold capture data for 4ns            */
++#define VFAC_CTL3_CAP_HOLD_2NS                0x04    /* hold capture data for 2ns            */
++#define VFAC_CTL3_CAP_HOLD_6NS                0x08    /* hold capture data for 6ns            */
++#define VFAC_CTL3_CAP_HOLD_0NS                0x0c    /* hold capture data for 0ns            */
++#define VFAC_CTL3_CHROMAKEY           0x20    /* capture data will be chromakeyed     */
+ #define VFAC_CTL3_CAP_IRQ             0x40    /* enable capture interrupt             */
+ #define CAP_MEM_START         0xeb            /* 18 bits                              */
+@@ -235,26 +364,98 @@
+ #define BM_COUNT              0xbc090         /* read-only                            */
+ /*
+- * Graphics Co-processor
++ * TV registers
+  */
+-#define CO_CMD_L_PATTERN_FGCOL        0x8000
+-#define CO_CMD_L_INC_LEFT     0x0004
+-#define CO_CMD_L_INC_UP               0x0002
+-
+-#define CO_CMD_H_SRC_PIXMAP   0x2000
+-#define CO_CMD_H_BLITTER      0x0800
++#define TV_VBLANK_EVEN_START  0xbe43c
++#define TV_VBLANK_EVEN_END    0xbe440
++#define TV_VBLANK_ODD_START   0xbe444
++#define TV_VBLANK_ODD_END     0xbe448
++#define TV_SYNC_YGAIN         0xbe44c
++#define TV_UV_GAIN            0xbe450
++#define TV_PED_UVDET          0xbe454
++#define TV_UV_BURST_AMP               0xbe458
++#define TV_HSYNC_START                0xbe45c
++#define TV_HSYNC_END          0xbe460
++#define TV_Y_DELAY1           0xbe464
++#define TV_Y_DELAY2           0xbe468
++#define TV_UV_DELAY1          0xbe46c
++#define TV_BURST_START                0xbe470
++#define TV_BURST_END          0xbe474
++#define TV_HBLANK_START               0xbe478
++#define TV_HBLANK_END         0xbe47c
++#define TV_PED_EVEN_START     0xbe480
++#define TV_PED_EVEN_END               0xbe484
++#define TV_PED_ODD_START      0xbe488
++#define TV_PED_ODD_END                0xbe48c
++#define TV_VSYNC_EVEN_START   0xbe490
++#define TV_VSYNC_EVEN_END     0xbe494
++#define TV_VSYNC_ODD_START    0xbe498
++#define TV_VSYNC_ODD_END      0xbe49c
++#define TV_SCFL                       0xbe4a0
++#define TV_SCFH                       0xbe4a4
++#define TV_SCP                        0xbe4a8
++#define TV_DELAYBYPASS                0xbe4b4
++#define TV_EQL_END            0xbe4bc
++#define TV_SERR_START         0xbe4c0
++#define TV_SERR_END           0xbe4c4
++#define TV_CTL                        0xbe4dc /* reflects a previous register- MVFCLR, MVPCLR etc P241*/
++#define TV_VSYNC_VGA_HS               0xbe4e8
++#define TV_FLICK_XMIN         0xbe514
++#define TV_FLICK_XMAX         0xbe518
++#define TV_FLICK_YMIN         0xbe51c
++#define TV_FLICK_YMAX         0xbe520
++/*
++ * Graphics Co-processor
++ */
+ #define CO_REG_CONTROL                0xbf011
++#define CO_CTRL_BUSY                  0x80
++#define CO_CTRL_CMDFULL                       0x04
++#define CO_CTRL_FIFOEMPTY             0x02
++#define CO_CTRL_READY                 0x01
++
+ #define CO_REG_SRC_WIDTH      0xbf018
+-#define CO_REG_PIX_FORMAT     0xbf01c
+-#define CO_REG_FORE_MIX               0xbf048
+-#define CO_REG_FOREGROUND     0xbf058
+-#define CO_REG_WIDTH          0xbf060
+-#define CO_REG_HEIGHT         0xbf062
++#define CO_REG_PIXFMT         0xbf01c
++#define CO_PIXFMT_32BPP                       0x03
++#define CO_PIXFMT_24BPP                       0x02
++#define CO_PIXFMT_16BPP                       0x01
++#define CO_PIXFMT_8BPP                        0x00
++
++#define CO_REG_FGMIX          0xbf048
++#define CO_FG_MIX_ZERO                        0x00
++#define CO_FG_MIX_SRC_AND_DST         0x01
++#define CO_FG_MIX_SRC_AND_NDST                0x02
++#define CO_FG_MIX_SRC                 0x03
++#define CO_FG_MIX_NSRC_AND_DST                0x04
++#define CO_FG_MIX_DST                 0x05
++#define CO_FG_MIX_SRC_XOR_DST         0x06
++#define CO_FG_MIX_SRC_OR_DST          0x07
++#define CO_FG_MIX_NSRC_AND_NDST               0x08
++#define CO_FG_MIX_SRC_XOR_NDST                0x09
++#define CO_FG_MIX_NDST                        0x0a
++#define CO_FG_MIX_SRC_OR_NDST         0x0b
++#define CO_FG_MIX_NSRC                        0x0c
++#define CO_FG_MIX_NSRC_OR_DST         0x0d
++#define CO_FG_MIX_NSRC_OR_NDST                0x0e
++#define CO_FG_MIX_ONES                        0x0f
++
++#define CO_REG_FGCOLOUR               0xbf058
++#define CO_REG_BGCOLOUR               0xbf05c
++#define CO_REG_PIXWIDTH               0xbf060
++#define CO_REG_PIXHEIGHT      0xbf062
+ #define CO_REG_X_PHASE                0xbf078
+ #define CO_REG_CMD_L          0xbf07c
++#define CO_CMD_L_PATTERN_FGCOL                0x8000
++#define CO_CMD_L_INC_LEFT             0x0004
++#define CO_CMD_L_INC_UP                       0x0002
++
+ #define CO_REG_CMD_H          0xbf07e
+-#define CO_REG_SRC_PTR                0xbf170
++#define CO_CMD_H_BGSRCMAP             0x8000  /* otherwise bg colour */
++#define CO_CMD_H_FGSRCMAP             0x2000  /* otherwise fg colour */
++#define CO_CMD_H_BLITTER              0x0800
++
++#define CO_REG_SRC1_PTR               0xbf170
++#define CO_REG_SRC2_PTR               0xbf174
+ #define CO_REG_DEST_PTR               0xbf178
+ #define CO_REG_DEST_WIDTH     0xbf218
+@@ -269,6 +470,7 @@
+       char            *fb;
+       char            dev_name[32];
+       unsigned int    fb_size;
++      unsigned int    chip_id;
+       /*
+        * The following is a pointer to be passed into the
+@@ -288,10 +490,19 @@
+       void (*disable_extregs)(struct cfb_info *);
+ };
++#define ID_IGA_1682           0
++#define ID_CYBERPRO_2000      1
++#define ID_CYBERPRO_2010      2
++#define ID_CYBERPRO_5000      3
++
++struct fb_var_screeninfo;
++
+ /*
+  * Note! Writing to the Cyber20x0 registers from an interrupt
+  * routine is definitely a bad idea atm.
+  */
+ int cyber2000fb_attach(struct cyberpro_info *info, int idx);
+ void cyber2000fb_detach(int idx);
+-
++void cyber2000fb_enable_extregs(struct cfb_info *cfb);
++void cyber2000fb_disable_extregs(struct cfb_info *cfb);
++void cyber2000fb_get_fb_var(struct cfb_info *cfb, struct fb_var_screeninfo *var);
+--- /dev/null
++++ linux-2.4.27/drivers/video/dbmx1fb.c
+@@ -0,0 +1,2002 @@
++/******************************************************************************
++      Copyright (C) 2002 Motorola GSG-China
++
++      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.
++*******************************************************************************/
++/*****************************************************************************
++ * File Name: dbmx1fb.c
++ *
++ * Progammers:        Chen Ning, Zhang Juan
++ *
++ * Date of Creations: 10 DEC,2001
++ *
++ * Synopsis:
++ *
++ * Descirption: DB-MX1 LCD controller Linux frame buffer driver
++ *            This file is subject to the terms and conditions of the
++ *            GNU General Public License.  See the file COPYING in the main
++ *            directory of this archive for more details.
++ *
++ * Modification History:
++ * 10 DEC, 2001, initialization version, frame work for frame buffer driver
++ *
++*******************************************************************************/
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ctype.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/fb.h>
++#include <linux/delay.h>
++#include <linux/wrapper.h>
++#include <linux/selection.h>
++#include <linux/console.h>
++#include <linux/kd.h>
++#include <linux/vt_kern.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++#include <asm/uaccess.h>
++#include <asm/proc/pgtable.h>
++
++#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 "asm/arch/hardware.h"
++#include "asm/arch/platform.h"
++#include "asm/arch/memory.h"
++
++#include "dbmx1fb.h"
++
++#undef SUP_TTY0
++
++#define LCD_PM
++#ifdef LCD_PM
++#include <linux/pm.h>
++struct pm_dev *pm;
++#endif
++
++// PLAM - make sure fbmem.c also has this defined for full screen frame
++// buffer support in SDRAM
++#define FULL_SCREEN
++
++#undef HARDWARE_CURSOR
++// #undef HARDWARE_CURSOR
++#undef DEBUG
++
++
++/********************************************************************************/
++#ifdef DEBUG
++#  define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
++#define FUNC_START    DPRINTK(KERN_ERR"start\n");
++#define FUNC_END      DPRINTK(KERN_ERR"end\n");
++#else
++#  define DPRINTK(fmt, args...)
++#define FUNC_START
++#define FUNC_END
++#endif
++
++#define _IO_ADDRESS(r)                ((r)+0xf0000000)
++unsigned int READREG(unsigned int r)
++{
++      volatile unsigned int * reg;
++      reg = (volatile unsigned int*) _IO_ADDRESS(r);
++      return *reg;
++}
++void WRITEREG(unsigned int r, unsigned int val)
++{
++      volatile unsigned int *reg;
++      reg = (volatile unsigned int*) _IO_ADDRESS(r);
++      *reg = val;
++      return;
++}
++
++#define FONT_DATA ((unsigned char *)font->data)
++struct fbcon_font_desc *font;
++
++/* Local LCD controller parameters */
++struct dbmx1fb_par{
++      u_char          *screen_start_address;  /* Screen Start Address */
++      u_char          *v_screen_start_address;/* Virtul Screen Start Address */
++      unsigned long   screen_memory_size;     /* screen memory size */
++      unsigned int    palette_size;
++      unsigned int    max_xres;
++      unsigned int    max_yres;
++      unsigned int    xres;
++      unsigned int    yres;
++      unsigned int    xres_virtual;
++      unsigned int    yres_virtual;
++      unsigned int    max_bpp;
++      unsigned int    bits_per_pixel;
++      unsigned int    currcon;
++      unsigned int    visual;
++      unsigned int    TFT :1;
++      unsigned int    color :1 ;
++      unsigned int    sharp :1 ;
++
++        unsigned short cfb16[16];
++};
++
++#ifdef HARDWARE_CURSOR
++/* hardware cursor parameters */
++struct dbmx1fb_cursor{
++      //      int     enable;
++              int     startx;
++              int     starty;
++              int     blinkenable;
++              int     blink_rate;
++              int     width;
++              int     height;
++              int     color[3];
++              int     state;
++};
++
++/* Frame buffer of LCD information */
++struct dbmx1fb_info{
++      struct display_switch dispsw;
++      struct dbmx1fb_cursor cursor;
++};
++#endif // HARDWARE_CURSOR
++
++static u_char*        p_framebuffer_memory_address;
++static u_char*        v_framebuffer_memory_address;
++
++/* Fake monspecs to fill in fbinfo structure */
++static struct fb_monspecs monspecs __initdata = {
++       30000, 70000, 50, 65, 0        /* Generic */
++};
++
++/* color map initial */
++static unsigned short __attribute__((unused)) color4map[16] = {
++      0x0000, 0x000f, 0x00f0, 0x0f2a, 0x0f00, 0x0f0f, 0x0f88, 0x0ccc,
++      0x0888, 0x00ff, 0x00f8, 0x0f44, 0x0fa6, 0x0f22, 0x0ff0, 0x0fff
++};
++
++static unsigned short gray4map[16] = {
++      0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
++      0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f
++};
++
++static struct display global_disp;      /* Initial (default) Display Settings */
++static struct fb_info fb_info;
++static struct fb_var_screeninfo init_var = {};
++static struct dbmx1fb_par current_par={ };
++
++/* Frame buffer device API */
++static int  dbmx1fb_get_fix(struct fb_fix_screeninfo *fix, int con,
++              struct fb_info *info);
++static int  dbmx1fb_get_var(struct fb_var_screeninfo *var, int con,
++              struct fb_info *info);
++static int  dbmx1fb_set_var(struct fb_var_screeninfo *var, int con,
++              struct fb_info *info);
++static int  dbmx1fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++              struct fb_info *info);
++static int  dbmx1fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
++              struct fb_info *info);
++
++/* Interface to the low level driver  */
++static int  dbmx1fb_switch(int con, struct fb_info *info);
++static void dbmx1fb_blank(int blank, struct fb_info *info);
++static int dbmx1fb_updatevar(int con, struct fb_info *info);
++
++/*  Internal routines  */
++static int  _reserve_fb_memory(void);
++static void _install_cmap(int con, struct fb_info *info);
++static void _enable_lcd_controller(void);
++static void _disable_lcd_controller(void);
++static int  _encode_var(struct fb_var_screeninfo *var,
++                      struct dbmx1fb_par *par);
++static int  _decode_var(struct fb_var_screeninfo *var,
++                    struct dbmx1fb_par *par);
++
++/* initialization routines */
++static void __init _init_lcd_system(void);
++static int  __init _init_lcd(void);
++static void __init _init_fbinfo(void);
++static int  __init _reserve_fb_memory(void);
++
++/* frame buffer ops */
++static struct fb_ops dbmx1fb_ops = {
++        owner:          THIS_MODULE,
++        fb_get_fix:     dbmx1fb_get_fix,
++        fb_get_var:     dbmx1fb_get_var,
++        fb_set_var:     dbmx1fb_set_var,
++        fb_get_cmap:    dbmx1fb_get_cmap,
++        fb_set_cmap:    dbmx1fb_set_cmap,
++};
++
++#ifdef HARDWARE_CURSOR
++/* Hardware Cursor */
++static void dbmx1fb_cursor(struct display *p, int mode, int x, int y);
++static int dbmx1fb_set_font(struct display *d, int width, int height);
++static UINT8 cursor_color_map[] = {0xf8};
++static void dbmx1fb_set_cursor_state(struct dbmx1fb_info *fb,UINT32 state);
++static void dbmx1fb_set_cursor(struct dbmx1fb_info *fb);
++static void dbmx1fb_set_cursor_blink(struct dbmx1fb_info *fb,int blink);
++
++struct display_switch dbmx1fb_cfb4 = {
++          setup:              fbcon_cfb4_setup,
++          bmove:              fbcon_cfb4_bmove,
++          clear:              fbcon_cfb4_clear,
++          putc:               fbcon_cfb4_putc,
++          putcs:              fbcon_cfb4_putcs,
++          revc:               fbcon_cfb4_revc,
++          cursor:             dbmx1fb_cursor,
++          set_font:           dbmx1fb_set_font,
++          fontwidthmask:      FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++
++struct display_switch dbmx1fb_cfb8 = {
++          setup:              fbcon_cfb8_setup,
++          bmove:              fbcon_cfb8_bmove,
++          clear:              fbcon_cfb8_clear,
++          putc:               fbcon_cfb8_putc,
++          putcs:              fbcon_cfb8_putcs,
++          revc:               fbcon_cfb8_revc,
++          cursor:             dbmx1fb_cursor,
++          set_font:           dbmx1fb_set_font,
++          fontwidthmask:      FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++
++struct display_switch dbmx1fb_cfb16 = {
++          setup:              fbcon_cfb16_setup,
++          bmove:              fbcon_cfb16_bmove,
++          clear:              fbcon_cfb16_clear,
++          putc:               fbcon_cfb16_putc,
++          putcs:              fbcon_cfb16_putcs,
++          revc:               fbcon_cfb16_revc,
++          cursor:             dbmx1fb_cursor,
++          set_font:           dbmx1fb_set_font,
++          fontwidthmask:      FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(16)
++};
++#endif // HARDWARE_CURSOR
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_getcolreg()
++ *
++ * Input:     regno   : Color register ID
++ *            red     : Color map red[]
++ *            green   : Color map green[]
++ *            blue    : Color map blue[]
++ *    transparent     : Flag
++ *            info    : Fb_info database
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Description: Transfer to fb_xxx_cmap handlers as parameters to
++ *            control color registers
++ *
++ * Modification History:
++ *    10 DEC,2001, Chen Ning
++******************************************************************************/
++#define RED   0xf00
++#define GREEN 0xf0
++#define BLUE  0x0f
++static int dbmx1fb_getcolreg(u_int regno, u_int *red, u_int *green,
++      u_int *blue, u_int *trans, struct fb_info *info)
++{
++      unsigned int val;
++
++      FUNC_START;
++
++      if(regno >= current_par.palette_size)
++              return 1;
++
++      val = READREG(DBMX1_LCD_MAPRAM+regno);
++
++      if((current_par.bits_per_pixel == 4)&&(!current_par.color))
++      {
++              *red = *green = *blue = (val & BLUE) << 4;//TODO:
++              *trans = 0;
++      }
++      else
++      {
++              *red = (val & RED) << 4;
++              *green = (val & GREEN) << 8;
++              *blue = (val & BLUE) << 12;
++              *trans = 0;
++      }
++
++      FUNC_END;
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_setcolreg()
++ *
++ * Input:     regno   : Color register ID
++ *            red     : Color map red[]
++ *            green   : Color map green[]
++ *            blue    : Color map blue[]
++ *    transparent     : Flag
++ *            info    : Fb_info database
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Description: Transfer to fb_xxx_cmap handlers as parameters to
++ *            control color registers
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++ *****************************************************************************/
++static int
++dbmx1fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++              u_int trans, struct fb_info *info)
++{
++      unsigned int val=0;
++      FUNC_START
++      if(regno >= current_par.palette_size)
++              return 1;
++
++      if((current_par.bits_per_pixel == 4)&&(!current_par.color))
++              val = (blue & 0x00f) << 12;//TODO:
++      else
++      {
++              val = (blue >> 12 ) & BLUE;
++              val |= (green >> 8) & GREEN;
++              val |= (red >> 4) & RED;
++      }
++
++        if (regno < 16) {
++              current_par.cfb16[regno] =
++                      regno | regno << 5 | regno << 10;
++}
++
++      WRITEREG(DBMX1_LCD_MAPRAM+regno, val);
++      FUNC_END;
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_cmap()
++ *
++ * Input:     cmap    : Ouput data pointer
++ *            kspc    : Kernel space flag
++ *            con     : Console ID
++ *            info    : Frame buffer information
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Description: Data is copied from hardware or local or system DISPAY,
++ *            and copied to cmap.
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
++                               struct fb_info *info)
++{
++        int err = 0;
++
++      FUNC_START;
++        DPRINTK("current_par.visual=%d\n", current_par.visual);
++        if (con == current_par.currcon)
++              err = fb_get_cmap(cmap, kspc, dbmx1fb_getcolreg, info);
++        else if (fb_display[con].cmap.len)
++              fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
++        else
++              fb_copy_cmap(fb_default_cmap(current_par.palette_size),
++                             cmap, kspc ? 0 : 2);
++        FUNC_END;
++      return err;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_cmap()
++ *
++ * Input:     cmap    : Ouput data pointer
++ *            kspc    : Kernel space flag
++ *            con     : Console ID
++ *            info    : Frame buffer information
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Description: Copy data from cmap and copy to DISPLAY. If DISPLAy has no cmap,
++ *            allocate memory for it. If DISPLAY is current console and visible,
++ *            then hardware color map shall be set.
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++{
++        int err = 0;
++
++      FUNC_START;
++        DPRINTK("current_par.visual=%d\n", current_par.visual);
++        if (!fb_display[con].cmap.len)
++                err = fb_alloc_cmap(&fb_display[con].cmap,
++                                    current_par.palette_size, 0);
++
++        if (!err) {
++                if (con == current_par.currcon)
++                        err = fb_set_cmap(cmap, kspc, dbmx1fb_setcolreg,
++                                          info);
++                fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
++        }
++      FUNC_END;
++        return err;
++}
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_var()
++ *
++ * Input:     var     : Iuput data pointer
++ *            con     : Console ID
++ *            info    : Frame buffer information
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Functions Called:  _encode_var()
++ *
++ * Description: Get color map from current, or global display[console]
++ *            used by ioctl
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++        if (con == -1) {
++              _encode_var(var, &current_par);
++        } else
++              *var = fb_display[con].var;
++        return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_updatevar()
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Fill in display switch with LCD information,
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int dbmx1fb_updatevar(int con, struct fb_info *info)
++{
++        DPRINTK("entered\n");
++        return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_dispsw()
++ *
++ * Input:     display         : Iuput data pointer
++ *            dbmx1fb_info    : Frame buffer of LCD information
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Fill in display switch with LCD information,
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void dbmx1fb_set_dispsw(struct display *disp
++#ifdef HARDWARE_CURSOR
++              ,struct dbmx1fb_info *info
++#endif
++              )
++{
++      FUNC_START;
++      switch (disp->var.bits_per_pixel) {
++#ifdef HARDWARE_CURSOR
++#ifdef FBCON_HAS_CFB4
++              case 4:
++                    fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++                  info->dispsw = dbmx1fb_cfb4;
++                  disp->dispsw = &info->dispsw;
++                  disp->dispsw_data = NULL;
++                  break;
++#endif
++#ifdef FBCON_HAS_CFB8
++              case 8:
++                    fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++                  info->dispsw = dbmx1fb_cfb8;
++                  disp->dispsw = &info->dispsw;
++                  disp->dispsw_data = NULL;
++                  break;
++#endif
++#ifdef FBCON_HAS_CFB16
++              case 12:
++              case 16:
++                    fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++                  info->dispsw = dbmx1fb_cfb16;
++                  disp->dispsw = &info->dispsw;
++                  disp->dispsw_data = current_par.cfb16;
++                  break;
++#endif
++#else //!HARDWARE_CURSOR
++                  /* first step disable the hardware cursor */
++#ifdef FBCON_HAS_CFB4
++              case 4:
++                    fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++                  disp->dispsw = &fbcon_cfb4;
++                  disp->dispsw_data = NULL;
++                  break;
++#endif
++#ifdef FBCON_HAS_CFB8
++              case 8:
++                    fb_info.fix.visual = FB_VISUAL_PSEUDOCOLOR;
++                  disp->dispsw = &fbcon_cfb8;
++                  disp->dispsw_data = NULL;
++                  break;
++#endif
++#ifdef FBCON_HAS_CFB16
++              case 12:
++              case 16:
++                    fb_info.fix.visual = FB_VISUAL_DIRECTCOLOR;
++                  disp->dispsw = &fbcon_cfb16;
++                  disp->dispsw_data = current_par.cfb16;
++                  break;
++#endif
++
++#endif // HARDWARE_CURSOR
++              default:
++                  disp->dispsw = &fbcon_dummy;
++                  disp->dispsw_data = NULL;
++      }
++#ifdef HARDWARE_CURSOR
++      if (&info->cursor)
++      {
++              info->dispsw.cursor = dbmx1fb_cursor;
++              info->dispsw.set_font = dbmx1fb_set_font;
++      }
++#endif // HARDWARE_CURSOR
++      FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_var()
++ *
++ * Input:     var     : Iuput data pointer
++ *            con     : Console ID
++ *            info    : Frame buffer information
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Functions Called:  dbmx1fb_decode_var()
++ *                    dbmx1fb_encode_var()
++ *                    dbmx1fb_set_dispsw()
++ *
++ * Description: set current_par by var, also set display data, specially the console
++ *            related fileops, then enable the lcd controller, and set cmap to
++ *            hardware.
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++{
++      struct display *display;
++      int err, chgvar = 0;
++      struct dbmx1fb_par par;
++
++      FUNC_START;
++      if (con >= 0)
++              display = &fb_display[con]; /* Display settings for console */
++        else
++                display = &global_disp;     /* Default display settings */
++
++        /* Decode var contents into a par structure, adjusting any */
++        /* out of range values. */
++        if ((err = _decode_var(var, &par))){
++              DPRINTK("decode var error!");
++                return err;
++      }
++
++      // Store adjusted par values into var structure
++      _encode_var(var, &par);
++
++      if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
++              return 0;
++
++      else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
++                      ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
++              return -EINVAL;
++
++      if (con >= 0) {
++              if ((display->var.xres != var->xres) ||
++                      (display->var.yres != var->yres) ||
++                      (display->var.xres_virtual != var->xres_virtual) ||
++                      (display->var.yres_virtual != var->yres_virtual) ||
++                      (display->var.sync != var->sync)                 ||
++                      (display->var.bits_per_pixel != var->bits_per_pixel) ||
++                      (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
++                      (memcmp(&display->var.green, &var->green, sizeof(var->green)
++                              )) ||
++                      (memcmp(&display->var.blue, &var->blue, sizeof(var->blue))))
++                      chgvar = 1;
++      }
++
++      display->var            = *var;
++      display->screen_base    = par.v_screen_start_address;
++      display->visual         = par.visual;
++      display->type           = FB_TYPE_PACKED_PIXELS;
++      display->type_aux       = 0;
++      display->ypanstep       = 0;
++      display->ywrapstep      = 0;
++      display->line_length    =
++      display->next_line      = (var->xres * 16) / 8;
++
++      display->can_soft_blank = 1;
++      display->inverse        = 0;
++
++      dbmx1fb_set_dispsw(display
++#ifdef HARDWARE_CURSOR
++                      , (struct dbmx1fb_info *)info
++#endif // HARDWARE_CURSOR
++                      );
++
++      /* If the console has changed and the console has defined */
++      /* a changevar function, call that function. */
++      if (chgvar && info && info->changevar)
++              info->changevar(con);   // TODO:
++
++      /* If the current console is selected and it's not truecolor,
++      *  update the palette
++      */
++      if ((con == current_par.currcon) &&
++                      (current_par.visual != FB_VISUAL_TRUECOLOR)) {
++              struct fb_cmap *cmap;
++
++              current_par = par;      // TODO ?
++              if (display->cmap.len)
++                      cmap = &display->cmap;
++              else
++                      cmap = fb_default_cmap(current_par.palette_size);
++
++              fb_set_cmap(cmap, 1, dbmx1fb_setcolreg, info);
++      }
++
++      /* If the current console is selected, activate the new var. */
++      if (con == current_par.currcon){
++              init_var = *var;        // TODO:gcc support structure copy?
++              _enable_lcd_controller();
++      }
++
++      FUNC_END;
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_get_fix()
++ *
++ * Input:     fix     : Ouput data pointer
++ *            con     : Console ID
++ *            info    : Frame buffer information
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Functions Called: VOID
++ *
++ * Description: get fix from display data, current_par data
++ *            used by ioctl
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int
++dbmx1fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++{
++        struct display *display;
++
++      FUNC_START;
++        memset(fix, 0, sizeof(struct fb_fix_screeninfo));
++        strcpy(fix->id, DBMX1_NAME);
++
++        if (con >= 0)
++        {
++                DPRINTK("Using console specific display for con=%d\n",con);
++                display = &fb_display[con];  /* Display settings for console */
++        }
++        else
++                display = &global_disp;      /* Default display settings */
++
++        fix->smem_start  = (unsigned long)current_par.screen_start_address;
++        fix->smem_len    = current_par.screen_memory_size;
++//printk("dbmx1fb_get_fix, pointer fix: 0x%08x, smem_len: 0x%08x\n",fix,fix->smem_len);
++        fix->type        = display->type;
++        fix->type_aux    = display->type_aux;
++        fix->xpanstep    = 0;
++        fix->ypanstep    = display->ypanstep;
++        fix->ywrapstep   = display->ywrapstep;
++        fix->visual      = display->visual;
++        fix->line_length = display->line_length;
++        fix->accel       = FB_ACCEL_NONE;
++
++      FUNC_END;
++        return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_inter_handler()
++ *
++ * Input:
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Interrupt handler
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void dbmx1fb_inter_handler(int irq, void *dev_id, struct pt_regs *regs)
++{
++      unsigned int intsr;
++      FUNC_START;
++      intsr = READREG(DBMX1_LCD_INTSR);               // read to clear status
++      printk(KERN_ERR"lcd interrupt!\n");
++      FUNC_END;
++      // handle
++}
++
++#ifdef LCD_PM
++#define PM_OPT " [pm]"
++#define LCD_PMST_RESUME 0
++#define LCD_PMST_SUSPEND 1
++static unsigned int lcd_pm_status = LCD_PMST_RESUME;
++
++void lcd_pm_resume(void)
++{
++      if(lcd_pm_status == LCD_PMST_RESUME)
++              return;
++      WRITEREG(0x21c21c, 0x10000);    // light on
++      WRITEREG(DBMX1_LCD_REFMCR, 0xf000002);
++      WRITEREG(DBMX1_LCD_PWMR, 0x00a9008a);
++      lcd_pm_status = LCD_PMST_RESUME;
++//    printk(KERN_ERR"lcd resumed\n");
++}
++
++void lcd_pm_suspend(void)
++{
++      unsigned val;
++      if(lcd_pm_status == LCD_PMST_SUSPEND)
++              return;
++      val = READREG(0x20502c);
++      val |= 0x8000;
++      WRITEREG(0x20502c, val);
++      //To produce enough dealy time before trun off the LCDC.
++      for(val=0;val<=600000;val++);
++      val = READREG(0x21c21c);
++      val &= ~0x10000;
++      WRITEREG(0x21c21c, val);        // light off
++      WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++      lcd_pm_status = LCD_PMST_SUSPEND;
++//    printk(KERN_ERR"lcd suspended\n");
++}
++
++int lcd_pm_handler(struct pm_dev *dev, pm_request_t rqst, void *data)
++{
++      switch(rqst){
++              case PM_RESUME:
++                      lcd_pm_resume();
++                      break;
++              case PM_SUSPEND:
++                      lcd_pm_suspend();
++                      break;
++              default:
++                      break;
++              }
++      return 0;
++}
++#endif // LCD_PM
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_init()
++ *
++ * Input: VOID
++ *
++ * Value Returned: int                : Return status.If no error, return 0.
++ *
++ * Functions Called:  _init_fbinfo()
++ *                    disable_irq()
++ *                    enable_irq()
++ *                    _init_lcd()
++ *                    dbmx1fb_init_cursor()
++ *
++ * Description: initialization module, all of init routine's entry point
++ *            initialize fb_info, init_var, current_par
++ *            and setup interrupt, memory, lcd controller
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++int __init dbmx1fb_init(void)
++{
++      int ret;
++#ifdef HARDWARE_CURSOR
++      struct dbmx1fb_info *info;
++#endif // HARDWARE_CURSOR
++
++      _init_lcd_system();
++
++      _init_fbinfo();
++
++      if ((ret = _reserve_fb_memory()) != 0){
++              printk(KERN_ERR"failed for reserved DBMX frame buffer memory\n");
++              return ret;
++      }
++
++#if 0
++      if (request_irq(IRQ_LCD,
++                              dbmx1fb_inter_handler,
++                              SA_INTERRUPT,
++                              DEV_NAME,
++                              NULL) != 0) {
++              printk(KERN_ERR "dbmx1fb: failed in request_irq\n");
++              return -EBUSY;
++      }
++
++      disable_irq(IRQ_LCD);
++#endif
++        if (dbmx1fb_set_var(&init_var, -1, &fb_info))
++              ; //current_par.allow_modeset = 0;
++
++      _init_lcd();
++      _enable_lcd_controller();
++
++#ifdef HARDWARE_CURSOR
++      info = kmalloc(sizeof(struct dbmx1fb_info), GFP_KERNEL);
++      if(info == NULL){
++              printk(KERN_ERR"can not kmalloc dbmx1fb_info memory\n");
++              return -1;
++      }
++
++      memset(info,0,sizeof(struct dbmx1fb_info));
++
++      info->cursor.blink_rate = DEFAULT_CURSOR_BLINK_RATE;
++      info->cursor.blinkenable = 0;
++      info->cursor.state = LCD_CURSOR_OFF;
++      WRITEREG(DBMX1_LCD_LCXYP,0x90010001);
++      WRITEREG(DBMX1_LCD_CURBLKCR,0x1F1F0000);
++      WRITEREG(DBMX1_LCD_LCHCC,0x0000F800);
++
++      DPRINTK(KERN_ERR"LCXYP = %x\n",READREG(DBMX1_LCD_LCXYP));
++      DPRINTK(KERN_ERR"CURBLICR = %x\n",READREG(DBMX1_LCD_CURBLKCR));
++      DPRINTK(KERN_ERR"LCHCC = %x\n",READREG(DBMX1_LCD_LCHCC));
++
++      //dbmx1fb_set_cursor(info);
++      //info->cursor = dbmx1fb_init_cursor(info);
++#endif // HARDWARE_CURSOR
++
++      register_framebuffer(&fb_info);
++
++#ifdef LCD_PM
++      pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, lcd_pm_handler);
++      printk("register LCD power management successfully.\n");
++#endif
++#if 0
++      enable_irq(IRQ_LCD);    // TODO:
++#endif
++      /* This driver cannot be unloaded at the moment */
++      MOD_INC_USE_COUNT;
++
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_setup()
++ *
++ * Input: info        : VOID
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Functions Called: VOID
++ *
++ * Description: basically, this routine used to parse command line parameters, which
++ *            is initialization parameters for lcd controller, such as freq, xres,
++ *            yres, and so on
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++int __init dbmx1fb_setup(char *options)
++{
++      FUNC_START;
++      FUNC_END;
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _init_fbinfo()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: while 16bpp is used to store a 12 bits pixels packet, but
++ *            it is not a really 16bpp system. maybe in-compatiable with
++ *            other system or GUI.There are some field in var which specify
++ *            the red/green/blue offset in a 16bit word, just little endian is
++ *            concerned
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void __init _init_fbinfo(void)
++{
++// Thomas Wong add this for debugging
++//    *((unsigned char *)0xF5000008) = '&';
++
++      FUNC_START;
++        strcpy(fb_info.modename, DBMX1_NAME);
++        strcpy(fb_info.fontname, "Acorn8x8");
++
++        fb_info.node            = -1;
++        fb_info.flags           = FBINFO_FLAG_DEFAULT;        // Low-level driver is not a module
++        fb_info.fbops           = &dbmx1fb_ops;
++        fb_info.monspecs        = monspecs;
++        fb_info.disp            = &global_disp;
++        fb_info.changevar       = NULL;
++        fb_info.switch_con      = dbmx1fb_switch;
++        fb_info.updatevar       = dbmx1fb_updatevar;
++        fb_info.blank           = dbmx1fb_blank;
++
++/*
++ * * setup initial parameters
++ * */
++        memset(&init_var, 0, sizeof(init_var));
++
++        init_var.transp.length  = 0;
++        init_var.nonstd         = 0;
++        init_var.activate       = FB_ACTIVATE_NOW;
++        init_var.xoffset        = 0;
++        init_var.yoffset        = 0;
++        init_var.height         = -1;
++        init_var.width          = -1;
++        init_var.vmode          = FB_VMODE_NONINTERLACED;
++
++        if (1) {
++                current_par.max_xres    = LCD_MAXX;
++                current_par.max_yres    = LCD_MAXY;
++                current_par.max_bpp     = LCD_MAX_BPP;                // 12
++                init_var.red.length     = 5;  // 5;
++                init_var.green.length   = 6;  // 6;
++                init_var.blue.length    = 5;  // 5;
++#ifdef __LITTLE_ENDIAN
++                init_var.red.offset    = 11;
++                init_var.green.offset  = 5;
++                init_var.blue.offset   = 0;
++#endif //__LITTLE_ENDIAN
++              init_var.grayscale      = 16;   // i suppose, TODO
++                init_var.sync           = 0;
++                init_var.pixclock       = 171521;     // TODO
++        }
++
++        current_par.screen_start_address       = NULL;
++        current_par.v_screen_start_address       = NULL;
++        current_par.screen_memory_size         = MAX_PIXEL_MEM_SIZE;
++// Thomas Wong add this for debugging
++//printk("_init_fbinfo, pointer to current_par: 0x%08x, screen_memory_size: 0x%08x\n", &current_par,current_par.screen_memory_size);
++        current_par.currcon             = -1; // TODO
++
++        init_var.xres                   = current_par.max_xres;
++        init_var.yres                   = current_par.max_yres;
++        init_var.xres_virtual           = init_var.xres;
++        init_var.yres_virtual           = init_var.yres;
++        init_var.bits_per_pixel         = current_par.max_bpp;
++
++      FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_blank()
++ *
++ * Input:     blank   : Blank flag
++ *            info    : Frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:  _enable_lcd_controller()
++ *                    _disable_lcd_controller()
++ *
++ * Description: blank the screen, if blank, disable lcd controller, while if no blank
++ *            set cmap and enable lcd controller
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void
++dbmx1fb_blank(int blank, struct fb_info *info)
++{
++#ifdef SUP_TTY0
++        int i;
++
++      FUNC_START;
++        DPRINTK("blank=%d info->modename=%s\n", blank, info->modename);
++        if (blank) {
++                if (current_par.visual != FB_VISUAL_TRUECOLOR)
++                    for (i = 0; i < current_par.palette_size; i++)
++                            ; // TODO
++//printk("Disable LCD\n");
++                                       _disable_lcd_controller();
++        }
++        else {
++                if (current_par.visual != FB_VISUAL_TRUECOLOR)
++                      dbmx1fb_set_cmap(&fb_display[current_par.currcon].cmap,
++                                      1,
++                                      current_par.currcon, info);
++//printk("Enable LCD\n");
++                _enable_lcd_controller();
++        }
++      FUNC_END;
++#endif //SUP_TTY0
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_switch()
++ *
++ * Input:     info    : Frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: Switch to another console
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int dbmx1fb_switch(int con, struct fb_info *info)
++{
++      FUNC_START;
++      if (current_par.visual != FB_VISUAL_TRUECOLOR) {
++              struct fb_cmap *cmap;
++              if (current_par.currcon >= 0) {
++                      // Get the colormap for the selected console
++                      cmap = &fb_display[current_par.currcon].cmap;
++
++              if (cmap->len)
++                      fb_get_cmap(cmap, 1, dbmx1fb_getcolreg, info);
++              }
++      }
++
++      current_par.currcon = con;
++      fb_display[con].var.activate = FB_ACTIVATE_NOW;
++      dbmx1fb_set_var(&fb_display[con].var, con, info);
++      FUNC_END;
++      return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _encode_par()
++ *
++ * Input:     var     : Input var data
++ *            par     : LCD controller parameters
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: use current_par to set a var structure
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int _encode_var(struct fb_var_screeninfo *var,
++              struct dbmx1fb_par *par)
++{
++        // Don't know if really want to zero var on entry.
++      // Look at set_var to see.  If so, may need to add extra params to par
++
++      //      memset(var, 0, sizeof(struct fb_var_screeninfo));
++
++      var->xres = par->xres;
++      var->yres = par->yres;
++      var->xres_virtual = par->xres_virtual;
++      var->yres_virtual = par->yres_virtual;
++
++      var->bits_per_pixel = par->bits_per_pixel;
++
++      DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
++      switch(var->bits_per_pixel) {
++      case 2:
++      case 4:
++      case 8:
++              var->red.length    = 4;
++              var->green         = var->red;
++              var->blue          = var->red;
++              var->transp.length = 0;
++              break;
++      case 12:          // This case should differ for Active/Passive mode
++      case 16:
++              if (1) {
++                      var->red.length    = 4;
++                      var->blue.length   = 4;
++                      var->green.length  = 4;
++                      var->transp.length = 0;
++#ifdef __LITTLE_ENDIAN
++                      var->red.offset    = 8;
++                      var->green.offset  = 4;
++                      var->blue.offset   = 0;
++                      var->transp.offset = 0;
++#endif // __LITTLE_ENDIAN
++              }
++              else
++              {
++                      var->red.length    = 5;
++                      var->blue.length   = 5;
++                      var->green.length  = 6;
++                      var->transp.length = 0;
++                      var->red.offset    = 11;
++                      var->green.offset  = 5;
++                      var->blue.offset   = 0;
++                      var->transp.offset = 0;
++              }
++              break;
++        }
++
++        return 0;
++}
++
++/*****************************************************************************
++ * Function Name: _decode_var
++ *
++ * Input:     var     : Input var data
++ *            par     : LCD controller parameters
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Get the video params out of 'var'. If a value doesn't fit,
++ *            round it up,if it's too big, return -EINVAL.
++ *
++ * Cautions: Round up in the following order: bits_per_pixel, xres,
++ *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
++ *    bitfields, horizontal timing, vertical timing.
++ *
++ * Modification History:
++ *    10 DEC,2001, Chen Ning
++******************************************************************************/
++static int _decode_var(struct fb_var_screeninfo *var,
++                    struct dbmx1fb_par *par)
++{
++      *par = current_par;
++
++      if ((par->xres = var->xres) < MIN_XRES)
++              par->xres = MIN_XRES;
++      if ((par->yres = var->yres) < MIN_YRES)
++              par->yres = MIN_YRES;
++      if (par->xres > current_par.max_xres)
++              par->xres = current_par.max_xres;
++      if (par->yres > current_par.max_yres)
++              par->yres = current_par.max_yres;
++      par->xres_virtual =
++              var->xres_virtual < par->xres ? par->xres : var->xres_virtual;
++        par->yres_virtual =
++              var->yres_virtual < par->yres ? par->yres : var->yres_virtual;
++        par->bits_per_pixel = var->bits_per_pixel;
++
++      switch (par->bits_per_pixel) {
++#ifdef FBCON_HAS_CFB4
++        case 4:
++              par->visual = FB_VISUAL_PSEUDOCOLOR;
++              par->palette_size = 16;
++                break;
++#endif
++#ifdef FBCON_HAS_CFB8
++      case 8:
++              par->visual = FB_VISUAL_PSEUDOCOLOR;
++              par->palette_size = 256;
++              break;
++#endif
++#ifdef FBCON_HAS_CFB16
++      case 12:        // RGB 444
++      case 16:        /* RGB 565 */
++              par->visual = FB_VISUAL_TRUECOLOR;
++              par->palette_size = 0;
++              break;
++#endif
++      default:
++              return -EINVAL;
++      }
++
++      par->screen_start_address  =(u_char*)(
++                      (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++      par->v_screen_start_address =(u_char*)(
++                      (u_long)v_framebuffer_memory_address+PAGE_SIZE);
++
++// Thomas Wong - try to change start address here (map to SRAM, instead of SDRAM)
++#ifndef FULL_SCREEN
++      par->screen_start_address  =(u_char*)(0x00300000);
++      par->v_screen_start_address =(u_char*)(0xF0300000);
++#endif
++
++//    par->screen_start_address  =(u_char*)(0x0BE00000);
++//    par->v_screen_start_address =(u_char*)(0xFBE00000);
++
++//    par->screen_start_address  =(u_char*)(0x12000000);
++//    par->v_screen_start_address =(u_char*)(0xF2000000);
++
++      return 0;
++}
++
++
++/*****************************************************************************
++ * Function Name: _reserve_fb_memory()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: get data out of var structure and set related LCD controller registers
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int __init _reserve_fb_memory(void)
++{
++        u_int  required_pages;
++        u_int  extra_pages;
++        u_int  order;
++        struct page *page;
++        char   *allocated_region;
++
++      DPRINTK("frame buffer memory size = %x\n", (unsigned int)ALLOCATED_FB_MEM_SIZE);
++        if (v_framebuffer_memory_address != NULL)
++                return -EINVAL;
++
++        /* Find order required to allocate enough memory for framebuffer */
++        required_pages = ALLOCATED_FB_MEM_SIZE >> PAGE_SHIFT;
++        for (order = 0 ; required_pages >> order ; order++) {;}
++        extra_pages = (1 << order) - required_pages;
++
++        if ((allocated_region =
++              (char *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)) == NULL){
++
++              DPRINTK("can not allocated memory\n");
++              return -ENOMEM;
++      }
++
++
++        v_framebuffer_memory_address = (u_char *)allocated_region +
++              (extra_pages << PAGE_SHIFT);
++        p_framebuffer_memory_address = (u_char *)__virt_to_phys(
++                      (u_long)v_framebuffer_memory_address);
++#if 0
++      printk(KERN_ERR"Frame buffer __get_free_pages vd:= %x, pd= %x",
++                      (unsigned int)v_framebuffer_memory_address,
++                      (unsigned int)p_framebuffer_memory_address);
++#endif
++        /* Free all pages that we don't need but were given to us because */
++        /* __get_free_pages() works on powers of 2. */
++        for (;extra_pages;extra_pages--)
++          free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
++
++      /* Set reserved flag for fb memory to allow it to be remapped into */
++        /* user space by the common fbmem driver using remap_page_range(). */
++        for(page = virt_to_page(v_framebuffer_memory_address);
++            page < virt_to_page(v_framebuffer_memory_address
++                  + ALLOCATED_FB_MEM_SIZE);
++          page++)
++              mem_map_reserve(page);
++#if 0
++        /* Remap the fb memory to a non-buffered, non-cached region */
++        v_framebuffer_memory_address = (u_char *)__ioremap(
++                      (u_long)p_framebuffer_memory_address,
++                      ALLOCATED_FB_MEM_SIZE,
++                      L_PTE_PRESENT   |
++                      L_PTE_YOUNG     |
++                      L_PTE_DIRTY     |
++                      L_PTE_WRITE);
++#endif
++      current_par.screen_start_address  =(u_char*)(
++                      (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++      current_par.v_screen_start_address =(u_char*)(
++                      (u_long)v_framebuffer_memory_address+PAGE_SIZE);
++
++      DPRINTK("physical screen start addres: %x\n",
++                      (u_long)p_framebuffer_memory_address+PAGE_SIZE);
++
++#ifndef FULL_SCREEN
++// Thomas Wong - we'll try to change the screen start address here
++//    printk("\n\rMap LCD screen to SDRAM.\n\r");
++
++      printk("\n\rMap LCD screen to embedded SRAM.\n\r");
++      current_par.screen_start_address  =(u_char*)(0x00300000);
++      current_par.v_screen_start_address =(u_char*)(0xF0300000);
++#endif
++
++//    printk("\n\rMap LCD screen to SDRAM 0xFBE00000.\n\r");
++//    current_par.screen_start_address  =(u_char*)(0x0BE00000);
++//    current_par.v_screen_start_address =(u_char*)(0xFBE00000);
++
++//    printk("\n\rMap LCD screen to SRAM 0x12000000.\n\r");
++//    current_par.screen_start_address  =(u_char*)(0x12000000);
++//    current_par.v_screen_start_address =(u_char*)(0xF2000000);
++
++        return (v_framebuffer_memory_address == NULL ? -EINVAL : 0);
++}
++
++/*****************************************************************************
++ * Function Name: _enable_lcd_controller()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: enable Lcd controller, setup registers,
++ *            base on current_par value
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _enable_lcd_controller(void)
++{
++      unsigned int val;
++#if 0
++      int i;
++      val =0;
++
++      FUNC_START;
++      _decode_var(&init_var, &current_par);
++
++      val = current_par.xres/16;
++      val <<= 20;
++      val += current_par.yres;
++      WRITEREG(DBMX1_LCD_XYMAX, val);
++
++      val=0;
++      val=current_par.xres_virtual /2;
++      WRITEREG(DBMX1_LCD_VPW, val);
++
++      switch(current_par.bits_per_pixel ){
++      case 4: // for gray only
++              for(i=0; i<16; i++){
++                      WRITEREG(DBMX1_LCD_MAPRAM+i, gray4map[i]);
++              }
++              WRITEREG(DBMX1_LCD_PANELCFG, PANELCFG_VAL_4);
++              WRITEREG(DBMX1_LCD_VCFG, VCFG_VAL_4);
++              WRITEREG(DBMX1_LCD_HCFG, HCFG_VAL_4);
++              WRITEREG(DBMX1_LCD_INTCR, INTCR_VAL_4); // no interrupt
++              WRITEREG(DBMX1_LCD_REFMCR, REFMCR_VAL_4);
++              WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_4);
++              WRITEREG(DBMX1_LCD_PWMR, PWMR_VAL);
++              break;
++      case 12:
++      case 16:
++              WRITEREG(DBMX1_LCD_PANELCFG, PANELCFG_VAL_12);
++              WRITEREG(DBMX1_LCD_HCFG, HCFG_VAL_12);
++              WRITEREG(DBMX1_LCD_VCFG, VCFG_VAL_12);
++              WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++              WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_12);
++              WRITEREG(DBMX1_LCD_PWMR, 0x00008200);
++              WRITEREG(DBMX1_LCD_REFMCR, 0x0f000002);
++              WRITEREG(DBMX1_LCD_PWMR, 0x0000008a);
++
++              break;
++      }
++#else
++      val = READREG(0x21c21c);
++      val |= 0x0010000;
++      WRITEREG(0x21c21c, val);
++
++      WRITEREG(DBMX1_LCD_PWMR, 0x8200);
++      WRITEREG(DBMX1_LCD_REFMCR, 0xf000002);
++
++      // Thomas Wong - we want 0x8A not 0x200
++//    WRITEREG(DBMX1_LCD_PWMR, 0x200);
++// PLAM -- for rev2 (endian bit)
++//    WRITEREG(DBMX1_LCD_PWMR, 0x0000008A);
++      WRITEREG(DBMX1_LCD_PWMR, 0x00A9008A);
++// end PLAM
++#endif
++
++      FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: _disable_lcd_controller()
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: just disable the LCD controller
++ *            disable lcd interrupt. others, i have no ideas
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _disable_lcd_controller(void)
++{
++      unsigned int val;
++      val = READREG(0x21c21c);
++      val &= ~0x0010000;
++      WRITEREG(0x21c21c, val);
++
++      WRITEREG(DBMX1_LCD_PWMR, 0x8200);
++//    WRITEREG(DBMX1_LCD_REFMCR, DISABLELCD_VAL);
++      WRITEREG(0x205034, 0x0);
++}
++
++/*****************************************************************************
++ * Function Name: _install_cmap
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: set color map to hardware
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void _install_cmap(int con, struct fb_info *info)
++{
++        if (con != current_par.currcon)
++                return ;
++        if (fb_display[con].cmap.len)
++                fb_set_cmap(&fb_display[con].cmap, 1, dbmx1fb_setcolreg, info);
++        else
++                fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
++                              1,
++                              dbmx1fb_setcolreg,
++                              info);
++      return ;
++}
++
++/*****************************************************************************
++ * Function Name: _init_lcd_system
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called:
++ *
++ * Description: initialize the gpio port C and port D with DMA enable
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static void __init _init_lcd_system(void)
++{
++unsigned int val;
++
++/* Thomas Wong - we don't need DMA here
++      // dma reset & enable
++      val = READREG(0x209000);
++      val |= 0x02;
++      WRITEREG(0x209000, val);
++      val = READREG(0x209000);
++      val |= 0x01;
++      WRITEREG(0x209000, val);
++*/
++      // gpio
++      //clear port D for LCD signal
++      WRITEREG(0x21c320, 0x0);
++      WRITEREG(0x21c338, 0x0);
++
++      val = READREG(0x21c220);
++      val |= 0x10000;
++      WRITEREG(0x21c220, val);
++      val = READREG(0x21c208);
++      val |= 0x03;
++      WRITEREG(0x21c208, val);
++      val = READREG(0x21c200);
++      val |= 0x10000;
++      WRITEREG(0x21c200, val);
++      val = READREG(0x21c238);
++      val |= 0x10000;
++      WRITEREG(0x21c238, val);
++      val = READREG(0x21c21c);
++      val |= 0x10000;
++      WRITEREG(0x21c21c, val);
++
++}
++
++static void set_pclk(unsigned int fmhz)
++{
++      unsigned int div= 96/fmhz;
++      unsigned int reg;
++      reg = READREG(0x21b020);
++      reg &= ~0xf0;
++      WRITEREG(0x21b020, reg);
++      reg |= ((div-1)<<4) &0xf0;
++      WRITEREG(0x21b020, reg);
++}
++
++/*****************************************************************************
++ * Function Name: _init_lcd
++ *
++ * Input: VOID
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: _decode_var()
++ *
++ * Description: initialize the LCD controller, use current_par for 12bpp
++ *
++ * Modification History:
++ *            10 DEC,2001, Chen Ning
++******************************************************************************/
++static int __init _init_lcd()
++{
++
++      unsigned int val, rate;
++      int i;
++      unsigned char * pscr;
++      //unsigned long pclk,temp,PCLKDIV2;
++      //unsigned long MFI,MFN,PD,MFD,tempReg,MCUPLLCLK;
++
++
++      _decode_var(&init_var, &current_par);
++
++      // gpio begin
++      val = READREG(0x21c21c);
++      val &= ~0x00010000;
++      WRITEREG(0x21c21c, val);
++      DPRINTK(KERN_ERR"DR=%x\n", READREG(0x21c21c));
++
++      // LCD regs
++      DPRINTK(KERN_ERR"write SSA by %x\n",
++                      (unsigned int)current_par.screen_start_address);
++      WRITEREG(DBMX1_LCD_SSA, (unsigned int)current_par.screen_start_address);
++      DPRINTK(KERN_ERR"SSA=%x\n", READREG(DBMX1_LCD_SSA));
++
++      val =0;
++      val = current_par.xres/16;
++      val = val<<20;
++      val += current_par.yres;
++      DPRINTK(KERN_ERR"par.x=%x, y=%x\n",
++                      current_par.xres,
++                      current_par.yres);
++      WRITEREG(DBMX1_LCD_XYMAX, val);
++      DPRINTK(KERN_ERR"XYMAX=%x\n", READREG(DBMX1_LCD_XYMAX));
++
++      val=0;
++      val=current_par.xres_virtual/2;
++      WRITEREG(DBMX1_LCD_VPW, val);
++      DPRINTK(KERN_ERR"VPW=%x\n", READREG(DBMX1_LCD_VPW));
++
++      set_pclk(4);
++
++      DPRINTK(KERN_ERR"DBMX1_LCD_PANELCFG=%x\n",
++                      READREG(DBMX1_LCD_PANELCFG));
++
++      WRITEREG(DBMX1_LCD_HCFG, 0x04000f06);
++      DPRINTK(KERN_ERR"DBMX1_LCD_HCFG=%x\n",
++                      READREG(DBMX1_LCD_HCFG));
++
++      WRITEREG(DBMX1_LCD_VCFG, 0x04000907);
++      DPRINTK(KERN_ERR"DBMX1_LCD_VCFG=%x\n",
++                      READREG(DBMX1_LCD_VCFG));
++
++      WRITEREG(DBMX1_LCD_REFMCR, 0x0);
++      DPRINTK(KERN_ERR"DBMX1_LCD_REFMCR=%x\n",
++                      READREG(DBMX1_LCD_REFMCR));
++
++      WRITEREG(DBMX1_LCD_DMACR, DMACR_VAL_12);
++      DPRINTK(KERN_ERR"DBMX1_LCD_DMACR=%x\n",
++                      READREG(DBMX1_LCD_DMACR));
++
++      WRITEREG(DBMX1_LCD_PWMR, 0x00008200);
++      DPRINTK(KERN_ERR"DBMX1_LCD_PWMR=%x\n",
++                      READREG(DBMX1_LCD_PWMR));
++
++      // Thomas Wong - we don't want to turn it on here
++      /*
++      WRITEREG(DBMX1_LCD_REFMCR, 0x0f000002)
++      DPRINTK(KERN_ERR"DBMX1_LCD_REFMCR=%x\n",
++                      READREG(DBMX1_LCD_REFMCR));
++      */
++
++      WRITEREG(DBMX1_LCD_PWMR, 0x0000008a);
++      DPRINTK(KERN_ERR"DBMX1_LCD_PWMR=%x\n",
++                      READREG(DBMX1_LCD_PWMR));
++
++      // Thomas Wong
++      WRITEREG(0x21B020, 0x000B005B);
++// PLAM -- for rev2 (new register and endian bits)
++      WRITEREG(DBMX1_LCD_PANELCFG, 0xF8008B42);       // little endian
++//    WRITEREG(DBMX1_LCD_PANELCFG, 0xF8048B42);       // big endian
++      WRITEREG(DBMX1_LCD_LGPMR, 0x00090300);
++//    WRITEREG(DBMX1_LCD_PWMR, 0x0000008A);
++      WRITEREG (DBMX1_LCD_PWMR, 0x009A008A);
++// end PLAM
++
++// PLAM -- for rev2 (new DMACR setting)
++//    WRITEREG(DBMX1_LCD_DMACR, 0x800C0002);
++      WRITEREG(DBMX1_LCD_DMACR, 0x00040008);
++//end PLAM
++
++
++#define DMACR_VAL_12  0x800C0002
++
++
++//    WRITEREG(DBMX1_LCD_INTCR, INTCR_VAL_12);
++//    DPRINTK(KERN_ERR"DBMX1_LCD_INTCR=%x\n",
++//                    READREG(DBMX1_LCD_INTCR));
++
++      // warm up LCD
++      for(i=0;i<10000000;i++) ;
++
++      // gpio end
++      val = READREG(0x21c21c);
++      val |= 0x00010000;
++      WRITEREG(0x21c21c, val);
++      DPRINTK(KERN_ERR"DR=%x\n",
++                      READREG(0x21c21c));
++#if 0
++      // clear screen
++      pscr = current_par.v_screen_start_address;
++      for(i=0; i< (current_par.screen_memory_size - 2*PAGE_SIZE); i++){
++              *pscr++ = 0xff;
++      }
++#endif
++      DPRINTK(KERN_ERR"_init_lcd end \n");
++
++      return 0;
++}
++
++#ifdef HARDWARE_CURSOR
++
++/*
++ * Hardware cursor support
++ */
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_color()
++ *
++ * Input:   fb        : frame buffer database
++ *        red : red component level in the cursor
++ *      green : green component level in the cursor
++ *       blue : blue component level in the cursor
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set color of hardware cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_color(struct dbmx1fb_info *fb, UINT8 *red, UINT8 *green, UINT8 *blue)
++{
++      struct dbmx1fb_cursor *c = &fb->cursor;
++      UINT32 color;
++
++      FUNC_START;
++      c->color[0] = *red;
++      c->color[1] = *green;
++      c->color[2] = *blue;
++      color = (UINT32)*red;
++      color |= (UINT32)(*green>>5);
++      color |= (UINT32)(*blue>>11);
++
++      WRITEREG(DBMX1_LCD_LCHCC, color);
++      FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor()
++ *
++ * Input:   fb        : frame buffer database
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Load information of hardware cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor(struct dbmx1fb_info *fb)
++{
++      struct dbmx1fb_cursor *c = &fb->cursor;
++      UINT32 temp,tempReg,x,y;
++
++      FUNC_START;
++      //DPRINTK(KERN_ERR"BLINK_RATE=%x\n",c->blink_rate);
++//    DPRINTK(KERN_ERR"width=%x\n",c->width);
++//    DPRINTK(KERN_ERR"height=%x\n",c->height);
++
++      x = c->startx << 16;
++      if (c->state == LCD_CURSOR_ON)
++      x |= CURSOR_ON_MASK;
++
++#ifdef FBCON_HAS_CFB4
++    else if(c->state == LCD_CURSOR_REVERSED)
++              x |= CURSOR_REVERSED_MASK;
++    else if(c->state == LCD_CURSOR_ON_WHITE)
++              x |= CURSOR_WHITE_MASK;
++#elif defined(FBCON_HAS_CFB8)||defined(FBCON_HAS_CFB16)
++    else if(c->state == LCD_CURSOR_INVERT_BGD)
++              x |= CURSOR_INVERT_MASK;
++    else if(c->state == LCD_CURSOR_AND_BGD)
++              x |= CURSOR_AND_BGD_MASK;
++    else if(c->state == LCD_CURSOR_OR_BGD)
++              x |= CURSOR_OR_BGD_MASK;
++    else if(c->state == LCD_CURSOR_XOR_BGD)
++              x |= CURSOR_XOR_BGD_MASK;
++#endif
++    else
++      x = c->startx;
++
++      y = c->starty;
++
++      temp = (UINT32)x | (UINT32)y;
++      WRITEREG(DBMX1_LCD_LCXYP, temp);
++      //DPRINTK(KERN_ERR"lcxyp=%x\n",READREG(DBMX1_LCD_LCXYP));
++
++      temp = (UINT32)((c->width<<8) | (c->height));
++      tempReg = (UINT32)((temp<<16) | c->blink_rate);
++
++      WRITEREG(DBMX1_LCD_CURBLKCR, tempReg);
++
++      //c->blink_rate = 10;
++      if (c->blinkenable)
++              dbmx1fb_set_cursor_blink(fb,c->blink_rate);
++      DPRINTK(KERN_ERR"CURBLKCR=%x\n",READREG(DBMX1_LCD_CURBLKCR));
++      FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_blink()
++ *
++ * Input:   fb  : frame buffer database
++ *     blink  : input blink frequency of cursor
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set blink frequency of hardware cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_blink(struct dbmx1fb_info *fb,int blink)
++{
++      struct dbmx1fb_cursor *c = &fb->cursor;
++
++      unsigned long   temp,tempReg;
++      unsigned long   PCD, XMAX, YMAX, PCLKDIV2;
++      unsigned long   tempMicroPeriod;
++
++      FUNC_START;
++      DPRINTK(KERN_ERR"dbmx1fb_set_cursor_blink\n");
++
++      if(!c){
++              DPRINTK(KERN_ERR"dangerouts, for c == null\n");
++      }
++      c->blink_rate = blink;
++
++      tempReg = READREG(DBMX1_LCD_XYMAX);
++      XMAX = (tempReg & XMAX_MASK) >> 20;
++      YMAX = tempReg & YMAX_MASK;
++      //XMAX = 240;
++      //YMAX = 320;
++      tempReg = READREG(PCDR);
++      PCLKDIV2 = (tempReg & PCLKDIV2_MASK) >> 4;
++      tempReg = READREG(DBMX1_LCD_PANELCFG);
++      PCD = tempReg & PCD_MASK;
++
++      temp = (PCLKDIV2 + 1);
++
++      if (!blink)
++      {
++              /* disable the blinking cursor function when frequency is 0 */
++              tempReg = READREG(DBMX1_LCD_CURBLKCR);
++              tempReg &= CURSORBLINK_DIS_MASK;
++              WRITEREG(DBMX1_LCD_CURBLKCR,tempReg);
++      }
++      else
++      {
++
++              tempMicroPeriod = temp * XMAX * YMAX * (PCD + 1);
++              temp = 96*10000000/(blink * tempMicroPeriod);
++              tempReg = READREG(DBMX1_LCD_CURBLKCR);
++              tempReg |= CURSORBLINK_EN_MASK;
++              tempReg |= temp;
++              WRITEREG(DBMX1_LCD_CURBLKCR,tempReg);
++              DPRINTK(KERN_ERR"Inter_CURBLKCR=%x\n",READREG(DBMX1_LCD_CURBLKCR));
++      }
++
++      FUNC_END;
++}
++
++ /*****************************************************************************
++ * Function Name: dbmx1fb_set_cursor_state()
++ *
++ * Input:   fb  : frame buffer database
++ *     state  : The status of the cursor to be set. e.g.
++ *                            LCD_CURSOR_OFF
++ *                      LCD_CURSOR_ON
++ *                      LCD_CURSOR_REVERSED
++ *                      LCD_CURSOR_ON_WHITE
++ *                      LCD_CURSOR_OR_BGD
++ *                      LCD_CURSOR_XOR_BGD
++ *                      LCD_CURSOR_AND_BGD
++ *
++ * Value Returned: VOID
++ *
++ * Functions Called: VOID
++ *
++ * Description: Set state of cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_set_cursor_state(struct dbmx1fb_info *fb,UINT32 state)
++{
++      struct dbmx1fb_cursor *c = &fb->cursor;
++      UINT32 temp;
++
++      FUNC_START;
++      c->state = state;
++      temp = READREG(DBMX1_LCD_LCXYP);
++      temp &= CURSOR_OFF_MASK;
++
++      if (state == LCD_CURSOR_OFF)
++              temp = temp;
++      else if (state == LCD_CURSOR_ON)
++              temp |= CURSOR_ON_MASK;
++#ifdef FBCON_HAS_CFB4
++      else if (state == LCD_CURSOR_REVERSED)
++              temp |= CURSOR_REVERSED_MASK;
++      else if (state == LCD_CURSOR_ON_WHITE)
++              temp |= CURSOR_WHITE_MASK;
++#elif defined(FBCON_HAS_CFB8)||defined(FBCON_HAS_CFB16)
++      else if(state == LCD_CURSOR_INVERT_BGD)
++              temp |= CURSOR_INVERT_MASK;
++      else if (state == LCD_CURSOR_OR_BGD)
++              temp |= CURSOR_OR_BGD_MASK;
++      else if (state == LCD_CURSOR_XOR_BGD)
++              temp |= CURSOR_XOR_BGD_MASK;
++      else if (state == LCD_CURSOR_AND_BGD)
++              temp |= CURSOR_AND_BGD_MASK;
++#endif
++      WRITEREG(DBMX1_LCD_LCXYP,temp);
++      DPRINTK(KERN_ERR"LCDXYP=%x\n",READREG(DBMX1_LCD_LCXYP));
++      FUNC_END;
++}
++
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_cursor()
++ *
++ * Input:   fb                : frame buffer database
++ *
++ * Value Returned:    cursor : The structure of hardware cursor
++ *
++ * Functions Called:  dbmx1fb_set_cursor()
++ *                    dbmx1fb_set_cursor_state()
++ *
++ * Description: The entry for display switch to operate hardware cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static void dbmx1fb_cursor(struct display *p, int mode, int x, int y)
++{
++      struct dbmx1fb_info *fb = (struct dbmx1fb_info *)p->fb_info;
++      struct dbmx1fb_cursor *c = &fb->cursor;
++
++      FUNC_START;
++      if (c==0) return;
++
++
++      x *= fontwidth(p);
++      y *= fontheight(p);
++
++      c->startx = x;
++      c->starty = y;
++
++      switch (mode) {
++      case CM_ERASE:
++              dbmx1fb_set_cursor_state(fb,LCD_CURSOR_OFF);
++              break;
++
++      case CM_DRAW:
++      case CM_MOVE:
++              c->state = LCD_CURSOR_ON;
++              dbmx1fb_set_cursor(fb);
++              dbmx1fb_set_cursor_state(fb, c->state);
++              break;
++      }
++      FUNC_END;
++}
++
++/*****************************************************************************
++ * Function Name: dbmx1fb_set_font()
++ *
++ * Input:   display   : console datebase
++ *        width       : The new width of cursor to be set.
++ *        height      : The new height of cursor position to be set
++ *
++ * Value Returned: int        : Return status.If no error, return 0.
++ *
++ * Functions Called: dbmx1fb_set_cursor()
++ *                 dbmx1fb_set_cursor_color()
++ *
++ * Description: Set  font for cursor
++ *
++ * Modification History:
++ *            10 DEC,2001, Zhang Juan
++******************************************************************************/
++static int dbmx1fb_set_font(struct display *d, int width, int height)
++{
++      struct dbmx1fb_info *fb=(struct dbmx1fb_info *)d->fb_info;
++      struct dbmx1fb_cursor *c = &fb->cursor;
++
++      FUNC_START;
++      if(!d){
++              printk(KERN_ERR"dbmx1fb_set_font d=null\n");
++              return -1;
++      }
++
++      if (c) {
++              if (!width || !height) {
++                      width = 16;
++                      height = 16;
++              }
++
++              c->width = width;
++              c->height = height;
++
++              DPRINTK(KERN_ERR"set cursor\n");
++              dbmx1fb_set_cursor(fb);
++              DPRINTK(KERN_ERR"set color cursor\n");
++              dbmx1fb_set_cursor_color(fb, cursor_color_map,
++                              cursor_color_map, cursor_color_map);
++      }else{
++              DPRINTK(KERN_ERR"set cursor failed, cursor == null\n");
++      }
++
++      FUNC_END;
++      return 1;
++}
++#endif // HARDWARE_CURSOR
++/* end of file */
++
+--- /dev/null
++++ linux-2.4.27/drivers/video/dbmx1fb.h
+@@ -0,0 +1,213 @@
++/****************************************************************************
++ *
++ * Copyright (C) 2001, Chen Ning All Rights Reserved
++ *
++ * File Name: dbmx1fb.h
++ *
++ * Progammers:        Chen Ning, Zhang Juan
++ *
++ * Date of Creations: 10 DEC,2001
++ *
++ * Synopsis:
++ *
++ * Descirption:
++ *
++ * Modification History:
++ * 10 DEC, 2001, initialization version, frame work for frame buffer driver
++ *
++*******************************************************************************/
++
++#ifndef LCDFB_H
++#define LCDFB_H
++#include "asm/arch/hardware.h"
++#include "asm/arch/platform.h"
++
++typedef signed char           BOOLEAN;
++typedef unsigned char         UINT8;                          /* Unsigned  8 bit quantity     */
++typedef signed char           SINT8;                          /* Signed    8 bit quantity             */
++typedef unsigned short        UINT16;                         /* Unsigned 16 bit quantity     */
++typedef signed short          SINT16;                         /* Signed   16 bit quantity             */
++typedef unsigned long         UINT32;                         /* Unsigned 32 bit quantity     */
++typedef signed long           SINT32;                         /* Signed   32 bit quantity             */
++
++typedef volatile BOOLEAN      VBOOLEAN;
++typedef volatile UINT8        VUINT8;                         /* Unsigned  8 bit quantity     */
++typedef volatile SINT8        VSINT8;                         /* Signed    8 bit quantity             */
++typedef volatile UINT16       VUINT16;                /* Unsigned 16 bit quantity     */
++typedef volatile SINT16       VSINT16;                /* Signed   16 bit quantity             */
++typedef volatile UINT32       VUINT32;                /* Unsigned 32 bit quantity     */
++typedef volatile SINT32       VSINT32;                /* Signed   32 bit quantity             */
++
++#define LCDBASE               0x00205000
++#define LCD_REGIONSIZE        0xc00
++
++#define DBMX1_LCD_SSA         0x00205000
++#define DBMX1_LCD_XYMAX               0x00205004
++#define DBMX1_LCD_VPW         0x00205008
++#define DBMX1_LCD_LCXYP               0x0020500c
++#define DBMX1_LCD_CURBLKCR    0x00205010
++#define DBMX1_LCD_LCHCC               0x00205014
++#define DBMX1_LCD_PANELCFG    0x00205018
++#define DBMX1_LCD_HCFG                0x0020501c
++#define DBMX1_LCD_VCFG                0x00205020
++#define DBMX1_LCD_POS         0x00205024
++#define DBMX1_LCD_LGPMR               0x00205028
++#define DBMX1_LCD_PWMR                0x0020502c
++#define DBMX1_LCD_DMACR               0x00205030
++#define DBMX1_LCD_REFMCR      0x00205034
++#define DBMX1_LCD_INTCR               0x00205038
++#define DBMX1_LCD_INTSR               0x00205040
++#define DBMX1_LCD_MAPRAM      0x00205800
++
++#define MPCTL0                        0x0021B004
++#define PCDR                  0X0021B020
++
++#define SSA           DBMX1_LCD_SSA
++#define XYMAX         DBMX1_LCD_XYMAX
++#define VPW           DBMX1_LCD_VPW
++#define LCXYP         DBMX1_LCD_LCXYP
++#define CURBLKCR      DBMX1_LCD_CURBLKCR
++#define LCHCC         DBMX1_LCD_LCHCC
++#define PANELCFG      DBMX1_LCD_PANELCFG
++#define VCFG          DBMX1_LCD_VCFG
++#define HCFG          DBMX1_LCD_HCFG
++#define POS           DBMX1_LCD_POS
++#define LGPMR         DBMX1_LCD_LGPMR
++#define PWMR          DBMX1_LCD_PWMR
++#define DMACR         DBMX1_LCD_DMACR
++#define REFMCR                DBMX1_LCD_REFMCR
++#define INTCR         DBMX1_LCD_INTCR
++#define INTSR         DBMX1_LCD_INTSR
++#define MAPRAM                DBMX1_LCD_MAPRAM
++// default value
++#define SHARP_TFT_240x320
++
++#ifdef SHARP_TFT_240x320
++#define PANELCFG_VAL_12       0xf8008b48
++#define HCFG_VAL_12   0x04000f06
++#define VCFG_VAL_12   0x04000907
++#define PWMR_VAL      0x0000008a
++#define LCD_MAXX      240
++#define LCD_MAXY      320
++#else //SHARP_TFT_240x320
++
++#define PANELCFG_VAL_12       0xf8088c6b
++#define HCFG_VAL_12   0x04000f06
++#define VCFG_VAL_12   0x04010c03
++#define PWMR_VAL      0x00000200
++#define LCD_MAXX      320
++#define LCD_MAXY      240
++#endif // SHARP_TFT_240x320
++
++#define PANELCFG_VAL_4        0x20008c09
++#define PANELCFG_VAL_4C       0x60008c09
++
++#define HCFG_VAL_4    0x04000f07
++
++#define VCFG_VAL_4    0x04010c03
++
++
++#define REFMCR_VAL_4  0x00000003
++#define REFMCR_VAL_12 0x00000003
++#define DISABLELCD_VAL        0x00000000
++
++#define DMACR_VAL_4   0x800c0003      // 12 & 3 TRIGGER
++#define DMACR_VAL_12  0x00020008
++
++#define INTCR_VAL_4   0x00000000
++#define INTCR_VAL_12  0x00000000
++
++#define INTSR_UDRERR  0x00000008
++#define INTSR_ERRRESP 0x00000004
++#define INTSR_EOF     0x00000002
++#define INTSR_BOF     0x00000001
++
++#define MIN_XRES        64
++#define MIN_YRES        64
++
++#define LCD_MAX_BPP   16
++
++#if 0
++#define READREG(r)    \
++      inl(IO_ADDRESS(r))
++
++#define WRITEREG(r, val) \
++      outl(val, IO_ADDRESS(r))
++#else
++#endif // 0
++
++#define MAX_PALETTE_NUM_ENTRIES         256
++#define MAX_PIXEL_MEM_SIZE \
++        ((current_par.max_xres * current_par.max_yres * current_par.max_bpp)/8)
++#define MAX_FRAMEBUFFER_MEM_SIZE \
++              (MAX_PIXEL_MEM_SIZE + 32)
++#define ALLOCATED_FB_MEM_SIZE \
++              (PAGE_ALIGN(MAX_FRAMEBUFFER_MEM_SIZE + PAGE_SIZE * 2))
++
++      // TODO:
++#define FBCON_HAS_CFB4
++#define FBCON_HAS_CFB8
++#define FBCON_HAS_CFB16
++
++#define IRQ_LCD                       12              // TODO: which irq?
++#define DBMX1_NAME            "DBMX1FB"
++#define DEV_NAME              DBMX1_NAME
++#define MAX_PALETTE_NUM_ENTRIES 256
++#define DEFAULT_CURSOR_BLINK_RATE (20)
++#define CURSOR_DRAW_DELAY  (2)
++/*cursor status*/
++#define LCD_CURSOR_OFF                    0
++#define LCD_CURSOR_ON               1
++
++#ifdef FBCON_HAS_CFB4
++      #define LCD_CURSOR_REVERSED     2
++      #define LCD_CURSOR_ON_WHITE     3
++#elif defined(FBCON_HAS_CFB8) || defined(FBCON_HAS_CFB16)
++      #define LCD_CURSOR_INVERT_BGD   2
++      #define LCD_CURSOR_AND_BGD      3
++      #define LCD_CURSOR_OR_BGD       4
++      #define LCD_CURSOR_XOR_BGD      5
++#endif //FBCON_HAS_CFB4
++
++/* MASK use for caculating MCDUPLLCLK */
++#define MFI_MASK              0x00003C00
++#define MFN_MASK              0x000003FF
++#define PD_MASK                       0x3C000000
++#define MFD_MASK              0x03FF0000
++#define PCLKDIV2_MASK         0x000000F0
++#define PCD_MASK              0x0000003F
++#define XMAX_MASK                   0xF3F00000
++#define YMAX_MASK                   0x000001FF
++#define HWAIT1_MASK                 0x0000FF00
++#define HWAIT2_MASK                 0x000000FF
++#define HWIDTH_MASK                 0xFC000000
++#define PASSDIV_MASK          0x00FF0000
++#define VWAIT1_MASK                 0x0000FF00
++#define VWAIT2_MASK                 0x000000FF
++#define VWIDTH_MASK                 0xFC000000
++#define CURSORBLINK_DIS_MASK  0x80000000
++
++#define DISPLAY_MODE_MASK             0x80000000
++
++#define MCU_FREQUENCE               32768
++#define COLOR_MASK    0x40000000
++
++/*  MASK use for indicating the cursor status  */
++#define CURSOR_ON_MASK              0x40000000
++#define CURSOR_OFF_MASK                  0x0FFFFFFF
++#define MAX_CURSOR_WIDTH            31
++#define MAX_CURSOR_HEIGHT           31
++#define CURSORBLINK_EN_MASK           0x80000000
++
++#ifdef FBCON_HAS_CFB4
++#define CURSOR_REVERSED_MASK        0x80000000
++#define CURSOR_WHITE_MASK           0xC0000000
++#else
++#define CURSOR_INVERT_MASK            0x80000000
++#define CURSOR_AND_BGD_MASK         0xC0000000
++#define CURSOR_OR_BGD_MASK          0x50000000
++#define CURSOR_XOR_BGD_MASK         0x90000000
++#endif // FBCON_HAS_CFB4
++
++#endif //LCDFB_H
++
+--- linux-2.4.27/drivers/video/fbmem.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/fbmem.c
+@@ -61,7 +61,9 @@
+ extern int pm2fb_setup(char*);
+ extern int pm3fb_init(void);
+ extern int pm3fb_setup(char*);
++extern int clps711xfb_init(void);
+ extern int cyber2000fb_init(void);
++extern int cyber2000fb_setup(char*);
+ extern int retz3fb_init(void);
+ extern int retz3fb_setup(char*);
+ extern int clgenfb_init(void);
+@@ -145,6 +147,8 @@
+ extern int sstfb_setup(char*);
+ extern int it8181fb_init(void);
+ extern int it8181fb_setup(char*);
++extern int anakinfb_init(void);
++extern int dbmx1fb_init(void);
+ static struct {
+       const char *name;
+@@ -170,11 +174,14 @@
+ #ifdef CONFIG_FB_AMIGA
+       { "amifb", amifb_init, amifb_setup },
+ #endif
++#ifdef CONFIG_FB_CLPS711X
++      { "clps711xfb", clps711xfb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_CYBER
+       { "cyber", cyberfb_init, cyberfb_setup },
+ #endif
+ #ifdef CONFIG_FB_CYBER2000
+-      { "cyber2000", cyber2000fb_init, NULL },
++      { "cyber2000", cyber2000fb_init, cyber2000fb_setup },
+ #endif
+ #ifdef CONFIG_FB_PM2
+       { "pm2fb", pm2fb_init, pm2fb_setup },
+@@ -226,10 +233,10 @@
+ #endif
+ #ifdef CONFIG_FB_S3TRIO
+       { "s3trio", s3triofb_init, NULL },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_FM2
+       { "fm2fb", fm2fb_init, fm2fb_setup },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_SIS
+       { "sisfb", sisfb_init, sisfb_setup },
+ #endif
+@@ -242,7 +249,7 @@
+       /*
+        * Generic drivers that are used as fallbacks
+-       * 
++       *
+        * These depend on resource management and must be initialized
+        * _after_ all other frame buffer devices that use resource
+        * management!
+@@ -253,7 +260,7 @@
+ #endif
+ #ifdef CONFIG_FB_VESA
+       { "vesa", vesafb_init, vesafb_setup },
+-#endif 
++#endif
+       /*
+        * Chipset specific drivers that don't use resource management (yet)
+@@ -276,7 +283,7 @@
+ #endif
+ #ifdef CONFIG_FB_HGA
+       { "hga", hgafb_init, hgafb_setup },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_IGA
+       { "igafb", igafb_init, igafb_setup },
+ #endif
+@@ -291,7 +298,7 @@
+ #endif
+ #ifdef CONFIG_FB_HP300
+       { "hpfb", hpfb_init, NULL },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_G364
+       { "g364", g364fb_init, NULL },
+ #endif
+@@ -307,6 +314,9 @@
+ #ifdef CONFIG_FB_TX3912
+       { "tx3912", tx3912fb_init, NULL },
+ #endif
++#ifdef CONFIG_FB_ANAKIN
++      { "anakinfb", anakinfb_init, NULL },
++#endif
+ #ifdef CONFIG_FB_E1355
+       { "e1355fb", e1355fb_init, e1355fb_setup },
+ #endif
+@@ -330,10 +340,13 @@
+ #endif
+ #ifdef CONFIG_FB_AU1100
+       { "au1100fb", au1100fb_init, au1100fb_setup },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_IT8181
+       { "it8181fb", it8181fb_init, it8181fb_setup },
+ #endif
++#ifdef CONFIG_FB_DBMX1
++      { "dbmx1fb", dbmx1fb_init, NULL },
++#endif
+       /*
+@@ -342,7 +355,7 @@
+ #ifdef CONFIG_FB_VGA16
+       { "vga16", vga16fb_init, vga16fb_setup },
+-#endif 
++#endif
+ #ifdef CONFIG_FB_STI
+       { "stifb", stifb_init, stifb_setup },
+ #endif
+@@ -371,7 +384,7 @@
+ struct fb_info *registered_fb[FB_MAX];
+ int num_registered_fb;
+-extern int fbcon_softback_size; 
++extern int fbcon_softback_size;
+ static int first_fb_vc;
+ static int last_fb_vc = MAX_NR_CONSOLES-1;
+@@ -482,7 +495,7 @@
+ }
+ #endif /* CONFIG_KMOD */
+-static int 
++static int
+ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+        unsigned long arg)
+ {
+@@ -494,7 +507,7 @@
+       struct fb_fix_screeninfo fix;
+       struct fb_con2fbmap con2fb;
+       int i;
+-      
++
+       if (! fb)
+               return -ENODEV;
+       switch (cmd) {
+@@ -578,7 +591,7 @@
+       }
+ }
+-static int 
++static int
+ fb_mmap(struct file *file, struct vm_area_struct * vma)
+ {
+       int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
+@@ -669,11 +682,11 @@
+ #elif defined(__sh__)
+       pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE;
+ #elif defined(__hppa__)
+-      pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; 
++      pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+ #elif defined(__ia64__)
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ #elif defined(__hppa__)
+-      pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; 
++      pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+ #else
+ #warning What do we have to do here??
+ #endif
+@@ -726,7 +739,7 @@
+       return res;
+ }
+-static int 
++static int
+ fb_release(struct inode *inode, struct file *file)
+ {
+       int fbidx = GET_FB_IDX(inode->i_rdev);
+@@ -858,7 +871,7 @@
+  *
+  */
+-void __init 
++void __init
+ fbmem_init(void)
+ {
+       int i;
+@@ -906,7 +919,7 @@
+     if (!options || !*options)
+           return 0;
+-          
++
+     if (!strncmp(options, "scrollback:", 11)) {
+           options += 11;
+           if (*options) {
+@@ -932,7 +945,7 @@
+                   }
+           return 0;
+     }
+-    
++
+     if (!strncmp(options, "vc:", 3)) {
+           options += 3;
+           if (*options)
+--- linux-2.4.27/drivers/video/font_acorn_8x8.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/font_acorn_8x8.c
+@@ -11,33 +11,33 @@
+ /* 03 */  0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^C */
+ /* 04 */  0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^D */
+ /* 05 */  0x00, 0x18, 0x3c, 0xe7, 0xe7, 0x3c, 0x18, 0x00, /* ^E */
+-/* 06 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 07 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 08 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 09 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0A */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0B */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0C */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0D */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0E */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 0F */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 06 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 07 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 08 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 09 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0A */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0B */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0C */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0D */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0E */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 0F */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* 10 */  0x00, 0x60, 0x78, 0x7e, 0x7e, 0x78, 0x60, 0x00, /* |> */
+ /* 11 */  0x00, 0x06, 0x1e, 0x7e, 0x7e, 0x1e, 0x06, 0x00, /* <| */
+-/* 12 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 13 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 14 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 15 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 16 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 17 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 18 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 19 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1A */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1B */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1C */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 1D */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 12 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 13 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 14 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 15 */  0x3c, 0x60, 0x3c, 0x66, 0x3c, 0x06, 0x3c, 0x00,
++/* 16 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 17 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 18 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 19 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1A */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1B */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1C */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 1D */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* 1E */  0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0x00, /* /\ */
+ /* 1F */  0x00, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00, /* \/ */
+-/* 20 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*   */
++/* 20 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*   */
+ /* 21 */  0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, /* ! */
+ /* 22 */  0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, /* " */
+ /* 23 */  0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, /* # */
+@@ -132,55 +132,55 @@
+ /* 7C */  0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, /* | */
+ /* 7D */  0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, /* } */
+ /* 7E */  0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, /* ~ */
+-/* 7F */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  /* \7f */
+-/* 80 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 81 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 82 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 83 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 84 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 85 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 86 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 87 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 88 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 89 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8A */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8B */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8C */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8D */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8E */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 8F */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 90 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 91 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 92 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 93 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 94 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 95 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 96 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 97 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 98 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 99 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9A */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9B */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9C */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9D */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9E */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* 9F */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A0 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A1 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A2 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A3 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A4 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A8 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* A9 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AA */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AB */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AC */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AD */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AE */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* AF */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* 7F */  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* \7f */
++/* 80 */  0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x30, 0x60,
++/* 81 */  0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 82 */  0x0c, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 83 */  0x18, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 84 */  0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 85 */  0x30, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 86 */  0x3c, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* 87 */  0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x60,
++/* 88 */  0x3c, 0x66, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 89 */  0x66, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 8A */  0x30, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
++/* 8B */  0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8C */  0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8D */  0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* 8E */  0x66, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00,
++/* 8F */  0x18, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00,
++/* 90 */  0x0c, 0x18, 0x7e, 0x60, 0x7c, 0x60, 0x7e, 0x00,
++/* 91 */  0x00, 0x00, 0x3f, 0x0d, 0x3f, 0x6c, 0x3f, 0x00,
++/* 92 */  0x3f, 0x66, 0x66, 0x7f, 0x66, 0x66, 0x67, 0x00,
++/* 93 */  0x3c, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 94 */  0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 95 */  0x30, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* 96 */  0x3c, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 97 */  0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* 98 */  0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c,
++/* 99 */  0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00,
++/* 9A */  0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
++/* 9B */  0x08, 0x3e, 0x6b, 0x68, 0x6b, 0x3e, 0x08, 0x00,
++/* 9C */  0x1c, 0x36, 0x30, 0x7c, 0x30, 0x30, 0x7e, 0x00,
++/* 9D */  0x66, 0x3c, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00,
++/* 9E */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* 9F */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* A0 */  0x0c, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00,
++/* A1 */  0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00,
++/* A2 */  0x0c, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00,
++/* A3 */  0x0c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00,
++/* A4 */  0x36, 0x6c, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x00,
++/* A5 */  0x36, 0x6c, 0x00, 0x66, 0x76, 0x6e, 0x66, 0x00,
++/* A6 */  0x1c, 0x06, 0x1e, 0x36, 0x1e, 0x00, 0x3e, 0x00,
++/* A7 */  0x1c, 0x36, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00,
++/* A8 */  0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3c, 0x00,
++/* A9 */  0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* AA */  0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* AB */  0x40, 0xc0, 0x40, 0x4f, 0x41, 0x0f, 0x08, 0x0f,
++/* AC */  0x40, 0xc0, 0x40, 0x48, 0x48, 0x0a, 0x0f, 0x02,
++/* AD */  0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00,
++/* AE */  0x00, 0x33, 0x66, 0xcc, 0xcc, 0x66, 0x33, 0x00,
++/* AF */  0x00, 0xcc, 0x66, 0x33, 0x33, 0x66, 0xcc, 0x00,
+ /* B0 */  0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ /* B1 */  0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ /* B2 */  0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+@@ -229,37 +229,37 @@
+ /* DD */  0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ /* DE */  0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ /* DF */  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+-/* E0 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E1 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E2 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E3 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E4 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E8 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* E9 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EA */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EB */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EC */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* ED */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EE */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* EF */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F0 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F1 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F2 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F3 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F4 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F5 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F6 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F7 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F8 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* F9 */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FA */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FB */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FC */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FD */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-/* FE */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* E0 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E1 */  0x3c, 0x66, 0x66, 0x6c, 0x66, 0x66, 0x6c, 0xc0,
++/* E2 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E3 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E4 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E5 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E6 */  0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x60,
++/* E7 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E8 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* E9 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EA */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EB */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EC */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* ED */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EE */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* EF */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F0 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F1 */  0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00,
++/* F2 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F3 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F4 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F5 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F6 */  0x00, 0x18, 0x00, 0xff, 0x00, 0x18, 0x00, 0x00,
++/* F7 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* F8 */  0x3c, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* F9 */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FA */  0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
++/* FB */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FC */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
++/* FD */  0x38, 0x04, 0x18, 0x20, 0x3c, 0x00, 0x00, 0x00,
++/* FE */  0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ /* FF */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+--- linux-2.4.27/drivers/video/pm3fb.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/pm3fb.c
+@@ -5,7 +5,6 @@
+  *  Based on code written by:
+  *           Sven Luther, <luther@dpt-info.u-strasbg.fr>
+  *           Alan Hourihane, <alanh@fairlite.demon.co.uk>
+- *           Russel King, <rmk@arm.linux.org.uk>
+  *  Based on linux/drivers/video/skeletonfb.c:
+  *    Copyright (C) 1997 Geert Uytterhoeven
+  *  Based on linux/driver/video/pm2fb.c:
+@@ -16,14 +15,9 @@
+  *  License. See the file COPYING in the main directory of this archive for
+  *  more details.
+  *
+- *  $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $
++ *  $Header: /home/pm3fb/pm3fb/pm3fb.c,v 1.139 2001/08/28 08:13:54 dolbeau Exp $
+  *
+  *  CHANGELOG:
+- *  Wed Nov 13 11:19:34 MET 2002, v 1.4.11C: option flatpanel: wasn't available in module, fixed.
+- *  Mon Feb 11 10:35:48 MET 2002, v 1.4.11B: Cosmetic update.
+- *  Wed Jan 23 14:16:59 MET 2002, v 1.4.11: Preliminary 2.5.x support, patch for 2.5.2.
+- *  Wed Nov 28 11:08:29 MET 2001, v 1.4.10: potential bug fix for SDRAM-based board, patch for 2.4.16.
+- *  Thu Sep 20 10:24:42 MET DST 2001, v 1.4.9: sync bug fix, preliminary flatpanel support, better timings.
+  *  Tue Aug 28 10:13:01 MET DST 2001, v 1.4.8: memory timings check, minor bug fixes.
+  *  Wed Jul 18 19:06:14 CEST 2001, v 1.4.7: Mode fix (800x600-100, 1024x768-100 changed), using HW panning + accel bug fix.
+  *  Mon Jun 25 10:33:56 MET DST 2001, v 1.4.6: Depth 12 fix, chip reset ioctl, moved memory erase ioctl to DEBUG.
+@@ -54,8 +48,8 @@
+  */
+ #include <linux/config.h>
+-#include <linux/module.h>
+ #include <linux/version.h>
++#include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/string.h>
+@@ -81,7 +75,6 @@
+ #include <asm/io.h>
+ #include <asm/uaccess.h>
+-
+ #ifdef CONFIG_FB_OF
+ #include <asm/prom.h>
+ #endif
+@@ -129,7 +122,6 @@
+       unsigned long refresh;
+       unsigned long powerdown;
+ };
+-typedef enum pm3fb_timing_result { pm3fb_timing_ok, pm3fb_timing_problem, pm3fb_timing_retry } pm3fb_timing_result;
+ #define PM3FB_UNKNOWN_TIMING_VALUE ((unsigned long)-1)
+ #define PM3FB_UNKNOWN_TIMINGS { PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE, PM3FB_UNKNOWN_TIMING_VALUE }
+@@ -192,21 +184,13 @@
+                           PM3VideoControl_VSYNC_ACTIVE_HIGH
+                           | PM3VideoControl_PIXELSIZE_8BIT}}, {
+               "1024x768-74-32", {
+-                      78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38,
+-                      806, 1024, 0, 32,
+-                      PM3VideoControl_ENABLE |
+-                      PM3VideoControl_HSYNC_ACTIVE_HIGH
+-                      |
+-                      PM3VideoControl_VSYNC_ACTIVE_HIGH
+-                      | PM3VideoControl_PIXELSIZE_32BIT}},
+-/* Generated mode : "1600x1024", for the SGI 1600SW flat panel*/
+-      {
+-              "SGI1600SW", {
+-                      108000, 1600, 1024, 16, 56, 104, 1704, 3, 6, 32,
+-                      1056, 1600, 0, 8,
+-                      PM3VideoControl_ENABLE|
+-                      PM3VideoControl_HSYNC_ACTIVE_LOW|PM3VideoControl_VSYNC_ACTIVE_LOW|
+-                      PM3VideoControl_PIXELSIZE_32BIT}}, 
++      78752, 1024, 768, 32, 128, 304, 1328, 1, 4, 38,
++                          806, 1024, 0, 32,
++                          PM3VideoControl_ENABLE |
++                          PM3VideoControl_HSYNC_ACTIVE_HIGH
++                          |
++                          PM3VideoControl_VSYNC_ACTIVE_HIGH
++                          | PM3VideoControl_PIXELSIZE_32BIT}},
+ /* ##### auto-generated mode, by fbtimings2pm3 */
+ /* Generated mode : "640x480-60" */
+       {
+@@ -555,11 +539,9 @@
+ short noaccel[PM3_MAX_BOARD];
+ char fontn[PM3_MAX_BOARD][PM3_FONTNAME_SIZE];
+ short depth[PM3_MAX_BOARD];
+-short flatpanel[PM3_MAX_BOARD];
+ static struct display disp[PM3_MAX_BOARD];
+ static char g_options[PM3_OPTIONS_SIZE] __initdata = "pm3fb,dummy";
+ short printtimings = 0;
+-short forcesize[PM3_MAX_BOARD];
+ /* ********************* */
+ /* ***** prototype ***** */
+@@ -567,8 +549,7 @@
+ /* card-specific */
+ static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info);
+ /* permedia3-specific */
+-static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info);
+-static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info);
++static int pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info);
+ static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info);
+ static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info,
+                                       unsigned long r);
+@@ -684,7 +665,7 @@
+           NULL, NULL
+ };
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ struct fbgen_hwswitch pm3fb_switch = {
+       pm3fb_detect, pm3fb_encode_fix, pm3fb_decode_var, pm3fb_encode_var,
+       pm3fb_get_par, pm3fb_set_par, pm3fb_getcolreg, pm3fb_setcolreg,
+@@ -697,7 +678,7 @@
+       fbgen_get_fix, fbgen_get_var, fbgen_set_var,
+       fbgen_get_cmap, fbgen_set_cmap, fbgen_pan_display, pm3fb_ioctl, NULL, NULL
+ };
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ #ifdef PM3FB_USE_ACCEL
+ #ifdef FBCON_HAS_CFB32
+ static struct display_switch pm3fb_cfb32 = {
+@@ -728,75 +709,52 @@
+ #endif /* FBCON_HAS_CFB8 */
+ #endif /* PM3FB_USE_ACCEL */
+-/* ****************************** */
+-/* ***** card-specific data ***** */
+-/* ****************************** */
+-struct pm3fb_card_timings {
+-      unsigned long memsize; /* 0 for last value (i.e. default) */
+-      struct pm3fb_timings memt;
+-};
+-
+-static struct pm3fb_card_timings t_FormacProFormance3[] = {
+-      { 16, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} },
+-      { 0, { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} } /* from 16 MB PF3 */
+-};
+-
+-static struct pm3fb_card_timings t_AppianJeronimo2000[] = {
+-      { 32, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} },
+-      { 0, { 0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} } /* from 32MB J2000 */
+-};
+-
+-static struct pm3fb_card_timings t_3DLabsOxygenVX1[] = {
+-      { 32, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} },
+-      { 0, { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000} } /* from 32MB VX1 */
+-};
+-
++/* ********************************** */
++/* ***** card-specific function ***** */
++/* ********************************** */
+ static struct {
+               char cardname[32]; /* recognized card name */
+               u16 subvendor; /* subvendor of the card */
+               u16 subdevice; /* subdevice of the card */
+               u8  func; /* function of the card to which the extra init apply */
+               void (*specific_setup)(struct pm3fb_info *l_fb_info); /* card/func specific setup, done before _any_ FB access */
+-      struct pm3fb_card_timings *c_memt; /* defauls timings for the boards */
++      struct pm3fb_timings memt; /* default timing for the board WARNING : might be *card* - specific */
+ } cardbase[] = {
+-      { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, NULL },
+-      { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL,
+-        t_AppianJeronimo2000
+-      },
++      { "Unknown Permedia3 board", 0xFFFF, 0xFFFF, 0xFF, NULL, PM3FB_UNKNOWN_TIMINGS },
++      { "Appian Jeronimo 2000 head 1", 0x1097, 0x3d32, 1, NULL, PM3FB_UNKNOWN_TIMINGS },
+       { "Appian Jeronimo 2000 head 2", 0x1097, 0x3d32, 2, pm3fb_j2000_setup,
+-        t_AppianJeronimo2000
++        {0x02e311B8, 0x07424905, 0x0c000003, 0x00000061, 0x00000000} /* also in pm3fb_j2000_setup */
+       },
+       { "Formac ProFormance 3", PCI_VENDOR_ID_3DLABS, 0x000a, 0, NULL, /* Formac use 3DLabs ID ?!? */
+-        t_FormacProFormance3
++        { 0x02e311b8, 0x06100205, 0x08000002, 0x00000079, 0x00000000} /* from the 16Mb ProFormance 3 */
+       },
+-      { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, NULL },
++      { "3DLabs Permedia3 Create!", PCI_VENDOR_ID_3DLABS, 0x0127, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
+       { "3DLabs Oxygen VX1 PCI", PCI_VENDOR_ID_3DLABS, 0x0121, 0, NULL,
+-        t_3DLabsOxygenVX1
++        { 0x30e311b8, 0x08501204, 0x08000002, 0x0000006b, 0x00000000 }
+       },
+-      { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, NULL },
+-      { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, NULL },
+-      { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, NULL },
+-      { "\0", 0x0, 0x0, 0, NULL, NULL }
++      { "3DLabs Oxygen VX1 AGP", PCI_VENDOR_ID_3DLABS, 0x0125, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++      { "3DLabs Oxygen VX1-16 AGP", PCI_VENDOR_ID_3DLABS, 0x0140, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++      { "3DLabs Oxygen VX1-1600SW PCI", PCI_VENDOR_ID_3DLABS, 0x0800, 0, NULL, PM3FB_UNKNOWN_TIMINGS },
++      { "\0", 0x0, 0x0, 0, NULL, PM3FB_UNKNOWN_TIMINGS }
+ };
+-/* ********************************** */
+-/* ***** card-specific function ***** */
+-/* ********************************** */
+ static void pm3fb_j2000_setup(struct pm3fb_info *l_fb_info)
+ {       /* the appian j2000 require more initialization of the second head */
+       /* l_fb_info must point to the _second_ head of the J2000 */
+       
+       DTRACE;
+-
+-      l_fb_info->memt = t_AppianJeronimo2000[0].memt; /* 32 MB, first and only j2000 ? */
++      
++      /* Memory timings for the Appian J2000 board. also in cardbase */
++      l_fb_info->memt.caps = 0x02e311B8;
++      l_fb_info->memt.timings = 0x07424905;
++      l_fb_info->memt.control = 0x0c000003;
++      l_fb_info->memt.refresh = 0x00000061;
++      l_fb_info->memt.powerdown = 0x00000000;
+       
+       pm3fb_write_memory_timings(l_fb_info);
+ }
+-/* *************************************** */
+-/* ***** permedia3-specific function ***** */
+-/* *************************************** */
+-static pm3fb_timing_result pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info)
++static int pm3fb_preserve_memory_timings(struct pm3fb_info *l_fb_info)
+ {
+       l_fb_info->memt.caps = PM3_READ_REG(PM3LocalMemCaps);
+       l_fb_info->memt.timings = PM3_READ_REG(PM3LocalMemTimings);
+@@ -811,35 +769,20 @@
+           (l_fb_info->memt.powerdown == PM3FB_UNKNOWN_TIMING_VALUE))
+       {
+               printk(KERN_ERR "pm3fb: invalid memory timings in permedia3 board #%ld\n", l_fb_info->board_num);
+-              return(pm3fb_try_memory_timings(l_fb_info));
+-      }
+-      return(pm3fb_timing_ok);
+-}
+-
+-static pm3fb_timing_result pm3fb_try_memory_timings(struct pm3fb_info *l_fb_info)
+-{
+-      if (cardbase[l_fb_info->board_type].c_memt)
+-      {
+-              int i = 0, done = 0;
+-              while (!done)
++              if ((cardbase[l_fb_info->board_type].memt.caps != PM3FB_UNKNOWN_TIMING_VALUE) &&
++                  (cardbase[l_fb_info->board_type].memt.timings != PM3FB_UNKNOWN_TIMING_VALUE) &&
++                  (cardbase[l_fb_info->board_type].memt.control != PM3FB_UNKNOWN_TIMING_VALUE) &&
++                  (cardbase[l_fb_info->board_type].memt.refresh != PM3FB_UNKNOWN_TIMING_VALUE) &&
++                  (cardbase[l_fb_info->board_type].memt.powerdown != PM3FB_UNKNOWN_TIMING_VALUE))
+               {
+-                      if ((cardbase[l_fb_info->board_type].c_memt[i].memsize == l_fb_info->fb_size)
+-                          || !(cardbase[l_fb_info->board_type].c_memt[i].memsize))
+-                      { /* will use the 0-sized timings by default */
+-                              done = 1;
+-                              l_fb_info->memt = cardbase[l_fb_info->board_type].c_memt[i].memt;
+-                              printk(KERN_WARNING  "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s, %ld MB)\n",
+-                                     l_fb_info->board_num,
+-                                     cardbase[l_fb_info->board_type].cardname,
+-                                     cardbase[l_fb_info->board_type].c_memt[i].memsize);
+-                              pm3fb_write_memory_timings(l_fb_info);
+-                              return(pm3fb_timing_retry);
+-                      }
+-                      i++;
++                      l_fb_info->memt = cardbase[l_fb_info->board_type].memt;
++                      printk(KERN_WARNING "pm3fb: trying to use predefined memory timings for permedia3 board #%ld (%s)\n", l_fb_info->board_num, cardbase[l_fb_info->board_type].cardname);
++                      pm3fb_write_memory_timings(l_fb_info);
+               }
+-      } else
+-              return(pm3fb_timing_problem);
+-      return(pm3fb_timing_ok);
++              else
++                      return(1);
++      }
++      return(0);
+ }
+ static void pm3fb_write_memory_timings(struct pm3fb_info *l_fb_info)
+@@ -874,6 +817,10 @@
+                         PM3RD_SClkControl_ENABLE);
+ }
++/* *************************************** */
++/* ***** permedia3-specific function ***** */
++/* *************************************** */
++
+ static unsigned long pm3fb_read_dac_reg(struct pm3fb_info *l_fb_info,
+                                       unsigned long r)
+ {
+@@ -1068,7 +1015,6 @@
+ /* write the mode to registers */
+ static void pm3fb_write_mode(struct pm3fb_info *l_fb_info)
+ {
+-      char tempsync = 0x00, tempmisc = 0x00;
+       DTRACE;
+       PM3_SLOW_WRITE_REG(PM3MemBypassWriteMask, 0xffffffff);
+@@ -1198,26 +1144,22 @@
+       /*
+          PM3_SLOW_WRITE_REG(PM3RD_IndexControl, 0x00);
+        */
+-      if ((l_fb_info->current_par->video & PM3VideoControl_HSYNC_MASK) ==
+-          PM3VideoControl_HSYNC_ACTIVE_HIGH)
+-              tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
+-      if ((l_fb_info->current_par->video & PM3VideoControl_VSYNC_MASK) ==
+-          PM3VideoControl_VSYNC_ACTIVE_HIGH)
+-              tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;
+-      
+-      PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync);
+-      DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync);
+-      
+-      if (flatpanel[l_fb_info->board_num])
+       {
+-              PM3_WRITE_DAC_REG(PM3RD_DACControl, PM3RD_DACControl_BLANK_PEDESTAL_ENABLE);
+-              PM3_WAIT(2);
+-              PM3_WRITE_REG(PM3VSConfiguration, 0x06);
+-              PM3_WRITE_REG(0x5a00, 1 << 14); /* black magic... */
+-              tempmisc = PM3RD_MiscControl_VSB_OUTPUT_ENABLE;
++              char tempsync = 0x00;
++
++              if ((l_fb_info->current_par->
++                   video & PM3VideoControl_HSYNC_MASK) ==
++                  PM3VideoControl_HSYNC_ACTIVE_HIGH)
++                      tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
++              if ((l_fb_info->current_par->
++                   video & PM3VideoControl_VSYNC_MASK) ==
++                  PM3VideoControl_VSYNC_ACTIVE_HIGH)
++                      tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;
++
++              PM3_WRITE_DAC_REG(PM3RD_SyncControl, tempsync);
++              DPRINTK(2, "PM3RD_SyncControl: %d\n", tempsync);
+       }
+-      else
+-              PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00);
++      PM3_WRITE_DAC_REG(PM3RD_DACControl, 0x00);
+       switch (l_fb_info->current_par->depth) {
+       case 8:
+@@ -1226,7 +1168,8 @@
+               PM3_WRITE_DAC_REG(PM3RD_ColorFormat,
+                                 PM3RD_ColorFormat_CI8_COLOR |
+                                 PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
+-              tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++              PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++                                PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+               break;
+       case 12:
+               PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1235,8 +1178,9 @@
+                                 PM3RD_ColorFormat_4444_COLOR |
+                                 PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+                                 PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+-              tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+-                      PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++              PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++                                PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++                                PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+               break;          
+       case 15:
+               PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1245,8 +1189,9 @@
+                                 PM3RD_ColorFormat_5551_FRONT_COLOR |
+                                 PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+                                 PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+-              tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+-                      PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++              PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++                                PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++                                PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+               break;          
+       case 16:
+               PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1255,8 +1200,9 @@
+                                 PM3RD_ColorFormat_565_FRONT_COLOR |
+                                 PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
+                                 PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
+-              tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+-                      PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++              PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++                                PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++                                PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+               break;
+       case 32:
+               PM3_WRITE_DAC_REG(PM3RD_PixelSize,
+@@ -1264,12 +1210,12 @@
+               PM3_WRITE_DAC_REG(PM3RD_ColorFormat,
+                                 PM3RD_ColorFormat_8888_COLOR |
+                                 PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
+-              tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
+-                      PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
++              PM3_WRITE_DAC_REG(PM3RD_MiscControl,
++                                PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
++                                PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE);
+               break;
+       }
+-      PM3_WRITE_DAC_REG(PM3RD_MiscControl, tempmisc);
+-      
++
+       PM3_SHOW_CUR_MODE;
+ }
+@@ -1391,9 +1337,8 @@
+ static unsigned long pm3fb_size_memory(struct pm3fb_info *l_fb_info)
+ {
+-      unsigned long memsize = 0, tempBypass, i, temp1, temp2;
++      unsigned long memsize, tempBypass, i, temp1, temp2;
+       u16 subvendor, subdevice;
+-      pm3fb_timing_result ptr;
+       DTRACE;
+@@ -1439,7 +1384,7 @@
+       
+       /* card-specific setup is done, we preserve the final
+            memory timing for future reference */
+-      if ((ptr = pm3fb_preserve_memory_timings(l_fb_info)) == pm3fb_timing_problem) { /* memory timings were wrong ! oops.... */
++      if (pm3fb_preserve_memory_timings(l_fb_info)) { /* memory timings were wrong ! oops.... */
+               return(0);
+       }
+       
+@@ -1465,12 +1410,12 @@
+               temp1 = readl((l_fb_info->v_fb + (i * 1048576)));
+ #endif
+ #endif        /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+               fb_writel(i * 0x00345678,
+                         (l_fb_info->v_fb + (i * 1048576)));
+               mb();
+               temp1 = fb_readl((l_fb_info->v_fb + (i * 1048576)));
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif        /* KERNEL_2_4 */
+               /* Let's check for wrapover, write will fail at 16MB boundary */
+               if (temp1 == (i * 0x00345678))
+                       memsize = i;
+@@ -1513,7 +1458,7 @@
+                                  ((i - 32) * 1048576)));
+ #endif
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+                       fb_writel(i * 0x00345678,
+                                 (l_fb_info->v_fb + (i * 1048576)));
+                       mb();
+@@ -1522,7 +1467,7 @@
+                       temp2 =
+                           fb_readl((l_fb_info->v_fb +
+                                     ((i - 32) * 1048576)));
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+                       if ((temp1 == (i * 0x00345678)) && (temp2 == 0))        /* different value, different RAM... */
+                               memsize = i;
+                       else
+@@ -1539,21 +1484,8 @@
+       DPRINTK(2, "Returning 0x%08lx bytes\n", memsize);
+-      if (forcesize[l_fb_info->board_num] && ((forcesize[l_fb_info->board_num] * 1048576) != memsize))
+-      {
+-              printk(KERN_WARNING "pm3fb: mismatch between probed (%ld MB) and specified (%hd MB) memory size, using SPECIFIED !\n", memsize, forcesize[l_fb_info->board_num]);
+-              memsize = 1048576 * forcesize[l_fb_info->board_num];
+-      }
+-      
+       l_fb_info->fb_size = memsize;
+-      
+-      if (ptr == pm3fb_timing_retry)
+-      {
+-              printk(KERN_WARNING "pm3fb: retrying memory timings check");
+-              if (pm3fb_try_memory_timings(l_fb_info) == pm3fb_timing_problem)
+-                      return(0);
+-      }
+-      
++
+       return (memsize);
+ }
+@@ -1572,7 +1504,7 @@
+               writel(cc, (l_fb_info->v_fb + (i * sizeof(u32))));
+ #endif
+ #endif
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+               fb_writel(cc, (l_fb_info->v_fb + (i * sizeof(u32))));
+ #endif
+       }
+@@ -1601,7 +1533,7 @@
+       disp[l_fb_info->board_num].scrollmode = 0;      /* SCROLL_YNOMOVE; *//* 0 means "let fbcon choose" */
+       l_fb_info->gen.parsize = sizeof(struct pm3fb_par);
+       l_fb_info->gen.info.changevar = NULL;
+-      l_fb_info->gen.info.node = B_FREE;
++      l_fb_info->gen.info.node = -1;
+       l_fb_info->gen.info.fbops = &pm3fb_ops;
+       l_fb_info->gen.info.disp = &(disp[l_fb_info->board_num]);
+       if (fontn[l_fb_info->board_num][0])
+@@ -1766,7 +1698,6 @@
+       }
+       PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xffffffff);
+-      PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff);
+       PM3_SLOW_WRITE_REG(PM3FBWriteMode,
+                          PM3FBWriteMode_WriteEnable |
+                          PM3FBWriteMode_OpaqueSpan |
+@@ -1787,7 +1718,9 @@
+                       PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, 4095);
+               else
+                       PM3_SLOW_WRITE_REG(PM3SizeOfFramebuffer, sofb);
+-              
++
++              PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xffffffff);
++
+               switch (l_fb_info->current_par->depth) {
+               case 8:
+                       PM3_SLOW_WRITE_REG(PM3DitherMode,
+@@ -1843,10 +1776,7 @@
+       height = height * fontheight(p);
+       c = ((u32 *) p->dispsw_data)[attr_bgcol_ec(p, conp)];
+-      /* block fills in 32bpp are hard, but in low res (width <= 1600 :-)
+-         we can use 16bpp operations, but not if NoWriteMask is on (SDRAM)  */
+-      if ((l_fb_info->current_par->width > 1600) ||
+-          (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)) {
++      if (l_fb_info->current_par->width > 1600) {
+               PM3_WAIT(4);
+               PM3_WRITE_REG(PM3Config2D,
+@@ -1868,7 +1798,7 @@
+                             PM3Render2D_SpanOperation |
+                             (PM3Render2D_Width(width)) |
+                             (PM3Render2D_Height(height)));
+-      } else {
++      } else {                /* block fills in 32bpp are hard, but in low res (width <= 1600 :-) we can use 16bpp operations */
+               PM3_WAIT(8);
+               PM3_WRITE_REG(PM3FBBlockColor, c);
+@@ -1993,10 +1923,7 @@
+       PM3_WAIT(4);
+-      if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-              PM3_WRITE_REG(PM3ForegroundColor, c);
+-      else
+-              PM3_WRITE_REG(PM3FBBlockColor, c);
++      PM3_WRITE_REG(PM3FBBlockColor, c);
+       PM3_WRITE_REG(PM3Config2D,
+                                 PM3Config2D_UseConstantSource |
+@@ -2007,23 +1934,14 @@
+       PM3_WRITE_REG(PM3RectanglePosition,
+                     (PM3RectanglePosition_XOffset(sx)) |
+                     (PM3RectanglePosition_YOffset(sy)));
+-      
+-      if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-              PM3_WRITE_REG(PM3Render2D,
+-                            PM3Render2D_XPositive |
+-                            PM3Render2D_YPositive |
+-                            PM3Render2D_Operation_Normal |
+-                            PM3Render2D_SpanOperation |
+-                            (PM3Render2D_Width(width)) |
+-                            (PM3Render2D_Height(height)));
+-      else
+-              PM3_WRITE_REG(PM3Render2D,
+-                            PM3Render2D_XPositive |
+-                            PM3Render2D_YPositive |
+-                            PM3Render2D_Operation_Normal |
+-                            (PM3Render2D_Width(width)) |
+-                            (PM3Render2D_Height(height)));
+-      
++
++      PM3_WRITE_REG(PM3Render2D,
++                    PM3Render2D_XPositive |
++                    PM3Render2D_YPositive |
++                    PM3Render2D_Operation_Normal |
++                    (PM3Render2D_Width(width)) |
++                    (PM3Render2D_Height(height)));
++
+       pm3fb_wait_pm3(l_fb_info);
+ }
+@@ -2049,69 +1967,45 @@
+                                         PM3Config2D_ForegroundROPEnable |
+                                         (PM3Config2D_ForegroundROP(0x3)) |    /* Ox3 is GXcopy */
+                                         PM3Config2D_FBWriteEnable);
+-              
+-              if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-                      PM3_WRITE_REG(PM3ForegroundColor, c);
+-              else
+-                      PM3_WRITE_REG(PM3FBBlockColor, c);
+-              
++
++              PM3_WRITE_REG(PM3FBBlockColor, c);
++
+               PM3_WRITE_REG(PM3RectanglePosition,
+                             (PM3RectanglePosition_XOffset
+                              (p->var.xoffset +
+                               sx)) | (PM3RectanglePosition_YOffset(p->
+                                                                    var.
+                                                                    yoffset)));
+-              if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-                      PM3_WRITE_REG(PM3Render2D,
+-                                    PM3Render2D_XPositive |
+-                                    PM3Render2D_YPositive |
+-                                    PM3Render2D_Operation_Normal |
+-                                    PM3Render2D_SpanOperation |
+-                                    (PM3Render2D_Width(p->var.xres - sx)) |
+-                                    (PM3Render2D_Height(p->var.yres)));
+-              else
+-                      PM3_WRITE_REG(PM3Render2D,
+-                                    PM3Render2D_XPositive |
+-                                    PM3Render2D_YPositive |
+-                                    PM3Render2D_Operation_Normal |
+-                                    (PM3Render2D_Width(p->var.xres - sx)) |
+-                                    (PM3Render2D_Height(p->var.yres)));
++
++              PM3_WRITE_REG(PM3Render2D,
++                            PM3Render2D_XPositive |
++                            PM3Render2D_YPositive |
++                            PM3Render2D_Operation_Normal |
++                            (PM3Render2D_Width(p->var.xres - sx)) |
++                            (PM3Render2D_Height(p->var.yres)));
+       }
+-      
++
+       /* bottom margin left -> right */
+       PM3_WAIT(4);
+-      
++
+       PM3_WRITE_REG(PM3Config2D,
+-                    PM3Config2D_UseConstantSource |
+-                    PM3Config2D_ForegroundROPEnable |
+-                    (PM3Config2D_ForegroundROP(0x3)) |        /* Ox3 is GXcopy */
+-                    PM3Config2D_FBWriteEnable);
+-      
+-      if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-              PM3_WRITE_REG(PM3ForegroundColor, c);
+-      else
+-              PM3_WRITE_REG(PM3FBBlockColor, c);
+-      
+-      
++                                PM3Config2D_UseConstantSource |
++                                PM3Config2D_ForegroundROPEnable |
++                                (PM3Config2D_ForegroundROP(0x3)) |    /* Ox3 is GXcopy */
++                                PM3Config2D_FBWriteEnable);
++
++      PM3_WRITE_REG(PM3FBBlockColor, c);
++
+       PM3_WRITE_REG(PM3RectanglePosition,
+                     (PM3RectanglePosition_XOffset(p->var.xoffset)) |
+                     (PM3RectanglePosition_YOffset(p->var.yoffset + sy)));
+-      
+-      if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-              PM3_WRITE_REG(PM3Render2D,
+-                            PM3Render2D_XPositive |
+-                            PM3Render2D_YPositive |
+-                            PM3Render2D_Operation_Normal |
+-                            PM3Render2D_SpanOperation |
+-                            (PM3Render2D_Width(p->var.xres)) |
+-                            (PM3Render2D_Height(p->var.yres - sy)));
+-      else
+-              PM3_WRITE_REG(PM3Render2D,
+-                            PM3Render2D_XPositive |
+-                            PM3Render2D_YPositive |
+-                            PM3Render2D_Operation_Normal |
+-                            (PM3Render2D_Width(p->var.xres)) |
+-                            (PM3Render2D_Height(p->var.yres - sy)));
++
++      PM3_WRITE_REG(PM3Render2D,
++                    PM3Render2D_XPositive |
++                    PM3Render2D_YPositive |
++                    PM3Render2D_Operation_Normal |
++                    (PM3Render2D_Width(p->var.xres)) |
++                    (PM3Render2D_Height(p->var.yres - sy)));
+       pm3fb_wait_pm3(l_fb_info);
+ }
+@@ -2289,7 +2183,7 @@
+                           int c, int yy, int xx)
+ {
+       struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info;
+-      u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0;
++      u8 *cdat, asx = 0, asy = 0, o_x, o_y;
+       u32 fgx, bgx, ldat;
+       int sx, sy, i;
+@@ -2399,7 +2293,7 @@
+                            int xx)
+ {
+       struct pm3fb_info *l_fb_info = (struct pm3fb_info *) p->fb_info;
+-      u8 *cdat, asx = 0, asy = 0, o_x = 0, o_y = 0;
++      u8 *cdat, asx = 0, asy = 0, o_x, o_y;
+       u32 fgx, bgx, ldat;
+       int sx, sy, i, j;
+       u16 sc;
+@@ -2517,12 +2411,7 @@
+       yy = yy * fontheight(p);
+       if (l_fb_info->current_par->depth == 8)
+-      {
+-              if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-                      PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0x0F0F0F0F);
+-              else
+-                      PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F);
+-      }
++              PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0x0F0F0F0F);
+       PM3_WAIT(3);
+@@ -2548,12 +2437,7 @@
+       pm3fb_wait_pm3(l_fb_info);
+       if (l_fb_info->current_par->depth == 8)
+-      {
+-              if (l_fb_info->memt.caps & PM3LocalMemCaps_NoWriteMask)
+-                      PM3_SLOW_WRITE_REG(PM3FBSoftwareWriteMask, 0xFFFFFFFF);
+-              else
+-                      PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF);
+-      }
++              PM3_SLOW_WRITE_REG(PM3FBHardwareWriteMask, 0xFFFFFFFF);
+ }
+ #endif /* FBCON_HAS_CFB8 || FBCON_HAS_CFB16 || FBCON_HAS_CFB32 */
+@@ -2641,25 +2525,12 @@
+       unsigned long bd = simple_strtoul(bds, (char **) NULL, 10);
+       if (!(depth_supported(bd))) {
+-              printk(KERN_WARNING "pm3fb: ignoring invalid depth %s for board #%ld\n",
+-                     bds, board_num);
++              DPRINTK(1, "Invalid depth: %s\n", bds);
+               return;
+       }
+       depth[board_num] = bd;
+ }
+-static void pm3fb_forcesize_setup(char *bds, unsigned long board_num)
+-{
+-      unsigned long bd = simple_strtoul(bds, (char **) NULL, 10);
+-
+-      if (bd > 64) {
+-              printk(KERN_WARNING "pm3fb: ignoring invalid memory size %s for board #%ld\n",
+-                     bds, board_num);
+-              return;
+-      }
+-      forcesize[board_num] = bd;
+-}
+-
+ static char *pm3fb_boardnum_setup(char *options, unsigned long *bn)
+ {
+       char *next;
+@@ -2753,12 +2624,6 @@
+                       pm3fb_bootdepth_setup(options, bn);
+               } else if (!strncmp(options, "printtimings", 12)) {
+                       printtimings = 1;
+-              } else if (!strncmp(options, "flatpanel:", 10)) {
+-                      options = pm3fb_boardnum_setup(options + 10, &bn);
+-                      flatpanel[bn] = 1;
+-              } else if (!strncmp(options, "forcesize:", 10)) {
+-                      options = pm3fb_boardnum_setup(options + 10, &bn);
+-                      pm3fb_forcesize_setup(options, bn);
+               }
+               options = next;
+       }
+@@ -3496,7 +3361,7 @@
+                                   pci_resource_start(l_fb_info->dev, 1);
+                               l_fb_info->v_fb = (unsigned char *) -1;
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)      /* full resource management, new in linux-2.4.x */
++#ifdef KERNEL_2_4             /* full resource management, new in linux-2.4.x */
+                               if (!request_mem_region
+                                   ((unsigned long)l_fb_info->p_fb, 64 * 1024 * 1024, /* request full aperture size */
+                                    "pm3fb")) {
+@@ -3513,10 +3378,8 @@
+                                            l_fb_info->board_num);
+                                       continue;
+                               }
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
+-                              if (forcesize[l_fb_info->board_num])
+-                                      l_fb_info->fb_size = forcesize[l_fb_info->board_num];
+-                              
++#endif /* KERNEL_2_4 */
++
+                               l_fb_info->fb_size =
+                                   pm3fb_size_memory(l_fb_info);
+@@ -3612,7 +3475,7 @@
+ /* ***** standard FB API init functions ***** */
+ /* ****************************************** */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ int __init pm3fb_setup(char *options)
+ #endif
+ #ifdef KERNEL_2_2
+@@ -3628,12 +3491,12 @@
+               PM3_OPTIONS_SIZE) ? PM3_OPTIONS_SIZE : (opsi + 1));
+       g_options[PM3_OPTIONS_SIZE - 1] = 0;
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+       return (0);
+ #endif
+ }
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+ int __init pm3fb_init(void)
+ #endif
+ #ifdef KERNEL_2_2
+@@ -3642,7 +3505,7 @@
+ {
+       DTRACE;
+-      DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /cvsroot/linux/drivers/video/pm3fb.c,v 1.1 2002/02/25 19:11:06 marcelo Exp $");
++      DPRINTK(2, "This is pm3fb.c, CVS version: $Header: /home/pm3fb/pm3fb/pm3fb.c,v 1.139 2001/08/28 08:13:54 dolbeau Exp $");
+       pm3fb_real_setup(g_options);
+@@ -3651,7 +3514,7 @@
+       if (!fb_info[0].dev) {  /* not even one board ??? */
+               DPRINTK(1, "No PCI Permedia3 board detected\n");
+       }
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+       return (0);
+ #endif
+ }
+@@ -3754,11 +3617,7 @@
+ MODULE_PARM(depth,PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+ MODULE_PARM_DESC(depth,"boot-time depth");
+ MODULE_PARM(printtimings, "h");
+-MODULE_PARM_DESC(printtimings, "print the memory timings of the card(s)");
+-MODULE_PARM(forcesize, PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+-MODULE_PARM_DESC(forcesize, "force specified memory size");
+-MODULE_PARM(flatpanel, PM3_MAX_BOARD_MODULE_ARRAY_SHORT);
+-MODULE_PARM_DESC(flatpanel, "flatpanel (LCD) support (preliminary)");
++MODULE_PARM_DESC(printtimings, "print the memory timngs of the card(s)");
+ /*
+ MODULE_SUPPORTED_DEVICE("Permedia3 PCI boards")
+ MODULE_GENERIC_TABLE(gtype,name)
+@@ -3803,11 +3662,6 @@
+                       sprintf(ts, ",depth:%d:%d", i, depth[i]);
+                       strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options));
+               }
+-                if (flatpanel[i])
+-              {
+-                      sprintf(ts, ",flatpanel:%d:", i);
+-                      strncat(g_options, ts, PM3_OPTIONS_SIZE - strlen(g_options));
+-              }
+       }
+       g_options[PM3_OPTIONS_SIZE - 1] = '\0';
+       DPRINTK(1, "pm3fb use options: %s\n", g_options);
+@@ -3837,14 +3691,14 @@
+                               if (l_fb_info->vIOBase !=
+                                   (unsigned char *) -1) {
+                                       pm3fb_unmapIO(l_fb_info);
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5)
++#ifdef KERNEL_2_4
+                                       release_mem_region(l_fb_info->p_fb,
+                                                          l_fb_info->
+                                                          fb_size);
+                                       release_mem_region(l_fb_info->
+                                                          pIOBase,
+                                                          PM3_REGS_SIZE);
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+                               }
+                               unregister_framebuffer(&l_fb_info->gen.
+                                                      info);
+--- linux-2.4.27/drivers/video/pm3fb.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/pm3fb.h
+@@ -8,7 +8,7 @@
+  *  License. See the file COPYING in the main directory of this archive for
+  *  more details.
+  *
+- *  $Header: /cvsroot/linux/drivers/video/pm3fb.h,v 1.1 2002/02/25 19:11:06 marcelo Exp $
++ *  $Header: /home/pm3fb/pm3fb/pm3fb.h,v 1.30 2001/08/22 09:13:46 dolbeau Exp $
+  *
+  */
+@@ -92,7 +92,6 @@
+ #define PM3MemBypassWriteMask                                 0x1008
+ #define PM3MemScratch                                         0x1010
+ #define PM3LocalMemCaps                                               0x1018
+-        #define PM3LocalMemCaps_NoWriteMask                     (1 << 28)
+ #define PM3LocalMemTimings                                    0x1020
+ #define PM3LocalMemControl                                    0x1028
+ #define PM3LocalMemRefresh                                    0x1030
+@@ -1121,10 +1120,6 @@
+ /* kernel -specific definitions */
+ /* what kernel is this ? */
+-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)))
+-#define KERNEL_2_5
+-#endif
+-
+ #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)))
+ #define KERNEL_2_4
+ #endif
+@@ -1138,8 +1133,8 @@
+ #endif
+ #endif
+-#if (!defined(KERNEL_2_2)) && (!defined(KERNEL_2_4)) && (!defined(KERNEL_2_5))
+-#error "Only kernel 2.2.x, kernel 2.4.y and kernel 2.5.z might work"
++#if (!defined(KERNEL_2_2)) && (!defined(KERNEL_2_4))
++#error "Only kernel 2.2.x and kernel 2.4.y might work"
+ #endif
+ /* not sure if/why it's needed. doesn't work without on my PowerMac... */
+@@ -1147,11 +1142,6 @@
+ #define MUST_BYTESWAP
+ #endif
+-/* for compatibility between 2.5, 2.4 and 2.2 */
+-#ifndef B_FREE
+-#define B_FREE   -1
+-#endif
+-
+ /* permedia3 -specific definitions */
+ #define PM3_SCALE_TO_CLOCK(pr, fe, po) ((2 * PM3_REF_CLOCK * fe) / (pr * (1 << (po))))
+ #define PICOS2KHZ(a) (1000000000UL/(a))
+@@ -1219,10 +1209,10 @@
+ #define PM3_READ_REG(r) readl((l_fb_info->vIOBase + r))
+ #endif /* MUST_BYTESWAP */
+ #endif /* KERNEL_2_2 */
+-#if (defined KERNEL_2_4) || (defined KERNEL_2_5) /* native-endian access */
++#ifdef KERNEL_2_4 /* native-endian access */
+ #define PM3_WRITE_REG(r, v) fb_writel(v, (l_fb_info->vIOBase + r))
+ #define PM3_READ_REG(r) fb_readl((l_fb_info->vIOBase + r))
+-#endif /* KERNEL_2_4 or KERNEL_2_5 */
++#endif /* KERNEL_2_4 */
+ #define depth2bpp(d) ((d + 7L) & ~7L)
+--- linux-2.4.27/drivers/video/sa1100fb.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/sa1100fb.c
+@@ -23,11 +23,11 @@
+  * Thank you.
+  *
+  * Known problems:
+- *  - With the Neponset plugged into an Assabet, LCD powerdown
+- *    doesn't work (LCD stays powered up).  Therefore we shouldn't
+- *    blank the screen.
+- *  - We don't limit the CPU clock rate nor the mode selection
+- *    according to the available SDRAM bandwidth.
++ *    - With the Neponset plugged into an Assabet, LCD powerdown
++ *      doesn't work (LCD stays powered up).  Therefore we shouldn't
++ *      blank the screen.
++ *    - We don't limit the CPU clock rate nor the mode selection
++ *      according to the available SDRAM bandwidth.
+  *
+  * Other notes:
+  *    - Linear grayscale palettes and the kernel.
+@@ -41,6 +41,17 @@
+  *      David Neuer.  It's around 8 lines of C code, plus another 4 to
+  *      detect if we are using grayscale.
+  *
++ *    - The following must never be specified in a panel definition:
++ *         LCCR0_LtlEnd, LCCR3_PixClkDiv, LCCR3_VrtSnchL, LCCR3_HorSnchL
++ *
++ *    - The following should be specified:
++ *         either LCCR0_Color or LCCR0_Mono
++ *         either LCCR0_Sngl or LCCR0_Dual
++ *         either LCCR0_Act or LCCR0_Pas
++ *         either LCCR3_OutEnH or LCCD3_OutEnL
++ *         either LCCR3_PixRsEdg or LCCR3_PixFlEdg
++ *         either LCCR3_ACBsDiv or LCCR3_ACBsCntOff
++ *
+  * Code Status:
+  * 1999/04/01:
+  *    - Driver appears to be working for Brutus 320x200x8bpp mode.  Other
+@@ -147,6 +158,10 @@
+  *
+  * 2001/10/12: <rmk@arm.linux.org.uk>
+  *    - Add patch 681/1 and clean up stork definitions.
++ *
++ * 2002/02/21: <abraham@2d3d.co.za>
++ *  - Added support for ICP LCD-Kit01 on Frodo.
++ *  - Added support for backlight via CPLDs on Frodo.
+  */
+ #include <linux/config.h>
+@@ -169,6 +184,7 @@
+ #include <asm/mach-types.h>
+ #include <asm/uaccess.h>
+ #include <asm/arch/assabet.h>
++#include <asm/arch/shannon.h>
+ #include <video/fbcon.h>
+ #include <video/fbcon-mfb.h>
+@@ -177,11 +193,6 @@
+ #include <video/fbcon-cfb16.h>
+ /*
+- * enable this if your panel appears to have broken
+- */
+-#undef CHECK_COMPAT
+-
+-/*
+  * debugging?
+  */
+ #define DEBUG 0
+@@ -197,243 +208,6 @@
+ void (*sa1100fb_blank_helper)(int blank);
+ EXPORT_SYMBOL(sa1100fb_blank_helper);
+-
+-#ifdef CHECK_COMPAT
+-static void
+-sa1100fb_check_shadow(struct sa1100fb_lcd_reg *new_regs,
+-                         struct fb_var_screeninfo *var, u_int pcd)
+-{
+-      struct sa1100fb_lcd_reg shadow;
+-      int different = 0;
+-
+-      /*
+-       * These machines are good machines!
+-       */
+-      if (machine_is_assabet() || machine_is_h3600())
+-              return;
+-
+-      /*
+-       * The following ones are bad, bad, bad.
+-       * Please make yours good!
+-       */
+-      if (machine_is_pangolin()) {
+-              DPRINTK("Configuring Pangolin LCD\n");
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_LDM +
+-                  LCCR0_BAM + LCCR0_ERM + LCCR0_Act +
+-                  LCCR0_LtlEnd + LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(64) +
+-                  LCCR1_BegLnDel(160) + LCCR1_EndLnDel(24);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(7) +
+-                  LCCR2_BegFrmDel(7) + LCCR2_EndFrmDel(1);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(pcd) + LCCR3_HorSnchH +
+-                  LCCR3_VrtSnchH + LCCR3_PixFlEdg + LCCR3_OutEnH;
+-
+-              DPRINTK("pcd = %x, PixCldDiv(pcd)=%x\n",
+-                      pcd, LCCR3_PixClkDiv(pcd));
+-      }
+-      if (machine_is_freebird()) {
+-              DPRINTK("Configuring  Freebird LCD\n");
+-#if 1
+-              shadow.lccr0 = 0x00000038;
+-              shadow.lccr1 = 0x010108e0;
+-              shadow.lccr2 = 0x0000053f;
+-              shadow.lccr3 = 0x00000c20;
+-#else
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
+-                  LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
+-                  LCCR0_LtlEnd + LCCR0_DMADel(0);
+-              /* Check ,Chester */
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
+-                  LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
+-              /* Check ,Chester */
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+-                  LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
+-              /* Check ,Chester */
+-              shadow.lccr3 =
+-                  LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
+-                  LCCR3_HorSnchH + LCCR3_ACBsCntOff +
+-                  LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(pcd);
+-#endif
+-      }
+-      if (machine_is_brutus()) {
+-              DPRINTK("Configuring  Brutus LCD\n");
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
+-                  LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+-                  LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(3) +
+-                  LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+-                  LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
+-              shadow.lccr3 =
+-                  LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
+-                  LCCR3_HorSnchH + LCCR3_ACBsCntOff +
+-                  LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
+-      }
+-      if (machine_is_huw_webpanel()) {
+-              DPRINTK("Configuring  HuW LCD\n");
+-              shadow.lccr0 = LCCR0_LEN + LCCR0_Dual + LCCR0_LDM;
+-              shadow.lccr1 = LCCR1_DisWdth(var->xres) +
+-                  LCCR1_HorSnchWdth(3) +
+-                  LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
+-              shadow.lccr2 = 239 + LCCR2_VrtSnchWdth(1);
+-              shadow.lccr3 = 8 + LCCR3_OutEnH +
+-                  LCCR3_PixRsEdg + LCCR3_VrtSnchH +
+-                  LCCR3_HorSnchH + LCCR3_ACBsCntOff + LCCR3_ACBsDiv(2);
+-      }
+-      if (machine_is_lart()) {
+-              DPRINTK("Configuring LART LCD\n");
+-#if defined LART_GREY_LCD
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
+-                  LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+-                  LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(1) +
+-                  LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+-                  LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
+-                  LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH;
+-#endif
+-#if defined LART_COLOR_LCD
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+-                  LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+-                  LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(2) +
+-                  LCCR1_BegLnDel(69) + LCCR1_EndLnDel(8);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(3) +
+-                  LCCR2_BegFrmDel(14) + LCCR2_EndFrmDel(4);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
+-                  LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL +
+-                  LCCR3_PixFlEdg;
+-#endif
+-#if defined LART_VIDEO_OUT
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+-                  LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+-                  LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(640) + LCCR1_HorSnchWdth(95) +
+-                  LCCR1_BegLnDel(40) + LCCR1_EndLnDel(24);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(480) + LCCR2_VrtSnchWdth(2) +
+-                  LCCR2_BegFrmDel(32) + LCCR2_EndFrmDel(11);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(8) + LCCR3_ACBsDiv(512) +
+-                  LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH +
+-                  LCCR3_PixFlEdg + LCCR3_OutEnL;
+-#endif
+-      }
+-      if (machine_is_graphicsclient()) {
+-              DPRINTK("Configuring GraphicsClient LCD\n");
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act;
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(9) +
+-                  LCCR1_EndLnDel(54) + LCCR1_BegLnDel(54);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(9) +
+-                  LCCR2_EndFrmDel(32) + LCCR2_BegFrmDel(24);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(10) + LCCR3_ACBsDiv(2) +
+-                  LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL;
+-      }
+-      if (machine_is_omnimeter()) {
+-              DPRINTK("Configuring  OMNI LCD\n");
+-              shadow.lccr0 = LCCR0_LEN | LCCR0_CMS | LCCR0_DPD;
+-              shadow.lccr1 =
+-                  LCCR1_BegLnDel(10) + LCCR1_EndLnDel(10) +
+-                  LCCR1_HorSnchWdth(1) + LCCR1_DisWdth(var->xres);
+-              shadow.lccr2 = LCCR2_DisHght(var->yres);
+-              shadow.lccr3 =
+-                  LCCR3_ACBsDiv(0xFF) + LCCR3_PixClkDiv(44);
+-//jca (GetPCD(25) << LCD3_V_PCD);
+-      }
+-      if (machine_is_xp860()) {
+-              DPRINTK("Configuring XP860 LCD\n");
+-              shadow.lccr0 =
+-                  LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+-                  LCCR0_LtlEnd + LCCR0_LDM + LCCR0_ERM + LCCR0_DMADel(0);
+-              shadow.lccr1 =
+-                  LCCR1_DisWdth(var->xres) +
+-                  LCCR1_HorSnchWdth(var->hsync_len) +
+-                  LCCR1_BegLnDel(var->left_margin) +
+-                  LCCR1_EndLnDel(var->right_margin);
+-              shadow.lccr2 =
+-                  LCCR2_DisHght(var->yres) +
+-                  LCCR2_VrtSnchWdth(var->vsync_len) +
+-                  LCCR2_BegFrmDel(var->upper_margin) +
+-                  LCCR2_EndFrmDel(var->lower_margin);
+-              shadow.lccr3 =
+-                  LCCR3_PixClkDiv(6) + LCCR3_HorSnchL + LCCR3_VrtSnchL;
+-      }
+-
+-      /*
+-       * Ok, since we're calculating these values, we want to know
+-       * if the calculation is correct.  If you see any of these
+-       * messages _PLEASE_ report the incident to me for diagnosis,
+-       * including details about what was happening when the
+-       * messages appeared. --rmk, 30 March 2001
+-       */
+-      if (shadow.lccr0 != new_regs->lccr0) {
+-              printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
+-                      shadow.lccr1, new_regs->lccr1);
+-              different = 1;
+-      }
+-      if (shadow.lccr1 != new_regs->lccr1) {
+-              printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
+-                      shadow.lccr1, new_regs->lccr1);
+-              different = 1;
+-      }
+-      if (shadow.lccr2 != new_regs->lccr2) {
+-              printk(KERN_ERR "LCCR2 mismatch: 0x%08x != 0x%08x\n",
+-                      shadow.lccr2, new_regs->lccr2);
+-              different = 1;
+-      }
+-      if (shadow.lccr3 != new_regs->lccr3) {
+-              printk(KERN_ERR "LCCR3 mismatch: 0x%08x != 0x%08x\n",
+-                      shadow.lccr3, new_regs->lccr3);
+-              different = 1;
+-      }
+-      if (different) {
+-              printk(KERN_ERR "var: xres=%d hslen=%d lm=%d rm=%d\n",
+-                      var->xres, var->hsync_len,
+-                      var->left_margin, var->right_margin);
+-              printk(KERN_ERR "var: yres=%d vslen=%d um=%d bm=%d\n",
+-                      var->yres, var->vsync_len,
+-                      var->upper_margin, var->lower_margin);
+-
+-              printk(KERN_ERR "Please report this to Russell King "
+-                      "<rmk@arm.linux.org.uk>\n");
+-      }
+-
+-      DPRINTK("olccr0 = 0x%08x\n", shadow.lccr0);
+-      DPRINTK("olccr1 = 0x%08x\n", shadow.lccr1);
+-      DPRINTK("olccr2 = 0x%08x\n", shadow.lccr2);
+-      DPRINTK("olccr3 = 0x%08x\n", shadow.lccr3);
+-}
+-#else
+-#define sa1100fb_check_shadow(regs,var,pcd)
+-#endif
+-
+-
+-
+ /*
+  * IMHO this looks wrong.  In 8BPP, length should be 8.
+  */
+@@ -488,42 +262,56 @@
+ #endif
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+-static struct sa1100fb_mach_info h3600_info __initdata = {
+-#ifdef CONFIG_IPAQ_H3100
+-      pixclock:       407766,         bpp:            4,
++#ifdef CONFIG_SA1100_H3XXX
++static struct sa1100fb_mach_info h3800_info __initdata = {
++      pixclock:       174757,         bpp:            16,
+       xres:           320,            yres:           240,
+-      hsync_len:      26,             vsync_len:      41,
+-      left_margin:    4,              upper_margin:   0,
+-      right_margin:   4,              lower_margin:   0,
++      hsync_len:      3,              vsync_len:      3,
++      left_margin:    12,             upper_margin:   10,
++      right_margin:   17,             lower_margin:   1,
+-      sync:           FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+-      cmap_greyscale: 1,              cmap_static:    1,
+-      cmap_inverse:   1,
++      sync:           0,              cmap_static:    1,
+-      lccr0:          LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
+-      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+-#else
+-      pixclock:       174757,         bpp:            16,
++      lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++      lccr3:          LCCR3_ACBsCntOff | LCCR3_PixFlEdg | LCCR3_OutEnH,
++};
++
++static struct sa1100fb_mach_info h3600_info __initdata = {
++      pixclock:       174757,         bpp:            16,
+       xres:           320,            yres:           240,
+       hsync_len:      3,              vsync_len:      3,
+       left_margin:    12,             upper_margin:   10,
+       right_margin:   17,             lower_margin:   1,
+-      sync:           0,
++      sync:           0,              cmap_static:    1,
+       lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+-      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+-#endif
++      lccr3:          LCCR3_ACBsCntOff | LCCR3_OutEnH | LCCR3_PixFlEdg,
+ };
+ static struct sa1100fb_rgb h3600_rgb_16 = {
+       red:    { offset: 12, length: 4, },
+       green:  { offset: 7,  length: 4, },
+       blue:   { offset: 1,  length: 4, },
+-      transp: { offset: 0,  length: 0, },
++      transp: { offset: 0,  length: 0, },
++};
++
++static struct sa1100fb_mach_info h3100_info __initdata = {
++      pixclock:       406977,         bpp:            4,
++      xres:           320,            yres:           240,
++
++      hsync_len:      26,             vsync_len:      41,
++      left_margin:    4,              upper_margin:   0,
++      right_margin:   4,              lower_margin:   0,
++
++      sync:           FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
++      cmap_greyscale: 1,
++      cmap_inverse:   1,
++
++      lccr0:          LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
+ };
+ #endif
+@@ -618,6 +406,58 @@
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+ static struct sa1100fb_mach_info graphicsclient_info __initdata = {
++// for LQ64D343
++      pixclock:       53500,          bpp:            8,
++      xres:           640,            yres:           480,
++
++      hsync_len:      9,              vsync_len:      9,
++      left_margin:    54,             upper_margin:   24,
++      right_margin:   54,             lower_margin:   32,
++
++      sync:           0,
++
++      lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++static struct sa1100fb_mach_info graphicsmaster_info __initdata = {
++// for LQ64D343
++      pixclock:       53500,          bpp:            8,
++      xres:           640,            yres:           480,
++
++      hsync_len:      9,              vsync_len:      9,
++      left_margin:    54,             upper_margin:   24,
++      right_margin:   54,             lower_margin:   32,
++
++      sync:           0,
++
++      lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_ADSBITSY
++static struct sa1100fb_mach_info adsbitsy_info __initdata = {
++// for LQ64D343
++      pixclock:       53500,          bpp:            8,
++      xres:           640,            yres:           480,
++
++      hsync_len:      9,              vsync_len:      9,
++      left_margin:    54,             upper_margin:   24,
++      right_margin:   54,             lower_margin:   32,
++
++      sync:           0,
++
++      lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
++};
++#endif
++
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++static struct sa1100fb_mach_info adsbitsyplus_info __initdata = {
++// for LQ64D343
+       pixclock:       53500,          bpp:            8,
+       xres:           640,            yres:           480,
+@@ -699,7 +539,6 @@
+       lccr3:          LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
+ };
+ #endif
+-
+ #ifdef LART_KIT01_LCD
+ static struct sa1100fb_mach_info lart_kit01_info __initdata =
+ {
+@@ -707,7 +546,7 @@
+       xres:           640,            yres:           480,
+       hsync_len:      64,             vsync_len:      3,
+-      left_margin:    122,            upper_margin:   45,
++      left_margin:    122,    upper_margin:   45,
+       right_margin:   10,             lower_margin:   10,
+       sync:           0,
+@@ -717,6 +556,40 @@
+ };
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++static struct sa1100fb_mach_info frodo_kit01_info __initdata =
++{
++      /* best would be 41731 (25.8mhz), but we can only do 14.743mhz at 191.7mhz clock speed */
++      pixclock:               73030,          bpp:            16,
++      xres:                   640,            yres:           480,
++
++      hsync_len:              32,             vsync_len:              19,
++      left_margin:    120,    upper_margin:   33,
++      right_margin:   17,             lower_margin:   12,
++
++      sync:                   0,
++
++      lccr0:                  LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
++      lccr3:                  LCCR3_OutEnH | LCCR3_PixFlEdg
++};
++#endif
++
++#ifdef CONFIG_SA1100_SHANNON
++static struct sa1100fb_mach_info shannon_info __initdata = {
++      pixclock:       152500,         bpp:            8,
++      xres:           640,            yres:           480,
++
++      hsync_len:      4,              vsync_len:      3,
++      left_margin:    2,              upper_margin:   0,
++      right_margin:   1,              lower_margin:   0,
++
++      sync:           FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 
++
++      lccr0:          LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
++      lccr3:          LCCR3_ACBsDiv(512),
++};
++#endif
++
+ #ifdef CONFIG_SA1100_OMNIMETER
+ static struct sa1100fb_mach_info omnimeter_info __initdata = {
+       pixclock:       0,              bpp:            4,
+@@ -752,7 +625,24 @@
+       sync:           FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+       lccr0:          LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
+-      lccr3:          LCCR3_OutEnH | LCCR3_PixFlEdg,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsCntOff,
++};
++#endif
++
++#ifdef CONFIG_SA1100_SIMPUTER
++static struct sa1100fb_mach_info simputer_info __initdata = {
++      pixclock:       70000,          bpp:            4,
++      xres:           320,            yres:           240,
++
++      hsync_len:      9,              vsync_len:      2,
++      left_margin:    9,              upper_margin:   0,
++      right_margin:   2,              lower_margin:   0,
++
++      cmap_greyscale: 1,
++      sync:           FB_SYNC_HOR_HIGH_ACT |  FB_SYNC_VERT_HIGH_ACT ,
++
++      lccr0:          LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
++      lccr3:          LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(202),
+ };
+ #endif
+@@ -828,7 +718,6 @@
+ #endif
+-
+ static struct sa1100fb_mach_info * __init
+ sa1100fb_get_machine_info(struct sa1100fb_info *fbi)
+ {
+@@ -849,11 +738,17 @@
+ #endif
+       }
+ #endif
+-#ifdef CONFIG_SA1100_H3600
++#ifdef CONFIG_SA1100_H3XXX
+       if (machine_is_h3600()) {
+               inf = &h3600_info;
+               fbi->rgb[RGB_16] = &h3600_rgb_16;
+       }
++      if (machine_is_h3100()) {
++              inf = &h3100_info;
++      }
++      if (machine_is_h3800()) {
++              inf = &h3800_info;
++      }
+ #endif
+ #ifdef CONFIG_SA1100_BRUTUS
+       if (machine_is_brutus()) {
+@@ -876,6 +771,22 @@
+               inf = &graphicsclient_info;
+       }
+ #endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++      if (machine_is_graphicsmaster()) {
++              inf = &graphicsmaster_info;
++      }
++#endif
++#ifdef CONFIG_SA1100_ADSBITSY
++      if (machine_is_adsbitsy()) {
++              inf = &adsbitsy_info;
++      }
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++      if (machine_is_adsbitsyplus()) {
++              inf = &adsbitsyplus_info;
++              }
++      }
++#endif
+ #ifdef CONFIG_SA1100_HUW_WEBPANEL
+       if (machine_is_huw_webpanel()) {
+               inf = &huw_webpanel_info;
+@@ -897,6 +808,21 @@
+ #endif
+       }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++      if (machine_is_frodo()) {
++              inf = &frodo_kit01_info;
++      }
++#endif
++#ifdef CONFIG_SA1100_SHANNON
++      if (machine_is_shannon()) {
++              inf = &shannon_info;
++      }
++#endif
++#ifdef CONFIG_SA1100_SIMPUTER
++      if (machine_is_simputer()) {
++              inf = &simputer_info;
++      }
++#endif
+ #ifdef CONFIG_SA1100_OMNIMETER
+       if (machine_is_omnimeter()) {
+               inf = &omnimeter_info;
+@@ -1556,7 +1482,8 @@
+       unsigned int pcd;
+       if (pixclock) {
+-              pcd = get_cclk_frequency() * pixclock;
++              pcd = cpufreq_get(0) / 100;
++              pcd *= pixclock;
+               pcd /= 10000000;
+               pcd += 1;       /* make up for integer math truncations */
+       } else {
+@@ -1580,6 +1507,7 @@
+       return pcd;
+ }
++
+ /*
+  * sa1100fb_activate_var():
+  *    Configures LCD Controller based on entries in var parameter.  Settings are      
+@@ -1659,8 +1587,6 @@
+       if (pcd)
+               new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
+-      sa1100fb_check_shadow(&new_regs, var, pcd);
+-
+       DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
+       DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
+       DPRINTK("nlccr2 = 0x%08x\n", new_regs.lccr2);
+@@ -1733,6 +1659,10 @@
+       if (machine_is_omnimeter())
+               LEDBacklightOn();
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++      if (machine_is_frodo())
++              frodo_cpld_set (FRODO_CPLD_GENERAL,FRODO_LCD_BACKLIGHT);
++#endif
+ }
+ /*
+@@ -1755,6 +1685,10 @@
+       if (machine_is_omnimeter())
+               LEDBacklightOff();
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++      if (machine_is_frodo())
++              frodo_cpld_clear (FRODO_CPLD_GENERAL,FRODO_LCD_BACKLIGHT);
++#endif
+ }
+ static void sa1100fb_power_up_lcd(struct sa1100fb_info *fbi)
+@@ -1773,20 +1707,25 @@
+       if (machine_is_omnimeter())
+               LCDPowerOn();
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+-      if (machine_is_h3600()) {
+-              set_h3600_egpio(EGPIO_H3600_LCD_ON |
+-                              EGPIO_H3600_LCD_PCI |
+-                              EGPIO_H3600_LCD_5V_ON |
+-                              EGPIO_H3600_LVDD_ON);
+-      }
+-#endif
++      if (machine_is_h3xxx())
++              set_h3600_egpio( IPAQ_EGPIO_LCD_ON );     /* Turn on power to the LCD */
+ #ifdef CONFIG_SA1100_STORK
+       if (machine_is_stork()) {
+               storkSetLCDCPLD(0, 1);
+               storkSetLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+       }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++      if (machine_is_frodo())
++              sa1100fb_backlight_on(fbi);
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++      if (machine_is_adsbitsyplus()) {
++              ADS_CPLD_PCON &= ~ADS_PCON_PANEL_ON;
++              ADS_CPLD_SUPPC |= ADS_SUPPC_VEE_ON;
++      }
++#endif
++
+ }
+ static void sa1100fb_power_down_lcd(struct sa1100fb_info *fbi)
+@@ -1802,20 +1741,24 @@
+       if (machine_is_huw_webpanel())
+               BCR_set(BCR_TFT_NPWR);
+ #endif
+-#ifdef CONFIG_SA1100_H3600
+-      if (machine_is_h3600()) {
+-              clr_h3600_egpio(EGPIO_H3600_LCD_ON |
+-                              EGPIO_H3600_LCD_PCI |
+-                              EGPIO_H3600_LCD_5V_ON |
+-                              EGPIO_H3600_LVDD_ON);
+-      }
+-#endif
++      if (machine_is_h3xxx())
++              clr_h3600_egpio( IPAQ_EGPIO_LCD_ON );
+ #ifdef CONFIG_SA1100_STORK
+       if (machine_is_stork()) {
+               storkSetLCDCPLD(0, 0);
+               storkClearLatchA(STORK_LCD_BACKLIGHT_INVERTER_ON);
+       }
+ #endif
++#ifdef CONFIG_SA1100_FRODO
++      if (machine_is_frodo())
++              sa1100fb_backlight_off(fbi);
++#endif
++#ifdef CONFIG_SA1100_ADSBITSYPLUS
++      if (machine_is_adsbitsyplus()) {
++              ADS_CPLD_PCON |= ADS_PCON_PANEL_ON;
++              ADS_CPLD_SUPPC &= ~ADS_SUPPC_VEE_ON;
++      }
++#endif
+ }
+ static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
+@@ -1911,15 +1854,29 @@
+       LCCR0 |= LCCR0_LEN;
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#error Where is GPIO24 set as an output?  Can we fit this in somewhere else?
+       if (machine_is_graphicsclient()) {
+               // From ADS doc again...same as disable
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(20 * HZ / 1000);
+-              GPSR |= GPIO_GPIO24;
++              GPDR |= GPIO_GPIO24;
++              GPSR = GPIO_GPIO24;
++      }
++#endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++      if (machine_is_graphicsmaster()) {
++              // From ADS doc again...same as disable
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              schedule_timeout(20 * HZ / 1000);
++              GPDR |= GPIO_GPIO24;
++              GPSR = GPIO_GPIO24;
+       }
+ #endif
++      if (machine_is_shannon()) {
++              GPDR |= SHANNON_GPIO_DISP_EN;
++              GPSR |= SHANNON_GPIO_DISP_EN;
++      }       
++
+       DPRINTK("DBAR1 = %p\n", DBAR1);
+       DPRINTK("DBAR2 = %p\n", DBAR2);
+       DPRINTK("LCCR0 = 0x%08x\n", LCCR0);
+@@ -1935,7 +1892,6 @@
+       DPRINTK("Disabling LCD controller\n");
+ #ifdef CONFIG_SA1100_GRAPHICSCLIENT
+-#error Where is GPIO24 set as an output?  Can we fit this in somewhere else?
+       if (machine_is_graphicsclient()) {
+               /*
+                * From ADS internal document:
+@@ -1944,6 +1900,22 @@
+                *
+                * We'll wait 20msec.
+                */
++              GPDR |= GPIO_GPIO24;
++              GPCR |= GPIO_GPIO24;
++              set_current_state(TASK_UNINTERRUPTIBLE);
++              schedule_timeout(20 * HZ / 1000);
++      }
++#endif
++#ifdef CONFIG_SA1100_GRAPHICSMASTER
++      if (machine_is_graphicsmaster()) {
++              /*
++               * From ADS internal document:
++               *  GPIO24 should be LOW at least 10msec prior to disabling
++               *  the LCD interface.
++               *
++               * We'll wait 20msec.
++               */
++              GPDR |= GPIO_GPIO24;
+               GPCR |= GPIO_GPIO24;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(20 * HZ / 1000);
+@@ -1958,12 +1930,15 @@
+       }
+ #endif
++      if (machine_is_shannon()) {
++              GPCR |= SHANNON_GPIO_DISP_EN;
++      }       
++
+       add_wait_queue(&fbi->ctrlr_wait, &wait);
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       LCSR = 0xffffffff;      /* Clear LCD Status Register */
+       LCCR0 &= ~LCCR0_LDM;    /* Enable LCD Disable Done Interrupt */
+-      enable_irq(IRQ_LCD);    /* Enable LCD IRQ */
+       LCCR0 &= ~LCCR0_LEN;    /* Disable LCD Controller */
+       schedule_timeout(20 * HZ / 1000);
+@@ -2006,12 +1981,13 @@
+                * Disable controller for clock change.  If the
+                * controller is already disabled, then do nothing.
+                */
+-              if (old_state != C_DISABLE) {
++              if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
+                       fbi->state = state;
+                       sa1100fb_disable_controller(fbi);
+               }
+               break;
++      case C_DISABLE_PM:
+       case C_DISABLE:
+               /*
+                * Disable controller
+@@ -2050,6 +2026,16 @@
+               }
+               break;
++      case C_ENABLE_PM:
++              /*
++               * Re-enable the controller after PM.  This is not
++               * perfect - think about the case where we were doing
++               * a clock change, and we suspended half-way through.
++               */
++              if (old_state != C_DISABLE_PM)
++                      break;
++              /* fall through */
++
+       case C_ENABLE:
+               /*
+                * Power up the LCD screen, enable controller, and
+@@ -2162,10 +2148,10 @@
+               if (state == 0) {
+                       /* Enter D0. */
+-                      set_ctrlr_state(fbi, C_ENABLE);
++                      set_ctrlr_state(fbi, C_ENABLE_PM);
+               } else {
+                       /* Enter D1-D3.  Disable the LCD controller.  */
+-                      set_ctrlr_state(fbi, C_DISABLE);
++                      set_ctrlr_state(fbi, C_DISABLE_PM);
+               }
+       }
+       DPRINTK("done\n");
+@@ -2304,7 +2290,7 @@
+               goto failed;
+       ret = request_irq(IRQ_LCD, sa1100fb_handle_irq, SA_INTERRUPT,
+-                        fbi->fb.fix.id, fbi);
++                        "LCD", fbi);
+       if (ret) {
+               printk(KERN_ERR "sa1100fb: request_irq failed: %d\n", ret);
+               goto failed;
+--- linux-2.4.27/drivers/video/sa1100fb.h~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/sa1100fb.h
+@@ -127,6 +127,8 @@
+ #define C_DISABLE_CLKCHANGE   (2)
+ #define C_ENABLE_CLKCHANGE    (3)
+ #define C_REENABLE            (4)
++#define C_DISABLE_PM          (5)
++#define C_ENABLE_PM           (6)
+ #define SA1100_NAME   "SA1100"
+--- linux-2.4.27/drivers/video/vga16fb.c~2.4.27-vrs1
++++ linux-2.4.27/drivers/video/vga16fb.c
+@@ -142,7 +142,7 @@
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+       strcpy(fix->id,"VGA16 VGA");
+-      fix->smem_start = VGA_FB_PHYS;
++      fix->smem_start = VGA_MAP_MEM(VGA_FB_PHYS);
+       fix->smem_len = VGA_FB_PHYS_LEN;
+       fix->type = FB_TYPE_VGA_PLANES;
+       fix->visual = FB_VISUAL_PSEUDOCOLOR;
+@@ -896,7 +896,7 @@
+       /* XXX share VGA_FB_PHYS region with vgacon */
+-        vga16fb.video_vbase = ioremap(VGA_FB_PHYS, VGA_FB_PHYS_LEN);
++        vga16fb.video_vbase = ioremap(VGA_MAP_MEM(VGA_FB_PHYS), VGA_FB_PHYS_LEN);
+       if (!vga16fb.video_vbase) {
+               printk(KERN_ERR "vga16fb: unable to map device\n");
+               return -ENOMEM;
+--- linux-2.4.27/fs/adfs/dir.c~2.4.27-vrs1
++++ linux-2.4.27/fs/adfs/dir.c
+@@ -23,7 +23,7 @@
+ /*
+  * For future.  This should probably be per-directory.
+  */
+-static rwlock_t adfs_dir_lock;
++static rwlock_t adfs_dir_lock = RW_LOCK_UNLOCKED;
+ static int
+ adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+--- linux-2.4.27/fs/adfs/map.c~2.4.27-vrs1
++++ linux-2.4.27/fs/adfs/map.c
+@@ -13,24 +13,27 @@
+ #include <linux/adfs_fs.h>
+ #include <linux/spinlock.h>
++#include <asm/unaligned.h>
++
+ #include "adfs.h"
+ /*
+  * For the future...
+  */
+-static rwlock_t adfs_map_lock;
++static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED;
++/*
++ * This is fun.  We need to load up to 19 bits from the map at an
++ * arbitary bit alignment.  (We're limited to 19 bits by F+ version
++ * 2).
++ */
+ #define GET_FRAG_ID(_map,_start,_idmask)                              \
+       ({                                                              \
+-              unsigned long _v2, _frag;                               \
+-              unsigned int _tmp;                                      \
+-              _tmp = _start >> 5;                                     \
+-              _frag = le32_to_cpu(_map[_tmp]);                        \
+-              _v2   = le32_to_cpu(_map[_tmp + 1]);                    \
+-              _tmp = start & 31;                                      \
+-              _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp));         \
++              unsigned char *_m = _map + (_start >> 3);               \
++              u32 _frag = get_unaligned((u32 *)_m);                   \
++              _frag >>= (_start & 7);                                 \
+               _frag & _idmask;                                        \
+-      })
++      })      
+ /*
+  * return the map bit offset of the fragment frag_id in
+@@ -44,14 +47,13 @@
+           const unsigned int frag_id, unsigned int *offset)
+ {
+       const unsigned int mapsize = dm->dm_endbit;
+-      const unsigned int idmask = (1 << idlen) - 1;
+-      unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
++      const u32 idmask = (1 << idlen) - 1;
++      unsigned char *map = dm->dm_bh->b_data + 4;
+       unsigned int start = dm->dm_startbit;
+       unsigned int mapptr;
++      u32 frag;
+       do {
+-              unsigned long frag;
+-
+               frag = GET_FRAG_ID(map, start, idmask);
+               mapptr = start + idlen;
+@@ -59,15 +61,17 @@
+                * find end of fragment
+                */
+               {
+-                      unsigned long v2;
++                      u32 v, *_map = (u32 *)map;
+-                      while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
++                      v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
++                      while (v == 0) {
+                               mapptr = (mapptr & ~31) + 32;
+                               if (mapptr >= mapsize)
+                                       goto error;
++                              v = le32_to_cpu(_map[mapptr >> 5]);
+                       }
+-                      mapptr += 1 + ffz(~v2);
++                      mapptr += 1 + ffz(~v);
+               }
+               if (frag == frag_id)
+@@ -75,8 +79,11 @@
+ again:
+               start = mapptr;
+       } while (mapptr < mapsize);
++      return -1;
+ error:
++      printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
++              frag, start, mapptr);
+       return -1;
+ found:
+@@ -102,10 +109,10 @@
+       const unsigned int mapsize = dm->dm_endbit + 32;
+       const unsigned int idlen  = asb->s_idlen;
+       const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
+-      const unsigned int idmask = (1 << frag_idlen) - 1;
+-      unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
++      const u32 idmask = (1 << frag_idlen) - 1;
++      unsigned char *map = dm->dm_bh->b_data;
+       unsigned int start = 8, mapptr;
+-      unsigned long frag;
++      u32 frag;
+       unsigned long total = 0;
+       /*
+@@ -133,15 +140,17 @@
+                * find end of fragment
+                */
+               {
+-                      unsigned long v2;
++                      u32 v, *_map = (u32 *)map;
+-                      while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
++                      v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
++                      while (v == 0) {
+                               mapptr = (mapptr & ~31) + 32;
+                               if (mapptr >= mapsize)
+                                       goto error;
++                              v = le32_to_cpu(_map[mapptr >> 5]);
+                       }
+-                      mapptr += 1 + ffz(~v2);
++                      mapptr += 1 + ffz(~v);
+               }
+               total += mapptr - start;
+--- linux-2.4.27/fs/adfs/super.c~2.4.27-vrs1
++++ linux-2.4.27/fs/adfs/super.c
+@@ -386,6 +386,14 @@
+       sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
+       sb->u.adfs_sb.s_version  = dr->format_version;
+       sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
++
++      printk(KERN_DEBUG "ADFS: idlen %d map bit size %d sector size %d\n",
++              dr->idlen, 1 << dr->log2bpmb, 1 << dr->log2secsize);
++      printk(KERN_DEBUG "ADFS: map size %d map2blk %d version %d share size %d\n",
++              sb->u.adfs_sb.s_map_size,
++              sb->u.adfs_sb.s_map2blk,
++              sb->u.adfs_sb.s_version,
++              1 << sb->u.adfs_sb.s_log2sharesize);
+       
+       sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
+       if (!sb->u.adfs_sb.s_map)
+@@ -393,6 +401,8 @@
+       brelse(bh);
++      printk(KERN_DEBUG "ADFS: ids per zone %d\n", sb->u.adfs_sb.s_ids_per_zone);
++
+       /*
+        * set up enough so that we can read an inode
+        */
+--- linux-2.4.27/fs/binfmt_aout.c~2.4.27-vrs1
++++ linux-2.4.27/fs/binfmt_aout.c
+@@ -422,7 +422,11 @@
+       start_thread(regs, ex.a_entry, current->mm->start_stack);
+       if (current->ptrace & PT_PTRACED)
+               send_sig(SIGTRAP, current, 0);
++#ifndef __arm__
+       return 0;
++#else
++      return regs->ARM_r0;
++#endif
+ }
+ static int load_aout_library(struct file *file)
+@@ -452,8 +456,11 @@
+       /* For  QMAGIC, the starting address is 0x20 into the page.  We mask
+          this off to get the starting address for the page */
+-
+-      start_addr =  ex.a_entry & 0xfffff000;
++#ifndef __arm__
++      start_addr = ex.a_entry & 0xfffff000;
++#else
++      start_addr = ex.a_entry & 0xffff8000;
++#endif
+       if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+               static unsigned long error_time;
+--- linux-2.4.27/fs/binfmt_elf.c~2.4.27-vrs1
++++ linux-2.4.27/fs/binfmt_elf.c
+@@ -636,7 +636,6 @@
+       }
+       
+       current->mm->start_stack = bprm->p;
+-
+       /* Now we do a little grungy work by mmaping the ELF image into
+          the correct location in memory.  At this point, we assume that
+          the image should be loaded at fixed address, not at a variable
+--- linux-2.4.27/fs/exec.c~2.4.27-vrs1
++++ linux-2.4.27/fs/exec.c
+@@ -315,6 +315,7 @@
+       spin_unlock(&tsk->mm->page_table_lock);
+       /* no need for flush_tlb */
++      memc_update_addr(tsk->mm, *pte, address);
+       return;
+ out:
+       spin_unlock(&tsk->mm->page_table_lock);
+--- linux-2.4.27/fs/jffs/inode-v23.c~2.4.27-vrs1
++++ linux-2.4.27/fs/jffs/inode-v23.c
+@@ -10,7 +10,7 @@
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+- * $Id: inode-v23.c,v 1.70 2001/10/02 09:16:02 dwmw2 Exp $
++ * $Id: inode-v23.c,v 1.72 2002/01/31 11:42:57 cdavies Exp $
+  *
+  * Ported to Linux 2.3.x and MTD:
+  * Copyright (C) 2000  Alexander Larsson (alex@cendio.se), Cendio Systems AB
+@@ -48,6 +48,7 @@
+ #include <linux/stat.h>
+ #include <linux/blkdev.h>
+ #include <linux/quotaops.h>
++#include <linux/compatmac.h>
+ #include <asm/semaphore.h>
+ #include <asm/byteorder.h>
+ #include <asm/uaccess.h>
+@@ -58,6 +59,11 @@
+ #include "jffs_proc.h"
+ #endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2)
++#define minor(x) MINOR(x)
++#define major(x) MAJOR(x)
++#endif
++
+ static int jffs_remove(struct inode *dir, struct dentry *dentry, int type);
+ static struct super_operations jffs_ops;
+@@ -81,7 +87,7 @@
+       D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n",
+                 kdevname(dev)));
+-      if (MAJOR(dev) != MTD_BLOCK_MAJOR) {
++      if (major(dev) != MTD_BLOCK_MAJOR) {
+               printk(KERN_WARNING "JFFS: Trying to mount a "
+                      "non-mtd device.\n");
+               return 0;
+@@ -358,7 +364,7 @@
+       inode->i_nlink = raw_inode->nlink;
+       inode->i_uid = raw_inode->uid;
+       inode->i_gid = raw_inode->gid;
+-      inode->i_rdev = 0;
++      inode->i_rdev = NODEV;
+       inode->i_size = raw_inode->dsize;
+       inode->i_atime = raw_inode->atime;
+       inode->i_mtime = raw_inode->mtime;
+--- linux-2.4.27/fs/jffs/intrep.c~2.4.27-vrs1
++++ linux-2.4.27/fs/jffs/intrep.c
+@@ -10,7 +10,7 @@
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+- * $Id: intrep.c,v 1.102 2001/09/23 23:28:36 dwmw2 Exp $
++ * $Id: intrep.c,v 1.104 2002/03/05 14:07:39 dwmw2 Exp $
+  *
+  * Ported to Linux 2.3.x and MTD:
+  * Copyright (C) 2000  Alexander Larsson (alex@cendio.se), Cendio Systems AB
+@@ -772,6 +772,9 @@
+       __u32 free_chunk_size1;
+       __u32 free_chunk_size2;
++      __u32 largest_hole         = 0;
++      __u32 hole_end_offset      = 0;
++      __u32 head_offset;
+       
+ #define NUMFREEALLOWED     2        /* 2 chunks of at least erase size space allowed */
+       int num_free_space = 0;       /* Flag err if more than TWO
+@@ -884,6 +887,21 @@
+                                                         (unsigned int) start,
+                                                         (unsigned int)(test_start - start)));
++                                              D1(printk("Reducing start to 0x%x from 0x%x\n",
++                                                        test_start, start));
++                                              if (largest_hole < test_start - start){
++                                                      
++                                                      D3(printk("was hole = %x end_offset = %x\n",
++                                                                largest_hole, hole_end_offset));
++                                                      if (fmc->head) {
++                                                              largest_hole    = test_start - start;
++                                                              hole_end_offset = test_start;
++                                                      }
++                                              }
++
++                                              D3(printk("now = %x end_offset = %x\n",
++                                                      largest_hole, hole_end_offset));
++
+                                               /* below, space from "start" to "pos" will be marked dirty. */
+                                               start = test_start; 
+                                               
+@@ -956,6 +974,19 @@
+                                          num_free_space++;
+                                          D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+                                                    (unsigned int) start, (unsigned int) (pos - start)));
++
++                                         if (largest_hole < pos - start) {
++                                                 
++                                                 D3(printk("was hole = %x end_offset = %x\n",
++                                                        largest_hole, hole_end_offset));
++                                                 if (fmc->head){
++                                                         largest_hole    = pos - start;
++                                                         hole_end_offset = pos;
++                                                      }
++
++                                                 D3(printk("now = %x end_offset = %x\n",
++                                                        largest_hole, hole_end_offset));
++                                              }
+                                }else{
+                                        num_free_spc_not_accp++;
+                                        D1(printk("Free space (#%i) found but *Not* accepted: Starting "
+@@ -968,7 +999,7 @@
+                                                  (unsigned int) start, (unsigned int) (pos - start)));
+                                        jffs_fmalloced(fmc, (__u32) start,
+                                                       (__u32) (pos - start), 0);                                         
+-                               }
++                              }
+                                
+                       }
+                       if(num_free_space > NUMFREEALLOWED){
+@@ -1002,9 +1033,11 @@
+                          to scan for the magic pattern.  */
+                       D1(printk("*************** Dirty flash memory or "
+                                 "bad inode: "
+-                                "hexdump(pos = 0x%lx, len = 128):\n",
+-                                (long)pos));
+-                      D1(jffs_hexdump(fmc->mtd, pos, 128));
++                                "hexdump(pos = 0x%lx, len = %d):\n",
++                                (long)pos,
++                                end - pos > 128 ? 128 : end - pos));
++                      D1(jffs_hexdump(fmc->mtd, pos,
++                                      end - pos > 128 ? 128 : end - pos));
+                       for (pos += 4; pos < end; pos += 4) {
+                               switch (flash_read_u32(fmc->mtd, pos)) {
+@@ -1197,12 +1230,6 @@
+                               return -ENOMEM;
+                       }
+-                      if ((err = jffs_insert_node(c, 0, &raw_inode,
+-                                                  name, node)) < 0) {
+-                              printk("JFFS: Failed to handle raw inode. "
+-                                     "(err = %d)\n", err);
+-                              break;
+-                      }
+                       if (raw_inode.rename) {
+                               struct jffs_delete_list *dl
+                               = (struct jffs_delete_list *)
+@@ -1226,6 +1253,12 @@
+                               c->delete_list = dl;
+                               node->data_size = 0;
+                       }
++                      if ((err = jffs_insert_node(c, 0, &raw_inode,
++                                                  name, node)) < 0) {
++                              printk("JFFS: Failed to handle raw inode. "
++                                     "(err = %d)\n", err);
++                              break;
++                      }
+                       D3(jffs_print_node(node));
+                       node = 0; /* Don't free the node!  */
+               }
+@@ -1242,7 +1275,19 @@
+               jffs_free_node(node);
+               DJM(no_jffs_node--);
+       }
+-      jffs_build_end(fmc);
++      if (fmc->head && fmc->tail_extra &&
++          fmc->head->offset + fmc->flash_size -
++                      fmc->tail_extra->offset - fmc->tail_extra->size > largest_hole) {
++              head_offset = fmc->head->offset;
++      }
++      else {
++              head_offset = hole_end_offset;
++      }
++      
++      if (jffs_build_end(fmc, head_offset) < 0) {
++              D(printk("jffs_build_end() failed\n"));
++              return -ENOMEM;
++      }
+       /* Free read buffer */
+       kfree (read_buf);
+--- linux-2.4.27/fs/jffs/jffs_fm.c~2.4.27-vrs1
++++ linux-2.4.27/fs/jffs/jffs_fm.c
+@@ -10,7 +10,7 @@
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+- * $Id: jffs_fm.c,v 1.27 2001/09/20 12:29:47 dwmw2 Exp $
++ * $Id: jffs_fm.c,v 1.29 2002/01/22 09:48:16 cdavies Exp $
+  *
+  * Ported to Linux 2.3.x and MTD:
+  * Copyright (C) 2000  Alexander Larsson (alex@cendio.se), Cendio Systems AB
+@@ -20,8 +20,14 @@
+ #include <linux/slab.h>
+ #include <linux/blkdev.h>
+ #include <linux/jffs.h>
++#include <linux/compatmac.h>
+ #include "jffs_fm.h"
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2)
++#define minor(x) MINOR(x)
++#define major(x) MAJOR(x)
++#endif
++
+ #if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
+ static int jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset);
+ #endif
+@@ -46,7 +52,7 @@
+       }
+       DJM(no_jffs_fmcontrol++);
+-      mtd = get_mtd_device(NULL, MINOR(dev));
++      mtd = get_mtd_device(NULL, minor(dev));
+       if (!mtd) {
+               kfree(fmc);
+@@ -89,8 +95,8 @@
+ /* When the flash memory scan has completed, this function should be called
+    before use of the control structure.  */
+-void
+-jffs_build_end(struct jffs_fmcontrol *fmc)
++int
++jffs_build_end(struct jffs_fmcontrol *fmc, __u32 head_offset)
+ {
+       D3(printk("jffs_build_end()\n"));
+@@ -99,13 +105,100 @@
+               fmc->tail = fmc->tail_extra;
+       }
+       else if (fmc->head_extra) {
+-              fmc->tail_extra->next = fmc->head;
+-              fmc->head->prev = fmc->tail_extra;
+-              fmc->head = fmc->head_extra;
++              struct jffs_fm *fm, *cur;
++
++              if (head_offset == fmc->head->offset){
++                      fmc->tail->next = fmc->head_extra;
++                      fmc->head_extra->prev = fmc->tail;
++                      fmc->tail = fmc->tail_extra;
++              }
++              else {
++                      fmc->tail_extra->next = fmc->head;
++                      fmc->head->prev = fmc->tail_extra;
++                      fmc->head = fmc->head_extra;
++                      while (fmc->head->offset != head_offset){
++                              fmc->tail->next = fmc->head;
++                              fmc->head = fmc->head->next;
++                              fmc->head->prev = 0;
++                              fmc->tail->next->prev = fmc->tail;
++                              fmc->tail = fmc->tail->next;
++                              fmc->tail->next = 0;
++                      }
++              }
++                              /* Make sure the only free space we have is between tail and head.
++                               */
++              for (cur = fmc->head; cur && cur != fmc->tail;) {
++                      if (cur->offset + cur->size < cur->next->offset) {
++                              if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))) {
++                                      D(printk("jffs_buid_end(): kmalloc failed!\n"));
++                                      return -ENOMEM;
++                              }
++                              DJM(no_jffs_fm++);
++                              fm->size = cur->next->offset - cur->offset - cur->size;
++                              fm->offset = cur->offset + cur->size;
++                              fm->nodes = 0;
++                              fm->next = cur->next;
++                              fm->prev = cur;
++                              cur->next->prev = fm;
++                              cur->next = fm;
++                              cur = fm->next;
++                              fmc->free_size -= fm->size;
++                              fmc->dirty_size += fm->size;
++                      }
++                      else if (cur->offset > cur->next->offset) {
++                              if (cur->offset + cur->size < fmc->flash_size){
++                                      if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))){
++                                              
++                                              D(printk("jffs_buid_end(): kmalloc failed!\n"));
++                                              return -ENOMEM;
++                                      }
++                                      DJM(no_jffs_fm++);
++                                      fm->size = fmc->flash_size -
++                                                 cur->offset - cur->size;
++                                      fm->nodes = 0;
++                                      fm->offset = cur->offset + cur->size;
++                                      fm->next = cur->next;
++                                      fm->prev = cur;
++                                      cur->next->prev = fm;
++                                      cur->next = fm;
++                                      cur = fm->next;
++                                      fmc->free_size -= fm->size;
++                                      fmc->dirty_size += fm->size;
++                              }
++                              else {
++                                      cur = cur->next;
++                              }
++                              if (cur->offset > 0) {
++                                      
++                                      if (!(fm = kmalloc(sizeof(struct jffs_fm), GFP_KERNEL))) {
++                                              D(printk("jffs_buid_end(): kmalloc failed!\n"));
++                                              return -ENOMEM;
++                                      }
++                                      DJM(no_jffs_fm++);
++                                      fm->size = cur->offset;
++                                      fm->nodes = 0;
++                                      fm->offset = 0;
++                                      fm->next = cur;
++                                      fm->prev = cur->prev;
++                                      cur->prev->next = fm;
++                                      cur->prev = fm;
++                                      fmc->free_size -= fm->size;
++                                      fmc->dirty_size += fm->size;
++                              }
++                      }
++                      else if (cur->offset + cur->size != cur->next->offset) {
++                              printk("jffs_build_end(): Internal error.\n");
++                              return -EINVAL;
++                      }
++                      else {
++                              cur = cur->next;
++                      }
++              }
+       }
+       fmc->head_extra = 0; /* These two instructions should be omitted.  */
+       fmc->tail_extra = 0;
+       D3(jffs_print_fmcontrol(fmc));
++      return 0;
+ }
+--- linux-2.4.27/fs/jffs/jffs_fm.h~2.4.27-vrs1
++++ linux-2.4.27/fs/jffs/jffs_fm.h
+@@ -10,7 +10,7 @@
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+- * $Id: jffs_fm.h,v 1.13 2001/01/11 12:03:25 dwmw2 Exp $
++ * $Id: jffs_fm.h,v 1.14 2001/12/10 17:37:12 asanochkin Exp $
+  *
+  * Ported to Linux 2.3.x and MTD:
+  * Copyright (C) 2000  Alexander Larsson (alex@cendio.se), Cendio Systems AB
+@@ -123,7 +123,7 @@
+ struct jffs_fmcontrol *jffs_build_begin(struct jffs_control *c, kdev_t dev);
+-void jffs_build_end(struct jffs_fmcontrol *fmc);
++int jffs_build_end(struct jffs_fmcontrol *fmc, __u32 head_offset);
+ void jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc);
+ int jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size,
+--- linux-2.4.27/fs/partitions/Config.in~2.4.27-vrs1
++++ linux-2.4.27/fs/partitions/Config.in
+@@ -6,6 +6,7 @@
+    bool '  Acorn partition support' CONFIG_ACORN_PARTITION
+    if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then
+ #      bool '    Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA
++      bool '    EESOX partition support' CONFIG_ACORN_PARTITION_EESOX
+       bool '    ICS partition support' CONFIG_ACORN_PARTITION_ICS
+       bool '    Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS
+       bool '    PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC
+@@ -52,6 +53,7 @@
+       define_bool CONFIG_ACORN_PARTITION y
+       define_bool CONFIG_ACORN_PARTITION_ADFS y
+ #      define_bool CONFIG_ACORN_PARTITION_CUMANA y
++      define_bool CONFIG_ACORN_PARTITION_EESOX y
+       define_bool CONFIG_ACORN_PARTITION_ICS y
+       define_bool CONFIG_ACORN_PARTITION_POWERTEC y
+       define_bool CONFIG_ACORN_PARTITION_RISCIX y
+--- linux-2.4.27/fs/partitions/acorn.c~2.4.27-vrs1
++++ linux-2.4.27/fs/partitions/acorn.c
+@@ -7,7 +7,10 @@
+  * it under the terms of the GNU General Public License version 2 as
+  * published by the Free Software Foundation.
+  *
+- *  Scan ADFS partitions on hard disk drives.
++ *  Scan ADFS partitions on hard disk drives.  Unfortunately, there
++ *  isn't a standard for partitioning drives on Acorn machines, so
++ *  every single manufacturer of SCSI and IDE cards created their own
++ *  method.
+  */
+ #include <linux/config.h>
+ #include <linux/kernel.h>
+@@ -18,10 +21,18 @@
+ #include <linux/genhd.h>
+ #include <linux/fs.h>
+ #include <linux/pagemap.h>
++#include <linux/adfs_fs.h>
+ #include "check.h"
+ #include "acorn.h"
++/*
++ * Partition types. (Oh for reusability)
++ */
++#define PARTITION_RISCIX_MFM  1
++#define PARTITION_RISCIX_SCSI 2
++#define PARTITION_LINUX               9
++
+ static void
+ adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads)
+ {
+@@ -61,6 +72,21 @@
+ }
+ #ifdef CONFIG_ACORN_PARTITION_RISCIX
++
++struct riscix_part {
++      __u32   start;
++      __u32   length;
++      __u32   one;
++      char    name[16];
++};
++
++struct riscix_record {
++      __u32   magic;
++#define RISCIX_MAGIC  (0x4a657320)
++      __u32   date;
++      struct riscix_part part[8];
++};
++
+ static int
+ riscix_partition(struct gendisk *hd, struct block_device *bdev,
+               unsigned long first_sect, int minor, unsigned long nr_sects)
+@@ -102,6 +128,15 @@
+ }
+ #endif
++#define LINUX_NATIVE_MAGIC 0xdeafa1de
++#define LINUX_SWAP_MAGIC   0xdeafab1e
++
++struct linux_part {
++      __u32 magic;
++      __u32 start_sect;
++      __u32 nr_sects;
++};
++
+ static int
+ linux_partition(struct gendisk *hd, struct block_device *bdev,
+               unsigned long first_sect, int minor, unsigned long nr_sects)
+@@ -136,7 +171,7 @@
+ }
+ #ifdef CONFIG_ACORN_PARTITION_CUMANA
+-static int
++int
+ adfspart_check_CUMANA(struct gendisk *hd, struct block_device *bdev,
+                     unsigned long first_sector, int minor)
+ {
+@@ -147,7 +182,7 @@
+       int first = 1;
+       /*
+-       * Try Cumana style partitions - sector 3 contains ADFS boot block
++       * Try Cumana style partitions - sector 6 contains ADFS boot block
+        * with pointer to next 'drive'.
+        *
+        * There are unknowns in this code - is the 'cylinder number' of the
+@@ -163,13 +198,13 @@
+               struct adfs_discrecord *dr;
+               unsigned int nr_sects;
+-              if (!(minor & mask))
+-                      break;
+-
+               data = read_dev_sector(bdev, start_blk * 2 + 6, &sect);
+               if (!data)
+                       return -1;
++              if (!(minor & mask))
++                      break;
++
+               dr = adfs_partition(hd, name, data, first_sector, minor++);
+               if (!dr)
+                       break;
+@@ -229,7 +264,7 @@
+  *        hda1 = ADFS partition on first drive.
+  *        hda2 = non-ADFS partition.
+  */
+-static int
++int
+ adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
+                  unsigned long first_sector, int minor)
+ {
+@@ -282,11 +317,18 @@
+                       break;
+               }
+       }
++      printk("\n");
+       return 1;
+ }
+ #endif
+ #ifdef CONFIG_ACORN_PARTITION_ICS
++
++struct ics_part {
++      __u32 start;
++      __s32 size;
++};
++
+ static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block)
+ {
+       Sector sect;
+@@ -303,6 +345,22 @@
+ }
+ /*
++ * Check for a valid ICS partition using the checksum.
++ */
++static inline int valid_ics_sector(const unsigned char *data)
++{
++      unsigned long sum;
++      int i;
++
++      for (i = 0, sum = 0x50617274; i < 508; i++)
++              sum += data[i];
++
++      sum -= le32_to_cpu(*(__u32 *)(&data[508]));
++
++      return sum == 0;
++}
++
++/*
+  * Purpose: allocate ICS partitions.
+  * Params : hd                - pointer to gendisk structure to store partition info.
+  *        dev         - device number to access.
+@@ -314,15 +372,14 @@
+  *        hda2 = ADFS partition 1 on first drive.
+  *            ..etc..
+  */
+-static int
++int
+ adfspart_check_ICS(struct gendisk *hd, struct block_device *bdev,
+                  unsigned long first_sector, int minor)
+ {
++      const unsigned char *data;
++      const struct ics_part *p;
++      unsigned int mask = (1 << hd->minor_shift) - 1;
+       Sector sect;
+-      unsigned char *data;
+-      unsigned long sum;
+-      unsigned int i, mask = (1 << hd->minor_shift) - 1;
+-      struct ics_part *p;
+       /*
+        * Try ICS style partitions - sector 0 contains partition info.
+@@ -331,21 +388,14 @@
+       if (!data)
+               return -1;
+-      /*
+-       * check for a valid checksum
+-       */
+-      for (i = 0, sum = 0x50617274; i < 508; i++)
+-              sum += data[i];
+-
+-      sum -= le32_to_cpu(*(__u32 *)(&data[508]));
+-      if (sum) {
+-              put_dev_sector(sect);
+-              return 0; /* not ICS partition table */
++      if (!valid_ics_sector(data)) {
++              put_dev_sector(sect);
++              return 0;
+       }
+       printk(" [ICS]");
+-      for (p = (struct ics_part *)data; p->size; p++) {
++      for (p = (const struct ics_part *)data; p->size; p++) {
+               unsigned long start;
+               long size;
+@@ -355,12 +405,19 @@
+               start = le32_to_cpu(p->start);
+               size  = le32_to_cpu(p->size);
++              /*
++               * Negative sizes tell the RISC OS ICS driver to ignore
++               * this partition - in effect it says that this does not
++               * contain an ADFS filesystem.
++               */
+               if (size < 0) {
+                       size = -size;
+                       /*
+-                       * We use the first sector to identify what type
+-                       * this partition is...
++                       * Our own extension - We use the first sector
++                       * of the partition to identify what type this
++                       * partition is.  We must not make this visible
++                       * to the filesystem.
+                        */
+                       if (size > 1 && adfspart_check_ICSLinux(bdev, start)) {
+                               start += 1;
+@@ -375,10 +432,32 @@
+       }
+       put_dev_sector(sect);
++      printk("\n");
+       return 1;
+ }
+ #endif
++#ifdef CONFIG_ACORN_PARTITION_POWERTEC
++struct ptec_part {
++      __u32 unused1;
++      __u32 unused2;
++      __u32 start;
++      __u32 size;
++      __u32 unused5;
++      char type[8];
++};
++
++static inline int valid_ptec_sector(const unsigned char *data)
++{
++      unsigned char checksum = 0x2a;
++      int i;
++
++      for (i = 0; i < 511; i++)
++              checksum += data[i];
++
++      return checksum == data[511];
++}
++
+ /*
+  * Purpose: allocate ICS partitions.
+  * Params : hd                - pointer to gendisk structure to store partition info.
+@@ -391,32 +470,27 @@
+  *        hda2 = ADFS partition 1 on first drive.
+  *            ..etc..
+  */
+-#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+-static int
++int
+ adfspart_check_POWERTEC(struct gendisk *hd, struct block_device *bdev,
+                       unsigned long first_sector, int minor)
+ {
+       Sector sect;
+-      unsigned char *data;
+-      struct ptec_partition *p;
+-      unsigned char checksum;
++      const unsigned char *data;
++      const struct ptec_part *p;
+       int i;
+       data = read_dev_sector(bdev, 0, &sect);
+       if (!data)
+               return -1;
+-      for (checksum = 0x2a, i = 0; i < 511; i++)
+-              checksum += data[i];
+-
+-      if (checksum != data[511]) {
++      if (!valid_ptec_sector(data)) {
+               put_dev_sector(sect);
+               return 0;
+       }
+       printk(" [POWERTEC]");
+-      for (i = 0, p = (struct ptec_partition *)data; i < 12; i++, p++) {
++      for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) {
+               unsigned long start;
+               unsigned long size;
+@@ -430,49 +504,82 @@
+       }
+       put_dev_sector(sect);
++      printk("\n");
+       return 1;
+ }
+ #endif
+-static int (*partfn[])(struct gendisk *, struct block_device *, unsigned long, int) = {
+-#ifdef CONFIG_ACORN_PARTITION_ICS
+-      adfspart_check_ICS,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+-      adfspart_check_POWERTEC,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_CUMANA
+-      adfspart_check_CUMANA,
+-#endif
+-#ifdef CONFIG_ACORN_PARTITION_ADFS
+-      adfspart_check_ADFS,
+-#endif
+-      NULL
++#ifdef CONFIG_ACORN_PARTITION_EESOX
++struct eesox_part {
++      char    magic[6];
++      char    name[10];
++      u32     start;
++      u32     unused6;
++      u32     unused7;
++      u32     unused8;
+ };
++
+ /*
+- * Purpose: initialise all the partitions on an ADFS drive.
+- *          These may be other ADFS partitions or a Linux/RiscBSD/RISCiX
+- *        partition.
++ * Guess who created this format?
++ */
++static const char eesox_name[] = {
++      'N', 'e', 'i', 'l', ' ',
++      'C', 'r', 'i', 't', 'c', 'h', 'e', 'l', 'l', ' ', ' '
++};
++
++/*
++ * EESOX SCSI partition format.
+  *
+- * Params : hd                - pointer to gendisk structure
+- *          dev               - device number to access
+- *        first_sect  - first available sector on the disk.
+- *        first_minor - first available minor on this device.
++ * This is a goddamned awful partition format.  We don't seem to store
++ * the size of the partition in this table, only the start addresses.
+  *
+- * Returns: -1 on error, 0 if not ADFS format, 1 if ok.
++ * There are two possibilities where the size comes from:
++ *  1. The individual ADFS boot block entries that are placed on the disk.
++ *  2. The start address of the next entry.
+  */
+-int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+-                  unsigned long first_sect, int first_minor)
++int
++adfspart_check_EESOX(struct gendisk *hd, struct block_device *bdev,
++                   unsigned long first_sector, int minor)
+ {
++      Sector sect;
++      const unsigned char *data;
++      unsigned char buffer[256];
++      struct eesox_part *p;
++      u32 start = first_sector;
+       int i;
+-      for (i = 0; partfn[i]; i++) {
+-              int r = partfn[i](hd, bdev, first_sect, first_minor);
+-              if (r) {
+-                      if (r > 0)
+-                              printk("\n");
+-                      return r;
+-              }
++      data = read_dev_sector(bdev, 7, &sect);
++      if (!data)
++              return -1;
++
++      /*
++       * "Decrypt" the partition table.  God knows why...
++       */
++      for (i = 0; i < 256; i++)
++              buffer[i] = data[i] ^ eesox_name[i & 15];
++
++      put_dev_sector(sect);
++
++      for (i = 0, p = (struct eesox_part *)buffer; i < 8; i++, p++) {
++              u32 next;
++
++              if (memcmp(p->magic, "Eesox", 6))
++                      break;
++
++              next = le32_to_cpu(p->start) + first_sector;
++              if (i)
++                      add_gd_partition(hd, minor++, start, next - start);
++              start = next;
+       }
+-      return 0;
++
++      if (i != 0) {
++              unsigned long size;
++
++              size = hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects;
++              add_gd_partition(hd, minor++, start, size - start);
++              printk("\n");
++      }
++
++      return i ? 1 : 0;
+ }
++#endif
+--- linux-2.4.27/fs/partitions/acorn.h~2.4.27-vrs1
++++ linux-2.4.27/fs/partitions/acorn.h
+@@ -1,55 +1,28 @@
+ /*
+- * fs/partitions/acorn.h
++ * linux/fs/partitions/acorn.h
+  *
+- * Copyright (C) 1996-1998 Russell King
+- */
+-#include <linux/adfs_fs.h>
+-
+-/*
+- * Partition types. (Oh for reusability)
++ * Copyright (C) 1996-2001 Russell King.
++ *
++ *  I _hate_ this partitioning mess - why can't we have one defined
++ *  format, and everyone stick to it?
+  */
+-#define PARTITION_RISCIX_MFM  1
+-#define PARTITION_RISCIX_SCSI 2
+-#define PARTITION_LINUX               9
+-
+-struct riscix_part {
+-      __u32  start;
+-      __u32  length;
+-      __u32  one;
+-      char name[16];
+-};
+-struct riscix_record {
+-      __u32  magic;
+-#define RISCIX_MAGIC  (0x4a657320)
+-      __u32  date;
+-      struct riscix_part part[8];
+-};
+-
+-#define LINUX_NATIVE_MAGIC 0xdeafa1de
+-#define LINUX_SWAP_MAGIC   0xdeafab1e
+-
+-struct linux_part {
+-      __u32 magic;
+-      __u32 start_sect;
+-      __u32 nr_sects;
+-};
++int
++adfspart_check_CUMANA(struct gendisk *hd, struct block_device *bdev,
++                    unsigned long first_sector, int minor);
+-struct ics_part {
+-      __u32 start;
+-      __s32 size;
+-};
++int
++adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
++                 unsigned long first_sector, int minor);
+-struct ptec_partition {
+-      __u32 unused1;
+-      __u32 unused2;
+-      __u32 start;
+-      __u32 size;
+-      __u32 unused5;
+-      char type[8];
+-};
+-      
++int
++adfspart_check_ICS(struct gendisk *hd, struct block_device *bdev,
++                 unsigned long first_sector, int minor);
+-int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+-                 unsigned long first_sect, int first_minor);
++int
++adfspart_check_POWERTEC(struct gendisk *hd, struct block_device *bdev,
++                      unsigned long first_sector, int minor);
++int
++adfspart_check_EESOX(struct gendisk *hd, struct block_device *bdev,
++                   unsigned long first_sector, int minor);
+--- linux-2.4.27/fs/partitions/check.c~2.4.27-vrs1
++++ linux-2.4.27/fs/partitions/check.c
+@@ -40,8 +40,30 @@
+ int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
+ static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = {
+-#ifdef CONFIG_ACORN_PARTITION
+-      acorn_partition,
++      /*
++       * Probe partition formats with tables at disk address 0
++       * that also have an ADFS boot block at 0xdc0.
++       */
++#ifdef CONFIG_ACORN_PARTITION_ICS
++      adfspart_check_ICS,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_POWERTEC
++      adfspart_check_POWERTEC,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_EESOX
++      adfspart_check_EESOX,
++#endif
++
++      /*
++       * Now move on to formats that only have partition info at
++       * disk address 0xdc0.  These should come before MSDOS
++       * partition tables.
++       */
++#ifdef CONFIG_ACORN_PARTITION_CUMANA
++      adfspart_check_CUMANA,
++#endif
++#ifdef CONFIG_ACORN_PARTITION_ADFS
++      adfspart_check_ADFS,
+ #endif
+ #ifdef CONFIG_SGI_PARTITION
+       sgi_partition,
+@@ -79,13 +101,25 @@
+       NULL
+ };
++static char *raid_name (struct gendisk *hd, unsigned int unit, unsigned int part,
++                      int major_base, char *buf)
++{
++      int ctlr = hd->major - major_base;
++      if (part == 0)
++              sprintf(buf, "%s/c%dd%d", hd->major_name, ctlr, unit);
++      else
++              sprintf(buf, "%s/c%dd%dp%d", hd->major_name, ctlr, unit,
++                      part);
++      return buf;
++}
++
+ /*
+  *    This is ucking fugly but its probably the best thing for 2.4.x
+  *    Take it as a clear reminder that: 1) we should put the device name
+  *    generation in the object kdev_t points to in 2.5.
+  *    and 2) ioctls better work on half-opened devices.
+  */
+- 
++
+ #ifdef CONFIG_ARCH_S390
+ int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
+ int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
+@@ -104,10 +138,11 @@
+ char *disk_name (struct gendisk *hd, int minor, char *buf)
+ {
+       const char *maj = hd->major_name;
+-      unsigned int unit = (minor >> hd->minor_shift);
+-      unsigned int part = (minor & ((1 << hd->minor_shift) -1 ));
++      unsigned int unit = minor >> hd->minor_shift;
++      unsigned int part = minor & (( 1 << hd->minor_shift) - 1);
++      char *p;
+-      if ((unit < hd->nr_real) && hd->part[minor].de) {
++      if (unit < hd->nr_real && hd->part[minor].de) {
+               int pos;
+               pos = devfs_generate_path (hd->part[minor].de, buf, 64);
+@@ -153,51 +188,32 @@
+       }
+       if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
+               unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
+-              if (unit+'a' > 'z') {
+-                      unit -= 26;
+-                      sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26);
+-                      if (part)
+-                              sprintf(buf + 4, "%d", part);
+-                      return buf;
+-              }
+       }
+       if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
+-              int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
+-              if (part == 0)
+-                      sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+-              else
+-                      sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+-              return buf;
+-      }
++              return raid_name(hd, unit, part, COMPAQ_SMART2_MAJOR, buf);
++      }
+       if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) {
+-                int ctlr = hd->major - COMPAQ_CISS_MAJOR;
+-                if (part == 0)
+-                        sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+-                else
+-                        sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+-                return buf;
++              return raid_name(hd, unit, part, COMPAQ_CISS_MAJOR, buf);
+       }
+       if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) {
+-              int ctlr = hd->major - DAC960_MAJOR;
++              return raid_name(hd, unit, part, DAC960_MAJOR, buf);
++      }
++      if (hd->major == ATARAID_MAJOR) {
++              int disk = minor >> hd->minor_shift;
++              int part = minor & (( 1 << hd->minor_shift) - 1);
+               if (part == 0)
+-                      sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
++                      sprintf(buf, "%s/d%d", maj, disk);
+               else
+-                      sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
++                      sprintf(buf, "%s/d%dp%d", maj, disk, part);
+               return buf;
+       }
+-      if (hd->major == ATARAID_MAJOR) {
+-              int disk = minor >> hd->minor_shift;
+-              int part = minor & (( 1 << hd->minor_shift) - 1);
+-              if (part == 0)
+-                      sprintf(buf, "%s/d%d", maj, disk);
+-              else
+-                      sprintf(buf, "%s/d%dp%d", maj, disk, part);
+-              return buf;
+-      }
+-      if (part)
+-              sprintf(buf, "%s%c%d", maj, unit+'a', part);
++      p = buf;
++      if (unit <= 26)
++              p += sprintf(buf, "%s%c", maj, 'a' + unit);
+       else
+-              sprintf(buf, "%s%c", maj, unit+'a');
++              p += sprintf(buf, "%s%c%c", maj, 'a' + unit / 26, 'a' + unit % 26);
++      if (part)
++              sprintf(p, "%d", part);
+       return buf;
+ }
+@@ -207,7 +223,7 @@
+ void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
+ {
+ #ifndef CONFIG_DEVFS_FS
+-      char buf[40];
++      char buf[MAX_DISKNAME_LEN];
+ #endif
+       hd->part[minor].start_sect = start;
+@@ -229,7 +245,7 @@
+       static int first_time = 1;
+       unsigned long first_sector;
+       struct block_device *bdev;
+-      char buf[64];
++      char buf[MAX_DISKNAME_LEN];
+       int i;
+       if (first_time)
+--- linux-2.4.27/fs/proc/array.c~2.4.27-vrs1
++++ linux-2.4.27/fs/proc/array.c
+@@ -362,15 +362,15 @@
+               task->cmin_flt,
+               task->maj_flt,
+               task->cmaj_flt,
+-              task->times.tms_utime,
+-              task->times.tms_stime,
+-              task->times.tms_cutime,
+-              task->times.tms_cstime,
++              hz_to_std(task->times.tms_utime),
++              hz_to_std(task->times.tms_stime),
++              hz_to_std(task->times.tms_cutime),
++              hz_to_std(task->times.tms_cstime),
+               priority,
+               nice,
+               0UL /* removed */,
+               task->it_real_value,
+-              task->start_time,
++              hz_to_std(task->start_time),
+               vsize,
+               mm ? mm->rss : 0, /* you might want to shift this left 3 */
+               task->rlim[RLIMIT_RSS].rlim_cur,
+@@ -417,6 +417,7 @@
+               end = PMD_SIZE;
+       do {
+               pte_t page = *pte;
++              unsigned long pfn;
+               struct page *ptpage;
+               address += PAGE_SIZE;
+@@ -426,8 +427,11 @@
+               ++*total;
+               if (!pte_present(page))
+                       continue;
+-              ptpage = pte_page(page);
+-              if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
++              pfn = pte_pfn(page);
++              if (!pfn_valid(pfn))
++                      continue;
++              ptpage = pfn_to_page(pfn);
++              if (PageReserved(ptpage))
+                       continue;
+               ++*pages;
+               if (pte_dirty(page))
+@@ -609,14 +613,14 @@
+       len = sprintf(buffer,
+               "cpu  %lu %lu\n",
+-              task->times.tms_utime,
+-              task->times.tms_stime);
++              hz_to_std(task->times.tms_utime),
++              hz_to_std(task->times.tms_stime));
+               
+       for (i = 0 ; i < smp_num_cpus; i++)
+               len += sprintf(buffer + len, "cpu%d %lu %lu\n",
+                       i,
+-                      task->per_cpu_utime[cpu_logical_map(i)],
+-                      task->per_cpu_stime[cpu_logical_map(i)]);
++                      hz_to_std(task->per_cpu_utime[cpu_logical_map(i)]),
++                      hz_to_std(task->per_cpu_stime[cpu_logical_map(i)]));
+       return len;
+ }
+--- linux-2.4.27/fs/proc/proc_misc.c~2.4.27-vrs1
++++ linux-2.4.27/fs/proc/proc_misc.c
+@@ -308,16 +308,16 @@
+ {
+       int i, len = 0;
+       extern unsigned long total_forks;
+-      unsigned long jif = jiffies;
++      unsigned long jif = hz_to_std(jiffies);
+       unsigned int sum = 0, user = 0, nice = 0, system = 0;
+       int major, disk;
+       for (i = 0 ; i < smp_num_cpus; i++) {
+               int cpu = cpu_logical_map(i), j;
+-              user += kstat.per_cpu_user[cpu];
+-              nice += kstat.per_cpu_nice[cpu];
+-              system += kstat.per_cpu_system[cpu];
++              user += hz_to_std(kstat.per_cpu_user[cpu]);
++              nice += hz_to_std(kstat.per_cpu_nice[cpu]);
++              system += hz_to_std(kstat.per_cpu_system[cpu]);
+ #if !defined(CONFIG_ARCH_S390)
+               for (j = 0 ; j < NR_IRQS ; j++)
+                       sum += kstat.irqs[cpu][j];
+@@ -331,10 +331,10 @@
+               proc_sprintf(page, &off, &len,
+                       "cpu%d %u %u %u %lu\n",
+                       i,
+-                      kstat.per_cpu_user[cpu_logical_map(i)],
+-                      kstat.per_cpu_nice[cpu_logical_map(i)],
+-                      kstat.per_cpu_system[cpu_logical_map(i)],
+-                      jif - (  kstat.per_cpu_user[cpu_logical_map(i)] \
++                      hz_to_std(kstat.per_cpu_user[cpu_logical_map(i)]),
++                      hz_to_std(kstat.per_cpu_nice[cpu_logical_map(i)]),
++                      hz_to_std(kstat.per_cpu_system[cpu_logical_map(i)]),
++                      jif - hz_to_std(  kstat.per_cpu_user[cpu_logical_map(i)] \
+                                  + kstat.per_cpu_nice[cpu_logical_map(i)] \
+                                  + kstat.per_cpu_system[cpu_logical_map(i)]));
+       proc_sprintf(page, &off, &len,
+@@ -440,12 +440,14 @@
+       return proc_calc_metrics(page, start, off, count, eof, len);
+ }
++#ifdef CONFIG_GENERIC_ISA_DMA
+ static int dma_read_proc(char *page, char **start, off_t off,
+                                int count, int *eof, void *data)
+ {
+       int len = get_dma_list(page);
+       return proc_calc_metrics(page, start, off, count, eof, len);
+ }
++#endif
+ static int cmdline_read_proc(char *page, char **start, off_t off,
+                                int count, int *eof, void *data)
+@@ -614,7 +616,9 @@
+               {"interrupts",  interrupts_read_proc},
+ #endif
+               {"filesystems", filesystems_read_proc},
++#ifdef CONFIG_GENERIC_ISA_DMA
+               {"dma",         dma_read_proc},
++#endif
+               {"cmdline",     cmdline_read_proc},
+ #ifdef CONFIG_SGI_DS1286
+               {"rtc",         ds1286_read_proc},
+--- linux-2.4.27/fs/stat.c~2.4.27-vrs1
++++ linux-2.4.27/fs/stat.c
+@@ -26,7 +26,9 @@
+ }
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++    !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++    !defined(__x86_64__) && !defined(__mips__)
+ /*
+  * For backward compatibility?  Maybe this should be moved
+@@ -38,7 +40,7 @@
+       struct __old_kernel_stat tmp;
+       memset(&tmp, 0, sizeof(struct __old_kernel_stat));
+-      
++
+       if (warncount > 0) {
+               warncount--;
+               printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n",
+@@ -60,7 +62,7 @@
+ #if BITS_PER_LONG == 32
+       if (inode->i_size > MAX_NON_LFS)
+               return -EOVERFLOW;
+-#endif        
++#endif
+       tmp.st_size = inode->i_size;
+       tmp.st_atime = inode->i_atime;
+       tmp.st_mtime = inode->i_mtime;
+@@ -88,7 +90,7 @@
+ #if BITS_PER_LONG == 32
+       if (inode->i_size > MAX_NON_LFS)
+               return -EOVERFLOW;
+-#endif        
++#endif
+       tmp.st_size = inode->i_size;
+       tmp.st_atime = inode->i_atime;
+       tmp.st_mtime = inode->i_mtime;
+@@ -133,7 +135,9 @@
+ }
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++    !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++    !defined(__x86_64__) && !defined(__mips__)
+ /*
+  * For backward compatibility?  Maybe this should be moved
+  * into arch/i386 instead?
+@@ -169,7 +173,9 @@
+       return error;
+ }
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++    !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++    !defined(__x86_64__) && !defined(__mips__)
+ /*
+  * For backward compatibility?  Maybe this should be moved
+@@ -207,7 +213,9 @@
+       return error;
+ }
+-#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && \
++    !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__arm__) && \
++    !defined(__x86_64__) && !defined(__mips__)
+ /*
+  * For backward compatibility?  Maybe this should be moved
+--- linux-2.4.27/include/asm-alpha/ide.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-alpha/ide.h
+@@ -19,35 +19,15 @@
+ #define MAX_HWIFS     4
+ #endif
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+-      switch (base) {
+-              case 0x1f0: return 14;
+-              case 0x170: return 15;
+-              case 0x1e8: return 11;
+-              case 0x168: return 10;
+-              default:
+-                      return 0;
+-      }
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+-      switch (index) {
+-              case 0: return 0x1f0;
+-              case 1: return 0x170;
+-              case 2: return 0x1e8;
+-              case 3: return 0x168;
+-              default:
+-                      return 0;
+-      }
+-}
++#define ide_default_io_base(i)        ((ide_ioreg_t)0)
++#define ide_default_irq(b)    (0)
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+       ide_ioreg_t reg = data_port;
+       int i;
++      memset(hw, 0, sizeof(*hw));
+       for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+               hw->io_ports[i] = reg;
+               reg += 1;
+@@ -55,7 +35,7 @@
+       if (ctrl_port) {
+               hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+       } else {
+-              hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
++              hw->io_ports[IDE_CONTROL_OFFSET] = data_port + 0x206;
+       }
+       if (irq != NULL)
+               *irq = 0;
+@@ -70,13 +50,19 @@
+ {
+ #ifndef CONFIG_BLK_DEV_IDEPCI
+       hw_regs_t hw;
+-      int index;
+-      for (index = 0; index < MAX_HWIFS; index++) {
+-              ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+-              hw.irq = ide_default_irq(ide_default_io_base(index));
+-              ide_register_hw(&hw, NULL);
+-      }
++      ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
++      hw.irq = 14;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x170, 0x376, NULL);
++      hw.irq = 15;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x1e8, 0x3ee, NULL);
++      hw.irq = 11;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x168, 0x36e, NULL);
++      hw.irq = 10;
++      ide_register_hw(&hw, NULL);
+ #endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+--- linux-2.4.27/include/asm-alpha/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-alpha/param.h
+@@ -13,6 +13,9 @@
+ # else
+ #  define HZ  1200
+ # endif
++#ifdef __KERNEL__
++# define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 8192
+--- linux-2.4.27/include/asm-arm/arch-at91rm9200/AT91RM9200.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/AT91RM9200.h
+@@ -171,7 +171,7 @@
+ #define AT91C_PA23_TXD2               (AT91C_PIO_PA23) //  USART 2 Transmit Data
+ #define AT91C_PA23_IRQ3               (AT91C_PIO_PA23) //  Interrupt input 3
+ #define AT91C_PIO_PA24                (1 << 24)
+-#define AT91C_PA24_SCK2               (AT91C_PIO_PA24) //  USART2 Serial Clock
++#define AT91C_PA24_SCK2               (AT91C_PIO_PA24) //  USART 2 Serial Clock
+ #define AT91C_PA24_PCK1               (AT91C_PIO_PA24) //  PMC Programmable Clock Output 1
+ #define AT91C_PIO_PA25                (1 << 25)
+ #define AT91C_PA25_TWD                (AT91C_PIO_PA25) //  TWI Two-wire Serial Data
+@@ -260,7 +260,7 @@
+ #define AT91C_PIO_PB21                (1 << 21)
+ #define AT91C_PB21_RXD1               (AT91C_PIO_PB21) //  USART 1 Receive Data
+ #define AT91C_PIO_PB22                (1 << 22)
+-#define AT91C_PB22_SCK1               (AT91C_PIO_PB22) //  USART1 Serial Clock
++#define AT91C_PB22_SCK1               (AT91C_PIO_PB22) //  USART 1 Serial Clock
+ #define AT91C_PIO_PB23                (1 << 23)
+ #define AT91C_PB23_DCD1               (AT91C_PIO_PB23) //  USART 1 Data Carrier Detect
+ #define AT91C_PIO_PB24                (1 << 24)
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/AT91RM9200_MCI.h
+@@ -0,0 +1,127 @@
++// ----------------------------------------------------------------------------
++//          ATMEL Microcontroller Software Support  -  ROUSSET  -
++// ----------------------------------------------------------------------------
++//  The software is delivered "AS IS" without warranty or condition of any
++//  kind, either express, implied or statutory. This includes without
++//  limitation any warranty or condition with respect to merchantability or
++//  fitness for any particular purpose, or against the infringements of
++//  intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name           : AT91RM9200.h
++// Object              : AT91RM9200 / MCI definitions
++// Generated           : AT91 SW Application Group  12/03/2002 (10:48:02)
++// 
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_MCI_H
++#define AT91RM9200_MCI_H
++
++// *****************************************************************************
++//              SOFTWARE API DEFINITION  FOR Multimedia Card Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_MCI {
++      AT91_REG         MCI_CR;        // MCI Control Register
++      AT91_REG         MCI_MR;        // MCI Mode Register
++      AT91_REG         MCI_DTOR;      // MCI Data Timeout Register
++      AT91_REG         MCI_SDCR;      // MCI SD Card Register
++      AT91_REG         MCI_ARGR;      // MCI Argument Register
++      AT91_REG         MCI_CMDR;      // MCI Command Register
++      AT91_REG         Reserved0[2];  // 
++      AT91_REG         MCI_RSPR[4];   // MCI Response Register
++      AT91_REG         MCI_RDR;       // MCI Receive Data Register
++      AT91_REG         MCI_TDR;       // MCI Transmit Data Register
++      AT91_REG         Reserved1[2];  // 
++      AT91_REG         MCI_SR;        // MCI Status Register
++      AT91_REG         MCI_IER;       // MCI Interrupt Enable Register
++      AT91_REG         MCI_IDR;       // MCI Interrupt Disable Register
++      AT91_REG         MCI_IMR;       // MCI Interrupt Mask Register
++      AT91_REG         Reserved2[44];         // 
++      AT91_REG         MCI_RPR;       // Receive Pointer Register
++      AT91_REG         MCI_RCR;       // Receive Counter Register
++      AT91_REG         MCI_TPR;       // Transmit Pointer Register
++      AT91_REG         MCI_TCR;       // Transmit Counter Register
++      AT91_REG         MCI_RNPR;      // Receive Next Pointer Register
++      AT91_REG         MCI_RNCR;      // Receive Next Counter Register
++      AT91_REG         MCI_TNPR;      // Transmit Next Pointer Register
++      AT91_REG         MCI_TNCR;      // Transmit Next Counter Register
++      AT91_REG         MCI_PTCR;      // PDC Transfer Control Register
++      AT91_REG         MCI_PTSR;      // PDC Transfer Status Register
++} AT91S_MCI, *AT91PS_MCI;
++
++#endif
++
++// -------- MCI_CR : (MCI Offset: 0x0) MCI Control Register -------- 
++#define AT91C_MCI_MCIEN       ((unsigned int) 0x1 <<  0) // (MCI) Multimedia Interface Enable
++#define AT91C_MCI_MCIDIS      ((unsigned int) 0x1 <<  1) // (MCI) Multimedia Interface Disable
++#define AT91C_MCI_PWSEN       ((unsigned int) 0x1 <<  2) // (MCI) Power Save Mode Enable
++#define AT91C_MCI_PWSDIS      ((unsigned int) 0x1 <<  3) // (MCI) Power Save Mode Disable
++// -------- MCI_MR : (MCI Offset: 0x4) MCI Mode Register -------- 
++#define AT91C_MCI_CLKDIV      ((unsigned int) 0x1 <<  0) // (MCI) Clock Divider
++#define AT91C_MCI_PWSDIV      ((unsigned int) 0x1 <<  8) // (MCI) Power Saving Divider
++#define AT91C_MCI_PDCPADV     ((unsigned int) 0x1 << 14) // (MCI) PDC Padding Value
++#define AT91C_MCI_PDCMODE     ((unsigned int) 0x1 << 15) // (MCI) PDC Oriented Mode
++#define AT91C_MCI_BLKLEN      ((unsigned int) 0x1 << 18) // (MCI) Data Block Length
++// -------- MCI_DTOR : (MCI Offset: 0x8) MCI Data Timeout Register -------- 
++#define AT91C_MCI_DTOCYC      ((unsigned int) 0x1 <<  0) // (MCI) Data Timeout Cycle Number
++#define AT91C_MCI_DTOMUL      ((unsigned int) 0x7 <<  4) // (MCI) Data Timeout Multiplier
++#define       AT91C_MCI_DTOMUL_1                    ((unsigned int) 0x0 <<  4) // (MCI) DTOCYC x 1
++#define       AT91C_MCI_DTOMUL_16                   ((unsigned int) 0x1 <<  4) // (MCI) DTOCYC x 16
++#define       AT91C_MCI_DTOMUL_128                  ((unsigned int) 0x2 <<  4) // (MCI) DTOCYC x 128
++#define       AT91C_MCI_DTOMUL_256                  ((unsigned int) 0x3 <<  4) // (MCI) DTOCYC x 256
++#define       AT91C_MCI_DTOMUL_1024                 ((unsigned int) 0x4 <<  4) // (MCI) DTOCYC x 1024
++#define       AT91C_MCI_DTOMUL_4096                 ((unsigned int) 0x5 <<  4) // (MCI) DTOCYC x 4096
++#define       AT91C_MCI_DTOMUL_65536                ((unsigned int) 0x6 <<  4) // (MCI) DTOCYC x 65536
++#define       AT91C_MCI_DTOMUL_1048576              ((unsigned int) 0x7 <<  4) // (MCI) DTOCYC x 1048576
++// -------- MCI_SDCR : (MCI Offset: 0xc) MCI SD Card Register -------- 
++#define AT91C_MCI_SCDSEL      ((unsigned int) 0x1 <<  0) // (MCI) SD Card Selector
++#define AT91C_MCI_SCDBUS      ((unsigned int) 0x1 <<  7) // (MCI) SD Card Bus Width
++// -------- MCI_CMDR : (MCI Offset: 0x14) MCI Command Register -------- 
++#define AT91C_MCI_CMDNB       ((unsigned int) 0x1F <<  0) // (MCI) Command Number
++#define AT91C_MCI_RSPTYP      ((unsigned int) 0x3 <<  6) // (MCI) Response Type
++#define       AT91C_MCI_RSPTYP_NO                   ((unsigned int) 0x0 <<  6) // (MCI) No response
++#define       AT91C_MCI_RSPTYP_48                   ((unsigned int) 0x1 <<  6) // (MCI) 48-bit response
++#define       AT91C_MCI_RSPTYP_136                  ((unsigned int) 0x2 <<  6) // (MCI) 136-bit response
++#define AT91C_MCI_SPCMD       ((unsigned int) 0x7 <<  8) // (MCI) Special CMD
++#define       AT91C_MCI_SPCMD_NONE                 ((unsigned int) 0x0 <<  8) // (MCI) Not a special CMD
++#define       AT91C_MCI_SPCMD_INIT                 ((unsigned int) 0x1 <<  8) // (MCI) Initialization CMD
++#define       AT91C_MCI_SPCMD_SYNC                 ((unsigned int) 0x2 <<  8) // (MCI) Synchronized CMD
++#define       AT91C_MCI_SPCMD_IT_CMD               ((unsigned int) 0x4 <<  8) // (MCI) Interrupt command
++#define       AT91C_MCI_SPCMD_IT_REP               ((unsigned int) 0x5 <<  8) // (MCI) Interrupt response
++#define AT91C_MCI_OPDCMD      ((unsigned int) 0x1 << 11) // (MCI) Open Drain Command
++#define AT91C_MCI_MAXLAT      ((unsigned int) 0x1 << 12) // (MCI) Maximum Latency for Command to respond
++#define AT91C_MCI_TRCMD       ((unsigned int) 0x3 << 16) // (MCI) Transfer CMD
++#define       AT91C_MCI_TRCMD_NO                   ((unsigned int) 0x0 << 16) // (MCI) No transfer
++#define       AT91C_MCI_TRCMD_START                ((unsigned int) 0x1 << 16) // (MCI) Start transfer
++#define       AT91C_MCI_TRCMD_STOP                 ((unsigned int) 0x2 << 16) // (MCI) Stop transfer
++#define AT91C_MCI_TRDIR       ((unsigned int) 0x1 << 18) // (MCI) Transfer Direction
++#define AT91C_MCI_TRTYP       ((unsigned int) 0x3 << 19) // (MCI) Transfer Type
++#define       AT91C_MCI_TRTYP_BLOCK                ((unsigned int) 0x0 << 19) // (MCI) Block Transfer type
++#define       AT91C_MCI_TRTYP_MULTIPLE             ((unsigned int) 0x1 << 19) // (MCI) Multiple Block transfer type
++#define       AT91C_MCI_TRTYP_STREAM               ((unsigned int) 0x2 << 19) // (MCI) Stream transfer type
++// -------- MCI_SR : (MCI Offset: 0x40) MCI Status Register -------- 
++#define AT91C_MCI_CMDRDY      ((unsigned int) 0x1 <<  0) // (MCI) Command Ready flag
++#define AT91C_MCI_RXRDY       ((unsigned int) 0x1 <<  1) // (MCI) RX Ready flag
++#define AT91C_MCI_TXRDY       ((unsigned int) 0x1 <<  2) // (MCI) TX Ready flag
++#define AT91C_MCI_BLKE        ((unsigned int) 0x1 <<  3) // (MCI) Data Block Transfer Ended flag
++#define AT91C_MCI_DTIP        ((unsigned int) 0x1 <<  4) // (MCI) Data Transfer in Progress flag
++#define AT91C_MCI_NOTBUSY     ((unsigned int) 0x1 <<  5) // (MCI) Data Line Not Busy flag
++#define AT91C_MCI_ENDRX       ((unsigned int) 0x1 <<  6) // (MCI) End of RX Buffer flag
++#define AT91C_MCI_ENDTX       ((unsigned int) 0x1 <<  7) // (MCI) End of TX Buffer flag
++#define AT91C_MCI_RXBUFF      ((unsigned int) 0x1 << 14) // (MCI) RX Buffer Full flag
++#define AT91C_MCI_TXBUFE      ((unsigned int) 0x1 << 15) // (MCI) TX Buffer Empty flag
++#define AT91C_MCI_RINDE       ((unsigned int) 0x1 << 16) // (MCI) Response Index Error flag
++#define AT91C_MCI_RDIRE       ((unsigned int) 0x1 << 17) // (MCI) Response Direction Error flag
++#define AT91C_MCI_RCRCE       ((unsigned int) 0x1 << 18) // (MCI) Response CRC Error flag
++#define AT91C_MCI_RENDE       ((unsigned int) 0x1 << 19) // (MCI) Response End Bit Error flag
++#define AT91C_MCI_RTOE        ((unsigned int) 0x1 << 20) // (MCI) Response Time-out Error flag
++#define AT91C_MCI_DCRCE       ((unsigned int) 0x1 << 21) // (MCI) data CRC Error flag
++#define AT91C_MCI_DTOE        ((unsigned int) 0x1 << 22) // (MCI) Data timeout Error flag
++#define AT91C_MCI_OVRE        ((unsigned int) 0x1 << 30) // (MCI) Overrun flag
++#define AT91C_MCI_UNRE        ((unsigned int) 0x1 << 31) // (MCI) Underrun flag
++// -------- MCI_IER : (MCI Offset: 0x44) MCI Interrupt Enable Register -------- 
++// -------- MCI_IDR : (MCI Offset: 0x48) MCI Interrupt Disable Register -------- 
++// -------- MCI_IMR : (MCI Offset: 0x4c) MCI Interrupt Mask Register -------- 
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/AT91RM9200_SSC.h
+@@ -0,0 +1,129 @@
++// ----------------------------------------------------------------------------
++//          ATMEL Microcontroller Software Support  -  ROUSSET  -
++// ----------------------------------------------------------------------------
++//  The software is delivered "AS IS" without warranty or condition of any
++//  kind, either express, implied or statutory. This includes without
++//  limitation any warranty or condition with respect to merchantability or
++//  fitness for any particular purpose, or against the infringements of
++//  intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name           : AT91RM9200.h
++// Object              : AT91RM9200 / SSC definitions
++// Generated           : AT91 SW Application Group  12/03/2002 (10:48:02)
++// 
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_SSC_H
++#define AT91RM9200_SSC_H
++
++// *****************************************************************************
++//              SOFTWARE API DEFINITION  FOR Synchronous Serial Controller Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_SSC {
++      AT91_REG         SSC_CR;        // Control Register
++      AT91_REG         SSC_CMR;       // Clock Mode Register
++      AT91_REG         Reserved0[2];  // 
++      AT91_REG         SSC_RCMR;      // Receive Clock ModeRegister
++      AT91_REG         SSC_RFMR;      // Receive Frame Mode Register
++      AT91_REG         SSC_TCMR;      // Transmit Clock Mode Register
++      AT91_REG         SSC_TFMR;      // Transmit Frame Mode Register
++      AT91_REG         SSC_RHR;       // Receive Holding Register
++      AT91_REG         SSC_THR;       // Transmit Holding Register
++      AT91_REG         Reserved1[2];  // 
++      AT91_REG         SSC_RSHR;      // Receive Sync Holding Register
++      AT91_REG         SSC_TSHR;      // Transmit Sync Holding Register
++      AT91_REG         SSC_RC0R;      // Receive Compare 0 Register
++      AT91_REG         SSC_RC1R;      // Receive Compare 1 Register
++      AT91_REG         SSC_SR;        // Status Register
++      AT91_REG         SSC_IER;       // Interrupt Enable Register
++      AT91_REG         SSC_IDR;       // Interrupt Disable Register
++      AT91_REG         SSC_IMR;       // Interrupt Mask Register
++      AT91_REG         Reserved2[44];         // 
++      AT91_REG         SSC_RPR;       // Receive Pointer Register
++      AT91_REG         SSC_RCR;       // Receive Counter Register
++      AT91_REG         SSC_TPR;       // Transmit Pointer Register
++      AT91_REG         SSC_TCR;       // Transmit Counter Register
++      AT91_REG         SSC_RNPR;      // Receive Next Pointer Register
++      AT91_REG         SSC_RNCR;      // Receive Next Counter Register
++      AT91_REG         SSC_TNPR;      // Transmit Next Pointer Register
++      AT91_REG         SSC_TNCR;      // Transmit Next Counter Register
++      AT91_REG         SSC_PTCR;      // PDC Transfer Control Register
++      AT91_REG         SSC_PTSR;      // PDC Transfer Status Register
++} AT91S_SSC, *AT91PS_SSC;
++
++#endif
++
++// -------- SSC_CR : (SSC Offset: 0x0) SSC Control Register -------- 
++#define AT91C_SSC_RXEN        ( 0x1 <<  0) // (SSC) Receive Enable
++#define AT91C_SSC_RXDIS       ( 0x1 <<  1) // (SSC) Receive Disable
++#define AT91C_SSC_TXEN        ( 0x1 <<  8) // (SSC) Transmit Enable
++#define AT91C_SSC_TXDIS       ( 0x1 <<  9) // (SSC) Transmit Disable
++#define AT91C_SSC_SWRST       ( 0x1 << 15) // (SSC) Software Reset
++// -------- SSC_RCMR : (SSC Offset: 0x10) SSC Receive Clock Mode Register -------- 
++#define AT91C_SSC_CKS         ( 0x3 <<  0) // (SSC) Receive/Transmit Clock Selection
++#define       AT91C_SSC_CKS_DIV                  ( 0x0) // (SSC) Divided Clock
++#define       AT91C_SSC_CKS_TK                   ( 0x1) // (SSC) TK Clock signal
++#define       AT91C_SSC_CKS_RK                   ( 0x2) // (SSC) RK pin
++#define AT91C_SSC_CKO         ( 0x7 <<  2) // (SSC) Receive/Transmit Clock Output Mode Selection
++#define       AT91C_SSC_CKO_NONE                 ( 0x0 <<  2) // (SSC) Receive/Transmit Clock Output Mode: None RK pin: Input-only
++#define       AT91C_SSC_CKO_CONTINOUS            ( 0x1 <<  2) // (SSC) Continuous Receive/Transmit Clock RK pin: Output
++#define       AT91C_SSC_CKO_DATA_TX              ( 0x2 <<  2) // (SSC) Receive/Transmit Clock only during data transfers RK pin: Output
++#define AT91C_SSC_CKI         ( 0x1 <<  5) // (SSC) Receive/Transmit Clock Inversion
++#define AT91C_SSC_CKG         ( 0x3 <<  6) // (SSC) Receive/Transmit Clock Gating Selection
++#define       AT91C_SSC_CKG_NONE                 ( 0x0 <<  6) // (SSC) Receive/Transmit Clock Gating: None, continuous clock
++#define       AT91C_SSC_CKG_LOW                  ( 0x1 <<  6) // (SSC) Receive/Transmit Clock enabled only if RF Low
++#define       AT91C_SSC_CKG_HIGH                 ( 0x2 <<  6) // (SSC) Receive/Transmit Clock enabled only if RF High
++#define AT91C_SSC_START       ( 0xF <<  8) // (SSC) Receive/Transmit Start Selection
++#define       AT91C_SSC_START_CONTINOUS            ( 0x0 <<  8) // (SSC) Continuous, as soon as the receiver is enabled, and immediately after the end of transfer of the previous data.
++#define       AT91C_SSC_START_TX                   ( 0x1 <<  8) // (SSC) Transmit/Receive start
++#define       AT91C_SSC_START_LOW_RF               ( 0x2 <<  8) // (SSC) Detection of a low level on RF input
++#define       AT91C_SSC_START_HIGH_RF              ( 0x3 <<  8) // (SSC) Detection of a high level on RF input
++#define       AT91C_SSC_START_FALL_RF              ( 0x4 <<  8) // (SSC) Detection of a falling edge on RF input
++#define       AT91C_SSC_START_RISE_RF              ( 0x5 <<  8) // (SSC) Detection of a rising edge on RF input
++#define       AT91C_SSC_START_LEVEL_RF             ( 0x6 <<  8) // (SSC) Detection of any level change on RF input
++#define       AT91C_SSC_START_EDGE_RF              ( 0x7 <<  8) // (SSC) Detection of any edge on RF input
++#define       AT91C_SSC_START_0                    ( 0x8 <<  8) // (SSC) Compare 0
++#define AT91C_SSC_STOP        ( 0x1 << 12) // (SSC) Receive Stop Selection
++#define AT91C_SSC_STTOUT      ( 0x1 << 15) // (SSC) Receive/Transmit Start Output Selection
++#define AT91C_SSC_STTDLY      ( 0xFF << 16) // (SSC) Receive/Transmit Start Delay
++#define AT91C_SSC_PERIOD      ( 0xFF << 24) // (SSC) Receive/Transmit Period Divider Selection
++// -------- SSC_RFMR : (SSC Offset: 0x14) SSC Receive Frame Mode Register -------- 
++#define AT91C_SSC_DATLEN      ( 0x1F <<  0) // (SSC) Data Length
++#define AT91C_SSC_LOOP        ( 0x1 <<  5) // (SSC) Loop Mode
++#define AT91C_SSC_MSBF        ( 0x1 <<  7) // (SSC) Most Significant Bit First
++#define AT91C_SSC_DATNB       ( 0xF <<  8) // (SSC) Data Number per Frame
++#define AT91C_SSC_FSLEN       ( 0xF << 16) // (SSC) Receive/Transmit Frame Sync length
++#define AT91C_SSC_FSOS        ( 0x7 << 20) // (SSC) Receive/Transmit Frame Sync Output Selection
++#define       AT91C_SSC_FSOS_NONE                 ( 0x0 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: None RK pin Input-only
++#define       AT91C_SSC_FSOS_NEGATIVE             ( 0x1 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Negative Pulse
++#define       AT91C_SSC_FSOS_POSITIVE             ( 0x2 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Positive Pulse
++#define       AT91C_SSC_FSOS_LOW                  ( 0x3 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver Low during data transfer
++#define       AT91C_SSC_FSOS_HIGH                 ( 0x4 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Driver High during data transfer
++#define       AT91C_SSC_FSOS_TOGGLE               ( 0x5 << 20) // (SSC) Selected Receive/Transmit Frame Sync Signal: Toggling at each start of data transfer
++#define AT91C_SSC_FSEDGE      ( 0x1 << 24) // (SSC) Frame Sync Edge Detection
++// -------- SSC_TCMR : (SSC Offset: 0x18) SSC Transmit Clock Mode Register -------- 
++// -------- SSC_TFMR : (SSC Offset: 0x1c) SSC Transmit Frame Mode Register -------- 
++#define AT91C_SSC_DATDEF      ( 0x1 <<  5) // (SSC) Data Default Value
++#define AT91C_SSC_FSDEN       ( 0x1 << 23) // (SSC) Frame Sync Data Enable
++// -------- SSC_SR : (SSC Offset: 0x40) SSC Status Register -------- 
++#define AT91C_SSC_TXRDY       ( 0x1 <<  0) // (SSC) Transmit Ready
++#define AT91C_SSC_TXEMPTY     ( 0x1 <<  1) // (SSC) Transmit Empty
++#define AT91C_SSC_ENDTX       ( 0x1 <<  2) // (SSC) End Of Transmission
++#define AT91C_SSC_TXBUFE      ( 0x1 <<  3) // (SSC) Transmit Buffer Empty
++#define AT91C_SSC_RXRDY       ( 0x1 <<  4) // (SSC) Receive Ready
++#define AT91C_SSC_OVRUN       ( 0x1 <<  5) // (SSC) Receive Overrun
++#define AT91C_SSC_ENDRX       ( 0x1 <<  6) // (SSC) End of Reception
++#define AT91C_SSC_RXBUFF      ( 0x1 <<  7) // (SSC) Receive Buffer Full
++#define AT91C_SSC_CP0         ( 0x1 <<  8) // (SSC) Compare 0
++#define AT91C_SSC_CP1         ( 0x1 <<  9) // (SSC) Compare 1
++#define AT91C_SSC_TXSYN       ( 0x1 << 10) // (SSC) Transmit Sync
++#define AT91C_SSC_RXSYN       ( 0x1 << 11) // (SSC) Receive Sync
++#define AT91C_SSC_TXENA       ( 0x1 << 16) // (SSC) Transmit Enable
++#define AT91C_SSC_RXENA       ( 0x1 << 17) // (SSC) Receive Enable
++// -------- SSC_IER : (SSC Offset: 0x44) SSC Interrupt Enable Register -------- 
++// -------- SSC_IDR : (SSC Offset: 0x48) SSC Interrupt Disable Register -------- 
++// -------- SSC_IMR : (SSC Offset: 0x4c) SSC Interrupt Mask Register -------- 
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/AT91RM9200_TC.h
+@@ -0,0 +1,165 @@
++// ----------------------------------------------------------------------------
++//          ATMEL Microcontroller Software Support  -  ROUSSET  -
++// ----------------------------------------------------------------------------
++//  The software is delivered "AS IS" without warranty or condition of any
++//  kind, either express, implied or statutory. This includes without
++//  limitation any warranty or condition with respect to merchantability or
++//  fitness for any particular purpose, or against the infringements of
++//  intellectual property rights of others.
++// ----------------------------------------------------------------------------
++// File Name           : AT91RM9200.h
++// Object              : AT91RM9200 definitions
++// Generated           : AT91 SW Application Group  12/03/2002 (10:48:02)
++// 
++// ----------------------------------------------------------------------------
++
++#ifndef AT91RM9200_TC_H
++#define AT91RM9200_TC_H
++
++// *****************************************************************************
++//              SOFTWARE API DEFINITION  FOR Timer Counter Channel Interface
++// *****************************************************************************
++#ifndef __ASSEMBLY__
++
++typedef struct _AT91S_TC {
++      AT91_REG         TC_CCR;        // Channel Control Register
++      AT91_REG         TC_CMR;        // Channel Mode Register
++      AT91_REG         Reserved0[2];  // 
++      AT91_REG         TC_CV;         // Counter Value
++      AT91_REG         TC_RA;         // Register A
++      AT91_REG         TC_RB;         // Register B
++      AT91_REG         TC_RC;         // Register C
++      AT91_REG         TC_SR;         // Status Register
++      AT91_REG         TC_IER;        // Interrupt Enable Register
++      AT91_REG         TC_IDR;        // Interrupt Disable Register
++      AT91_REG         TC_IMR;        // Interrupt Mask Register
++} AT91S_TC, *AT91PS_TC;
++
++typedef struct _AT91S_TCB {
++      AT91S_TC         TCB_TC0;       // TC Channel 0
++      AT91_REG         Reserved0[4];  // 
++      AT91S_TC         TCB_TC1;       // TC Channel 1
++      AT91_REG         Reserved1[4];  // 
++      AT91S_TC         TCB_TC2;       // TC Channel 2
++      AT91_REG         Reserved2[4];  // 
++      AT91_REG         TCB_BCR;       // TC Block Control Register
++      AT91_REG         TCB_BMR;       // TC Block Mode Register
++} AT91S_TCB, *AT91PS_TCB;
++
++#endif
++
++// -------- TC_CCR : (TC Offset: 0x0) TC Channel Control Register -------- 
++#define AT91C_TC_CLKEN        ( 0x1 <<  0) // (TC) Counter Clock Enable Command
++#define AT91C_TC_CLKDIS       ( 0x1 <<  1) // (TC) Counter Clock Disable Command
++#define AT91C_TC_SWTRG        ( 0x1 <<  2) // (TC) Software Trigger Command
++// -------- TC_CMR : (TC Offset: 0x4) TC Channel Mode Register: Capture Mode / Waveform Mode -------- 
++#define AT91C_TC_TCCLKS       ( 0x7 <<  0) // (TC) Clock Selection
++#define               AT91C_TC_TIMER_DIV1_CLOCK             ( 0x0 <<  0) // (TC) MCK/2
++#define               AT91C_TC_TIMER_DIV2_CLOCK             ( 0x1 <<  0) // (TC) MCK/8
++#define               AT91C_TC_TIMER_DIV3_CLOCK             ( 0x2 <<  0) // (TC) MCK/32
++#define               AT91C_TC_TIMER_DIV4_CLOCK             ( 0x3 <<  0) // (TC) MCK/128
++#define               AT91C_TC_TIMER_DIV5_CLOCK             ( 0x4 <<  0) // (TC) MCK/256 = SLOW CLOCK
++#define               AT91C_TC_TIMER_XC0                    ( 0x5 <<  0) // (TC) XC0
++#define               AT91C_TC_TIMER_XC1                    ( 0x6 <<  0) // (TC) XC1
++#define               AT91C_TC_TIMER_XC2                    ( 0x7 <<  0) // (TC) XC2
++#define       AT91C_TC_CLKI         ( 0x1 <<  3) // (TC) Clock Invert
++#define AT91C_TC_BURST        ( 0x3 <<  4) // (TC) Burst Signal Selection
++#define AT91C_TC_CPCSTOP      ( 0x1 <<  6) // (TC) Counter Clock Stopped with RC Compare
++#define AT91C_TC_CPCDIS       ( 0x1 <<  7) // (TC) Counter Clock Disable with RC Compare
++#define AT91C_TC_EEVTEDG      ( 0x3 <<  8) // (TC) External Event Edge Selection
++#define       AT91C_TC_EEVTEDG_NONE                 ( 0x0 <<  8) // (TC) Edge: None
++#define       AT91C_TC_EEVTEDG_RISING               ( 0x1 <<  8) // (TC) Edge: rising edge
++#define       AT91C_TC_EEVTEDG_FALLING              ( 0x2 <<  8) // (TC) Edge: falling edge
++#define       AT91C_TC_EEVTEDG_BOTH                 ( 0x3 <<  8) // (TC) Edge: each edge
++#define AT91C_TC_EEVT         ( 0x3 << 10) // (TC) External Event  Selection
++#define       AT91C_TC_EEVT_NONE                 ( 0x0 << 10) // (TC) Signal selected as external event: TIOB TIOB direction: input
++#define       AT91C_TC_EEVT_RISING               ( 0x1 << 10) // (TC) Signal selected as external event: XC0 TIOB direction: output
++#define       AT91C_TC_EEVT_FALLING              ( 0x2 << 10) // (TC) Signal selected as external event: XC1 TIOB direction: output
++#define       AT91C_TC_EEVT_BOTH                 ( 0x3 << 10) // (TC) Signal selected as external event: XC2 TIOB direction: output
++#define AT91C_TC_ENETRG       ( 0x1 << 12) // (TC) External Event Trigger enable
++#define AT91C_TC_WAVESEL      ( 0x3 << 13) // (TC) Waveform  Selection
++#define       AT91C_TC_WAVESEL_UP                   ( 0x0 << 13) // (TC) UP mode without atomatic trigger on RC Compare
++#define       AT91C_TC_WAVESEL_UP_AUTO              ( 0x1 << 13) // (TC) UP mode with automatic trigger on RC Compare
++#define       AT91C_TC_WAVESEL_UPDOWN               ( 0x2 << 13) // (TC) UPDOWN mode without automatic trigger on RC Compare
++#define       AT91C_TC_WAVESEL_UPDOWN_AUTO          ( 0x3 << 13) // (TC) UPDOWN mode with automatic trigger on RC Compare
++#define AT91C_TC_CPCTRG       ( 0x1 << 14) // (TC) RC Compare Trigger Enable
++#define AT91C_TC_WAVE         ( 0x1 << 15) // (TC) 
++#define AT91C_TC_ACPA         ( 0x3 << 16) // (TC) RA Compare Effect on TIOA
++#define       AT91C_TC_ACPA_NONE                 ( 0x0 << 16) // (TC) Effect: none
++#define       AT91C_TC_ACPA_SET                  ( 0x1 << 16) // (TC) Effect: set
++#define       AT91C_TC_ACPA_CLEAR                ( 0x2 << 16) // (TC) Effect: clear
++#define       AT91C_TC_ACPA_TOGGLE               ( 0x3 << 16) // (TC) Effect: toggle
++#define AT91C_TC_ACPC         ( 0x3 << 18) // (TC) RC Compare Effect on TIOA
++#define       AT91C_TC_ACPC_NONE                 ( 0x0 << 18) // (TC) Effect: none
++#define       AT91C_TC_ACPC_SET                  ( 0x1 << 18) // (TC) Effect: set
++#define       AT91C_TC_ACPC_CLEAR                ( 0x2 << 18) // (TC) Effect: clear
++#define       AT91C_TC_ACPC_TOGGLE               ( 0x3 << 18) // (TC) Effect: toggle
++#define AT91C_TC_AEEVT        ( 0x3 << 20) // (TC) External Event Effect on TIOA
++#define       AT91C_TC_AEEVT_NONE                 ( 0x0 << 20) // (TC) Effect: none
++#define       AT91C_TC_AEEVT_SET                  ( 0x1 << 20) // (TC) Effect: set
++#define       AT91C_TC_AEEVT_CLEAR                ( 0x2 << 20) // (TC) Effect: clear
++#define       AT91C_TC_AEEVT_TOGGLE               ( 0x3 << 20) // (TC) Effect: toggle
++#define AT91C_TC_ASWTRG       ( 0x3 << 22) // (TC) Software Trigger Effect on TIOA
++#define       AT91C_TC_ASWTRG_NONE                 ( 0x0 << 22) // (TC) Effect: none
++#define       AT91C_TC_ASWTRG_SET                  ( 0x1 << 22) // (TC) Effect: set
++#define       AT91C_TC_ASWTRG_CLEAR                ( 0x2 << 22) // (TC) Effect: clear
++#define       AT91C_TC_ASWTRG_TOGGLE               ( 0x3 << 22) // (TC) Effect: toggle
++#define AT91C_TC_BCPB         ( 0x3 << 24) // (TC) RB Compare Effect on TIOB
++#define       AT91C_TC_BCPB_NONE                 ( 0x0 << 24) // (TC) Effect: none
++#define       AT91C_TC_BCPB_SET                  ( 0x1 << 24) // (TC) Effect: set
++#define       AT91C_TC_BCPB_CLEAR                ( 0x2 << 24) // (TC) Effect: clear
++#define       AT91C_TC_BCPB_TOGGLE               ( 0x3 << 24) // (TC) Effect: toggle
++#define AT91C_TC_BCPC         ( 0x3 << 26) // (TC) RC Compare Effect on TIOB
++#define       AT91C_TC_BCPC_NONE                 ( 0x0 << 26) // (TC) Effect: none
++#define       AT91C_TC_BCPC_SET                  ( 0x1 << 26) // (TC) Effect: set
++#define       AT91C_TC_BCPC_CLEAR                ( 0x2 << 26) // (TC) Effect: clear
++#define       AT91C_TC_BCPC_TOGGLE               ( 0x3 << 26) // (TC) Effect: toggle
++#define AT91C_TC_BEEVT        ( 0x3 << 28) // (TC) External Event Effect on TIOB
++#define       AT91C_TC_BEEVT_NONE                 ( 0x0 << 28) // (TC) Effect: none
++#define       AT91C_TC_BEEVT_SET                  ( 0x1 << 28) // (TC) Effect: set
++#define       AT91C_TC_BEEVT_CLEAR                ( 0x2 << 28) // (TC) Effect: clear
++#define       AT91C_TC_BEEVT_TOGGLE               ( 0x3 << 28) // (TC) Effect: toggle
++#define AT91C_TC_BSWTRG       ( 0x3 << 30) // (TC) Software Trigger Effect on TIOB
++#define       AT91C_TC_BSWTRG_NONE                 ( 0x0 << 30) // (TC) Effect: none
++#define       AT91C_TC_BSWTRG_SET                  ( 0x1 << 30) // (TC) Effect: set
++#define       AT91C_TC_BSWTRG_CLEAR                ( 0x2 << 30) // (TC) Effect: clear
++#define       AT91C_TC_BSWTRG_TOGGLE               ( 0x3 << 30) // (TC) Effect: toggle
++// -------- TC_SR : (TC Offset: 0x20) TC Channel Status Register -------- 
++#define AT91C_TC_COVFS        ( 0x1 <<  0) // (TC) Counter Overflow
++#define AT91C_TC_LOVRS        ( 0x1 <<  1) // (TC) Load Overrun
++#define AT91C_TC_CPAS         ( 0x1 <<  2) // (TC) RA Compare
++#define AT91C_TC_CPBS         ( 0x1 <<  3) // (TC) RB Compare
++#define AT91C_TC_CPCS         ( 0x1 <<  4) // (TC) RC Compare
++#define AT91C_TC_LDRAS        ( 0x1 <<  5) // (TC) RA Loading
++#define AT91C_TC_LDRBS        ( 0x1 <<  6) // (TC) RB Loading
++#define AT91C_TC_ETRCS        ( 0x1 <<  7) // (TC) External Trigger
++#define AT91C_TC_ETRGS        ( 0x1 << 16) // (TC) Clock Enabling
++#define AT91C_TC_MTIOA        ( 0x1 << 17) // (TC) TIOA Mirror
++#define AT91C_TC_MTIOB        ( 0x1 << 18) // (TC) TIOA Mirror
++// -------- TC_IER : (TC Offset: 0x24) TC Channel Interrupt Enable Register -------- 
++// -------- TC_IDR : (TC Offset: 0x28) TC Channel Interrupt Disable Register -------- 
++// -------- TC_IMR : (TC Offset: 0x2c) TC Channel Interrupt Mask Register -------- 
++
++// *****************************************************************************
++//              SOFTWARE API DEFINITION  FOR Timer Counter Interface
++// *****************************************************************************
++// -------- TCB_BCR : (TCB Offset: 0xc0) TC Block Control Register -------- 
++#define AT91C_TCB_SYNC        ( 0x1 <<  0) // (TCB) Synchro Command
++// -------- TCB_BMR : (TCB Offset: 0xc4) TC Block Mode Register -------- 
++#define AT91C_TCB_TC0XC0S     ( 0x1 <<  0) // (TCB) External Clock Signal 0 Selection
++#define       AT91C_TCB_TC0XC0S_TCLK0                ( 0x0) // (TCB) TCLK0 connected to XC0
++#define       AT91C_TCB_TC0XC0S_NONE                 ( 0x1) // (TCB) None signal connected to XC0
++#define       AT91C_TCB_TC0XC0S_TIOA1                ( 0x2) // (TCB) TIOA1 connected to XC0
++#define       AT91C_TCB_TC0XC0S_TIOA2                ( 0x3) // (TCB) TIOA2 connected to XC0
++#define AT91C_TCB_TC1XC1S     ( 0x1 <<  2) // (TCB) External Clock Signal 1 Selection
++#define       AT91C_TCB_TC1XC1S_TCLK1                ( 0x0 <<  2) // (TCB) TCLK1 connected to XC1
++#define       AT91C_TCB_TC1XC1S_NONE                 ( 0x1 <<  2) // (TCB) None signal connected to XC1
++#define       AT91C_TCB_TC1XC1S_TIOA0                ( 0x2 <<  2) // (TCB) TIOA0 connected to XC1
++#define       AT91C_TCB_TC1XC1S_TIOA2                ( 0x3 <<  2) // (TCB) TIOA2 connected to XC1
++#define AT91C_TCB_TC2XC2S     ( 0x1 <<  4) // (TCB) External Clock Signal 2 Selection
++#define       AT91C_TCB_TC2XC2S_TCLK2                ( 0x0 <<  4) // (TCB) TCLK2 connected to XC2
++#define       AT91C_TCB_TC2XC2S_NONE                 ( 0x1 <<  4) // (TCB) None signal connected to XC2
++#define       AT91C_TCB_TC2XC2S_TIOA0                ( 0x2 <<  4) // (TCB) TIOA0 connected to XC2
++#define       AT91C_TCB_TC2XC2S_TIOA2                ( 0x3 <<  4) // (TCB) TIOA2 connected to XC2
++
++#endif
+--- linux-2.4.27/include/asm-arm/arch-at91rm9200/at91rm9200dk.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/at91rm9200dk.h
+@@ -17,9 +17,9 @@
+ /* AT91RM92000 clocks */
+ #define AT91C_MAIN_CLOCK      179712000       /* from 18.432 MHz crystal (18432000 / 4 * 39) */
+-#define AT91C_MASTER_CLOCK    59904000        /* peripheral clock (AT91C_MASTER_CLOCK / 3) */
++#define AT91C_MASTER_CLOCK    59904000        /* peripheral clock (AT91C_MAIN_CLOCK / 3) */
+ #define AT91C_SLOW_CLOCK      32768           /* slow clock */
+-
++#define AT91_PLLB_INIT                0x1048be0e      /* (18.432 / 14 * 73) /2 = 47.9714  */
+ /* FLASH */
+ #define AT91_FLASH_BASE               0x10000000      // NCS0: Flash physical base address
+@@ -68,6 +68,7 @@
+ #define AT91_SMR_IRQ5 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ5)
+ #define AT91_SMR_IRQ6 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ6)
++#define AT91C_CONSOLE_DEFAULT_BAUDRATE 115200 /* default serial console baud-rate */
+ /*
+  * Serial port configuration.
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/csb337.h
+@@ -0,0 +1,82 @@
++/*
++ * linux/include/asm-arm/arch-at91rm9200/csb337.h
++ *
++ *  Copyright (c) 2003 Christopher Bahns & David Knickerbocker
++ *                     Polaroid Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++
++#ifndef __ASM_ARCH_HARDWARE_CSB337_H
++#define __ASM_ARCH_HARDWARE_CSB337_H
++
++
++/* AT91RM92000 clocks on CSB337 */
++#define AT91C_MAIN_CLOCK      184320000
++#define AT91C_MASTER_CLOCK    46080000        /* peripheral clock (AT91C_MAIN_CLOCK / 4) */
++#define AT91C_SLOW_CLOCK      32768           /* slow clock */
++#define AT91_PLLB_INIT                0x128a3e19      /* (3.6864 * (650+1) / 25) /2 = 47.9969 */
++
++/* FLASH */
++#define AT91_FLASH_BASE               0x10000000      // NCS0: Flash physical base address
++
++/* SDRAM */
++#define AT91_SDRAM_BASE               0x20000000      // NCS1: SDRAM physical base address
++
++/* SmartMedia */
++#define AT91_SMARTMEDIA_BASE  0x40000000      // NCS3: Smartmedia physical base address
++
++/* Multi-Master Memory controller */
++#define AT91_UHP_BASE         0x00300000      // USB Host controller
++
++
++/* Peripheral interrupt configuration */
++#define AT91_SMR_FIQ  (AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (FIQ)
++#define AT91_SMR_SYS  (AT91C_AIC_PRIOR_HIGHEST | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // System Peripheral
++#define AT91_SMR_PIOA (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Parallel IO Controller A
++#define AT91_SMR_PIOB (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Parallel IO Controller B
++#define AT91_SMR_PIOC (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Parallel IO Controller C
++#define AT91_SMR_PIOD (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Parallel IO Controller D
++#define AT91_SMR_US0  (AT91C_AIC_PRIOR_6       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USART 0
++#define AT91_SMR_US1  (AT91C_AIC_PRIOR_6       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USART 1
++#define AT91_SMR_US2  (AT91C_AIC_PRIOR_6       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USART 2
++#define AT91_SMR_US3  (AT91C_AIC_PRIOR_6       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USART 3
++#define AT91_SMR_MCI  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Multimedia Card Interface
++#define AT91_SMR_UDP  (AT91C_AIC_PRIOR_4       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USB Device Port
++#define AT91_SMR_TWI  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Two-Wire Interface
++#define AT91_SMR_SPI  (AT91C_AIC_PRIOR_6       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Serial Peripheral Interface
++#define AT91_SMR_SSC0 (AT91C_AIC_PRIOR_5       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Serial Synchronous Controller 0
++#define AT91_SMR_SSC1 (AT91C_AIC_PRIOR_5       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Serial Synchronous Controller 1
++#define AT91_SMR_SSC2 (AT91C_AIC_PRIOR_5       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Serial Synchronous Controller 2
++#define AT91_SMR_TC0  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 0
++#define AT91_SMR_TC1  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 1
++#define AT91_SMR_TC2  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 2
++#define AT91_SMR_TC3  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 3
++#define AT91_SMR_TC4  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 4
++#define AT91_SMR_TC5  (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Timer Counter 5
++#define AT91_SMR_UHP  (AT91C_AIC_PRIOR_3       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // USB Host port
++#define AT91_SMR_EMAC (AT91C_AIC_PRIOR_3       | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Ethernet MAC
++#define AT91_SMR_IRQ0 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ0)
++#define AT91_SMR_IRQ1 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ1)
++#define AT91_SMR_IRQ2 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ2)
++#define AT91_SMR_IRQ3 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ3)
++#define AT91_SMR_IRQ4 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ4)
++#define AT91_SMR_IRQ5 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ5)
++#define AT91_SMR_IRQ6 (AT91C_AIC_PRIOR_LOWEST  | AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE)       // Advanced Interrupt Controller (IRQ6)
++
++
++#define AT91C_CONSOLE_DEFAULT_BAUDRATE 38400
++
++/*
++ * Serial port configuration.
++ *    0 .. 3 = USART0 .. USART3
++ *    4      = DBGU
++ */
++#define AT91C_UART_MAP                { 4, 1, -1, -1, -1 }    /* ttyS0, ..., ttyS4 */
++#define AT91C_CONSOLE         0                       /* ttyS0 */
++
++#endif
+--- linux-2.4.27/include/asm-arm/arch-at91rm9200/hardware.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/hardware.h
+@@ -60,6 +60,7 @@
+ #define AT91C_BASE_SRAM               0x00200000      /* Internal SRAM base address */
++#define AT91C_SRAM_SIZE               0x00004000      /* Internal SRAM SIZE (16Kb) */
+ #define AT91C_NR_UART         5               /* 4 USART3's and one DBGU port */
+@@ -82,5 +83,8 @@
+ #include <asm/arch/at91rm9200dk.h>
+ #endif
++#ifdef CONFIG_MACH_CSB337
++#include <asm/arch/csb337.h>
++#endif
+ #endif
+--- linux-2.4.27/include/asm-arm/arch-at91rm9200/pio.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/pio.h
+@@ -17,7 +17,15 @@
+ static inline void AT91_CfgPIO_USART0(void) {
+       AT91_SYS->PIOA_PDR = AT91C_PA17_TXD0 | AT91C_PA18_RXD0
+-              | AT91C_PA20_CTS0 | AT91C_PA21_RTS0;
++              | AT91C_PA20_CTS0;
++
++      /*
++       * Errata #39 - RTS0 is not internally connected to PA21.  We need to drive
++       *  the pin manually.  Default is off (RTS is active low).
++       */
++      AT91_SYS->PIOA_PER = AT91C_PA21_RTS0;
++      AT91_SYS->PIOA_OER = AT91C_PA21_RTS0;
++      AT91_SYS->PIOA_SODR = AT91C_PA21_RTS0;
+ }
+ static inline void AT91_CfgPIO_USART1(void) {
+@@ -66,6 +74,18 @@
+ }
+ /*
++ * Configure interrupt from Ethernet PHY.
++ */
++static inline void AT91_CfgPIO_EMAC_PHY(void) {
++      AT91_SYS->PMC_PCER = 1 << AT91C_ID_PIOC;        /* enable peripheral clock */
++#ifdef CONFIG_MACH_CSB337
++      AT91_SYS->PIOC_ODR = AT91C_PIO_PC2;
++#else
++      AT91_SYS->PIOC_ODR = AT91C_PIO_PC4;
++#endif
++}
++
++/*
+  * Enable the Two-Wire interface.
+  */
+ static inline void AT91_CfgPIO_TWI(void) {
+--- linux-2.4.27/include/asm-arm/arch-at91rm9200/time.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-at91rm9200/time.h
+@@ -27,6 +27,21 @@
+ extern unsigned long (*gettimeoffset)(void);
+ /*
++ * The ST_CRTR is updated asynchronously to the master clock.  It is therefore
++ *  necessary to read it twice (with the same value) to ensure accuracy.
++ */ 
++static inline unsigned long read_CRTR(void) {
++      unsigned long x1, x2;
++
++      do {
++              x1 = AT91_SYS->ST_CRTR;
++              x2 = AT91_SYS->ST_CRTR;
++      } while (x1 != x2);
++
++      return x1;
++}
++
++/*
+  * Returns number of microseconds since last timer interrupt.  Note that interrupts
+  * will have been disabled by do_gettimeofday()
+  *  'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy.
+@@ -36,7 +51,7 @@
+ {
+       unsigned long elapsed;
+-      elapsed = (AT91_SYS->ST_CRTR - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV;
++      elapsed = (read_CRTR() - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV;
+       return (unsigned long)(elapsed * tick) / LATCH;
+ }
+@@ -48,11 +63,12 @@
+ {
+       if (AT91_SYS->ST_SR & AT91C_ST_PITS) {  /* This is a shared interrupt */
+               do {
++                      do_leds();
+                       do_timer(regs);
+                       AT91_SYS->ST_RTAR = (AT91_SYS->ST_RTAR + LATCH) & AT91C_ST_ALMV;
+-              } while (((AT91_SYS->ST_CRTR - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV) >= LATCH);
++              } while (((read_CRTR() - AT91_SYS->ST_RTAR) & AT91C_ST_ALMV) >= LATCH);
+               do_profile(regs);
+       }
+--- linux-2.4.27/include/asm-arm/arch-clps711x/system.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-clps711x/system.h
+@@ -28,8 +28,8 @@
+ {
+       clps_writel(1, HALT);
+       __asm__ __volatile__(
+-      "mov    r0, r0
+-      mov     r0, r0");
++      "mov    r0, r0\n\t"
++      "mov    r0, r0");
+ }
+ static inline void arch_reset(char mode)
+--- linux-2.4.27/include/asm-arm/arch-integrator/uncompress.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-integrator/uncompress.h
+@@ -18,6 +18,8 @@
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  */
++#include <asm/hardware/serial_amba.h>
++
+ #define AMBA_UART_DR  (*(volatile unsigned char *)0x16000000)
+ #define AMBA_UART_LCRH        (*(volatile unsigned char *)0x16000008)
+ #define AMBA_UART_LCRM        (*(volatile unsigned char *)0x1600000c)
+@@ -30,21 +32,25 @@
+  */
+ static void puts(const char *s)
+ {
++      /* Do nothing if the UART is not enabled. */
++      if (!(AMBA_UART_CR & AMBA_UARTCR_UARTEN))
++              return;
++
+       while (*s) {
+-              while (AMBA_UART_FR & (1 << 5))
++              while (AMBA_UART_FR & AMBA_UARTFR_TXFF)
+                       barrier();
+               AMBA_UART_DR = *s;
+               if (*s == '\n') {
+-                      while (AMBA_UART_FR & (1 << 5))
++                      while (AMBA_UART_FR & AMBA_UARTFR_TXFF)
+                               barrier();
+                       AMBA_UART_DR = '\r';
+               }
+               s++;
+       }
+-      while (AMBA_UART_FR & (1 << 3));
++      while (AMBA_UART_FR & AMBA_UARTFR_BUSY);
+ }
+ /*
+--- linux-2.4.27/include/asm-arm/arch-riscstation/system.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-riscstation/system.h
+@@ -1,5 +1,5 @@
+ /*
+- *  linux/include/asm-arm/arch-rpc/system.h
++ *  linux/include/asm-arm/arch-riscstation/system.h
+  *
+  *  Copyright (C) 1996-1999 Russell King.
+  *
+@@ -18,7 +18,7 @@
+ static inline void arch_reset(char mode)
+ {
+-      iomd_writeb(0, IOMD_ROMCR0);
++      iomd_writeb(0x40, IOMD_ROMCR0);
+       /*
+        * Jump into the ROM
+--- linux-2.4.27/include/asm-arm/arch-rpc/uncompress.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/arch-rpc/uncompress.h
+@@ -11,29 +11,12 @@
+  
+ #include <asm/hardware.h>
+ #include <asm/io.h>
++#include <asm/setup.h>
+ int video_num_columns, video_num_lines, video_size_row;
+ int white, bytes_per_char_h;
+ extern unsigned long con_charconvtable[256];
+-struct param_struct {
+-      unsigned long page_size;
+-      unsigned long nr_pages;
+-      unsigned long ramdisk_size;
+-      unsigned long mountrootrdonly;
+-      unsigned long rootdev;
+-      unsigned long video_num_cols;
+-      unsigned long video_num_rows;
+-      unsigned long video_x;
+-      unsigned long video_y;
+-      unsigned long memc_control_reg;
+-      unsigned char sounddefault;
+-      unsigned char adfsdrives;
+-      unsigned char bytes_per_char_h;
+-      unsigned char bytes_per_char_v;
+-      unsigned long unused[256/4-11];
+-};
+-
+ static const unsigned long palette_4[16] = {
+       0x00000000,
+       0x000000cc,
+@@ -69,8 +52,12 @@
+       unsigned char c;
+       char *ptr;
+-      x = params->video_x;
+-      y = params->video_y;
++      /* Don't bother when using a tagged list */
++      if (((struct tag *)params)->hdr.tag == ATAG_CORE)
++              return;
++
++      x = params->u1.s.video_x;
++      y = params->u1.s.video_y;
+       while ( ( c = *(unsigned char *)s++ ) != '\0' ) {
+               if ( c == '\n' ) {
+@@ -79,7 +66,7 @@
+                               y--;
+                       }
+               } else {
+-                      ptr = VIDMEM + ((y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h);
++                      ptr = VIDMEM + ((y*video_num_columns*params->u1.s.bytes_per_char_v+x)*bytes_per_char_h);
+                       ll_write_char(ptr, c, white);
+                       if ( ++x >= video_num_columns ) {
+                               x = 0;
+@@ -90,8 +77,6 @@
+               }
+       }
+-      params->video_x = x;
+-      params->video_y = y;
+ }
+ static void error(char *x);
+@@ -103,9 +88,13 @@
+ {
+       int i;
+       
+-      video_num_lines = params->video_num_rows;
+-      video_num_columns = params->video_num_cols;
+-      bytes_per_char_h = params->bytes_per_char_h;
++      /* Don't bother when using a tagged list */
++      if (((struct tag *)params)->hdr.tag == ATAG_CORE)
++              return;
++
++      video_num_lines = params->u1.s.video_num_rows;
++      video_num_columns = params->u1.s.video_num_cols;
++      bytes_per_char_h = params->u1.s.bytes_per_char_h;
+       video_size_row = video_num_columns * bytes_per_char_h;
+       if (bytes_per_char_h == 4)
+               for (i = 0; i < 256; i++)
+@@ -140,7 +129,7 @@
+               white = 7;
+       }
+-      if (params->nr_pages * params->page_size < 4096*1024) error("<4M of mem\n");
++      if (params->u1.s.nr_pages * params->u1.s.page_size < 4096*1024) error("<4M of mem\n");
+ }
+ #endif
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-clock.h
+@@ -0,0 +1,76 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-clock.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 clock register definitions
++ *
++ *  Changelog:
++ *    19-06-2003     BJD     Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_CLOCK_H
++#define ASMARM_ARCH_S3C2410_CLOCK_H
++
++#define S3C2410_CLKREG(x) ((x) + S3C2410_VA_CLKPWR)
++
++#define S3C2410_PLLVAL(_m,_p,_s) ((_m) << 12 | ((_p) << 4) | ((_s)))
++
++#define S3C2410_LOCKTIME    S3C2410_CLKREG(0x00)
++#define S3C2410_MPLLCON     S3C2410_CLKREG(0x04)
++#define S3C2410_UPLLCON     S3C2410_CLKREG(0x08)
++#define S3C2410_CLKCON      S3C2410_CLKREG(0x0C)
++#define S3C2410_CLKSLOW     S3C2410_CLKREG(0x10)
++#define S3C2410_CLKDIVN     S3C2410_CLKREG(0x14)
++
++#define S3C2410_PLLCON_MDIVSHIFT     12
++#define S3C2410_PLLCON_PDIVSHIFT     4
++#define S3C2410_PLLCON_SDIVSHIFT     0
++#define S3C2410_PLLCON_MDIVMASK      ((1<<(1+(19-12)))-1)
++#define S3C2410_PLLCON_PDIVMASK      ((1<<5)-1)
++#define S3C2410_PLLCON_SDIVMASK      3
++
++/* DCLKCON register addresses in gpio.h */
++
++#define S3C2410_DCLKCON_DCLK0EN      (1<<0)
++#define S3C2410_DCLKCON_DCLK0_PCLK   (0<<1)
++#define S3C2410_DCLKCON_DCLK0_UCLK   (1<<1)
++#define S3C2410_DCLKCON_DCLK0_DIV(x) (((x) - 1 )<<4)
++#define S3C2410_DCLKCON_DCLK0_CMP(x) (((x) - 1 )<<8)
++
++#define S3C2410_DCLKCON_DCLK1EN      (1<<16)
++#define S3C2410_DCLKCON_DCLK1_PCLK   (0<<17)
++#define S3C2410_DCLKCON_DCLK1_UCLK   (1<<17)
++#define S3C2410_DCLKCON_DCLK1_DIV(x) (((x) - 1) <<20)
++
++#define S3C2410_CLKDIVN_PDIVN        (1<<0)
++#define S3C2410_CLKDIVN_HDIVN        (1<<1)
++
++static inline unsigned int
++s3c2410_get_pll(int pllval, int baseclk)
++{
++  int mdiv, pdiv, sdiv;
++
++  mdiv = pllval >> S3C2410_PLLCON_MDIVSHIFT;
++  pdiv = pllval >> S3C2410_PLLCON_PDIVSHIFT;
++  sdiv = pllval >> S3C2410_PLLCON_SDIVSHIFT;
++
++  mdiv &= S3C2410_PLLCON_MDIVMASK;
++  pdiv &= S3C2410_PLLCON_PDIVMASK;
++  sdiv &= S3C2410_PLLCON_SDIVMASK;
++
++  return (baseclk * (mdiv + 8)) / ((pdiv + 2) << sdiv);
++}
++
++#endif /* ASMARM_ARCH_S3C2410_CLOCK_H */
++
++
++
++
++
++
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-gpio.h
+@@ -0,0 +1,602 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-gpio.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 GPIO register definitions
++ *
++ *  Changelog:
++ *    19-06-2003     BJD     Created file
++ *    23-06-2003     BJD     Updated GSTATUS registers
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_GPIO_H
++#define ASMARM_ARCH_S3C2410_GPIO_H
++
++/* configure GPIO ports A..G */
++
++#define S3C2410_GPIOREG(x) ((x) + S3C2410_VA_GPIO)
++
++/* port A - 22bits, zero in bit X makes pin X output
++ * 1 makes port special function, this is default
++*/
++#define S3C2410_GPACON     S3C2410_GPIOREG(0x00)
++#define S3C2410_GPADAT     S3C2410_GPIOREG(0x04)
++
++/* 0x08 and 0x0c are reserved */
++
++/* GPB is 10 IO pins, each configured by 2 bits each in GPBCON.
++ *   00 = input, 01 = output, 10=special function, 11=reserved
++ * bit 0,1 = pin 0, 2,3= pin 1...
++ *
++ * CPBUP = pull up resistor control, 1=disabled, 0=enabled
++*/
++
++#define S3C2410_GPBCON     S3C2410_GPIOREG(0x10)
++#define S3C2410_GPBDAT     S3C2410_GPIOREG(0x14)
++#define S3C2410_GPBUP      S3C2410_GPIOREG(0x18)
++
++/* no i/o pin in port b can have value 3! */
++
++#define S3C2410_GPB0_INP     (0x00 << 0)
++#define S3C2410_GPB0_OUTP    (0x01 << 0)
++#define S3C2410_GPB0_TOUT0   (0x02 << 0)
++
++#define S3C2410_GPB1_INP     (0x00 << 2)
++#define S3C2410_GPB1_OUTP    (0x01 << 2)
++#define S3C2410_GPB1_TOUT1   (0x02 << 2)
++
++#define S3C2410_GPB2_INP     (0x00 << 4)
++#define S3C2410_GPB2_OUTP    (0x01 << 4)
++#define S3C2410_GPB2_TOUT2   (0x02 << 4)
++
++#define S3C2410_GPB3_INP     (0x00 << 6)
++#define S3C2410_GPB3_OUTP    (0x01 << 6)
++#define S3C2410_GPB3_TOUT3   (0x02 << 6)
++
++#define S3C2410_GPB4_INP     (0x00 << 8)
++#define S3C2410_GPB4_OUTP    (0x01 << 8)
++#define S3C2410_GPB4_TCLK0   (0x02 << 8)
++#define S3C2410_GPB4_MASK    (0x03 << 8)
++
++#define S3C2410_GPB5_INP     (0x00 << 10)
++#define S3C2410_GPB5_OUTP    (0x01 << 10)
++#define S3C2410_GPB5_nXBACK  (0x02 << 10)
++
++#define S3C2410_GPB6_INP     (0x00 << 12)
++#define S3C2410_GPB6_OUTP    (0x01 << 12)
++#define S3C2410_GPB6_nXBREQ  (0x02 << 12)
++
++#define S3C2410_GPB7_INP     (0x00 << 14)
++#define S3C2410_GPB7_OUTP    (0x01 << 14)
++#define S3C2410_GPB7_nXDACK1 (0x02 << 14)
++
++#define S3C2410_GPB8_INP     (0x00 << 16)
++#define S3C2410_GPB8_OUTP    (0x01 << 16)
++#define S3C2410_GPB8_nXDREQ1 (0x02 << 16)
++
++#define S3C2410_GPB9_INP     (0x00 << 18)
++#define S3C2410_GPB9_OUTP    (0x01 << 18)
++#define S3C2410_GPB9_nXDACK0 (0x02 << 18)
++
++#define S3C2410_GPB10_INP     (0x00 << 18)
++#define S3C2410_GPB10_OUTP    (0x01 << 18)
++#define S3C2410_GPB10_nXDRE0 (0x02 << 18)
++
++/* Port C consits of 16 GPIO/Special function
++ *
++ * almost identical setup to port b, but the special functions are mostly
++ * to do with the video system's sync/etc.
++*/
++
++#define S3C2410_GPCCON     S3C2410_GPIOREG(0x20)
++#define S3C2410_GPCDAT     S3C2410_GPIOREG(0x24)
++#define S3C2410_GPCUP      S3C2410_GPIOREG(0x28)
++
++#define S3C2410_GPC0_INP        (0x00 << 0)
++#define S3C2410_GPC0_OUTP       (0x01 << 0)
++#define S3C2410_GPC0_LEND       (0x02 << 0)
++
++#define S3C2410_GPC1_INP        (0x00 << 2)
++#define S3C2410_GPC1_OUTP       (0x01 << 2)
++#define S3C2410_GPC1_VCLK       (0x02 << 2)
++
++#define S3C2410_GPC2_INP        (0x00 << 4)
++#define S3C2410_GPC2_OUTP       (0x01 << 4)
++#define S3C2410_GPC2_VLINE      (0x02 << 4)
++
++#define S3C2410_GPC3_INP        (0x00 << 6)
++#define S3C2410_GPC3_OUTP       (0x01 << 6)
++#define S3C2410_GPC3_VFRAME     (0x02 << 6)
++
++#define S3C2410_GPC4_INP        (0x00 << 8)
++#define S3C2410_GPC4_OUTP       (0x01 << 8)
++#define S3C2410_GPC4_VM         (0x02 << 8)
++
++#define S3C2410_GPC5_INP        (0x00 << 10)
++#define S3C2410_GPC5_OUTP       (0x01 << 10)
++#define S3C2410_GPC5_LCDVF0     (0x02 << 10)
++
++#define S3C2410_GPC6_INP        (0x00 << 12)
++#define S3C2410_GPC6_OUTP       (0x01 << 12)
++#define S3C2410_GPC6_LCDVF1     (0x02 << 12)
++
++#define S3C2410_GPC7_INP        (0x00 << 14)
++#define S3C2410_GPC7_OUTP       (0x01 << 14)
++#define S3C2410_GPC7_LCDVF2     (0x02 << 14)
++
++#define S3C2410_GPC8_INP        (0x00 << 16)
++#define S3C2410_GPC8_OUTP       (0x01 << 16)
++#define S3C2410_GPC8_VD0        (0x02 << 16)
++
++#define S3C2410_GPC9_INP        (0x00 << 18)
++#define S3C2410_GPC9_OUTP       (0x01 << 18)
++#define S3C2410_GPC9_VD1        (0x02 << 18)
++
++#define S3C2410_GPC10_INP       (0x00 << 20)
++#define S3C2410_GPC10_OUTP      (0x01 << 20)
++#define S3C2410_GPC10_VD2       (0x02 << 20)
++
++#define S3C2410_GPC11_INP       (0x00 << 22)
++#define S3C2410_GPC11_OUTP      (0x01 << 22)
++#define S3C2410_GPC11_VD3       (0x02 << 22)
++
++#define S3C2410_GPC12_INP       (0x00 << 24)
++#define S3C2410_GPC12_OUTP      (0x01 << 24)
++#define S3C2410_GPC12_VD4       (0x02 << 24)
++
++#define S3C2410_GPC13_INP       (0x00 << 26)
++#define S3C2410_GPC13_OUTP      (0x01 << 26)
++#define S3C2410_GPC13_VD5       (0x02 << 26)
++
++#define S3C2410_GPC14_INP       (0x00 << 28)
++#define S3C2410_GPC14_OUTP      (0x01 << 28)
++#define S3C2410_GPC14_VD6       (0x02 << 28)
++
++#define S3C2410_GPC15_INP       (0x00 << 30)
++#define S3C2410_GPC15_OUTP      (0x01 << 30)
++#define S3C2410_GPC15_VD7       (0x02 << 30)
++
++/* Port D consists of 16 GPIO/Special function
++ *
++ * almost identical setup to port b, but the special functions are mostly
++ * to do with the video system's data.
++*/
++
++#define S3C2410_GPDCON     S3C2410_GPIOREG(0x30)
++#define S3C2410_GPDDAT     S3C2410_GPIOREG(0x34)
++#define S3C2410_GPDUP      S3C2410_GPIOREG(0x38)
++
++#define S3C2410_GPD0_INP        (0x00 << 0)
++#define S3C2410_GPD0_OUTP       (0x01 << 0)
++#define S3C2410_GPD0_VD8        (0x02 << 0)
++
++#define S3C2410_GPD1_INP        (0x00 << 2)
++#define S3C2410_GPD1_OUTP       (0x01 << 2)
++#define S3C2410_GPD1_VD9        (0x02 << 2)
++
++#define S3C2410_GPD2_INP        (0x00 << 4)
++#define S3C2410_GPD2_OUTP       (0x01 << 4)
++#define S3C2410_GPD2_VD10       (0x02 << 4)
++
++#define S3C2410_GPD3_INP        (0x00 << 6)
++#define S3C2410_GPD3_OUTP       (0x01 << 6)
++#define S3C2410_GPD3_VD11       (0x02 << 6)
++
++#define S3C2410_GPD4_INP        (0x00 << 8)
++#define S3C2410_GPD4_OUTP       (0x01 << 8)
++#define S3C2410_GPD4_VD12       (0x02 << 8)
++
++#define S3C2410_GPD5_INP        (0x00 << 10)
++#define S3C2410_GPD5_OUTP       (0x01 << 10)
++#define S3C2410_GPD5_VD13       (0x02 << 10)
++
++#define S3C2410_GPD6_INP        (0x00 << 12)
++#define S3C2410_GPD6_OUTP       (0x01 << 12)
++#define S3C2410_GPD6_VD14       (0x02 << 12)
++
++#define S3C2410_GPD7_INP        (0x00 << 14)
++#define S3C2410_GPD7_OUTP       (0x01 << 14)
++#define S3C2410_GPD7_VD15       (0x02 << 14)
++
++#define S3C2410_GPD8_INP        (0x00 << 16)
++#define S3C2410_GPD8_OUTP       (0x01 << 16)
++#define S3C2410_GPD8_VD16       (0x02 << 16)
++
++#define S3C2410_GPD9_INP        (0x00 << 18)
++#define S3C2410_GPD9_OUTP       (0x01 << 18)
++#define S3C2410_GPD9_VD17       (0x02 << 18)
++
++#define S3C2410_GPD10_INP       (0x00 << 20)
++#define S3C2410_GPD10_OUTP      (0x01 << 20)
++#define S3C2410_GPD10_VD18      (0x02 << 20)
++
++#define S3C2410_GPD11_INP       (0x00 << 22)
++#define S3C2410_GPD11_OUTP      (0x01 << 22)
++#define S3C2410_GPD11_VD19      (0x02 << 22)
++
++#define S3C2410_GPD12_INP       (0x00 << 24)
++#define S3C2410_GPD12_OUTP      (0x01 << 24)
++#define S3C2410_GPD12_VD20      (0x02 << 24)
++
++#define S3C2410_GPD13_INP       (0x00 << 26)
++#define S3C2410_GPD13_OUTP      (0x01 << 26)
++#define S3C2410_GPD13_VD21      (0x02 << 26)
++
++#define S3C2410_GPD14_INP       (0x00 << 28)
++#define S3C2410_GPD14_OUTP      (0x01 << 28)
++#define S3C2410_GPD14_VD22      (0x02 << 28)
++
++#define S3C2410_GPD15_INP       (0x00 << 30)
++#define S3C2410_GPD15_OUTP      (0x01 << 30)
++#define S3C2410_GPD15_VD23      (0x02 << 30)
++
++/* Port E consists of 16 GPIO/Special function
++ *
++ * again, the same as port B, but dealing with I2S, SDI, and
++ * more miscellaneous functions
++*/
++
++#define S3C2410_GPECON     S3C2410_GPIOREG(0x40)
++#define S3C2410_GPEDAT     S3C2410_GPIOREG(0x44)
++#define S3C2410_GPEUP      S3C2410_GPIOREG(0x48)
++
++#define S3C2410_GPE0_INP       (0x00 << 0)
++#define S3C2410_GPE0_OUTP      (0x01 << 0)
++#define S3C2410_GPE0_I2SLRCK   (0x02 << 0)
++#define S3C2410_GPE0_MASK      (0x03 << 0)
++
++#define S3C2410_GPE1_INP       (0x00 << 2)
++#define S3C2410_GPE1_OUTP      (0x01 << 2)
++#define S3C2410_GPE1_I2SSCLK   (0x02 << 2)
++#define S3C2410_GPE1_MASK      (0x03 << 2)
++
++#define S3C2410_GPE2_INP       (0x00 << 4)
++#define S3C2410_GPE2_OUTP      (0x01 << 4)
++#define S3C2410_GPE2_CDCLK     (0x02 << 4)
++
++#define S3C2410_GPE3_INP       (0x00 << 6)
++#define S3C2410_GPE3_OUTP      (0x01 << 6)
++#define S3C2410_GPE3_I2SSDI    (0x02 << 6)
++#define S3C2410_GPE3_MASK      (0x03 << 6)
++
++#define S3C2410_GPE4_INP       (0x00 << 8)
++#define S3C2410_GPE4_OUTP      (0x01 << 8)
++#define S3C2410_GPE4_I2SSDO    (0x02 << 8)
++#define S3C2410_GPE4_MASK      (0x03 << 8)
++
++#define S3C2410_GPE5_INP       (0x00 << 10)
++#define S3C2410_GPE5_OUTP      (0x01 << 10)
++#define S3C2410_GPE5_SDCLK     (0x02 << 10)
++
++#define S3C2410_GPE6_INP       (0x00 << 12)
++#define S3C2410_GPE6_OUTP      (0x01 << 12)
++#define S3C2410_GPE6_SDCLK     (0x02 << 12)
++
++#define S3C2410_GPE7_INP       (0x00 << 14)
++#define S3C2410_GPE7_OUTP      (0x01 << 14)
++#define S3C2410_GPE7_SDCMD     (0x02 << 14)
++
++#define S3C2410_GPE8_INP       (0x00 << 16)
++#define S3C2410_GPE8_OUTP      (0x01 << 16)
++#define S3C2410_GPE8_SDDAT1    (0x02 << 16)
++
++#define S3C2410_GPE9_INP       (0x00 << 18)
++#define S3C2410_GPE9_OUTP      (0x01 << 18)
++#define S3C2410_GPE9_SDDAT2    (0x02 << 18)
++
++#define S3C2410_GPE10_INP      (0x00 << 20)
++#define S3C2410_GPE10_OUTP     (0x01 << 20)
++#define S3C2410_GPE10_SDDAT3   (0x02 << 20)
++
++#define S3C2410_GPE11_INP      (0x00 << 22)
++#define S3C2410_GPE11_OUTP     (0x01 << 22)
++#define S3C2410_GPE11_SPIMISO0 (0x02 << 22)
++
++#define S3C2410_GPE12_INP      (0x00 << 24)
++#define S3C2410_GPE12_OUTP     (0x01 << 24)
++#define S3C2410_GPE12_SPIMOSI0 (0x02 << 24)
++
++#define S3C2410_GPE13_INP      (0x00 << 26)
++#define S3C2410_GPE13_OUTP     (0x01 << 26)
++#define S3C2410_GPE13_SPICLK0  (0x02 << 26)
++
++#define S3C2410_GPE14_INP      (0x00 << 28)
++#define S3C2410_GPE14_OUTP     (0x01 << 28)
++#define S3C2410_GPE14_IICSCL   (0x02 << 28)
++#define S3C2410_GPE14_MASK     (0x03 << 28)
++
++#define S3C2410_GPE15_INP      (0x00 << 30)
++#define S3C2410_GPE15_OUTP     (0x01 << 30)
++#define S3C2410_GPE15_IICSDA   (0x02 << 30)
++#define S3C2410_GPE15_MASK     (0x03 << 30)
++
++#define S3C2410_GPE_PUPDIS(x)  (1<<(x))
++
++/* Port F consists of 8 GPIO/Special function
++ *
++ * GPIO / interrupt inputs
++ *
++ * GPFCON has 2 bits for each of the input pins on port F
++ *   00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 undefined
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPFCON     S3C2410_GPIOREG(0x50)
++#define S3C2410_GPFDAT     S3C2410_GPIOREG(0x54)
++#define S3C2410_GPFUP      S3C2410_GPIOREG(0x58)
++
++
++#define S3C2410_GPF0_INP    (0x00 << 0)
++#define S3C2410_GPF0_OUTP   (0x01 << 0)
++#define S3C2410_GPF0_EINT0  (0x02 << 0)
++
++#define S3C2410_GPF1_INP    (0x00 << 2)
++#define S3C2410_GPF1_OUTP   (0x01 << 2)
++#define S3C2410_GPF1_EINT1  (0x02 << 2)
++
++#define S3C2410_GPF2_INP    (0x00 << 4)
++#define S3C2410_GPF2_OUTP   (0x01 << 4)
++#define S3C2410_GPF2_EINT2  (0x02 << 4)
++
++#define S3C2410_GPF3_INP    (0x00 << 6)
++#define S3C2410_GPF3_OUTP   (0x01 << 6)
++#define S3C2410_GPF3_EINT3  (0x02 << 6)
++
++#define S3C2410_GPF4_INP    (0x00 << 8)
++#define S3C2410_GPF4_OUTP   (0x01 << 8)
++#define S3C2410_GPF4_EINT4  (0x02 << 8)
++
++#define S3C2410_GPF5_INP    (0x00 << 10)
++#define S3C2410_GPF5_OUTP   (0x01 << 10)
++#define S3C2410_GPF5_EINT5  (0x02 << 10)
++
++#define S3C2410_GPF6_INP    (0x00 << 12)
++#define S3C2410_GPF6_OUTP   (0x01 << 12)
++#define S3C2410_GPF6_EINT6  (0x02 << 12)
++
++#define S3C2410_GPF7_INP    (0x00 << 14)
++#define S3C2410_GPF7_OUTP   (0x01 << 14)
++#define S3C2410_GPF7_EINT7  (0x02 << 14)
++
++/* Port G consists of 8 GPIO/IRQ/Special function
++ *
++ * GPGCON has 2 bits for each of the input pins on port F
++ *   00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 special func
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPGCON     S3C2410_GPIOREG(0x60)
++#define S3C2410_GPGDAT     S3C2410_GPIOREG(0x64)
++#define S3C2410_GPGUP      S3C2410_GPIOREG(0x68)
++
++#define S3C2410_GPG0_INP      (0x00 << 0)
++#define S3C2410_GPG0_OUTP     (0x01 << 0)
++#define S3C2410_GPG0_EINT8    (0x02 << 0)
++
++#define S3C2410_GPG1_INP      (0x00 << 2)
++#define S3C2410_GPG1_OUTP     (0x01 << 2)
++#define S3C2410_GPG1_EINT9    (0x02 << 2)
++
++#define S3C2410_GPG2_INP      (0x00 << 4)
++#define S3C2410_GPG2_OUTP     (0x01 << 4)
++#define S3C2410_GPG2_EINT10   (0x02 << 4)
++
++#define S3C2410_GPG3_INP      (0x00 << 6)
++#define S3C2410_GPG3_OUTP     (0x01 << 6)
++#define S3C2410_GPG3_EINT11   (0x02 << 6)
++
++#define S3C2410_GPG4_INP      (0x00 << 8)
++#define S3C2410_GPG4_OUTP     (0x01 << 8)
++#define S3C2410_GPG4_EINT12   (0x02 << 8)
++#define S3C2410_GPG4_LCDPWREN (0x03 << 8)
++
++#define S3C2410_GPG5_INP      (0x00 << 10)
++#define S3C2410_GPG5_OUTP     (0x01 << 10)
++#define S3C2410_GPG5_EINT13   (0x02 << 10)
++#define S3C2410_GPG5_SPIMISO1 (0x03 << 10)
++
++#define S3C2410_GPG6_INP      (0x00 << 12)
++#define S3C2410_GPG6_OUTP     (0x01 << 12)
++#define S3C2410_GPG6_EINT14   (0x02 << 12)
++#define S3C2410_GPG6_SPIMOSI1 (0x03 << 12)
++
++#define S3C2410_GPG7_INP      (0x00 << 14)
++#define S3C2410_GPG7_OUTP     (0x01 << 14)
++#define S3C2410_GPG7_EINT15   (0x02 << 14)
++#define S3C2410_GPG7_SPICLK1  (0x03 << 14)
++
++#define S3C2410_GPG8_INP      (0x00 << 16)
++#define S3C2410_GPG8_OUTP     (0x01 << 16)
++#define S3C2410_GPG8_EINT16   (0x02 << 16)
++
++#define S3C2410_GPG9_INP      (0x00 << 18)
++#define S3C2410_GPG9_OUTP     (0x01 << 18)
++#define S3C2410_GPG9_EINT17   (0x02 << 18)
++
++#define S3C2410_GPG10_INP     (0x00 << 20)
++#define S3C2410_GPG10_OUTP    (0x01 << 20)
++#define S3C2410_GPG10_EINT18  (0x02 << 20)
++
++#define S3C2410_GPG11_INP     (0x00 << 22)
++#define S3C2410_GPG11_OUTP    (0x01 << 22)
++#define S3C2410_GPG11_EINT19  (0x02 << 22)
++#define S3C2410_GPG11_TCLK1   (0x03 << 22)
++
++#define S3C2410_GPG12_INP     (0x00 << 24)
++#define S3C2410_GPG12_OUTP    (0x01 << 24)
++#define S3C2410_GPG12_EINT18  (0x02 << 24)
++#define S3C2410_GPG12_XMON    (0x03 << 24)
++
++#define S3C2410_GPG13_INP     (0x00 << 26)
++#define S3C2410_GPG13_OUTP    (0x01 << 26)
++#define S3C2410_GPG13_EINT18  (0x02 << 26)
++#define S3C2410_GPG13_nXPON   (0x03 << 26)
++
++#define S3C2410_GPG14_INP     (0x00 << 28)
++#define S3C2410_GPG14_OUTP    (0x01 << 28)
++#define S3C2410_GPG14_EINT18  (0x02 << 28)
++#define S3C2410_GPG14_YMON    (0x03 << 28)
++
++#define S3C2410_GPG15_INP     (0x00 << 30)
++#define S3C2410_GPG15_OUTP    (0x01 << 30)
++#define S3C2410_GPG15_EINT18  (0x02 << 30)
++#define S3C2410_GPG15_nYPON   (0x03 << 30)
++
++
++#define S3C2410_GPG_PUPDIS(x)  (1<<(x))
++
++/* Port H consists of11 GPIO/serial/Misc pins
++ *
++ * GPGCON has 2 bits for each of the input pins on port F
++ *   00 = 0 input, 1 output, 2 interrupt (EINT0..7), 3 special func
++ *
++ * pull up works like all other ports.
++*/
++
++#define S3C2410_GPHCON     S3C2410_GPIOREG(0x70)
++#define S3C2410_GPHDAT     S3C2410_GPIOREG(0x74)
++#define S3C2410_GPHUP      S3C2410_GPIOREG(0x78)
++
++#define S3C2410_GPH0_INP    (0x00 << 0)
++#define S3C2410_GPH0_OUTP   (0x01 << 0)
++#define S3C2410_GPH0_nCTS0  (0x02 << 0)
++
++#define S3C2410_GPH1_INP    (0x00 << 2)
++#define S3C2410_GPH1_OUTP   (0x01 << 2)
++#define S3C2410_GPH1_nRTS0  (0x02 << 2)
++
++#define S3C2410_GPH2_INP    (0x00 << 4)
++#define S3C2410_GPH2_OUTP   (0x01 << 4)
++#define S3C2410_GPH2_TXD0   (0x02 << 4)
++
++#define S3C2410_GPH3_INP    (0x00 << 6)
++#define S3C2410_GPH3_OUTP   (0x01 << 6)
++#define S3C2410_GPH3_RXD0   (0x02 << 6)
++
++#define S3C2410_GPH4_INP    (0x00 << 8)
++#define S3C2410_GPH4_OUTP   (0x01 << 8)
++#define S3C2410_GPH4_TXD1   (0x02 << 8)
++
++#define S3C2410_GPH5_INP    (0x00 << 10)
++#define S3C2410_GPH5_OUTP   (0x01 << 10)
++#define S3C2410_GPH5_RXD1   (0x02 << 10)
++
++#define S3C2410_GPH6_INP    (0x00 << 12)
++#define S3C2410_GPH6_OUTP   (0x01 << 12)
++#define S3C2410_GPH6_TXD2   (0x02 << 12)
++#define S3C2410_GPH6_nRTS1  (0x03 << 12)
++
++#define S3C2410_GPH7_INP    (0x00 << 14)
++#define S3C2410_GPH7_OUTP   (0x01 << 14)
++#define S3C2410_GPH7_RXD2   (0x02 << 14)
++#define S3C2410_GPH7_nCTS1  (0x03 << 14)
++
++#define S3C2410_GPH8_INP    (0x00 << 16)
++#define S3C2410_GPH8_OUTP   (0x01 << 16)
++#define S3C2410_GPH8_UCLK   (0x02 << 16)
++
++#define S3C2410_GPH9_INP     (0x00 << 18)
++#define S3C2410_GPH9_OUTP    (0x01 << 18)
++#define S3C2410_GPH9_CLKOUT0 (0x02 << 18)
++
++#define S3C2410_GPH10_INP   (0x00 << 20)
++#define S3C2410_GPH10_OUTP  (0x01 << 20)
++#define S3C2410_GPH10_CLKOUT1  (0x02 << 20)
++
++/* miscellaneous control */
++
++#define S3C2410_MISCCR     S3C2410_GPIOREG(0x80)
++#define S3C2410_DCLKCON    S3C2410_GPIOREG(0x84)
++
++/* see clock.h for dclk definitions */
++
++/* pullup control on databus */
++#define S3C2410_MISCCR_SPUCR_HEN    (0)
++#define S3C2410_MISCCR_SPUCR_HDIS   (1<<0)
++#define S3C2410_MISCCR_SPUCR_LEN    (0)
++#define S3C2410_MISCCR_SPUCR_LDIS   (1<<1)
++
++#define S3C2410_MISCCR_USBDEV       (0)
++#define S3C2410_MISCCR_USBHOST      (1<<3)
++
++#define S3C2410_MISCCR_CLK0_MPLL    (0<<4)
++#define S3C2410_MISCCR_CLK0_UPLL    (1<<4)
++#define S3C2410_MISCCR_CLK0_FCLK    (2<<4)
++#define S3C2410_MISCCR_CLK0_HCLK    (3<<4)
++#define S3C2410_MISCCR_CLK0_PCLK    (4<<4)
++#define S3C2410_MISCCR_CLK0_DCLK0   (5<<4)
++
++#define S3C2410_MISCCR_CLK1_MPLL    (0<<8)
++#define S3C2410_MISCCR_CLK1_UPLL    (1<<8)
++#define S3C2410_MISCCR_CLK1_FCLK    (2<<8)
++#define S3C2410_MISCCR_CLK1_HCLK    (3<<8)
++#define S3C2410_MISCCR_CLK1_PCLK    (4<<8)
++#define S3C2410_MISCCR_CLK1_DCLK1   (5<<8)
++
++#define S3C2410_MISCCR_USBSUSPND0   (1<<12)
++#define S3C2410_MISCCR_USBSUSPND1   (1<<13)
++
++#define S3C2410_MISCCR_nRSTCON      (1<<16)
++
++/* external interrupt control... */
++/* S3C2410_EXTINT0 -> irq sense control for EINT0..EINT7
++ * S3C2410_EXTINT1 -> irq sense control for EINT8..EINT15
++ * S3C2410_EXTINT2 -> irq sense control for EINT16..EINT23
++ *
++ * note S3C2410_EXTINT2 has filtering options for EINT16..EINT23
++ *
++ * Samsung datasheet p9-25
++*/
++
++#define S3C2410_EXTINT0    S3C2410_GPIOREG(0x88)
++#define S3C2410_EXTINT1    S3C2410_GPIOREG(0x8C)
++#define S3C2410_EXTINT2    S3C2410_GPIOREG(0x90)
++
++/* values for S3C2410_EXTINT0/1/2 */
++#define S3C2410_EXTINT_LOWLEV    (0x00)
++#define S3C2410_EXTINT_HILEV     (0x01)
++#define S3C2410_EXTINT_FALLEDGE  (0x02)
++#define S3C2410_EXTINT_RISEEDGE  (0x04)
++#define S3C2410_EXTINT_BOTHEDGE  (0x06)
++
++/* interrupt filtering conrrol for EINT16..EINT23 */
++#define S3C2410_EINFLT0    S3C2410_GPIOREG(0x94)
++#define S3C2410_EINFLT1    S3C2410_GPIOREG(0x98)
++#define S3C2410_EINFLT2    S3C2410_GPIOREG(0x9C)
++#define S3C2410_EINFLT3    S3C2410_GPIOREG(0xA0)
++
++/* mask: 0=enable, 1=disable
++ * 1 bit EINT, 4=EINT4, 23=EINT23
++ * EINT0,1,2,3 are not handled here.
++*/
++#define S3C2410_EINTMASK   S3C2410_GPIOREG(0xA4)
++#define S3C2410_EINTPEND   S3C2410_GPIOREG(0xA8)
++
++/* GSTATUS have miscellaneous information in them
++ *
++ */
++
++#define S3C2410_GSTATUS0   S3C2410_GPIOREG(0x0AC)
++#define S3C2410_GSTATUS1   S3C2410_GPIOREG(0x0B0)
++#define S3C2410_GSTATUS2   S3C2410_GPIOREG(0x0B4)
++#define S3C2410_GSTATUS3   S3C2410_GPIOREG(0x0B8)
++#define S3C2410_GSTATUS4   S3C2410_GPIOREG(0x0BC)
++
++#define S3C2410_GSTATUS0_nWAIT     (1<<3)
++#define S3C2410_GSTATUS0_NCON      (1<<2)
++#define S3C2410_GSTATUS0_RnB       (1<<1)
++#define S3C2410_GSTATUS0_nBATTFLT  (1<<0)
++
++#define S3C2410_GSTATUS2_WTRESET   (1<<2)
++#define S3C2410_GSTATUs2_OFFRESET  (1<<1)
++#define S3C2410_GSTATUS2_PONRESET  (1<<0)
++
++#endif  /* ASMARM_ARCH_S3C2410_GPIO_H */
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-iis.h
+@@ -0,0 +1,61 @@
++/* linux/include/asm/hardware/s3c2410/iis.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 IIS register definition
++ *
++ *  Changelog:
++ *    19-06-2003     BJD     Created file
++ *    26-06-2003     BJD     Finished off definitions for register addresses
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_IIS_H
++#define ASMARM_ARCH_S3C2410_IIS_H
++
++#define S3C2410_IISCON   (S3C2410_VA_IIS + 0x00)
++
++#define S3C2410_IISCON_LRINDEX    (1<<8)
++#define S3C2410_IISCON_TXFIFORDY  (1<<7)
++#define S3C2410_IISCON_RXFIFORDY  (1<<6)
++#define S3C2410_IISCON_TXDMAEN    (1<<5)
++#define S3C2410_IISCON_RXDMAEN    (1<<4)
++#define S3C2410_IISCON_TXIDLE     (1<<3)
++#define S3C2410_IISCON_RXIDLE     (1<<2)
++#define S3C2410_IISCON_IISEN      (1<<0)
++
++#define S3C2410_IISMOD   (S3C2410_VA_IIS + 0x04)
++
++#define S3C2410_IISMOD_SLAVE      (1<<8)
++#define S3C2410_IISMOD_NOXFER     (0<<6)
++#define S3C2410_IISMOD_RXMODE     (1<<6)
++#define S3C2410_IISMOD_TXMODE     (2<<6)
++#define S3C2410_IISMOD_TXRXMODE   (3<<6)
++#define S3C2410_IISMOD_LR_LLOW    (0<<5)
++#define S3C2410_IISMOD_LR_RLOW    (1<<5)
++#define S3C2410_IISMOD_IIS        (0<<4)
++#define S3C2410_IISMOD_MSB        (1<<4)
++#define S3C2410_IISMOD_8BIT       (0<<3)
++#define S3C2410_IISMOD_16BIT      (1<<3)
++#define S3C2410_IISMOD_256FS      (0<<1)
++#define S3C2410_IISMOD_384FS      (1<<1)
++#define S3C2410_IISMOD_16FS       (0<<0)
++#define S3C2410_IISMOD_32FS       (1<<0)
++#define S3C2410_IISMOD_48FS       (2<<0)
++
++#define S3C2410_IISPSR   (S3C2410_VA_IIS + 0x08)
++
++#define S3C2410_IISFCON  (S3C2410_VA_IIS + 0x0c)
++
++#define S3C2410_IISFCON_TXDMA     (1<<15)
++#define S3C2410_IISFCON_RXDMA     (1<<14)
++#define S3C2410_IISFCON_TXENABLE  (1<<13)
++#define S3C2410_IISFCON_RXENABLE  (1<<12)
++
++#define S3C2410_IISFIFO  (S3C2410_VA_IIS + 0x10)
++
++#endif /* ASMARM_ARCH_S3C2410_IIS_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-irq.h
+@@ -0,0 +1,34 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-irq.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ *  Changelog:
++ *    19-06-2003     BJD     Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_IRQ_H
++#define ASMARM_ARCH_S3C2410_IRQ_H
++
++/* interrupt controller */
++
++#define S3C2410_IRQREG(x)   ((x) + S3C2410_VA_IRQ)
++#define S3C2410_EINTREG(x)  ((x) + S3C2410_VA_GPIO)
++
++#define S3C2410_SRCPND         S3C2410_IRQREG(0x000)
++#define S3C2410_INTMOD         S3C2410_IRQREG(0x004)
++#define S3C2410_INTMSK         S3C2410_IRQREG(0x008)
++#define S3C2410_PRIORITY       S3C2410_IRQREG(0x00C)
++#define S3C2410_INTPND         S3C2410_IRQREG(0x010)
++#define S3C2410_INTOFFSET      S3C2410_IRQREG(0x014)
++#define S3C2410_SUBSRCPND      S3C2410_IRQREG(0x018)
++#define S3C2410_INTSUBMSK      S3C2410_IRQREG(0x01C)
++
++#define S3C2410_EINTMASK       S3C2410_EINTREG(0x0A4)
++#define S3C2410_EINTPEND       S3C2410_EINTREG(0X0A8)
++
++#endif /* ASMARM_ARCH_S3C2410_IRQ_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-lcd.h
+@@ -0,0 +1,107 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-lcd.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ *
++ *
++ *  Changelog:
++ *    12-06-2003     BJD     Created file
++ *    26-06-2003     BJD     Updated LCDCON register definitions
++*/
++
++#ifndef ASMARM_ARCH_S3C2410_LCD_H
++#define ASMARM_ARCH_S3C2410_LCD_H
++
++#define S3C2410_LCDREG(x) ((x) + S3C2410_VA_LCD)
++
++/* LCD control registers */
++#define S3C2410_LCDCON1     S3C2410_LCDREG(0x00)
++#define S3C2410_LCDCON2     S3C2410_LCDREG(0x04)
++#define S3C2410_LCDCON3     S3C2410_LCDREG(0x08)
++#define S3C2410_LCDCON4     S3C2410_LCDREG(0x0C)
++#define S3C2410_LCDCON5     S3C2410_LCDREG(0x10)
++
++#define S3C2410_LCDCON1_CLKVAL(x)  ((x) << 8)
++#define S3C2410_LCDCON1_MMODE      (1<<7)
++#define S3C2410_LCDCON1_DSCAN4     (0<<5)
++#define S3C2410_LCDCON1_STN4       (1<<5)
++#define S3C2410_LCDCON1_STN8       (2<<5)
++#define S3C2410_LCDCON1_TFT        (3<<5)
++
++#define S3C2410_LCDCON1_STN1BPP    (0<<1)
++#define S3C2410_LCDCON1_STN2GREY   (1<<1)
++#define S3C2410_LCDCON1_STN4GREY   (2<<1)
++#define S3C2410_LCDCON1_STN8BPP    (3<<1)
++#define S3C2410_LCDCON1_STN12BPP   (4<<1)
++
++#define S3C2410_LCDCON1_TFT1BPP    (8<<1)
++#define S3C2410_LCDCON1_TFT2BPP    (9<<1)
++#define S3C2410_LCDCON1_TFT4BPP    (10<<1)
++#define S3C2410_LCDCON1_TFT8BPP    (11<<1)
++#define S3C2410_LCDCON1_TFT16BPP   (12<<1)
++#define S3C2410_LCDCON1_TFT24BPP   (13<<1)
++
++#define S3C2410_LCDCON1_ENVDI      (1)
++
++#define S3C2410_LCDCON2_VBPD(x)     ((x) << 24)
++#define S3C2410_LCDCON2_LINEVAL(x)  ((x) << 14)
++#define S3C2410_LCDCON2_VFPD(x)     ((x) << 6)
++#define S3C2410_LCDCON2_VSPW(x)     ((x) << 0)
++
++#define S3C2410_LCDCON3_HBPD(x)     ((x) << 19)
++#define S3C2410_LCDCON3_WDLY(x)     ((x) << 19)
++#define S3C2410_LCDCON3_HOZVAL(x)   ((x) << 8)
++#define S3C2410_LCDCON3_HFPD(x)     ((x) << 0)
++#define S3C2410_LCDCON3_LINEBLANK(x)((x) << 0)
++
++#define S3C2410_LCDCON4_MVAL(x)     ((x) << 8)
++#define S3C2410_LCDCON4_HSPW(x)     ((x) << 0)
++#define S3C2410_LCDCON4_WLH(x)      ((x) << 0)
++
++#define S3C2410_LCDCON5_BPP24BL     (1<<12)
++#define S3C2410_LCDCON5_FRM565      (1<<11)
++#define S3C2410_LCDCON5_INVVCLK     (1<<10)
++#define S3C2410_LCDCON5_INVVLINE    (1<<9)
++#define S3C2410_LCDCON5_INVVFRAME   (1<<8)
++#define S3C2410_LCDCON5_INVVD       (1<<7)
++#define S3C2410_LCDCON5_INVVSYNC    (1<<8)
++#define S3C2410_LCDCON5_INVHSYNC    (1<<9)
++#define S3C2410_LCDCON5_INVVDEN     (1<<6)
++#define S3C2410_LCDCON5_INVPWREN    (1<<5)
++#define S3C2410_LCDCON5_INVLEND     (1<<4)
++#define S3C2410_LCDCON5_PWREN       (1<<3)
++#define S3C2410_LCDCON5_ENLEND      (1<<2)
++#define S3C2410_LCDCON5_BSWP        (1<<1)
++#define S3C2410_LCDCON5_HWSWP       (1<<0)
++
++/* framebuffer start addressed */
++#define S3C2410_LCDSADDR1   S3C2410_LCDREG(0x14)
++#define S3C2410_LCDSADDR2   S3C2410_LCDREG(0x18)
++#define S3C2410_LCDSADDR3   S3C2410_LCDREG(0x1C)
++
++/* colour lookup and miscellaneous controls */
++
++#define S3C2410_REDLUT     S3C2410_LCDREG(0x20)
++#define S3C2410_GREENLUT   S3C2410_LCDREG(0x24)
++#define S3C2410_BLUELUT    S3C2410_LCDREG(0x28)
++
++#define S3C2410_DITHMODE   S3C2410_LCDREG(0x4C)
++#define S3C2410_TPAL       S3C2410_LCDREG(0x50)
++
++/* interrupt info */
++#define S3C2410_LCDINTPND  S3C2410_LCDREG(0x54)
++#define S3C2410_LCDSRCPND  S3C2410_LCDREG(0x58)
++#define S3C2410_LCDINTMSK  S3C2410_LCDREG(0x5C)
++#define S3C2410_LPCSEL     S3C2410_LCDREG(0x60)
++
++#define S3C2410_TFTPAL(x)  S3C2410_LCDREG((0x400 + (x)*4))
++
++#endif /* ASMARM_ARCH_S3C2410_LCD_H */
++
++
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-rtc.h
+@@ -0,0 +1,61 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-rtc.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Timer configuration
++ *
++ *  Changelog:
++ *    05-06-2003     BJD     Created file
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_RTC_H
++#define ASMARM_ARCH_S3C2410_RTC_H
++
++#define S3C2410_RTCREG(x) ((x) + S3C2410_VA_RTC)
++
++#define S3C2410_RTCCON        S3C2410_RTCREG(0x40)
++#define S3C2410_RTCCON_RTCEN  (1<<0)
++#define S3C2410_RTCCON_CLKRST (1<<3)
++
++#define S3C2410_TICNT         S3C2410_RTCREG(0x44)
++#define S3C2410_TICNT_ENABLE  (1<<7)
++
++#define S3C2410_RTCALM        S3C2410_RTCREG(0x50)
++#define S3C2410_RTCALM_ALMEN  (1<<6)
++#define S3C2410_RTCALM_YEAREN (1<<5)
++#define S3C2410_RTCALM_MONEN  (1<<4)
++#define S3C2410_RTCALM_DAYEN  (1<<3)
++#define S3C2410_RTCALM_HOUREN (1<<2)
++#define S3C2410_RTCALM_MINEN  (1<<1)
++#define S3C2410_RTCALM_SECEN  (1<<0)
++
++#define S3C2410_RTCALM_ALL \
++  S3C2410_RTCALM_ALMEN | S3C2410_RTCALM_YEAREN | S3C2410_RTCALM_MONEN |\
++  S3C2410_RTCALM_DAYEN | S3C2410_RTCALM_HOUREN | S3C2410_RTCALM_MINEN |\
++  S3C2410_RTCALM_SECEN
++
++
++#define S3C2410_ALMSEC        S3C2410_RTCREG(0x54)
++#define S3C2410_ALMMIN        S3C2410_RTCREG(0x58)
++#define S3C2410_ALMHOUR       S3C2410_RTCREG(0x5c)
++
++#define S3C2410_ALMDATE       S3C2410_RTCREG(0x60)
++#define S3C2410_ALMMON        S3C2410_RTCREG(0x64)
++#define S3C2410_ALMYEAR       S3C2410_RTCREG(0x68)
++
++#define S3C2410_RTCRST        S3C2410_RTCREG(0x6c)
++
++#define S3C2410_RTCSEC        S3C2410_RTCREG(0x70)
++#define S3C2410_RTCMIN        S3C2410_RTCREG(0x74)
++#define S3C2410_RTCHOUR       S3C2410_RTCREG(0x78)
++#define S3C2410_RTCDATE       S3C2410_RTCREG(0x7c)
++#define S3C2410_RTCDAY        S3C2410_RTCREG(0x80)
++#define S3C2410_RTCMON        S3C2410_RTCREG(0x84)
++#define S3C2410_RTCYEAR       S3C2410_RTCREG(0x88)
++
++#endif /* ASMARM_ARCH_S3C2410_RTC_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-serial.h
+@@ -0,0 +1,98 @@
++/*
++ *  linux/include/asm-arm/arch-s3c2410/S3C2410-serial.h
++ *
++ *  Internal header file for Samsung S3C2410 serial ports (UART0-2)
++ *
++ *  Copyright (C) 2002 Shane Nay (shane@minirl.com)
++ *
++ *  Additional defines, (c) 2003 Simtec Electronics (linux@simtec.co.uk)
++ *
++ *  Adapted from:
++ *
++ *  Internal header file for MX1ADS serial ports (UART1 & 2)
++ *
++ *  Copyright (C) 2002 Shane Nay (shane@minirl.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 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
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_SERIAL_H
++#define ASMARM_ARCH_S3C2410_SERIAL_H
++
++#define S3C2410_UARTRXH0_OFF      (0x24)
++#define S3C2410_UARTTXH0_OFF      (0x20)
++#define S3C2410_UARTLCON_OFF      (0x00)
++#define S3C2410_UARTCON_OFF       (0x04)
++#define S3C2410_UARTFCON_OFF      (0x08)
++#define S3C2410_UARTMCON_OFF      (0x0C)
++#define S3C2410_UARTBRDIV_OFF     (0x28)
++#define S3C2410_UARTTRSTAT_OFF    (0x10)
++#define S3C2410_UARTERSTAT_OFF    (0x14)
++#define S3C2410_UARTFSTAT_OFF     (0x18)
++#define S3C2410_UARTMSTAT_OFF     (0x1C)
++
++
++#define S3C2410_UART1_OFF         (0x4000)
++#define S3C2410_UART2_OFF         (0x8000)
++
++#define S3C2410_LCON_CFGMASK      ((0xF<<3)|(0x3))
++
++#define S3C2410_LCON_CS5          (0x0)
++#define S3C2410_LCON_CS6          (0x1)
++#define S3C2410_LCON_CS7          (0x2)
++#define S3C2410_LCON_CS8          (0x3)
++
++#define S3C2410_LCON_PNONE        (0x0)
++#define S3C2410_LCON_PEVEN        ((0x5)<<3)
++#define S3C2410_LCON_PODD         ((0x4)<<3)
++
++#define S3C2410_UMCON_AFC         (0x10)
++#define S3C2410_UMCON_RTS         (0x1)
++
++#define S3C2410_UMSTAT_CTS        (0x1)
++
++#define S3C2410_UCON_SBREAK       (1<<4)
++
++#define S3C2410_UCON_TXILEVEL     (1<<9)
++#define S3C2410_UCON_RXILEVEL     (1<<8)
++#define S3C2410_UCON_TXIRQMODE    (1<<2)
++#define S3C2410_UCON_RXIRQMODE    (1<<0)
++#define S3C2410_UCON_RXFIFO_TOI   (1<<7)
++
++#define S3C2410_UCON_DEFAULT      (S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL \
++                                   | S3C2410_UCON_TXIRQMODE | S3C2410_UCON_RXIRQMODE \
++                                 | S3C2410_UCON_RXFIFO_TOI)
++
++#define S3C2410_UFCON_FIFOMODE    (1<<0)
++#define S3C2410_UFCON_TXTRIG0     (0<<6)
++#define S3C2410_UFCON_RXTRIG8     (1<<4)
++#define S3C2410_UFCON_RXTRIG12    (2<<4)
++
++#define S3C2410_UFCON_RESETBOTH   (3<<1)
++
++#define S3C2410_UFCON_DEFAULT     (S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_TXTRIG0 \
++                                  | S3C2410_UFCON_RXTRIG8 )
++
++#define S3C2410_UFSTAT_TXFULL     (1<<9)
++#define S3C2410_UFSTAT_RXFULL     (1<<8)
++#define S3C2410_UFSTAT_TXMASK     (15<<4)
++#define S3C2410_UFSTAT_TXSHIFT    (4)
++#define S3C2410_UFSTAT_RXMASK     (15<<0)
++#define S3C2410_UFSTAT_RXSHIFT    (0)
++
++#define S3C2410_UTRSTAT_TXFE      (1<<1)
++#define S3C2410_UTRSTAT_RXDR      (1<<0)
++
++#endif /* ASMARM_ARCH_S3C2410_SERIAL_H */
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-timer.h
+@@ -0,0 +1,81 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-timer.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Timer configuration
++ *
++ *  Changelog:
++ *    05-06-2003     BJD     Created file
++ *    26-06-2003     BJD     Added more timer definitions to mux / control
++ */
++
++#ifndef ASMARM_ARCH_S3C2410_TIMER_H
++#define ASMARM_ARCH_S3C2410_TIMER_H
++
++#define S3C2410_TIMERREG(x) (S3C2410_VA_TIMER + (x))
++#define S3C2410_TIMERREG2(tmr,reg) S3C2410_TIMERREG((reg)+0x0c+((tmr)*0x0c))
++
++#define S3C2410_TCFG0         S3C2410_TIMERREG(0x00)
++#define S3C2410_TCFG1         S3C2410_TIMERREG(0x04)
++#define S3C2410_TCON          S3C2410_TIMERREG(0x08)
++
++
++#define S3C2410_TCFG1_MUX4_TCLK1  (4<<16)
++#define S3C2410_TCFG1_MUX4_MASK   (15<<16)
++
++#define S3C2410_TCFG1_MUX3_TCLK1  (4<<12)
++#define S3C2410_TCFG1_MUX3_MASK   (15<<12)
++
++#define S3C2410_TCFG1_MUX2_TCLK1  (4<<8)
++#define S3C2410_TCFG1_MUX2_MASK   (15<<8)
++
++#define S3C2410_TCFG1_MUX1_TCLK0  (4<<4)
++#define S3C2410_TCFG1_MUX1_MASK   (15<<4)
++
++#define S3C2410_TCFG1_MUX0_TCLK0  (4<<0)
++#define S3C2410_TCFG1_MUX0_MASK   (15<<0)
++
++/* for each timer, we have an count buffer, an compare buffer and an
++ * observation buffer
++ */
++
++/* WARNING - timer 4 has no buffer reg, and it's observation is at +4 */
++
++#define S3C2410_TCNTB(tmr)    S3C2410_TIMERREG2(tmr, 0x00)
++#define S3C2410_TCMPB(tmr)    S3C2410_TIMERREG2(tmr, 0x04)
++#define S3C2410_TCNTO(tmr)    S3C2410_TIMERREG2(tmr, (((tmr) == 4) ? 0x04 : 0x08))
++
++#define S3C2410_TCON_T4RELOAD     (1<<22)
++#define S3C2410_TCON_T4MANUALUPD  (1<<21)
++#define S3C2410_TCON_T4START      (1<<20)
++
++#define S3C2410_TCON_T3RELOAD     (1<<19)
++#define S3C2410_TCON_T3INVERT     (1<<18)
++#define S3C2410_TCON_T3MANUALUPD  (1<<17)
++#define S3C2410_TCON_T3START      (1<<16)
++
++#define S3C2410_TCON_T2RELOAD     (1<<15)
++#define S3C2410_TCON_T2INVERT     (1<<14)
++#define S3C2410_TCON_T2MANUALUPD  (1<<13)
++#define S3C2410_TCON_T2START      (1<<12)
++
++#define S3C2410_TCON_T1RELOAD     (1<<11)
++#define S3C2410_TCON_T1INVERT     (1<<10)
++#define S3C2410_TCON_T1MANUALUPD  (1<<9)
++#define S3C2410_TCON_T1START      (1<<8)
++
++#define S3C2410_TCON_T0DEADZONE   (1<<4)
++#define S3C2410_TCON_T0RELOAD     (1<<3)
++#define S3C2410_TCON_T0INVERT     (1<<2)
++#define S3C2410_TCON_T0MANUALUPD  (1<<1)
++#define S3C2410_TCON_T0START      (1<<0)
++
++#endif /* ASMARM_ARCH_S3C2410_TIMER_H */
++
++
++
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h
+@@ -0,0 +1,40 @@
++/* linux/include/asm-arm/arch-s3c2410/S3C2410-watchdog.h
++ *
++ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
++ *                    http://www.simtec.co.uk/products/SWLINUX/
++ *
++ * 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.
++ *
++ * S3C2410 Watchdog timer control
++ *
++ *  Changelog:
++ *    21-06-2003     BJD     Created file
++*/
++
++#ifndef ASMARM_ARCH_S3C2410_WATCHDOG_H
++#define ASMARM_ARCH_S3C2410_WATCHDOG_H
++
++#define S3C2410_WDOGREG(x) ((x) + S3C2410_VA_WATCHDOG)
++
++#define S3C2410_WTCON      S3C2410_WDOGREG(0x00)
++#define S3C2410_WTDAT      S3C2410_WDOGREG(0x04)
++#define S3C2410_WTCNT      S3C2410_WDOGREG(0x08)
++
++/* the watchdog can either generate a reset pulse, or an interrupt. */
++
++#define S3C2410_WTCON_RSTEN   (0x01)
++#define S3C2410_WTCON_INTEN   (1<<2)
++#define S3C2410_WTCON_ENABLE  (1<<5)
++
++#define S3C2410_WTCON_DIV16   (0<<3)
++#define S3C2410_WTCON_DIV32   (1<<3)
++#define S3C2410_WTCON_DIV64   (2<<3)
++#define S3C2410_WTCON_DIV128  (3<<3)
++
++#define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
++
++#endif /* ASMARM_ARCH_S3C2410_WATCHDOG_H */
++
++
+--- linux-2.4.27/include/asm-arm/bugs.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/bugs.h
+@@ -10,8 +10,17 @@
+ #ifndef __ASM_BUGS_H
+ #define __ASM_BUGS_H
++#include <linux/config.h>
+ #include <asm/proc-fns.h>
+-#define check_bugs() cpu_check_bugs()
++extern void check_writebuffer_bugs(void);
++
++static inline void check_bugs(void)
++{
++#ifdef CONFIG_CPU_32
++      check_writebuffer_bugs();
++#endif
++      cpu_check_bugs();
++}
+ #endif
+--- linux-2.4.27/include/asm-arm/div64.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/div64.h
+@@ -4,9 +4,13 @@
+ /* We're not 64-bit, but... */
+ #define do_div(n,base)                                                \
+ ({                                                            \
+-      int __res;                                              \
+-      __res = ((unsigned long)n) % (unsigned int)base;        \
+-      n = ((unsigned long)n) / (unsigned int)base;            \
++      register int __res asm("r2") = base;                    \
++      register unsigned long long __n asm("r0") = n;          \
++      asm("bl do_div64"                                       \
++              : "=r" (__n), "=r" (__res)                      \
++              : "0" (__n), "1" (__res)                        \
++              : "r3", "ip", "lr", "cc");                      \
++      n = __n;                                                \
+       __res;                                                  \
+ })
+--- linux-2.4.27/include/asm-arm/elf.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/elf.h
+@@ -45,8 +45,8 @@
+ #define ELF_ET_DYN_BASE       (2 * TASK_SIZE / 3)
+-/* When the program starts, a1 contains a pointer to a function to be 
+-   registered with atexit, as per the SVR4 ABI.  A value of 0 means we 
++/* When the program starts, a1 contains a pointer to a function to be
++   registered with atexit, as per the SVR4 ABI.  A value of 0 means we
+    have no such handler.  */
+ #define ELF_PLAT_INIT(_r, load_addr)  (_r)->ARM_r0 = 0
+--- linux-2.4.27/include/asm-arm/hc_sl811-hw.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/hc_sl811-hw.h
+@@ -38,7 +38,7 @@
+  */
+ static void inline sl811_write_index (hcipriv_t *hp, __u8 index)
+ {
+-      writeb (offset, hp->hcport);
++      writeb (index, hp->hcport);
+       wmb ();
+ }
+@@ -55,7 +55,7 @@
+  */
+ static void inline sl811_write_index_data (hcipriv_t *hp, __u8 index, __u8 data)
+ {
+-      writeb (offset, hp->hcport);
++      writeb (index, hp->hcport);
+       writeb (data, hp->hcport2);
+       wmb ();
+ }
+--- linux-2.4.27/include/asm-arm/mach/dma.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/mach/dma.h
+@@ -41,6 +41,7 @@
+       unsigned int    dma_base;       /* Controller base address      */
+       int             dma_irq;        /* Controller IRQ               */
+       struct scatterlist cur_sg;      /* Current controller buffer    */
++      unsigned int    state;          /* RiscPC DMA status            */
+       struct dma_ops  *d_ops;
+ };
+--- linux-2.4.27/include/asm-arm/mach/serial_at91rm9200.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/mach/serial_at91rm9200.h
+@@ -10,7 +10,6 @@
+ #include <linux/config.h>
+ struct uart_port;
+-struct uart_info;
+ /*
+  * This is a temporary structure for registering these
+@@ -22,11 +21,11 @@
+       void    (*enable_ms)(struct uart_port *);
+       void    (*pm)(struct uart_port *, u_int, u_int);
+       int     (*set_wake)(struct uart_port *, u_int);
+-      int     (*open)(struct uart_port *, struct uart_info *);
+-      void    (*close)(struct uart_port *, struct uart_info *);
++      int     (*open)(struct uart_port *);
++      void    (*close)(struct uart_port *);
+ };
+-#if defined(CONFIG_SERIAL_AT91RM9200)
++#if defined(CONFIG_SERIAL_AT91)
+ void at91rm9200_register_uart_fns(struct at91rm9200_port_fns *fns);
+ void at91rm9200_register_uart(int idx, int port);
+ #else
+--- linux-2.4.27/include/asm-arm/pci.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/pci.h
+@@ -113,6 +113,28 @@
+       /* nothing to do */
+ }
++/*
++ * pci_{map,unmap}_page maps a kernel page to a dma_addr_t. identical
++ * to pci_map_single, but takes a struct page instead of a virtual address
++ */
++static inline dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page,
++                                    unsigned long offset, size_t size,
++                                    int direction)
++{
++      void *start;
++      BUG_ON(direction == PCI_DMA_NONE);
++      start = page_address(page) + offset;
++      consistent_sync(start, size, direction);
++      return virt_to_bus(start);
++}
++
++static inline void pci_unmap_page(struct pci_dev *hwdev, dma_addr_t dma_address,
++                                size_t size, int direction)
++{
++      BUG_ON(direction == PCI_DMA_NONE);
++      /* Nothing to do */
++}
++
+ /* Whether pci_unmap_{single,page} is a nop depends upon the
+  * configuration.
+  */
+--- linux-2.4.27/include/asm-arm/proc-armv/processor.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/proc-armv/processor.h
+@@ -54,7 +54,9 @@
+               regs->ARM_cpsr = USR_MODE;                              \
+       else                                                            \
+               regs->ARM_cpsr = USR26_MODE;                            \
+-      regs->ARM_pc = pc;              /* pc */                        \
++        if (elf_hwcap & HWCAP_THUMB && pc & 1)                          \
++                regs->ARM_cpsr |= PSR_T_BIT;                            \
++        regs->ARM_pc = pc & ~1;         /* pc */                        \
+       regs->ARM_sp = sp;              /* sp */                        \
+       regs->ARM_r2 = stack[2];        /* r2 (envp) */                 \
+       regs->ARM_r1 = stack[1];        /* r1 (argv) */                 \
+--- linux-2.4.27/include/asm-arm/proc-armv/ptrace.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/proc-armv/ptrace.h
+@@ -33,6 +33,16 @@
+ #define CC_N_BIT      (1 << 31)
+ #define PCMASK                0
++/* 2.5 versions */
++#define PSR_T_BIT     0x00000020
++#define PSR_F_BIT     0x00000040
++#define PSR_I_BIT     0x00000080
++#define PSR_J_BIT     0x01000000
++#define PSR_V_BIT     0x10000000
++#define PSR_C_BIT     0x20000000
++#define PSR_Z_BIT     0x40000000
++#define PSR_N_BIT     0x80000000
++
+ #ifndef __ASSEMBLY__
+ /* this struct defines the way the registers are stored on the
+--- linux-2.4.27/include/asm-arm/proc-armv/uaccess.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/proc-armv/uaccess.h
+@@ -12,12 +12,11 @@
+  * Note that this is actually 0x1,0000,0000
+  */
+ #define KERNEL_DS     0x00000000
+-#define USER_DS               PAGE_OFFSET
++#define USER_DS               TASK_SIZE
+ static inline void set_fs (mm_segment_t fs)
+ {
+       current->addr_limit = fs;
+-
+       modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER);
+ }
+@@ -38,7 +37,7 @@
+               : "cc"); \
+       (flag == 0); })
+-#define __put_user_asm_byte(x,addr,err)                               \
++#define __put_user_asm_byte(x,__pu_addr,err)                  \
+       __asm__ __volatile__(                                   \
+       "1:     strbt   %1,[%2],#0\n"                           \
+       "2:\n"                                                  \
+@@ -51,18 +50,18 @@
+       "       .align  3\n"                                    \
+       "       .long   1b, 3b\n"                               \
+       "       .previous"                                      \
+-      : "=r" (err)                                            \
+-      : "r" (x), "r" (addr), "i" (-EFAULT), "0" (err))
++      : "+r" (err)                                            \
++      : "r" (x), "r" (__pu_addr), "i" (-EFAULT)               \
++      : "cc")
+-#define __put_user_asm_half(x,addr,err)                               \
++#define __put_user_asm_half(x,__pu_addr,err)                  \
+ ({                                                            \
+       unsigned long __temp = (unsigned long)(x);              \
+-      unsigned long __ptr  = (unsigned long)(addr);           \
+-      __put_user_asm_byte(__temp, __ptr, err);                \
+-      __put_user_asm_byte(__temp >> 8, __ptr + 1, err);       \
++      __put_user_asm_byte(__temp, __pu_addr, err);            \
++      __put_user_asm_byte(__temp >> 8, __pu_addr + 1, err);   \
+ })
+-#define __put_user_asm_word(x,addr,err)                               \
++#define __put_user_asm_word(x,__pu_addr,err)                  \
+       __asm__ __volatile__(                                   \
+       "1:     strt    %1,[%2],#0\n"                           \
+       "2:\n"                                                  \
+@@ -75,8 +74,28 @@
+       "       .align  3\n"                                    \
+       "       .long   1b, 3b\n"                               \
+       "       .previous"                                      \
+-      : "=r" (err)                                            \
+-      : "r" (x), "r" (addr), "i" (-EFAULT), "0" (err))
++      : "+r" (err)                                            \
++      : "r" (x), "r" (__pu_addr), "i" (-EFAULT)               \
++      : "cc")
++
++#define __put_user_asm_dword(x,__pu_addr,err)                 \
++      __asm__ __volatile__(                                   \
++      "1:     strt    %R2, [%1], #4\n"                        \
++      "2:     strt    %Q2, [%1], #0\n"                        \
++      "3:\n"                                                  \
++      "       .section .fixup,\"ax\"\n"                       \
++      "       .align  2\n"                                    \
++      "4:     mov     %0, %3\n"                               \
++      "       b       3b\n"                                   \
++      "       .previous\n"                                    \
++      "       .section __ex_table,\"a\"\n"                    \
++      "       .align  3\n"                                    \
++      "       .long   1b, 4b\n"                               \
++      "       .long   2b, 4b\n"                               \
++      "       .previous"                                      \
++      : "+r" (err), "+r" (__pu_addr)                          \
++      : "r" (x), "i" (-EFAULT)                                \
++      : "cc")
+ #define __get_user_asm_byte(x,addr,err)                               \
+       __asm__ __volatile__(                                   \
+@@ -92,18 +111,18 @@
+       "       .align  3\n"                                    \
+       "       .long   1b, 3b\n"                               \
+       "       .previous"                                      \
+-      : "=r" (err), "=&r" (x)                                 \
+-      : "r" (addr), "i" (-EFAULT), "0" (err))
++      : "+r" (err), "=&r" (x)                                 \
++      : "r" (addr), "i" (-EFAULT)                             \
++      : "cc")
+-#define __get_user_asm_half(x,addr,err)                               \
++#define __get_user_asm_half(x,__gu_addr,err)                  \
+ ({                                                            \
+-      unsigned long __b1, __b2, __ptr = (unsigned long)addr;  \
+-      __get_user_asm_byte(__b1, __ptr, err);                  \
+-      __get_user_asm_byte(__b2, __ptr + 1, err);              \
++      unsigned long __b1, __b2;                               \
++      __get_user_asm_byte(__b1, __gu_addr, err);              \
++      __get_user_asm_byte(__b2, __gu_addr + 1, err);          \
+       (x) = __b1 | (__b2 << 8);                               \
+ })
+-
+ #define __get_user_asm_word(x,addr,err)                               \
+       __asm__ __volatile__(                                   \
+       "1:     ldrt    %1,[%2],#0\n"                           \
+@@ -118,8 +137,9 @@
+       "       .align  3\n"                                    \
+       "       .long   1b, 3b\n"                               \
+       "       .previous"                                      \
+-      : "=r" (err), "=&r" (x)                                 \
+-      : "r" (addr), "i" (-EFAULT), "0" (err))
++      : "+r" (err), "=&r" (x)                                 \
++      : "r" (addr), "i" (-EFAULT)                             \
++      : "cc")
+ extern unsigned long __arch_copy_from_user(void *to, const void *from, unsigned long n);
+ #define __do_copy_from_user(to,from,n)                                \
+--- linux-2.4.27/include/asm-arm/proc-fns.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/proc-fns.h
+@@ -76,6 +76,30 @@
+ #   define CPU_NAME arm926
+ #  endif
+ # endif
++# ifdef CONFIG_CPU_ARM1020
++#  ifdef CPU_NAME
++#   undef  MULTI_CPU
++#   define MULTI_CPU
++#  else
++#   define CPU_NAME arm1020
++#  endif
++# endif
++# ifdef CONFIG_CPU_ARM1020E
++#  ifdef CPU_NAME
++#   undef  MULTI_CPU
++#   define MULTI_CPU
++#  else
++#   define CPU_NAME arm1020E
++#  endif
++# endif
++# ifdef CONFIG_CPU_ARM1022
++#  ifdef CPU_NAME
++#   undef  MULTI_CPU
++#   define MULTI_CPU
++#  else
++#   define CPU_NAME arm1022
++#  endif
++# endif
+ # ifdef CONFIG_CPU_ARM1026
+ #  ifdef CPU_NAME
+ #   undef  MULTI_CPU
+--- linux-2.4.27/include/asm-arm/processor.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/processor.h
+@@ -43,6 +43,7 @@
+ #include <asm/atomic.h>
+ #include <asm/ptrace.h>
+ #include <asm/arch/memory.h>
++#include <asm/elf.h>
+ #include <asm/proc/processor.h>
+ #include <asm/types.h>
+--- linux-2.4.27/include/asm-arm/system.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/system.h
+@@ -29,6 +29,10 @@
+ void die_if_kernel(const char *str, struct pt_regs *regs, int err);
++void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
++                                     struct pt_regs *),
++                   int sig, const char *name);
++
+ #include <asm/proc-fns.h>
+ #define xchg(ptr,x) \
+@@ -89,6 +93,14 @@
+ #define sti()                 local_irq_enable()
+ #define clf()                 __clf()
+ #define stf()                 __stf()
++
++#define irqs_disabled()                       \
++({                                    \
++      unsigned long flags;            \
++      local_save_flags(flags);        \
++      flags & PSR_I_BIT;              \
++})
++
+ #define save_flags(x)         local_save_flags(x)
+ #define restore_flags(x)      local_irq_restore(x)
+ #define save_flags_cli(x)     local_irq_save(x)
+--- linux-2.4.27/include/asm-arm/termios.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/termios.h
+@@ -47,6 +47,8 @@
+ #define TIOCM_OUT2    0x4000
+ #define TIOCM_LOOP    0x8000
++#define TIOCM_MODEM_BITS      TIOCM_OUT2      /* IRDA support */
++
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+ /* line disciplines */
+--- linux-2.4.27/include/asm-arm/uaccess.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/uaccess.h
+@@ -74,14 +74,14 @@
+          __asm__ __volatile__ ("bl    __get_user_" #__s               \
+               : "=&r" (__e), "=r" (__r1)                              \
+               : "0" (__p)                                             \
+-              : __i)
++              : __i, "cc")
+ #define get_user(x,p)                                                 \
+       ({                                                              \
+               const register typeof(*(p)) *__p asm("r0") = (p);       \
+               register typeof(*(p)) __r1 asm("r1");                   \
+               register int __e asm("r0");                             \
+-              switch (sizeof(*(p))) {                                 \
++              switch (sizeof(*(__p))) {                               \
+               case 1:                                                 \
+                       __get_user_x(__r1, __p, __e, 1, "lr");          \
+                       break;                                          \
+@@ -100,8 +100,31 @@
+               __e;                                                    \
+       })
+-#define __get_user(x,p)               __get_user_nocheck((x),(p),sizeof(*(p)))
+-#define __get_user_error(x,p,e)       __get_user_nocheck_error((x),(p),sizeof(*(p)),(e))
++#define __get_user(x,ptr)                                             \
++({                                                                    \
++      long __gu_err = 0;                                              \
++      __get_user_err((x),(ptr),__gu_err);                             \
++      __gu_err;                                                       \
++})
++
++#define __get_user_error(x,ptr,err)                                   \
++({                                                                    \
++      __get_user_err((x),(ptr),err);                                  \
++      (void) 0;                                                       \
++})
++
++#define __get_user_err(x,ptr,err)                                     \
++do {                                                                  \
++      unsigned long __gu_addr = (unsigned long)(ptr);                 \
++      unsigned long __gu_val;                                         \
++      switch (sizeof(*(ptr))) {                                       \
++      case 1: __get_user_asm_byte(__gu_val,__gu_addr,err);    break;  \
++      case 2: __get_user_asm_half(__gu_val,__gu_addr,err);    break;  \
++      case 4: __get_user_asm_word(__gu_val,__gu_addr,err);    break;  \
++      default: (__gu_val) = __get_user_bad();                         \
++      }                                                               \
++      (x) = (__typeof__(*(ptr)))__gu_val;                             \
++} while (0)
+ extern int __put_user_1(void *, unsigned int);
+ extern int __put_user_2(void *, unsigned int);
+@@ -113,22 +136,22 @@
+          __asm__ __volatile__ ("bl    __put_user_" #__s               \
+               : "=&r" (__e)                                           \
+               : "0" (__p), "r" (__r1)                                 \
+-              : __i)
++              : __i, "cc")
+ #define put_user(x,p)                                                 \
+       ({                                                              \
+               const register typeof(*(p)) __r1 asm("r1") = (x);       \
+               const register typeof(*(p)) *__p asm("r0") = (p);       \
+               register int __e asm("r0");                             \
+-              switch (sizeof(*(p))) {                                 \
++              switch (sizeof(*(__p))) {                               \
+               case 1:                                                 \
+-                      __put_user_x(__r1, __p, __e, 1, "r2", "lr");    \
++                      __put_user_x(__r1, __p, __e, 1, "ip", "lr");    \
+                       break;                                          \
+               case 2:                                                 \
+-                      __put_user_x(__r1, __p, __e, 2, "r2", "lr");    \
++                      __put_user_x(__r1, __p, __e, 2, "ip", "lr");    \
+                       break;                                          \
+               case 4:                                                 \
+-                      __put_user_x(__r1, __p, __e, 4, "r2", "lr");    \
++                      __put_user_x(__r1, __p, __e, 4, "ip", "lr");    \
+                       break;                                          \
+               case 8:                                                 \
+                       __put_user_x(__r1, __p, __e, 8, "ip", "lr");    \
+@@ -138,8 +161,31 @@
+               __e;                                                    \
+       })
+-#define __put_user(x,p)               __put_user_nocheck((__typeof(*(p)))(x),(p),sizeof(*(p)))
+-#define __put_user_error(x,p,e)       __put_user_nocheck_error((x),(p),sizeof(*(p)),(e))
++#define __put_user(x,ptr)                                             \
++({                                                                    \
++      long __pu_err = 0;                                              \
++      __put_user_err((x),(ptr),__pu_err);                             \
++      __pu_err;                                                       \
++})
++
++#define __put_user_error(x,ptr,err)                                   \
++({                                                                    \
++      __put_user_err((x),(ptr),err);                                  \
++      (void) 0;                                                       \
++})
++
++#define __put_user_err(x,ptr,err)                                     \
++do {                                                                  \
++      unsigned long __pu_addr = (unsigned long)(ptr);                 \
++      __typeof__(*(ptr)) __pu_val = (x);                              \
++      switch (sizeof(*(ptr))) {                                       \
++      case 1: __put_user_asm_byte(__pu_val,__pu_addr,err);    break;  \
++      case 2: __put_user_asm_half(__pu_val,__pu_addr,err);    break;  \
++      case 4: __put_user_asm_word(__pu_val,__pu_addr,err);    break;  \
++      case 8: __put_user_asm_dword(__pu_val,__pu_addr,err);   break;  \
++      default: __put_user_bad();                                      \
++      }                                                               \
++} while (0)
+ static __inline__ unsigned long copy_from_user(void *to, const void *from, unsigned long n)
+ {
+@@ -209,82 +255,4 @@
+       return res;
+ }
+-/*
+- * These are the work horses of the get/put_user functions
+- */
+-#if 0
+-#define __get_user_check(x,ptr,size)                                  \
+-({                                                                    \
+-      long __gu_err = -EFAULT, __gu_val = 0;                          \
+-      const __typeof__(*(ptr)) *__gu_addr = (ptr);                    \
+-      if (access_ok(VERIFY_READ,__gu_addr,size)) {                    \
+-              __gu_err = 0;                                           \
+-              __get_user_size(__gu_val,__gu_addr,(size),__gu_err);    \
+-      }                                                               \
+-      (x) = (__typeof__(*(ptr)))__gu_val;                             \
+-      __gu_err;                                                       \
+-})
+-#endif
+-
+-#define __get_user_nocheck(x,ptr,size)                                        \
+-({                                                                    \
+-      long __gu_err = 0, __gu_val;                                    \
+-      __get_user_size(__gu_val,(ptr),(size),__gu_err);                \
+-      (x) = (__typeof__(*(ptr)))__gu_val;                             \
+-      __gu_err;                                                       \
+-})
+-
+-#define __get_user_nocheck_error(x,ptr,size,err)                      \
+-({                                                                    \
+-      long __gu_val;                                                  \
+-      __get_user_size(__gu_val,(ptr),(size),(err));                   \
+-      (x) = (__typeof__(*(ptr)))__gu_val;                             \
+-      (void) 0;                                                       \
+-})
+-
+-#define __put_user_check(x,ptr,size)                                  \
+-({                                                                    \
+-      long __pu_err = -EFAULT;                                        \
+-      __typeof__(*(ptr)) *__pu_addr = (ptr);                          \
+-      if (access_ok(VERIFY_WRITE,__pu_addr,size)) {                   \
+-              __pu_err = 0;                                           \
+-              __put_user_size((x),__pu_addr,(size),__pu_err);         \
+-      }                                                               \
+-      __pu_err;                                                       \
+-})
+-
+-#define __put_user_nocheck(x,ptr,size)                                        \
+-({                                                                    \
+-      long __pu_err = 0;                                              \
+-      __typeof__(*(ptr)) *__pu_addr = (ptr);                          \
+-      __put_user_size((x),__pu_addr,(size),__pu_err);                 \
+-      __pu_err;                                                       \
+-})
+-
+-#define __put_user_nocheck_error(x,ptr,size,err)                      \
+-({                                                                    \
+-      __put_user_size((x),(ptr),(size),err);                          \
+-      (void) 0;                                                       \
+-})
+-
+-#define __get_user_size(x,ptr,size,retval)                            \
+-do {                                                                  \
+-      switch (size) {                                                 \
+-      case 1: __get_user_asm_byte(x,ptr,retval);      break;          \
+-      case 2: __get_user_asm_half(x,ptr,retval);      break;          \
+-      case 4: __get_user_asm_word(x,ptr,retval);      break;          \
+-      default: (x) = __get_user_bad();                                \
+-      }                                                               \
+-} while (0)
+-
+-#define __put_user_size(x,ptr,size,retval)                            \
+-do {                                                                  \
+-      switch (size) {                                                 \
+-      case 1: __put_user_asm_byte(x,ptr,retval);      break;          \
+-      case 2: __put_user_asm_half(x,ptr,retval);      break;          \
+-      case 4: __put_user_asm_word(x,ptr,retval);      break;          \
+-      default: __put_user_bad();                                      \
+-      }                                                               \
+-} while (0)
+-
+ #endif /* _ASMARM_UACCESS_H */
+--- linux-2.4.27/include/asm-arm/unistd.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-arm/unistd.h
+@@ -31,7 +31,7 @@
+ #define __NR_write                    (__NR_SYSCALL_BASE+  4)
+ #define __NR_open                     (__NR_SYSCALL_BASE+  5)
+ #define __NR_close                    (__NR_SYSCALL_BASE+  6)
+-#define __NR_waitpid                  (__NR_SYSCALL_BASE+  7)
++                                      /* 7 was sys_waitpid */
+ #define __NR_creat                    (__NR_SYSCALL_BASE+  8)
+ #define __NR_link                     (__NR_SYSCALL_BASE+  9)
+ #define __NR_unlink                   (__NR_SYSCALL_BASE+ 10)
+@@ -41,8 +41,8 @@
+ #define __NR_mknod                    (__NR_SYSCALL_BASE+ 14)
+ #define __NR_chmod                    (__NR_SYSCALL_BASE+ 15)
+ #define __NR_lchown                   (__NR_SYSCALL_BASE+ 16)
+-#define __NR_break                    (__NR_SYSCALL_BASE+ 17)
+-
++                                      /* 17 was sys_break */
++                                      /* 18 was sys_stat */
+ #define __NR_lseek                    (__NR_SYSCALL_BASE+ 19)
+ #define __NR_getpid                   (__NR_SYSCALL_BASE+ 20)
+ #define __NR_mount                    (__NR_SYSCALL_BASE+ 21)
+@@ -52,14 +52,14 @@
+ #define __NR_stime                    (__NR_SYSCALL_BASE+ 25)
+ #define __NR_ptrace                   (__NR_SYSCALL_BASE+ 26)
+ #define __NR_alarm                    (__NR_SYSCALL_BASE+ 27)
+-
++                                      /* 28 was sys_fstat */
+ #define __NR_pause                    (__NR_SYSCALL_BASE+ 29)
+ #define __NR_utime                    (__NR_SYSCALL_BASE+ 30)
+-#define __NR_stty                     (__NR_SYSCALL_BASE+ 31)
+-#define __NR_gtty                     (__NR_SYSCALL_BASE+ 32)
++                                      /* 31 was sys_stty */
++                                      /* 32 was sys_gtty */
+ #define __NR_access                   (__NR_SYSCALL_BASE+ 33)
+ #define __NR_nice                     (__NR_SYSCALL_BASE+ 34)
+-#define __NR_ftime                    (__NR_SYSCALL_BASE+ 35)
++                                      /* 35 was sys_ftime */
+ #define __NR_sync                     (__NR_SYSCALL_BASE+ 36)
+ #define __NR_kill                     (__NR_SYSCALL_BASE+ 37)
+ #define __NR_rename                   (__NR_SYSCALL_BASE+ 38)
+@@ -68,22 +68,23 @@
+ #define __NR_dup                      (__NR_SYSCALL_BASE+ 41)
+ #define __NR_pipe                     (__NR_SYSCALL_BASE+ 42)
+ #define __NR_times                    (__NR_SYSCALL_BASE+ 43)
+-#define __NR_prof                     (__NR_SYSCALL_BASE+ 44)
++                                      /* 44 was sys_prof */
+ #define __NR_brk                      (__NR_SYSCALL_BASE+ 45)
+ #define __NR_setgid                   (__NR_SYSCALL_BASE+ 46)
+ #define __NR_getgid                   (__NR_SYSCALL_BASE+ 47)
+-#define __NR_signal                   (__NR_SYSCALL_BASE+ 48)
++                                      /* 48 was sys_signal */
+ #define __NR_geteuid                  (__NR_SYSCALL_BASE+ 49)
+ #define __NR_getegid                  (__NR_SYSCALL_BASE+ 50)
+ #define __NR_acct                     (__NR_SYSCALL_BASE+ 51)
+ #define __NR_umount2                  (__NR_SYSCALL_BASE+ 52)
+-#define __NR_lock                     (__NR_SYSCALL_BASE+ 53)
++                                      /* 53 was sys_lock */
+ #define __NR_ioctl                    (__NR_SYSCALL_BASE+ 54)
+ #define __NR_fcntl                    (__NR_SYSCALL_BASE+ 55)
+-#define __NR_mpx                      (__NR_SYSCALL_BASE+ 56)
++                                      /* 56 was sys_mpx */
+ #define __NR_setpgid                  (__NR_SYSCALL_BASE+ 57)
+ #define __NR_ulimit                   (__NR_SYSCALL_BASE+ 58)
+-
++                                      /* 58 was sys_ulimit */
++                                      /* 59 was sys_olduname */
+ #define __NR_umask                    (__NR_SYSCALL_BASE+ 60)
+ #define __NR_chroot                   (__NR_SYSCALL_BASE+ 61)
+ #define __NR_ustat                    (__NR_SYSCALL_BASE+ 62)
+@@ -92,8 +93,8 @@
+ #define __NR_getpgrp                  (__NR_SYSCALL_BASE+ 65)
+ #define __NR_setsid                   (__NR_SYSCALL_BASE+ 66)
+ #define __NR_sigaction                        (__NR_SYSCALL_BASE+ 67)
+-#define __NR_sgetmask                 (__NR_SYSCALL_BASE+ 68)
+-#define __NR_ssetmask                 (__NR_SYSCALL_BASE+ 69)
++                                      /* 68 was sys_sgetmask */
++                                      /* 69 was sys_ssetmask */
+ #define __NR_setreuid                 (__NR_SYSCALL_BASE+ 70)
+ #define __NR_setregid                 (__NR_SYSCALL_BASE+ 71)
+ #define __NR_sigsuspend                       (__NR_SYSCALL_BASE+ 72)
+@@ -108,7 +109,7 @@
+ #define __NR_setgroups                        (__NR_SYSCALL_BASE+ 81)
+ #define __NR_select                   (__NR_SYSCALL_BASE+ 82)
+ #define __NR_symlink                  (__NR_SYSCALL_BASE+ 83)
+-
++                                      /* 84 was sys_lstat */
+ #define __NR_readlink                 (__NR_SYSCALL_BASE+ 85)
+ #define __NR_uselib                   (__NR_SYSCALL_BASE+ 86)
+ #define __NR_swapon                   (__NR_SYSCALL_BASE+ 87)
+@@ -122,10 +123,10 @@
+ #define __NR_fchown                   (__NR_SYSCALL_BASE+ 95)
+ #define __NR_getpriority              (__NR_SYSCALL_BASE+ 96)
+ #define __NR_setpriority              (__NR_SYSCALL_BASE+ 97)
+-#define __NR_profil                   (__NR_SYSCALL_BASE+ 98)
++                                      /* 98 was sys_profil */
+ #define __NR_statfs                   (__NR_SYSCALL_BASE+ 99)
+ #define __NR_fstatfs                  (__NR_SYSCALL_BASE+100)
+-#define __NR_ioperm                   (__NR_SYSCALL_BASE+101)
++                                      /* 101 was sys_ioperm */
+ #define __NR_socketcall                       (__NR_SYSCALL_BASE+102)
+ #define __NR_syslog                   (__NR_SYSCALL_BASE+103)
+ #define __NR_setitimer                        (__NR_SYSCALL_BASE+104)
+@@ -133,10 +134,10 @@
+ #define __NR_stat                     (__NR_SYSCALL_BASE+106)
+ #define __NR_lstat                    (__NR_SYSCALL_BASE+107)
+ #define __NR_fstat                    (__NR_SYSCALL_BASE+108)
+-
+-
++                                      /* 109 was sys_uname */
++                                      /* 110 was sys_iopl */
+ #define __NR_vhangup                  (__NR_SYSCALL_BASE+111)
+-#define __NR_idle                     (__NR_SYSCALL_BASE+112)
++                                      /* 112 was sys_idle */
+ #define __NR_syscall                  (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */
+ #define __NR_wait4                    (__NR_SYSCALL_BASE+114)
+ #define __NR_swapoff                  (__NR_SYSCALL_BASE+115)
+@@ -147,7 +148,7 @@
+ #define __NR_clone                    (__NR_SYSCALL_BASE+120)
+ #define __NR_setdomainname            (__NR_SYSCALL_BASE+121)
+ #define __NR_uname                    (__NR_SYSCALL_BASE+122)
+-#define __NR_modify_ldt                       (__NR_SYSCALL_BASE+123)
++                                      /* 123 was sys_modify_ldt */
+ #define __NR_adjtimex                 (__NR_SYSCALL_BASE+124)
+ #define __NR_mprotect                 (__NR_SYSCALL_BASE+125)
+ #define __NR_sigprocmask              (__NR_SYSCALL_BASE+126)
+@@ -161,7 +162,7 @@
+ #define __NR_bdflush                  (__NR_SYSCALL_BASE+134)
+ #define __NR_sysfs                    (__NR_SYSCALL_BASE+135)
+ #define __NR_personality              (__NR_SYSCALL_BASE+136)
+-#define __NR_afs_syscall              (__NR_SYSCALL_BASE+137) /* Syscall for Andrew File System */
++                                      /* 137 was sys_afs_syscall */
+ #define __NR_setfsuid                 (__NR_SYSCALL_BASE+138)
+ #define __NR_setfsgid                 (__NR_SYSCALL_BASE+139)
+ #define __NR__llseek                  (__NR_SYSCALL_BASE+140)
+@@ -190,7 +191,7 @@
+ #define __NR_mremap                   (__NR_SYSCALL_BASE+163)
+ #define __NR_setresuid                        (__NR_SYSCALL_BASE+164)
+ #define __NR_getresuid                        (__NR_SYSCALL_BASE+165)
+-#define __NR_vm86                     (__NR_SYSCALL_BASE+166)
++                                      /* 166 was sys_vm86 */
+ #define __NR_query_module             (__NR_SYSCALL_BASE+167)
+ #define __NR_poll                     (__NR_SYSCALL_BASE+168)
+ #define __NR_nfsservctl                       (__NR_SYSCALL_BASE+169)
+--- linux-2.4.27/include/asm-i386/ide.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-i386/ide.h
+@@ -23,39 +23,15 @@
+ # endif
+ #endif
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+-      switch (base) {
+-              case 0x1f0: return 14;
+-              case 0x170: return 15;
+-              case 0x1e8: return 11;
+-              case 0x168: return 10;
+-              case 0x1e0: return 8;
+-              case 0x160: return 12;
+-              default:
+-                      return 0;
+-      }
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+-      switch (index) {
+-              case 0: return 0x1f0;
+-              case 1: return 0x170;
+-              case 2: return 0x1e8;
+-              case 3: return 0x168;
+-              case 4: return 0x1e0;
+-              case 5: return 0x160;
+-              default:
+-                      return 0;
+-      }
+-}
++#define ide_default_io_base(i)        ((ide_ioreg_t)0)
++#define ide_default_irq(b)    (0)
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+       ide_ioreg_t reg = data_port;
+       int i;
++      memset(hw, 0, sizeof(*hw));
+       for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+               hw->io_ports[i] = reg;
+               reg += 1;
+@@ -63,7 +39,7 @@
+       if (ctrl_port) {
+               hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+       } else {
+-              hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
++              hw->io_ports[IDE_CONTROL_OFFSET] = data_port + 0x206;
+       }
+       if (irq != NULL)
+               *irq = 0;
+@@ -74,14 +50,25 @@
+ {
+ #ifndef CONFIG_BLK_DEV_IDEPCI
+       hw_regs_t hw;
+-      int index;
+-      for(index = 0; index < MAX_HWIFS; index++) {
+-              memset(&hw, 0, sizeof hw);
+-              ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+-              hw.irq = ide_default_irq(ide_default_io_base(index));
+-              ide_register_hw(&hw, NULL);
+-      }
++      ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
++      hw.irq = 14;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x170, 0x376, NULL);
++      hw.irq = 15;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x1e8, 0x3ee, NULL);
++      hw.irq = 11;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x168, 0x36e, NULL);
++      hw.irq = 10;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x1e0, 0x3e6, NULL);
++      hw.irq = 8;
++      ide_register_hw(&hw, NULL);
++      ide_init_hwif_ports(&hw, 0x160, 0x366, NULL);
++      hw.irq = 12;
++      ide_register_hw(&hw, NULL);
+ #endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+--- linux-2.4.27/include/asm-i386/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-i386/param.h
+@@ -3,6 +3,16 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#if HZ == 100
++/* X86 is defined to provide userspace with a world where HZ=100
++   We have to do this, (x*const)/const2 isnt optimised out because its not
++   a null operation as it might overflow.. */
++#define hz_to_std(a) (a)
++#else
++#define hz_to_std(a) ((a)*(100/HZ)+((a)*(100%HZ))/HZ)
++#endif
++#endif
+ #endif
+ #define EXEC_PAGESIZE 4096
+--- linux-2.4.27/include/asm-i386/pgtable.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-i386/pgtable.h
+@@ -361,7 +361,6 @@
+ #endif /* !__ASSEMBLY__ */
+ /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+-#define PageSkip(page)                (0)
+ #define kern_addr_valid(addr) (1)
+ #define io_remap_page_range remap_page_range
+--- linux-2.4.27/include/asm-ia64/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-ia64/param.h
+@@ -4,12 +4,14 @@
+ /*
+  * Fundamental kernel parameters.
+  *
+- * Based on <asm-i386/param.h>.
+- *
+- * Modified 1998, 1999, 2002-2003
+- *    David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
++ * Copyright (C) 1998, 1999, 2002-2003 Hewlett-Packard Co
++ *    David Mosberger-Tang <davidm@hpl.hp.com>
+  */
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
++
+ #define EXEC_PAGESIZE 65536
+ #ifndef NGROUPS
+--- linux-2.4.27/include/asm-m68k/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-m68k/param.h
+@@ -3,6 +3,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 8192
+--- linux-2.4.27/include/asm-mips/ide.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-mips/ide.h
+@@ -27,7 +27,7 @@
+       int (*ide_default_irq)(ide_ioreg_t base);
+       ide_ioreg_t (*ide_default_io_base)(int index);
+       void (*ide_init_hwif_ports)(hw_regs_t *hw, ide_ioreg_t data_port,
+-                                  ide_ioreg_t ctrl_port, int *irq);
++                                  ide_ioreg_t ctrl_port, int *irq);
+ };
+ extern struct ide_ops *ide_ops;
+--- linux-2.4.27/include/asm-ppc/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-ppc/param.h
+@@ -3,6 +3,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 4096
+--- linux-2.4.27/include/asm-s390/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-s390/param.h
+@@ -11,6 +11,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 4096
+--- linux-2.4.27/include/asm-sh/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-sh/param.h
+@@ -3,6 +3,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 4096
+--- linux-2.4.27/include/asm-sparc/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-sparc/param.h
+@@ -4,6 +4,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 8192    /* Thanks for sun4's we carry baggage... */
+--- linux-2.4.27/include/asm-sparc64/ide.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-sparc64/ide.h
+@@ -25,21 +25,12 @@
+ # endif
+ #endif
+-static __inline__ int ide_default_irq(ide_ioreg_t base)
+-{
+-      return 0;
+-}
+-
+-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+-{
+-      return 0;
+-}
+-
+ static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
+ {
+-      ide_ioreg_t reg =  data_port;
++      ide_ioreg_t reg = data_port;
+       int i;
++      memset(&hw, 0, sizeof(hw));
+       for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+               hw->io_ports[i] = reg;
+               reg += 1;
+@@ -60,16 +51,6 @@
+  */
+ static __inline__ void ide_init_default_hwifs(void)
+ {
+-#ifndef CONFIG_BLK_DEV_IDEPCI
+-      hw_regs_t hw;
+-      int index;
+-
+-      for (index = 0; index < MAX_HWIFS; index++) {
+-              ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL);
+-              hw.irq = ide_default_irq(ide_default_io_base(index));
+-              ide_register_hw(&hw, NULL);
+-      }
+-#endif /* CONFIG_BLK_DEV_IDEPCI */
+ }
+ #undef  SUPPORT_SLOW_DATA_PORTS
+--- linux-2.4.27/include/asm-sparc64/param.h~2.4.27-vrs1
++++ linux-2.4.27/include/asm-sparc64/param.h
+@@ -4,6 +4,9 @@
+ #ifndef HZ
+ #define HZ 100
++#ifdef __KERNEL__
++#define hz_to_std(a) (a)
++#endif
+ #endif
+ #define EXEC_PAGESIZE 8192    /* Thanks for sun4's we carry baggage... */
+--- linux-2.4.27/include/linux/console_struct.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/console_struct.h
+@@ -7,6 +7,8 @@
+  * Fields marked with [#] must be set by the low-level driver.
+  * Fields marked with [!] can be changed by the low-level driver
+  * to achieve effects such as fast scrolling by changing the origin.
++ *
++ *  11/07/1998        RMK     Changed vc_state to be a function pointer
+  */
+ #ifndef _LINUX_CONSOLE_STRUCT_H_
+@@ -34,7 +36,11 @@
+       unsigned short  vc_s_complement_mask;   /* Saved mouse pointer mask */
+       unsigned int    vc_x, vc_y;             /* Cursor position */
+       unsigned int    vc_top, vc_bottom;      /* Scrolling region */
++#ifdef CONSOLE_WIP
++      int (*vc_state)(int currcons, struct tty_struct *tty, unsigned int c);
++#else
+       unsigned int    vc_state;               /* Escape sequence parser state */
++#endif
+       unsigned int    vc_npar,vc_par[NPAR];   /* Parameters of current escape sequence */
+       unsigned long   vc_origin;              /* [!] Start of real screen */
+       unsigned long   vc_scr_end;             /* [!] End of real screen */
+--- /dev/null
++++ linux-2.4.27/include/linux/cpufreq.h
+@@ -0,0 +1,94 @@
++/*
++ *  linux/include/linux/cpufreq.h
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ * $Id: cpufreq.h,v 1.5.2.1 2002/05/03 13:26:27 rmk 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_CPUFREQ_H
++#define _LINUX_CPUFREQ_H
++
++#include <linux/config.h>
++#include <linux/notifier.h>
++
++#ifndef CONFIG_SMP
++#define cpufreq_current(cpu)  ((void)(cpu), __cpufreq_cur)
++#define cpufreq_max(cpu)      ((void)(cpu), __cpufreq_max)
++#define cpufreq_min(cpu)      ((void)(cpu), __cpufreq_min)
++#else
++/*
++ * Should be something like:
++ *
++ * typedef struct {
++ *    u_int current;
++ *    u_int max;
++ *    u_int min;
++ * } __cacheline_aligned cpufreq_info_t;
++ *
++ * static cpufreq_info_t cpufreq_info;
++ *
++ * #define cpufreq_current(cpu)       (cpufreq_info[cpu].current)
++ * #define cpufreq_max(cpu)   (cpufreq_info[cpu].max)
++ * #define cpufreq_min(cpu)   (cpufreq_info[cpu].min)
++ *
++ * Maybe we should find some other per-cpu structure to
++ * bury this in?
++ */
++#error fill in SMP version
++#endif
++
++struct cpufreq_info {
++      unsigned int old_freq;
++      unsigned int new_freq;
++};
++
++/*
++ * The max and min frequency rates that the registered device
++ * can tolerate.  Never set any element this structure directly -
++ * always use cpu_updateminmax.
++ */
++struct cpufreq_minmax {
++      unsigned int min_freq;
++      unsigned int max_freq;
++      unsigned int cur_freq;
++      unsigned int new_freq;
++};
++
++static inline
++void cpufreq_updateminmax(void *arg, unsigned int min, unsigned int max)
++{
++      struct cpufreq_minmax *minmax = arg;
++
++      if (minmax->min_freq < min)
++              minmax->min_freq = min;
++      if (minmax->max_freq > max)
++              minmax->max_freq = max;
++}
++
++#define CPUFREQ_MINMAX                (0)
++#define CPUFREQ_PRECHANGE     (1)
++#define CPUFREQ_POSTCHANGE    (2)
++
++int cpufreq_register_notifier(struct notifier_block *nb);
++int cpufreq_unregister_notifier(struct notifier_block *nb);
++
++int cpufreq_setmax(void);
++int cpufreq_restore(void);
++int cpufreq_set(unsigned int khz);
++unsigned int cpufreq_get(int cpu);
++
++/*
++ * These two functions are only available at init time.
++ */
++void cpufreq_init(unsigned int khz,
++                unsigned int min_freq,
++                unsigned int max_freq);
++
++void cpufreq_setfunctions(unsigned int (*validate)(unsigned int),
++                        void (*setspeed)(unsigned int));
++
++#endif
+--- linux-2.4.27/include/linux/i2c-id.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/i2c-id.h
+@@ -20,16 +20,16 @@
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
+ /* ------------------------------------------------------------------------- */
+-/* $Id: i2c-id.h,v 1.35 2001/08/12 17:22:20 mds Exp $ */
++/* $Id: i2c-id.h,v 1.52 2002/07/10 13:28:44 abz Exp $ */
+ #ifndef I2C_ID_H
+ #define I2C_ID_H
+ /*
+  * This file is part of the i2c-bus package and contains the identifier
+  * values for drivers, adapters and other folk populating these serial
+- * worlds. 
++ * worlds.
+  *
+- * These will change often (i.e. additions) , therefore this has been 
++ * These will change often (i.e. additions) , therefore this has been
+  * separated from the functional interface definitions of the i2c api.
+  *
+  */
+@@ -38,10 +38,10 @@
+  * ---- Driver types -----------------------------------------------------
+  *       device id name + number        function description, i2c address(es)
+  *
+- *  Range 1000-1999 range is defined in sensors/sensors.h 
+- *  Range 0x100 - 0x1ff is for V4L2 Common Components 
++ *  Range 1000-1999 range is defined in sensors/sensors.h
++ *  Range 0x100 - 0x1ff is for V4L2 Common Components
+  *  Range 0xf000 - 0xffff is reserved for local experimentation, and should
+- *        never be used in official drivers 
++ *        never be used in official drivers
+  */
+ #define I2C_DRIVERID_MSP3400   1
+@@ -90,7 +90,12 @@
+ #define I2C_DRIVERID_DRP3510  43     /* ADR decoder (Astra Radio)     */
+ #define I2C_DRIVERID_SP5055   44     /* Satellite tuner               */
+ #define I2C_DRIVERID_STV0030  45     /* Multipurpose switch           */
++#define I2C_DRIVERID_SAA7108  46     /* video decoder, image scaler   */
++#define I2C_DRIVERID_DS1307   47     /* DS1307 real time clock        */
+ #define I2C_DRIVERID_ADV7175  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_MAX1617  56     /* temp sensor                   */
+ #define I2C_DRIVERID_SAA7191  57     /* video decoder                 */
+ #define I2C_DRIVERID_INDYCAM  58     /* SGI IndyCam                   */
+@@ -102,8 +107,10 @@
+ #define I2C_DRIVERID_I2CDEV   900
+ #define I2C_DRIVERID_I2CPROC  901
++#define I2C_DRIVERID_ARP        902    /* SMBus ARP Client              */
++#define I2C_DRIVERID_ALERT      903    /* SMBus Alert Responder Client  */
+-/* IDs --   Use DRIVERIDs 1000-1999 for sensors. 
++/* IDs --   Use DRIVERIDs 1000-1999 for sensors.
+    These were originally in sensors.h in the lm_sensors package */
+ #define I2C_DRIVERID_LM78 1002
+ #define I2C_DRIVERID_LM75 1003
+@@ -131,6 +138,12 @@
+ #define I2C_DRIVERID_ADM1024 1025
+ #define I2C_DRIVERID_IT87 1026
+ #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */
++#define I2C_DRIVERID_FSCPOS 1028
++#define I2C_DRIVERID_FSCSCY 1029
++#define I2C_DRIVERID_PCF8591 1030
++#define I2C_DRIVERID_SMSC47M1 1031
++#define I2C_DRIVERID_VT1211 1032
++#define I2C_DRIVERID_LM92 1033
+ /*
+  * ---- Adapter types ----------------------------------------------------
+@@ -147,7 +160,8 @@
+ #define I2C_ALGO_ISA  0x050000        /* lm_sensors ISA pseudo-adapter */
+ #define I2C_ALGO_SAA7146 0x060000     /* SAA 7146 video decoder bus   */
+ #define I2C_ALGO_ACB  0x070000        /* ACCESS.bus algorithm         */
+-
++#define I2C_ALGO_IIC    0x080000      /* ITE IIC bus */
++#define I2C_ALGO_SAA7134 0x090000
+ #define I2C_ALGO_EC     0x100000        /* ACPI embedded controller     */
+ #define I2C_ALGO_MPC8XX 0x110000      /* MPC8xx PowerPC I2C algorithm */
+@@ -156,13 +170,15 @@
+ #define I2C_ALGO_SGI  0x130000        /* SGI algorithm                */
++#define I2C_ALGO_OCP    0x120000      /* IBM or otherwise On-chip I2C algorithm */
++
+ #define I2C_ALGO_EXP  0x800000        /* experimental                 */
+ #define I2C_ALGO_MASK 0xff0000        /* Mask for algorithms          */
+ #define I2C_ALGO_SHIFT        0x10    /* right shift to get index values      */
+ #define I2C_HW_ADAPS  0x10000         /* # adapter types              */
+-#define I2C_HW_MASK   0xffff          
++#define I2C_HW_MASK   0xffff
+ /* hw specific modules that are defined per algorithm layer
+@@ -182,9 +198,13 @@
+ #define I2C_HW_B_I810 0x0a    /* Intel I810                           */
+ #define I2C_HW_B_VOO  0x0b    /* 3dfx Voodoo 3 / Banshee              */
+ #define I2C_HW_B_PPORT  0x0c  /* Primitive parallel port adapter      */
++#define I2C_HW_B_SAVG 0x0d    /* Savage 4                             */
+ #define I2C_HW_B_RIVA 0x10    /* Riva based graphics cards            */
+ #define I2C_HW_B_IOC  0x11    /* IOC bit-wiggling                     */
+ #define I2C_HW_B_TSUNA  0x12  /* DEC Tsunami chipset                  */
++#define I2C_HW_B_FRODO        0x13    /* 2d3D, Inc. SA-1110 Development Board */
++#define I2C_HW_B_OMAHA  0x14    /* Omaha I2C interface                  */
++#define I2C_HW_B_GUIDE  0x15    /* Guide bit-basher                     */
+ /* --- PCF 8584 based algorithms                                      */
+ #define I2C_HW_P_LP   0x00    /* Parallel port interface              */
+@@ -197,6 +217,12 @@
+ /* --- MPC8xx PowerPC adapters                                                */
+ #define I2C_HW_MPC8XX_EPON 0x00       /* Eponymous MPC8xx I2C adapter         */
++/* --- ITE based algorithms                                           */
++#define I2C_HW_I_IIC  0x00    /* controller on the ITE */
++
++/* --- PowerPC on-chip adapters                                               */
++#define I2C_HW_OCP 0x00       /* IBM on-chip I2C adapter      */
++
+ /* --- Broadcom SiByte adapters                                               */
+ #define I2C_HW_SIBYTE 0x00
+--- linux-2.4.27/include/linux/ide.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/ide.h
+@@ -287,7 +287,9 @@
+  * Check for an interrupt and acknowledge the interrupt status
+  */
+ struct hwif_s;
++struct ide_drive_s;
+ typedef int (ide_ack_intr_t)(struct hwif_s *);
++typedef void (ide_xfer_data_t)(struct ide_drive_s *, void *, unsigned int);
+ #ifndef NO_DMA
+ #define NO_DMA  255
+@@ -311,10 +313,14 @@
+ typedef struct hw_regs_s {
+       ide_ioreg_t     io_ports[IDE_NR_PORTS]; /* task file registers */
+       int             irq;                    /* our irq number */
+-      int             dma;                    /* our dma entry */
++      int             dma;                    /* our dma number */
+       ide_ack_intr_t  *ack_intr;              /* acknowledge interrupt */
++      ide_xfer_data_t *ide_output_data;       /* write data to i/face */
++      ide_xfer_data_t *ide_input_data;        /* read data from i/face */
++      ide_xfer_data_t *atapi_output_bytes;    /* write bytes to i/face */
++      ide_xfer_data_t *atapi_input_bytes;     /* read bytes from i/face */
+       void            *priv;                  /* interface specific data */
+-      hwif_chipset_t  chipset;
++      hwif_chipset_t  chipset;
+       sata_ioreg_t    sata_scr[SATA_NR_PORTS];
+       sata_ioreg_t    sata_misc[SATA_NR_PORTS];
+ } hw_regs_t;
+--- /dev/null
++++ linux-2.4.27/include/linux/l3/algo-bit.h
+@@ -0,0 +1,39 @@
++/*
++ *  linux/include/linux/l3/algo-bit.h
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ * L3 Bus bit-banging algorithm.  Derived from i2c-algo-bit.h by
++ * Simon G. Vogl.
++ */
++#ifndef L3_ALGO_BIT_H
++#define L3_ALGO_BIT_H 1
++
++#include <linux/l3/l3.h>
++
++struct l3_algo_bit_data {
++      void (*setdat) (void *data, int state);
++      void (*setclk) (void *data, int state);
++      void (*setmode)(void *data, int state);
++      void (*setdir) (void *data, int in);    /* set data direction */
++      int  (*getdat) (void *data);
++
++      void *data;
++
++      /* bus timings (us) */
++      int     data_hold;
++      int     data_setup;
++      int     clock_high;
++      int     mode_hold;
++      int     mode_setup;
++      int     mode;
++};
++
++int l3_bit_add_bus(struct l3_adapter *);
++int l3_bit_del_bus(struct l3_adapter *);
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/linux/l3/l3.h
+@@ -0,0 +1,208 @@
++/*
++ *  linux/include/linux/l3/l3.h
++ *
++ *  Copyright (C) 2001 Russell King, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ * Derived from i2c.h by Simon G. Vogl
++ */
++#ifndef L3_H
++#define L3_H
++
++struct l3_msg {
++      unsigned char   addr;   /* slave address        */
++      unsigned char   flags;          
++#define L3_M_RD               0x01
++#define L3_M_NOADDR   0x02
++      unsigned short  len;    /* msg length           */
++      unsigned char   *buf;   /* pointer to msg data  */
++};
++
++#ifdef __KERNEL__
++
++#include <linux/types.h>
++#include <linux/list.h>
++
++struct l3_client;
++
++struct l3_ops {
++      int     (*open)(struct l3_client *);
++      int     (*command)(struct l3_client *, int cmd, void *arg);
++      void    (*close)(struct l3_client *);
++};
++
++/*
++ * A driver is capable of handling one or more physical devices present on
++ * L3 adapters. This information is used to inform the driver of adapter
++ * events.
++ */
++struct l3_driver {
++      /*
++       * This name is used to uniquely identify the driver.
++       * It should be the same as the module name.
++       */
++      char                    name[32];
++
++      /*
++       * Notifies the driver that a new client wishes to use its
++       * services.  Note that the module use count will be increased
++       * prior to this function being called.  In addition, the
++       * clients driver and adapter fields will have been setup.
++       */
++      int                     (*attach_client)(struct l3_client *);
++
++      /*
++       * Notifies the driver that the client has finished with its
++       * services, and any memory that it allocated for this client
++       * should be cleaned up.  In addition the chip should be
++       * shut down.
++       */
++      void                    (*detach_client)(struct l3_client *);
++
++      /*
++       * Possible operations on the driver.
++       */
++      struct l3_ops           *ops;
++
++      /*
++       * Module structure, if any.    
++       */
++      struct module           *owner;
++
++      /*
++       * drivers list
++       */
++      struct list_head        drivers;
++};
++
++struct l3_adapter;
++
++struct l3_algorithm {
++      /* textual description */
++      char name[32];
++
++      /* perform bus transactions */
++      int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num);
++};
++
++struct semaphore;
++
++/*
++ * l3_adapter is the structure used to identify a physical L3 bus along
++ * with the access algorithms necessary to access it.
++ */
++struct l3_adapter {
++      /*
++       * This name is used to uniquely identify the adapter.
++       * It should be the same as the module name.
++       */
++      char                    name[32];
++
++      /*
++       * the algorithm to access the bus
++       */
++      struct l3_algorithm     *algo;
++
++      /*
++       * Algorithm specific data
++       */
++      void                    *algo_data;
++
++      /*
++       * This may be NULL, or should point to the module struct
++       */
++      struct module           *owner;
++
++      /*
++       * private data for the adapter
++       */
++      void                    *data;
++
++      /*
++       * Our lock.  Unlike the i2c layer, we allow this to be used for
++       * other stuff, like the i2c layer lock.  Some people implement
++       * i2c stuff using the same signals as the l3 bus.
++       */
++      struct semaphore        *lock;
++
++      /*
++       * List of attached clients.
++       */
++      struct list_head        clients;
++
++      /*
++       * List of all adapters.
++       */
++      struct list_head        adapters;
++};
++
++/*
++ * l3_client identifies a single device (i.e. chip) that is connected to an 
++ * L3 bus. The behaviour is defined by the routines of the driver. This
++ * function is mainly used for lookup & other admin. functions.
++ */
++struct l3_client {
++      struct l3_adapter       *adapter;       /* the adapter we sit on        */
++      struct l3_driver        *driver;        /* and our access routines      */
++      void                    *driver_data;   /* private driver data          */
++      struct list_head        __adap;
++};
++
++
++extern int l3_add_adapter(struct l3_adapter *);
++extern int l3_del_adapter(struct l3_adapter *);
++
++extern int l3_add_driver(struct l3_driver *);
++extern int l3_del_driver(struct l3_driver *);
++
++extern int l3_attach_client(struct l3_client *, const char *, const char *);
++extern int l3_detach_client(struct l3_client *);
++
++extern int l3_transfer(struct l3_adapter *, struct l3_msg msgs[], int);
++extern int l3_write(struct l3_client *, int, const char *, int);
++extern int l3_read(struct l3_client *, int, char *, int);
++
++/**
++ * l3_command - send a command to a L3 device driver
++ * @client: registered client structure
++ * @cmd: device driver command
++ * @arg: device driver arguments
++ *
++ * Ask the L3 device driver to perform some function.  Further information
++ * should be sought from the device driver in question.
++ *
++ * Returns negative error code on failure.
++ */
++static inline int l3_command(struct l3_client *clnt, int cmd, void *arg)
++{
++      struct l3_ops *ops = clnt->driver->ops;
++      int ret = -EINVAL;
++
++      if (ops && ops->command)
++              ret = ops->command(clnt, cmd, arg);
++
++      return ret;
++}
++
++static inline int l3_open(struct l3_client *clnt)
++{
++      struct l3_ops *ops = clnt->driver->ops;
++      int ret = 0;
++
++      if (ops && ops->open)
++              ret = ops->open(clnt);
++      return ret;
++}
++
++static inline void l3_close(struct l3_client *clnt)
++{
++      struct l3_ops *ops = clnt->driver->ops;
++      if (ops && ops->close)
++              ops->close(clnt);
++}
++#endif
++
++#endif /* L3_H */
+--- /dev/null
++++ linux-2.4.27/include/linux/l3/uda1341.h
+@@ -0,0 +1,50 @@
++/*
++ *  linux/include/linux/l3/uda1341.h
++ *
++ * Philips UDA1341 mixer device driver
++ *
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++#define UDA1341_NAME "uda1341"
++
++struct uda1341_cfg {
++      unsigned int fs:16;
++      unsigned int format:3;
++};
++
++#define FMT_I2S               0
++#define FMT_LSB16     1
++#define FMT_LSB18     2
++#define FMT_LSB20     3
++#define FMT_MSB               4
++#define FMT_LSB16MSB  5
++#define FMT_LSB18MSB  6
++#define FMT_LSB20MSB  7
++
++#define L3_UDA1341_CONFIGURE  0x13410001
++
++struct l3_gain {
++      unsigned int    left:8;
++      unsigned int    right:8;
++      unsigned int    unused:8;
++      unsigned int    channel:8;
++};
++
++#define L3_SET_VOLUME         0x13410002
++#define L3_SET_TREBLE         0x13410003
++#define L3_SET_BASS           0x13410004
++#define L3_SET_GAIN           0x13410005
++
++struct l3_agc {
++      unsigned int    level:8;
++      unsigned int    enable:1;
++      unsigned int    attack:7;
++      unsigned int    decay:8;
++      unsigned int    channel:8;
++};
++
++#define L3_INPUT_AGC          0x13410006
+--- linux-2.4.27/include/linux/mm.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/mm.h
+@@ -685,6 +685,12 @@
+ extern struct page * vmalloc_to_page(void *addr);
++#ifndef __arm__
++#define memc_update_addr(x,y,z)
++#define memc_update_mm(x)
++#define memc_clear(x,y)
++#endif
++
+ #endif /* __KERNEL__ */
+ #endif
+--- linux-2.4.27/include/linux/mtd/cfi.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/mtd/cfi.h
+@@ -287,8 +287,10 @@
+ #define P_ID_AMD_STD 2
+ #define P_ID_INTEL_STD 3
+ #define P_ID_AMD_EXT 4
++#define P_ID_ST_ADV 32
+ #define P_ID_MITSUBISHI_STD 256
+ #define P_ID_MITSUBISHI_EXT 257
++#define P_ID_SST_PAGE 258
+ #define P_ID_RESERVED 65535
+--- linux-2.4.27/include/linux/pci.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/pci.h
+@@ -413,6 +413,7 @@
+       char            name[90];       /* device name */
+       char            slot_name[8];   /* slot name */
++      u32             saved_state[16]; /* for saving the config space before suspend */
+       int             active;         /* ISAPnP: device is active */
+       int             ro;             /* ISAPnP: read only */
+       unsigned short  regs;           /* ISAPnP: supported registers */
+@@ -499,6 +500,8 @@
+ struct pbus_set_ranges_data
+ {
++      int found_vga:1;
++      int prefetch_valid:1;
+       unsigned long io_start, io_end;
+       unsigned long mem_start, mem_end;
+       unsigned long prefetch_start, prefetch_end;
+@@ -641,6 +644,8 @@
+ int pci_restore_state(struct pci_dev *dev, u32 *buffer);
+ int pci_set_power_state(struct pci_dev *dev, int state);
+ int pci_enable_wake(struct pci_dev *dev, u32 state, int enable);
++int pci_generic_suspend_save(struct pci_dev *pdev, u32 state);
++int pci_generic_resume_restore(struct pci_dev *pdev);
+ /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
+--- /dev/null
++++ linux-2.4.27/include/linux/pld/pld_epxa.h
+@@ -0,0 +1,35 @@
++#ifndef __LINUX_EPXAPLD_H
++#define __LINUX_EPXAPLD_H
++
++/*
++ *  linux/drivers/char/pld/epxapld.h
++ *
++ *  Pld driver for Altera EPXA Excalibur devices
++ *
++ *  Copyright 2001 Altera Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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: $
++ *
++ */
++#define PLD_IOC_MAGIC 'p'
++#if !defined(KERNEL) || defined(CONFIG_PLD_HOTSWAP)
++#define PLD_IOC_ADD_PLD_DEV _IOW(PLD_IOC_MAGIC, 0xa0, struct pldhs_dev_desc)
++#define PLD_IOC_REMOVE_PLD_DEVS _IO(PLD_IOC_MAGIC, 0xa1)
++#define PLD_IOC_SET_INT_MODE _IOW(PLD_IOC_MAGIC, 0xa2, int)
++#endif
++
++#endif
+--- /dev/null
++++ linux-2.4.27/include/linux/pld/pld_hotswap.h
+@@ -0,0 +1,87 @@
++#ifndef __LINUX_PLD_HOTSWAP_H
++#define __LINUX_PLD_HOTSWAP_H
++/*
++ *  linux/drivers/char/pld/pld_hotswap.h
++ *
++ *  Pld driver for Altera EPXA Excalibur devices
++ *
++ *  Copyright 2001 Altera Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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: $
++ *
++ */
++
++/*
++ * The pld_hotswap ops contain the basic operation required for adding
++ * and removing devices from the system.
++ */
++
++struct pldhs_dev_info{
++       unsigned int size;
++       unsigned short vendor_id;
++       unsigned short product_id;
++       unsigned char fsize;
++       unsigned char nsize;
++       unsigned short pssize;
++       unsigned short cmdsize;
++       unsigned char irq;
++       unsigned char version;
++       unsigned int base_addr;
++       unsigned int reg_size;
++};
++
++struct pldhs_dev_desc{
++       struct pldhs_dev_info* info;
++       char* name;
++       void* data;
++};
++
++#include <linux/pld/pld_epxa.h>
++
++
++#ifdef __KERNEL__
++struct pld_hotswap_ops{
++       struct list_head list;
++       char* name;
++       int (*add_device)(struct pldhs_dev_info *dev_info, void* dev_ps_data);
++       int (*remove_devices)(void);
++       int (*proc_read)(char* buf,char** start,off_t offset,int count,int *eof,void *data);
++};
++
++/*
++ * These functions are called by the device drivers to register functions
++ * which should be called when devices are added and removed
++ */
++extern int pldhs_register_driver(struct pld_hotswap_ops *drv);
++extern int pldhs_unregister_driver(char *driver);
++
++/*
++ * These functions are called by the pld loader to add and remove
++ * device instances from the system. The call the functions regsistered
++ * the the particular deriver for this purpose
++ */
++extern int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data);
++extern int pldhs_remove_devices(void);
++
++/* Macro for formatting data to appear in the proc/pld file */
++#define PLDHS_READ_PROC_DATA(buf,name,index,base,irq,ps_string) \
++                    sprintf(buf,":%s:%d Base Address, %#lx, IRQ, %d#\n%s\n",\
++                    name,index,base,irq,ps_string)
++
++#endif
++
++#endif
+--- linux-2.4.27/include/linux/serial.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/serial.h
+@@ -48,7 +48,7 @@
+       unsigned char   *iomem_base;
+       unsigned short  iomem_reg_shift;
+       unsigned int    port_high;
+-      int     reserved[1];
++      unsigned long   iomap_base;     /* cookie passed into ioremap */
+ };
+ /*
+--- linux-2.4.27/include/linux/serialP.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/serialP.h
+@@ -157,8 +157,8 @@
+       struct pci_dev          *dev;
+ };
+-extern int pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
+-extern int pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
++//extern int pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
++//extern int pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable);
+ #ifndef PCI_ANY_ID
+ #define PCI_ANY_ID (~0)
+--- /dev/null
++++ linux-2.4.27/include/linux/serial_core.h
+@@ -0,0 +1,441 @@
++/*
++ *  linux/drivers/char/serial_core.h
++ *
++ *  Copyright (C) 2000 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
++ * 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: serial_core.h,v 1.9.2.3 2001/11/26 22:32:45 rmk Exp $
++ */
++
++/*
++ * The type definitions.  These are from Ted Ts'o's serial.h
++ */
++#define PORT_UNKNOWN  0
++#define PORT_8250     1
++#define PORT_16450    2
++#define PORT_16550    3
++#define PORT_16550A   4
++#define PORT_CIRRUS   5
++#define PORT_16650    6
++#define PORT_16650V2  7
++#define PORT_16750    8
++#define PORT_STARTECH 9
++#define PORT_16C950   10
++#define PORT_16654    11
++#define PORT_16850    12
++#define PORT_RSA      13
++#define PORT_NS16550A 14
++#define PORT_MAX_8250 14      /* max port ID */
++
++/*
++ * ARM specific type numbers.  These are not currently guaranteed
++ * to be implemented, and will change in the future.  These are
++ * separate so any additions to the old serial.c that occur before
++ * we are merged can be easily merged here.
++ */
++#define PORT_AMBA     32
++#define PORT_CLPS711X 33
++#define PORT_SA1100   34
++#define PORT_UART00   35
++#define PORT_21285    37
++
++/* Sparc type numbers.  */
++#define PORT_SUNZILOG 38
++#define PORT_SUNSAB   39
++
++/* NEC v850.  */
++#define PORT_NB85E_UART       40
++
++/* NEC PC-9800 */
++#define PORT_8251_PC98        41
++#define PORT_19K_PC98 42
++#define PORT_FIFO_PC98        43
++#define PORT_VFAST_PC98       44
++#define PORT_PC9861   45
++#define PORT_PC9801_101       46
++
++/* DZ */
++#define PORT_DZ               47
++
++#define PORT_OMAHA    48
++#define PORT_AT91RM9200       49
++
++
++#ifdef __KERNEL__
++
++#include <linux/config.h>
++#include <linux/interrupt.h>
++#include <linux/circ_buf.h>
++#include <linux/serial.h>
++#include <linux/spinlock.h>
++
++struct uart_port;
++struct uart_info;
++struct serial_struct;
++
++/*
++ * This structure describes all the operations that can be
++ * done on the physical hardware.
++ */
++struct uart_ops {
++      unsigned int    (*tx_empty)(struct uart_port *);
++      void            (*set_mctrl)(struct uart_port *, unsigned int mctrl);
++      unsigned int    (*get_mctrl)(struct uart_port *);
++      void            (*stop_tx)(struct uart_port *, unsigned int tty_stop);
++      void            (*start_tx)(struct uart_port *, unsigned int tty_start);
++      void            (*send_xchar)(struct uart_port *, char ch);
++      void            (*stop_rx)(struct uart_port *);
++      void            (*enable_ms)(struct uart_port *);
++      void            (*break_ctl)(struct uart_port *, int ctl);
++      int             (*startup)(struct uart_port *);
++      void            (*shutdown)(struct uart_port *);
++      void            (*change_speed)(struct uart_port *, unsigned int cflag, unsigned int iflag, unsigned int quot);
++      void            (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
++      int             (*set_wake)(struct uart_port *, unsigned int state);
++
++      /*
++       * Return a string describing the type of the port
++       */
++      const char *(*type)(struct uart_port *);
++
++      /*
++       * Release IO and memory resources used by the port.
++       * This includes iounmap if necessary.
++       */
++      void            (*release_port)(struct uart_port *);
++
++      /*
++       * Request IO and memory resources used by the port.
++       * This includes iomapping the port if necessary.
++       */
++      int             (*request_port)(struct uart_port *);
++      void            (*config_port)(struct uart_port *, int);
++      int             (*verify_port)(struct uart_port *, struct serial_struct *);
++      int             (*ioctl)(struct uart_port *, unsigned int, unsigned long);
++};
++
++#define UART_CONFIG_TYPE      (1 << 0)
++#define UART_CONFIG_IRQ               (1 << 1)
++
++struct uart_icount {
++      __u32   cts;
++      __u32   dsr;
++      __u32   rng;
++      __u32   dcd;
++      __u32   rx;
++      __u32   tx;
++      __u32   frame;
++      __u32   overrun;
++      __u32   parity;
++      __u32   brk;
++      __u32   buf_overrun;
++};
++
++struct uart_port {
++      spinlock_t              lock;                   /* port lock */
++      unsigned int            iobase;                 /* in/out[bwl] */
++      char                    *membase;               /* read/write[bwl] */
++      unsigned char           fifosize;               /* tx fifo size */
++      unsigned char           x_char;                 /* xon/xoff char */
++      unsigned char           regshift;               /* reg offset shift */
++      unsigned char           iotype;                 /* io access style */
++
++#define UPIO_PORT             (0)
++#define UPIO_HUB6             (1)
++#define UPIO_MEM              (2)
++
++      unsigned int            read_status_mask;       /* driver specific */
++      unsigned int            ignore_status_mask;     /* driver specific */
++
++      struct uart_info        *info;                  /* pointer to parent info */
++      struct uart_icount      icount;                 /* statistics */
++
++      struct console          *cons;                  /* struct console, if any */
++      unsigned long           sysrq;                  /* sysrq timeout */
++
++      unsigned int            flags;
++
++#define UPF_HUP_NOTIFY                ASYNC_HUP_NOTIFY
++#define UPF_FOURPORT          ASYNC_FOURPORT
++#define UPF_SAK                       ASYNC_SAK
++#define UPF_SPLIT_TERMIOS     ASYNC_SPLIT_TERMIOS
++#define UPF_SPD_MASK          (0x1030)
++#define UPF_SPD_HI            (0x0010)
++#define UPF_SPD_VHI           (0x0020)
++#define UPF_SPD_CUST          (0x0030)
++#define UPF_SPD_SHI           (0x1000)
++#define UPF_SPD_WARP          (0x1010)
++#define UPF_SKIP_TEST         ASYNC_SKIP_TEST
++#define UPF_AUTO_IRQ          ASYNC_AUTO_IRQ
++#define UPF_SESSION_LOCKOUT   ASYNC_SESSION_LOCKOUT
++#define UPF_PGRP_LOCKOUT      ASYNCPGRP_LOCKOUT
++#define UPF_CALLOUT_NOHUP     ASYNC_CALLOUT_NOHUP
++#define UPF_HARDPPS_CD                ASYNC_HARDPPS_CD
++                              /* 12 */
++#define UPF_LOW_LATENCY               ASYNC_LOW_LATENCY
++#define UPF_BUGGY_UART                ASYNC_BUGGY_UART
++#define UPF_AUTOPROBE         ASYNC_AUTOPROBE
++#define UPF_MAGIC_MULTIPLIER  (1 << 16)
++#define UPF_BOOT_ONLYMCA      ASYNC_BOOT_ONLYMCA
++#define UPF_CONS_FLOW         ASYNC_CONS_FLOW
++#define UPF_SHARE_IRQ         ASYNC_SHARE_IRQ
++#define UPF_BOOT_AUTOCONF     ASYNC_BOOT_AUTOCONF
++#define UPF_RESOURCES         (1 << 30)
++#define UPF_IOREMAP           (1 << 31)
++
++#define UPF_CHANGE_MASK               (0x17fff)
++#define UPF_USR_MASK          (UPF_SPD_MASK|UPF_LOW_LATENCY)
++
++      unsigned int            mctrl;                  /* current modem ctrl settings */
++      unsigned int            timeout;                /* character-based timeout */
++      unsigned int            type;                   /* port type */
++      struct uart_ops         *ops;
++      unsigned int            uartclk;                /* base uart clock */
++      unsigned int            custom_divisor;
++      unsigned int            irq;                    /* irq number */
++      unsigned int            line;                   /* port index */
++      unsigned long           mapbase;                /* for ioremap */
++      unsigned char           hub6;                   /* this should be in the 8250 driver */
++      unsigned char           unused[7];
++};
++
++/*
++ * This is the state information which is persistent across opens.
++ * The low level driver must not to touch any elements contained
++ * within.
++ */
++struct uart_state {
++      unsigned int            close_delay;
++      unsigned int            closing_wait;
++
++#define USF_CLOSING_WAIT_INF  (0)
++#define USF_CLOSING_WAIT_NONE (65535)
++
++      struct termios          normal_termios;
++      struct termios          callout_termios;
++
++      int                     count;
++      struct uart_info        *info;
++      struct uart_port        *port;
++
++      struct semaphore        sem;
++#ifdef CONFIG_PM
++      struct pm_dev           *pm;
++#endif
++};
++
++#define UART_XMIT_SIZE 1024
++/*
++ * This is the state information which is only valid when the port
++ * is open; it may be freed by the core driver once the device has
++ * been closed.  Either the low level driver or the core can modify
++ * stuff here.
++ */
++struct uart_info {
++      struct tty_struct       *tty;
++      struct circ_buf         xmit;
++      unsigned int            flags;
++
++/*
++ * These are the flags that specific to info->flags, and reflect our
++ * internal state.  They can not be accessed via port->flags.  Low
++ * level drivers must not change these, but may query them instead.
++ */
++#define UIF_CHECK_CD          ASYNC_CHECK_CD
++#define UIF_CTS_FLOW          ASYNC_CTS_FLOW
++#define UIF_CALLOUT_ACTIVE    ASYNC_CALLOUT_ACTIVE
++#define UIF_NORMAL_ACTIVE     ASYNC_NORMAL_ACTIVE
++#define UIF_INITIALIZED               ASYNC_INITIALIZED
++
++      unsigned char           *tmpbuf;
++      struct semaphore        tmpbuf_sem;
++
++      unsigned int            driver_priv;
++      int                     blocked_open;
++      pid_t                   session;
++      pid_t                   pgrp;
++
++      struct tasklet_struct   tlet;
++
++      wait_queue_head_t       open_wait;
++      wait_queue_head_t       delta_msr_wait;
++};
++
++/* number of characters left in xmit buffer before we ask for more */
++#define WAKEUP_CHARS          256
++
++struct module;
++struct tty_driver;
++
++struct uart_driver {
++      struct module           *owner;
++      int                      normal_major;
++      const char              *normal_name;
++      struct tty_driver       *normal_driver;
++      int                      callout_major;
++      const char              *callout_name;
++      struct tty_driver       *callout_driver;
++      struct tty_struct       **table;
++      struct termios          **termios;
++      struct termios          **termios_locked;
++      int                      minor;
++      int                      nr;
++      struct console          *cons;
++
++      /*
++       * these are private; the low level driver should not
++       * touch these; they should be initialised to NULL
++       */
++      struct uart_state       *state;
++};
++
++void uart_write_wakeup(struct uart_port *port);
++struct uart_port *uart_get_console(struct uart_port *ports, int nr,
++                                 struct console *c);
++void uart_parse_options(char *options, int *baud, int *parity, int *bits,
++                      int *flow);
++int uart_set_options(struct uart_port *port, struct console *co, int baud,
++                   int parity, int bits, int flow);
++
++/*
++ * Port/driver registration/removal
++ */
++int uart_register_driver(struct uart_driver *uart);
++void uart_unregister_driver(struct uart_driver *uart);
++void uart_unregister_port(struct uart_driver *reg, int line);
++int uart_register_port(struct uart_driver *reg, struct uart_port *port);
++int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
++int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
++
++#define uart_circ_empty(circ)         ((circ)->head == (circ)->tail)
++#define uart_circ_clear(circ)         ((circ)->head = (circ)->tail = 0)
++
++#define uart_circ_chars_pending(circ) \
++      (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define uart_circ_chars_free(circ)    \
++      (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define uart_tx_stopped(port)         \
++      ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
++
++/*
++ * The following are helper functions for the low level drivers.
++ */
++#ifdef SUPPORT_SYSRQ
++static inline int
++uart_handle_sysrq_char(struct uart_port *port, unsigned int ch,
++                     struct pt_regs *regs)
++{
++      if (port->sysrq) {
++              if (ch && time_before(jiffies, port->sysrq)) {
++                      handle_sysrq(ch, regs, NULL, NULL);
++                      port->sysrq = 0;
++                      return 1;
++              }
++              port->sysrq = 0;
++      }
++      return 0;
++}
++#else
++#define uart_handle_sysrq_char(port,ch,regs)  (0)
++#endif
++
++/*
++ * We do the SysRQ and SAK checking like this...
++ */
++static inline int uart_handle_break(struct uart_port *port)
++{
++      struct uart_info *info = port->info;
++#ifdef SUPPORT_SYSRQ
++      if (port->cons && port->cons->index == port->line) {
++              if (!port->sysrq) {
++                      port->sysrq = jiffies + HZ*5;
++                      return 1;
++              }
++              port->sysrq = 0;
++      }
++#endif
++      if (port->flags & UPF_SAK)
++              do_SAK(info->tty);
++      return 0;
++}
++
++/**
++ *    uart_handle_dcd_change - handle a change of carrier detect state
++ *    @port: uart_port structure for the open port
++ *    @status: new carrier detect status, nonzero if active
++ */
++static inline void
++uart_handle_dcd_change(struct uart_port *port, unsigned int status)
++{
++      struct uart_info *info = port->info;
++
++      port->icount.dcd++;
++
++#ifdef CONFIG_HARD_PPS
++      if ((port->flags & UPF_HARDPPS_CD) && status)
++              hardpps();
++#endif
++
++      if (info->flags & UIF_CHECK_CD) {
++              if (status)
++                      wake_up_interruptible(&info->open_wait);
++              else if (!((info->flags & UIF_CALLOUT_ACTIVE) &&
++                         (port->flags & UPF_CALLOUT_NOHUP))) {
++                      if (info->tty)
++                              tty_hangup(info->tty);
++              }
++      }
++}
++
++/**
++ *    uart_handle_cts_change - handle a change of clear-to-send state
++ *    @port: uart_port structure for the open port
++ *    @status: new clear to send status, nonzero if active
++ */
++static inline void
++uart_handle_cts_change(struct uart_port *port, unsigned int status)
++{
++      struct uart_info *info = port->info;
++      struct tty_struct *tty = info->tty;
++
++      port->icount.cts++;
++
++      if (info->flags & UIF_CTS_FLOW) {
++              if (tty->hw_stopped) {
++                      if (status) {
++                              tty->hw_stopped = 0;
++                              port->ops->start_tx(port, 0);
++                              uart_write_wakeup(port);
++                      }
++              } else {
++                      if (!status) {
++                              tty->hw_stopped = 1;
++                              port->ops->stop_tx(port, 0);
++                      }
++              }
++      }
++}
++
++/*
++ *    UART_ENABLE_MS - determine if port should enable modem status irqs
++ */
++#define UART_ENABLE_MS(port,cflag)    ((port)->flags & UPF_HARDPPS_CD || \
++                                       (cflag) & CRTSCTS || \
++                                       !((cflag) & CLOCAL))
++
++#endif
+--- linux-2.4.27/include/linux/serial_reg.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/serial_reg.h
+@@ -121,6 +121,7 @@
+ /*
+  * These are the definitions for the Modem Control Register
+  */
++#define UART_MCR_AFE  0x20    /* Enable auto-RTS/CTS (TI16C750) */
+ #define UART_MCR_LOOP 0x10    /* Enable loopback test mode */
+ #define UART_MCR_OUT2 0x08    /* Out2 complement */
+ #define UART_MCR_OUT1 0x04    /* Out1 complement */
+--- linux-2.4.27/include/linux/zlib.h~2.4.27-vrs1
++++ linux-2.4.27/include/linux/zlib.h
+@@ -31,7 +31,7 @@
+ #ifndef _ZLIB_H
+ #define _ZLIB_H
+-#include <linux/zconf.h>
++#include "zconf.h"
+ #ifdef __cplusplus
+ extern "C" {
+--- linux-2.4.27/init/do_mounts.c~2.4.27-vrs1
++++ linux-2.4.27/init/do_mounts.c
+@@ -888,7 +888,7 @@
+  */
+ void prepare_namespace(void)
+ {
+-      int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
++      int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR || MAJOR(ROOT_DEV) == 31;
+ #ifdef CONFIG_ALL_PPC
+       extern void arch_discover_root(void);
+       arch_discover_root();
+@@ -951,6 +951,7 @@
+ static unsigned inptr;   /* index of next byte to be processed in inbuf */
+ static unsigned outcnt;  /* bytes in output buffer */
+ static int exit_code;
++static int unzip_error;
+ static long bytes_out;
+ static int crd_infd, crd_outfd;
+@@ -998,13 +999,17 @@
+ /* ===========================================================================
+  * Fill the input buffer. This is called only when the buffer is empty
+  * and at least one byte is really needed.
++ * Returning -1 does not guarantee that gunzip() will ever return.
+  */
+ static int __init fill_inbuf(void)
+ {
+       if (exit_code) return -1;
+       
+       insize = read(crd_infd, inbuf, INBUFSIZ);
+-      if (insize == 0) return -1;
++      if (insize == 0) {
++              error("RAMDISK: ran out of compressed data\n");
++              return -1;
++      }
+       inptr = 1;
+@@ -1018,10 +1023,15 @@
+ static void __init flush_window(void)
+ {
+     ulg c = crc;         /* temporary variable */
+-    unsigned n;
++    unsigned n, written;
+     uch *in, ch;
+     
+-    write(crd_outfd, window, outcnt);
++    written = write(crd_outfd, window, outcnt);
++    if (written != outcnt && unzip_error == 0) {
++      printk(KERN_ERR "RAMDISK: incomplete write (%d != %d), "
++             "only wrote %ld\n", written, outcnt, bytes_out);
++      unzip_error = 1;
++    }
+     in = window;
+     for (n = 0; n < outcnt; n++) {
+           ch = *in++;
+@@ -1036,6 +1046,7 @@
+ {
+       printk(KERN_ERR "%s", x);
+       exit_code = 1;
++      unzip_error = 1;
+ }
+ static int __init crd_load(int in_fd, int out_fd)
+@@ -1064,6 +1075,8 @@
+       }
+       makecrc();
+       result = gunzip();
++      if (unzip_error)
++              result = 1;
+       kfree(inbuf);
+       kfree(window);
+       return result;
+--- linux-2.4.27/kernel/Makefile~2.4.27-vrs1
++++ linux-2.4.27/kernel/Makefile
+@@ -9,16 +9,19 @@
+ O_TARGET := kernel.o
+-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o printk.o
++export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
++            printk.o fork.o cpufreq.o
+-obj-y     = sched.o dma.o fork.o exec_domain.o panic.o printk.o \
++obj-y     = sched.o fork.o exec_domain.o panic.o printk.o \
+           module.o exit.o itimer.o info.o time.o softirq.o resource.o \
+           sysctl.o acct.o capability.o ptrace.o timer.o user.o \
+           signal.o sys.o kmod.o context.o
++obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
+ obj-$(CONFIG_UID16) += uid16.o
+ obj-$(CONFIG_MODULES) += ksyms.o
+ obj-$(CONFIG_PM) += pm.o
++obj-$(CONFIG_CPU_FREQ) += cpufreq.o
+ ifneq ($(CONFIG_IA64),y)
+ # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
+--- /dev/null
++++ linux-2.4.27/kernel/cpufreq.c
+@@ -0,0 +1,536 @@
++/*
++ *  linux/kernel/cpufreq.c
++ *
++ *  Copyright (C) 2001 Russell King
++ *
++ *  $Id: cpufreq.c,v 1.17.2.2 2002/05/03 13:26:27 rmk 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.
++ *
++ * CPU speed changing core functionality.  We provide the following
++ * services to the system:
++ *  - notifier lists to inform other code of the freq change both
++ *    before and after the freq change.
++ *  - the ability to change the freq speed
++ *
++ * ** You'll need to add CTL_CPU = 10 to include/linux/sysctl.h if
++ * it's not already present.
++ *
++ * When this appears in the kernel, the sysctl enums will move to
++ * include/linux/sysctl.h
++ */
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/notifier.h>
++#include <linux/cpufreq.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/fs.h>
++#include <linux/sysctl.h>
++
++#include <asm/semaphore.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <linux/interrupt.h>  /* requires system.h */
++
++/*
++ * This list is for kernel code that needs to handle
++ * changes to devices when the CPU clock speed changes.
++ */
++static struct notifier_block *cpufreq_notifier_list;
++static DECLARE_MUTEX_LOCKED(cpufreq_sem);
++static unsigned long cpufreq_ref_loops;
++static unsigned int cpufreq_ref_freq;
++static int cpufreq_initialised;
++static unsigned int (*cpufreq_validatespeed)(unsigned int);
++static void (*cpufreq_setspeed)(unsigned int);
++
++#ifndef CONFIG_SMP
++static unsigned int __cpufreq_max;
++static unsigned int __cpufreq_min;
++static unsigned int __cpufreq_cur;
++#endif
++
++static unsigned long scale(unsigned long ref, u_int div, u_int mult)
++{
++      unsigned long new_jiffy_l, new_jiffy_h;
++
++      /*
++       * Recalculate loops_per_jiffy.  We do it this way to
++       * avoid math overflow on 32-bit machines.  Maybe we
++       * should make this architecture dependent?  If you have
++       * a better way of doing this, please replace!
++       *
++       *    new = old * mult / div
++       */
++      new_jiffy_h = ref / div;
++      new_jiffy_l = (ref % div) / 100;
++      new_jiffy_h *= mult;
++      new_jiffy_l = new_jiffy_l * mult / div;
++
++      return new_jiffy_h + new_jiffy_l * 100;
++}
++
++/*
++ * cpufreq_max command line parameter.  Use:
++ *  cpufreq=59000-221000
++ * to set the CPU frequency to 59 to 221MHz.
++ */
++static int __init cpufreq_setup(char *str)
++{
++      unsigned int min, max;
++      int i;
++
++      min = 0;
++      max = simple_strtoul(str, &str, 0);
++      if (*str == '-') {
++              min = max;
++              max = simple_strtoul(str + 1, NULL, 0);
++      }
++
++      for (i = 0; i < smp_num_cpus; i++) {
++              cpufreq_max(i) = max;
++              cpufreq_min(i) = min;
++      }
++
++      return 1;
++}
++
++__setup("cpufreq=", cpufreq_setup);
++
++/**
++ *    cpufreq_register_notifier - register a driver with cpufreq
++ *    @nb: notifier function to register
++ *
++ *    Add a driver to the list of drivers that which to be notified about
++ *    CPU clock rate changes. The driver will be called three times on
++ *    clock change.
++ *
++ *    This function may sleep, and has the same return conditions as
++ *    notifier_chain_register.
++ */
++int cpufreq_register_notifier(struct notifier_block *nb)
++{
++      int ret;
++
++      down(&cpufreq_sem);
++      ret = notifier_chain_register(&cpufreq_notifier_list, nb);
++      up(&cpufreq_sem);
++
++      return ret;
++}
++
++EXPORT_SYMBOL(cpufreq_register_notifier);
++
++/**
++ *    cpufreq_unregister_notifier - unregister a driver with cpufreq
++ *    @nb: notifier block to be unregistered
++ *
++ *    Remove a driver from the CPU frequency notifier lists.
++ *
++ *    This function may sleep, and has the same return conditions as
++ *    notifier_chain_unregister.
++ */
++int cpufreq_unregister_notifier(struct notifier_block *nb)
++{
++      int ret;
++
++      down(&cpufreq_sem);
++      ret = notifier_chain_unregister(&cpufreq_notifier_list, nb);
++      up(&cpufreq_sem);
++
++      return ret;
++}
++
++EXPORT_SYMBOL(cpufreq_unregister_notifier);
++
++/*
++ * This notifier alters the system "loops_per_jiffy" for the clock
++ * speed change.  We ignore CPUFREQ_MINMAX here.
++ */
++static void adjust_jiffies(unsigned long val, struct cpufreq_info *ci)
++{
++      if ((val == CPUFREQ_PRECHANGE  && ci->old_freq < ci->new_freq) ||
++          (val == CPUFREQ_POSTCHANGE && ci->old_freq > ci->new_freq))
++              loops_per_jiffy = scale(cpufreq_ref_loops, cpufreq_ref_freq,
++                                      ci->new_freq);
++}
++
++#ifdef CONFIG_PM
++/**
++ *    cpufreq_restore - restore the CPU clock frequency after resume
++ *
++ *    Restore the CPU clock frequency so that our idea of the current
++ *    frequency reflects the actual hardware.
++ */
++int cpufreq_restore(void)
++{
++      unsigned long old_cpus;
++      int cpu = smp_processor_id();
++      int ret;
++
++      if (!cpufreq_initialised)
++              panic("cpufreq_restore() called before initialisation!");
++      if (in_interrupt())
++              panic("cpufreq_restore() called from interrupt context!");
++
++      /*
++       * Bind to the current CPU.
++       */
++      old_cpus = current->cpus_allowed;
++      current->cpus_allowed = 1UL << cpu_logical_map(cpu);
++
++      down(&cpufreq_sem);
++
++      ret = -ENXIO;
++      if (cpufreq_setspeed) {
++              cpufreq_setspeed(cpufreq_current(cpu));
++              ret = 0;
++      }
++
++      up(&cpufreq_sem);
++
++      current->cpus_allowed = old_cpus;
++
++      return ret;
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_restore);
++#endif
++
++/**
++ *    cpu_setfreq - change the CPU clock frequency.
++ *    @freq: frequency (in kHz) at which we should run.
++ *
++ *    Set the CPU clock frequency, informing all registered users of
++ *    the change. We bound the frequency according to the cpufreq_max
++ *    command line parameter, and the parameters the registered users
++ *    will allow.
++ *
++ *    This function must be called from process context, and on the
++ *    cpu that we wish to change the frequency of.
++ *
++ *    We return 0 if successful. (we are currently always successful).
++ */
++int cpufreq_set(unsigned int freq)
++{
++      unsigned long old_cpus;
++      struct cpufreq_info clkinfo;
++      struct cpufreq_minmax minmax;
++      int cpu = smp_processor_id();
++      int ret;
++
++      if (!cpufreq_initialised)
++              panic("cpufreq_set() called before initialisation!");
++      if (in_interrupt())
++              panic("cpufreq_set() called from interrupt context!");
++
++      /*
++       * Bind to the current CPU.
++       */
++      old_cpus = current->cpus_allowed;
++      current->cpus_allowed = 1UL << cpu_logical_map(cpu);
++
++      down(&cpufreq_sem);
++      ret = -ENXIO;
++      if (!cpufreq_setspeed || !cpufreq_validatespeed)
++              goto out;
++
++      /*
++       * Don't allow the CPU to be clocked over the limit.
++       */
++      minmax.min_freq = cpufreq_min(cpu);
++      minmax.max_freq = cpufreq_max(cpu);
++      minmax.cur_freq = cpufreq_current(cpu);
++      minmax.new_freq = freq;
++
++      /*
++       * Find out what the registered devices will currently tolerate,
++       * and limit the requested clock rate to these values.  Drivers
++       * must not rely on the 'new_freq' value - it is only a guide.
++       */
++      notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_MINMAX, &minmax);
++      if (freq < minmax.min_freq)
++              freq = minmax.min_freq;
++      if (freq > minmax.max_freq)
++              freq = minmax.max_freq;
++
++      /*
++       * Ask the CPU specific code to validate the speed.  If the speed
++       * is not acceptable, make it acceptable.  Current policy is to
++       * round the frequency down to the value the processor actually
++       * supports.
++       */
++      freq = cpufreq_validatespeed(freq);
++
++      if (cpufreq_current(cpu) != freq) {
++              clkinfo.old_freq = cpufreq_current(cpu);
++              clkinfo.new_freq = freq;
++
++              notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_PRECHANGE,
++                                  &clkinfo);
++
++              adjust_jiffies(CPUFREQ_PRECHANGE, &clkinfo);
++
++              /*
++               * Actually set the CPU frequency.
++               */
++              cpufreq_setspeed(freq);
++              cpufreq_current(cpu) = freq;
++              adjust_jiffies(CPUFREQ_POSTCHANGE, &clkinfo);
++
++              notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_POSTCHANGE,
++                                  &clkinfo);
++      }
++
++      ret = 0;
++
++ out:
++      up(&cpufreq_sem);
++
++      current->cpus_allowed = old_cpus;
++
++      return ret;
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_set);
++
++/**
++ *    cpufreq_setmax - set the CPU to maximum frequency
++ *
++ *    Sets the CPU this function is executed on to maximum frequency.
++ */
++int cpufreq_setmax(void)
++{
++      return cpufreq_set(cpufreq_max(smp_processor_id()));
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_setmax);
++
++/**
++ *    cpufreq_get - return the current CPU clock frequency in 1kHz
++ *    @cpu: cpu number to obtain frequency for
++ *
++ *    Returns the specified CPUs frequency in kHz.
++ */
++unsigned int cpufreq_get(int cpu)
++{
++      if (!cpufreq_initialised)
++              panic("cpufreq_get() called before initialisation!");
++      return cpufreq_current(cpu);
++}
++
++EXPORT_SYMBOL(cpufreq_get);
++
++#ifdef CONFIG_SYSCTL
++
++static int
++cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
++              void *buffer, size_t *lenp)
++{
++      char buf[16], *p;
++      int cpu = 0, len, left = *lenp;
++
++      if (!left || (filp->f_pos && !write)) {
++              *lenp = 0;
++              return 0;
++      }
++
++      if (write) {
++              unsigned int freq;
++
++              len = left;
++              if (left > sizeof(buf))
++                      left = sizeof(buf);
++              if (copy_from_user(buf, buffer, left))
++                      return -EFAULT;
++              buf[sizeof(buf) - 1] = '\0';
++
++              freq = simple_strtoul(buf, &p, 0);
++              cpufreq_set(freq);
++      } else {
++              len = sprintf(buf, "%d\n", cpufreq_get(cpu));
++              if (len > left)
++                      len = left;
++              if (copy_to_user(buffer, buf, len))
++                      return -EFAULT;
++      }
++
++      *lenp = len;
++      filp->f_pos += len;
++      return 0;
++}
++
++static int
++cpufreq_sysctl(ctl_table *table, int *name, int nlen,
++             void *oldval, size_t *oldlenp,
++             void *newval, size_t newlen, void **context)
++{
++      int cpu = 0;
++
++      if (oldval && oldlenp) {
++              size_t oldlen;
++
++              if (get_user(oldlen, oldlenp))
++                      return -EFAULT;
++
++              if (oldlen != sizeof(unsigned int))
++                      return -EINVAL;
++
++              if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
++                  put_user(sizeof(unsigned int), oldlenp))
++                      return -EFAULT;
++      }
++      if (newval && newlen) {
++              unsigned int freq;
++
++              if (newlen != sizeof(unsigned int))
++                      return -EINVAL;
++
++              if (get_user(freq, (unsigned int *)newval))
++                      return -EFAULT;
++
++              cpufreq_set(freq);
++      }
++      return 1;
++}
++
++enum {
++      CPU_NR_FREQ_MAX = 1,
++      CPU_NR_FREQ_MIN = 2,
++      CPU_NR_FREQ = 3
++};
++
++static ctl_table ctl_cpu_vars[4] = {
++      {
++              ctl_name:       CPU_NR_FREQ_MAX,
++              procname:       "speed-max",
++              data:           &cpufreq_max(0),
++              maxlen:         sizeof(cpufreq_max(0)),
++              mode:           0444,
++              proc_handler:   proc_dointvec,
++      },
++      {
++              ctl_name:       CPU_NR_FREQ_MIN,
++              procname:       "speed-min",
++              data:           &cpufreq_min(0),
++              maxlen:         sizeof(cpufreq_min(0)),
++              mode:           0444,
++              proc_handler:   proc_dointvec,
++      },
++      {
++              ctl_name:       CPU_NR_FREQ,
++              procname:       "speed",
++              mode:           0644,
++              proc_handler:   cpufreq_procctl,
++              strategy:       cpufreq_sysctl,
++      },
++      {
++              ctl_name:       0,
++      }
++};
++
++enum {
++      CPU_NR = 1,
++};
++
++static ctl_table ctl_cpu_nr[2] = {
++      {
++              ctl_name:       CPU_NR,
++              procname:       "0",
++              mode:           0555,
++              child:          ctl_cpu_vars,
++      },
++      {
++              ctl_name:       0,
++      }
++};
++
++static ctl_table ctl_cpu[2] = {
++      {
++              ctl_name:       CTL_CPU,
++              procname:       "cpu",
++              mode:           0555,
++              child:          ctl_cpu_nr,
++      },
++      {
++              ctl_name:       0,
++      }
++};
++
++static inline void cpufreq_sysctl_init(void)
++{
++      register_sysctl_table(ctl_cpu, 0);
++}
++
++#else
++#define cpufreq_sysctl_init()
++#endif
++
++/**
++ *    cpufreq_setfunctions - Set CPU clock functions
++ *    @validate: pointer to validation function
++ *    @setspeed: pointer to setspeed function
++ */
++void
++cpufreq_setfunctions(unsigned int (*validate)(unsigned int),
++                   void (*setspeed)(unsigned int))
++{
++      down(&cpufreq_sem);
++      cpufreq_validatespeed = validate;
++      cpufreq_setspeed = setspeed;
++      up(&cpufreq_sem);
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_setfunctions);
++
++/**
++ *    cpufreq_init - Initialise the cpufreq core
++ *    @freq: current CPU clock speed.
++ *    @min_freq: minimum CPU clock speed.
++ *    @max_freq: maximum CPU clock speed.
++ *
++ *    Initialise the cpufreq core. If the cpufreq_max command line
++ *    parameter has not been specified, we set the maximum clock rate
++ *    to the current CPU clock rate.
++ */
++void cpufreq_init(unsigned int freq,
++                unsigned int min_freq,
++                unsigned int max_freq)
++{
++      /*
++       * If the user doesn't tell us their maximum frequency,
++       * or if it is invalid, use the values determined 
++       * by the cpufreq-arch-specific initialization functions.
++       * The validatespeed code is responsible for limiting
++       * this further.
++       */
++      if (max_freq && ((cpufreq_max(0) == 0) || (cpufreq_max(0) > max_freq)))
++              cpufreq_max(0) = max_freq;
++      if (min_freq && ((cpufreq_min(0) == 0) || (cpufreq_min(0) < min_freq)))
++              cpufreq_min(0) = min_freq;
++
++      if (cpufreq_max(0) == 0)
++              cpufreq_max(0) = freq;
++
++      printk(KERN_INFO "CPU clock: %d.%03d MHz (%d.%03d-%d.%03d MHz)\n",
++              freq / 1000, freq % 1000,
++              cpufreq_min(0) / 1000, cpufreq_min(0) % 1000,
++              cpufreq_max(0) / 1000, cpufreq_max(0) % 1000);
++
++      cpufreq_ref_loops = loops_per_jiffy;
++      cpufreq_ref_freq = freq;
++      cpufreq_current(smp_processor_id()) = freq;
++
++      cpufreq_initialised = 1;
++      up(&cpufreq_sem);
++
++      cpufreq_sysctl_init();
++}
++
++EXPORT_SYMBOL_GPL(cpufreq_init);
+--- linux-2.4.27/kernel/exit.c~2.4.27-vrs1
++++ linux-2.4.27/kernel/exit.c
+@@ -587,7 +587,7 @@
+       return retval;
+ }
+-#if !defined(__alpha__) && !defined(__ia64__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
+ /*
+  * sys_waitpid() remains for compatibility. waitpid() should be
+--- linux-2.4.27/kernel/fork.c~2.4.27-vrs1
++++ linux-2.4.27/kernel/fork.c
+@@ -75,7 +75,11 @@
+        * value: the thread structures can take up at most half
+        * of memory.
+        */
++#if THREAD_SIZE > PAGE_SIZE
+       max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8;
++#else
++      max_threads = (mempages * PAGE_SIZE) / (8 * THREAD_SIZE);
++#endif
+       init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
+       init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+@@ -220,6 +224,7 @@
+ fail_nomem:
+       flush_tlb_mm(current->mm);
++      memc_update_mm(mm);
+       return retval;
+ }
+--- linux-2.4.27/kernel/ksyms.c~2.4.27-vrs1
++++ linux-2.4.27/kernel/ksyms.c
+@@ -345,6 +345,7 @@
+ /* tty routines */
+ EXPORT_SYMBOL(tty_hangup);
++EXPORT_SYMBOL(tty_vhangup);
+ EXPORT_SYMBOL(tty_wait_until_sent);
+ EXPORT_SYMBOL(tty_check_change);
+ EXPORT_SYMBOL(tty_hung_up_p);
+@@ -439,9 +440,11 @@
+ EXPORT_SYMBOL(kiobuf_wait_for_io);
+ /* dma handling */
++#ifdef CONFIG_GENERIC_ISA_DMA
+ EXPORT_SYMBOL(request_dma);
+ EXPORT_SYMBOL(free_dma);
+ EXPORT_SYMBOL(dma_spin_lock);
++#endif
+ #ifdef HAVE_DISABLE_HLT
+ EXPORT_SYMBOL(disable_hlt);
+ EXPORT_SYMBOL(enable_hlt);
+--- linux-2.4.27/kernel/signal.c~2.4.27-vrs1
++++ linux-2.4.27/kernel/signal.c
+@@ -778,8 +778,8 @@
+       info.si_uid = tsk->uid;
+       /* FIXME: find out whether or not this is supposed to be c*time. */
+-      info.si_utime = tsk->times.tms_utime;
+-      info.si_stime = tsk->times.tms_stime;
++      info.si_utime = hz_to_std(tsk->times.tms_utime);
++      info.si_stime = hz_to_std(tsk->times.tms_stime);
+       status = tsk->exit_code & 0x7f;
+       why = SI_KERNEL;        /* shouldn't happen */
+@@ -1277,7 +1277,7 @@
+ #endif /* __sparc__ */
+ #endif
+-#if !defined(__alpha__) && !defined(__ia64__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__arm__)
+ /*
+  * For backwards compatibility.  Functionality superseded by sigprocmask.
+  */
+@@ -1305,7 +1305,8 @@
+ }
+ #endif /* !defined(__alpha__) */
+-#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__)
++#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips__) && \
++    !defined(__arm__)
+ /*
+  * For backwards compatibility.  Functionality superseded by sigaction.
+  */
+@@ -1322,4 +1323,4 @@
+       return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
+ }
+-#endif /* !alpha && !__ia64__ && !defined(__mips__) */
++#endif /* !alpha && !__ia64__ && !defined(__mips__) && !defined(__arm__) */
+--- linux-2.4.27/kernel/sys.c~2.4.27-vrs1
++++ linux-2.4.27/kernel/sys.c
+@@ -801,16 +801,23 @@
+ asmlinkage long sys_times(struct tms * tbuf)
+ {
++      struct tms temp;
++
+       /*
+        *      In the SMP world we might just be unlucky and have one of
+        *      the times increment as we use it. Since the value is an
+        *      atomically safe type this is just fine. Conceptually its
+        *      as if the syscall took an instant longer to occur.
+        */
+-      if (tbuf)
+-              if (copy_to_user(tbuf, &current->times, sizeof(struct tms)))
++      if (tbuf) {
++              temp.tms_utime = hz_to_std(current->times.tms_utime);
++              temp.tms_stime = hz_to_std(current->times.tms_stime);
++              temp.tms_cutime = hz_to_std(current->times.tms_cutime);
++              temp.tms_cstime = hz_to_std(current->times.tms_cstime);
++              if (copy_to_user(tbuf, &temp, sizeof(struct tms)))
+                       return -EFAULT;
+-      return jiffies;
++      }
++      return hz_to_std(jiffies);
+ }
+ /*
+--- linux-2.4.27/lib/string.c~2.4.27-vrs1
++++ linux-2.4.27/lib/string.c
+@@ -188,10 +188,10 @@
+  */
+ char * strchr(const char * s, int c)
+ {
+-      for(; *s != (char) c; ++s)
+-              if (*s == '\0')
+-                      return NULL;
+-      return (char *) s;
++      for(; *s != '\0'; ++s)
++              if (*s == (char) c)
++                      return (char *) s;
++      return NULL;
+ }
+ #endif
+--- linux-2.4.27/mm/Makefile~2.4.27-vrs1
++++ linux-2.4.27/mm/Makefile
+@@ -18,4 +18,5 @@
+ obj-$(CONFIG_HIGHMEM) += highmem.o
++obj-y += debug.o
+ include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.27/mm/debug.c
+@@ -0,0 +1,159 @@
++/*
++ * linux/mm/debug.c
++ *
++ *  Copyright (C) 2001 Russell King.
++ *
++ * 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.
++ *
++ *  Dump out page information on SysRQ-G, and provide show_page_info()
++ *
++ *  You are advised to use a serial console with this patch - it
++ *  saturates a 38400baud link for 1 minute on a 32MB machine.
++ */
++#include <linux/kernel.h>
++#include <linux/mm.h>
++#include <linux/pagemap.h>
++#include <linux/sysrq.h>
++#include <linux/init.h>
++
++/*
++ * We print out the following information for each page in the system:
++ *    address: use count, age, mapping, [RSsr] rD [acd]
++ *
++ * The flags are:
++ *    R - reserved
++ *    S - in swapcache
++ *    s - slab page
++ *
++ *    r - referenced
++ *    D - dirty
++ *
++ *    a - active page
++ */
++static void page_detail(struct page *page)
++{
++      if (!page)
++              return;
++
++      printk("%p: %2d %p [%c%c%c] %c%c [%c]\n",
++                      page_address(page),
++                      page_count(page),
++                      page->mapping,
++
++                      PageReserved(page) ? 'R' : '-',
++                      PageSwapCache(page) ? 'S' : '-',
++                      PageSlab(page) ? 's' : '-',
++
++                      PageReferenced(page) ? 'r' : '-',
++                      PageDirty(page) ? 'D' : '-',
++
++                      PageActive(page) ? 'a' : '-');
++}
++
++/*
++ * This version collects statistics
++ */
++static int anon_pages, slab_pages, resvd_pages, unused_pages;
++
++static void page_statistics(struct page *page)
++{
++      if (page) {
++              if (PageReserved(page))
++                      resvd_pages++;
++              else if (PageSlab(page))
++                      slab_pages++;
++              else if (!page_count(page))
++                      unused_pages ++;
++              else if (!page->mapping)
++                      anon_pages ++;
++              return;
++      }
++
++      printk("  anon: %d, slab: %d, reserved: %d, free: %d\n",
++              anon_pages, slab_pages, resvd_pages, unused_pages);
++}
++
++static void show_zone_info(zone_t *zone, void (*fn)(struct page *))
++{
++      int i;
++
++      printk("  total %ld, free %ld\n",
++              zone->size, zone->free_pages);
++
++      anon_pages    = 0;
++      slab_pages    = 0;
++      resvd_pages   = 0;
++      unused_pages  = 0;
++
++      for (i = 0; i < zone->size; i++) {
++              struct page *page = zone->zone_mem_map + i;
++
++              fn(page);
++      }
++
++      fn(NULL);
++}
++
++static void show_node_info(pg_data_t *pg, void (*fn)(struct page *))
++{
++      int type;
++
++      for (type = 0; type < MAX_NR_ZONES; type++) {
++              zone_t *zone = pg->node_zones + type;
++
++              if (zone->size == 0)
++                      continue;
++
++              printk("----- Zone %d ------\n", type);
++
++              show_zone_info(zone, fn);
++      }
++}
++
++static void __show_page_info(void (*fn)(struct page *))
++{
++      pg_data_t *pg;
++      int pgdat = 0;
++
++      for (pg = pgdat_list; pg; pg = pg->node_next) {
++
++              printk("===== Node %d =====\n", pgdat++);
++
++              show_node_info(pg, fn);
++      }       
++}
++
++void show_page_info(void)
++{
++      __show_page_info(page_detail);
++}
++
++static void
++show_pg_info(int key, struct pt_regs *regs, struct kbd_struct *kd,
++           struct tty_struct *tty)
++{
++      void (*fn)(struct page *);
++      show_mem();
++      if (key == 'g')
++              fn = page_detail;
++      else
++              fn = page_statistics;
++      __show_page_info(fn);
++}
++
++static struct sysrq_key_op page_info_op = {
++      handler:        show_pg_info,
++      help_msg:       "paGeinfo",
++      action_msg:     "Page Info",
++};
++
++static int __init debug_mm_init(void)
++{
++      register_sysrq_key('g', &page_info_op);
++      register_sysrq_key('h', &page_info_op);
++      return 0;
++}
++
++__initcall(debug_mm_init);
+--- linux-2.4.27/mm/filemap.c~2.4.27-vrs1
++++ linux-2.4.27/mm/filemap.c
+@@ -2210,10 +2210,14 @@
+       pte_t pte = *ptep;
+       if (pte_present(pte)) {
+-              struct page *page = pte_page(pte);
+-              if (VALID_PAGE(page) && !PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
+-                      flush_tlb_page(vma, address);
+-                      set_page_dirty(page);
++              unsigned long pfn = pte_pfn(pte);
++
++              if (pfn_valid(pfn)) {
++                      struct page *page = pfn_to_page(pfn);
++                      if (!PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
++                              flush_tlb_page(vma, address);
++                              set_page_dirty(page);
++                      }
+               }
+       }
+       return 0;
+--- linux-2.4.27/mm/memory.c~2.4.27-vrs1
++++ linux-2.4.27/mm/memory.c
+@@ -77,8 +77,13 @@
+  */
+ void __free_pte(pte_t pte)
+ {
+-      struct page *page = pte_page(pte);
+-      if ((!VALID_PAGE(page)) || PageReserved(page))
++      unsigned long pfn = pte_pfn(pte);
++      struct page *page;
++
++      if (!pfn_valid(pfn))
++              return;
++      page = pfn_to_page(pfn);
++      if (PageReserved(page))
+               return;
+       if (pte_dirty(pte))
+               set_page_dirty(page);           
+@@ -232,6 +237,7 @@
+                       do {
+                               pte_t pte = *src_pte;
+                               struct page *ptepage;
++                              unsigned long pfn;
+                               
+                               /* copy_one_pte */
+@@ -241,9 +247,11 @@
+                                       swap_duplicate(pte_to_swp_entry(pte));
+                                       goto cont_copy_pte_range;
+                               }
+-                              ptepage = pte_page(pte);
+-                              if ((!VALID_PAGE(ptepage)) || 
+-                                  PageReserved(ptepage))
++                              pfn = pte_pfn(pte);
++                              if (!pfn_valid(pfn))
++                                      goto cont_copy_pte_range;
++                              ptepage = pfn_to_page(pfn);
++                              if (PageReserved(ptepage))
+                                       goto cont_copy_pte_range;
+                               /* If it's a COW mapping, write protect it both in the parent and the child */
+@@ -314,9 +322,13 @@
+               if (pte_none(pte))
+                       continue;
+               if (pte_present(pte)) {
+-                      struct page *page = pte_page(pte);
+-                      if (VALID_PAGE(page) && !PageReserved(page))
+-                              freed ++;
++                      unsigned long pfn = pte_pfn(pte);
++
++                      if (pfn_valid(pfn)) {
++                              struct page *page = pfn_to_page(pfn);
++                              if (!PageReserved(page))
++                                      freed ++;
++                      }
+                       /* This will eventually call __free_pte on the pte. */
+                       tlb_remove_page(tlb, ptep, address + offset);
+               } else {
+@@ -354,17 +366,37 @@
+       return freed;
+ }
++void unmap_page_range(mmu_gather_t *tlb, struct mm_struct *mm, unsigned long address, unsigned long end)
++{
++      int freed = 0;
++      pgd_t * dir;
++
++      if (address >= end)
++              BUG();
++      dir = pgd_offset(mm, address);
++      do {
++              freed += zap_pmd_range(tlb, dir, address, end - address);
++              address = (address + PGDIR_SIZE) & PGDIR_MASK;
++              dir++;
++      } while (address && (address < end));
++
++      /*
++       * Update rss for the mm_struct (not necessarily current->mm)
++       * Notice that rss is an unsigned long.
++       */
++      if (mm->rss > freed)
++              mm->rss -= freed;
++      else
++              mm->rss = 0;
++}
++
+ /*
+  * remove user pages in a given range.
+  */
+ void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size)
+ {
+-      mmu_gather_t *tlb;
+-      pgd_t * dir;
+       unsigned long start = address, end = address + size;
+-      int freed = 0;
+-
+-      dir = pgd_offset(mm, address);
++      mmu_gather_t *tlb;
+       /*
+        * This is a long-lived spinlock. That's fine.
+@@ -377,25 +409,10 @@
+               BUG();
+       spin_lock(&mm->page_table_lock);
+       flush_cache_range(mm, address, end);
+-      tlb = tlb_gather_mmu(mm);
+-      do {
+-              freed += zap_pmd_range(tlb, dir, address, end - address);
+-              address = (address + PGDIR_SIZE) & PGDIR_MASK;
+-              dir++;
+-      } while (address && (address < end));
+-
+-      /* this will flush any remaining tlb entries */
++      tlb = tlb_gather_mmu(mm);
++      unmap_page_range(tlb, mm, address, end);
+       tlb_finish_mmu(tlb, start, end);
+-
+-      /*
+-       * Update rss for the mm_struct (not necessarily current->mm)
+-       * Notice that rss is an unsigned long.
+-       */
+-      if (mm->rss > freed)
+-              mm->rss -= freed;
+-      else
+-              mm->rss = 0;
+       spin_unlock(&mm->page_table_lock);
+ }
+@@ -407,6 +424,7 @@
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *ptep, pte;
++      unsigned long pfn;
+       pgd = pgd_offset(mm, address);
+       if (pgd_none(*pgd) || pgd_bad(*pgd))
+@@ -423,8 +441,11 @@
+       pte = *ptep;
+       if (pte_present(pte)) {
+               if (!write ||
+-                  (pte_write(pte) && pte_dirty(pte)))
+-                      return pte_page(pte);
++                  (pte_write(pte) && pte_dirty(pte))) {
++                      pfn = pte_pfn(pte);
++                      if (pfn_valid(pfn))
++                              return pfn_to_page(pfn);
++              }
+       }
+ out:
+@@ -439,7 +460,8 @@
+ static inline struct page * get_page_map(struct page *page)
+ {
+-      if (!VALID_PAGE(page))
++      unsigned long pfn = page_to_pfn(page);
++      if (!pfn_valid(pfn))
+               return 0;
+       return page;
+ }
+@@ -826,22 +848,22 @@
+       unsigned long phys_addr, pgprot_t prot)
+ {
+       unsigned long end;
++      unsigned long pfn;
+       address &= ~PMD_MASK;
+       end = address + size;
+       if (end > PMD_SIZE)
+               end = PMD_SIZE;
++      pfn = phys_addr >> PAGE_SHIFT;
+       do {
+-              struct page *page;
+               pte_t oldpage;
+               oldpage = ptep_get_and_clear(pte);
+-              page = virt_to_page(__va(phys_addr));
+-              if ((!VALID_PAGE(page)) || PageReserved(page))
+-                      set_pte(pte, mk_pte_phys(phys_addr, prot));
++              if (!pfn_valid(pfn) || PageReserved(pfn_to_page(pfn)))
++                      set_pte(pte, pfn_pte(pfn, prot));
+               forget_pte(oldpage);
+               address += PAGE_SIZE;
+-              phys_addr += PAGE_SIZE;
++              pfn++;
+               pte++;
+       } while (address && (address < end));
+ }
+@@ -949,10 +971,11 @@
+       unsigned long address, pte_t *page_table, pte_t pte)
+ {
+       struct page *old_page, *new_page;
++      unsigned long pfn = pte_pfn(pte);
+-      old_page = pte_page(pte);
+-      if (!VALID_PAGE(old_page))
++      if (!pfn_valid(pfn))
+               goto bad_wp_page;
++      old_page = pte_page(pte);
+       if (!TryLockPage(old_page)) {
+               int reuse = can_share_swap_page(old_page);
+@@ -996,7 +1019,7 @@
+ bad_wp_page:
+       spin_unlock(&mm->page_table_lock);
+-      printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n",address,(unsigned long)old_page);
++      printk("do_wp_page: bogus page at address %08lx\n", address);
+       return -1;
+ no_mem:
+       page_cache_release(old_page);
+--- linux-2.4.27/mm/mmap.c~2.4.27-vrs1
++++ linux-2.4.27/mm/mmap.c
+@@ -18,6 +18,9 @@
+ #include <asm/uaccess.h>
+ #include <asm/pgalloc.h>
++#include <asm/tlb.h>
++
++extern void unmap_page_range(mmu_gather_t *tlb, struct mm_struct *mm, unsigned long start, unsigned long end);
+ /*
+  * WARNING: the debugging will use recursive algorithms so never enable this
+@@ -915,6 +918,8 @@
+        * old method of shifting the VA >> by PGDIR_SHIFT doesn't work.
+        */
+       start_index = pgd_index(first);
++      if (start_index < FIRST_USER_PGD_NR)
++              start_index = FIRST_USER_PGD_NR;
+       end_index = pgd_index(last);
+       if (end_index > start_index) {
+               clear_page_tables(mm, start_index, end_index - start_index);
+@@ -1046,7 +1051,6 @@
+       len = PAGE_ALIGN(len);
+       if (!len)
+               return addr;
+-
+       if ((addr + len) > TASK_SIZE || (addr + len) < addr)
+               return -EINVAL;
+@@ -1135,45 +1139,58 @@
+ /* Release all mmaps. */
+ void exit_mmap(struct mm_struct * mm)
+ {
++      mmu_gather_t *tlb;
+       struct vm_area_struct * mpnt;
+       release_segments(mm);
+       spin_lock(&mm->page_table_lock);
++
++      tlb = tlb_gather_mmu(mm);
++
++      flush_cache_mm(mm);
++      mpnt = mm->mmap;
++      while (mpnt) {
++              unsigned long start = mpnt->vm_start;
++              unsigned long end = mpnt->vm_end;
++
++              mm->map_count--;
++              remove_shared_vm_struct(mpnt);
++              unmap_page_range(tlb, mm, start, end);
++              mpnt = mpnt->vm_next;
++      }
++
++      /* This is just debugging */
++      if (mm->map_count)
++              BUG();
++
++      tlb_finish_mmu(tlb, 0, TASK_SIZE);
++
+       mpnt = mm->mmap;
+       mm->mmap = mm->mmap_cache = NULL;
+       mm->mm_rb = RB_ROOT;
+       mm->rss = 0;
+-      spin_unlock(&mm->page_table_lock);
+       mm->total_vm = 0;
+       mm->locked_vm = 0;
+-      flush_cache_mm(mm);
++      spin_unlock(&mm->page_table_lock);
++
++      clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
++
++      /*
++       * Walk the list again, actually closing and freeing it
++       * without holding any MM locks.
++       */
+       while (mpnt) {
+               struct vm_area_struct * next = mpnt->vm_next;
+-              unsigned long start = mpnt->vm_start;
+-              unsigned long end = mpnt->vm_end;
+-              unsigned long size = end - start;
+-
+               if (mpnt->vm_ops) {
+                       if (mpnt->vm_ops->close)
+                               mpnt->vm_ops->close(mpnt);
+               }
+-              mm->map_count--;
+-              remove_shared_vm_struct(mpnt);
+-              zap_page_range(mm, start, size);
+               if (mpnt->vm_file)
+                       fput(mpnt->vm_file);
+               kmem_cache_free(vm_area_cachep, mpnt);
+               mpnt = next;
+       }
+-
+-      /* This is just debugging */
+-      if (mm->map_count)
+-              BUG();
+-
+-      clear_page_tables(mm, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
+-
+-      flush_tlb_mm(mm);
+ }
+ /* Insert vm structure into process list sorted by address
+--- linux-2.4.27/mm/slab.c~2.4.27-vrs1
++++ linux-2.4.27/mm/slab.c
+@@ -1391,7 +1391,7 @@
+ #if DEBUG
+ # define CHECK_NR(pg)                                         \
+       do {                                                    \
+-              if (!VALID_PAGE(pg)) {                          \
++              if (!virt_addr_valid(pg)) {                     \
+                       printk(KERN_ERR "kfree: out of range ptr %lxh.\n", \
+                               (unsigned long)objp);           \
+                       BUG();                                  \
+@@ -1950,12 +1950,12 @@
+       name = cachep->name; 
+       {
+       char tmp; 
+-      mm_segment_t    old_fs;
++      mm_segment_t old_fs;
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       if (__get_user(tmp, name)) 
+               name = "broken"; 
+-      set_fs(old_fs);
++      set_fs (old_fs);
+       }       
+       seq_printf(m, "%-17s %6lu %6lu %6u %4lu %4lu %4u",
+--- linux-2.4.27/mm/vmalloc.c~2.4.27-vrs1
++++ linux-2.4.27/mm/vmalloc.c
+@@ -44,8 +44,13 @@
+               if (pte_none(page))
+                       continue;
+               if (pte_present(page)) {
+-                      struct page *ptpage = pte_page(page);
+-                      if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
++                      unsigned long pfn = pte_pfn(page);
++                      struct page *ptpage;
++
++                      if (!pfn_valid(pfn))
++                              continue;
++                      ptpage = pfn_to_page(pfn);
++                      if (!PageReserved(ptpage))
+                               __free_page(ptpage);
+                       continue;
+               }
+--- linux-2.4.27/mm/vmscan.c~2.4.27-vrs1
++++ linux-2.4.27/mm/vmscan.c
+@@ -121,6 +121,7 @@
+ drop_pte:
+               mm->rss--;
+               UnlockPage(page);
++              memc_clear(vma->vm_mm, page);
+               {
+                       int freeable = page_count(page) - !!page->buffers <= 2;
+                       page_cache_release(page);
+@@ -206,13 +207,16 @@
+       do {
+               if (pte_present(*pte)) {
+-                      struct page *page = pte_page(*pte);
++                      unsigned long pfn = pte_pfn(*pte);
+-                      if (VALID_PAGE(page) && !PageReserved(page)) {
+-                              count -= try_to_swap_out(mm, vma, address, pte, page, classzone);
+-                              if (!count) {
+-                                      address += PAGE_SIZE;
+-                                      break;
++                      if (pfn_valid(pfn)) {
++                              struct page *page = pfn_to_page(pfn);
++                              if (!PageReserved(page)) {
++                                      count -= try_to_swap_out(mm, vma, address, pte, page, classzone);
++                                      if (!count) {
++                                              address += PAGE_SIZE;
++                                              break;
++                                      }
+                               }
+                       }
+               }
+--- linux-2.4.27/net/irda/af_irda.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/af_irda.c
+@@ -1176,7 +1176,7 @@
+ #endif /* CONFIG_IRDA_ULTRA */
+       kfree(self);
+       MOD_DEC_USE_COUNT;
+-      
++
+       return;
+ }
+@@ -2571,19 +2571,15 @@
+       sock_register(&irda_family_ops);
+       irda_packet_type.type = htons(ETH_P_IRDA);
+-        dev_add_pack(&irda_packet_type);
++      dev_add_pack(&irda_packet_type);
+       register_netdevice_notifier(&irda_dev_notifier);
+-
+       irda_init();
+ #ifdef MODULE
+-      irda_device_init();  /* Called by init/main.c when non-modular */
++      irda_device_init();  /* Called by init/main.c when non-modular */
+ #endif
+       return 0;
+ }
+-#ifdef MODULE
+-module_init(irda_proto_init); /* If non-module, called from init/main.c */
+-#endif
+ /*
+  * Function irda_proto_cleanup (void)
+@@ -2591,25 +2587,27 @@
+  *    Remove IrDA protocol layer
+  *
+  */
+-#ifdef MODULE
+-void irda_proto_cleanup(void)
++static void __exit irda_proto_cleanup(void)
+ {
+       irda_packet_type.type = htons(ETH_P_IRDA);
+-        dev_remove_pack(&irda_packet_type);
++      dev_remove_pack(&irda_packet_type);
++
++      unregister_netdevice_notifier(&irda_dev_notifier);
+-        unregister_netdevice_notifier(&irda_dev_notifier);
+-      
+       sock_unregister(PF_IRDA);
++
+       irda_cleanup();
+-      
+-        return;
+ }
++
++#ifdef MODULE
++module_init(irda_proto_init);
+ module_exit(irda_proto_cleanup);
+- 
++
+ MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+-MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem"); 
++MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem");
+ MODULE_LICENSE("GPL");
+ #ifdef CONFIG_IRDA_DEBUG
+ MODULE_PARM(irda_debug, "1l");
+ #endif
+-#endif /* MODULE */
++#endif
++
+--- linux-2.4.27/net/irda/iriap.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/iriap.c
+@@ -1004,18 +1004,20 @@
+                       
+                       switch (attrib->value->type) {
+                       case IAS_INTEGER:
+-                              len += sprintf(buf+len, "%d\n", 
++                              len += sprintf(buf+len, "%d", 
+                                              attrib->value->t.integer);
+                               break;
+                       case IAS_STRING:
+-                              len += sprintf(buf+len, "\"%s\"\n", 
++                              len += sprintf(buf+len, "\"%s\"", 
+                                              attrib->value->t.string);
+                               break;
+                       case IAS_OCT_SEQ:
+-                              len += sprintf(buf+len, "octet sequence (%d bytes)\n", attrib->value->len);
++                              len += sprintf(buf+len,
++                                             "octet sequence (%d bytes)",
++                                             attrib->value->len);
+                               break;
+                       case IAS_MISSING:
+-                              len += sprintf(buf+len, "missing\n");
++                              len += sprintf(buf+len, "missing");
+                               break;
+                       default:
+                               IRDA_DEBUG(0, "%s(), Unknown value type!\n", __FUNCTION__);
+@@ -1027,6 +1029,7 @@
+                               hashbin_get_next(obj->attribs);
+               }
+               obj = (struct ias_object *) hashbin_get_next(objects);
++              len += sprintf(buf+len, "\n");
+       } 
+       restore_flags(flags);
+--- linux-2.4.27/net/irda/irlan/irlan_common.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/irlan/irlan_common.c
+@@ -317,6 +317,9 @@
+       del_timer(&self->watchdog_timer);
++      /* since irlan_do_*_event gobble the skb, we must get it once -- rmk */
++      skb_get(skb);
++
+       /* If you want to pass the skb to *both* state machines, you will
+        * need to skb_clone() it, so that you don't free it twice.
+        * As the state machines don't need it, git rid of it here...
+--- linux-2.4.27/net/irda/irlap_event.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/irlap_event.c
+@@ -532,7 +532,7 @@
+ #endif /* CONFIG_IRDA_ULTRA */
+       case RECV_TEST_CMD:
+               /* Remove test frame header */
+-              skb_pull(skb, sizeof(struct test_frame));
++              skb_pull(skb, 10 /*sizeof(struct test_frame)*/);
+               /* 
+                * Send response. This skb will not be sent out again, and
+@@ -740,7 +740,7 @@
+       switch (event) {
+       case CONNECT_RESPONSE:
+-              skb_pull(skb, sizeof(struct snrm_frame));
++              skb_pull(skb, 11 /*sizeof(struct snrm_frame)*/);
+               ASSERT(self->netdev != NULL, return -1;);
+@@ -870,7 +870,7 @@
+                       ASSERT(self->netdev != NULL, return -1;);
+-                      skb_pull(skb, sizeof(struct snrm_frame));
++                      skb_pull(skb, 11 /*sizeof(struct snrm_frame)*/);
+                       irlap_qos_negotiate(self, skb);
+                       
+@@ -902,7 +902,7 @@
+               /* Negotiate connection parameters */
+               ASSERT(skb->len > 10, return -1;);
+-              skb_pull(skb, sizeof(struct ua_frame));
++              skb_pull(skb, 10 /*sizeof(struct ua_frame)*/);
+               ASSERT(self->netdev != NULL, return -1;);
+--- linux-2.4.27/net/irda/irlap_frame.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/irlap_frame.c
+@@ -1,5 +1,5 @@
+ /*********************************************************************
+- *                
++ *
+  * Filename:      irlap_frame.c
+  * Version:       1.0
+  * Description:   Build and transmit IrLAP frames
+@@ -8,18 +8,18 @@
+  * Created at:    Tue Aug 19 10:27:26 1997
+  * Modified at:   Wed Jan  5 08:59:04 2000
+  * Modified by:   Dag Brattli <dagb@cs.uit.no>
+- * 
+- *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, 
++ *
++ *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
+  *     All Rights Reserved.
+  *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.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 of 
++ *
++ *     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.
+  *
+  *     Neither Dag Brattli nor University of Tromsø admit liability nor
+- *     provide warranty for any of this software. This material is 
++ *     provide warranty for any of this software. This material is
+  *     provided "AS-IS" and at no charge.
+  *
+  ********************************************************************/
+@@ -29,11 +29,12 @@
+ #include <linux/if_ether.h>
+ #include <linux/netdevice.h>
+ #include <linux/irda.h>
+- 
++
+ #include <net/pkt_sched.h>
+ #include <net/sock.h>
+- 
++
+ #include <asm/byteorder.h>
++#include <asm/unaligned.h>
+ #include <net/irda/irda.h>
+ #include <net/irda/irda_device.h>
+@@ -46,18 +47,18 @@
+ /*
+  * Function irlap_insert_info (self, skb)
+  *
+- *    Insert minimum turnaround time and speed information into the skb. We 
++ *    Insert minimum turnaround time and speed information into the skb. We
+  *    need to do this since it's per packet relevant information. Safe to
+  *    have this function inlined since it's only called from one place
+  */
+-static inline void irlap_insert_info(struct irlap_cb *self, 
++static inline void irlap_insert_info(struct irlap_cb *self,
+                                    struct sk_buff *skb)
+ {
+       struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
+-      /*  
++      /*
+        * Insert MTT (min. turn time) and speed into skb, so that the
+-       * device driver knows which settings to use 
++       * device driver knows which settings to use
+        */
+       cb->magic = LAP_MAGIC;
+       cb->mtt = self->mtt_required;
+@@ -65,15 +66,15 @@
+       /* Reset */
+       self->mtt_required = 0;
+-      
+-      /* 
+-       * Delay equals negotiated BOFs count, plus the number of BOFs to 
+-       * force the negotiated minimum turnaround time 
++
++      /*
++       * Delay equals negotiated BOFs count, plus the number of BOFs to
++       * force the negotiated minimum turnaround time
+        */
+       cb->xbofs = self->bofs_count;
+       cb->next_xbofs = self->next_bofs;
+       cb->xbofs_delay = self->xbofs_delay;
+-      
++
+       /* Reset XBOF's delay (used only for getting min turn time) */
+       self->xbofs_delay = 0;
+       /* Put the correct xbofs value for the next packet */
+@@ -104,7 +105,7 @@
+  *
+  *    Transmits a connect SNRM command frame
+  */
+-void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos) 
++void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
+ {
+       struct sk_buff *skb;
+       struct snrm_frame *frame;
+@@ -118,7 +119,7 @@
+       if (!skb)
+               return;
+-      frame = (struct snrm_frame *) skb_put(skb, 2); 
++      frame = (struct snrm_frame *) skb_put(skb, 2);
+       /* Insert connection address field */
+       if (qos)
+@@ -128,17 +129,17 @@
+       /* Insert control field */
+       frame->control = SNRM_CMD | PF_BIT;
+-      
++
+       /*
+-       *  If we are establishing a connection then insert QoS paramerters 
++       *  If we are establishing a connection then insert QoS paramerters
+        */
+       if (qos) {
+               skb_put(skb, 9); /* 21 left */
+-              frame->saddr = cpu_to_le32(self->saddr);
+-              frame->daddr = cpu_to_le32(self->daddr);
++              put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++              put_unaligned(cpu_to_le32(self->daddr), &frame->daddr);
+               frame->ncaddr = self->caddr;
+-                              
++
+               ret = irlap_insert_qos_negotiation_params(self, skb);
+               if (ret < 0) {
+                       dev_kfree_skb(skb);
+@@ -154,13 +155,13 @@
+  *    Received SNRM (Set Normal Response Mode) command frame
+  *
+  */
+-static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, 
+-                              struct irlap_info *info) 
++static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
++                              struct irlap_info *info)
+ {
+       struct snrm_frame *frame;
+       frame = (struct snrm_frame *) skb->data;
+-      
++
+       if (skb->len >= sizeof(struct snrm_frame)) {
+               /* Copy the new connection address ignoring the C/R bit */
+               info->caddr = frame->ncaddr & 0xFE;
+@@ -170,11 +171,11 @@
+                       IRDA_DEBUG(3, "%s(), invalid connection address!\n", __FUNCTION__);
+                       return;
+               }
+-              
++
+               /* Copy peer device address */
+-              info->daddr = le32_to_cpu(frame->saddr);
+-              info->saddr = le32_to_cpu(frame->daddr);
+-              
++              info->daddr = le32_to_cpu(get_unaligned(&frame->saddr));
++              info->saddr = le32_to_cpu(get_unaligned(&frame->daddr));
++
+               /* Only accept if addressed directly to us */
+               if (info->saddr != self->saddr) {
+                       IRDA_DEBUG(2, "%s(), not addressed to us!\n", __FUNCTION__);
+@@ -198,9 +199,9 @@
+       struct sk_buff *skb;
+       struct ua_frame *frame;
+       int ret;
+-      
++
+       IRDA_DEBUG(2, "%s() <%ld>\n", __FUNCTION__, jiffies);
+-      
++
+       ASSERT(self != NULL, return;);
+       ASSERT(self->magic == LAP_MAGIC, return;);
+@@ -212,13 +213,13 @@
+               return;
+       frame = (struct ua_frame *) skb_put(skb, 10);
+-      
++
+       /* Build UA response */
+       frame->caddr = self->caddr;
+       frame->control = UA_RSP | PF_BIT;
+-      frame->saddr = cpu_to_le32(self->saddr);
+-      frame->daddr = cpu_to_le32(self->daddr);
++      put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++      put_unaligned(cpu_to_le32(self->daddr), &frame->daddr);
+       /* Should we send QoS negotiation parameters? */
+       if (qos) {
+@@ -243,7 +244,7 @@
+ {
+       struct sk_buff *skb = NULL;
+       __u8 *frame;
+-      
++
+       ASSERT(self != NULL, return;);
+       ASSERT(self->magic == LAP_MAGIC, return;);
+@@ -252,7 +253,7 @@
+               return;
+       frame = skb_put( skb, 2);
+-      
++
+       if (self->state == LAP_NDM)
+               frame[0] = CBROADCAST;
+       else
+@@ -260,7 +261,7 @@
+       frame[1] = DM_RSP | PF_BIT;
+-      irlap_queue_xmit(self, skb);    
++      irlap_queue_xmit(self, skb);
+ }
+ /*
+@@ -269,11 +270,11 @@
+  *    Send disconnect (DISC) frame
+  *
+  */
+-void irlap_send_disc_frame(struct irlap_cb *self) 
++void irlap_send_disc_frame(struct irlap_cb *self)
+ {
+       struct sk_buff *skb = NULL;
+       __u8 *frame;
+-      
++
+       IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
+       ASSERT(self != NULL, return;);
+@@ -284,7 +285,7 @@
+               return;
+       frame = skb_put(skb, 2);
+-      
++
+       frame[0] = self->caddr | CMD_FRAME;
+       frame[1] = DISC_CMD | PF_BIT;
+@@ -295,17 +296,17 @@
+  * Function irlap_send_discovery_xid_frame (S, s, command)
+  *
+  *    Build and transmit a XID (eXchange station IDentifier) discovery
+- *    frame. 
++ *    frame.
+  */
+-void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s, 
+-                                  __u8 command, discovery_t *discovery) 
++void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
++                                  __u8 command, discovery_t *discovery)
+ {
+       struct sk_buff *skb = NULL;
+       struct xid_frame *frame;
+-      __u32 bcast = BROADCAST;
++      __u32 bcast = BROADCAST, daddr;
+       __u8 *info;
+-      IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __FUNCTION__, s, S, 
++      IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __FUNCTION__, s, S,
+                  command);
+       ASSERT(self != NULL, return;);
+@@ -328,13 +329,14 @@
+       }
+       frame->ident = XID_FORMAT;
+-      frame->saddr = cpu_to_le32(self->saddr);
+-
+       if (command)
+-              frame->daddr = cpu_to_le32(bcast);
++              daddr = bcast;
+       else
+-              frame->daddr = cpu_to_le32(discovery->daddr);
+-      
++              daddr = discovery->daddr;
++
++      put_unaligned(cpu_to_le32(self->saddr), &frame->saddr);
++      put_unaligned(cpu_to_le32(daddr), &frame->daddr);
++
+       switch (S) {
+       case 1:
+               frame->flags = 0x00;
+@@ -353,10 +355,10 @@
+               break;
+       }
+-      frame->slotnr = s; 
++      frame->slotnr = s;
+       frame->version = 0x00;
+-      /*  
++      /*
+        *  Provide info for final slot only in commands, and for all
+        *  responses. Send the second byte of the hint only if the
+        *  EXTENSION bit is set in the first byte.
+@@ -365,7 +367,7 @@
+               int len;
+               if (discovery->hints.byte[0] & HINT_EXTENSION) {
+-                      info = skb_put(skb, 2);         
++                      info = skb_put(skb, 2);
+                       info[0] = discovery->hints.byte[0];
+                       info[1] = discovery->hints.byte[1];
+               } else {
+@@ -378,7 +380,7 @@
+               len = IRDA_MIN(discovery->name_len, skb_tailroom(skb));
+               info = skb_put(skb, len);
+               memcpy(info, discovery->nickname, len);
+-      } 
++      }
+       irlap_queue_xmit(self, skb);
+ }
+@@ -388,9 +390,9 @@
+  *    Received a XID discovery response
+  *
+  */
+-static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, 
+-                                       struct sk_buff *skb, 
+-                                       struct irlap_info *info) 
++static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
++                                       struct sk_buff *skb,
++                                       struct irlap_info *info)
+ {
+       struct xid_frame *xid;
+       discovery_t *discovery = NULL;
+@@ -404,8 +406,8 @@
+       xid = (struct xid_frame *) skb->data;
+-      info->daddr = le32_to_cpu(xid->saddr);
+-      info->saddr = le32_to_cpu(xid->daddr);
++      info->daddr = le32_to_cpu(get_unaligned(&xid->saddr));
++      info->saddr = le32_to_cpu(get_unaligned(&xid->daddr));
+       /* Make sure frame is addressed to us */
+       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+@@ -439,11 +441,11 @@
+               discovery->charset = discovery_info[1];
+               text = (char *) &discovery_info[2];
+       }
+-      /* 
+-       *  Terminate info string, should be safe since this is where the 
++      /*
++       *  Terminate info string, should be safe since this is where the
+        *  FCS bytes resides.
+        */
+-      skb->data[skb->len] = '\0'; 
++      skb->data[skb->len] = '\0';
+       strncpy(discovery->nickname, text, NICKNAME_MAX_LEN);
+       discovery->name_len = strlen(discovery->nickname);
+@@ -458,9 +460,9 @@
+  *    Received a XID discovery command
+  *
+  */
+-static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, 
+-                                       struct sk_buff *skb, 
+-                                       struct irlap_info *info) 
++static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
++                                       struct sk_buff *skb,
++                                       struct irlap_info *info)
+ {
+       struct xid_frame *xid;
+       discovery_t *discovery = NULL;
+@@ -469,8 +471,8 @@
+       xid = (struct xid_frame *) skb->data;
+-      info->daddr = le32_to_cpu(xid->saddr);
+-      info->saddr = le32_to_cpu(xid->daddr);
++      info->daddr = le32_to_cpu(get_unaligned(&xid->saddr));
++      info->saddr = le32_to_cpu(get_unaligned(&xid->daddr));
+       /* Make sure frame is addressed to us */
+       if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
+@@ -497,11 +499,11 @@
+               return;
+       }
+       info->s = xid->slotnr;
+-      
++
+       discovery_info = skb_pull(skb, sizeof(struct xid_frame));
+-      /* 
+-       *  Check if last frame 
++      /*
++       *  Check if last frame
+        */
+       if (info->s == 0xff) {
+               /* Check if things are sane at this point... */
+@@ -518,7 +520,7 @@
+                       WARNING("%s(), unable to malloc!\n", __FUNCTION__);
+                       return;
+               }
+-            
++
+               discovery->daddr = info->daddr;
+               discovery->saddr = self->saddr;
+               discovery->timestamp = jiffies;
+@@ -533,11 +535,11 @@
+                       discovery->charset = discovery_info[1];
+                       text = (char *) &discovery_info[2];
+               }
+-              /* 
+-               *  Terminate string, should be safe since this is where the 
++              /*
++               *  Terminate string, should be safe since this is where the
+                *  FCS bytes resides.
+                */
+-              skb->data[skb->len] = '\0'; 
++              skb->data[skb->len] = '\0';
+               strncpy(discovery->nickname, text, NICKNAME_MAX_LEN);
+               discovery->name_len = strlen(discovery->nickname);
+@@ -554,7 +556,7 @@
+  *    Build and transmit RR (Receive Ready) frame. Notice that it is currently
+  *    only possible to send RR frames with the poll bit set.
+  */
+-void irlap_send_rr_frame(struct irlap_cb *self, int command) 
++void irlap_send_rr_frame(struct irlap_cb *self, int command)
+ {
+       struct sk_buff *skb;
+       __u8 *frame;
+@@ -562,9 +564,9 @@
+       skb = dev_alloc_skb(16);
+       if (!skb)
+               return;
+-      
++
+       frame = skb_put(skb, 2);
+-      
++
+       frame[0] = self->caddr;
+       frame[0] |= (command) ? CMD_FRAME : 0;
+@@ -576,7 +578,7 @@
+ /*
+  * Function irlap_send_rd_frame (self)
+  *
+- *    Request disconnect. Used by a secondary station to request the 
++ *    Request disconnect. Used by a secondary station to request the
+  *    disconnection of the link.
+  */
+ void irlap_send_rd_frame(struct irlap_cb *self)
+@@ -587,9 +589,9 @@
+       skb = dev_alloc_skb(16);
+       if (!skb)
+               return;
+-      
++
+       frame = skb_put(skb, 2);
+-      
++
+       frame[0] = self->caddr;
+       frame[1] = RD_RSP | PF_BIT;
+@@ -603,8 +605,8 @@
+  *    making it inline since its called only from one single place
+  *    (irlap_driver_rcv).
+  */
+-static inline void irlap_recv_rr_frame(struct irlap_cb *self, 
+-                                     struct sk_buff *skb, 
++static inline void irlap_recv_rr_frame(struct irlap_cb *self,
++                                     struct sk_buff *skb,
+                                      struct irlap_info *info, int command)
+ {
+       info->nr = skb->data[1] >> 5;
+@@ -620,7 +622,7 @@
+ {
+       struct sk_buff *skb = NULL;
+       __u8 *frame;
+-      
++
+       ASSERT( self != NULL, return;);
+       ASSERT( self->magic == LAP_MAGIC, return;);
+@@ -629,7 +631,7 @@
+               return;
+       frame = skb_put( skb, 2);
+-      
++
+       frame[0] = self->caddr;
+       frame[0] |= (command) ? CMD_FRAME : 0;
+@@ -639,7 +641,7 @@
+       frame[2] = 0;
+-      IRDA_DEBUG(4, "%s(), vr=%d, %ld\n", __FUNCTION__, self->vr, jiffies); 
++      IRDA_DEBUG(4, "%s(), vr=%d, %ld\n", __FUNCTION__, self->vr, jiffies);
+       irlap_queue_xmit(self, skb);
+ }
+@@ -650,8 +652,8 @@
+  *    Received RNR (Receive Not Ready) frame from peer station
+  *
+  */
+-static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, 
+-                               struct irlap_info *info, int command) 
++static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
++                               struct irlap_info *info, int command)
+ {
+       info->nr = skb->data[1] >> 5;
+@@ -663,13 +665,13 @@
+               irlap_do_event(self, RECV_RNR_RSP, skb, info);
+ }
+-static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, 
++static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                struct irlap_info *info, int command)
+ {
+       IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
+       info->nr = skb->data[1] >> 5;
+-      
++
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_REJ_CMD, skb, info);
+@@ -677,13 +679,13 @@
+               irlap_do_event(self, RECV_REJ_RSP, skb, info);
+ }
+-static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, 
++static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+ {
+       IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
+       info->nr = skb->data[1] >> 5;
+-      
++
+       /* Check if this is a command or a response frame */
+       if (command)
+               irlap_do_event(self, RECV_SREJ_CMD, skb, info);
+@@ -691,7 +693,7 @@
+               irlap_do_event(self, RECV_SREJ_RSP, skb, info);
+ }
+-static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, 
++static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+ {
+       IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+@@ -709,9 +711,9 @@
+  *    Received UA (Unnumbered Acknowledgement) frame
+  *
+  */
+-static inline void irlap_recv_ua_frame(struct irlap_cb *self, 
+-                                     struct sk_buff *skb, 
+-                                     struct irlap_info *info) 
++static inline void irlap_recv_ua_frame(struct irlap_cb *self,
++                                     struct sk_buff *skb,
++                                     struct irlap_info *info)
+ {
+       irlap_do_event(self, RECV_UA_RSP, skb, info);
+ }
+@@ -728,25 +730,25 @@
+       if (skb->data[1] == I_FRAME) {
+-              /*  
++              /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+-              
++
+               /* Copy buffer */
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+-              
+-              /* 
+-               *  Insert frame in store, in case of retransmissions 
++
++              /*
++               *  Insert frame in store, in case of retransmissions
+                */
+               skb_queue_tail(&self->wx_list, skb_get(skb));
+-              
++
+               self->vs = (self->vs + 1) % 8;
+-              self->ack_required = FALSE;             
++              self->ack_required = FALSE;
+               self->window -= 1;
+               irlap_send_i_frame( self, tx_skb, CMD_FRAME);
+@@ -761,40 +763,40 @@
+  *
+  *    Send I(nformation) frame as primary with poll bit set
+  */
+-void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) 
++void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
+ {
+       struct sk_buff *tx_skb;
+       /* Stop P timer */
+       del_timer(&self->poll_timer);
+-              
++
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+-              
+-              /*  
++
++              /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+-              
++
+               /* Copy buffer */
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+               }
+-              
+-              /* 
+-               *  Insert frame in store, in case of retransmissions 
++
++              /*
++               *  Insert frame in store, in case of retransmissions
+                */
+               skb_queue_tail(&self->wx_list, skb_get(skb));
+-              
+-              /*  
++
++              /*
+                *  Set poll bit if necessary. We do this to the copied
+                *  skb, since retransmitted need to set or clear the poll
+-               *  bit depending on when they are sent.  
++               *  bit depending on when they are sent.
+                */
+               tx_skb->data[1] |= PF_BIT;
+-              
++
+               self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+@@ -827,8 +829,8 @@
+  *    Send I(nformation) frame as secondary with final bit set
+  *
+  */
+-void irlap_send_data_secondary_final(struct irlap_cb *self, 
+-                                   struct sk_buff *skb) 
++void irlap_send_data_secondary_final(struct irlap_cb *self,
++                                   struct sk_buff *skb)
+ {
+       struct sk_buff *tx_skb = NULL;
+@@ -839,26 +841,26 @@
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+-              /*  
++              /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+-              
++
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+-              }               
++              }
+               /* Insert frame in store */
+               skb_queue_tail(&self->wx_list, skb_get(skb));
+-              
++
+               tx_skb->data[1] |= PF_BIT;
+-              
+-              self->vs = (self->vs + 1) % 8; 
++
++              self->vs = (self->vs + 1) % 8;
+               self->ack_required = FALSE;
+-              
+-              irlap_send_i_frame(self, tx_skb, RSP_FRAME); 
++
++              irlap_send_i_frame(self, tx_skb, RSP_FRAME);
+       } else {
+               if (self->ack_required) {
+                       irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
+@@ -885,32 +887,32 @@
+  *    Send I(nformation) frame as secondary without final bit set
+  *
+  */
+-void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) 
++void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
+ {
+       struct sk_buff *tx_skb = NULL;
+       /* Is this reliable or unreliable data? */
+       if (skb->data[1] == I_FRAME) {
+-              
+-              /*  
++
++              /*
+                *  Insert frame sequence number (Vs) in control field before
+                *  inserting into transmit window queue.
+                */
+               skb->data[1] = I_FRAME | (self->vs << 1);
+-              
++
+               tx_skb = skb_clone(skb, GFP_ATOMIC);
+               if (tx_skb == NULL) {
+                       return;
+-              }               
+-              
++              }
++
+               /* Insert frame in store */
+               skb_queue_tail(&self->wx_list, skb_get(skb));
+-              
++
+               self->vs = (self->vs + 1) % 8;
+-              self->ack_required = FALSE;             
++              self->ack_required = FALSE;
+               self->window -= 1;
+-              irlap_send_i_frame(self, tx_skb, RSP_FRAME); 
++              irlap_send_i_frame(self, tx_skb, RSP_FRAME);
+       } else {
+               irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME);
+               self->window -= 1;
+@@ -920,8 +922,8 @@
+ /*
+  * Function irlap_resend_rejected_frames (nr)
+  *
+- *    Resend frames which has not been acknowledged. Should be safe to 
+- *    traverse the list without locking it since this function will only be 
++ *    Resend frames which has not been acknowledged. Should be safe to
++ *    traverse the list without locking it since this function will only be
+  *    called from interrupt context (BH)
+  */
+ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
+@@ -943,8 +945,8 @@
+       while (skb != NULL) {
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+-              /* We copy the skb to be retransmitted since we will have to 
+-               * modify it. Cloning will confuse packet sniffers 
++              /* We copy the skb to be retransmitted since we will have to
++               * modify it. Cloning will confuse packet sniffers
+                */
+               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
+               tx_skb = skb_copy(skb, GFP_ATOMIC);
+@@ -959,17 +961,17 @@
+               /* Clear old Nr field + poll bit */
+               tx_skb->data[1] &= 0x0f;
+-              /* 
++              /*
+                *  Set poll bit on the last frame retransmitted
+                */
+               if (count-- == 1)
+                       tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
+               else
+                       tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */
+-              
++
+               irlap_send_i_frame(self, tx_skb, command);
+-              /* 
++              /*
+                *  If our skb is the last buffer in the list, then
+                *  we are finished, if not, move to the next sk-buffer
+                */
+@@ -979,23 +981,23 @@
+                       skb = skb->next;
+       }
+ #if 0 /* Not yet */
+-      /* 
++      /*
+        *  We can now fill the window with additinal data frames
+        */
+       while (skb_queue_len( &self->txq) > 0) {
+-              
++
+               IRDA_DEBUG(0, "%s(), sending additional frames!\n", __FUNCTION__);
+-              if ((skb_queue_len( &self->txq) > 0) && 
++              if ((skb_queue_len( &self->txq) > 0) &&
+                   (self->window > 0)) {
+-                      skb = skb_dequeue( &self->txq); 
++                      skb = skb_dequeue( &self->txq);
+                       ASSERT(skb != NULL, return;);
+                       /*
+-                       *  If send window > 1 then send frame with pf 
++                       *  If send window > 1 then send frame with pf
+                        *  bit cleared
+-                       */ 
+-                      if ((self->window > 1) && 
+-                          skb_queue_len(&self->txq) > 0) 
++                       */
++                      if ((self->window > 1) &&
++                          skb_queue_len(&self->txq) > 0)
+                       {
+                               irlap_send_data_primary(self, skb);
+                       } else {
+@@ -1023,14 +1025,14 @@
+       if (skb != NULL) {
+               irlap_wait_min_turn_around(self, &self->qos_tx);
+-              /* We copy the skb to be retransmitted since we will have to 
+-               * modify it. Cloning will confuse packet sniffers 
++              /* We copy the skb to be retransmitted since we will have to
++               * modify it. Cloning will confuse packet sniffers
+                */
+               /* tx_skb = skb_clone( skb, GFP_ATOMIC); */
+               tx_skb = skb_copy(skb, GFP_ATOMIC);
+               if (!tx_skb) {
+                       IRDA_DEBUG(0, "%s(), unable to copy\n", __FUNCTION__);
+-                      return; 
++                      return;
+               }
+               /* Unlink tx_skb from list */
+               tx_skb->next = tx_skb->prev = NULL;
+@@ -1041,7 +1043,7 @@
+               /*  Set poll/final bit */
+               tx_skb->data[1] |= PF_BIT; /* Set p/f bit */
+-              
++
+               irlap_send_i_frame(self, tx_skb, command);
+       }
+ }
+@@ -1052,15 +1054,15 @@
+  *    Contruct and transmit an Unnumbered Information (UI) frame
+  *
+  */
+-void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, 
++void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
+                        __u8 caddr, int command)
+ {
+       IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+-      
++
+       ASSERT(self != NULL, return;);
+       ASSERT(self->magic == LAP_MAGIC, return;);
+       ASSERT(skb != NULL, return;);
+-      
++
+       /* Insert connection address */
+       skb->data[0] = caddr | ((command) ? CMD_FRAME : 0);
+@@ -1072,13 +1074,13 @@
+  *
+  *    Contruct and transmit Information (I) frame
+  */
+-void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, 
+-                      int command) 
++void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb,
++                      int command)
+ {
+       /* Insert connection address */
+       skb->data[0] = self->caddr;
+       skb->data[0] |= (command) ? CMD_FRAME : 0;
+-      
++
+       /* Insert next to receive (Vr) */
+       skb->data[1] |= (self->vr << 5);  /* insert nr */
+@@ -1091,9 +1093,9 @@
+  *    Receive and parse an I (Information) frame, no harm in making it inline
+  *    since it's called only from one single place (irlap_driver_rcv).
+  */
+-static inline void irlap_recv_i_frame(struct irlap_cb *self, 
+-                                    struct sk_buff *skb, 
+-                                    struct irlap_info *info, int command) 
++static inline void irlap_recv_i_frame(struct irlap_cb *self,
++                                    struct sk_buff *skb,
++                                    struct irlap_info *info, int command)
+ {
+       info->nr = skb->data[1] >> 5;          /* Next to receive */
+       info->pf = skb->data[1] & PF_BIT;      /* Final bit */
+@@ -1112,7 +1114,7 @@
+  *    Receive and parse an Unnumbered Information (UI) frame
+  *
+  */
+-static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, 
++static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
+                               struct irlap_info *info)
+ {
+       IRDA_DEBUG( 4, "%s()\n", __FUNCTION__);
+@@ -1128,19 +1130,19 @@
+  *    Received Frame Reject response.
+  *
+  */
+-static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, 
+-                                struct irlap_info *info) 
++static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
++                                struct irlap_info *info)
+ {
+       __u8 *frame;
+       int w, x, y, z;
+       IRDA_DEBUG(0, "%s()\n", __FUNCTION__);
+-      
++
+       ASSERT(self != NULL, return;);
+       ASSERT(self->magic == LAP_MAGIC, return;);
+       ASSERT(skb != NULL, return;);
+       ASSERT(info != NULL, return;);
+-      
++
+       frame = skb->data;
+       info->nr = frame[2] >> 5;          /* Next to receive */
+@@ -1151,11 +1153,11 @@
+       x = frame[3] & 0x02;
+       y = frame[3] & 0x04;
+       z = frame[3] & 0x08;
+-      
++
+       if (w) {
+               IRDA_DEBUG(0, "Rejected control field is undefined or not "
+                     "implemented.\n");
+-      } 
++      }
+       if (x) {
+               IRDA_DEBUG(0, "Rejected control field was invalid because it "
+                     "contained a non permitted I field.\n");
+@@ -1178,7 +1180,7 @@
+  *    Send a test frame response
+  *
+  */
+-void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, 
++void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
+                          struct sk_buff *cmd)
+ {
+       struct sk_buff *skb;
+@@ -1191,7 +1193,7 @@
+       /* Broadcast frames must include saddr and daddr fields */
+       if (caddr == CBROADCAST) {
+-              frame = (struct test_frame *) 
++              frame = (struct test_frame *)
+                       skb_put(skb, sizeof(struct test_frame));
+               /* Insert the swapped addresses */
+@@ -1218,28 +1220,28 @@
+  *    Receive a test frame
+  *
+  */
+-static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, 
++static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
+                                 struct irlap_info *info, int command)
+ {
+       struct test_frame *frame;
+       IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+-      
++
+       frame = (struct test_frame *) skb->data;
+-              
++
+       /* Broadcast frames must carry saddr and daddr fields */
+       if (info->caddr == CBROADCAST) {
+               if (skb->len < sizeof(struct test_frame)) {
+                       IRDA_DEBUG(0, "%s() test frame to short!\n", __FUNCTION__);
+                       return;
+               }
+-              
++
+               /* Read and swap addresses */
+-              info->daddr = le32_to_cpu(frame->saddr);
+-              info->saddr = le32_to_cpu(frame->daddr);
++              info->daddr = le32_to_cpu(get_unaligned(&frame->saddr));
++              info->saddr = le32_to_cpu(get_unaligned(&frame->daddr));
+               /* Make sure frame is addressed to us */
+-              if ((info->saddr != self->saddr) && 
++              if ((info->saddr != self->saddr) &&
+                   (info->saddr != BROADCAST)) {
+                       return;
+               }
+@@ -1254,18 +1256,18 @@
+ /*
+  * Function irlap_driver_rcv (skb, netdev, ptype)
+  *
+- *    Called when a frame is received. Dispatches the right receive function 
++ *    Called when a frame is received. Dispatches the right receive function
+  *    for processing of the frame.
+  *
+  */
+-int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, 
++int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
+                    struct packet_type *ptype)
+ {
+       struct irlap_info info;
+       struct irlap_cb *self;
+       int command;
+       __u8 control;
+-      
++
+       /* FIXME: should we get our own field? */
+       self = (struct irlap_cb *) dev->atalk_ptr;
+@@ -1281,10 +1283,10 @@
+               dev_kfree_skb(skb);
+               return -1;
+       }
+-      
++
+       command    = skb->data[0] & CMD_FRAME;
+       info.caddr = skb->data[0] & CBROADCAST;
+-      
++
+       info.pf      = skb->data[1] &  PF_BIT;
+       info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */
+@@ -1295,7 +1297,7 @@
+               IRDA_DEBUG(0, "%s(), wrong connection address!\n", __FUNCTION__);
+               goto out;
+       }
+-      /*  
++      /*
+        *  Optimize for the common case and check if the frame is an
+        *  I(nformation) frame. Only I-frames have bit 0 set to 0
+        */
+@@ -1304,11 +1306,11 @@
+               goto out;
+       }
+       /*
+-       *  We now check is the frame is an S(upervisory) frame. Only 
++       *  We now check is the frame is an S(upervisory) frame. Only
+        *  S-frames have bit 0 set to 1 and bit 1 set to 0
+        */
+       if (~control & 0x02) {
+-              /* 
++              /*
+                *  Received S(upervisory) frame, check which frame type it is
+                *  only the first nibble is of interest
+                */
+@@ -1332,8 +1334,8 @@
+               }
+               goto out;
+       }
+-      /* 
+-       *  This must be a C(ontrol) frame 
++      /*
++       *  This must be a C(ontrol) frame
+        */
+       switch (control) {
+       case XID_RSP:
+@@ -1369,6 +1371,6 @@
+               break;
+       }
+ out:
+-      dev_kfree_skb(skb); 
++      dev_kfree_skb(skb);
+       return 0;
+ }
+--- linux-2.4.27/net/irda/irttp.c~2.4.27-vrs1
++++ linux-2.4.27/net/irda/irttp.c
+@@ -1665,8 +1665,8 @@
+               }
+               self->rx_sdu_size = 0;
+       }
+-
+-      /*
++  
++      /*
+        * It's not trivial to keep track of how many credits are available
+        * by incrementing at each packet, because delivery may fail 
+        * (irttp_do_data_indication() may requeue the frame) and because
+@@ -1676,7 +1676,7 @@
+        * to send remote_credit.
+        * No need to spinlock, write is atomic and self correcting...
+        * Jean II
+-       */
++       */
+       self->avail_credit = (self->initial_credit -
+                             (self->remote_credit +
+                              skb_queue_len(&self->rx_queue) +
+--- linux-2.4.27/net/sched/Config.in~2.4.27-vrs1
++++ linux-2.4.27/net/sched/Config.in
+@@ -3,9 +3,9 @@
+ # 
+ tristate '  CBQ packet scheduler' CONFIG_NET_SCH_CBQ
+ tristate '  HTB packet scheduler' CONFIG_NET_SCH_HTB
+-tristate '  CSZ packet scheduler' CONFIG_NET_SCH_CSZ
++dep_tristate '  CSZ packet scheduler (experimental)' CONFIG_NET_SCH_CSZ $CONFIG_EXPERIMENTAL
+ #tristate '  H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ
+-tristate '  H-FSC packet scheduler' CONFIG_NET_SCH_HFSC
++tristate '  H-FSC packet scheduler' CONFIG_NET_SCH_HFCS
+ if [ "$CONFIG_ATM" = "y" -o "$CONFIG_ATM" = "m" ]; then
+    dep_tristate '  ATM pseudo-scheduler' CONFIG_NET_SCH_ATM $CONFIG_ATM
+ fi
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/defconfig-simpad b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/defconfig-simpad
new file mode 100644 (file)
index 0000000..88d334a
--- /dev/null
@@ -0,0 +1,1306 @@
+#
+# Automatically generated make config: 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 is not set
+# 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=y
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_AT91RM9200 is not set
+
+#
+# Archimedes/A5000 Implementations
+#
+
+#
+# Archimedes/A5000 Implementations (select only ONE)
+#
+# 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=y
+# CONFIG_SA1100_SIMPAD_SINUSPAD 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=m
+CONFIG_SA1100_USB_HOTPLUG=y
+CONFIG_SA1100_USB_NETLINK=m
+CONFIG_SA1100_USB_CHAR=m
+# CONFIG_SA1100_SSP is not set
+
+#
+# AT91RM9200 Implementations
+#
+# CONFIG_ARCH_AT91RM9200DK is not set
+# CONFIG_MACH_CSB337 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_TRIZEPS2 is not set
+# CONFIG_PXA_USB is not set
+# CONFIG_PXA_USB_NETLINK is not set
+# CONFIG_PXA_USB_CHAR is not set
+
+#
+# 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
+
+#
+# Processor Type
+#
+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=y
+# CONFIG_CPU_32v3 is not set
+CONFIG_CPU_32v4=y
+
+#
+# Processor Features
+#
+CONFIG_DISCONTIGMEM=y
+
+#
+# General setup
+#
+# CONFIG_PCI is not set
+CONFIG_ISA=y
+# CONFIG_ISA_DMA is not set
+# CONFIG_ZBOOT_ROM is not set
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+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=y
+# CONFIG_PCMCIA_PXA is not set
+
+#
+# MMC device drivers
+#
+# CONFIG_MMC is not set
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+
+#
+# At least one math emulation must be selected
+#
+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_SIMPAD_PM=y
+# CONFIG_ARTHUR is not set
+CONFIG_CMDLINE="mtdparts=sa1100:512k(boot),1m(kernel),14848k(root),-(home) console=ttySA root=1f02 noinitrd jffs2_orphaned_inodes=delete rootfstype=jffs2 mem=32M"
+CONFIG_LEDS=y
+# CONFIG_LEDS_TIMER is not set
+# CONFIG_LEDS_CPU is not set
+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=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+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_CFI_B1 is not set
+CONFIG_MTD_CFI_B2=y
+# CONFIG_MTD_CFI_B4 is not set
+# CONFIG_MTD_CFI_B8 is not set
+CONFIG_MTD_CFI_I1=y
+# CONFIG_MTD_CFI_I2 is not set
+# 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_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_PHYSMAP is not set
+# CONFIG_MTD_NORA is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_CDB89712 is not set
+CONFIG_MTD_SA1100=y
+# CONFIG_MTD_DC21285 is not set
+# CONFIG_MTD_IQ80310 is not set
+# CONFIG_MTD_FORTUNET is not set
+# CONFIG_MTD_EPXA is not set
+# CONFIG_MTD_AUTCPU12 is not set
+# CONFIG_MTD_EDB7312 is not set
+# CONFIG_MTD_IMPA7 is not set
+# CONFIG_MTD_CEIVA 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=y
+CONFIG_MTDRAM_TOTAL_SIZE=32768
+CONFIG_MTDRAM_ERASE_SIZE=1
+CONFIG_MTDRAM_ABS_POS=C2000000
+# CONFIG_MTD_BLKMTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC1000 is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOCPROBE is not set
+
+#
+# NAND Flash Device Drivers
+#
+# CONFIG_MTD_NAND 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_CISS_MONITOR_THREAD 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 is not set
+
+#
+# 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=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_NET_IPGRE_BROADCAST is not set
+# CONFIG_IP_MROUTE is not set
+CONFIG_ARPD=y
+CONFIG_INET_ECN=y
+CONFIG_SYN_COOKIES=y
+
+#
+#   IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_UNCLEAN=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_MIRROR=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_NAT_LOCAL=y
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_NAT_TFTP=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+#   IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+
+#
+# IPVS scheduler
+#
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+
+#
+# IPVS application helper
+#
+CONFIG_IP_VS_FTP=m
+CONFIG_IPV6=m
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_MOBILITY=m
+CONFIG_IPV6_MOBILITY_MN=m
+CONFIG_IPV6_MOBILITY_HA=m
+CONFIG_IPV6_MOBILITY_DEBUG=y
+
+#
+#   IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+# CONFIG_KHTTPD is not set
+
+#
+#    SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IPV6_SCTP__=m
+# CONFIG_IP_SCTP 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 is not set
+# 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
+CONFIG_IPSEC=y
+
+#
+# IPSec options (Openswan)
+#
+CONFIG_IPSEC_IPIP=y
+CONFIG_IPSEC_AH=y
+CONFIG_IPSEC_AUTH_HMAC_MD5=y
+CONFIG_IPSEC_AUTH_HMAC_SHA1=y
+CONFIG_IPSEC_ESP=y
+CONFIG_IPSEC_ENC_3DES=y
+CONFIG_IPSEC_ENC_AES=y
+CONFIG_IPSEC_ALG=y
+CONFIG_IPSEC_ALG_AES=y
+# CONFIG_IPSEC_ALG_CRYPTOAPI is not set
+CONFIG_IPSEC_IPCOMP=y
+CONFIG_IPSEC_DEBUG=y
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_DUMMY=m
+# 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 is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_TULIP is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_EEPRO100_PIO is not set
+# CONFIG_E100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_NE3210 is not set
+# CONFIG_ES3210 is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_SUNDANCE_MMIO is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
+# CONFIG_WINBOND_840 is not set
+CONFIG_NET_POCKET=y
+# CONFIG_DE600 is not set
+# CONFIG_DE620 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=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=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_AIRO is not set
+# CONFIG_HERMES is not set
+
+#
+# Wireless Pcmcia cards support
+#
+# CONFIG_PCMCIA_HERMES is not set
+# 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=m
+CONFIG_PCMCIA_3C574=m
+CONFIG_PCMCIA_FMVJ18X=m
+CONFIG_PCMCIA_PCNET=m
+CONFIG_PCMCIA_AXNET=m
+CONFIG_PCMCIA_NMCLAN=m
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+# CONFIG_ARCNET_COM20020_CS is not set
+# CONFIG_PCMCIA_IBMTR is not set
+CONFIG_NET_PCMCIA_RADIO=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_NETWAVE is not set
+# CONFIG_PCMCIA_WAVELAN is not set
+# CONFIG_AIRONET4500_CS is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+CONFIG_IRDA_ULTRA=y
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+CONFIG_IRPORT_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# 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_VIA_IRCC_FIR is not set
+CONFIG_SA1100_FIR=m
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI 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_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_UINPUT=y
+# CONFIG_INPUT_MX1TS is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=m
+# 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=y
+CONFIG_SERIAL_SA1100_CONSOLE=y
+CONFIG_SA1100_DEFAULT_BAUDRATE=115200
+# 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_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# L3 serial bus support
+#
+# CONFIG_L3 is not set
+# CONFIG_L3_ALGOBIT is not set
+# CONFIG_L3_BIT_SA1100_GPIO is not set
+
+#
+# Other L3 adapters
+#
+# 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
+
+#
+# Joysticks
+#
+# 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 is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+CONFIG_SA1100_RTC=y
+# 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
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+CONFIG_PCMCIA_SERIAL_CS=m
+# CONFIG_SYNCLINK_CS is not set
+CONFIG_TDA8007=m
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_REISERFS_FS=m
+# 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_HFSPLUS_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=m
+CONFIG_JBD=m
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=m
+CONFIG_UMSDOS_FS=m
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+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=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# 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
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_TRACE is not set
+# CONFIG_XFS_DEBUG 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=y
+# CONFIG_NFS_DIRECTIO 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_LOCKD_V4=y
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_SMB_UNIX=y
+# 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=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=y
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=y
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# 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 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_MQ200=y
+# 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=y
+CONFIG_FBCON_CFB8=y
+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=y
+CONFIG_FONT_8x8=y
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# 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_SA1100=y
+# CONFIG_SOUND_UDA1341 is not set
+# CONFIG_SOUND_ASSABET_UDA1341 is not set
+# CONFIG_SOUND_H3600_UDA1341 is not set
+# CONFIG_SOUND_PANGOLIN_UDA1341 is not set
+# CONFIG_SOUND_SA1111_UDA1341 is not set
+# CONFIG_SOUND_SA1111_AC97 is not set
+# CONFIG_SOUND_SA1100SSP 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 is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+# CONFIG_SOUND_WM97XX is not set
+
+#
+# Multimedia Capabilities Port drivers
+#
+CONFIG_MCP=y
+CONFIG_MCP_SA1100=y
+CONFIG_MCP_UCB1200=y
+CONFIG_MCP_UCB1200_AUDIO=y
+CONFIG_MCP_UCB1200_TS=y
+# CONFIG_MCP_UCB1400_TS is not set
+
+#
+# Console Switches
+#
+CONFIG_SWITCHES=y
+CONFIG_SWITCHES_SA1100=y
+CONFIG_SWITCHES_UCB1X00=y
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Support for USB gadgets
+#
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_N9604 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_SUPERH is not set
+# CONFIG_USB_GADGET_GOKU is not set
+CONFIG_USB_GADGET_SA1100=y
+# CONFIG_USB_GADGET_CONTROLLER is not set
+# CONFIG_USB_PXA2XX is not set
+# CONFIG_USB_GADGET_CONTROLLER is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+
+#
+# USB Gadget Drivers
+#
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+# CONFIG_USB_ETH_RNDIS is not set
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+
+#
+# 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 is not set
+CONFIG_BLUEZ_HCIUART=m
+CONFIG_BLUEZ_HCIUART_H4=y
+CONFIG_BLUEZ_HCIUART_BCSP=y
+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC=y
+# CONFIG_BLUEZ_HCIBFUSB is not set
+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_CRC32 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_FW_LOADER is not set
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-apm.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-apm.patch
new file mode 100644 (file)
index 0000000..900124e
--- /dev/null
@@ -0,0 +1,803 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/pm-common.c
+@@ -0,0 +1,268 @@
++/*
++ * 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>
++
++/*
++ * Debug macros
++ */
++#undef DEBUG
++
++\f
++
++static char pm_helper_path[128] = "/sbin/pm_helper";
++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");
++      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
++
++static struct ctl_table pm_table[] =
++{
++/*    {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_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.27/arch/arm/mach-sa1100/apm.c~simpad-apm
++++ linux-2.4.27/arch/arm/mach-sa1100/apm.c
+@@ -32,9 +32,7 @@
+ #include <asm/system.h>
+ #include <asm/hardware.h>
+-#if FIXME
+ #include <asm/arch-sa1100/pm.h>
+-#endif
+ #ifdef CONFIG_IPAQ_HANDHELD
+ #include <asm/arch-sa1100/h3600_hal.h>
+@@ -92,6 +90,8 @@
+       int             magic;
+       struct apm_user *       next;
+       int             suser: 1;
++      int             writer: 1;
++      int             reader: 1;
+       int             suspend_wait: 1;
+       int             suspend_result;
+       int             suspends_pending;
+@@ -111,7 +111,7 @@
+ /*
+  * Local variables
+  */
+-//static int                  suspends_pending;
++static int                    suspends_pending;
+ //static int                  standbys_pending;
+ //static int                  ignore_normal_resume;
+@@ -129,8 +129,6 @@
+ #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);
+@@ -190,6 +188,42 @@
+       return as->events[as->event_tail];
+ }
++static void queue_event(apm_event_t event, struct apm_user *sender)
++{
++      struct apm_user *       as;
++
++      if (user_list == NULL)
++              return;
++      for (as = user_list; as != NULL; as = as->next) {
++              if ((as == sender) || (!as->reader))
++                      continue;
++              as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
++              if (as->event_head == as->event_tail) {
++                      static int notified;
++
++                      if (notified++ == 0)
++                          printk(KERN_ERR "apm: an event queue overflowed\n");
++                      as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
++              }
++              as->events[as->event_head] = event;
++              if ((!as->suser) || (!as->writer))
++                      continue;
++              switch (event) {
++              case APM_SYS_SUSPEND:
++              case APM_USER_SUSPEND:
++                      as->suspends_pending++;
++                      suspends_pending++;
++                      break;
++
++              case APM_SYS_STANDBY:
++              case APM_USER_STANDBY:
++                      as->standbys_pending++;
++                      break;
++              }
++      }
++      wake_up_interruptible(&apm_waitqueue);
++}
++
+ static int check_apm_user(struct apm_user *as, const char *func)
+ {
+       if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+@@ -228,7 +262,6 @@
+       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;
+@@ -280,9 +313,17 @@
+               return -EPERM;
+       switch (cmd) {
+         case APM_IOC_SUSPEND:
+-#if FIXME
+-              pm_suggest_suspend();
+-#endif
++              if (as->suspends_read > 0) {
++                      as->suspends_read--;
++                      as->suspends_pending--;
++                      suspends_pending--;
++              } else {
++                      queue_event(APM_USER_SUSPEND, as);
++              }
++
++              if (suspends_pending <= 0)
++                      wake_up(&apm_suspend_waitqueue);
++              
+               break;
+       default:
+               return -EINVAL;
+@@ -299,6 +340,20 @@
+               return 0;
+       filp->private_data = NULL;
+       lock_kernel();
++      if (user_list == as)
++              user_list = as->next;
++      else {
++              struct apm_user *       as1;
++
++              for (as1 = user_list;
++                   (as1 != NULL) && (as1->next != as);
++                   as1 = as1->next)
++                      ;
++              if (as1 == NULL)
++                      printk(KERN_ERR "apm: filp not in user list\n");
++              else
++                      as1->next = as->next;
++      }
+       unlock_kernel();
+       kfree(as);
+       return 0;
+@@ -326,6 +381,8 @@
+        * privileged operation -- cevans
+        */
+       as->suser = capable(CAP_SYS_ADMIN);
++      as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
++      as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
+       as->next = user_list;
+       user_list = as;
+       filp->private_data = as;
+@@ -409,34 +466,6 @@
+       return p - buf;
+ }
+-#ifndef MODULE
+-static int __init apm_setup(char *str)
+-{
+-      int     invert;
+-
+-      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, "debug", 5) == 0)
+-                      debug = !invert;
+-              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,
+@@ -454,6 +483,48 @@
+ #define APM_INIT_ERROR_RETURN return -1
++static pid_t apmd_pid;
++static DECLARE_COMPLETION(apmd_exited);
++
++static int apm(void *unused)
++{
++      unsigned short  bx;
++      unsigned short  cx;
++      unsigned short  dx;
++      int             error;
++      char *          power_stat;
++      char *          bat_stat;
++      DECLARE_WAITQUEUE(wait, current);
++      struct apm_user au, *as;
++
++      lock_kernel();
++
++      daemonize();
++
++      strcpy(current->comm, "kapmd");
++
++      as = &au;
++      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;
++      as->suser = 1;
++      as->writer = 1;
++      as->reader = 0;
++
++      while (!signal_pending (current)) {
++              interruptible_sleep_on(&apm_suspend_waitqueue);
++
++              pm_suggest_suspend();
++
++              queue_event(APM_NORMAL_RESUME, as);
++      }
++
++      unlock_kernel();
++
++      complete_and_exit(&apmd_exited, 0);
++}
++
+ /*
+  * 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
+@@ -492,6 +563,8 @@
+       misc_register(&apm_device);
++      apmd_pid = kernel_thread(apm, NULL, 0);
++
+       return 0;
+ }
+@@ -499,11 +572,10 @@
+ {
+       misc_deregister(&apm_device);
+       remove_proc_entry("apm", NULL);
++      kill_proc (apmd_pid, SIGTERM, 1);
++      wait_for_completion(&apmd_exited);
+       if (power_off)
+               pm_power_off = NULL;
+-      exit_kapmd = 1;
+-      while (kapmd_running)
+-              schedule();
+       pm_active = 0;
+ }
+@@ -512,6 +584,7 @@
+ MODULE_AUTHOR("Jamey Hicks, pulling bits from original by Stephen Rothwell");
+ MODULE_DESCRIPTION("A minimal emulation of APM");
++MODULE_LICENSE("GPL");
+ MODULE_PARM(debug, "i");
+ MODULE_PARM_DESC(debug, "Enable debug mode");
+ MODULE_PARM(power_off, "i");
+--- /dev/null
++++ linux-2.4.27/include/asm-arm/arch-sa1100/pm.h
+@@ -0,0 +1,20 @@
++/*
++ *
++ * Declarations for ARM Linux Power Management
++ *
++ * Copyright 2002 Compaq Computer Corporation.
++ *
++ * 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.
++ *
++ * Author: Jamey Hicks.
++ *
++ */
++
++
++extern int (*pm_suggest_suspend_hook)(int state);
++extern int (*pm_sysctl_suspend_hook)(int state);
++extern int pm_use_sbin_pm_helper; 
++extern int pm_suspend(void);
++extern int pm_suggest_suspend(void); /* triggers /sbin/pm_helper or queueing event to apmd */
+--- linux-2.4.27/arch/arm/mach-sa1100/Makefile~simpad-apm
++++ linux-2.4.27/arch/arm/mach-sa1100/Makefile
+@@ -19,7 +19,7 @@
+               flexanet.o freebird.o frodo.o generic.o h3600.o \
+               huw_webpanel.o irq.o sa1111.o sa1111-pcibuf.o \
+               system3.o yopy.o usb_ctl.o usb_recv.o usb_send.o simputer.o ssp.o \
+-              simpad.o
++              simpad.o pm-sa1100.o
+ # These aren't present yet, and prevents a plain -ac kernel building.
+ # hwtimer.o
+@@ -105,7 +105,7 @@
+ obj-$(CONFIG_SA1100_USB_CHAR) += usb-char.o
+ # Miscelaneous functions
+-obj-$(CONFIG_PM) += pm.o sleep.o
++obj-$(CONFIG_PM) += pm-sa1100.o sleep.o
+ obj-$(CONFIG_APM) += apm.o
+ # SIMpad specific
+--- /dev/null
++++ linux-2.4.27/arch/arm/mach-sa1100/pm-sa1100.c
+@@ -0,0 +1,225 @@
++/*
++ * 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>
++
++
++#ifdef CONFIG_IPAQ_HANDHELD
++#include <asm/arch/h3600_asic.h>
++#endif
++
++#define __KERNEL_SYSCALLS__
++#include <linux/unistd.h>
++
++extern void sa1100_cpu_suspend(void);
++extern void sa1100_cpu_resume(void);
++extern int debug_pm;
++
++#define SAVE(x)               sleep_save[SLEEP_SAVE_##x] = x
++#define RESTORE(x)    x = sleep_save[SLEEP_SAVE_##x]
++
++/*
++ * List of global SA11x0 peripheral registers to preserve.
++ * More ones like CP and general purpose register values are preserved
++ * with the stack location in sleep.S.
++ */
++enum {        SLEEP_SAVE_START = 0,
++
++      SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER,
++      SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3,
++
++      SLEEP_SAVE_GPDR, SLEEP_SAVE_GRER, SLEEP_SAVE_GFER, SLEEP_SAVE_GAFR,
++      SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
++
++      SLEEP_SAVE_ICMR,
++#ifdef CONFIG_SA1100_SIMPAD
++      SLEEP_SAVE_MECR, /* needed by SIMpad to get PCMCIA working after resume */
++#endif
++      SLEEP_SAVE_Ser1SDCR0,
++
++        SLEEP_SAVE_PWER,
++        SLEEP_SAVE_MSC1, SLEEP_SAVE_MSC2,
++
++      SLEEP_SAVE_SIZE
++};
++
++
++int pm_do_suspend(void)
++{
++      unsigned long sleep_save[SLEEP_SAVE_SIZE];
++
++      cli();
++
++      leds_event(led_stop);
++
++      /* preserve current time */
++      RCNR = xtime.tv_sec;
++
++      /* save vital registers */
++      SAVE(OSCR);
++      SAVE(OSMR0);
++      SAVE(OSMR1);
++      SAVE(OSMR2);
++      SAVE(OSMR3);
++      SAVE(OIER);
++
++      SAVE(GPDR);
++      SAVE(GRER);
++      SAVE(GFER);
++      SAVE(GAFR);
++
++      SAVE(PPDR);
++      SAVE(PPSR);
++      SAVE(PPAR);
++      SAVE(PSDR);
++
++      SAVE(Ser1SDCR0);
++
++      SAVE(ICMR);
++#ifdef CONFIG_SA1100_SIMPAD
++      SAVE(MECR);
++#endif
++        SAVE(PWER);
++        SAVE(MSC1);
++        SAVE(MSC2);
++
++      /* ... maybe a global variable initialized by arch code to set this? */
++      GRER &= PWER;
++      GFER &= PWER;
++      // Ugly, but I need the AC inserted event
++      // In the future, we're going to care about DCD and USB interrupts as well
++      if ( machine_is_h3800()) {
++#ifdef CONFIG_IPAQ_HANDHELD
++              GFER = GPIO_H3800_AC_IN;
++#endif
++      } else {
++              GFER = 0;
++              if (machine_is_jornada56x()) {
++                      /* jca */
++                      GFER = PWER;
++                      ICMR |= PWER;
++              }
++      }
++      GEDR = GEDR;
++
++      /* Clear previous reset status */
++      RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
++
++      /* set resume return address */
++      PSPR = virt_to_phys(sa1100_cpu_resume);
++
++      /* go zzz */
++      sa1100_cpu_suspend();
++
++      /* ensure not to come back here if it wasn't intended */
++      PSPR = 0;
++
++      if (debug_pm)
++              printk(KERN_CRIT "*** made it back from resume\n");
++
++#ifdef CONFIG_IPAQ_HANDHELD
++      if ( machine_is_ipaq()) {
++              ipaq_model_ops.gedr = GEDR;
++              ipaq_model_ops.icpr = ICPR;
++      }
++#endif
++
++      /* restore registers */
++      RESTORE(GPDR);
++      RESTORE(GRER);
++      RESTORE(GFER);
++      RESTORE(GAFR);
++
++      /* clear any edge detect bit */
++      GEDR = GEDR;
++
++      RESTORE(PPDR);
++      RESTORE(PPSR);
++      RESTORE(PPAR);
++      RESTORE(PSDR);
++
++      RESTORE(Ser1SDCR0);
++
++      PSSR = PSSR_PH;
++
++      RESTORE(OSMR0);
++      RESTORE(OSMR1);
++      RESTORE(OSMR2);
++      RESTORE(OSMR3);
++      RESTORE(OSCR);
++      RESTORE(OIER);
++
++#ifdef CONFIG_IPAQ_HANDHELD
++/* OSMR0 may have fired before we went to sleep, but after interrupts
++   were shut off.  Set OSMR0 to something plausible */
++      OSMR0 = OSCR + LATCH;
++#endif
++      ICLR = 0;
++      ICCR = 1;
++      RESTORE(ICMR);
++#ifdef CONFIG_SA1100_SIMPAD
++      RESTORE(MECR);
++#endif
++      RESTORE(PWER);
++      RESTORE(MSC1);
++      RESTORE(MSC2);
++      /* restore current time */
++      xtime.tv_sec = RCNR;
++
++      leds_event(led_start);
++      
++      sti();
++
++      if (debug_pm)
++              printk("interrupts are enabled\n");
++
++      /*
++       * Restore the CPU frequency settings.
++       */
++#ifdef CONFIG_CPU_FREQ
++      cpufreq_restore();
++#endif
++      return 0;
++}
++
++unsigned long sleep_phys_sp(void *sp)
++{
++      return virt_to_phys(sp);
++}
++
++#include "pm-common.c"
diff --git a/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-pm-updates.patch b/packages/linux/opensimpad-2.4.27-vrs1-pxa1-jpm1/simpad-pm-updates.patch
new file mode 100644 (file)
index 0000000..020a625
--- /dev/null
@@ -0,0 +1,35 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/arch/arm/mach-sa1100/pm-sa1100.c~simpad-pm-updates
++++ linux-2.4.27/arch/arm/mach-sa1100/pm-sa1100.c
+@@ -69,6 +69,7 @@
+       SLEEP_SAVE_ICMR,
+ #ifdef CONFIG_SA1100_SIMPAD
+       SLEEP_SAVE_MECR, /* needed by SIMpad to get PCMCIA working after resume */
++      SLEEP_SAVE_Ser4MCCR0, SLEEP_SAVE_Ser4MCSR, SLEEP_SAVE_Ser4MCCR1, /* touchscreen */
+ #endif
+       SLEEP_SAVE_Ser1SDCR0,
+@@ -113,6 +114,9 @@
+       SAVE(ICMR);
+ #ifdef CONFIG_SA1100_SIMPAD
+       SAVE(MECR);
++      SAVE(Ser4MCCR0);
++      SAVE(Ser4MCSR);
++      SAVE(Ser4MCCR1);
+ #endif
+         SAVE(PWER);
+         SAVE(MSC1);
+@@ -194,6 +198,9 @@
+       RESTORE(ICMR);
+ #ifdef CONFIG_SA1100_SIMPAD
+       RESTORE(MECR);
++      RESTORE(Ser4MCCR0);
++      RESTORE(Ser4MCSR);
++      RESTORE(Ser4MCCR1);
+ #endif
+       RESTORE(PWER);
+       RESTORE(MSC1);
diff --git a/packages/linux/opensimpad-64+0_2.4.27-vrs1-pxa1-jpm1.bb b/packages/linux/opensimpad-64+0_2.4.27-vrs1-pxa1-jpm1.bb
new file mode 100644 (file)
index 0000000..fb12c70
--- /dev/null
@@ -0,0 +1,5 @@
+SECTION = "kernel"
+include opensimpad_${PV}.bb
+
+SIMPAD_MEM = "64"
+SIMPAD_RD = "0"
diff --git a/packages/linux/opensimpad/.mtn2git_empty b/packages/linux/opensimpad/.mtn2git_empty
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/packages/linux/opensimpad/disable-pcmcia-probe.patch b/packages/linux/opensimpad/disable-pcmcia-probe.patch
new file mode 100644 (file)
index 0000000..0ab9439
--- /dev/null
@@ -0,0 +1,17 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/pcmcia/Config.in~disable-pcmcia-probe
++++ linux-2.4.27/drivers/pcmcia/Config.in
+@@ -15,9 +15,6 @@
+ tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA
+ if [ "$CONFIG_PCMCIA" != "n" ]; then
+    # yes, I really mean the following...
+-   if [ "$CONFIG_ISA" = "y" -o "$CONFIG_ARCH_SA1100" = "y" ]; then
+-      define_bool CONFIG_PCMCIA_PROBE y
+-   fi
+    if [ "$CONFIG_PCI" != "n" ]; then
+       bool '  CardBus support' CONFIG_CARDBUS
+    fi
diff --git a/packages/linux/opensimpad/mkdep.patch b/packages/linux/opensimpad/mkdep.patch
new file mode 100644 (file)
index 0000000..6dc5140
--- /dev/null
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/Makefile~mkdep
++++ linux-2.4.27/Makefile
+@@ -501,7 +501,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
diff --git a/packages/linux/opensimpad/mppe-20040216.patch b/packages/linux/opensimpad/mppe-20040216.patch
new file mode 100644 (file)
index 0000000..c4195c1
--- /dev/null
@@ -0,0 +1,1225 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/net/Config.in~mppe-20040216
++++ linux-2.4.27/drivers/net/Config.in
+@@ -332,6 +332,7 @@
+    dep_tristate '  PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP
+    dep_tristate '  PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
+    dep_tristate '  PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP
++   dep_tristate '  PPP MPPE compression (encryption)' CONFIG_PPP_MPPE $CONFIG_PPP
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+       dep_tristate '  PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP
+    fi
+--- /dev/null
++++ linux-2.4.27/drivers/net/arcfour.c
+@@ -0,0 +1,75 @@
++/*
++ * arcfour.c
++ * by Frank Cusack <frank@google.com>
++ * 100% public domain
++ *
++ * Implemented from the description in _Applied Cryptography_, 2nd ed.
++ *
++ * ** Distribution ** of this software is unlimited and unrestricted.
++ *
++ * ** Use ** of this software is almost certainly legal; however, refer
++ * to <http://theory.lcs.mit.edu/~rivest/faq.html>.
++ */
++
++#include "arcfour.h"
++#if defined(__linux__)
++#include <linux/string.h>
++#endif
++
++#define swap(a, b)            \
++{                             \
++    unsigned char t = b;      \
++    b = a;                    \
++    a = t;                    \
++}
++
++/*
++ * Initialize arcfour from a key.
++ */
++void
++arcfour_setkey(arcfour_context *context, const unsigned char *key,
++             unsigned keylen)
++{
++    unsigned i, j;
++    unsigned char K[256];
++
++    context->i = context->j = 0;
++
++    for (i = 0; i < 256; i++) {
++      context->S[i] = i;
++      K[i] = key[i % keylen];
++    }
++
++    j = 0;
++    for (i = 0; i < 256; i++) {
++      j = (j + context->S[i] + K[i]) % 256;
++      swap(context->S[i], context->S[j]);
++    }
++
++    memset(K, 0, sizeof(K));
++}
++
++/*
++ * plaintext -> ciphertext (or vice versa)
++ */
++void
++arcfour_encrypt(arcfour_context *context, const unsigned char *in, unsigned len,
++              unsigned char *out)
++{
++    unsigned i = context->i;
++    unsigned j = context->j;
++    unsigned char *S = context->S;
++    unsigned char K;
++
++    while (len--) {
++      i = (i + 1) % 256;
++      j = (j + S[i]) % 256;
++      swap(S[i], S[j]);
++      K = S[(S[i] + S[j]) % 256];
++      *out++ = *in++ ^ K;
++    }
++
++    context->i = i;
++    context->j = j;
++}
++
+--- /dev/null
++++ linux-2.4.27/drivers/net/arcfour.h
+@@ -0,0 +1,17 @@
++/* arcfour.h */
++
++#ifndef _ARCFOUR_H
++#define _ARCFOUR_H
++
++typedef struct {
++    unsigned i;
++    unsigned j;
++    unsigned char S[256];
++} arcfour_context;
++
++extern void arcfour_setkey(arcfour_context *, const unsigned char *, unsigned);
++extern void arcfour_encrypt(arcfour_context *, const unsigned char *, unsigned,
++                          unsigned char *);
++#define arcfour_decrypt arcfour_encrypt
++
++#endif /* _ARCFOUR_H */
+--- linux-2.4.27/drivers/net/ppp_generic.c~mppe-20040216
++++ linux-2.4.27/drivers/net/ppp_generic.c
+@@ -102,6 +102,7 @@
+       spinlock_t      rlock;          /* lock for receive side 58 */
+       spinlock_t      wlock;          /* lock for transmit side 5c */
+       int             mru;            /* max receive unit 60 */
++      int             mru_alloc;      /* MAX(1500,MRU) for dev_alloc_skb() */
+       unsigned int    flags;          /* control bits 64 */
+       unsigned int    xstate;         /* transmit state bits 68 */
+       unsigned int    rstate;         /* receive state bits 6c */
+@@ -129,6 +130,7 @@
+       struct sock_fprog pass_filter;  /* filter for packets to pass */
+       struct sock_fprog active_filter;/* filter for pkts to reset idle */
+ #endif /* CONFIG_PPP_FILTER */
++      int             xpad;           /* ECP or CCP (MPPE) transmit padding */
+ };
+ /*
+@@ -552,7 +554,9 @@
+       case PPPIOCSMRU:
+               if (get_user(val, (int *) arg))
+                       break;
+-              ppp->mru = val;
++              ppp->mru_alloc = ppp->mru = val;
++              if (ppp->mru_alloc < PPP_MRU)
++                  ppp->mru_alloc = PPP_MRU;   /* increase for broken peers */
+               err = 0;
+               break;
+@@ -1031,8 +1035,8 @@
+       /* try to do packet compression */
+       if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state != 0
+           && proto != PPP_LCP && proto != PPP_CCP) {
+-              new_skb = alloc_skb(ppp->dev->mtu + ppp->dev->hard_header_len,
+-                                  GFP_ATOMIC);
++              new_skb = alloc_skb(ppp->dev->mtu + ppp->dev->hard_header_len
++                                  + ppp->xpad, GFP_ATOMIC);
+               if (new_skb == 0) {
+                       printk(KERN_ERR "PPP: no memory (comp pkt)\n");
+                       goto drop;
+@@ -1044,15 +1048,28 @@
+               /* compressor still expects A/C bytes in hdr */
+               len = ppp->xcomp->compress(ppp->xc_state, skb->data - 2,
+                                          new_skb->data, skb->len + 2,
+-                                         ppp->dev->mtu + PPP_HDRLEN);
++                                         ppp->dev->mtu + ppp->xpad
++                                         + PPP_HDRLEN);
+               if (len > 0 && (ppp->flags & SC_CCP_UP)) {
+                       kfree_skb(skb);
+                       skb = new_skb;
+                       skb_put(skb, len);
+                       skb_pull(skb, 2);       /* pull off A/C bytes */
+-              } else {
++              } else if (len == 0) {
+                       /* didn't compress, or CCP not up yet */
+                       kfree_skb(new_skb);
++              } else {
++                      /*
++                       * (len < 0)
++                       * MPPE requires that we do not send unencrypted
++                       * frames.  The compressor will return -1 if we
++                       * should drop the frame.  We cannot simply test
++                       * the compress_proto because MPPE and MPPC share
++                       * the same number.
++                       */
++                      printk(KERN_ERR "ppp: compressor dropped pkt\n");
++                      kfree_skb(new_skb);
++                      goto drop;
+               }
+       }
+@@ -1540,14 +1557,15 @@
+       int len;
+       if (proto == PPP_COMP) {
+-              ns = dev_alloc_skb(ppp->mru + PPP_HDRLEN);
++              ns = dev_alloc_skb(ppp->mru_alloc + PPP_HDRLEN);
+               if (ns == 0) {
+                       printk(KERN_ERR "ppp_decompress_frame: no memory\n");
+                       goto err;
+               }
+               /* the decompressor still expects the A/C bytes in the hdr */
+               len = ppp->rcomp->decompress(ppp->rc_state, skb->data - 2,
+-                              skb->len + 2, ns->data, ppp->mru + PPP_HDRLEN);
++                              skb->len + 2, ns->data,
++                              ppp->mru_alloc + PPP_HDRLEN);
+               if (len < 0) {
+                       /* Pass the compressed frame to pppd as an
+                          error indication. */
+@@ -1982,6 +2000,20 @@
+                               ocomp->comp_free(ostate);
+                       err = 0;
+               }
++              if (ccp_option[0] == CI_MPPE)
++                      /*
++                       * pppd (userland) has reduced the MTU by MPPE_PAD,
++                       * to accomodate "compressor" growth.  We must
++                       * increase the space allocated for compressor
++                       * output in ppp_send_frame() accordingly.  Note
++                       * that from a purist's view, it may be more correct
++                       * to require multilink and fragment large packets,
++                       * but that seems inefficient compared to this
++                       * little trick.
++                       */
++                      ppp->xpad = MPPE_PAD;
++              else
++                      ppp->xpad = 0;
+       } else {
+               state = cp->decomp_alloc(ccp_option, data.length);
+@@ -2253,6 +2285,7 @@
+       /* Initialize the new ppp unit */
+       ppp->file.index = unit;
+       ppp->mru = PPP_MRU;
++      ppp->mru_alloc = PPP_MRU;
+       init_ppp_file(&ppp->file, INTERFACE);
+       ppp->file.hdrlen = PPP_HDRLEN - 2;      /* don't count proto bytes */
+       for (i = 0; i < NUM_NP; ++i)
+--- /dev/null
++++ linux-2.4.27/drivers/net/ppp_mppe_compress.c
+@@ -0,0 +1,643 @@
++/*
++ *  ==FILEVERSION 20020521==
++ *
++ * ppp_mppe_compress.c - interface MPPE to the PPP code.
++ * This version is for use with Linux kernel 2.2.19+ and 2.4.x.
++ *
++ * By Frank Cusack <frank@google.com>.
++ * Copyright (c) 2002 Google, Inc.
++ * All rights reserved.
++ *
++ * Permission to use, copy, modify, and distribute this software and its
++ * documentation is hereby granted, provided that the above copyright
++ * notice appears in all copies.  This software is provided without any
++ * warranty, express or implied.
++ *
++ * Changelog:
++ *      2/15/04 - TS: added #include <version.h> and testing for Kernel
++ *                    version before using 
++ *                    MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
++ *                    depreciated in 2.6
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/version.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/string.h>
++
++#include <linux/ppp_defs.h>
++#include <linux/ppp-comp.h>
++
++#include "arcfour.h"
++#include "sha1.h"
++
++/*
++ * State for an MPPE (de)compressor.
++ */
++typedef struct ppp_mppe_state {
++    unsigned char     master_key[MPPE_MAX_KEY_LEN];
++    unsigned char     session_key[MPPE_MAX_KEY_LEN];
++    arcfour_context   arcfour_context; /* encryption state */
++    unsigned          keylen;         /* key length in bytes             */
++                                      /* NB: 128-bit == 16, 40-bit == 8! */
++                                      /* If we want to support 56-bit,   */
++                                      /* the unit has to change to bits  */
++    unsigned char     bits;           /* MPPE control bits */
++    unsigned          ccount;         /* 12-bit coherency count (seqno)  */
++    unsigned          stateful;       /* stateful mode flag */
++    int                       discard;        /* stateful mode packet loss flag */
++    int                       sanity_errors;  /* take down LCP if too many */
++    int                       unit;
++    int                       debug;
++    struct compstat   stats;
++} ppp_mppe_state;
++
++/* ppp_mppe_state.bits definitions */
++#define MPPE_BIT_A    0x80    /* Encryption table were (re)inititalized */
++#define MPPE_BIT_B    0x40    /* MPPC only (not implemented) */
++#define MPPE_BIT_C    0x20    /* MPPC only (not implemented) */
++#define MPPE_BIT_D    0x10    /* This is an encrypted frame */
++
++#define MPPE_BIT_FLUSHED      MPPE_BIT_A
++#define MPPE_BIT_ENCRYPTED    MPPE_BIT_D
++
++#define MPPE_BITS(p) ((p)[4] & 0xf0)
++#define MPPE_CCOUNT(p) ((((p)[4] & 0x0f) << 8) + (p)[5])
++#define MPPE_CCOUNT_SPACE 0x1000      /* The size of the ccount space */
++
++#define MPPE_OVHD     2               /* MPPE overhead/packet */
++#define SANITY_MAX    1600            /* Max bogon factor we will tolerate */
++
++static void   GetNewKeyFromSHA __P((unsigned char *StartKey,
++                                    unsigned char *SessionKey,
++                                    unsigned SessionKeyLength,
++                                    unsigned char *InterimKey));
++static void   mppe_rekey __P((ppp_mppe_state *state, int));
++static void   *mppe_alloc __P((unsigned char *options, int optlen));
++static void   mppe_free __P((void *state));
++static int    mppe_init __P((void *state, unsigned char *options,
++                             int optlen, int unit, int debug, const char *));
++static int    mppe_comp_init __P((void *state, unsigned char *options,
++                                  int optlen,
++                                  int unit, int hdrlen, int debug));
++static int    mppe_decomp_init __P((void *state, unsigned char *options,
++                                    int optlen, int unit,
++                                    int hdrlen, int mru, int debug));
++static int    mppe_compress __P((void *state, unsigned char *ibuf,
++                                 unsigned char *obuf,
++                                 int isize, int osize));
++static void   mppe_incomp __P((void *state, unsigned char *ibuf, int icnt));
++static int    mppe_decompress __P((void *state, unsigned char *ibuf,
++                                   int isize, unsigned char *obuf,int osize));
++static void   mppe_comp_reset __P((void *state));
++static void   mppe_decomp_reset __P((void *state));
++static void   mppe_comp_stats __P((void *state, struct compstat *stats));
++
++
++/*
++ * Key Derivation, from RFC 3078, RFC 3079.
++ * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
++ */
++static void
++GetNewKeyFromSHA(unsigned char *MasterKey, unsigned char *SessionKey,
++               unsigned SessionKeyLength, unsigned char *InterimKey)
++{
++    SHA1_CTX Context;
++    unsigned char Digest[SHA1_SIGNATURE_SIZE];
++
++    unsigned char SHApad1[40] =
++    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
++    unsigned char SHApad2[40] =
++    { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
++      0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
++      0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
++      0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
++
++    /* assert(SessionKeyLength <= SHA1_SIGNATURE_SIZE); */
++
++    SHA1_Init(&Context);
++    SHA1_Update(&Context, MasterKey, SessionKeyLength);
++    SHA1_Update(&Context, SHApad1, sizeof(SHApad1));
++    SHA1_Update(&Context, SessionKey, SessionKeyLength);
++    SHA1_Update(&Context, SHApad2, sizeof(SHApad2));
++    SHA1_Final(Digest, &Context);
++
++    memcpy(InterimKey, Digest, SessionKeyLength);
++}
++
++/*
++ * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
++ * Well, not what's written there, but rather what they meant.
++ */
++static void
++mppe_rekey(ppp_mppe_state *state, int initial_key)
++{
++    unsigned char InterimKey[MPPE_MAX_KEY_LEN];
++
++    GetNewKeyFromSHA(state->master_key, state->session_key,
++                   state->keylen, InterimKey);
++    if (!initial_key) {
++      arcfour_setkey(&state->arcfour_context, InterimKey, state->keylen);
++      arcfour_encrypt(&state->arcfour_context, InterimKey, state->keylen,
++                      state->session_key);
++    } else {
++      memcpy(state->session_key, InterimKey, state->keylen);
++    }
++    if (state->keylen == 8) {
++      /* See RFC 3078 */
++      state->session_key[0] = 0xd1;
++      state->session_key[1] = 0x26;
++      state->session_key[2] = 0x9e;
++    }
++    arcfour_setkey(&state->arcfour_context, state->session_key, state->keylen);
++}
++
++
++/*
++ * Allocate space for a (de)compressor.
++ */
++static void *
++mppe_alloc(unsigned char *options, int optlen)
++{
++    ppp_mppe_state *state;
++
++    if (optlen != CILEN_MPPE + sizeof(state->master_key)
++      || options[0] != CI_MPPE
++      || options[1] != CILEN_MPPE)
++      return NULL;
++
++    state = (ppp_mppe_state *) kmalloc(sizeof(*state), GFP_KERNEL);
++    if (state == NULL)
++      return NULL;
++
++/* 
++   Added to avoid module warnings about MOD_INC 
++   being depreciated in 2.6.x 
++*/ 
++#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) )
++    try_module_get(THIS_MODULE);
++#else
++    MOD_INC_USE_COUNT;
++#endif
++    memset(state, 0, sizeof(*state));
++
++    /* Save keys. */
++    memcpy(state->master_key, &options[CILEN_MPPE], sizeof(state->master_key));
++    memcpy(state->session_key, state->master_key, sizeof(state->master_key));
++    /*
++     * We defer initial key generation until mppe_init(), as mppe_alloc()
++     * is called frequently during negotiation.
++     */
++
++    return (void *) state;
++}
++
++/*
++ * Deallocate space for a (de)compressor.
++ */
++static void
++mppe_free(void *arg)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++
++    if (state) {
++      kfree(state);
++/* 
++   Added to avoid module warnings about MOD_DEC_USE_COUNT 
++   being depreciated in 2.6.x 
++*/ 
++#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) )
++        module_put(THIS_MODULE);
++#else
++      MOD_DEC_USE_COUNT;
++#endif
++    }
++}
++
++
++/* 
++ * Initialize (de)compressor state.
++ */
++static int
++mppe_init(void *arg, unsigned char *options, int optlen, int unit, int debug,
++        const char *debugstr)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++    unsigned char mppe_opts;
++
++    if (optlen != CILEN_MPPE
++      || options[0] != CI_MPPE
++      || options[1] != CILEN_MPPE)
++      return 0;
++
++    MPPE_CI_TO_OPTS(&options[2], mppe_opts);
++    if (mppe_opts & MPPE_OPT_128)
++      state->keylen = 16;
++    else if (mppe_opts & MPPE_OPT_40)
++      state->keylen = 8;
++    else {
++      printk(KERN_WARNING "%s[%d]: unknown key length\n", debugstr, unit);
++      return 0;
++    }
++    if (mppe_opts & MPPE_OPT_STATEFUL)
++      state->stateful = 1;
++
++    /* Generate the initial session key. */
++    mppe_rekey(state, 1);
++
++    if (debug) {
++      int i;
++      char mkey[sizeof(state->master_key) * 2 + 1];
++      char skey[sizeof(state->session_key) * 2 + 1];
++
++      printk(KERN_DEBUG "%s[%d]: initialized with %d-bit %s mode\n", debugstr,
++             unit, (state->keylen == 16)? 128: 40,
++             (state->stateful)? "stateful": "stateless");
++
++      for (i = 0; i < sizeof(state->master_key); i++)
++          sprintf(mkey + i * 2, "%.2x", state->master_key[i]);
++      for (i = 0; i < sizeof(state->session_key); i++)
++          sprintf(skey + i * 2, "%.2x", state->session_key[i]);
++      printk(KERN_DEBUG "%s[%d]: keys: master: %s initial session: %s\n",
++             debugstr, unit, mkey, skey);
++    }
++
++    /*
++     * Initialize the coherency count.  The initial value is not specified
++     * in RFC 3078, but we can make a reasonable assumption that it will
++     * start at 0.  Setting it to the max here makes the comp/decomp code
++     * do the right thing (determined through experiment).
++     */
++    state->ccount = MPPE_CCOUNT_SPACE - 1;
++
++    /*
++     * Note that even though we have initialized the key table, we don't
++     * set the FLUSHED bit.  This is contrary to RFC 3078, sec. 3.1.
++     */
++    state->bits = MPPE_BIT_ENCRYPTED;
++
++    state->unit  = unit;
++    state->debug = debug;
++
++    return 1;
++}
++
++
++
++static int
++mppe_comp_init(void *arg, unsigned char *options, int optlen, int unit,
++             int hdrlen, int debug)
++{
++    /* ARGSUSED */
++    return mppe_init(arg, options, optlen, unit, debug, "mppe_comp_init");
++}
++
++/*
++ * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
++ * tell the compressor to rekey.  Note that we MUST NOT rekey for
++ * every CCP Reset-Request; we only rekey on the next xmit packet.
++ * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
++ * So, rekeying for every CCP Reset-Request is broken as the peer will not
++ * know how many times we've rekeyed.  (If we rekey and THEN get another
++ * CCP Reset-Request, we must rekey again.)
++ */
++static void
++mppe_comp_reset(void *arg)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++
++    state->bits |= MPPE_BIT_FLUSHED;
++}
++
++/*
++ * Compress (encrypt) a packet.
++ * It's strange to call this a compressor, since the output is always
++ * MPPE_OVHD + 2 bytes larger than the input.
++ */
++int
++mppe_compress(void *arg, unsigned char *ibuf, unsigned char *obuf,
++            int isize, int osize)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++    int proto;
++
++    /*
++     * Check that the protocol is in the range we handle.
++     */
++    proto = PPP_PROTOCOL(ibuf);
++    if (proto < 0x0021 || proto > 0x00fa)
++      return 0;
++
++    /* Make sure we have enough room to generate an encrypted packet. */
++    if (osize < isize + MPPE_OVHD + 2) {
++      /* Drop the packet if we should encrypt it, but can't. */
++      printk(KERN_DEBUG "mppe_compress[%d]: osize too small! "
++             "(have: %d need: %d)\n", state->unit,
++             osize, osize + MPPE_OVHD + 2);
++      return -1;
++    }
++
++    osize = isize + MPPE_OVHD + 2;
++
++    /*
++     * Copy over the PPP header and set control bits.
++     */
++    obuf[0] = PPP_ADDRESS(ibuf);
++    obuf[1] = PPP_CONTROL(ibuf);
++    obuf[2] = PPP_COMP >> 8;          /* isize + MPPE_OVHD + 1 */
++    obuf[3] = PPP_COMP;                       /* isize + MPPE_OVHD + 2 */
++    obuf += PPP_HDRLEN;
++
++    state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
++    if (state->debug >= 7)
++      printk(KERN_DEBUG "mppe_compress[%d]: ccount %d\n", state->unit,
++             state->ccount);
++    obuf[0] = state->ccount >> 8;
++    obuf[1] = state->ccount & 0xff;
++
++    if (!state->stateful ||                   /* stateless mode     */
++      ((state->ccount & 0xff) == 0xff) ||     /* "flag" packet      */
++      (state->bits & MPPE_BIT_FLUSHED)) {     /* CCP Reset-Request  */
++      /* We must rekey */
++      if (state->debug && state->stateful)
++          printk(KERN_DEBUG "mppe_compress[%d]: rekeying\n", state->unit);
++      mppe_rekey(state, 0);
++      state->bits |= MPPE_BIT_FLUSHED;
++    }
++    obuf[0] |= state->bits;
++    state->bits &= ~MPPE_BIT_FLUSHED; /* reset for next xmit */
++
++    obuf  += MPPE_OVHD;
++    ibuf  += 2;       /* skip to proto field */
++    isize -= 2;
++
++    /* Encrypt packet */
++    arcfour_encrypt(&state->arcfour_context, ibuf, isize, obuf);
++
++    state->stats.unc_bytes += isize;
++    state->stats.unc_packets++;
++    state->stats.comp_bytes += osize;
++    state->stats.comp_packets++;
++
++    return osize;
++}
++
++/*
++ * Since every frame grows by MPPE_OVHD + 2 bytes, this is always going
++ * to look bad ... and the longer the link is up the worse it will get.
++ */
++static void
++mppe_comp_stats(void *arg, struct compstat *stats)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++
++    *stats = state->stats;
++}
++
++
++static int
++mppe_decomp_init(void *arg, unsigned char *options, int optlen, int unit,
++               int hdrlen, int mru, int debug)
++{
++    /* ARGSUSED */
++    return mppe_init(arg, options, optlen, unit, debug, "mppe_decomp_init");
++}
++
++/*
++ * We received a CCP Reset-Ack.  Just ignore it.
++ */
++static void
++mppe_decomp_reset(void *arg)
++{
++    /* ARGSUSED */
++    return;
++}
++
++/*
++ * Decompress (decrypt) an MPPE packet.
++ */
++int
++mppe_decompress(void *arg, unsigned char *ibuf, int isize, unsigned char *obuf,
++              int osize)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++    unsigned ccount;
++    int flushed = MPPE_BITS(ibuf) & MPPE_BIT_FLUSHED;
++    int sanity = 0;
++
++    if (isize <= PPP_HDRLEN + MPPE_OVHD) {
++      if (state->debug)
++          printk(KERN_DEBUG "mppe_decompress[%d]: short pkt (%d)\n",
++                 state->unit, isize);
++      return DECOMP_ERROR;
++    }
++
++    /* Make sure we have enough room to decrypt the packet. */
++    if (osize < isize - MPPE_OVHD - 2) {
++      printk(KERN_DEBUG "mppe_decompress[%d]: osize too small! "
++             "(have: %d need: %d)\n", state->unit,
++             osize, isize - MPPE_OVHD - 2);
++      return DECOMP_ERROR;
++    }
++    osize = isize - MPPE_OVHD - 2;
++
++    ccount = MPPE_CCOUNT(ibuf);
++    if (state->debug >= 7)
++      printk(KERN_DEBUG "mppe_decompress[%d]: ccount %d\n", state->unit,
++             ccount);
++
++    /* sanity checks -- terminate with extreme prejudice */
++    if (!(MPPE_BITS(ibuf) & MPPE_BIT_ENCRYPTED)) {
++      printk(KERN_DEBUG "mppe_decompress[%d]: ENCRYPTED bit not set!\n",
++             state->unit);
++      state->sanity_errors += 100;
++      sanity = 1;
++    }
++    if (!state->stateful && !flushed) {
++      printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set in "
++             "stateless mode!\n", state->unit);
++      state->sanity_errors += 100;
++      sanity = 1;
++    }
++    if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
++      printk(KERN_DEBUG "mppe_decompress[%d]: FLUSHED bit not set on "
++             "flag packet!\n", state->unit);
++      state->sanity_errors += 100;
++      sanity = 1;
++    }
++
++    if (sanity) {
++      if (state->sanity_errors < SANITY_MAX)
++          return DECOMP_ERROR;
++      else
++          /*
++           * Take LCP down if the peer is sending too many bogons.
++           * We don't want to do this for a single or just a few
++           * instances since it could just be due to packet corruption.
++           */
++          return DECOMP_FATALERROR;
++    }
++
++    /*
++     * Check the coherency count.
++     */
++
++    if (!state->stateful) {
++      /* RFC 3078, sec 8.1.  Rekey for every packet. */
++      while (state->ccount != ccount) {
++          mppe_rekey(state, 0);
++          state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
++      }
++    } else {
++      /* RFC 3078, sec 8.2. */
++      if (!state->discard) {
++          /* normal state */
++          state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
++          if (ccount != state->ccount) {
++              /*
++               * (ccount > state->ccount)
++               * Packet loss detected, enter the discard state.
++               * Signal the peer to rekey (by sending a CCP Reset-Request).
++               */
++              state->discard = 1;
++              return DECOMP_ERROR;
++          }
++      } else {
++          /* discard state */
++         if (!flushed) {
++              /* ccp.c will be silent (no additional CCP Reset-Requests). */
++              return DECOMP_ERROR;
++          } else {
++              /* Rekey for every missed "flag" packet. */
++              while ((ccount & ~0xff) != (state->ccount & ~0xff)) {
++                  mppe_rekey(state, 0);
++                  state->ccount = (state->ccount + 256) % MPPE_CCOUNT_SPACE;
++              }
++
++              /* reset */
++              state->discard = 0;
++              state->ccount = ccount;
++              /*
++               * Another problem with RFC 3078 here.  It implies that the
++               * peer need not send a Reset-Ack packet.  But RFC 1962
++               * requires it.  Hopefully, M$ does send a Reset-Ack; even
++               * though it isn't required for MPPE synchronization, it is
++               * required to reset CCP state.
++               */
++          }
++      }
++      if (flushed)
++          mppe_rekey(state, 0);
++    }
++
++    /*
++     * Fill in the first part of the PPP header.  The protocol field
++     * comes from the decrypted data.
++     */
++    obuf[0] = PPP_ADDRESS(ibuf);      /* +1 */
++    obuf[1] = PPP_CONTROL(ibuf);      /* +1 */
++    obuf  += 2;
++    ibuf  += PPP_HDRLEN + MPPE_OVHD;
++    isize -= PPP_HDRLEN + MPPE_OVHD;  /* -6 */
++                                      /* net osize: isize-4 */
++
++    /* And finally, decrypt the packet. */
++    arcfour_decrypt(&state->arcfour_context, ibuf, isize, obuf);
++
++    state->stats.unc_bytes += osize;
++    state->stats.unc_packets++;
++    state->stats.comp_bytes += isize;
++    state->stats.comp_packets++;
++
++    /* good packet credit */
++    state->sanity_errors >>= 1;
++
++    return osize;
++}
++
++/*
++ * Incompressible data has arrived (this should never happen!).
++ * We should probably drop the link if the protocol is in the range
++ * of what should be encrypted.  At the least, we should drop this
++ * packet.  (How to do this?)
++ */
++static void
++mppe_incomp(void *arg, unsigned char *ibuf, int icnt)
++{
++    ppp_mppe_state *state = (ppp_mppe_state *) arg;
++
++    if (state->debug &&
++      (PPP_PROTOCOL(ibuf) >= 0x0021 && PPP_PROTOCOL(ibuf) <= 0x00fa))
++      printk(KERN_DEBUG "mppe_incomp[%d]: incompressible (unencrypted) data! "
++             "(proto %04x)\n", state->unit, PPP_PROTOCOL(ibuf));
++
++    state->stats.inc_bytes += icnt;
++    state->stats.inc_packets++;
++    state->stats.unc_bytes += icnt;
++    state->stats.unc_packets++;
++}
++
++/*************************************************************
++ * Module interface table
++ *************************************************************/
++
++/* These are in ppp.c (2.2.x) or ppp_generic.c (2.4.x) */
++extern int  ppp_register_compressor   (struct compressor *cp);
++extern void ppp_unregister_compressor (struct compressor *cp);
++
++/*
++ * Procedures exported to if_ppp.c.
++ */
++struct compressor ppp_mppe = {
++    CI_MPPE,          /* compress_proto */
++    mppe_alloc,               /* comp_alloc */
++    mppe_free,                /* comp_free */
++    mppe_comp_init,   /* comp_init */
++    mppe_comp_reset,  /* comp_reset */
++    mppe_compress,    /* compress */
++    mppe_comp_stats,  /* comp_stat */
++    mppe_alloc,               /* decomp_alloc */
++    mppe_free,                /* decomp_free */
++    mppe_decomp_init, /* decomp_init */
++    mppe_decomp_reset,        /* decomp_reset */
++    mppe_decompress,  /* decompress */
++    mppe_incomp,      /* incomp */
++    mppe_comp_stats,  /* decomp_stat */
++};
++
++/* 2.2 compatibility defines */
++#ifndef __init
++#define __init
++#endif
++#ifndef __exit
++#define __exit
++#endif
++#ifndef MODULE_LICENSE
++#define MODULE_LICENSE(license)
++#endif
++
++int __init
++ppp_mppe_init(void)
++{  
++    int answer = ppp_register_compressor(&ppp_mppe);
++
++    if (answer == 0)
++      printk(KERN_INFO "PPP MPPE Compression module registered\n");
++    return answer;
++}
++
++void __exit
++ppp_mppe_cleanup(void)
++{
++    ppp_unregister_compressor(&ppp_mppe);
++}
++
++module_init(ppp_mppe_init);
++module_exit(ppp_mppe_cleanup);
++MODULE_LICENSE("BSD without advertisement clause");
+--- /dev/null
++++ linux-2.4.27/drivers/net/sha1.c
+@@ -0,0 +1,185 @@
++/*
++ * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c
++ * 
++ * SHA-1 in C
++ * By Steve Reid <steve@edmweb.com>
++ * 100% Public Domain
++ * 
++ * Test Vectors (from FIPS PUB 180-1)
++ * "abc"
++ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
++ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
++ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
++ * A million repetitions of "a"
++ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
++ */
++
++/* #define SHA1HANDSOFF * Copies data before messing with it. */
++
++#if defined(__linux__)
++#include <asm/byteorder.h>
++#include <linux/string.h>
++#elif defined(__solaris__)
++#include <sys/isa_defs.h>
++#include <sys/ddi.h>
++#include <sys/sunddi.h>
++#define memcpy(d, s, c) bcopy(s, d, c)
++#define memset(d, b, c) bzero(d, c)
++#endif
++
++#include "sha1.h"
++
++static void SHA1_Transform(unsigned long[5], const unsigned char[64]);
++
++#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
++
++/* blk0() and blk() perform the initial expand. */
++/* I got the idea of expanding during the round function from SSLeay */
++#if defined(__LITTLE_ENDIAN) || defined(_LITTLE_ENDIAN)
++#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
++    |(rol(block->l[i],8)&0x00FF00FF))
++#elif defined(__BIG_ENDIAN) || defined(_BIG_ENDIAN)
++#define blk0(i) block->l[i]
++#else
++#error Endianness not defined
++#endif
++#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
++    ^block->l[(i+2)&15]^block->l[i&15],1))
++
++/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
++#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
++#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
++#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
++#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
++
++
++/* Hash a single 512-bit block. This is the core of the algorithm. */
++
++static void
++SHA1_Transform(unsigned long state[5], const unsigned char buffer[64])
++{
++    unsigned long a, b, c, d, e;
++    typedef union {
++      unsigned char c[64];
++      unsigned long l[16];
++    } CHAR64LONG16;
++    CHAR64LONG16 *block;
++
++#ifdef SHA1HANDSOFF
++    static unsigned char workspace[64];
++    block = (CHAR64LONG16 *) workspace;
++    memcpy(block, buffer, 64);
++#else
++    block = (CHAR64LONG16 *) buffer;
++#endif
++    /* Copy context->state[] to working vars */
++    a = state[0];
++    b = state[1];
++    c = state[2];
++    d = state[3];
++    e = state[4];
++    /* 4 rounds of 20 operations each. Loop unrolled. */
++    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
++    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
++    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
++    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
++    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
++    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
++    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
++    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
++    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
++    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
++    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
++    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
++    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
++    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
++    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
++    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
++    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
++    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
++    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
++    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
++    /* Add the working vars back into context.state[] */
++    state[0] += a;
++    state[1] += b;
++    state[2] += c;
++    state[3] += d;
++    state[4] += e;
++    /* Wipe variables */
++    a = b = c = d = e = 0;
++}
++
++
++/* SHA1Init - Initialize new context */
++
++void
++SHA1_Init(SHA1_CTX *context)
++{
++    /* SHA1 initialization constants */
++    context->state[0] = 0x67452301;
++    context->state[1] = 0xEFCDAB89;
++    context->state[2] = 0x98BADCFE;
++    context->state[3] = 0x10325476;
++    context->state[4] = 0xC3D2E1F0;
++    context->count[0] = context->count[1] = 0;
++}
++
++
++/* Run your data through this. */
++
++void
++SHA1_Update(SHA1_CTX *context, const unsigned char *data, unsigned int len)
++{
++    unsigned int i, j;
++
++    j = (context->count[0] >> 3) & 63;
++    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
++    context->count[1] += (len >> 29);
++    if ((j + len) > 63) {
++      memcpy(&context->buffer[j], data, (i = 64-j));
++      SHA1_Transform(context->state, context->buffer);
++      for ( ; i + 63 < len; i += 64) {
++          SHA1_Transform(context->state, &data[i]);
++      }
++      j = 0;
++    }
++    else
++      i = 0;
++
++    memcpy(&context->buffer[j], &data[i], len - i);
++}
++
++
++/* Add padding and return the message digest. */
++
++void
++SHA1_Final(unsigned char digest[20], SHA1_CTX *context)
++{
++    unsigned long i, j;
++    unsigned char finalcount[8];
++
++    for (i = 0; i < 8; i++) {
++        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
++         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
++    }
++    SHA1_Update(context, (unsigned char *) "\200", 1);
++    while ((context->count[0] & 504) != 448) {
++      SHA1_Update(context, (unsigned char *) "\0", 1);
++    }
++    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
++    for (i = 0; i < 20; i++) {
++      digest[i] = (unsigned char)
++                   ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
++    }
++    /* Wipe variables */
++    i = j = 0;
++    memset(context->buffer, 0, 64);
++    memset(context->state, 0, 20);
++    memset(context->count, 0, 8);
++    memset(&finalcount, 0, 8);
++#ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */
++    SHA1Transform(context->state, context->buffer);
++#endif
++}
++
+--- /dev/null
++++ linux-2.4.27/drivers/net/sha1.h
+@@ -0,0 +1,18 @@
++/* sha1.h */
++
++#ifndef _SHA1_H
++#define _SHA1_H
++
++typedef struct {
++    unsigned long state[5];
++    unsigned long count[2];
++    unsigned char buffer[64];
++} SHA1_CTX;
++
++#define SHA1_SIGNATURE_SIZE 20
++
++extern void SHA1_Init(SHA1_CTX *);
++extern void SHA1_Update(SHA1_CTX *, const unsigned char *, unsigned int);
++extern void SHA1_Final(unsigned char[SHA1_SIGNATURE_SIZE], SHA1_CTX *);
++
++#endif /* _SHA1_H */
+--- linux-2.4.27/include/linux/ppp-comp.h~mppe-20040216
++++ linux-2.4.27/include/linux/ppp-comp.h
+@@ -187,6 +187,100 @@
+ #define DEFLATE_CHK_SEQUENCE  0
+ /*
++ * Definitions for MPPE.
++ */
++
++#define CI_MPPE                       18      /* config option for MPPE */
++#define CILEN_MPPE            6       /* length of config option */
++
++#define MPPE_PAD              4       /* MPPE growth per frame */
++#define MPPE_MAX_KEY_LEN      16      /* largest key length (128-bit) */
++
++/* option bits for ccp_options.mppe */
++#define MPPE_OPT_40           0x01    /* 40 bit */
++#define MPPE_OPT_128          0x02    /* 128 bit */
++#define MPPE_OPT_STATEFUL     0x04    /* stateful mode */
++/* unsupported opts */
++#define MPPE_OPT_56           0x08    /* 56 bit */
++#define MPPE_OPT_MPPC         0x10    /* MPPC compression */
++#define MPPE_OPT_D            0x20    /* Unknown */
++#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
++#define MPPE_OPT_UNKNOWN      0x40    /* Bits !defined in RFC 3078 were set */
++
++/*
++ * This is not nice ... the alternative is a bitfield struct though.
++ * And unfortunately, we cannot share the same bits for the option
++ * names above since C and H are the same bit.  We could do a u_int32
++ * but then we have to do a htonl() all the time and/or we still need
++ * to know which octet is which.
++ */
++#define MPPE_C_BIT            0x01    /* MPPC */
++#define MPPE_D_BIT            0x10    /* Obsolete, usage unknown */
++#define MPPE_L_BIT            0x20    /* 40-bit */
++#define MPPE_S_BIT            0x40    /* 128-bit */
++#define MPPE_M_BIT            0x80    /* 56-bit, not supported */
++#define MPPE_H_BIT            0x01    /* Stateless (in a different byte) */
++
++/* Does not include H bit; used for least significant octet only. */
++#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
++
++/* Build a CI from mppe opts (see RFC 3078) */
++#define MPPE_OPTS_TO_CI(opts, ci)             \
++    do {                                      \
++      u_char *ptr = ci; /* u_char[4] */       \
++                                              \
++      /* H bit */                             \
++      if (opts & MPPE_OPT_STATEFUL)           \
++          *ptr++ = 0x0;                       \
++      else                                    \
++          *ptr++ = MPPE_H_BIT;                \
++      *ptr++ = 0;                             \
++      *ptr++ = 0;                             \
++                                              \
++      /* S,L bits */                          \
++      *ptr = 0;                               \
++      if (opts & MPPE_OPT_128)                \
++          *ptr |= MPPE_S_BIT;                 \
++      if (opts & MPPE_OPT_40)                 \
++          *ptr |= MPPE_L_BIT;                 \
++      /* M,D,C bits not supported */          \
++    } while (/* CONSTCOND */ 0)
++
++/* The reverse of the above */
++#define MPPE_CI_TO_OPTS(ci, opts)             \
++    do {                                      \
++      u_char *ptr = ci; /* u_char[4] */       \
++                                              \
++      opts = 0;                               \
++                                              \
++      /* H bit */                             \
++      if (!(ptr[0] & MPPE_H_BIT))             \
++          opts |= MPPE_OPT_STATEFUL;          \
++                                              \
++      /* S,L bits */                          \
++      if (ptr[3] & MPPE_S_BIT)                \
++          opts |= MPPE_OPT_128;               \
++      if (ptr[3] & MPPE_L_BIT)                \
++          opts |= MPPE_OPT_40;                \
++                                              \
++      /* M,D,C bits */                        \
++      if (ptr[3] & MPPE_M_BIT)                \
++          opts |= MPPE_OPT_56;                \
++      if (ptr[3] & MPPE_D_BIT)                \
++          opts |= MPPE_OPT_D;                 \
++      if (ptr[3] & MPPE_C_BIT)                \
++          opts |= MPPE_OPT_MPPC;              \
++                                              \
++      /* Other bits */                        \
++      if (ptr[0] & ~MPPE_H_BIT)               \
++          opts |= MPPE_OPT_UNKNOWN;           \
++      if (ptr[1] || ptr[2])                   \
++          opts |= MPPE_OPT_UNKNOWN;           \
++      if (ptr[3] & ~MPPE_ALL_BITS)            \
++          opts |= MPPE_OPT_UNKNOWN;           \
++    } while (/* CONSTCOND */ 0)
++
++/*
+  * Definitions for other, as yet unsupported, compression methods.
+  */
+--- linux-2.4.27/drivers/net/Makefile~mppe-20040216
++++ linux-2.4.27/drivers/net/Makefile
+@@ -18,8 +18,9 @@
+ export-objs     :=    8390.o arlan.o aironet4500_core.o aironet4500_card.o \
+                       ppp_async.o ppp_generic.o slhc.o pppox.o auto_irq.o \
+                       net_init.o mii.o
+-list-multi    :=      rcpci.o
++list-multi    :=      rcpci.o ppp_mppe.o
+ rcpci-objs    :=      rcpci45.o rclanmtl.o
++ppp_mppe-objs   :=      ppp_mppe_compress.o sha1.o arcfour.o
+ ifeq ($(CONFIG_TULIP),y)
+   obj-y += tulip/tulip.o
+@@ -164,6 +165,14 @@
+ obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o
+ obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
++ifeq ($(CONFIG_PPP_MPPE),y)
++  obj-y += $(ppp_mppe-objs)
++else
++  ifeq ($(CONFIG_PPP_MPPE),m)
++    obj-m += ppp_mppe.o
++  endif
++endif
++
+ obj-$(CONFIG_SLIP) += slip.o
+ ifeq ($(CONFIG_SLIP_COMPRESSED),y)
+   obj-$(CONFIG_SLIP) += slhc.o
+@@ -272,3 +281,7 @@
+ rcpci.o: $(rcpci-objs)
+       $(LD) -r -o $@ $(rcpci-objs)
++
++ppp_mppe.o: $(ppp_mppe-objs)
++      $(LD) -r -o $@ $(ppp_mppe-objs)
++
diff --git a/packages/linux/opensimpad/sa1100-usb-tcl1.patch b/packages/linux/opensimpad/sa1100-usb-tcl1.patch
new file mode 100644 (file)
index 0000000..da7f50d
--- /dev/null
@@ -0,0 +1,216 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/arch/arm/mach-sa1100/sa1100_usb.h~sa1100-usb
++++ linux-2.4.27/arch/arm/mach-sa1100/sa1100_usb.h
+@@ -10,6 +10,11 @@
+ #define _SA1100_USB_H
+ #include <asm/byteorder.h>
++#define SA1100_USB_DEBUG
++#ifdef SA1100_USB_DEBUG
++extern int sa1100_usb_debug;
++#endif
++
+ typedef void (*usb_callback_t)(int flag, int size);
+ /* in usb_ctl.c (see also descriptor methods at bottom of file) */
+--- linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.c~sa1100-usb
++++ linux-2.4.27/arch/arm/mach-sa1100/usb_ctl.c
+@@ -28,6 +28,12 @@
+ #include "sa1100_usb.h"
+ #include "usb_ctl.h"
++// Toby Churchill Ltd modification to re assert Ser0UDCOMP when it gets corrupted (why?)
++// Identified by Chris Jones for TCL.
++// Added 25/3/2004 N C Bane for balloon board.
++#define TCL_FIX
++
++
+ //////////////////////////////////////////////////////////////////////////////
+ // Prototypes
+ //////////////////////////////////////////////////////////////////////////////
+@@ -109,6 +115,45 @@
+ //////////////////////////////////////////////////////////////////////////////
+ // Async
+ //////////////////////////////////////////////////////////////////////////////
++
++#ifdef CONFIG_SA1100_USB_HOTPLUG
++// user space notification support for sa1100 usb state change
++// Copyright (c) 2003 N C Bane
++#include <linux/kmod.h>
++#include <linux/interrupt.h>
++//static int usb_hotplug_state=USB_STATE_DEFAULT;
++
++static void do_usb_helper (void *status)
++{
++      char *argv [3], *envp [3];
++      int v;
++
++      argv [0] = "/sbin/sausb-hotplug";
++
++      argv [1] = device_state_names[(int)status];
++      argv [2] = NULL;
++
++      envp [0] = "HOME=/";
++      envp [1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++      envp [2] = NULL;
++
++      v = call_usermodehelper (argv [0], argv, envp);
++
++      if (v != 0)
++              printk ("sausb hotplug returned 0x%x", v);
++}
++
++static struct tq_struct usb_task;
++
++static void usb_helper (int status) {
++//    usb_hotplug_state = status;
++    usb_task.routine=do_usb_helper;
++    usb_task.data=(void *)status;
++    schedule_task(&usb_task);
++}
++
++#endif
++
+ static void core_kicker(void);
+ static inline void enable_resume_mask_suspend( void );
+@@ -119,6 +164,7 @@
+ {
+       __u32 status = Ser0UDCSR;
++//static int reset;
+       /* ReSeT Interrupt Request - UDC has been reset */
+       if ( status & UDCSR_RSTIR )
+       {
+@@ -133,6 +179,7 @@
+               // mask reset ints, they flood during sequence, enable
+               // suspend and resume
+               Ser0UDCCR |= UDCCR_REM;    // mask reset
++//reset=true;
+               Ser0UDCCR &= ~(UDCCR_SUSIM | UDCCR_RESIM); // enable suspend and resume
+               UDC_flip(  Ser0UDCSR, status ); // clear all pending sources
+               return;         // <-- no reason to continue if resetting
+@@ -160,6 +207,13 @@
+       if (status & UDCSR_EIR)
+                ep0_int_hndlr();
++#ifdef TCL_FIX
++      if (Ser0UDCOMP!=63) {
++          printk("%s: Ser0UDCOMP = %d. Reset to 63\n",__FUNCTION__,Ser0UDCOMP);
++          Ser0UDCOMP=63;
++      }
++#endif
++
+       if (status & UDCSR_RIR)
+               ep1_int_hndlr(status);
+@@ -443,6 +497,12 @@
+                          ) {
+                         configured_callback();
+                       }
++#ifdef CONFIG_SA1100_USB_HOTPLUG
++                  if (next_device_state == USB_STATE_CONFIGURED)
++                      usb_helper(next_device_state);
++                  if ((next_device_state == USB_STATE_SUSPENDED) && (usbd_info.state == USB_STATE_CONFIGURED))
++                      usb_helper(next_device_state);
++#endif
+                       usbd_info.state = next_device_state;
+                       ep1_state_change_notify( next_device_state );
+                       ep2_state_change_notify( next_device_state );
+@@ -683,6 +743,43 @@
+        return len;
+ }
++#ifdef SA1100_USB_DEBUG
++#include <asm/uaccess.h>                /* to copy to/from userspace */
++struct proc_dir_entry *debug_entry;
++
++static int proc_debug_read (struct file * file, char * buf,
++              size_t nbytes, loff_t *ppos)
++{
++      char outputbuf[8];
++      int count;
++      // read completed?
++      if (*ppos)
++          return 0;
++      count=sprintf(outputbuf, "%d\n",sa1100_usb_debug);
++      if (copy_to_user(buf, outputbuf, count))
++              return -EFAULT;
++      return count;
++}
++
++static ssize_t proc_debug_write(struct file * file, const char * buffer,
++              size_t count, loff_t *ppos)
++{
++      char *endp;
++      if (*ppos)
++          return -EINVAL;
++      sa1100_usb_debug = simple_strtoul(buffer,&endp,0);
++      // we claim all is read
++      return count;
++}
++
++
++
++static struct file_operations proc_debug_operations = {
++      read:   proc_debug_read,
++      write:  proc_debug_write
++};
++
++#endif
+ #endif  /* CONFIG_PROC_FS */
+ //////////////////////////////////////////////////////////////////////////////
+@@ -703,6 +800,15 @@
+ #if CONFIG_PROC_FS
+       create_proc_read_entry ( PROC_NODE_NAME, 0, NULL, usbctl_read_proc, NULL);
++#ifdef SA1100_USB_DEBUG
++      {
++          debug_entry = create_proc_entry("sa1100_usb_debug",
++                              S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH,
++                              &proc_root);
++          if (debug_entry) 
++                  debug_entry->proc_fops = &proc_debug_operations;
++      }
++#endif
+ #endif
+       /* setup rx dma */
+@@ -751,6 +857,9 @@
+ #if CONFIG_PROC_FS
+     remove_proc_entry ( PROC_NODE_NAME, NULL);
++#ifdef SA1100_USB_DEBUG
++    remove_proc_entry("sa1100_usb_debug",&proc_root);
++#endif
+ #endif
+     sa1100_free_dma(usbd_info.dmach_rx);
+@@ -769,6 +878,10 @@
+ EXPORT_SYMBOL( sa1100_usb_get_string_descriptor );
+ EXPORT_SYMBOL( sa1100_usb_kmalloc_string_descriptor );
++#ifdef SA1100_USB_DEBUG
++int sa1100_usb_debug=0;
++EXPORT_SYMBOL(sa1100_usb_debug);
++#endif
+ module_init( usbctl_init );
+ module_exit( usbctl_exit );
+--- linux-2.4.27/arch/arm/config.in~sa1100-usb
++++ linux-2.4.27/arch/arm/config.in
+@@ -138,6 +138,9 @@
+ dep_bool '  Yopy' CONFIG_SA1100_YOPY $CONFIG_ARCH_SA1100
+ dep_tristate 'SA1100 USB function support' CONFIG_SA1100_USB $CONFIG_ARCH_SA1100
++if [ "$CONFIG_SA1100_USB" != "n" ]; then
++   bool '  Support for SA11x0 USB usb-hotplug' CONFIG_SA1100_USB_HOTPLUG
++fi
+ dep_tristate '  Support for SA11x0 USB network link function' CONFIG_SA1100_USB_NETLINK $CONFIG_SA1100_USB
+ dep_tristate '  Support for SA11x0 USB character device emulation' CONFIG_SA1100_USB_CHAR $CONFIG_SA1100_USB
diff --git a/packages/linux/opensimpad/simpad-backlight-if.patch b/packages/linux/opensimpad/simpad-backlight-if.patch
new file mode 100644 (file)
index 0000000..4e04afb
--- /dev/null
@@ -0,0 +1,102 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/video/mq200fb.c~simpad-backlight-if
++++ linux-2.4.27/drivers/video/mq200fb.c
+@@ -82,6 +82,20 @@
+       write:  proc_write_reg
+ };
++#ifdef CONFIG_SA1100_SIMPAD
++
++static ssize_t proc_read_light(struct file * file, char * buf,
++              size_t nbytes, loff_t *ppos);
++static ssize_t proc_write_light(struct file * file, const char * buffer,
++              size_t count, loff_t *ppos);
++
++static struct file_operations proc_light_operations = {
++      read:   proc_read_light,
++      write:  proc_write_light
++};
++#endif
++
++
+ typedef struct sa1110_reg_entry {
+       u32 phyaddr;
+       char* name;
+@@ -622,6 +636,20 @@
+        }
+     }
++#ifdef CONFIG_SA1100_SIMPAD
++      entry = create_proc_entry("backlight",
++                               S_IRWXU | S_IRWXG | S_IRWXO,
++                               mq200dir);
++      if(entry) {
++        entry->proc_fops = &proc_light_operations;
++      } 
++    else {
++        printk( KERN_ERR
++                "mq200fb: can't create /proc/" MQ200_DIRNAME
++                "/backlight\n");
++        return(-ENOMEM);
++    }
++ #endif
+ #ifdef MQ_SA1110
+@@ -1879,7 +1907,7 @@
+ static void writeBrightness(void *pMQMMIO, int brightness)
+ {
+     unsigned long dutyCycle, pwmcontrol;
+-    int MAX_BRIGHT_REG = 0x000000fc; /* int 254 */
++    int MAX_BRIGHT_REG = 0x000000fe; /* int 254 */
+     
+     if(brightness > MAX_BRIGHT_REG)
+       return;
+@@ -1961,3 +1989,43 @@
+       return (count+endp-buffer);
+ }
++#ifdef CONFIG_SA1100_SIMPAD
++
++#define SIMPAD_BACKLIGHT_MASK 0x00a10044
++
++static int proc_read_light(struct file * file, char * buf,
++              size_t nbytes, loff_t *ppos)
++{
++      char outputbuf[15];
++      int count;
++      u32 pwmctl;
++      if (*ppos>0) /* Assume reading completed in previous read*/
++              return 0;
++
++      pwmctl = *((volatile *) mq200_p2v(0x4be0e03c));
++      pwmctl &= ~SIMPAD_BACKLIGHT_MASK;
++      pwmctl = pwmctl >> 8;   
++      pwmctl = 254 - pwmctl;
++              
++      count = sprintf(outputbuf, "%d\n",pwmctl);
++      *ppos+=count;
++      if (count>nbytes)  /* Assume output can be read at one time */
++              return -EINVAL;
++      if (copy_to_user(buf, outputbuf, count))
++              return -EFAULT;
++      return count;
++}
++
++static ssize_t proc_write_light(struct file * file, const char * buffer,
++              size_t count, loff_t *ppos)
++{
++      void * pMQMMIO = (void *) mqMmioAddr;
++      char *endp;
++      unsigned long newvalue = simple_strtoul(buffer,&endp,0);
++      if (newvalue > 254)
++              newvalue = 254;
++      writeBrightness(pMQMMIO,newvalue);
++      mq200_backlight(pMQMMIO,(int)newvalue);
++      return (count+endp-buffer);
++}
++#endif
diff --git a/packages/linux/opensimpad/simpad-proc-sys-board.patch b/packages/linux/opensimpad/simpad-proc-sys-board.patch
new file mode 100644 (file)
index 0000000..e6a7e12
--- /dev/null
@@ -0,0 +1,317 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/char/Makefile~simpad-proc-sys-board
++++ linux-2.4.27/drivers/char/Makefile
+@@ -134,6 +134,9 @@
+   ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
+     KEYBD    += cerf_keyb.o
+   endif
++  ifeq ($(CONFIG_SA1100_SIMPAD),y)
++    obj-$(CONFIG_SA1100_SIMPAD) += sysctl.o
++  endif
+   ifeq ($(CONFIG_ARCH_FORTUNET),y)
+     KEYMAP   := defkeymap.o
+   endif
+--- /dev/null
++++ linux-2.4.27/drivers/char/sysctl.c
+@@ -0,0 +1,297 @@
++/*
++ *  /proc/sys/board - Interface to the SIMpad cs3 register
++ *
++ *  (c) 2004 by Till Harbaum, BeeCon GmbH, <harbaum@beecon.de>
++ */
++
++#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/simpad.h>
++#include <asm/uaccess.h>
++
++extern long get_cs3_shadow(void);
++extern void set_cs3_bit(int value);
++extern void clear_cs3_bit(int value);
++
++/*
++ * This is the number for the "board" entry in /proc/sys:
++ */
++#define SIMPAD_SYSCTL 1312
++
++/*
++ * These are the numbers for the entries in /etc/sys/board
++ */
++enum {
++      CTL_NAME=991,
++      CTL_CS3,          // the complete latch
++      CTL_VCC_5V_EN,    // For 5V PCMCIA 
++        CTL_VCC_3V_EN,    // FOR 3.3V PCMCIA
++        CTL_EN1,          // This is only for EPROM's
++        CTL_EN0,          // Both should be enable for 3.3V or 5V
++        CTL_DISPLAY_ON,
++        CTL_PCMCIA_BUFF_DIS,
++        CTL_MQ_RESET,
++        CTL_PCMCIA_RESET,
++        CTL_DECT_POWER_ON,
++        CTL_IRDA_SD,      // Shutdown for powersave
++        CTL_RS232_ON, 
++        CTL_SD_MEDIAQ,    // Shutdown for powersave
++        CTL_LED2_ON,
++        CTL_IRDA_MODE,    // Fast/Slow IrDA mode
++        CTL_ENABLE_5V,    // Enable 5V circuit
++        CTL_RESET_SIMCARD
++};
++
++static const char simpad_board_name[] = "SIMpad";
++static int  dummy_int;
++static char dummy_str[80];
++
++static int
++simpad_sysctl_handler(ctl_table * ctl, int write, struct file *filp,
++                    void *buffer, size_t * lenp)
++{
++      int *valp = ctl->data;
++      int val;
++      int ret;
++
++      // Update parameters from the real registers
++      switch (ctl->ctl_name) {
++      case CTL_CS3:
++          sprintf(dummy_str, "0x%04lx", get_cs3_shadow());
++          return proc_dostring(ctl,write,filp,buffer,lenp);
++          break;
++
++        // the 16 control bits of the cs3 register
++        case CTL_VCC_5V_EN:
++      case CTL_VCC_3V_EN:
++      case CTL_EN1:
++      case CTL_EN0:
++      case CTL_DISPLAY_ON:
++      case CTL_PCMCIA_BUFF_DIS:
++      case CTL_MQ_RESET:
++      case CTL_PCMCIA_RESET:
++      case CTL_DECT_POWER_ON:
++      case CTL_IRDA_SD:
++      case CTL_RS232_ON:
++      case CTL_SD_MEDIAQ:
++      case CTL_LED2_ON:
++      case CTL_IRDA_MODE:
++      case CTL_ENABLE_5V:
++      case CTL_RESET_SIMCARD:
++              *valp = (get_cs3_shadow() & 
++                      (1u << (ctl->ctl_name-CTL_VCC_5V_EN)))?1:0;
++                break;
++
++      default:
++              // Just ignore unsupported parameters
++              break;
++      }
++
++      // the strings are all handled now and ran onto a return;
++
++      // 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) {
++
++                // the 16 control bits of the cs3 register
++              case CTL_DISPLAY_ON:
++              case CTL_DECT_POWER_ON:
++              case CTL_IRDA_SD:
++              case CTL_SD_MEDIAQ:
++              case CTL_LED2_ON:
++              case CTL_IRDA_MODE:
++              case CTL_RESET_SIMCARD:
++                      if (val) 
++                              set_cs3_bit(1u << (ctl->ctl_name-CTL_VCC_5V_EN));
++                      else     
++                              clear_cs3_bit(1u << (ctl->ctl_name-CTL_VCC_5V_EN));
++                break;
++
++              default:
++                      // Just ignore unsupported parameters
++                      break;
++              }
++      }
++
++      return ret;
++}
++
++#define PROC_RDONLY 0444
++#define PROC_RDWR   0664
++
++static ctl_table simpad_table[] = {
++        {
++       procname:      "sys_name",
++       ctl_name:      CTL_NAME, 
++       data:          &simpad_board_name,
++       maxlen:        sizeof(simpad_board_name),
++         proc_handler:        &proc_dostring,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "cs3",
++       ctl_name:      CTL_CS3, 
++       data:          &dummy_str,
++       maxlen:        sizeof(dummy_str),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "vcc_5v_en",
++       ctl_name:      CTL_VCC_5V_EN,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "vcc_3v_en",
++       ctl_name:      CTL_VCC_3V_EN,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "en1",
++       ctl_name:      CTL_EN1,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "en0",
++       ctl_name:      CTL_EN0,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "display_on",
++       ctl_name:      CTL_DISPLAY_ON,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "pcmcia_buff_dis",
++       ctl_name:      CTL_PCMCIA_BUFF_DIS,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "mq_reset",
++       ctl_name:      CTL_MQ_RESET,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "pcmcia_reset",
++       ctl_name:      CTL_PCMCIA_RESET,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "dect_power_on",
++       ctl_name:      CTL_DECT_POWER_ON,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "irda_sd",
++       ctl_name:      CTL_IRDA_SD,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "rs232_on",
++       ctl_name:      CTL_RS232_ON,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "sd_mediaq",
++       ctl_name:      CTL_SD_MEDIAQ,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "led2_on",
++       ctl_name:      CTL_LED2_ON,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "irda_mode",
++       ctl_name:      CTL_IRDA_MODE,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      }, {
++       procname:      "enable_5v",
++       ctl_name:      CTL_ENABLE_5V,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDONLY,
++      }, {
++       procname:      "reset_simcard",
++       ctl_name:      CTL_RESET_SIMCARD,
++       data:          &dummy_int,
++       maxlen:        sizeof(int),
++         proc_handler:        &simpad_sysctl_handler,
++       mode:          PROC_RDWR,
++      },
++        {0}
++        };
++
++static ctl_table simpad_root_table[] = {
++        {SIMPAD_SYSCTL, "board", NULL, 0, 0555, simpad_table},
++        {0}
++        };
++
++
++static struct ctl_table_header *simpad_table_header;
++
++
++static int __init simpad_sysctl_init(void) 
++{
++        simpad_table_header = register_sysctl_table(simpad_root_table, 0);
++        if (!simpad_table_header)
++                return -ENOMEM;
++        return 0;
++}
++
++static void __exit simpad_sysctl_exit(void)
++{
++        unregister_sysctl_table(simpad_table_header);
++}
++
++
++module_init(simpad_sysctl_init);
++module_exit(simpad_sysctl_exit);
++
++MODULE_AUTHOR("Till Harbaum <harbaum@beecon.de>");
++MODULE_DESCRIPTION("Implements /proc/sys/board");
++MODULE_LICENSE("GPL");
diff --git a/packages/linux/opensimpad/simpad-serial.patch b/packages/linux/opensimpad/simpad-serial.patch
new file mode 100644 (file)
index 0000000..e54c682
--- /dev/null
@@ -0,0 +1,267 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/include/asm-arm/arch-sa1100/simpad.h~simpad-serial
++++ linux-2.4.27/include/asm-arm/arch-sa1100/simpad.h
+@@ -16,7 +16,7 @@
+ #error "include <asm/hardware.h> instead"
+ #endif
+-#define GPIO_UART1_RTS        GPIO_GPIO14
++#define GPIO_UART1_RTS        GPIO_GPIO9
+ #define GPIO_UART1_DTR        GPIO_GPIO7
+ #define GPIO_UART1_CTS        GPIO_GPIO8
+ #define GPIO_UART1_DCD        GPIO_GPIO23
+@@ -31,12 +31,12 @@
+ #define GPIO_POWER_BUTTON       GPIO_GPIO0
+ #define GPIO_UCB1300_IRQ      GPIO_GPIO (22)  /* UCB GPIO and touchscreen */
+-#define IRQ_UART1_CTS IRQ_GPIO15
+-#define IRQ_UART1_DCD GPIO_GPIO23
+-#define IRQ_UART1_DSR GPIO_GPIO6
+-#define IRQ_UART3_CTS GPIO_GPIO13
+-#define IRQ_UART3_DCD GPIO_GPIO18
+-#define IRQ_UART3_DSR GPIO_GPIO17
++#define IRQ_GPIO_UART1_CTS    IRQ_GPIO8
++#define IRQ_GPIO_UART1_DCD    IRQ_GPIO23
++#define IRQ_GPIO_UART1_DSR    IRQ_GPIO6
++#define IRQ_GPIO_UART3_CTS    IRQ_GPIO13
++#define IRQ_GPIO_UART3_DCD    IRQ_GPIO18
++#define IRQ_GPIO_UART3_DSR    IRQ_GPIO17
+ #define IRQ_GPIO_UCB1300_IRQ IRQ_GPIO22
+ #define IRQ_GPIO_POWER_BUTTON    IRQ_GPIO0
+--- linux-2.4.27/arch/arm/mach-sa1100/simpad.c~simpad-serial
++++ linux-2.4.27/arch/arm/mach-sa1100/simpad.c
+@@ -22,9 +22,11 @@
+ #include "generic.h"
++#undef SIMPAD_UART_USE_IRQ  // irq handling on CTS/DCD doesn't work yet
++
+ long cs3_shadow;
+-long get_cs3_shadow()
++long get_cs3_shadow(void)
+ {
+       return cs3_shadow;
+ }
+@@ -107,18 +109,170 @@
+   LAST_DESC
+ };
++#define SER_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
++
++static void simpad_uart_set_mctrl(struct uart_port *port, u_int mctrl)
++{
++      if (port->mapbase == _Ser1UTCR0) {
++              /* internal serial port (ttySA1, DECT/Bluetooth) */
++              if (mctrl & TIOCM_RTS)  GPCR = GPIO_UART1_RTS;
++              else                    GPSR = GPIO_UART1_RTS;
++
++              if (mctrl & TIOCM_DTR)  GPCR = GPIO_UART1_DTR;
++              else                    GPSR = GPIO_UART1_DTR;
++      }
++
++      else if (port->mapbase == _Ser3UTCR0) {
++              /* external serial port (ttySA0, RS232) */
++              if (mctrl & TIOCM_RTS)  GPCR = GPIO_UART3_RTS;
++              else                    GPSR = GPIO_UART3_RTS;
++
++              if (mctrl & TIOCM_DTR)  GPCR = GPIO_UART3_DTR;
++              else                    GPSR = GPIO_UART3_DTR;
++      }
++}
++
++static u_int simpad_uart_get_mctrl(struct uart_port *port)
++{
++      u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
++
++      if (port->mapbase == _Ser1UTCR0) {
++              /* internal serial port (ttySA1, DECT/Bluetooth) */
++              int gplr = GPLR;
++              if (gplr & GPIO_UART1_DCD) ret &= ~TIOCM_CD;
++              if (gplr & GPIO_UART1_CTS) ret &= ~TIOCM_CTS;
++              if (gplr & GPIO_UART1_DSR) ret &= ~TIOCM_DSR;
++      }
++
++      else if (port->mapbase == _Ser3UTCR0) {
++              /* external serial port (ttySA0, RS232) */
++              int gplr = GPLR;
++              if (gplr & GPIO_UART3_DCD) ret &= ~TIOCM_CD;
++              if (gplr & GPIO_UART3_CTS) ret &= ~TIOCM_CTS;
++              if (gplr & GPIO_UART3_DSR) ret &= ~TIOCM_DSR;
++      }
++      return ret;
++}
++
+ static void simpad_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+ {
+-      if (port->mapbase == (u_int)&Ser1UTCR0) {
++      if (port->mapbase == (u_int)&Ser3UTCR0) {
+               if (state)
+                       clear_cs3_bit(RS232_ON);
+               else
+                       set_cs3_bit(RS232_ON);
+       }
+ }
++/*
++ * Enable/Disable wake up events for this serial port.
++ * Obviously, we only support this on the normal COM port.
++ */
++static int simpad_uart_set_wake(struct uart_port *port, u_int enable)
++{
++      int err = -EINVAL;
++
++#if 0  // TODO: port management
++      if (port->mapbase == _Ser3UTCR0) {
++              if (enable)
++                      PWER |= PWER_GPIO23 | PWER_GPIO25 ; /* DCD and CTS */
++              else
++                      PWER &= ~(PWER_GPIO23 | PWER_GPIO25); /* DCD and CTS */
++              err = 0;
++      }
++#endif
++
++      return err;
++}
++
++#ifdef SIMPAD_UART_USE_IRQ
++static void simpad_uart1_dcd_intr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      /* Note: should only call this if something has changed */
++      uart_handle_dcd_change(port, !(GPLR & GPIO_UART1_DCD));
++}
++
++static void simpad_uart1_cts_intr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      /* Note: should only call this if something has changed */
++      uart_handle_cts_change(port, !(GPLR & GPIO_UART1_CTS));
++}
++
++static void simpad_uart3_dcd_intr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      /* Note: should only call this if something has changed */
++      uart_handle_dcd_change(port, !(GPLR & GPIO_UART3_DCD));
++}
++
++static void simpad_uart3_cts_intr(int irq, void *dev_id, struct pt_regs *regs)
++{
++      struct uart_port *port = dev_id;
++      /* Note: should only call this if something has changed */
++      uart_handle_cts_change(port, !(GPLR & GPIO_UART3_CTS));
++}
++#endif
++
++static int simpad_uart_open(struct uart_port *port)
++{
++      int ret = 0;
++#ifdef SIMPAD_UART_USE_IRQ
++      if (port->mapbase == _Ser1UTCR0) {
++              set_GPIO_IRQ_edge(GPIO_UART1_DCD|GPIO_UART1_CTS,
++                                GPIO_BOTH_EDGES);
++
++              ret = request_irq(IRQ_GPIO_UART1_DCD, simpad_uart1_dcd_intr,
++                                0, "UART1 DCD", port);
++              if (ret)
++                      return ret;
++
++              ret = request_irq(IRQ_GPIO_UART1_CTS, simpad_uart1_cts_intr,
++                                0, "UART1 CTS", port);
++              if (ret)
++                      free_irq(IRQ_GPIO_UART1_DCD, port);
++      }
++
++      else if (port->mapbase == _Ser3UTCR0) {
++              set_GPIO_IRQ_edge(GPIO_UART3_DCD|GPIO_UART3_CTS,
++                                GPIO_BOTH_EDGES);
++
++              ret = request_irq(IRQ_GPIO_UART3_DCD, simpad_uart3_dcd_intr,
++                                0, "UART3 DCD", port);
++              if (ret)
++                      return ret;
++
++              ret = request_irq(IRQ_GPIO_UART3_CTS, simpad_uart3_cts_intr,
++                                0, "UART3 CTS", port);
++              if (ret)
++                      free_irq(IRQ_GPIO_UART3_DCD, port);
++      }
++#endif
++      return ret;
++}
++
++static void simpad_uart_close(struct uart_port *port)
++{
++#ifdef SIMPAD_UART_USE_IRQ
++      if (port->mapbase == _Ser1UTCR0) {
++              free_irq(IRQ_GPIO_UART1_DCD, port);
++              free_irq(IRQ_GPIO_UART1_CTS, port);
++      }
++
++      else if (port->mapbase == _Ser3UTCR0) {
++              free_irq(IRQ_GPIO_UART3_DCD, port);
++              free_irq(IRQ_GPIO_UART3_CTS, port);
++      }
++#endif
++}
+ static struct sa1100_port_fns simpad_port_fns __initdata = {
+-      .pm     = simpad_uart_pm,
++      .set_mctrl      = simpad_uart_set_mctrl,
++      .get_mctrl      = simpad_uart_get_mctrl,
++      .pm             = simpad_uart_pm,
++      .set_wake       = simpad_uart_set_wake,
++      .open           = simpad_uart_open,
++      .close          = simpad_uart_close,
+ };
+ static void __init simpad_map_io(void)
+@@ -129,13 +283,32 @@
+         set_cs3_bit (EN1 | EN0 | LED2_ON | DISPLAY_ON | RS232_ON |
+                       ENABLE_5V | nRESET_SIMCARD);
++      sa1100_register_uart_fns(&simpad_port_fns);
++
+         //It is only possible to register 3 UART in serial_sa1100.c
+         sa1100_register_uart(0, 3);
+         sa1100_register_uart(1, 1);
++      // txd and rxd use their alternate function
+       GAFR |= (GPIO_UART_TXD | GPIO_UART_RXD);
++
++      // the control lines are gpio
++      GAFR &= ~(GPIO_UART1_RTS | GPIO_UART1_CTS | GPIO_UART1_DCD);
++      GAFR &= ~(GPIO_UART1_DSR | GPIO_UART1_DTR);
++      GAFR &= ~(GPIO_UART3_RTS | GPIO_UART3_CTS | GPIO_UART3_DCD);
++      GAFR &= ~(GPIO_UART3_DSR | GPIO_UART3_DTR);
++
++      // txd, rts and dtr are outputs
+       GPDR |= GPIO_UART_TXD;
++      GPDR |= GPIO_UART1_RTS | GPIO_UART3_RTS;
++      GPDR |= GPIO_UART1_DTR | GPIO_UART3_DTR;
++
++      // cts, dcd, dsr and rxd are inputs
++      GPDR &= ~(GPIO_UART1_CTS | GPIO_UART3_CTS);
++      GPDR &= ~(GPIO_UART1_DCD | GPIO_UART3_DCD);
++      GPDR &= ~(GPIO_UART1_DSR | GPIO_UART3_DSR);
+       GPDR &= ~GPIO_UART_RXD;
++
+       PPAR |= PPAR_UPR;
+         set_GPIO_IRQ_edge(GPIO_UCB1300_IRQ, GPIO_RISING_EDGE);
+--- linux-2.4.27/drivers/video/mq200fb.c~simpad-serial
++++ linux-2.4.27/drivers/video/mq200fb.c
+@@ -672,7 +672,7 @@
+ #ifdef CONFIG_SA1100_SIMPAD
+     GPDR |=   (1<<3);
+-    GAFR |=  ~(1<<3);
++    GAFR &=  ~(1<<3);
+     GPSR |=   (1<<3);
+ #endif
diff --git a/packages/linux/opensimpad/simpad-switches-input.patch b/packages/linux/opensimpad/simpad-switches-input.patch
new file mode 100644 (file)
index 0000000..792b1af
--- /dev/null
@@ -0,0 +1,131 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/misc/switches.h~simpad-switches-input
++++ linux-2.4.27/drivers/misc/switches.h
+@@ -25,4 +25,14 @@
+ extern int  switches_ucb1x00_init(void);
+ extern void switches_ucb1x00_exit(void);
++#ifdef CONFIG_SA1100_SIMPAD   
++#define SIMPAD_KEY_SUSPEND    0x0002
++#define SIMPAD_KEY_WWW                0x0008
++#define SIMPAD_KEY_ENTER      0x0010
++#define SIMPAD_KEY_UP         0x0020
++#define SIMPAD_KEY_DOWN               0x0040
++#define SIMPAD_KEY_LEFT               0x0080
++#define SIMPAD_KEY_RIGHT      0x0100
++#endif
++
+ #endif  /* !defined(_SWITCHES_H) */
+--- linux-2.4.27/drivers/misc/switches-core.c~simpad-switches-input
++++ linux-2.4.27/drivers/misc/switches-core.c
+@@ -16,6 +16,9 @@
+  *  11 September 2001 - UCB1200 driver framework support added.
+  *
+  *  19 December 2001 - separated out SA-1100 and UCB1x00 code.
++ *
++ *  3 July 2004 - Added generating of keyboard events. 
++ *                Florian Boor <florian@handhelds.org>
+  */
+ #include <linux/config.h>
+@@ -30,7 +33,11 @@
+ #include <linux/slab.h>
+ #include <linux/wait.h>
++#include <linux/input.h>
++
+ #include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/keyboard.h>
+ #include "switches.h"
+@@ -53,6 +60,19 @@
+ DECLARE_WAIT_QUEUE_HEAD(switches_wait);
+ LIST_HEAD(switches_event_queue);
++#ifdef CONFIG_INPUT
++static struct input_dev idev;
++      
++int 
++dummy_k_translate(unsigned char scancode, unsigned char *keycode, char raw_mode)
++{
++      *keycode = scancode;
++      return 1;
++}
++
++extern int (*k_translate)(unsigned char, unsigned char *, char);
++
++#endif
+ static ssize_t switches_read(struct file *file, char *buffer,
+                            size_t count, loff_t *pos)
+@@ -148,6 +168,31 @@
+ {
+       struct switches_action *action;
++#ifdef CONFIG_INPUT
++      /* create input events, the events to send depends on the platform */
++#ifdef CONFIG_SA1100_SIMPAD   
++      if (machine_is_simpad()) {
++              if (SWITCHES_COUNT(mask) > 0)
++              {
++                      if (mask->events[0] & SIMPAD_KEY_SUSPEND)
++                              input_report_key(&idev, KEY_POWER, (mask->states[0] & SIMPAD_KEY_SUSPEND) ? 0 : 1);
++                      if (mask->events[0] & SIMPAD_KEY_ENTER)
++                              input_report_key(&idev, KEY_ENTER, (mask->states[0] & SIMPAD_KEY_ENTER) ? 1 : 0);
++                      if (mask->events[0] & SIMPAD_KEY_UP)
++                              input_report_key(&idev, KEY_UP, (mask->states[0] & SIMPAD_KEY_UP) ? 1 : 0);
++                      if (mask->events[0] & SIMPAD_KEY_DOWN)
++                              input_report_key(&idev, KEY_DOWN, (mask->states[0] & SIMPAD_KEY_DOWN) ? 1 : 0);
++                      if (mask->events[0] & SIMPAD_KEY_LEFT)
++                              input_report_key(&idev, KEY_LEFT, (mask->states[0] & SIMPAD_KEY_LEFT) ? 1 : 0);
++                      if (mask->events[0] & SIMPAD_KEY_RIGHT)
++                              input_report_key(&idev, KEY_RIGHT, (mask->states[0] & SIMPAD_KEY_RIGHT) ? 1 : 0);
++                      if (mask->events[0] & SIMPAD_KEY_WWW)
++                              input_report_key(&idev, KEY_F10, (mask->states[0] & SIMPAD_KEY_WWW) ? 1 : 0);
++              }
++      }
++#endif
++#endif
++      /* take care of switches device */
+       if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) {
+               if ((action = (struct switches_action *)
+@@ -197,6 +242,21 @@
+               return -EIO;
+       }
++#ifdef CONFIG_INPUT
++      /* init input driver stuff */
++      k_translate = dummy_k_translate;
++      idev.evbit[0] = BIT(EV_KEY); /* handle key events */
++
++      idev.keybit[LONG(KEY_POWER)] |= BIT(KEY_POWER);
++      idev.keybit[LONG(KEY_UP)] |= BIT(KEY_UP);
++      idev.keybit[LONG(KEY_DOWN)] |= BIT(KEY_DOWN);
++      idev.keybit[LONG(KEY_LEFT)] |= BIT(KEY_LEFT);
++      idev.keybit[LONG(KEY_RIGHT)] |= BIT(KEY_RIGHT);
++      idev.keybit[LONG(KEY_ENTER)] |= BIT(KEY_ENTER);
++      idev.keybit[LONG(KEY_F10)] |= BIT(KEY_F10);
++
++      input_register_device(&idev);
++#endif        
+       printk("Console switches initialized\n");
+       return 0;
+@@ -214,6 +274,10 @@
+       switches_ucb1x00_exit();
+ #endif
++#ifdef CONFIG_INPUT
++      input_unregister_device(&idev);
++#endif
++      
+       if (misc_deregister(&switches_misc) < 0)
+               printk(KERN_ERR "%s: unable to deregister misc device\n",
+                      SWITCHES_NAME);
diff --git a/packages/linux/opensimpad/simpad-switches-input2.patch b/packages/linux/opensimpad/simpad-switches-input2.patch
new file mode 100644 (file)
index 0000000..34529ac
--- /dev/null
@@ -0,0 +1,41 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/misc/switches-core.c~simpad-switches-input2
++++ linux-2.4.27/drivers/misc/switches-core.c
+@@ -66,7 +66,32 @@
+ int 
+ dummy_k_translate(unsigned char scancode, unsigned char *keycode, char raw_mode)
+ {
+-      *keycode = scancode;
++      if (scancode == KEY_UP) 
++              *keycode = 144;
++      else if (scancode == KEY_LEFT) 
++              *keycode = 146;
++      else if (scancode == KEY_RIGHT) 
++              *keycode = 151;
++      else if (scancode == KEY_DOWN) 
++              *keycode = 161;
++      else if (scancode == 144) 
++              *keycode = KEY_UP;
++      else if (scancode == 146) 
++              *keycode = KEY_LEFT;
++      else if (scancode == 151) 
++              *keycode = KEY_RIGHT;
++      else if (scancode == 161) 
++              *keycode = KEY_DOWN;
++      else if (scancode == KEY_KP8) 
++              *keycode = KEY_UP;
++      else if (scancode == KEY_KP4) 
++              *keycode = KEY_LEFT;
++      else if (scancode == KEY_KP6) 
++              *keycode = KEY_RIGHT;
++      else if (scancode == KEY_KP2) 
++              *keycode = KEY_DOWN;
++      else
++              *keycode = scancode;
+       return 1;
+ }
diff --git a/packages/linux/opensimpad/simpad-ts-noninput.patch b/packages/linux/opensimpad/simpad-ts-noninput.patch
new file mode 100644 (file)
index 0000000..20a908e
--- /dev/null
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/misc/ucb1x00-ts.c~simpad-ts-noninput
++++ linux-2.4.27/drivers/misc/ucb1x00-ts.c
+@@ -35,7 +35,7 @@
+ /*
+  * Define this if you want the UCB1x00 stuff to talk to the input layer
+  */
+-#ifdef CONFIG_INPUT
++#if defined(CONFIG_INPUT) && !defined(CONFIG_SA1100_SIMPAD)
+ #define USE_INPUT
+ #else
+ #undef USE_INPUT
diff --git a/packages/linux/opensimpad/sound-volume-reversed.patch b/packages/linux/opensimpad/sound-volume-reversed.patch
new file mode 100644 (file)
index 0000000..495d8f9
--- /dev/null
@@ -0,0 +1,16 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/drivers/misc/ucb1x00-audio.c~sound-volume-reversed
++++ linux-2.4.27/drivers/misc/ucb1x00-audio.c
+@@ -97,7 +97,7 @@
+                               ucba->output_level = gain | gain << 8;
+                               ucba->mod_cnt++;
+                               ucba->ctrl_b = (ucba->ctrl_b & 0xff00) |
+-                                             ((gain * 31) / 100);
++                                             (((100-gain) * 31) / 100);
+                               ucb1x00_reg_write(ucba->ucb, UCB_AC_B,
+                                                 ucba->ctrl_b);
+                               ret = 0;
diff --git a/packages/linux/opensimpad/support-128mb-flash.patch b/packages/linux/opensimpad/support-128mb-flash.patch
new file mode 100644 (file)
index 0000000..2a6b1bd
--- /dev/null
@@ -0,0 +1,25 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- linux-2.4.27/arch/arm/mach-sa1100/simpad.c~support-128mb-flash
++++ linux-2.4.27/arch/arm/mach-sa1100/simpad.c
+@@ -83,11 +83,16 @@
+ {
+ #ifdef CONFIG_SA1100_SIMPAD_SINUSPAD
+       SET_BANK( 0, 0xc0000000, 32*1024*1024 );
++      mi->nr_banks = 1;
+ #else
+       SET_BANK( 0, 0xc0000000, 64*1024*1024 );
+-#endif
+       mi->nr_banks = 1;
++#endif
++#ifdef CONFIG_SA1100_SIMPAD_128M
++      SET_BANK( 1, 0xc8000000, 64*1024*1024 );
++      mi->nr_banks = 2;
++#endif
+       setup_ramdisk( 1, 0, 0, 8192 );
+       setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 );
+ }
index 02583c0..8461683 100644 (file)
@@ -20,11 +20,11 @@ SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-${KV}.tar.bz2 \
            file://defconfig-${MACHINE} \
           http://www.openswan.org/download/openswan-2.2.0-kernel-2.4-klips.patch.gz;patch=1 \
            file://mipv6-1.1-v2.4.25.patch;patch=1 \
-           file://simpad-backlight-if.diff;patch=1;pnum=0 \
-           file://simpad-switches-input.diff;patch=1;pnum=0 \
-           file://simpad-switches-input2.diff;patch=1;pnum=0 \
+           file://simpad-backlight-if.patch;patch=1 \
+           file://simpad-switches-input.patch;patch=1 \
+           file://simpad-switches-input2.patch;patch=1 \
            file://simpad-apm.diff;patch=1;pnum=0 \
-           file://simpad-ts-noninput.diff;patch=1;pnum=0 \
+           file://simpad-ts-noninput.patch;patch=1 \
            file://simpad-pm-updates.patch;patch=1;pnum=0 \
            file://support-128mb-flash.patch;patch=1"
 
diff --git a/packages/linux/opensimpad_2.4.27-vrs1-pxa1-jpm1.bb b/packages/linux/opensimpad_2.4.27-vrs1-pxa1-jpm1.bb
new file mode 100644 (file)
index 0000000..3a2a1f8
--- /dev/null
@@ -0,0 +1,80 @@
+DESCRIPTION = "Linux kernel for the SIEMENS SIMpad family of devices."
+MAINTAINER = "Michael 'Mickey' Lauer <mickey@Vanille.de>"
+SECTION = "kernel"
+LICENSE = "GPL"
+KV = "${@bb.data.getVar('PV',d,True).split('-')[0]}"
+VRSV = "${@bb.data.getVar('PV',d,True).split('-')[1]}"
+PXAV = "${@bb.data.getVar('PV',d,True).split('-')[2]}"
+JPMV = "${@bb.data.getVar('PV',d,True).split('-')[3]}"
+USBV= "usb20040610"
+PR = "r1"
+
+FILESDIR = "${@os.path.dirname(bb.data.getVar('FILE',d,1))}/opensimpad-${PV}"
+
+SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-${KV}.tar.bz2 \
+           file://${KV}-${VRSV}.patch;patch=1 \
+           file://${KV}-${VRSV}-${PXAV}.patch;patch=1 \
+           file://${KV}-${VRSV}-${PXAV}-${JPMV}.patch;patch=1 \
+           file://${KV}-mh1.patch;patch=1 \
+           file://sound-volume-reversed.patch;patch=1 \
+          file://disable-pcmcia-probe.patch;patch=1 \
+           file://mkdep.patch;patch=1 \
+           file://defconfig-${MACHINE} \
+          http://www.openswan.org/download/openswan-2.2.0-kernel-2.4-klips.patch.gz;patch=1 \
+           file://mipv6-1.1-v${KV}.patch;patch=1 \
+           file://simpad-backlight-if.patch;patch=1 \
+           file://simpad-switches-input.patch;patch=1 \
+           file://simpad-switches-input2.patch;patch=1 \
+           file://simpad-apm.patch;patch=1 \
+           file://simpad-ts-noninput.patch;patch=1 \
+           file://simpad-pm-updates.patch;patch=1 \
+           file://support-128mb-flash.patch;patch=1 \
+           file://simpad-proc-sys-board.patch;patch=1 \
+           file://simpad-serial.patch;patch=1 \
+           file://mppe-20040216.patch;patch=1 \
+           file://sa1100-usb-tcl1.patch;patch=1 \
+"
+# This applies right after the jpm patch but is useless until we
+# have sa1100_udc.c
+#           file://${KV}-${VRSV}-${USBV}.patch;patch=1 \
+
+# apply this when we have a patch that allows building with gcc 3.x:
+# SRC_URI_append = file://gcc-3.3.patch;patch=1
+# SRC_URI_append = file://machtune-args.patch;patch=1
+
+S = "${WORKDIR}/linux-${KV}"
+
+inherit kernel
+
+KERNEL_CCSUFFIX = "-3.3.3"
+COMPATIBLE_HOST = "arm.*-linux"
+
+SIMPAD_MEM     = ${@bb.data.getVar("SIMPAD_MEMORY_SIZE",d,1)  or "32"}
+SIMPAD_RD      = ${@bb.data.getVar("SIMPAD_RAMDISK_SIZE",d,1) or "32"}
+export CMDLINE = ${@bb.data.getVar("SIMPAD_CMDLINE",d,1) or  "mtdparts=sa1100:512k(boot),1m(kernel),14848k(root),-(home) console=ttySA root=1f02 noinitrd jffs2_orphaned_inodes=delete rootfstype=jffs2 "}
+#EXTRA_OEMAKE = ""
+
+module_conf_sa1100_ir = "alias irda0 sa1100_ir"
+
+do_configure() { 
+       install -m 0644 ${WORKDIR}/defconfig-${MACHINE} ${S}/.config || die "No default configuration for ${MACHINE} available."
+              
+       mem=${SIMPAD_MEM}
+       rd=${SIMPAD_RD}
+        mempos=`echo "obase=16; $mem * 1024 * 1024" | bc`
+        rdsize=`echo "$rd * 1024" | bc`
+        total=`expr $mem + $rd`
+        addr=`echo "obase=16; ibase=16; C0000000 + $mempos" | bc`
+
+        if [ "$rd" == "0" ]
+        then
+                echo "# CONFIG_MTD_MTDRAM is not set" >> ${S}/.config
+        else
+                echo "CONFIG_MTD_MTDRAM=y"           >> ${S}/.config
+                echo "CONFIG_MTDRAM_TOTAL_SIZE=$rdsize"     >> ${S}/.config
+                echo "CONFIG_MTDRAM_ERASE_SIZE=1"           >> ${S}/.config
+                echo "CONFIG_MTDRAM_ABS_POS=$addr"          >> ${S}/.config
+        fi
+       echo "CONFIG_CMDLINE=\"${CMDLINE} mem=${mem}M\"" >> ${S}/.config
+        oe_runmake oldconfig
+}