diff -urN pcmcia-cs-3.1.14/Configure pcmcia-cs-3.1.14-arm/Configure
--- pcmcia-cs-3.1.14/Configure	Thu Mar 30 22:58:41 2000
+++ pcmcia-cs-3.1.14-arm/Configure	Wed Apr 26 16:53:45 2000
@@ -78,6 +78,7 @@
     echo "  --debug=FLAGS      set compiler flags for debugging"
     echo "  --uflags=FLAGS     set compiler flags for user-mode tools"
     echo "  --kflags=FLAGS     set compiler flags for kernel modules"
+    echo "  --arch=ARCH        build for target architecture"
     echo "  --{no}trust        disable or enable trusted user tools"
     echo "  --{no}cardbus      disable or enable CardBus card support"
     echo "  --{no}pnp          disable or enable PnP BIOS support"
@@ -98,6 +99,7 @@
     --moddir=*)		arg MODDIR $1		;;
     --cc=*)		arg CC "$1"		;;
     --ld=*)		arg LD "$1"		;;
+    --arch=*)           arg ARCH "$1"           ;;
     --debug=*)		arg PCDEBUG "$1"	;;
     --kflags=*)		arg KFLAGS "$1"		;;
     --uflags=*)		arg UFLAGS "$1"		;;
@@ -265,6 +267,13 @@
 CUR_D=`uname -v | sed -e 's/^#[0-9]* //;s/SMP //'`
 CUR_D=`echo $CUR_D | sed -e 's/\(:[0-9][0-9]\) .* \([12][90]\)/\1 \2/'`
 echo "The current kernel build date is $CUR_D."
+if [ "$CC" ] ; then
+    write_str CC
+fi
+if [ "$LD" ] ; then
+    write_str LD
+fi
+
 SRC_VERSION="unknown";
 if [ -f $LINUX/include/linux/compile.h ] ; then
     SRC_VERSION=`grep UTS_VERSION $LINUX/include/linux/compile.h |
@@ -423,8 +432,9 @@
 	fi
     fi
     if [ $ARCH = "arm" ] ; then
-	printflag "Brutus architecture" CONFIG_ARCH_BRUTUS
-	printflag "Itsy target platform" CONFIG_ITSY
+	printflag "Assabet target platform"       CONFIG_SA1100_ASSABET
+	printflag "Brutus target platform"        CONFIG_SA1100_BRUTUS
+	printflag "Itsy target platform (legacy)" CONFIG_ITSY
     fi
     printflag "/proc filesystem support" CONFIG_PROC_FS
     if [ "$BIGMEM" ] ; then
@@ -547,7 +557,8 @@
 	    configcheck $C
 	done
 	if [ $ARCH = "arm" ] ; then
-	    configcheck CONFIG_ARCH_BRUTUS
+	    configcheck CONFIG_SA1100_ASSABET
+	    configcheck CONFIG_SA1100_BRUTUS
 	    configcheck CONFIG_ITSY
 	fi
 	if [ "$BIGMEM" ] ; then
@@ -588,7 +599,12 @@
 
 AFLAGS=
 CONFIG_ISA=y
+CONFIG_VIRTUAL_BUS=n
 CONFIG_UID16=y
+if [ $ARCH = "arm" ] ; then
+    CONFIG_ISA=n
+    CONFIG_VIRTUAL_BUS=y
+fi
 if [ $ARCH = "ppc" ] ; then
     CONFIG_ISA=n
     AFLAGS="-fno-builtin -msoft-float"
@@ -606,6 +622,7 @@
 write_str HOST_ARCH
 write_str AFLAGS
 write_bool CONFIG_ISA
+write_bool CONFIG_VIRTUAL_BUS
 if [ $VERSION_CODE -ge `version 2 3 40` ] ; then
     write_bool CONFIG_UID16
 fi
diff -urN pcmcia-cs-3.1.14/README.sa1100 pcmcia-cs-3.1.14-arm/README.sa1100
--- pcmcia-cs-3.1.14/README.sa1100	Wed Dec 31 19:00:00 1969
+++ pcmcia-cs-3.1.14-arm/README.sa1100	Thu Apr 27 18:06:13 2000
@@ -0,0 +1,349 @@
+README - Card Services for StrongARM
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+John G Dorsey <john+@cs.cmu.edu>
+Updated: 27 April, 2000
+
+
+The Card Services for StrongARM project is an attempt to add support
+for PCMCIA to SA-1100-based Linux systems. The file you are reading is
+part of a patch which contains several modifications to the standard
+Card Services distribution that enable this support. The patch is
+distributed on the World Wide Web at:
+
+  http://www.cs.cmu.edu/~wearable/software/pcmcia-arm.html
+
+
+Card Services for StrongARM is not mature, and would benefit from any
+help developers are willing to contribute. The next few paragraphs
+introduce the aspects of the existing Card Services code which must
+be addressed in order to support the StrongARM. If you would like to
+approach any of these, or have discovered additional requirements,
+please contact John Dorsey <john+@cs.cmu.edu>.
+
+Card Services for Linux provides an organized software architecture
+which coordinates PCMCIA device drivers, socket controller drivers,
+the Linux kernel, and user-mode utilities. In general, the PCMCIA
+device driver code knows nothing about the specific PCMCIA
+implementation through which it talks to the card. Card Services
+provides an abstraction through which a variety of socket controllers,
+such as the i82365, may be used interchangeably.
+
+The StrongARM SA-1100 processor (and related models, such as the
+SA-1110) includes on-chip support for PCMCIA, thus eliminating the
+need for an external controller. With a modest amount of external
+logic, the memory spaces of PCMCIA devices appear in the physical
+address space of the processor. Various control interfaces, such as
+device voltage configuration, can be provided through a chipselect
+register which also appears in the physical address space of the
+processor.
+
+Because the SA-1100 can perform the operations expected of a socket
+controller, the code to support it will be placed in a module
+alongside i82365.o and tcic.o. The new module will implement all of
+the interfaces required of a socket driver (see "Linux PCMCIA
+Programmer's Guide," section 9, "The Socket Driver Layer"). See
+modules/sa1100.[ch] for the current status of this component.
+
+Although the StrongARM interface to PCMCIA is elegant, it is
+unfortunately underconstrained in the amount of freedom it grants
+board designers. A variety of features, from interrupt pins to memory
+locations, must all be known to the driver yet can vary from board to
+board. The existing Card Services code does not account for this,
+permitting user-specified I/O ports and memory ranges, but having no
+provision for listing, say, card detect pins. In order to permit
+binary distributions of the sa1100 module, support for these
+board-specific characteristics has been moved into the kernel proper.
+See linux/Documentation/arm/SA1100/PCMCIA for more information.
+
+Below is a quick itemization of the changes to Card Services found in
+this patch. Changes listed as "build-related" are simply those that
+were required to get Card Services to compile with the (GNU) ARM
+toolchain. "StrongARM" changes should be self-explanatory. "Itsy"
+changes deal with oddities of the Itsy that may or may not be relevant
+to developers with other boards.
+
+
+Build-related Fixes:
+
+- copy_to_user()/copy_from_user() (memcpy_tofs()/memcpy_fromfs())
+  have return type void in the 2.0.30 kernel source; added
+  preprocessor controls around these in wavelan_cs.c and netwave_cs.c
+  (compiler error for not ignoring return value). Return type is
+  unsigned long in newer kernels.
+
+- preprocessor controls (CONFIG_PCI) added around PCI shortcut
+  routines in i82365.c (these manipulate fields of socket_info_t which
+  are undefined when not configured for PCI)
+
+- Configure wasn't emitting CC and LD assignments to config.mk; added
+  calls to write_str to correct
+
+- cardmgr.c fails to compile due to the definition of struct fd_set
+  using the ARM egcs distribution; added conditional in cs_types.h to
+  prevent linux/types.h from being included when building for ARM
+
+- when cross-compiling, can't run pack_cis on the build host; added
+  --arch option to Configure, added conditional to etc/cis/Makefile so
+  that *.dat targets aren't built during cross-compile. Should change
+  --arch argument for StrongARM target to "sa1100" instead of "arm"
+  (consistent with the output of `uname -m`).
+
+- added sa1100.o module to build
+
+
+StrongARM Changes:
+
+- PCMCIA is mapped into the full 32-bit address space on the
+  StrongARM, and therefore cannot be indexed using a 16-bit u_short;
+  ioaddr_t (in cs_types.h) and pccard_io_map (in ss.h) have been
+  changed to reflect this
+
+- a driver ISR wrapper facility has been added to the Socket Services
+  interface in order to handle the StrongARM edge detect register on
+  behalf of device drivers (new flags to bus_request_irq(), as well)
+
+- support for RES_ATTR_RANGE and RES_IOMEM_RANGE added to
+  modules/rsrc_mgr.c (and removed. -jd)
+
+- modules/sa1100.[ch] socket driver added
+
+- CONFIG_VIRTUAL_BUS output added
+
+
+Itsy-specific Changes:
+
+- vremap()/ioremap() doesn't work on the Itsy, so we have to apply the
+  following hack:
+
+  1. construct a virtual mapping of the SA-1100 PCMCIA memory space
+     during kernel initialization (handled by a separate patch to the
+     Itsy kernel)
+
+  2. when cardmgr reads its configuration file, it passes down
+     the physical address ranges corresponding to the various portions
+     of the SA-1100 PCMCIA memory space
+
+  3. "normal" StrongARM systems should ioremap() these addresses
+     before using them, but since we've already done this in (1),
+     we simply ask for the translation
+
+  4. bus_ops.h has been modified to reflect the fact that the mapping
+     has already been performed (bus_ioremap() is a no-op)
+
+
+- the console beep on the Itsy doesn't work; beep() in cardmgr.c has
+  been modified to generate a syslog message instead
+
+- added checks for CONFIG_ARCH_BRUTUS and CONFIG_ITSY to Configure
+
+- Itsy support has been removed in patch 3.1.14-arm-4
+
+
+
+
+Card Services for StrongARM was originally developed on an Itsy using
+the Carnegie Mellon PCMCIA daughtercard. The first device to be
+brought up was a Lucent WaveLAN/IEEE (wavelan2_cs) card. Although the
+code for the WaveLAN device is proprietary and not distributable, it
+should be said that the patch which allows the core library to work on
+the StrongARM is 1718 bytes, and deals entirely with the Makefile. The
+patch which deals with the released-source portion of the driver is
+1238 bytes and is also concerned purely with the Makefile.
+
+In the course of making Card Services for StrongARM work on other
+boards, newer kernels, and different devices, there are several bugs
+(or at least, questionable design choices) of which to be aware:
+
+- my experience with the 3c589 (attribute memory maps fine, I/O memory
+  doesn't) suggests that there's still something lacking in the memory
+  configuration code (same experience on Socket LP-E CF+ card. -jd)
+
+- at one point, I thought compile-time configuration of I/O pins, &c.,
+  was the way to go. The awk scripts which implemented this were
+  removed in pcmcia-cs-3.1.8-arm-2.
+
+- at another point, I thought run-time configuration from userland was
+  the way to go (through insmod options). This was removed in
+  pcmcia-cs-3.1.14-arm-4, but the sdconfig awk script remains.
+
+- there are core clock frequency extraction routines for two different
+  oscillator frequencies: 3.6864MHz and 3.5795MHz. The Itsy uses the
+  former, and that's what's called in the code.
+
+- once you set the PCMCIA memory access times, you can't change the
+  processor core clock frequency
+
+- at one point I thought that the socket driver should see things like
+  attribute memory range configuration early, since these ranges must
+  be handled more precisely on the SA-1100 than if we were using a
+  conventional controller. The code was removed in
+  pcmcia-cs-3.1.8-arm-2, but is still floating about in the previous
+  patch.
+
+- inw()/outw()/insw()/outsw() deal in ISA words, which are 16 bits.
+  Your _kernel_ source should properly define these using the ARM
+  ldrh/strh instructions. DO NOT TRUST your compiler to do this for
+  you. (See the Itsy kernel patch for an example.) (2.3.99 seems to do
+  the right thing. -jd)
+
+- on Assabet, removing a card locks the machine; I don't know why.
+
+
+
+Now, for some advice on building. I compile on an i686-pc-linux-gnu
+system, and use `arm-unknown-linuxelf-gcc', &c. There are some
+modifications to `Configure' which cannot yet be used interactively,
+so rather than running `make config', you must run `Configure' and
+provide several options:
+
+  $ ./Configure --cc=arm-linux-gcc \
+                --ld=arm-linux-ld  \
+                --arch=arm         \
+                --debug=-DPCMCIA_DEBUG=4
+
+(`Configure' used to prompt for many of these, but they started to
+disappear around 3.1.0 or so.) The big deal is --arch, which sets up
+some tests and conditional builds.
+
+You will be presented with the interactive configuration questions, a
+la `make config'. Give the absolute path to your kernel source, say no
+to CardBus support, and set kernel-specific options by reading from
+the source tree (2). Just for comparison, here's what `Configure'
+comes up with after looking at my Itsy (4.1) 2.0.30 tree:
+
+Kernel configuration options:
+    Symmetric multiprocessing support is disabled.
+    PCI BIOS support is disabled.
+    Advanced Power Management (APM) support is disabled.
+    SCSI support is disabled.
+    Networking support is enabled.
+     Radio network interface support is disabled.
+     Token Ring device support is disabled.
+     Fast switching is disabled.
+    Module version checking is enabled.
+    PCMCIA IDE device support is disabled.
+    Brutus architecture is enabled.
+    Itsy target platform is enabled.
+    /proc filesystem support is enabled.
+
+
+
+That should be it; cross your fingers and `make all'.
+
+(Days pass.)
+
+Now that it actually compiles, a word on configuration. Because
+filesystem space is somewhat scarce on the Itsy, my setup is fairly
+minimal:
+
+  /lib/modules/`uname -r'/pcmcia:
+
+      ds.o
+      pcmcia_core.o
+      sa1100.o
+      wavelan2_cs.o
+      3c589.o
+      ...
+
+  /etc/pcmcia:
+
+      config
+      config.opts
+      network
+      network.opts
+      shared
+
+  /etc/rc.d/init.d:
+
+      pcmcia  (rc.pcmcia in the distribution)
+
+  /etc/rc.d/rc3.d:
+
+      S66pcmcia -> ../init.d/pcmcia
+
+
+
+That should be it! You're ready to start hacking. Here's a pointer to
+help you get started: begin by understanding how the various PCMCIA
+facilities work on your board, then worry about how to control those
+facilities using the Card Services framework. For example, this
+(hardwired) code fragment could be tossed in, say,
+sa1100_pcmcia_init() to sanity check that slot 1 attribute memory is
+accessible on an Itsy (ONLY):
+
+  /* (original by Ivo Clarysse) */
+
+  unsigned char row[8];
+  unsigned long j, ptr;
+  unsigned short *cs;
+
+  cs=(unsigned short *)cs_ioremap(_StMemBnk3, PAGE_SIZE);
+  ptr=(unsigned long)cs_ioremap(_PCMCIA1Attr, PAGE_SIZE);
+
+  MECR_BSA_SET(MECR, 1, 10);
+   
+  *cs=0x0500;
+  udelay(SA1100_PCMCIA_RESET_WAIT_USECS);
+
+  *cs=0x1500;
+  udelay(SA1100_PCMCIA_RESET_WAIT_USECS);
+    
+  *cs=0x0500;
+  udelay(SA1100_PCMCIA_RESET_WAIT_USECS);
+    
+  for(j=0; j<32; ++j){
+    row[j%8]=inb(ptr+(2*j));
+    if((j%8)==7)
+      printk(KERN_ERR "cis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+             row[0], row[1], row[2], row[3], row[4], row[5], row[6], 
+             row[7]);
+  }
+
+
+The output for a 3c589(D):          ...for WaveLAN/IEEE:
+
+  cis: 01 02 00 ff 17 03 43 02        cis: 01 03 00 00 ff 17 04 67
+  cis: ff 20 04 01 01 89 05 21        cis: 5a 08 ff 1d 05 01 67 5a
+  cis: 02 06 00 15 3a 04 01 33        cis: 08 ff 15 50 05 00 4c 75
+  cis: 43 6f 6d 20 43 6f 72 70        cis: 63 65 6e 74 20 54 65 63
+
+
+Please contact me (<john+@cs.cmu.edu>) with any questions that arise
+while playing with the code.
+
+
+Thanks:
+
+  Steve Schlosser (for the foundation on which this code builds)
+  Ivo Clarysse    (for another version of Brutus PCMCIA)
+  Bill Hamburgen  (for the Itsy!)
+  Debby Wallach   (for Itsy kernel help)
+  Jan Marteijn    (for the HCF code)
+  David Hinds     (for structural advice)
+
+
+Changes:
+
+  pcmcia-cs-3.1.14-arm-4
+
+    - experimental release only
+    - moved to kernel 2.3.99
+    - eliminated support for (2.0.30) Itsy
+    - exchanged userland configuration interface for in-kernel mechanism
+
+  pcmcia-cs-3.1.9-arm-3
+
+    - extensive retooling of pins/registers configuration interface
+    - added dual-slot support to resource manager
+    - added low-power voltage sense check
+
+  pcmcia-cs-3.1.8-arm-2
+
+    - removed compile-time configuration for GPIO pins, memory, &c.
+    - removed `config.opts' configuration for pins, masks, &c.
+    - added `insmod' configuration for pins, masks, &c.
+    - removed manipulation of attribute memory addresses from sa1100
+
+  pcmcia-cs-3.1.8-arm-1
+
+    - initial release
diff -urN pcmcia-cs-3.1.14/cardmgr/lex_config.c pcmcia-cs-3.1.14-arm/cardmgr/lex_config.c
--- pcmcia-cs-3.1.14/cardmgr/lex_config.c	Tue Dec 28 19:57:43 1999
+++ pcmcia-cs-3.1.14-arm/cardmgr/lex_config.c	Fri Apr 21 18:38:28 2000
@@ -715,7 +715,7 @@
 YY_DECL
 	{
 	register yy_state_type yy_current_state;
-	register char *yy_cp, *yy_bp;
+	register char *yy_cp = NULL, *yy_bp = NULL;
 	register int yy_act;
 
 #line 63 "lex_config.l"
diff -urN pcmcia-cs-3.1.14/clients/3c589_cs.c pcmcia-cs-3.1.14-arm/clients/3c589_cs.c
--- pcmcia-cs-3.1.14/clients/3c589_cs.c	Mon Feb 28 18:02:28 2000
+++ pcmcia-cs-3.1.14-arm/clients/3c589_cs.c	Thu Apr 20 21:41:48 2000
@@ -392,7 +392,9 @@
     link->io.IOAddrLines = 16;
     for (i = j = 0; j < 0x400; j += 0x10) {
 	if (multi && (j & 0x80)) continue;
+#ifndef __arm__
 	link->io.BasePort1 = j ^ 0x300;
+#endif
 	i = CardServices(RequestIO, link->handle, &link->io);
 	if (i == CS_SUCCESS) break;
     }
diff -urN pcmcia-cs-3.1.14/clients/pcnet_cs.c pcmcia-cs-3.1.14-arm/clients/pcnet_cs.c
--- pcmcia-cs-3.1.14/clients/pcnet_cs.c	Fri Mar 31 18:37:58 2000
+++ pcmcia-cs-3.1.14-arm/clients/pcnet_cs.c	Thu Apr 27 14:01:28 2000
@@ -47,6 +47,7 @@
 #include <../drivers/net/8390.h>
 
 #include <pcmcia/version.h>
+#include <pcmcia/bus_ops.h>
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
@@ -70,6 +71,8 @@
 
 static char *if_names[] = { "auto", "10baseT", "10base2"};
 
+static struct bus_operations *bus_ops=NULL;
+
 #ifdef PCMCIA_DEBUG
 static int pc_debug = PCMCIA_DEBUG;
 MODULE_PARM(pc_debug, "i");
@@ -439,23 +443,23 @@
 	return NULL;
     }
 
-    virt = ioremap(req.Base, req.Size);
+    virt = bus_ioremap(bus_ops, req.Base, req.Size);
     mem.Page = 0;
     for (i = 0; i < NR_INFO; i++) {
 	mem.CardOffset = hw_info[i].offset & ~(req.Size-1);
 	CardServices(MapMemPage, link->win, &mem);
 	base = &virt[hw_info[i].offset & (req.Size-1)];
-	if ((readb(base+0) == hw_info[i].a0) &&
-	    (readb(base+2) == hw_info[i].a1) &&
-	    (readb(base+4) == hw_info[i].a2))
+	if ((bus_readb(bus_ops, base+0) == hw_info[i].a0) &&
+	    (bus_readb(bus_ops, base+2) == hw_info[i].a1) &&
+	    (bus_readb(bus_ops, base+4) == hw_info[i].a2))
 	    break;
     }
     if (i < NR_INFO) {
 	for (j = 0; j < 6; j++)
-	    dev->dev_addr[j] = readb(base + (j<<1));
+	    dev->dev_addr[j] = bus_readb(bus_ops, base + (j<<1));
     }
     
-    iounmap(virt);
+    bus_iounmap(bus_ops, virt);
     j = CardServices(ReleaseWindow, link->win);
     if (j != CS_SUCCESS)
 	cs_error(link->handle, ReleaseWindow, j);
@@ -502,10 +506,13 @@
     udelay(10000);
 
     for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
-	outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+      //outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+      bus_outb(bus_ops, program_seq[i].value, ioaddr + program_seq[i].offset);
 
-    for (i = 0; i < 32; i++)
-	prom[i] = inb(ioaddr + PCNET_DATAPORT);
+    for (i = 0; i < 32; i++){
+	prom[i] = bus_inb(bus_ops, ioaddr + PCNET_DATAPORT);
+	printk(KERN_ERR "%s(): prom[%d] %x\n", __FUNCTION__, i, prom[i]);
+    }
     for (i = 0; i < NR_INFO; i++) {
 	if ((prom[0] == hw_info[i].a0) &&
 	    (prom[2] == hw_info[i].a1) &&
@@ -577,8 +584,13 @@
     if (link->io.BasePort1 == 0) {
 	link->io.IOAddrLines = 16;
 	for (j = 0; j < 0x400; j += 0x20) {
+#ifdef __arm__
+	    link->io.BasePort1 = j;
+	    link->io.BasePort2 = j + 0x10;
+#else
 	    link->io.BasePort1 = j ^ 0x300;
 	    link->io.BasePort2 = (j ^ 0x300) + 0x10;
+#endif
 	    ret = CardServices(RequestIO, link->handle, &link->io);
 	    if (ret == CS_SUCCESS) return ret;
 	}
@@ -823,6 +839,7 @@
 	break;
     case CS_EVENT_CARD_INSERTION:
 	link->state |= DEV_PRESENT;
+	bus_ops=args->bus;
 	pcnet_config(link);
 	break;
     case CS_EVENT_PM_SUSPEND:
@@ -888,7 +905,8 @@
     MOD_INC_USE_COUNT;
 
     set_misc_reg(dev);
-    request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
+    bus_request_irq(bus_ops, dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info,
+		    dev);
 
     /* Start by assuming the link is bad */
     info->state = 1;
@@ -937,13 +955,33 @@
 {
     ioaddr_t nic_base = dev->base_addr;
     int i;
+    unsigned char c;
 
     ei_status.txing = ei_status.dmaing = 0;
 
+    /* Note to developers attempting to debug this driver: on a ThinkPad,
+     * this next read from PCNET_RESET returns 0, consistently. (On my
+     * Assabet, it returns 0 occasionally.)
+     */
+
+#ifdef THIS_CODE_ILLUSTRATES_A_FLAW_IN_THE_STRONGARM_PORT
     outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
+#endif
+    for(i=0; i<20; ++i){
+      c = inb(nic_base+PCNET_RESET);
+      printk(KERN_ERR "%s(): reset is %x at %08x + %x\n", __FUNCTION__, c,
+	     nic_base, PCNET_RESET);
+    }
+    outb(c, nic_base+PCNET_RESET);
 
     for (i = 0; i < 100; i++) {
-	if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+      c = inb_p(nic_base+EN0_ISR);
+      printk(KERN_ERR "%s(): i %d  ISR %x at %08x + %x (%x)\n", __FUNCTION__,
+	     i, c, nic_base, EN0_ISR, ENISR_RESET);
+#ifdef THIS_CODE_ILLUSTRATES_A_FLAW_IN_THE_STRONGARM_PORT
+      if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+#endif
+      if((c & ENISR_RESET) != 0)
 	    break;
 	udelay(100L);
     }
@@ -1305,11 +1343,11 @@
     odd = (c & 1); c >>= 1;
 
     if (c) {
-	do { *d++ = readw_ns(s++); } while (--c);
+	do { *d++ = bus_readw_ns(bus_ops, s++); } while (--c);
     }
     /* get last byte by fetching a word and masking */
     if (odd)
-	*((u_char *)d) = readw(s) & 0xff;
+	*((u_char *)d) = bus_readw(bus_ops, s) & 0xff;
 }
 
 static void copyout(u_char *dest, const u_char *src, int c)
@@ -1322,11 +1360,12 @@
     odd = (c & 1); c >>= 1;
 
     if (c) {
-	do { writew_ns(*s++, d++); } while (--c);
+	do { bus_writew_ns(bus_ops, *s++, d++); } while (--c);
     }
     /* copy last byte doing a read-modify-write */
     if (odd)
-	writew((readw(d) & 0xff00) | *(u_char *)s, d);
+	bus_writew(bus_ops, (bus_readw(bus_ops, d) & 0xff00) |
+		   *(u_char *)s, d);
 }
 
 /*====================================================================*/
@@ -1408,15 +1447,15 @@
     CS_CHECK(MapMemPage, link->win, &mem);
 
     /* Try scribbling on the buffer */
-    info->base = ioremap(req.Base, window_size);
+    info->base = bus_ioremap(bus_ops, req.Base, window_size);
     for (i = 0; i < (TX_PAGES<<8); i += 2)
-	writew_ns((i>>1), info->base+offset+i);
+	bus_writew_ns(bus_ops, (i>>1), info->base+offset+i);
     udelay(100);
     for (i = 0; i < (TX_PAGES<<8); i += 2)
-	if (readw_ns(info->base+offset+i) != (i>>1)) break;
+	if (bus_readw_ns(bus_ops, info->base+offset+i) != (i>>1)) break;
     pcnet_reset_8390(dev);
     if (i != (TX_PAGES<<8)) {
-	iounmap(info->base);
+        bus_iounmap(bus_ops, info->base);
 	CardServices(ReleaseWindow, link->win);
 	info->base = NULL; link->win = NULL;
 	goto failed;
--- pcmcia-cs-3.1.14/etc/cis/Makefile	Fri Feb 11 19:24:06 2000
+++ pcmcia-cs-3.1.14-arm/etc/cis/Makefile	Thu Apr 20 21:41:48 2000
@@ -10,7 +10,11 @@
 
 CIS = $(patsubst %.cis,%.dat,$(wildcard *.cis))
 
+ifeq ($(HOST_ARCH),$(ARCH))
 all: $(CIS)
+else
+all:
+endif
 
 dep:
 
diff -urN pcmcia-cs-3.1.14/etc/sdconfig pcmcia-cs-3.1.14-arm/etc/sdconfig
--- pcmcia-cs-3.1.14/etc/sdconfig	Wed Dec 31 19:00:00 1969
+++ pcmcia-cs-3.1.14-arm/etc/sdconfig	Thu Apr 20 22:09:51 2000
@@ -0,0 +1,106 @@
+#!/bin/awk -f
+#
+# Socket Driver configuration preprocessor
+# John G Dorsey <john+@cs.cmu.edu>
+#
+# Usage: sdconfig <optfile>
+#
+# This program converts a socket driver options file into an `insmod'
+# parameter string. An options file may contain '#'-style comments, 
+# blank lines, and lines containing parameters. A parameter consists
+# of a keyword followed by one or more comma-delimited values. Values
+# can be single objects, such as integers, or can be in the special 
+# form of a range. Range values are of the form "base-bounds", and
+# both base and bounds may be expressed as decimal values, or in
+# hexadecimal, indicated by the "0x" prefix. Ranges will be converted
+# to a tuple of the form "base,size" (where size = bounds - base).
+# Note that a parameter keyword may appear on multiple lines in the
+# options file; the values over all appearances of the keyword will
+# be aggregated together (in order) under a single list in the 
+# resulting output. For ranges, this means that the target array in
+# the module will alternately contain base and size values.
+#
+# Note: this program uses gawk-specific features.
+
+BEGIN {
+  FS = "="
+  HEXDIGITS = "0123456789abcdef"
+  for (i = 0; i < 8; ++i) {
+    HEXPOWERS[i] = 1
+    for (j = 0; j < i; ++j)
+      HEXPOWERS[i] *= 16
+  }
+}
+
+// { ++linenum }
+
+$0 !~ /^#/ && $0 !~ /^$/ {
+  gsub(/[ \t]*#.*/, "")
+  if (NF != 2 || length($1) == 0 || length($2) == 0)
+    print ARGV[ARGC - 1] ": malformed line " linenum " - \"" $0 "\"" > \
+      "/dev/stderr"
+  else {
+    gsub(/[ \t]/, "")
+    count = split($2, values, ",")
+    for (i = 1; i <= count; ++i)
+      params[tolower($1), sprintf("%08d", ++size)] = tolower(values[i])
+  }
+}
+
+function todecimal (NUMBER,      len, i, num) {
+  if (match(NUMBER, /0x/) != 1)
+    return NUMBER
+  len = length(NUMBER)
+  for (i = len; i > 2; --i)
+    num += (index(HEXDIGITS, substr(NUMBER, i, 1)) - 1) * HEXPOWERS[len - i]
+  return int(num)
+}
+
+function ranges (ARRAY,      parts, count, base, bounds) {
+  for (item in ARRAY) {
+    count = split(ARRAY[item], parts, "-")
+    if (count > 2) {
+      print ARGV[ARGC - 1] ": ignoring bad range \"" ARRAY[item] "\"" > \
+        "/dev/stderr"
+      delete ARRAY[item]
+    } else if (count==2) {
+      base = todecimal(parts[1])
+      bounds = todecimal(parts[2])
+      if (base >= bounds) {
+        print ARGV[ARGC - 1] ": ignoring backwards range \"" ARRAY[item] \
+          "\"" > "/dev/stderr"
+        delete ARRAY[item]
+      } else
+        ARRAY[item] = sprintf("0x%x,0x%x", base, bounds - base)
+    }
+  }
+  return
+}
+
+function sort (ARRAY, INDICES,      i, j, size, tmp, min) {
+  for (item in ARRAY)
+    INDICES[++size] = item
+  for (i = 1; i < size; ++i) {
+    min = i
+    for (j = i + 1; j <= size; ++j)
+      if (INDICES[j] < INDICES[min])
+        min = j
+    tmp = INDICES[min]
+    INDICES[min] = INDICES[i]
+    INDICES[i] = tmp
+  }
+  return size
+}
+
+END {
+  ranges(params)
+  size = sort(params, indices)
+  for (i = 1; i <= size; ++i) {
+    split(indices[i], values, SUBSEP)
+    if (opt != values[1]) {
+      opt = values[1]
+      printf(" %s=%s", opt, params[indices[i]])
+    } else
+      printf(",%s", params[indices[i]]);
+  }
+}
diff -urN pcmcia-cs-3.1.14/include/pcmcia/cs_types.h pcmcia-cs-3.1.14-arm/include/pcmcia/cs_types.h
--- pcmcia-cs-3.1.14/include/pcmcia/cs_types.h	Mon Jan 17 20:14:36 2000
+++ pcmcia-cs-3.1.14-arm/include/pcmcia/cs_types.h	Fri Apr 21 18:26:52 2000
@@ -39,7 +39,11 @@
 #endif
 
 typedef u_short	socket_t;
-typedef u_short	ioaddr_t;
+#ifdef __arm__
+typedef u_long	ioaddr_t;
+#else
+typedef u_short ioaddr_t;
+#endif
 typedef u_int	event_t;
 typedef u_char	cisdata_t;
 typedef u_short	page_t;
diff -urN pcmcia-cs-3.1.14/include/pcmcia/ss.h pcmcia-cs-3.1.14-arm/include/pcmcia/ss.h
--- pcmcia-cs-3.1.14/include/pcmcia/ss.h	Wed Mar 15 21:26:02 2000
+++ pcmcia-cs-3.1.14-arm/include/pcmcia/ss.h	Mon Apr 24 14:34:08 2000
@@ -105,7 +105,7 @@
     u_char	map;
     u_char	flags;
     u_short	speed;
-    u_short	start, stop;
+    ioaddr_t	start, stop;
 } pccard_io_map;
 
 typedef struct pccard_mem_map {
diff -urN pcmcia-cs-3.1.14/modules/Makefile pcmcia-cs-3.1.14-arm/modules/Makefile
--- pcmcia-cs-3.1.14/modules/Makefile	Mon Feb 21 22:08:13 2000
+++ pcmcia-cs-3.1.14-arm/modules/Makefile	Thu Apr 20 21:41:48 2000
@@ -45,6 +45,11 @@
 CORE    += pci_fixup.o
 endif
 
+ifeq ($(ARCH),arm)
+SRCS	+= sa1100.c
+MODULES	+= sa1100.o
+endif
+
 ifdef CONFIG_PNP_BIOS
 SRCS    += pnp_bios.c pnp_proc.c pnp_rsrc.c
 CORE    += pnp_bios.o pnp_proc.o pnp_rsrc.o
diff -urN pcmcia-cs-3.1.14/modules/blah pcmcia-cs-3.1.14-arm/modules/blah
--- pcmcia-cs-3.1.14/modules/blah	Wed Dec 31 19:00:00 1969
+++ pcmcia-cs-3.1.14-arm/modules/blah	Sat Apr 22 23:11:04 2000
@@ -0,0 +1 @@
++#define BCR_IRDA_FSEL	(1<<3)	/* IRDA Frequency select (0 = SIR, 1 = MIR/ FIR) /*
diff -urN pcmcia-cs-3.1.14/modules/cs.c pcmcia-cs-3.1.14-arm/modules/cs.c
--- pcmcia-cs-3.1.14/modules/cs.c	Thu Mar 30 22:53:35 2000
+++ pcmcia-cs-3.1.14-arm/modules/cs.c	Thu Apr 27 13:40:01 2000
@@ -273,6 +273,13 @@
     for (i = 0; i < 2; i++) {
 	io.map = i;
 	s->ss_entry(s->sock, SS_SetIOMap, &io);
+#ifdef CONFIG_VIRTUAL_BUS
+	s->io[i].Attributes=0;
+	s->io[i].BasePort=io.start;
+	s->io[i].NumPorts=(io.stop-io.start)+1;
+	s->io[i].InUse=0;
+	s->io[i].Config=0;
+#endif
     }
     for (i = 0; i < 5; i++) {
 	mem.map = i;
@@ -761,6 +768,14 @@
 	align = 0;
     }
     for (i = 0; i < MAX_IO_WIN; i++) {
+
+      if(s->cap.features & SS_CAP_STATIC_MAP){
+	if(s->io[i].InUse==0){
+	  *base=s->io[i].BasePort;
+	  break;
+	}
+      } else {
+
 	if (s->io[i].NumPorts == 0) {
 	    if (find_io_region(base, num, align, name) == 0) {
 		s->io[i].Attributes = attr;
@@ -789,6 +804,7 @@
 		s->io[i].InUse += num;
 		break;
 	    }
+      }
     }
     return (i == MAX_IO_WIN);
 } /* alloc_io_space */
@@ -1567,7 +1583,8 @@
     s->state &= ~SOCKET_WIN_REQ(win->index);
 
     /* Release system memory */
-    release_mem_region(win->base, win->size);
+    if(!(s->cap.features & SS_CAP_STATIC_MAP))
+      release_mem_region(win->base, win->size);
     win->handle->state &= ~CLIENT_WIN_REQ(win->index);
 
     win->magic = 0;
diff -urN pcmcia-cs-3.1.14/modules/sa1100.c pcmcia-cs-3.1.14-arm/modules/sa1100.c
--- pcmcia-cs-3.1.14/modules/sa1100.c	Wed Dec 31 19:00:00 1969
+++ pcmcia-cs-3.1.14-arm/modules/sa1100.c	Thu Apr 27 16:31:51 2000
@@ -0,0 +1,991 @@
+/*======================================================================
+
+    Device driver for the PCMCIA control functionality of StrongARM
+    SA-1100 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 <pcmcia/config.h>
+#include <pcmcia/k_compat.h>
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bus_ops.h>
+
+#ifdef CONFIG_ITSY
+# include <asm/arch/Itsy.h>
+# include <asm/arch/io_reg.h>
+# include <asm/arch/mmu.h>
+# include <asm/arch/mmap.h>
+#endif
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/procinfo.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include "sa1100.h"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug=PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#endif
+
+MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-1100 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 sa1100_pcmcia_socket 
+sa1100_pcmcia_socket[SA1100_PCMCIA_MAX_SOCK];
+
+static unsigned int sa1100_pcmcia_socket_count=0;
+
+
+/* We currently don't do anything interesting in this driver with I/O
+ * or memory maps, as on the Itsy, this has been taken care of during
+ * kernel initialization. These structures are basically just regurgitated
+ * back to Card Services:
+ */
+static pccard_io_map  sa1100_pcmcia_io_map[SA1100_PCMCIA_IO_MAP_COUNT];
+static pccard_mem_map sa1100_pcmcia_mem_map[SA1100_PCMCIA_MEM_MAP_COUNT];
+
+static struct pcmcia_maps kernel_maps;
+
+
+/* Prototypes for routines which are used internally: */
+
+static int  sa1100_pcmcia_init(void);
+static void sa1100_pcmcia_shutdown(void);
+static void sa1100_pcmcia_interrupt(int irq, void *dev,
+				    struct pt_regs *regs);
+static void sa1100_pcmcia_wrapper(int irq, void *dev_id, 
+				  struct pt_regs *regs);
+static int  sa1100_pcmcia_service(unsigned int sock, unsigned int cmd,
+				  void *arg);
+
+
+#ifdef MODULE
+int init_module(void){
+  servinfo_t info;
+
+  printk(KERN_INFO "SA-1100 PCMCIA (CS release 0x%x)\n", CS_RELEASE_CODE);
+
+  CardServices(GetCardServicesInfo, &info);
+
+  if(info.Revision!=CS_RELEASE_CODE){
+    printk(KERN_ERR "Card Services release codes do not match\n");
+    return -1;
+  }
+
+  return sa1100_pcmcia_init();
+}
+
+void cleanup_module(void){
+  sa1100_pcmcia_shutdown();
+
+  printk(KERN_INFO "SA-1100 PCMCIA (CS release 0x%x) offline\n",
+	 CS_RELEASE_CODE);
+}
+#endif /* MODULE */
+
+
+
+static u32 sa1100_bus_in(void *bus, u32 port, s32 sz){
+  DEBUG(5, "%s(%08x, %d)\n", __FUNCTION__, port, sz);
+
+  switch(sz){
+  case  0: return inb(port);
+  case  1: return ntohs(inw(port));
+  case -1: return inw(port);
+  case  2: return ntohl(inl(port));
+  case -2: return inl(port);
+  }
+
+  printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  return 0;
+}
+
+static void sa1100_bus_out(void *bus, u32 val, u32 port, s32 sz){
+  DEBUG(5, "%s(%08x, %08x, %d)\n", __FUNCTION__, val, port, sz);
+
+  switch(sz){
+  case  0: outb(val, port);               break;
+  case  1: outw(htons(val), port);        break;
+  case -1: outw(val, port);               break;
+  case  2: outl(htonl(val), port);        break;
+  case -2: outl(val, port);               break;
+  default: printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  }
+}
+
+static void sa1100_bus_ins(void *bus, u32 port, void *buf, 
+			   u32 count, s32 sz){
+  DEBUG(5, "%s(%08x, %p, %u, %d)\n", __FUNCTION__, port, buf, count, sz);
+
+  switch(sz){
+  case 0:
+    while((sz--)>0)
+      *(((unsigned char *)buf)++)=inb(port);
+    break;
+
+  case 1:
+    while((sz--)>0)
+      *(((unsigned short *)buf)++)=ntohs(inw(port));
+    break;
+
+  case -1:
+    while((sz--)>0)
+      *(((unsigned short *)buf)++)=inw(port);
+    break;
+
+  case 2:
+    while((sz--)>0)
+      *(((unsigned int *)buf)++)=ntohl(inl(port));
+    break;
+
+  case -2:
+    while((sz--)>0)
+      *(((unsigned int *)buf)++)=inl(port);
+    break;
+
+  default: printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  }
+}
+
+static void sa1100_bus_outs(void *bus, u32 port, void *buf,
+			    u32 count, s32 sz){
+  DEBUG(5, "%s(%08x, %p, %u, %d)\n", __FUNCTION__, port, buf, count, sz);
+
+  switch(sz){
+  case 0:
+    while((sz--)>0)
+      outb(*(((unsigned char *)buf)++), port);
+    break;
+    
+  case 1:
+    while((sz--)>0)
+      outw(htons(*(((unsigned short *)buf)++)), port);
+    break;
+
+  case -1:
+    while((sz--)>0)
+      outw(*(((unsigned short *)buf)++), port);
+    break;
+
+  case 2:
+    while((sz--)>0)
+      outl(htonl(*(((unsigned int *)buf)++)), port);
+    break;
+
+  case -2:
+    while((sz--)>0)
+      outl(*(((unsigned int *)buf)++), port);
+    break;
+
+  default: printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  }
+}
+
+static void *sa1100_bus_ioremap(void *bus, u_long ofs, u_long sz){
+  DEBUG(5, "%s(%08lx, %lu)\n", __FUNCTION__, ofs, sz);
+  return (void *)ofs;
+}
+
+static void sa1100_bus_iounmap(void *bus, void *addr){
+  DEBUG(5, "%s(%p)\n", __FUNCTION__, addr);
+}
+
+static u32 sa1100_bus_read(void *bus, void *addr, s32 sz){
+  DEBUG(5, "%s(%p, %d)\n", __FUNCTION__, addr, sz);
+
+  switch(sz){
+  case  0: return *((unsigned char *)addr);
+  case  1: return ntohs(*((unsigned short *)addr));
+  case -1: return *((unsigned short *)addr);
+  case  2: return ntohl(*((unsigned int *)addr));
+  case -2: return *((unsigned int *)addr);
+  }
+
+  printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  return 0;
+}
+
+static void sa1100_bus_write(void *bus, u32 val, void *addr, s32 sz){
+  DEBUG(5, "%s(%x, %p, %d)\n", __FUNCTION__, val, addr, sz);
+
+  switch(sz){
+  case  0: *((unsigned char *)addr)=(unsigned char)val;           break;
+  case  1: *((unsigned short *)addr)=htons((unsigned short)val);  break;
+  case -1: *((unsigned short *)addr)=(unsigned short)val;         break;
+  case  2: *((unsigned int *)addr)=htonl((unsigned int)val);      break;
+  case -2: *((unsigned int *)addr)=(unsigned int)val;             break;
+  default: printk(KERN_ERR "%s(): invalid size %d\n", __FUNCTION__, sz);
+  }
+}
+
+static void sa1100_bus_copy_from(void *bus, void *d, void *s, u32 count){
+  DEBUG(5, "%s(%p, %p, %u)\n", __FUNCTION__, d, s, count);
+  _memcpy_fromio(d, (unsigned long)s, count);
+}
+
+static void sa1100_bus_copy_to(void *bus, void *d, void *s, u32 count){
+  DEBUG(5, "%s(%p, %p, %u)\n", __FUNCTION__, d, s, count);
+  _memcpy_toio((unsigned long)d, s, count);
+}
+
+static int sa1100_bus_request_irq(void *bus, u_int irq,
+				  void (*handler)(int, void *,
+						  struct pt_regs *),
+				  u_long flags, const char *device,
+				  void *dev_id){
+  unsigned int sock=(unsigned int)(((struct bus_operations *)bus)->priv);
+  struct pcmcia_irq kernel_irq;
+
+  DEBUG(2, "%s(%u, %p, %08lx, \"%s\", %p)\n", __FUNCTION__, irq, handler,
+	flags, device, dev_id);
+
+  sa1100_pcmcia_socket[sock].irq=irq;
+  sa1100_pcmcia_socket[sock].handler=handler;
+
+  kernel_irq.sock=sock;
+  kernel_irq.enable=1;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_SetDeviceIRQ, &kernel_irq)<0){
+    printk(KERN_ERR "Unable to setup up device IRQ for socket %u\n", sock);
+    return -1;
+  }
+
+  return request_irq(irq, sa1100_pcmcia_wrapper, 
+		     flags|((irq==11)?SA_SHIRQ:0), device, dev_id);
+}
+
+static void sa1100_bus_free_irq(void *bus, u_int irq, void *dev_id){
+  unsigned int sock=(unsigned int)(((struct bus_operations *)bus)->priv);
+  struct pcmcia_irq kernel_irq;
+
+  DEBUG(2, "%s(%u, %p)\n", __FUNCTION__, irq, dev_id);
+
+  free_irq(irq, dev_id);
+
+  kernel_irq.sock=sock;
+  kernel_irq.enable=0;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_SetDeviceIRQ, &kernel_irq)<0)
+    printk(KERN_ERR "Unable to tear down device IRQ for socket %u\n", sock);
+}
+
+
+/* sa1100_pcmcia_init()
+ * ^^^^^^^^^^^^^^^^^^^^
+ * This routine is a lot smaller than it used to be!
+ *
+ * Here, we perform three tasks:
+ *
+ *   1. Initialize the kernel low-level PCMCIA mechanism.
+ *   2. Determine initial state by issuing a fake IRQ sample.
+ *   3. Register with Card Services.
+ *
+ * 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 sa1100_pcmcia_init(void){
+  int i;
+  struct pcmcia_init pcmcia_init;
+  struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
+  struct pcmcia_state_array state_array;
+
+  pcmcia_init.handler=sa1100_pcmcia_interrupt;
+  pcmcia_init.maps=&kernel_maps;
+
+  if((sa1100_pcmcia_socket_count=sa1100_pcmcia(SA1100_PCMCIA_Init, 
+					       &pcmcia_init))<0){
+    printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n");
+    return -1;
+  }
+
+  state_array.size=sa1100_pcmcia_socket_count;
+  state_array.state=state;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_SocketIRQ, &state_array)<0){
+    printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n");
+    return -1;
+  }
+
+  memset(sa1100_pcmcia_socket, 0, sizeof(sa1100_pcmcia_socket));
+
+  for(i=0; i<sa1100_pcmcia_socket_count; ++i){
+    sa1100_pcmcia_socket[i].k_state=state[i];
+
+    /* As suggested, our "private data" is just a label to indicate the
+     * socket:
+     */
+    sa1100_pcmcia_socket[i].bus_ops.priv=i;
+
+    sa1100_pcmcia_socket[i].bus_ops.b_in=sa1100_bus_in;
+    sa1100_pcmcia_socket[i].bus_ops.b_ins=sa1100_bus_ins;
+    sa1100_pcmcia_socket[i].bus_ops.b_out=sa1100_bus_out;
+    sa1100_pcmcia_socket[i].bus_ops.b_outs=sa1100_bus_outs;
+    sa1100_pcmcia_socket[i].bus_ops.b_ioremap=sa1100_bus_ioremap;
+    sa1100_pcmcia_socket[i].bus_ops.b_iounmap=sa1100_bus_iounmap;
+    sa1100_pcmcia_socket[i].bus_ops.b_read=sa1100_bus_read;
+    sa1100_pcmcia_socket[i].bus_ops.b_write=sa1100_bus_write;
+    sa1100_pcmcia_socket[i].bus_ops.b_copy_from=sa1100_bus_copy_from;
+    sa1100_pcmcia_socket[i].bus_ops.b_copy_to=sa1100_bus_copy_to;
+    sa1100_pcmcia_socket[i].bus_ops.b_request_irq=sa1100_bus_request_irq;
+    sa1100_pcmcia_socket[i].bus_ops.b_free_irq=sa1100_bus_free_irq;
+
+  }
+
+  /* Only advertise as many sockets as we can detect: */
+  if(register_ss_entry(sa1100_pcmcia_socket_count, sa1100_pcmcia_service)<0){
+    printk(KERN_ERR "Unable to register socket service routine\n");
+    return -1;
+  }
+
+  DEBUG(1, "sa1100: initialization complete\n");
+
+  return 0;
+
+}  /* sa1100_pcmcia_init() */
+
+
+/* sa1100_pcmcia_shutdown()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ * Invokes the low-level kernel service to free IRQs associated with this
+ * socket controller and reset GPIO edge detection.
+ */
+static void sa1100_pcmcia_shutdown(void){
+
+  sa1100_pcmcia(SA1100_PCMCIA_Shutdown, NULL);
+
+  DEBUG(1, "sa1100: shutdown complete\n");
+}
+
+
+/* sa1100_pcmcia_interrupt()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Service routine for card detect interrupts (requested in 
+ * sa1100_pcmcia_init()).
+ *
+ * Whenever an interruptable PCMCIA card event occurs, this routine is 
+ * entered to update internal driver state, and possibly to call back
+ * Card Services. The routine loops over all of the slots it knows about, 
+ * and performs the following steps for each slot which has generated an
+ * interrupt:
+ *
+ *   1. Construct a bitmask of events to report back to Card Services.
+ *      Events that can be reported include card detection, card
+ *      status change, low battery voltage, and so forth.
+ *
+ *   2. Invoke the Card Services callback for the slot we're servicing,
+ *      passing the event mask we just constructed.
+ *
+ */
+static void sa1100_pcmcia_interrupt IRQ(int irq, void *dev,
+					struct pt_regs *regs){
+  unsigned int i, events;
+  struct pcmcia_state state[SA1100_PCMCIA_MAX_SOCK];
+  struct pcmcia_state_array state_array;
+
+  state_array.size=sa1100_pcmcia_socket_count;
+  state_array.state=state;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_SocketIRQ, &state_array)<0){
+    printk(KERN_ERR "Error in kernel PCMCIA interrupt service.\n");
+    return;
+  }
+
+  for(i=0; i<sa1100_pcmcia_socket_count; ++i){
+
+    events=0;
+
+    if(state[i].detect!=sa1100_pcmcia_socket[i].k_state.detect){
+
+      DEBUG(2, "%s(): card detect value %u (IRQ %d)\n", __FUNCTION__,
+	    state[i].detect, irq);
+
+      events|=sa1100_pcmcia_socket[i].cs_state.csc_mask&SS_DETECT;
+
+      if(state[i].detect==0){
+	sa1100_pcmcia_socket[i].cs_state.Vcc=0;
+	sa1100_pcmcia_socket[i].cs_state.Vpp=0;
+      }
+    }
+
+    if(state[i].ready!=sa1100_pcmcia_socket[i].k_state.ready){
+
+      DEBUG(2, "%s(): card ready value %u (IRQ %d)\n", __FUNCTION__,
+	    state[i].ready, irq);
+
+      events|=sa1100_pcmcia_socket[i].cs_state.csc_mask&
+	((sa1100_pcmcia_socket[i].cs_state.flags&SS_IOCARD)?0:SS_READY);
+    }
+
+    if(state[i].bvd1!=sa1100_pcmcia_socket[i].k_state.bvd1){
+
+      DEBUG(2, "%s(): card BVD1 value %u (IRQ %d)\n", __FUNCTION__,
+	    state[i].bvd1, irq);
+
+      events|=sa1100_pcmcia_socket[i].cs_state.csc_mask&
+	(sa1100_pcmcia_socket[i].cs_state.flags&SS_IOCARD)?\
+	SS_STSCHG:SS_BATDEAD;
+    }
+
+    if(state[i].bvd2!=sa1100_pcmcia_socket[i].k_state.bvd2){
+
+      DEBUG(2, "%s(): card BVD2 value %u (IRQ %d)\n", __FUNCTION__,
+	    state[i].bvd2, irq);
+
+      events|=sa1100_pcmcia_socket[i].cs_state.csc_mask&
+	(sa1100_pcmcia_socket[i].cs_state.flags&SS_IOCARD)?0:SS_BATWARN;
+    }
+
+    DEBUG(2, "events[%d]: %s%s%s%s%s%s\n", i,
+	  (events==0)?"<NONE>":"",
+	  (events&SS_DETECT)?"DETECT ":"",
+	  (events&SS_READY)?"READY ":"", 
+	  (events&SS_BATDEAD)?"BATDEAD ":"",
+	  (events&SS_BATWARN)?"BATWARN ":"",
+	  (events&SS_STSCHG)?"STSCHG ":"");
+
+    sa1100_pcmcia_socket[i].k_state=state[i];
+
+    if(events!=0 && sa1100_pcmcia_socket[i].callback.handler!=NULL)
+      sa1100_pcmcia_socket[i].callback.\
+	handler(sa1100_pcmcia_socket[i].callback.info, events);
+
+  }  /* for(i=0; ...) */
+
+}  /* sa1100_pcmcia_interrupt() */
+
+
+/* sa1100_pcmcia_RegisterCallback()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements a response to the SS_RegisterCallback request by Card
+ * Services. If the function pointer `call' 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, sa1100_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 sa1100_pcmcia_RegisterCallback(unsigned int sock,
+					  ss_callback_t *call){
+  if(call==NULL){
+    sa1100_pcmcia_socket[sock].callback.handler=NULL;
+    MOD_DEC_USE_COUNT;
+  } else {
+    MOD_INC_USE_COUNT;
+    sa1100_pcmcia_socket[sock].callback=*call;
+  }
+
+  return 0;
+}
+
+
+/* sa1100_pcmcia_InquireSocket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Responds to the SS_InquireSocket request by 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.)
+ *
+ * Returns: 0 on success, -1 if no pin has been configured for `sock'
+ */
+static int sa1100_pcmcia_InquireSocket(unsigned int sock,
+				       socket_cap_t *cap){
+  struct pcmcia_irq_mask irq_mask;
+
+  DEBUG(2, "%s() for sock %u\n", __FUNCTION__, sock);
+
+  if(sock>=sa1100_pcmcia_socket_count){
+    printk(KERN_ERR "sa1100: 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_VIRTUAL_BUS: readb(), &c., are defined to be troublemaking
+   *   stubs if the target architecture doesn't support PCI. 
+   *   Unfortunately, Card Services is littered with these calls.
+   *   Fortunately, we can override the literal invocations by
+   *   supplying our own table of routines.
+   *
+   * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory
+   *   resource database; we'll handle the memory mapping in this
+   *   driver.
+   *
+   * 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_VIRTUAL_BUS | 
+		 SS_CAP_STATIC_MAP | SS_CAP_PCCARD);
+
+  irq_mask.sock=sock;
+  irq_mask.mask=0;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_GetIRQMask, &irq_mask)<0){
+    printk(KERN_ERR "Error obtaining IRQ mask from kernel for socket %u\n",
+	   sock);
+    return -1;
+  }
+
+  cap->irq_mask=irq_mask.mask;
+  cap->map_size=PAGE_SIZE;
+  cap->pci_irq=0;
+  cap->cardbus=0;
+  cap->bus=&(sa1100_pcmcia_socket[sock].bus_ops);
+
+  return 0;
+}
+
+
+/* sa1100_pcmcia_GetStatus()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Handles the SS_GetStatus request by 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, Card Services issues
+ * three SetSocket commands in a row the first with RESET deasserted,
+ * the second with RESET asserted, and the last with RESET deasserted
+ * again. Following the third SetSocket, a GetStatus command will
+ * be issued. Card Services is looking for the SS_READY flag (see
+ * setup_socket(), reset_socket(), and unreset_socket() in cs.c).
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_GetStatus(unsigned int sock,
+				   unsigned int *status){
+
+  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+  *status=sa1100_pcmcia_socket[sock].k_state.detect?SS_DETECT:0;
+
+  *status|=sa1100_pcmcia_socket[sock].k_state.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|=sa1100_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0;
+
+  if(sa1100_pcmcia_socket[sock].cs_state.flags&SS_IOCARD)
+    *status|=sa1100_pcmcia_socket[sock].k_state.bvd1?SS_STSCHG:0;
+  else {
+    if(sa1100_pcmcia_socket[sock].k_state.bvd1==0)
+      *status|=SS_BATDEAD;
+    else if(sa1100_pcmcia_socket[sock].k_state.bvd2==0)
+      *status|=SS_BATWARN;
+  }
+
+  *status|=sa1100_pcmcia_socket[sock].k_state.vs_3v?SS_3VCARD:0;
+
+  *status|=sa1100_pcmcia_socket[sock].k_state.vs_Xv?SS_XVCARD:0;
+
+  DEBUG(4, "\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;
+
+}  /* sa1100_pcmcia_GetStatus() */
+
+
+/* sa1100_pcmcia_GetSocket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Responds to the SS_GetSocket request by Card Services. Not a very 
+ * exciting routine.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_GetSocket(unsigned int sock,
+				   socket_state_t *state){
+
+  DEBUG(5, "%s() for sock %u\n", __FUNCTION__, sock);
+
+  /* This information was given to us in an earlier call to SetSocket,
+   * so we're just regurgitating it here:
+   */
+  *state=sa1100_pcmcia_socket[sock].cs_state;
+
+  return 0;
+}
+
+
+/* sa1100_pcmcia_SetSocket()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Handles the SS_SetSocket request by 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 Card Services later.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_SetSocket(unsigned int sock,
+				   socket_state_t *state){
+  struct pcmcia_configure configure;
+
+  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);
+
+  configure.sock=sock;
+  configure.vcc=state->Vcc;
+  configure.vpp=state->Vpp;
+  configure.output=(state->flags&SS_OUTPUT_ENA)?1:0;
+  configure.speaker=(state->flags&SS_SPKR_ENA)?1:0;
+  configure.reset=(state->flags&SS_RESET)?1:0;
+
+  if(sa1100_pcmcia(SA1100_PCMCIA_ConfigureSocket, &configure)<0){
+    printk(KERN_ERR "Unable to configure socket %u\n", sock);
+    return -1;
+  }
+
+  sa1100_pcmcia_socket[sock].cs_state=*state;
+  
+  return 0;
+
+}  /* sa1100_pcmcia_SetSocket() */
+
+
+/* sa1100_pcmcia_GetIOMap()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements a response to the SS_GetIOMap request by Card Services.
+ * Just returns an I/O map descriptor which was assigned earlier by
+ * a SS_SetIOMap request.
+ *
+ * Returns: 0 on success, -1 if the map index was out of range
+ */
+static int sa1100_pcmcia_GetIOMap(unsigned int sock,
+				  pccard_io_map *map){
+
+  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+  if(map->map>=SA1100_PCMCIA_IO_MAP_COUNT){
+    printk(KERN_ERR "GetIOMap: map (%d) out of range\n", map->map);
+    return -1;
+  }
+
+  *map=sa1100_pcmcia_io_map[map->map];
+
+  return 0;
+}
+
+
+/* sa1100_pcmcia_SetIOMap()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ * Responds to the SS_SetIOMap request by Card Services. We record
+ * the map descriptor for use in a subsequent SS_GetIOMap request, and
+ * set up the I/O memory map.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_SetIOMap(unsigned int sock,
+				  pccard_io_map *map){
+  unsigned int clock, speed;
+
+  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>=SA1100_PCMCIA_IO_MAP_COUNT){
+    printk(KERN_ERR "SetIOMap: map (%d) out of range\n", map->map);
+    return -1;
+  }
+
+  if(map->flags&MAP_ACTIVE){
+
+    speed=(map->speed>0)?map->speed:SA1100_PCMCIA_IO_ACCESS;
+    
+    if((clock=SA1100_PCMCIA_CCF())==0){
+      printk(KERN_ERR "sa1100: invalid CCF code (0x%02x)\n",
+	     FExtr(PPCR, PPCR_CCF));
+      return -1;
+    }
+
+    MECR_BSIO_SET(MECR, sock, sa1100_pcmcia_mecr_bs(speed, clock));
+
+  } else 
+    /* This is extremely sketchy. What's going on here is that we're
+     * causing Card Services to change its mind about where it thinks
+     * the I/O base should be. We don't fiddle with the `stop' field
+     * because it's not really important; CS will remember the
+     * NumPorts value it used to originally calculate the `stop'
+     * address anyway. Right now we're taking it on faith that CS
+     * won't be asking for more space than is actually mapped for
+     * PCMCIA I/O (64MB).
+     */
+    map->start=kernel_maps.sock[sock].io.start;
+
+  sa1100_pcmcia_io_map[map->map]=*map;
+
+  return 0;
+
+}  /* sa1100_pcmcia_SetIOMap() */
+
+
+/* sa1100_pcmcia_GetMemMap()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements a response to the SS_GetMemMap request by Card Services.
+ * Just returns a memory map descriptor which was assigned earlier by
+ * a SS_SetMemMap request.
+ *
+ * Returns: 0 on success, -1 if the map index was out of range
+ */
+static int sa1100_pcmcia_GetMemMap(unsigned int sock,
+				   pccard_mem_map *map){
+
+  DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock);
+
+  if(map->map>=SA1100_PCMCIA_MEM_MAP_COUNT){
+    printk(KERN_ERR "GetMemMap: map (%d) out of range\n", map->map);
+    return -1;
+  }
+
+  *map=sa1100_pcmcia_mem_map[map->map];
+
+  return 0;
+}
+
+
+/* sa1100_pcmcia_SetMemMap()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Responds to the SS_SetMemMap request by Card Services. We record
+ * the map descriptor for use in a subsequent SS_GetMemMap request, and
+ * set up the memory map.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_SetMemMap(unsigned int sock,
+				   pccard_mem_map *map){
+  unsigned int clock=0;
+
+  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>=SA1100_PCMCIA_MEM_MAP_COUNT){
+    printk(KERN_ERR "SetMemMap: map (%d) out of range\n", map->map);
+    return -1;
+  }
+
+  if(map->flags&MAP_ACTIVE && map->speed>0){
+    
+    if((clock=SA1100_PCMCIA_CCF())==0){
+      printk(KERN_ERR "sa1100: invalid CCF code (0x%02x)\n",
+	     FExtr(PPCR, PPCR_CCF));
+      return -1;
+    }
+    
+    if(map->flags&MAP_ATTRIB)
+      MECR_BSA_SET(MECR, sock, sa1100_pcmcia_mecr_bs(map->speed, clock));
+    else
+      MECR_BSM_SET(MECR, sock, sa1100_pcmcia_mecr_bs(map->speed, clock));
+  }
+
+  /* As with SetIOMap above, here we're going to fool Card Services by
+   * changing the `sys_start' address. We don't bother with `sys_stop'
+   * because Card Services can just keep the size it was using when
+   * it invoked us.
+   */
+  if(map->flags&MAP_ATTRIB)
+    map->sys_start=kernel_maps.sock[sock].attr.start;
+  else
+    map->sys_start=kernel_maps.sock[sock].mem.start;
+  
+  sa1100_pcmcia_mem_map[map->map]=*map;
+
+  return 0;
+
+}  /* sa1100_pcmcia_SetMemMap() */
+
+
+/* sa1100_pcmcia_GetBridge()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements a no-op response to the SS_GetBridge request by Card
+ * Services (the SA-1100 does not support PCI bridge).
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_GetBridge(unsigned int sock,
+				   cb_bridge_map *map){
+  DEBUG(3, "%s() for sock %d\n", __FUNCTION__, sock);
+  return 0;
+}
+
+
+/* sa1100_pcmcia_SetBridge()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Implements a no-op response to the SS_SetBridge request by Card
+ * Services (the SA-1100 does not support PCI bridge).
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_SetBridge(unsigned int sock,
+				   cb_bridge_map *map){
+  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+  return 0;
+}
+
+
+/* sa1100_pcmcia_ProcSetup()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^
+ * Handles the SS_ProcSetup request by Card Services.
+ *
+ * Or rather, some day it will. Not yet implemented.
+ *
+ * Returns: 0
+ */
+static int sa1100_pcmcia_ProcSetup(unsigned int sock,
+				   struct proc_dir_entry *base){
+  DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock);
+  return 0;
+}
+
+
+/* sa1100_pcmcia_wrapper()
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ * This routine catches card ready interrupts and invokes the kernel
+ * low-level interface to actually deal with the GEDR, &c. It then
+ * calls the real client interrupt handler.
+ */
+static void sa1100_pcmcia_wrapper(int irq, void *dev_id, 
+				  struct pt_regs *regs){
+  unsigned int i;
+
+  DEBUG(2, "%s() for IRQ %d\n", __FUNCTION__, irq);
+  
+  for(i=0; i<sa1100_pcmcia_socket_count; ++i)
+    if(sa1100_pcmcia_socket[i].irq==irq &&
+       sa1100_pcmcia_socket[i].handler!=NULL){
+      sa1100_pcmcia(SA1100_PCMCIA_DeviceIRQ, (void *)i);
+      sa1100_pcmcia_socket[i].handler(irq, dev_id, regs);
+    }
+}
+
+
+/* sa1100_pcmcia_service()
+ * ^^^^^^^^^^^^^^^^^^^^^^^
+ * The main entry point into the driver from Card Services. Simply
+ * demultiplexes based on the value of `cmd' to invoke the appropriate
+ * handler routine.
+ *
+ * Returns: 0 on success, -1 on error
+ */
+static int sa1100_pcmcia_service(unsigned int sock, unsigned int cmd,
+				 void *arg){
+  switch(cmd){
+  case SS_RegisterCallback: return sa1100_pcmcia_RegisterCallback(sock, arg);
+  case SS_InquireSocket:    return sa1100_pcmcia_InquireSocket(sock, arg);
+  case SS_GetStatus:        return sa1100_pcmcia_GetStatus(sock, arg);
+  case SS_GetSocket:        return sa1100_pcmcia_GetSocket(sock, arg);
+  case SS_SetSocket:        return sa1100_pcmcia_SetSocket(sock, arg);
+  case SS_GetIOMap:         return sa1100_pcmcia_GetIOMap(sock, arg);
+  case SS_SetIOMap:         return sa1100_pcmcia_SetIOMap(sock, arg);
+  case SS_GetMemMap:        return sa1100_pcmcia_GetMemMap(sock, arg);
+  case SS_SetMemMap:        return sa1100_pcmcia_SetMemMap(sock, arg);
+  case SS_GetBridge:        return sa1100_pcmcia_GetBridge(sock, arg);
+  case SS_SetBridge:        return sa1100_pcmcia_SetBridge(sock, arg);
+  case SS_ProcSetup:        return sa1100_pcmcia_ProcSetup(sock, arg);
+  }
+  
+  printk(KERN_ERR "Unrecognized SS command %d for socket %u\n", cmd, sock);
+  return -1;
+}
diff -urN pcmcia-cs-3.1.14/modules/sa1100.h pcmcia-cs-3.1.14-arm/modules/sa1100.h
--- pcmcia-cs-3.1.14/modules/sa1100.h	Wed Dec 31 19:00:00 1969
+++ pcmcia-cs-3.1.14-arm/modules/sa1100.h	Thu Apr 27 14:01:42 2000
@@ -0,0 +1,191 @@
+/*======================================================================
+
+    Device driver for the PCMCIA control functionality of StrongARM
+    SA-1100 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(_MODULES_SA1100_H)
+# define _MODULES_SA1100_H
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+
+/* MECR: Expansion Memory Configuration Register
+ * (SA-1100 Developers Manual, p.10-13)
+ *
+ * BS_xx = ( PCMCIA_CYCLE / ( 2 * 3 * CPU_CYCLE ) ) - 1
+ *
+ * Official PCMCIA cycle times:
+ *
+ *   - I/O:                  255ns
+ *   - attribute:            300ns
+ *   - common memory (3.3V): 600, 250, 200, 150, 100ns
+ *   - common memory (5V):   250, 200, 150, 100ns
+ *
+ * MECR layout is:  
+ *
+ *   res BSM2<4:0> BSA2<4:0> BSIO2<4:0> res BSM1<4:0> BSA1<4:0> BSIO1<4:0>
+ */
+
+#define MECR_SOCKET_0_SHIFT (0)
+#define MECR_SOCKET_1_SHIFT (16)
+
+#define MECR_FIELD_MASK     (0x1f)
+#define MECR_FAST_MODE_MASK (0x8000)  /* SA-1110 (unused in SA-1100) */
+
+#define MECR_BSIO_SHIFT (0)
+#define MECR_BSA_SHIFT  (5)
+#define MECR_BSM_SHIFT  (10)
+
+#define MECR_BSIO_SET(mecr, s, bs) \
+((mecr)=((mecr)&~((MECR_FAST_MODE_MASK|(MECR_FIELD_MASK<<MECR_BSIO_SHIFT))<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\
+        (((bs)<<MECR_BSIO_SHIFT)<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))
+
+#define MECR_BSA_SET(mecr, s, bs) \
+((mecr)=((mecr)&~((MECR_FAST_MODE_MASK|(MECR_FIELD_MASK<<MECR_BSA_SHIFT))<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\
+        (((bs)<<MECR_BSA_SHIFT)<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))
+
+#define MECR_BSM_SET(mecr, s, bs) \
+((mecr)=((mecr)&~((MECR_FAST_MODE_MASK|(MECR_FIELD_MASK<<MECR_BSM_SHIFT))<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))|\
+        (((bs)<<MECR_BSM_SHIFT)<<\
+         ((s)==0?MECR_SOCKET_0_SHIFT:MECR_SOCKET_1_SHIFT)))
+
+
+/* PPCR: Power Manager Phase-Locked Loop Configuration Register
+ * (SA-1100 Developers Manual, p.8-2)
+ *
+ * The core clock frequency of the processor is determined by the
+ * frequency of the crystal oscillator (either 3.6864MHz or 3.5795MHz).
+ *
+ * We have to know the processor clock rate in order to set PCMCIA 
+ * memory access times. Note that this code currently does not support
+ * altering of the clock rate after initial configuration.
+ *
+ * sa1100_pcmcia_ccf_6864() assumes an oscillator frequency of 3.6864MHz
+ * (as used in the Itsy), while sa1100_pcmcia_ccf_5795() assumes a
+ * frequency of 3.5795MHz. Both return 0 if the PPCR contains an invalid
+ * clock code.
+ */
+static inline unsigned int sa1100_pcmcia_ccf_6864(void){
+  switch(FExtr(PPCR, PPCR_CCF)){
+  case PPCR_F59_0MHz:  return 59;
+  case PPCR_F73_7MHz:  return 73;
+  case PPCR_F88_5MHz:  return 88;
+  case PPCR_F103_2MHz: return 103;
+  case PPCR_F118_0MHz: return 118;
+  case PPCR_F132_7MHz: return 132;
+  case PPCR_F147_5MHz: return 147;
+  case PPCR_F162_2MHz: return 162;
+  case PPCR_F176_9MHz: return 176;
+  case PPCR_F191_7MHz: return 191;
+  case PPCR_F206_4MHz: return 206;
+  }
+  return 0;
+}
+
+static inline unsigned int sa1100_pcmcia_ccf_5795(void){
+  switch(FExtr(PPCR, PPCR_CCF)){
+  case PPCR_F57_3MHz:  return 57;
+  case PPCR_F71_6MHz:  return 71;
+  case PPCR_F85_9MHz:  return 85;
+  case PPCR_F100_2MHz: return 100;
+  case PPCR_F114_5MHz: return 114;
+  case PPCR_F128_9MHz: return 128;
+  case PPCR_F143_2MHz: return 143;
+  case PPCR_F157_5MHz: return 157;
+  case PPCR_F171_8MHz: return 171;
+  case PPCR_F186_1MHz: return 186;
+  case PPCR_F200_5MHz: return 200;
+  }
+  return 0;
+}
+
+
+/* Define this to be the correct lookup function for your board.
+ * (This could be a configurable parameter.)
+ */
+#define SA1100_PCMCIA_CCF sa1100_pcmcia_ccf_6864
+
+
+/* This function implements the BS value calculation for setting the MECR
+ * using integer arithmetic:
+ */
+static inline unsigned int sa1100_pcmcia_mecr_bs(unsigned int pcmcia_cycle_ns,
+						 unsigned int cpu_clock_mhz){
+  unsigned int t=((pcmcia_cycle_ns*cpu_clock_mhz)/6)-1000;
+  return (t/1000)+(((t%1000)==0)?0:1);
+}
+
+
+/* When configuring I/O maps, Card Services appears to adopt the policy
+ * that a memory access time of "0" means "use the default." The default
+ * PCMCIA I/O access time is 255ns:
+ */
+#define SA1100_PCMCIA_IO_ACCESS      (255)
+
+
+/* This structure encapsulates per-socket state which we might need to
+ * use when responding to a Card Services query of some kind.
+ */
+struct sa1100_pcmcia_socket {
+  ss_callback_t         callback;
+  socket_state_t        cs_state;
+  struct pcmcia_state   k_state;
+  unsigned int          irq;
+  void                  (*handler)(int, void *, struct pt_regs *);
+  struct bus_operations bus_ops;
+};
+
+
+/* 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
+
+
+/* These values are chosen in accordance with the assumption mentioned in
+ * the Linux PCMCIA Programmer's Guide (sections 9.2.5, 9.2.6):
+ */
+#define SA1100_PCMCIA_IO_MAP_COUNT  (2)
+#define SA1100_PCMCIA_MEM_MAP_COUNT (5)
+
+#endif  /* !defined(_MODULES_SA1100_H) */
