Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
new file mode 100644
index 0000000..6375ebc
--- /dev/null
+++ b/drivers/pcmcia/Kconfig
@@ -0,0 +1,203 @@
+#
+# PCMCIA bus subsystem configuration
+#
+# Right now the non-CardBus choices are not supported
+# by the integrated kernel driver.
+#
+
+menu "PCCARD (PCMCIA/CardBus) support"
+
+config PCCARD
+	tristate "PCCard (PCMCIA/CardBus) support"
+	select HOTPLUG
+	---help---
+	  Say Y here if you want to attach PCMCIA- or PC-cards to your Linux
+	  computer.  These are credit-card size devices such as network cards,
+	  modems or hard drives often used with laptops computers.  There are
+	  actually two varieties of these cards: the older 16 bit PCMCIA cards
+	  and the newer 32 bit CardBus cards.
+
+	  To compile this driver as modules, choose M here: the
+	  module will be called pcmcia_core.
+
+if PCCARD
+
+config PCMCIA_DEBUG
+	bool "Enable PCCARD debugging"
+	help
+	  Say Y here to enable PCMCIA subsystem debugging.  You
+	  will need to choose the debugging level either via the
+	  kernel command line, or module options depending whether
+	  you build the PCMCIA as modules.
+
+	  The kernel command line options are:
+	    pcmcia_core.pc_debug=N
+	    ds.pc_debug=N
+	    sa11xx_core.pc_debug=N
+
+	  The module option is called pc_debug=N
+
+	  In all the above examples, N is the debugging verbosity
+	  level.
+
+config PCMCIA
+	tristate "16-bit PCMCIA support"
+	default y
+	---help---
+	   This option enables support for 16-bit PCMCIA cards. Most older
+	   PC-cards are such 16-bit PCMCIA cards, so unless you know you're
+	   only using 32-bit CardBus cards, say Y or M here.
+
+	   To use 16-bit PCMCIA cards, you will need supporting software from 
+	   David Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+ 	   for location).  Please also read the PCMCIA-HOWTO, available from
+	   <http://www.tldp.org/docs.html#howto>.
+
+	   To compile this driver as modules, choose M here: the
+	   module will be called pcmcia.
+
+	   If unsure, say Y.
+
+config CARDBUS
+	bool "32-bit CardBus support"	
+	depends on PCI
+	default y
+	---help---
+	  CardBus is a bus mastering architecture for PC-cards, which allows
+	  for 32 bit PC-cards (the original PCMCIA standard specifies only
+	  a 16 bit wide bus). Many newer PC-cards are actually CardBus cards.
+
+	  To use 32 bit PC-cards, you also need a CardBus compatible host
+	  bridge. Virtually all modern PCMCIA bridges do this, and most of
+	  them are "yenta-compatible", so say Y or M there, too.
+
+	  If unsure, say Y.
+
+comment "PC-card bridges"
+
+config YENTA
+	tristate "CardBus yenta-compatible bridge support"
+	depends on PCI
+#fixme: remove dependendcy on CARDBUS
+	depends on CARDBUS
+	select PCCARD_NONSTATIC
+	---help---
+	  This option enables support for CardBus host bridges.  Virtually
+	  all modern PCMCIA bridges are CardBus compatible.  A "bridge" is 
+	  the hardware inside your computer that PCMCIA cards are plugged 
+	  into.
+
+	  To compile this driver as modules, choose M here: the
+	  module will be called yenta_socket.
+
+	  If unsure, say Y.
+
+config PD6729
+	tristate "Cirrus PD6729 compatible bridge support"
+	depends on PCMCIA && PCI
+	select PCCARD_NONSTATIC
+	help
+	  This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
+	  device, found in some older laptops and PCMCIA card readers.
+
+config I82092
+	tristate "i82092 compatible bridge support"
+	depends on PCMCIA && PCI
+	select PCCARD_NONSTATIC
+	help
+	  This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device,
+	  found in some older laptops and more commonly in evaluation boards for the
+	  chip.
+
+config I82365
+	tristate "i82365 compatible bridge support"
+	depends on PCMCIA && ISA
+	select PCCARD_NONSTATIC
+	help
+	  Say Y here to include support for ISA-bus PCMCIA host bridges that
+	  are register compatible with the Intel i82365.  These are found on
+	  older laptops and ISA-bus card readers for desktop systems.  A
+	  "bridge" is the hardware inside your computer that PCMCIA cards are
+	  plugged into. If unsure, say N.
+
+config TCIC
+	tristate "Databook TCIC host bridge support"
+	depends on PCMCIA
+	select PCCARD_NONSTATIC
+	help
+	  Say Y here to include support for the Databook TCIC family of PCMCIA
+	  host bridges. These are only found on a handful of old systems.
+	  "Bridge" is the name used for the hardware inside your computer that
+	  PCMCIA cards are plugged into. If unsure, say N.
+
+config HD64465_PCMCIA
+	tristate "HD64465 host bridge support"
+	depends on HD64465 && PCMCIA
+
+config PCMCIA_AU1X00
+	tristate "Au1x00 pcmcia support"
+	depends on SOC_AU1X00 && PCMCIA
+
+config PCMCIA_SA1100
+	tristate "SA1100 support"
+	depends on ARM && ARCH_SA1100 && PCMCIA
+	help
+	  Say Y here to include support for SA11x0-based PCMCIA or CF
+	  sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
+	  Xscale(R) embedded machines.
+
+	  This driver is also available as a module called sa1100_cs.
+
+config PCMCIA_SA1111
+	tristate "SA1111 support"
+	depends on ARM && ARCH_SA1100 && SA1111 && PCMCIA
+	help
+	  Say Y  here to include support for SA1111-based PCMCIA or CF
+	  sockets, found on the Jornada 720, Graphicsmaster and other
+	  StrongARM(R)/Xscale(R) embedded machines.
+
+	  This driver is also available as a module called sa1111_cs.
+
+config PCMCIA_PXA2XX
+	tristate "PXA2xx support"
+	depends on ARM && ARCH_PXA && PCMCIA
+	help
+	  Say Y here to include support for the PXA2xx PCMCIA controller
+
+config PCMCIA_PROBE
+	bool
+	default y if ISA && !ARCH_SA1100 && !ARCH_CLPS711X
+
+config M32R_PCC
+	bool "M32R PCMCIA I/F"
+	depends on M32R && CHIP_M32700 && PCMCIA
+	help
+	  Say Y here to use the M32R PCMCIA controller.
+
+config M32R_CFC
+	bool "M32R CF I/F Controller"
+	depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_OPSPUT)
+	help
+	  Say Y here to use the M32R CompactFlash controller.
+
+config M32R_CFC_NUM
+	int "M32R CF I/F number"
+	depends on M32R_CFC
+	default "1" if PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_OPSPUT
+	help
+	  Set the number of M32R CF slots.
+
+config PCMCIA_VRC4171
+	tristate "NEC VRC4171 Card Controllers support"
+	depends on VRC4171 && PCMCIA
+
+config PCMCIA_VRC4173
+	tristate "NEC VRC4173 CARDU support"
+	depends on CPU_VR41XX && PCI && PCMCIA
+
+config PCCARD_NONSTATIC
+	tristate
+
+endif	# PCCARD
+
+endmenu
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
new file mode 100644
index 0000000..50c2936
--- /dev/null
+++ b/drivers/pcmcia/Makefile
@@ -0,0 +1,65 @@
+#
+# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
+#
+
+ifeq ($(CONFIG_PCMCIA_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+pcmcia_core-y					+= cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
+pcmcia_core-$(CONFIG_CARDBUS)			+= cardbus.o
+obj-$(CONFIG_PCCARD)				+= pcmcia_core.o
+
+pcmcia-y					+= ds.o pcmcia_compat.o
+obj-$(CONFIG_PCMCIA)				+= pcmcia.o
+
+obj-$(CONFIG_PCCARD_NONSTATIC)			+= rsrc_nonstatic.o
+
+
+# socket drivers
+
+obj-$(CONFIG_YENTA) 				+= yenta_socket.o
+
+obj-$(CONFIG_PD6729)				+= pd6729.o
+obj-$(CONFIG_I82365)				+= i82365.o
+obj-$(CONFIG_I82092)				+= i82092.o
+obj-$(CONFIG_TCIC)				+= tcic.o
+obj-$(CONFIG_HD64465_PCMCIA)			+= hd64465_ss.o
+obj-$(CONFIG_PCMCIA_SA1100)			+= sa11xx_core.o sa1100_cs.o
+obj-$(CONFIG_PCMCIA_SA1111)			+= sa11xx_core.o sa1111_cs.o
+obj-$(CONFIG_PCMCIA_PXA2XX)                     += pxa2xx_core.o pxa2xx_cs.o
+obj-$(CONFIG_M32R_PCC)				+= m32r_pcc.o
+obj-$(CONFIG_M32R_CFC)				+= m32r_cfc.o
+obj-$(CONFIG_PCMCIA_AU1X00)			+= au1x00_ss.o
+obj-$(CONFIG_PCMCIA_VRC4171)			+= vrc4171_card.o
+obj-$(CONFIG_PCMCIA_VRC4173)			+= vrc4173_cardu.o
+
+sa11xx_core-y					+= soc_common.o sa11xx_base.o
+pxa2xx_core-y					+= soc_common.o pxa2xx_base.o
+
+au1x00_ss-y					+= au1000_generic.o
+au1x00_ss-$(CONFIG_MIPS_PB1000)			+= au1000_pb1x00.o
+au1x00_ss-$(CONFIG_MIPS_PB1100)			+= au1000_pb1x00.o
+au1x00_ss-$(CONFIG_MIPS_PB1500)			+= au1000_pb1x00.o
+au1x00_ss-$(CONFIG_MIPS_DB1000)			+= au1000_db1x00.o
+au1x00_ss-$(CONFIG_MIPS_DB1100)			+= au1000_db1x00.o
+au1x00_ss-$(CONFIG_MIPS_DB1500)			+= au1000_db1x00.o
+au1x00_ss-$(CONFIG_MIPS_DB1550)			+= au1000_db1x00.o
+au1x00_ss-$(CONFIG_MIPS_XXS1500)               += au1000_xxs1500.o
+
+sa1111_cs-y					+= sa1111_generic.o
+sa1111_cs-$(CONFIG_ASSABET_NEPONSET)		+= sa1100_neponset.o
+sa1111_cs-$(CONFIG_SA1100_BADGE4)		+= sa1100_badge4.o
+sa1111_cs-$(CONFIG_SA1100_JORNADA720)		+= sa1100_jornada720.o
+
+sa1100_cs-y					+= sa1100_generic.o
+sa1100_cs-$(CONFIG_SA1100_ASSABET)		+= sa1100_assabet.o
+sa1100_cs-$(CONFIG_SA1100_CERF)			+= sa1100_cerf.o
+sa1100_cs-$(CONFIG_SA1100_H3600)		+= sa1100_h3600.o
+sa1100_cs-$(CONFIG_SA1100_SHANNON)		+= sa1100_shannon.o
+sa1100_cs-$(CONFIG_SA1100_SIMPAD)		+= sa1100_simpad.o
+
+pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK)		+= pxa2xx_lubbock.o sa1111_generic.o
+pxa2xx_cs-$(CONFIG_MACH_MAINSTONE)		+= pxa2xx_mainstone.o
+pxa2xx_cs-$(CONFIG_PXA_SHARPSL)			+= pxa2xx_sharpsl.o
+
diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c
new file mode 100644
index 0000000..42cf8bf
--- /dev/null
+++ b/drivers/pcmcia/au1000_db1x00.c
@@ -0,0 +1,288 @@
+/*
+ *
+ * Alchemy Semi Db1x00 boards specific pcmcia routines.
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ * Copyright 2004 Pete Popov, updated the driver to 2.6.
+ * Followed the sa11xx API and largely copied many of the hardware
+ * independent functions.
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/signal.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-db1x00/db1x00.h>
+
+#include "au1000_generic.h"
+
+#if 0
+#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
+#else
+#define debug(x,args...)
+#endif
+
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+
+struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS];
+extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int);
+
+static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt)
+{
+#ifdef CONFIG_MIPS_DB1550
+	skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3;
+#else
+	skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2;
+#endif
+	return 0;
+}
+
+static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt)
+{
+	bcsr->pcmcia = 0; /* turn off power */
+	au_sync_delay(2);
+}
+
+static void
+db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+	u32 inserted;
+	unsigned char vs;
+
+	state->ready = 0;
+	state->vs_Xv = 0;
+	state->vs_3v = 0;
+	state->detect = 0;
+
+	switch (skt->nr) {
+	case 0:
+		vs = bcsr->status & 0x3;
+		inserted = !(bcsr->status & (1<<4));
+		break;
+	case 1:
+		vs = (bcsr->status & 0xC)>>2;
+		inserted = !(bcsr->status & (1<<5));
+		break;
+	default:/* should never happen */
+		return;
+	}
+
+	if (inserted)
+		debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n",
+				skt->nr, inserted, vs, bcsr->pcmcia);
+
+	if (inserted) {
+		switch (vs) {
+			case 0:
+			case 2:
+				state->vs_3v=1;
+				break;
+			case 3: /* 5V */
+				break;
+			default:
+				/* return without setting 'detect' */
+				printk(KERN_ERR "db1x00 bad VS (%d)\n",
+						vs);
+		}
+		state->detect = 1;
+		state->ready = 1;
+	}
+	else {
+		/* if the card was previously inserted and then ejected,
+		 * we should turn off power to it
+		 */
+		if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) {
+			bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST |
+					BCSR_PCMCIA_PC0DRVEN |
+					BCSR_PCMCIA_PC0VPP |
+					BCSR_PCMCIA_PC0VCC);
+			au_sync_delay(10);
+		}
+		else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) {
+			bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST |
+					BCSR_PCMCIA_PC1DRVEN |
+					BCSR_PCMCIA_PC1VPP |
+					BCSR_PCMCIA_PC1VCC);
+			au_sync_delay(10);
+		}
+	}
+
+	state->bvd1=1;
+	state->bvd2=1;
+	state->wrprot=0;
+}
+
+static int
+db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state)
+{
+	u16 pwr;
+	int sock = skt->nr;
+
+	debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n",
+			sock, state->Vcc, state->Vpp,
+			state->flags & SS_RESET);
+
+	/* pcmcia reg was set to zero at init time. Be careful when
+	 * initializing a socket not to wipe out the settings of the
+	 * other socket.
+	 */
+	pwr = bcsr->pcmcia;
+	pwr &= ~(0xf << sock*8); /* clear voltage settings */
+
+	state->Vpp = 0;
+	switch(state->Vcc){
+		case 0:  /* Vcc 0 */
+			pwr |= SET_VCC_VPP(0,0,sock);
+			break;
+		case 50: /* Vcc 5V */
+			switch(state->Vpp) {
+				case 0:
+					pwr |= SET_VCC_VPP(2,0,sock);
+					break;
+				case 50:
+					pwr |= SET_VCC_VPP(2,1,sock);
+					break;
+				case 12:
+					pwr |= SET_VCC_VPP(2,2,sock);
+					break;
+				case 33:
+				default:
+					pwr |= SET_VCC_VPP(0,0,sock);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n",
+							__FUNCTION__,
+							state->Vcc,
+							state->Vpp);
+					break;
+			}
+			break;
+		case 33: /* Vcc 3.3V */
+			switch(state->Vpp) {
+				case 0:
+					pwr |= SET_VCC_VPP(1,0,sock);
+					break;
+				case 12:
+					pwr |= SET_VCC_VPP(1,2,sock);
+					break;
+				case 33:
+					pwr |= SET_VCC_VPP(1,1,sock);
+					break;
+				case 50:
+				default:
+					pwr |= SET_VCC_VPP(0,0,sock);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n",
+							__FUNCTION__,
+							state->Vcc,
+							state->Vpp);
+					break;
+			}
+			break;
+		default: /* what's this ? */
+			pwr |= SET_VCC_VPP(0,0,sock);
+			printk(KERN_ERR "%s: bad Vcc %d\n",
+					__FUNCTION__, state->Vcc);
+			break;
+	}
+
+	bcsr->pcmcia = pwr;
+	au_sync_delay(300);
+
+	if (sock == 0) {
+		if (!(state->flags & SS_RESET)) {
+			pwr |= BCSR_PCMCIA_PC0DRVEN;
+			bcsr->pcmcia = pwr;
+			au_sync_delay(300);
+			pwr |= BCSR_PCMCIA_PC0RST;
+			bcsr->pcmcia = pwr;
+			au_sync_delay(100);
+		}
+		else {
+			pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN);
+			bcsr->pcmcia = pwr;
+			au_sync_delay(100);
+		}
+	}
+	else {
+		if (!(state->flags & SS_RESET)) {
+			pwr |= BCSR_PCMCIA_PC1DRVEN;
+			bcsr->pcmcia = pwr;
+			au_sync_delay(300);
+			pwr |= BCSR_PCMCIA_PC1RST;
+			bcsr->pcmcia = pwr;
+			au_sync_delay(100);
+		}
+		else {
+			pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN);
+			bcsr->pcmcia = pwr;
+			au_sync_delay(100);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation.  This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+void db1x00_socket_init(struct au1000_pcmcia_socket *skt)
+{
+	/* nothing to do for now */
+}
+
+/*
+ * Disable card status IRQs and PCMCIA bus on suspend.
+ */
+void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt)
+{
+	/* nothing to do for now */
+}
+
+struct pcmcia_low_level db1x00_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+
+	.hw_init 		= db1x00_pcmcia_hw_init,
+	.hw_shutdown		= db1x00_pcmcia_shutdown,
+
+	.socket_state		= db1x00_pcmcia_socket_state,
+	.configure_socket	= db1x00_pcmcia_configure_socket,
+
+	.socket_init		= db1x00_socket_init,
+	.socket_suspend		= db1x00_socket_suspend
+};
+
+int __init au1x_board_init(struct device *dev)
+{
+	int ret = -ENODEV;
+	bcsr->pcmcia = 0; /* turn off power, if it's not already off */
+	au_sync_delay(2);
+	ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2);
+	return ret;
+}
diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c
new file mode 100644
index 0000000..9fa7f15
--- /dev/null
+++ b/drivers/pcmcia/au1000_generic.c
@@ -0,0 +1,579 @@
+/*
+ *
+ * Alchemy Semi Au1000 pcmcia driver
+ *
+ * Copyright 2001-2003 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@embeddedalley.com or source@mvista.com
+ *
+ * Copyright 2004 Pete Popov, Embedded Alley Solutions, Inc.
+ * Updated the driver to 2.6. Followed the sa11xx API and largely
+ * copied many of the hardware independent functions.
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include "au1000_generic.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pete Popov <ppopov@embeddedalley.com>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: Au1x00 Socket Controller");
+
+#if 0
+#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
+#else
+#define debug(x,args...)
+#endif
+
+#define MAP_SIZE 0x100000
+extern struct au1000_pcmcia_socket au1000_pcmcia_socket[];
+#define PCMCIA_SOCKET(x)	(au1000_pcmcia_socket + (x))
+#define to_au1000_socket(x)	container_of(x, struct au1000_pcmcia_socket, socket)
+
+/* Some boards like to support CF cards as IDE root devices, so they
+ * grab pcmcia sockets directly.
+ */
+u32 *pcmcia_base_vaddrs[2];
+extern const unsigned long mips_io_port_base;
+
+DECLARE_MUTEX(pcmcia_sockets_lock);
+
+static int (*au1x00_pcmcia_hw_init[])(struct device *dev) = {
+	au1x_board_init,
+};
+
+static int
+au1x00_pcmcia_skt_state(struct au1000_pcmcia_socket *skt)
+{
+	struct pcmcia_state state;
+	unsigned int stat;
+
+	memset(&state, 0, sizeof(struct pcmcia_state));
+
+	skt->ops->socket_state(skt, &state);
+
+	stat = state.detect  ? SS_DETECT : 0;
+	stat |= state.ready  ? SS_READY  : 0;
+	stat |= state.wrprot ? SS_WRPROT : 0;
+	stat |= state.vs_3v  ? SS_3VCARD : 0;
+	stat |= state.vs_Xv  ? SS_XVCARD : 0;
+	stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
+
+	if (skt->cs_state.flags & SS_IOCARD)
+		stat |= state.bvd1 ? SS_STSCHG : 0;
+	else {
+		if (state.bvd1 == 0)
+			stat |= SS_BATDEAD;
+		else if (state.bvd2 == 0)
+			stat |= SS_BATWARN;
+	}
+	return stat;
+}
+
+/*
+ * au100_pcmcia_config_skt
+ *
+ * Convert PCMCIA socket state to our socket configure structure.
+ */
+static int
+au1x00_pcmcia_config_skt(struct au1000_pcmcia_socket *skt, socket_state_t *state)
+{
+	int ret;
+
+	ret = skt->ops->configure_socket(skt, state);
+	if (ret == 0) {
+		skt->cs_state = *state;
+	}
+
+	if (ret < 0)
+		debug("unable to configure socket %d\n", skt->nr);
+
+	return ret;
+}
+
+/* au1x00_pcmcia_sock_init()
+ *
+ * (Re-)Initialise the socket, turning on status interrupts
+ * and PCMCIA bus.  This must wait for power to stabilise
+ * so that the card status signals report correctly.
+ *
+ * Returns: 0
+ */
+static int au1x00_pcmcia_sock_init(struct pcmcia_socket *sock)
+{
+	struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+
+	debug("initializing socket %u\n", skt->nr);
+
+	skt->ops->socket_init(skt);
+	return 0;
+}
+
+/*
+ * au1x00_pcmcia_suspend()
+ *
+ * Remove power on the socket, disable IRQs from the card.
+ * Turn off status interrupts, and disable the PCMCIA bus.
+ *
+ * Returns: 0
+ */
+static int au1x00_pcmcia_suspend(struct pcmcia_socket *sock)
+{
+	struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+
+	debug("suspending socket %u\n", skt->nr);
+
+	skt->ops->socket_suspend(skt);
+
+	return 0;
+}
+
+static DEFINE_SPINLOCK(status_lock);
+
+/*
+ * au1x00_check_status()
+ */
+static void au1x00_check_status(struct au1000_pcmcia_socket *skt)
+{
+	unsigned int events;
+
+	debug("entering PCMCIA monitoring thread\n");
+
+	do {
+		unsigned int status;
+		unsigned long flags;
+
+		status = au1x00_pcmcia_skt_state(skt);
+
+		spin_lock_irqsave(&status_lock, flags);
+		events = (status ^ skt->status) & skt->cs_state.csc_mask;
+		skt->status = status;
+		spin_unlock_irqrestore(&status_lock, flags);
+
+		debug("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 "  : "");
+
+		if (events)
+			pcmcia_parse_events(&skt->socket, events);
+	} while (events);
+}
+
+/* 
+ * au1x00_pcmcia_poll_event()
+ * Let's poll for events in addition to IRQs since IRQ only is unreliable...
+ */
+static void au1x00_pcmcia_poll_event(unsigned long dummy)
+{
+	struct au1000_pcmcia_socket *skt = (struct au1000_pcmcia_socket *)dummy;
+	debug("polling for events\n");
+
+	mod_timer(&skt->poll_timer, jiffies + AU1000_PCMCIA_POLL_PERIOD);
+
+	au1x00_check_status(skt);
+}
+
+/* au1x00_pcmcia_get_status()
+ *
+ * From the sa11xx_core.c:
+ * 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
+au1x00_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status)
+{
+	struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+
+	skt->status = au1x00_pcmcia_skt_state(skt);
+	*status = skt->status;
+
+	return 0;
+}
+
+/* au1x00_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
+au1x00_pcmcia_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+  struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+
+  debug("for sock %u\n", skt->nr);
+  *state = skt->cs_state;
+  return 0;
+}
+
+/* au1x00_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
+au1x00_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+  struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+
+  debug("for sock %u\n", skt->nr);
+
+  debug("\tmask:  %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\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 ":"");
+  debug("\tVcc %d  Vpp %d  irq %d\n",
+	state->Vcc, state->Vpp, state->io_irq);
+
+  return au1x00_pcmcia_config_skt(skt, state);
+}
+
+int 
+au1x00_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map)
+{
+	struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+	unsigned int speed;
+
+	if(map->map>=MAX_IO_WIN){
+		debug("map (%d) out of range\n", map->map);
+		return -1;
+	}
+
+	if(map->flags&MAP_ACTIVE){
+		speed=(map->speed>0)?map->speed:AU1000_PCMCIA_IO_SPEED;
+		skt->spd_io[map->map] = speed;
+	}
+
+	map->start=(ioaddr_t)(u32)skt->virt_io;
+	map->stop=map->start+MAP_SIZE;
+	return 0;
+
+}  /* au1x00_pcmcia_set_io_map() */
+
+
+static int 
+au1x00_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map)
+{
+	struct au1000_pcmcia_socket *skt = to_au1000_socket(sock);
+	unsigned short speed = map->speed;
+
+	if(map->map>=MAX_WIN){
+		debug("map (%d) out of range\n", map->map);
+		return -1;
+	}
+
+	if (map->flags & MAP_ATTRIB) {
+		skt->spd_attr[map->map] = speed;
+		skt->spd_mem[map->map] = 0;
+	} else {
+		skt->spd_attr[map->map] = 0;
+		skt->spd_mem[map->map] = speed;
+	}
+
+	if (map->flags & MAP_ATTRIB) {
+		map->static_start = skt->phys_attr + map->card_start;
+	}
+	else {
+		map->static_start = skt->phys_mem + map->card_start;
+	}
+
+	debug("set_mem_map %d start %08lx card_start %08x\n",
+			map->map, map->static_start, map->card_start);
+	return 0;
+
+}  /* au1x00_pcmcia_set_mem_map() */
+
+static struct pccard_operations au1x00_pcmcia_operations = {
+	.init			= au1x00_pcmcia_sock_init,
+	.suspend		= au1x00_pcmcia_suspend,
+	.get_status		= au1x00_pcmcia_get_status,
+	.get_socket		= au1x00_pcmcia_get_socket,
+	.set_socket		= au1x00_pcmcia_set_socket,
+	.set_io_map		= au1x00_pcmcia_set_io_map,
+	.set_mem_map		= au1x00_pcmcia_set_mem_map,
+};
+
+static const char *skt_names[] = {
+	"PCMCIA socket 0",
+	"PCMCIA socket 1",
+};
+
+struct skt_dev_info {
+	int nskt;
+};
+
+int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr)
+{
+	struct skt_dev_info *sinfo;
+	int ret, i;
+
+	sinfo = kmalloc(sizeof(struct skt_dev_info), GFP_KERNEL);
+	if (!sinfo) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(sinfo, 0, sizeof(struct skt_dev_info));
+	sinfo->nskt = nr;
+
+	/*
+	 * Initialise the per-socket structure.
+	 */
+	for (i = 0; i < nr; i++) {
+		struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i);
+		memset(skt, 0, sizeof(*skt));
+
+		skt->socket.ops = &au1x00_pcmcia_operations;
+		skt->socket.owner = ops->owner;
+		skt->socket.dev.dev = dev;
+
+		init_timer(&skt->poll_timer);
+		skt->poll_timer.function = au1x00_pcmcia_poll_event;
+		skt->poll_timer.data = (unsigned long)skt;
+		skt->poll_timer.expires = jiffies + AU1000_PCMCIA_POLL_PERIOD;
+
+		skt->nr		= first + i;
+		skt->irq	= 255;
+		skt->dev	= dev;
+		skt->ops	= ops;
+
+		skt->res_skt.name	= skt_names[skt->nr];
+		skt->res_io.name	= "io";
+		skt->res_io.flags	= IORESOURCE_MEM | IORESOURCE_BUSY;
+		skt->res_mem.name	= "memory";
+		skt->res_mem.flags	= IORESOURCE_MEM;
+		skt->res_attr.name	= "attribute";
+		skt->res_attr.flags	= IORESOURCE_MEM;
+
+		/*
+		 * PCMCIA client drivers use the inb/outb macros to access the
+		 * IO registers. Since mips_io_port_base is added to the
+		 * access address of the mips implementation of inb/outb,
+		 * we need to subtract it here because we want to access the
+		 * I/O or MEM address directly, without going through this
+		 * "mips_io_port_base" mechanism.
+		 */
+		if (i == 0) {
+			skt->virt_io = (void *)
+				(ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) -
+				(u32)mips_io_port_base);
+			skt->phys_attr = AU1X_SOCK0_PSEUDO_PHYS_ATTR;
+			skt->phys_mem = AU1X_SOCK0_PSEUDO_PHYS_MEM;
+		}
+#ifndef CONFIG_MIPS_XXS1500
+		else  {
+			skt->virt_io = (void *)
+				(ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) -
+				(u32)mips_io_port_base);
+			skt->phys_attr = AU1X_SOCK1_PSEUDO_PHYS_ATTR;
+			skt->phys_mem = AU1X_SOCK1_PSEUDO_PHYS_MEM;
+		}
+#endif
+		pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io;
+		ret = ops->hw_init(skt);
+
+		skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
+		skt->socket.irq_mask = 0;
+		skt->socket.map_size = MAP_SIZE;
+		skt->socket.pci_irq = skt->irq;
+		skt->socket.io_offset = (unsigned long)skt->virt_io;
+
+		skt->status = au1x00_pcmcia_skt_state(skt);
+
+		ret = pcmcia_register_socket(&skt->socket);
+		if (ret)
+			goto out_err;
+
+		WARN_ON(skt->socket.sock != i);
+
+		add_timer(&skt->poll_timer);
+	}
+
+	dev_set_drvdata(dev, sinfo);
+	return 0;
+
+	do {
+		struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i);
+
+		del_timer_sync(&skt->poll_timer);
+		pcmcia_unregister_socket(&skt->socket);
+out_err:
+		flush_scheduled_work();
+		ops->hw_shutdown(skt);
+
+		i--;
+	} while (i > 0);
+	kfree(sinfo);
+out:
+	return ret;
+}
+
+int au1x00_drv_pcmcia_remove(struct device *dev)
+{
+	struct skt_dev_info *sinfo = dev_get_drvdata(dev);
+	int i;
+
+	down(&pcmcia_sockets_lock);
+	dev_set_drvdata(dev, NULL);
+
+	for (i = 0; i < sinfo->nskt; i++) {
+		struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i);
+
+		del_timer_sync(&skt->poll_timer);
+		pcmcia_unregister_socket(&skt->socket);
+		flush_scheduled_work();
+		skt->ops->hw_shutdown(skt);
+		au1x00_pcmcia_config_skt(skt, &dead_socket);
+		iounmap(skt->virt_io);
+		skt->virt_io = NULL;
+	}
+
+	kfree(sinfo);
+	up(&pcmcia_sockets_lock);
+	return 0;
+}
+
+
+/*
+ * PCMCIA "Driver" API
+ */
+
+static int au1x00_drv_pcmcia_probe(struct device *dev)
+{
+	int i, ret = -ENODEV;
+
+	down(&pcmcia_sockets_lock);
+	for (i=0; i < ARRAY_SIZE(au1x00_pcmcia_hw_init); i++) {
+		ret = au1x00_pcmcia_hw_init[i](dev);
+		if (ret == 0)
+			break;
+	}
+	up(&pcmcia_sockets_lock);
+	return ret;
+}
+
+
+static int au1x00_drv_pcmcia_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int au1x00_drv_pcmcia_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+
+static struct device_driver au1x00_pcmcia_driver = {
+	.probe		= au1x00_drv_pcmcia_probe,
+	.remove		= au1x00_drv_pcmcia_remove,
+	.name		= "au1x00-pcmcia",
+	.bus		= &platform_bus_type,
+	.suspend	= au1x00_drv_pcmcia_suspend,
+	.resume		= au1x00_drv_pcmcia_resume
+};
+
+static struct platform_device au1x00_device = {
+	.name = "au1x00-pcmcia",
+	.id = 0,
+};
+
+/* au1x00_pcmcia_init()
+ *
+ * This routine performs low-level PCMCIA initialization and then
+ * registers this socket driver with Card Services.
+ *
+ * Returns: 0 on success, -ve error code on failure
+ */
+static int __init au1x00_pcmcia_init(void)
+{
+	int error = 0;
+	if ((error = driver_register(&au1x00_pcmcia_driver)))
+		return error;
+	platform_device_register(&au1x00_device);
+	return error;
+}
+
+/* au1x00_pcmcia_exit()
+ * Invokes the low-level kernel service to free IRQs associated with this
+ * socket controller and reset GPIO edge detection.
+ */
+static void __exit au1x00_pcmcia_exit(void)
+{
+	driver_unregister(&au1x00_pcmcia_driver);
+	platform_device_unregister(&au1x00_device);
+}
+
+module_init(au1x00_pcmcia_init);
+module_exit(au1x00_pcmcia_exit);
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h
new file mode 100644
index 0000000..417bc15
--- /dev/null
+++ b/drivers/pcmcia/au1000_generic.h
@@ -0,0 +1,150 @@
+/*
+ * Alchemy Semi Au1000 pcmcia driver include file
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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 __ASM_AU1000_PCMCIA_H
+#define __ASM_AU1000_PCMCIA_H
+
+/* include the world */
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+#define AU1000_PCMCIA_POLL_PERIOD    (2*HZ)
+#define AU1000_PCMCIA_IO_SPEED       (255)
+#define AU1000_PCMCIA_MEM_SPEED      (300)
+
+#define AU1X_SOCK0_IO        0xF00000000
+#define AU1X_SOCK0_PHYS_ATTR 0xF40000000
+#define AU1X_SOCK0_PHYS_MEM  0xF80000000
+/* pseudo 32 bit phys addresses, which get fixed up to the
+ * real 36 bit address in fixup_bigphys_addr() */
+#define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000
+#define AU1X_SOCK0_PSEUDO_PHYS_MEM  0xF8000000
+
+/* pcmcia socket 1 needs external glue logic so the memory map
+ * differs from board to board.
+ */
+#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550)
+#define AU1X_SOCK1_IO        0xF08000000
+#define AU1X_SOCK1_PHYS_ATTR 0xF48000000
+#define AU1X_SOCK1_PHYS_MEM  0xF88000000
+#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000
+#define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8800000
+#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550)
+#define AU1X_SOCK1_IO        0xF04000000
+#define AU1X_SOCK1_PHYS_ATTR 0xF44000000
+#define AU1X_SOCK1_PHYS_MEM  0xF84000000
+#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000
+#define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8400000
+#endif
+
+struct pcmcia_state {
+  unsigned detect: 1,
+            ready: 1,
+           wrprot: 1,
+	     bvd1: 1,
+	     bvd2: 1,
+            vs_3v: 1,
+            vs_Xv: 1;
+};
+
+struct pcmcia_configure {
+  unsigned sock: 8,
+            vcc: 8,
+            vpp: 8,
+         output: 1,
+        speaker: 1,
+          reset: 1;
+};
+
+struct pcmcia_irqs {
+	int sock;
+	int irq;
+	const char *str;
+};
+
+
+struct au1000_pcmcia_socket {
+	struct pcmcia_socket socket;
+
+	/*
+	 * Info from low level handler
+	 */
+	struct device		*dev;
+	unsigned int		nr;
+	unsigned int		irq;
+
+	/*
+	 * Core PCMCIA state
+	 */
+	struct pcmcia_low_level *ops;
+
+	unsigned int 		status;
+	socket_state_t		cs_state;
+
+	unsigned short		spd_io[MAX_IO_WIN];
+	unsigned short		spd_mem[MAX_WIN];
+	unsigned short		spd_attr[MAX_WIN];
+
+	struct resource		res_skt;
+	struct resource		res_io;
+	struct resource		res_mem;
+	struct resource		res_attr;
+
+	void *                 	virt_io;
+	ioaddr_t              	phys_io;
+	unsigned int           	phys_attr;
+	unsigned int           	phys_mem;
+	unsigned short        	speed_io, speed_attr, speed_mem;
+
+	unsigned int		irq_state;
+
+	struct timer_list	poll_timer;
+};
+
+struct pcmcia_low_level {
+	struct module *owner;
+
+	int (*hw_init)(struct au1000_pcmcia_socket *);
+	void (*hw_shutdown)(struct au1000_pcmcia_socket *);
+
+	void (*socket_state)(struct au1000_pcmcia_socket *, struct pcmcia_state *);
+	int (*configure_socket)(struct au1000_pcmcia_socket *, struct socket_state_t *);
+
+	/*
+	 * Enable card status IRQs on (re-)initialisation.  This can
+	 * be called at initialisation, power management event, or
+	 * pcmcia event.
+	 */
+	void (*socket_init)(struct au1000_pcmcia_socket *);
+
+	/*
+	 * Disable card status IRQs and PCMCIA bus on suspend.
+	 */
+	void (*socket_suspend)(struct au1000_pcmcia_socket *);
+};
+
+extern int au1x_board_init(struct device *dev);
+
+#endif /* __ASM_AU1000_PCMCIA_H */
diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c
new file mode 100644
index 0000000..df19ce1
--- /dev/null
+++ b/drivers/pcmcia/au1000_pb1x00.c
@@ -0,0 +1,419 @@
+/*
+ *
+ * Alchemy Semi Pb1x00 boards specific pcmcia routines.
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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/init.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/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/bus_ops.h>
+#include "cs_internal.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/au1000.h>
+#include <asm/au1000_pcmcia.h>
+
+#define debug(fmt, arg...) do { } while (0)
+
+#ifdef CONFIG_MIPS_PB1000
+#include <asm/pb1000.h>
+#define PCMCIA_IRQ AU1000_GPIO_15
+#elif defined (CONFIG_MIPS_PB1500)
+#include <asm/pb1500.h>
+#define PCMCIA_IRQ AU1500_GPIO_203
+#elif defined (CONFIG_MIPS_PB1100)
+#include <asm/pb1100.h>
+#define PCMCIA_IRQ AU1000_GPIO_11
+#endif
+
+static int pb1x00_pcmcia_init(struct pcmcia_init *init)
+{
+#ifdef CONFIG_MIPS_PB1000
+	u16 pcr;
+	pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST;
+
+	au_writel(0x8000, PB1000_MDR); /* clear pcmcia interrupt */
+	au_sync_delay(100);
+	au_writel(0x4000, PB1000_MDR); /* enable pcmcia interrupt */
+	au_sync();
+
+	pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0);
+	pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,1);
+	au_writel(pcr, PB1000_PCR);
+	au_sync_delay(20);
+	  
+	return PCMCIA_NUM_SOCKS;
+
+#else /* fixme -- take care of the Pb1500 at some point */
+
+	u16 pcr;
+	pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */
+	pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
+	au_writew(pcr, PCMCIA_BOARD_REG);
+	au_sync_delay(500);
+	return PCMCIA_NUM_SOCKS;
+#endif
+}
+
+static int pb1x00_pcmcia_shutdown(void)
+{
+#ifdef CONFIG_MIPS_PB1000
+	u16 pcr;
+	pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST;
+	pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0);
+	pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,1);
+	au_writel(pcr, PB1000_PCR);
+	au_sync_delay(20);
+	return 0;
+#else
+	u16 pcr;
+	pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */
+	pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
+	au_writew(pcr, PCMCIA_BOARD_REG);
+	au_sync_delay(2);
+	return 0;
+#endif
+}
+
+static int 
+pb1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
+{
+	u32 inserted0, inserted1;
+	u16 vs0, vs1;
+
+#ifdef CONFIG_MIPS_PB1000
+	vs0 = vs1 = (u16)au_readl(PB1000_ACR1);
+	inserted0 = !(vs0 & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2));
+	inserted1 = !(vs1 & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2));
+	vs0 = (vs0 >> 4) & 0x3;
+	vs1 = (vs1 >> 12) & 0x3;
+#else
+	vs0 = (au_readw(BOARD_STATUS_REG) >> 4) & 0x3;
+#ifdef CONFIG_MIPS_PB1500
+	inserted0 = !((au_readl(GPIO2_PINSTATE) >> 1) & 0x1); /* gpio 201 */
+#else /* Pb1100 */
+	inserted0 = !((au_readl(SYS_PINSTATERD) >> 9) & 0x1); /* gpio 9 */
+#endif
+	inserted1 = 0;
+#endif
+
+	state->ready = 0;
+	state->vs_Xv = 0;
+	state->vs_3v = 0;
+	state->detect = 0;
+
+	if (sock == 0) {
+		if (inserted0) {
+			switch (vs0) {
+				case 0:
+				case 2:
+					state->vs_3v=1;
+					break;
+				case 3: /* 5V */
+					break;
+				default:
+					/* return without setting 'detect' */
+					printk(KERN_ERR "pb1x00 bad VS (%d)\n",
+							vs0);
+					return 0;
+			}
+			state->detect = 1;
+		}
+	}
+	else  {
+		if (inserted1) {
+			switch (vs1) {
+				case 0:
+				case 2:
+					state->vs_3v=1;
+					break;
+				case 3: /* 5V */
+					break;
+				default:
+					/* return without setting 'detect' */
+					printk(KERN_ERR "pb1x00 bad VS (%d)\n",
+							vs1);
+					return 0;
+			}
+			state->detect = 1;
+		}
+	}
+
+	if (state->detect) {
+		state->ready = 1;
+	}
+
+	state->bvd1=1;
+	state->bvd2=1;
+	state->wrprot=0; 
+	return 1;
+}
+
+
+static int pb1x00_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
+{
+
+	if(info->sock > PCMCIA_MAX_SOCK) return -1;
+
+	/*
+	 * Even in the case of the Pb1000, both sockets are connected
+	 * to the same irq line.
+	 */
+	info->irq = PCMCIA_IRQ;
+
+	return 0;
+}
+
+
+static int 
+pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure)
+{
+	u16 pcr;
+
+	if(configure->sock > PCMCIA_MAX_SOCK) return -1;
+
+#ifdef CONFIG_MIPS_PB1000
+	pcr = au_readl(PB1000_PCR);
+
+	if (configure->sock == 0) {
+		pcr &= ~(PCR_SLOT_0_VCC0 | PCR_SLOT_0_VCC1 | 
+				PCR_SLOT_0_VPP0 | PCR_SLOT_0_VPP1);
+	}
+	else  {
+		pcr &= ~(PCR_SLOT_1_VCC0 | PCR_SLOT_1_VCC1 | 
+				PCR_SLOT_1_VPP0 | PCR_SLOT_1_VPP1);
+	}
+
+	pcr &= ~PCR_SLOT_0_RST;
+	debug("Vcc %dV Vpp %dV, pcr %x\n", 
+			configure->vcc, configure->vpp, pcr);
+	switch(configure->vcc){
+		case 0:  /* Vcc 0 */
+			switch(configure->vpp) {
+				case 0:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_GND,
+							configure->sock);
+					break;
+				case 12:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_12V,
+							configure->sock);
+					break;
+				case 50:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_5V,
+							configure->sock);
+					break;
+				case 33:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_3V,
+							configure->sock);
+					break;
+				default:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,
+							configure->sock);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n", 
+							__FUNCTION__, 
+							configure->vcc, 
+							configure->vpp);
+					break;
+			}
+			break;
+		case 50: /* Vcc 5V */
+			switch(configure->vpp) {
+				case 0:
+					pcr |= SET_VCC_VPP(VCC_5V,VPP_GND,
+							configure->sock);
+					break;
+				case 50:
+					pcr |= SET_VCC_VPP(VCC_5V,VPP_5V,
+							configure->sock);
+					break;
+				case 12:
+					pcr |= SET_VCC_VPP(VCC_5V,VPP_12V,
+							configure->sock);
+					break;
+				case 33:
+					pcr |= SET_VCC_VPP(VCC_5V,VPP_3V,
+							configure->sock);
+					break;
+				default:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,
+							configure->sock);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n", 
+							__FUNCTION__, 
+							configure->vcc, 
+							configure->vpp);
+					break;
+			}
+			break;
+		case 33: /* Vcc 3.3V */
+			switch(configure->vpp) {
+				case 0:
+					pcr |= SET_VCC_VPP(VCC_3V,VPP_GND,
+							configure->sock);
+					break;
+				case 50:
+					pcr |= SET_VCC_VPP(VCC_3V,VPP_5V,
+							configure->sock);
+					break;
+				case 12:
+					pcr |= SET_VCC_VPP(VCC_3V,VPP_12V,
+							configure->sock);
+					break;
+				case 33:
+					pcr |= SET_VCC_VPP(VCC_3V,VPP_3V,
+							configure->sock);
+					break;
+				default:
+					pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,
+							configure->sock);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n", 
+							__FUNCTION__, 
+							configure->vcc, 
+							configure->vpp);
+					break;
+			}
+			break;
+		default: /* what's this ? */
+			pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,configure->sock);
+			printk(KERN_ERR "%s: bad Vcc %d\n", 
+					__FUNCTION__, configure->vcc);
+			break;
+	}
+
+	if (configure->sock == 0) {
+	pcr &= ~(PCR_SLOT_0_RST);
+		if (configure->reset)
+		pcr |= PCR_SLOT_0_RST;
+	}
+	else {
+		pcr &= ~(PCR_SLOT_1_RST);
+		if (configure->reset)
+			pcr |= PCR_SLOT_1_RST;
+	}
+	au_writel(pcr, PB1000_PCR);
+	au_sync_delay(300);
+
+#else
+
+	pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf;
+
+	debug("Vcc %dV Vpp %dV, pcr %x, reset %d\n", 
+			configure->vcc, configure->vpp, pcr, configure->reset);
+
+
+	switch(configure->vcc){
+		case 0:  /* Vcc 0 */
+			pcr |= SET_VCC_VPP(0,0);
+			break;
+		case 50: /* Vcc 5V */
+			switch(configure->vpp) {
+				case 0:
+					pcr |= SET_VCC_VPP(2,0);
+					break;
+				case 50:
+					pcr |= SET_VCC_VPP(2,1);
+					break;
+				case 12:
+					pcr |= SET_VCC_VPP(2,2);
+					break;
+				case 33:
+				default:
+					pcr |= SET_VCC_VPP(0,0);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n", 
+							__FUNCTION__, 
+							configure->vcc, 
+							configure->vpp);
+					break;
+			}
+			break;
+		case 33: /* Vcc 3.3V */
+			switch(configure->vpp) {
+				case 0:
+					pcr |= SET_VCC_VPP(1,0);
+					break;
+				case 12:
+					pcr |= SET_VCC_VPP(1,2);
+					break;
+				case 33:
+					pcr |= SET_VCC_VPP(1,1);
+					break;
+				case 50:
+				default:
+					pcr |= SET_VCC_VPP(0,0);
+					printk("%s: bad Vcc/Vpp (%d:%d)\n", 
+							__FUNCTION__, 
+							configure->vcc, 
+							configure->vpp);
+					break;
+			}
+			break;
+		default: /* what's this ? */
+			pcr |= SET_VCC_VPP(0,0);
+			printk(KERN_ERR "%s: bad Vcc %d\n", 
+					__FUNCTION__, configure->vcc);
+			break;
+	}
+
+	au_writew(pcr, PCMCIA_BOARD_REG);
+	au_sync_delay(300);
+
+	if (!configure->reset) {
+		pcr |= PC_DRV_EN;
+		au_writew(pcr, PCMCIA_BOARD_REG);
+		au_sync_delay(100);
+		pcr |= PC_DEASSERT_RST;
+		au_writew(pcr, PCMCIA_BOARD_REG);
+		au_sync_delay(100);
+	}
+	else {
+		pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN);
+		au_writew(pcr, PCMCIA_BOARD_REG);
+		au_sync_delay(100);
+	}
+#endif
+	return 0;
+}
+
+
+struct pcmcia_low_level pb1x00_pcmcia_ops = { 
+	pb1x00_pcmcia_init,
+	pb1x00_pcmcia_shutdown,
+	pb1x00_pcmcia_socket_state,
+	pb1x00_pcmcia_get_irq_info,
+	pb1x00_pcmcia_configure_socket
+};
diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c
new file mode 100644
index 0000000..1dfc776
--- /dev/null
+++ b/drivers/pcmcia/au1000_xxs1500.c
@@ -0,0 +1,191 @@
+/*
+ *
+ * MyCable board specific pcmcia routines.
+ *
+ * Copyright 2003 MontaVista Software Inc.
+ * Author: Pete Popov, MontaVista Software, Inc.
+ *         	ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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/init.h>
+#include <linux/config.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/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/bus_ops.h>
+#include "cs_internal.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/au1000.h>
+#include <asm/au1000_pcmcia.h>
+#include <asm/xxs1500.h>
+
+#if 0
+#define DEBUG(x,args...)	printk(__FUNCTION__ ": " x,##args)
+#else
+#define DEBUG(x,args...)
+#endif
+
+static int xxs1500_pcmcia_init(struct pcmcia_init *init)
+{
+	return PCMCIA_NUM_SOCKS;
+}
+
+static int xxs1500_pcmcia_shutdown(void)
+{
+	/* turn off power */
+	au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
+			GPIO2_OUTPUT);
+	au_sync_delay(100);
+
+	/* assert reset */
+	au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
+			GPIO2_OUTPUT);
+	au_sync_delay(100);
+	return 0;
+}
+
+
+static int
+xxs1500_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
+{
+	u32 inserted; u32 vs;
+	unsigned long gpio, gpio2;
+
+	if(sock > PCMCIA_MAX_SOCK) return -1;
+
+	gpio = au_readl(SYS_PINSTATERD);
+	gpio2 = au_readl(GPIO2_PINSTATE);
+
+	vs = gpio2 & ((1<<8) | (1<<9));
+	inserted = (!(gpio & 0x1) && !(gpio & 0x2));
+
+	state->ready = 0;
+	state->vs_Xv = 0;
+	state->vs_3v = 0;
+	state->detect = 0;
+
+	if (inserted) {
+		switch (vs) {
+			case 0:
+			case 1:
+			case 2:
+				state->vs_3v=1;
+				break;
+			case 3: /* 5V */
+			default:
+				/* return without setting 'detect' */
+				printk(KERN_ERR "au1x00_cs: unsupported VS\n",
+						vs);
+				return;
+		}
+		state->detect = 1;
+	}
+
+	if (state->detect) {
+		state->ready = 1;
+	}
+
+	state->bvd1= gpio2 & (1<<10);
+	state->bvd2 = gpio2 & (1<<11);
+	state->wrprot=0;
+	return 1;
+}
+
+
+static int xxs1500_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
+{
+
+	if(info->sock > PCMCIA_MAX_SOCK) return -1;
+	info->irq = PCMCIA_IRQ;
+	return 0;
+}
+
+
+static int
+xxs1500_pcmcia_configure_socket(const struct pcmcia_configure *configure)
+{
+
+	if(configure->sock > PCMCIA_MAX_SOCK) return -1;
+
+	DEBUG("Vcc %dV Vpp %dV, reset %d\n",
+			configure->vcc, configure->vpp, configure->reset);
+
+	switch(configure->vcc){
+		case 33: /* Vcc 3.3V */
+			/* turn on power */
+			DEBUG("turn on power\n");
+			au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<14))|(1<<30),
+					GPIO2_OUTPUT);
+			au_sync_delay(100);
+			break;
+		case 50: /* Vcc 5V */
+		default: /* what's this ? */
+			printk(KERN_ERR "au1x00_cs: unsupported VCC\n");
+		case 0:  /* Vcc 0 */
+			/* turn off power */
+			au_sync_delay(100);
+			au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
+					GPIO2_OUTPUT);
+			break;
+	}
+
+	if (!configure->reset) {
+		DEBUG("deassert reset\n");
+		au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<4))|(1<<20),
+				GPIO2_OUTPUT);
+		au_sync_delay(100);
+		au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<5))|(1<<21),
+				GPIO2_OUTPUT);
+	}
+	else {
+		DEBUG("assert reset\n");
+		au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
+				GPIO2_OUTPUT);
+	}
+	au_sync_delay(100);
+	return 0;
+}
+
+struct pcmcia_low_level xxs1500_pcmcia_ops = {
+	xxs1500_pcmcia_init,
+	xxs1500_pcmcia_shutdown,
+	xxs1500_pcmcia_socket_state,
+	xxs1500_pcmcia_get_irq_info,
+	xxs1500_pcmcia_configure_socket
+};
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
new file mode 100644
index 0000000..3ccb524
--- /dev/null
+++ b/drivers/pcmcia/cardbus.c
@@ -0,0 +1,247 @@
+/*
+ * cardbus.c -- 16-bit PCMCIA core support
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+/*
+ * Cardbus handling has been re-written to be more of a PCI bridge thing,
+ * and the PCI code basically does all the resource handling.
+ *
+ *		Linus, Jan 2000
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+/*====================================================================*/
+
+#define FIND_FIRST_BIT(n)	((n) - ((n) & ((n)-1)))
+
+/* Offsets in the Expansion ROM Image Header */
+#define ROM_SIGNATURE		0x0000	/* 2 bytes */
+#define ROM_DATA_PTR		0x0018	/* 2 bytes */
+
+/* Offsets in the CardBus PC Card Data Structure */
+#define PCDATA_SIGNATURE	0x0000	/* 4 bytes */
+#define PCDATA_VPD_PTR		0x0008	/* 2 bytes */
+#define PCDATA_LENGTH		0x000a	/* 2 bytes */
+#define PCDATA_REVISION		0x000c
+#define PCDATA_IMAGE_SZ		0x0010	/* 2 bytes */
+#define PCDATA_ROM_LEVEL	0x0012	/* 2 bytes */
+#define PCDATA_CODE_TYPE	0x0014
+#define PCDATA_INDICATOR	0x0015
+
+/*=====================================================================
+
+    Expansion ROM's have a special layout, and pointers specify an
+    image number and an offset within that image.  xlate_rom_addr()
+    converts an image/offset address to an absolute offset from the
+    ROM's base address.
+    
+=====================================================================*/
+
+static u_int xlate_rom_addr(void __iomem *b, u_int addr)
+{
+	u_int img = 0, ofs = 0, sz;
+	u_short data;
+	while ((readb(b) == 0x55) && (readb(b + 1) == 0xaa)) {
+		if (img == (addr >> 28))
+			return (addr & 0x0fffffff) + ofs;
+		data = readb(b + ROM_DATA_PTR) + (readb(b + ROM_DATA_PTR + 1) << 8);
+		sz = 512 * (readb(b + data + PCDATA_IMAGE_SZ) +
+			    (readb(b + data + PCDATA_IMAGE_SZ + 1) << 8));
+		if ((sz == 0) || (readb(b + data + PCDATA_INDICATOR) & 0x80))
+			break;
+		b += sz;
+		ofs += sz;
+		img++;
+	}
+	return 0;
+}
+
+/*=====================================================================
+
+    These are similar to setup_cis_mem and release_cis_mem for 16-bit
+    cards.  The "result" that is used externally is the cb_cis_virt
+    pointer in the struct pcmcia_socket structure.
+    
+=====================================================================*/
+
+static void cb_release_cis_mem(struct pcmcia_socket * s)
+{
+	if (s->cb_cis_virt) {
+		cs_dbg(s, 1, "cb_release_cis_mem()\n");
+		iounmap(s->cb_cis_virt);
+		s->cb_cis_virt = NULL;
+		s->cb_cis_res = NULL;
+	}
+}
+
+static int cb_setup_cis_mem(struct pcmcia_socket * s, struct resource *res)
+{
+	unsigned int start, size;
+
+	if (res == s->cb_cis_res)
+		return 0;
+
+	if (s->cb_cis_res)
+		cb_release_cis_mem(s);
+
+	start = res->start;
+	size = res->end - start + 1;
+	s->cb_cis_virt = ioremap(start, size);
+
+	if (!s->cb_cis_virt)
+		return -1;
+
+	s->cb_cis_res = res;
+
+	return 0;
+}
+
+/*=====================================================================
+
+    This is used by the CIS processing code to read CIS information
+    from a CardBus device.
+    
+=====================================================================*/
+
+int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void *ptr)
+{
+	struct pci_dev *dev;
+	struct resource *res;
+
+	cs_dbg(s, 3, "read_cb_mem(%d, %#x, %u)\n", space, addr, len);
+
+	dev = pci_find_slot(s->cb_dev->subordinate->number, 0);
+	if (!dev)
+		goto fail;
+
+	/* Config space? */
+	if (space == 0) {
+		if (addr + len > 0x100)
+			goto fail;
+		for (; len; addr++, ptr++, len--)
+			pci_read_config_byte(dev, addr, ptr);
+		return 0;
+	}
+
+	res = dev->resource + space - 1;
+	if (!res->flags)
+		goto fail;
+
+	if (cb_setup_cis_mem(s, res) != 0)
+		goto fail;
+
+	if (space == 7) {
+		addr = xlate_rom_addr(s->cb_cis_virt, addr);
+		if (addr == 0)
+			goto fail;
+	}
+
+	if (addr + len > res->end - res->start)
+		goto fail;
+
+	memcpy_fromio(ptr, s->cb_cis_virt + addr, len);
+	return 0;
+
+fail:
+	memset(ptr, 0xff, len);
+	return -1;
+}
+
+/*=====================================================================
+
+    cb_alloc() and cb_free() allocate and free the kernel data
+    structures for a Cardbus device, and handle the lowest level PCI
+    device setup issues.
+    
+=====================================================================*/
+
+/*
+ * Since there is only one interrupt available to CardBus
+ * devices, all devices downstream of this device must
+ * be using this IRQ.
+ */
+static void cardbus_assign_irqs(struct pci_bus *bus, int irq)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		u8 irq_pin;
+
+		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
+		if (irq_pin) {
+			dev->irq = irq;
+			pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+		}
+
+		if (dev->subordinate)
+			cardbus_assign_irqs(dev->subordinate, irq);
+	}
+}
+
+int cb_alloc(struct pcmcia_socket * s)
+{
+	struct pci_bus *bus = s->cb_dev->subordinate;
+	struct pci_dev *dev;
+	unsigned int max, pass;
+
+	s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0));
+//	pcibios_fixup_bus(bus);
+
+	max = bus->secondary;
+	for (pass = 0; pass < 2; pass++)
+		list_for_each_entry(dev, &bus->devices, bus_list)
+			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+				max = pci_scan_bridge(bus, dev, max, pass);
+
+	/*
+	 * Size all resources below the CardBus controller.
+	 */
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	cardbus_assign_irqs(bus, s->pci_irq);
+	pci_enable_bridges(bus);
+	pci_bus_add_devices(bus);
+
+	s->irq.AssignedIRQ = s->pci_irq;
+	return CS_SUCCESS;
+}
+
+void cb_free(struct pcmcia_socket * s)
+{
+	struct pci_dev *bridge = s->cb_dev;
+
+	cb_release_cis_mem(s);
+
+	if (bridge)
+		pci_remove_behind_bridge(bridge);
+}
diff --git a/drivers/pcmcia/cirrus.h b/drivers/pcmcia/cirrus.h
new file mode 100644
index 0000000..ecd4fc7
--- /dev/null
+++ b/drivers/pcmcia/cirrus.h
@@ -0,0 +1,157 @@
+/*
+ * cirrus.h 1.4 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_CIRRUS_H
+#define _LINUX_CIRRUS_H
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS		0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6729
+#define PCI_DEVICE_ID_CIRRUS_6729	0x1100
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_6832
+#define PCI_DEVICE_ID_CIRRUS_6832	0x1110
+#endif
+
+#define PD67_MISC_CTL_1		0x16	/* Misc control 1 */
+#define PD67_FIFO_CTL		0x17	/* FIFO control */
+#define PD67_MISC_CTL_2		0x1E	/* Misc control 2 */
+#define PD67_CHIP_INFO		0x1f	/* Chip information */
+#define PD67_ATA_CTL		0x026	/* 6730: ATA control */
+#define PD67_EXT_INDEX		0x2e	/* Extension index */
+#define PD67_EXT_DATA		0x2f	/* Extension data */
+
+/* PD6722 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_DATA_MASK0		0x01	/* Data mask 0 */
+#define PD67_DATA_MASK1		0x02	/* Data mask 1 */
+#define PD67_DMA_CTL		0x03	/* DMA control */
+
+/* PD6730 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD67_EXT_CTL_1		0x03	/* Extension control 1 */
+#define PD67_MEM_PAGE(n)	((n)+5)	/* PCI window bits 31:24 */
+#define PD67_EXTERN_DATA	0x0a
+#define PD67_MISC_CTL_3		0x25
+#define PD67_SMB_PWR_CTL	0x26
+
+/* I/O window address offset */
+#define PD67_IO_OFF(w)		(0x36+((w)<<1))
+
+/* Timing register sets */
+#define PD67_TIME_SETUP(n)	(0x3a + 3*(n))
+#define PD67_TIME_CMD(n)	(0x3b + 3*(n))
+#define PD67_TIME_RECOV(n)	(0x3c + 3*(n))
+
+/* Flags for PD67_MISC_CTL_1 */
+#define PD67_MC1_5V_DET		0x01	/* 5v detect */
+#define PD67_MC1_MEDIA_ENA	0x01	/* 6730: Multimedia enable */
+#define PD67_MC1_VCC_3V		0x02	/* 3.3v Vcc */
+#define PD67_MC1_PULSE_MGMT	0x04
+#define PD67_MC1_PULSE_IRQ	0x08
+#define PD67_MC1_SPKR_ENA	0x10
+#define PD67_MC1_INPACK_ENA	0x80
+
+/* Flags for PD67_FIFO_CTL */
+#define PD67_FIFO_EMPTY		0x80
+
+/* Flags for PD67_MISC_CTL_2 */
+#define PD67_MC2_FREQ_BYPASS	0x01
+#define PD67_MC2_DYNAMIC_MODE	0x02
+#define PD67_MC2_SUSPEND	0x04
+#define PD67_MC2_5V_CORE	0x08
+#define PD67_MC2_LED_ENA	0x10	/* IRQ 12 is LED enable */
+#define PD67_MC2_FAST_PCI	0x10	/* 6729: PCI bus > 25 MHz */
+#define PD67_MC2_3STATE_BIT7	0x20	/* Floppy change bit */
+#define PD67_MC2_DMA_MODE	0x40
+#define PD67_MC2_IRQ15_RI	0x80	/* IRQ 15 is ring enable */
+
+/* Flags for PD67_CHIP_INFO */
+#define PD67_INFO_SLOTS		0x20	/* 0 = 1 slot, 1 = 2 slots */
+#define PD67_INFO_CHIP_ID	0xc0
+#define PD67_INFO_REV		0x1c
+
+/* Fields in PD67_TIME_* registers */
+#define PD67_TIME_SCALE		0xc0
+#define PD67_TIME_SCALE_1	0x00
+#define PD67_TIME_SCALE_16	0x40
+#define PD67_TIME_SCALE_256	0x80
+#define PD67_TIME_SCALE_4096	0xc0
+#define PD67_TIME_MULT		0x3f
+
+/* Fields in PD67_DMA_CTL */
+#define PD67_DMA_MODE		0xc0
+#define PD67_DMA_OFF		0x00
+#define PD67_DMA_DREQ_INPACK	0x40
+#define PD67_DMA_DREQ_WP	0x80
+#define PD67_DMA_DREQ_BVD2	0xc0
+#define PD67_DMA_PULLUP		0x20	/* Disable socket pullups? */
+
+/* Fields in PD67_EXT_CTL_1 */
+#define PD67_EC1_VCC_PWR_LOCK	0x01
+#define PD67_EC1_AUTO_PWR_CLEAR	0x02
+#define PD67_EC1_LED_ENA	0x04
+#define PD67_EC1_INV_CARD_IRQ	0x08
+#define PD67_EC1_INV_MGMT_IRQ	0x10
+#define PD67_EC1_PULLUP_CTL	0x20
+
+/* Fields in PD67_MISC_CTL_3 */
+#define PD67_MC3_IRQ_MASK	0x03
+#define PD67_MC3_IRQ_PCPCI	0x00
+#define PD67_MC3_IRQ_EXTERN	0x01
+#define PD67_MC3_IRQ_PCIWAY	0x02
+#define PD67_MC3_IRQ_PCI	0x03
+#define PD67_MC3_PWR_MASK	0x0c
+#define PD67_MC3_PWR_SERIAL	0x00
+#define PD67_MC3_PWR_TI2202	0x08
+#define PD67_MC3_PWR_SMB	0x0c
+
+/* Register definitions for Cirrus PD6832 PCI-to-CardBus bridge */
+
+/* PD6832 extension registers -- indexed in PD67_EXT_INDEX */
+#define PD68_EXT_CTL_2			0x0b
+#define PD68_PCI_SPACE			0x22
+#define PD68_PCCARD_SPACE		0x23
+#define PD68_WINDOW_TYPE		0x24
+#define PD68_EXT_CSC			0x2e
+#define PD68_MISC_CTL_4			0x2f
+#define PD68_MISC_CTL_5			0x30
+#define PD68_MISC_CTL_6			0x31
+
+/* Extra flags in PD67_MISC_CTL_3 */
+#define PD68_MC3_HW_SUSP		0x10
+#define PD68_MC3_MM_EXPAND		0x40
+#define PD68_MC3_MM_ARM			0x80
+
+/* Bridge Control Register */
+#define  PD6832_BCR_MGMT_IRQ_ENA	0x0800
+
+/* Socket Number Register */
+#define PD6832_SOCKET_NUMBER		0x004c	/* 8 bit */
+
+#endif /* _LINUX_CIRRUS_H */
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
new file mode 100644
index 0000000..e29a6dd
--- /dev/null
+++ b/drivers/pcmcia/cistpl.c
@@ -0,0 +1,1490 @@
+/*
+ * cistpl.c -- 16-bit PCMCIA Card Information Structure parser
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+static const u_char mantissa[] = {
+    10, 12, 13, 15, 20, 25, 30, 35,
+    40, 45, 50, 55, 60, 70, 80, 90
+};
+
+static const u_int exponent[] = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+
+/* Convert an extended speed byte to a time in nanoseconds */
+#define SPEED_CVT(v) \
+    (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10)
+/* Convert a power byte to a current in 0.1 microamps */
+#define POWER_CVT(v) \
+    (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10)
+#define POWER_SCALE(v)		(exponent[(v)&7])
+
+/* Upper limit on reasonable # of tuples */
+#define MAX_TUPLES		200
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+INT_MODULE_PARM(cis_width,	0);		/* 16-bit CIS? */
+
+void release_cis_mem(struct pcmcia_socket *s)
+{
+    if (s->cis_mem.flags & MAP_ACTIVE) {
+	s->cis_mem.flags &= ~MAP_ACTIVE;
+	s->ops->set_mem_map(s, &s->cis_mem);
+	if (s->cis_mem.res) {
+	    release_resource(s->cis_mem.res);
+	    kfree(s->cis_mem.res);
+	    s->cis_mem.res = NULL;
+	}
+	iounmap(s->cis_virt);
+	s->cis_virt = NULL;
+    }
+}
+EXPORT_SYMBOL(release_cis_mem);
+
+/*
+ * Map the card memory at "card_offset" into virtual space.
+ * If flags & MAP_ATTRIB, map the attribute space, otherwise
+ * map the memory space.
+ */
+static void __iomem *
+set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
+{
+    pccard_mem_map *mem = &s->cis_mem;
+    if (!(s->features & SS_CAP_STATIC_MAP) && mem->res == NULL) {
+	mem->res = find_mem_region(0, s->map_size, s->map_size, 0, s);
+	if (mem->res == NULL) {
+	    printk(KERN_NOTICE "cs: unable to map card memory!\n");
+	    return NULL;
+	}
+	s->cis_virt = ioremap(mem->res->start, s->map_size);
+    }
+    mem->card_start = card_offset;
+    mem->flags = flags;
+    s->ops->set_mem_map(s, mem);
+    if (s->features & SS_CAP_STATIC_MAP) {
+	if (s->cis_virt)
+	    iounmap(s->cis_virt);
+	s->cis_virt = ioremap(mem->static_start, s->map_size);
+    }
+    return s->cis_virt;
+}
+
+/*======================================================================
+
+    Low-level functions to read and write CIS memory.  I think the
+    write routine is only useful for writing one-byte registers.
+    
+======================================================================*/
+
+/* Bits in attr field */
+#define IS_ATTR		1
+#define IS_INDIRECT	8
+
+int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
+		 u_int len, void *ptr)
+{
+    void __iomem *sys, *end;
+    unsigned char *buf = ptr;
+    
+    cs_dbg(s, 3, "read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+
+    if (attr & IS_INDIRECT) {
+	/* Indirect accesses use a bunch of special registers at fixed
+	   locations in common memory */
+	u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+	if (attr & IS_ATTR) {
+	    addr *= 2;
+	    flags = ICTRL0_AUTOINC;
+	}
+
+	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
+	if (!sys) {
+	    memset(ptr, 0xff, len);
+	    return -1;
+	}
+
+	writeb(flags, sys+CISREG_ICTRL0);
+	writeb(addr & 0xff, sys+CISREG_IADDR0);
+	writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
+	writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
+	writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
+	for ( ; len > 0; len--, buf++)
+	    *buf = readb(sys+CISREG_IDATA0);
+    } else {
+	u_int inc = 1, card_offset, flags;
+
+	flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+	if (attr) {
+	    flags |= MAP_ATTRIB;
+	    inc++;
+	    addr *= 2;
+	}
+
+	card_offset = addr & ~(s->map_size-1);
+	while (len) {
+	    sys = set_cis_map(s, card_offset, flags);
+	    if (!sys) {
+		memset(ptr, 0xff, len);
+		return -1;
+	    }
+	    end = sys + s->map_size;
+	    sys = sys + (addr & (s->map_size-1));
+	    for ( ; len > 0; len--, buf++, sys += inc) {
+		if (sys == end)
+		    break;
+		*buf = readb(sys);
+	    }
+	    card_offset += s->map_size;
+	    addr = 0;
+	}
+    }
+    cs_dbg(s, 3, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
+	  *(u_char *)(ptr+0), *(u_char *)(ptr+1),
+	  *(u_char *)(ptr+2), *(u_char *)(ptr+3));
+    return 0;
+}
+
+void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
+		   u_int len, void *ptr)
+{
+    void __iomem *sys, *end;
+    unsigned char *buf = ptr;
+    
+    cs_dbg(s, 3, "write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
+
+    if (attr & IS_INDIRECT) {
+	/* Indirect accesses use a bunch of special registers at fixed
+	   locations in common memory */
+	u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN;
+	if (attr & IS_ATTR) {
+	    addr *= 2;
+	    flags = ICTRL0_AUTOINC;
+	}
+
+	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
+	if (!sys)
+		return; /* FIXME: Error */
+
+	writeb(flags, sys+CISREG_ICTRL0);
+	writeb(addr & 0xff, sys+CISREG_IADDR0);
+	writeb((addr>>8) & 0xff, sys+CISREG_IADDR1);
+	writeb((addr>>16) & 0xff, sys+CISREG_IADDR2);
+	writeb((addr>>24) & 0xff, sys+CISREG_IADDR3);
+	for ( ; len > 0; len--, buf++)
+	    writeb(*buf, sys+CISREG_IDATA0);
+    } else {
+	u_int inc = 1, card_offset, flags;
+
+	flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
+	if (attr & IS_ATTR) {
+	    flags |= MAP_ATTRIB;
+	    inc++;
+	    addr *= 2;
+	}
+
+	card_offset = addr & ~(s->map_size-1);
+	while (len) {
+	    sys = set_cis_map(s, card_offset, flags);
+	    if (!sys)
+		return; /* FIXME: error */
+
+	    end = sys + s->map_size;
+	    sys = sys + (addr & (s->map_size-1));
+	    for ( ; len > 0; len--, buf++, sys += inc) {
+		if (sys == end)
+		    break;
+		writeb(*buf, sys);
+	    }
+	    card_offset += s->map_size;
+	    addr = 0;
+	}
+    }
+}
+
+/*======================================================================
+
+    This is a wrapper around read_cis_mem, with the same interface,
+    but which caches information, for cards whose CIS may not be
+    readable all the time.
+    
+======================================================================*/
+
+static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
+			   u_int len, void *ptr)
+{
+    struct cis_cache_entry *cis;
+    int ret;
+
+    if (s->fake_cis) {
+	if (s->fake_cis_len > addr+len)
+	    memcpy(ptr, s->fake_cis+addr, len);
+	else
+	    memset(ptr, 0xff, len);
+	return;
+    }
+
+    list_for_each_entry(cis, &s->cis_cache, node) {
+	if (cis->addr == addr && cis->len == len && cis->attr == attr) {
+	    memcpy(ptr, cis->cache, len);
+	    return;
+	}
+    }
+
+#ifdef CONFIG_CARDBUS
+    if (s->state & SOCKET_CARDBUS)
+	ret = read_cb_mem(s, attr, addr, len, ptr);
+    else
+#endif
+	ret = read_cis_mem(s, attr, addr, len, ptr);
+
+	if (ret == 0) {
+		/* Copy data into the cache */
+		cis = kmalloc(sizeof(struct cis_cache_entry) + len, GFP_KERNEL);
+		if (cis) {
+			cis->addr = addr;
+			cis->len = len;
+			cis->attr = attr;
+			memcpy(cis->cache, ptr, len);
+			list_add(&cis->node, &s->cis_cache);
+		}
+	}
+}
+
+static void
+remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
+{
+	struct cis_cache_entry *cis;
+
+	list_for_each_entry(cis, &s->cis_cache, node)
+		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
+			list_del(&cis->node);
+			kfree(cis);
+			break;
+		}
+}
+
+void destroy_cis_cache(struct pcmcia_socket *s)
+{
+	struct list_head *l, *n;
+
+	list_for_each_safe(l, n, &s->cis_cache) {
+		struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
+
+		list_del(&cis->node);
+		kfree(cis);
+	}
+
+	/*
+	 * If there was a fake CIS, destroy that as well.
+	 */
+	if (s->fake_cis) {
+		kfree(s->fake_cis);
+		s->fake_cis = NULL;
+	}
+}
+EXPORT_SYMBOL(destroy_cis_cache);
+
+/*======================================================================
+
+    This verifies if the CIS of a card matches what is in the CIS
+    cache.
+    
+======================================================================*/
+
+int verify_cis_cache(struct pcmcia_socket *s)
+{
+	struct cis_cache_entry *cis;
+	char *buf;
+
+	buf = kmalloc(256, GFP_KERNEL);
+	if (buf == NULL)
+		return -1;
+	list_for_each_entry(cis, &s->cis_cache, node) {
+		int len = cis->len;
+
+		if (len > 256)
+			len = 256;
+#ifdef CONFIG_CARDBUS
+		if (s->state & SOCKET_CARDBUS)
+			read_cb_mem(s, cis->attr, cis->addr, len, buf);
+		else
+#endif
+			read_cis_mem(s, cis->attr, cis->addr, len, buf);
+
+		if (memcmp(buf, cis->cache, len) != 0) {
+			kfree(buf);
+			return -1;
+		}
+	}
+	kfree(buf);
+	return 0;
+}
+
+/*======================================================================
+
+    For really bad cards, we provide a facility for uploading a
+    replacement CIS.
+    
+======================================================================*/
+
+int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis)
+{
+    if (s->fake_cis != NULL) {
+	kfree(s->fake_cis);
+	s->fake_cis = NULL;
+    }
+    if (cis->Length > CISTPL_MAX_CIS_SIZE)
+	return CS_BAD_SIZE;
+    s->fake_cis = kmalloc(cis->Length, GFP_KERNEL);
+    if (s->fake_cis == NULL)
+	return CS_OUT_OF_RESOURCE;
+    s->fake_cis_len = cis->Length;
+    memcpy(s->fake_cis, cis->Data, cis->Length);
+    return CS_SUCCESS;
+}
+
+/*======================================================================
+
+    The high-level CIS tuple services
+    
+======================================================================*/
+
+typedef struct tuple_flags {
+    u_int		link_space:4;
+    u_int		has_link:1;
+    u_int		mfc_fn:3;
+    u_int		space:4;
+} tuple_flags;
+
+#define LINK_SPACE(f)	(((tuple_flags *)(&(f)))->link_space)
+#define HAS_LINK(f)	(((tuple_flags *)(&(f)))->has_link)
+#define MFC_FN(f)	(((tuple_flags *)(&(f)))->mfc_fn)
+#define SPACE(f)	(((tuple_flags *)(&(f)))->space)
+
+int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int func, tuple_t *tuple);
+
+int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
+{
+    if (!s)
+	return CS_BAD_HANDLE;
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+    tuple->TupleLink = tuple->Flags = 0;
+#ifdef CONFIG_CARDBUS
+    if (s->state & SOCKET_CARDBUS) {
+	struct pci_dev *dev = s->cb_dev;
+	u_int ptr;
+	pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr);
+	tuple->CISOffset = ptr & ~7;
+	SPACE(tuple->Flags) = (ptr & 7);
+    } else
+#endif
+    {
+	/* Assume presence of a LONGLINK_C to address 0 */
+	tuple->CISOffset = tuple->LinkOffset = 0;
+	SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
+    }
+    if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
+	!(tuple->Attributes & TUPLE_RETURN_COMMON)) {
+	cisdata_t req = tuple->DesiredTuple;
+	tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
+	if (pccard_get_next_tuple(s, function, tuple) == CS_SUCCESS) {
+	    tuple->DesiredTuple = CISTPL_LINKTARGET;
+	    if (pccard_get_next_tuple(s, function, tuple) != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+	} else
+	    tuple->CISOffset = tuple->TupleLink = 0;
+	tuple->DesiredTuple = req;
+    }
+    return pccard_get_next_tuple(s, function, tuple);
+}
+EXPORT_SYMBOL(pccard_get_first_tuple);
+
+static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
+{
+    u_char link[5];
+    u_int ofs;
+
+    if (MFC_FN(tuple->Flags)) {
+	/* Get indirect link from the MFC tuple */
+	read_cis_cache(s, LINK_SPACE(tuple->Flags),
+		       tuple->LinkOffset, 5, link);
+	ofs = le32_to_cpu(*(u_int *)(link+1));
+	SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
+	/* Move to the next indirect link */
+	tuple->LinkOffset += 5;
+	MFC_FN(tuple->Flags)--;
+    } else if (HAS_LINK(tuple->Flags)) {
+	ofs = tuple->LinkOffset;
+	SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags);
+	HAS_LINK(tuple->Flags) = 0;
+    } else {
+	return -1;
+    }
+    if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
+	/* This is ugly, but a common CIS error is to code the long
+	   link offset incorrectly, so we check the right spot... */
+	read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+	if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
+	    (strncmp(link+2, "CIS", 3) == 0))
+	    return ofs;
+	remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
+	/* Then, we try the wrong spot... */
+	ofs = ofs >> 1;
+    }
+    read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+    if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
+	(strncmp(link+2, "CIS", 3) == 0))
+	return ofs;
+    remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5);
+    return -1;
+}
+
+int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
+{
+    u_char link[2], tmp;
+    int ofs, i, attr;
+
+    if (!s)
+	return CS_BAD_HANDLE;
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+
+    link[1] = tuple->TupleLink;
+    ofs = tuple->CISOffset + tuple->TupleLink;
+    attr = SPACE(tuple->Flags);
+
+    for (i = 0; i < MAX_TUPLES; i++) {
+	if (link[1] == 0xff) {
+	    link[0] = CISTPL_END;
+	} else {
+	    read_cis_cache(s, attr, ofs, 2, link);
+	    if (link[0] == CISTPL_NULL) {
+		ofs++; continue;
+	    }
+	}
+	
+	/* End of chain?  Follow long link if possible */
+	if (link[0] == CISTPL_END) {
+	    if ((ofs = follow_link(s, tuple)) < 0)
+		return CS_NO_MORE_ITEMS;
+	    attr = SPACE(tuple->Flags);
+	    read_cis_cache(s, attr, ofs, 2, link);
+	}
+
+	/* Is this a link tuple?  Make a note of it */
+	if ((link[0] == CISTPL_LONGLINK_A) ||
+	    (link[0] == CISTPL_LONGLINK_C) ||
+	    (link[0] == CISTPL_LONGLINK_MFC) ||
+	    (link[0] == CISTPL_LINKTARGET) ||
+	    (link[0] == CISTPL_INDIRECT) ||
+	    (link[0] == CISTPL_NO_LINK)) {
+	    switch (link[0]) {
+	    case CISTPL_LONGLINK_A:
+		HAS_LINK(tuple->Flags) = 1;
+		LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
+		read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		break;
+	    case CISTPL_LONGLINK_C:
+		HAS_LINK(tuple->Flags) = 1;
+		LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
+		read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		break;
+	    case CISTPL_INDIRECT:
+		HAS_LINK(tuple->Flags) = 1;
+		LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT;
+		tuple->LinkOffset = 0;
+		break;
+	    case CISTPL_LONGLINK_MFC:
+		tuple->LinkOffset = ofs + 3;
+		LINK_SPACE(tuple->Flags) = attr;
+		if (function == BIND_FN_ALL) {
+		    /* Follow all the MFC links */
+		    read_cis_cache(s, attr, ofs+2, 1, &tmp);
+		    MFC_FN(tuple->Flags) = tmp;
+		} else {
+		    /* Follow exactly one of the links */
+		    MFC_FN(tuple->Flags) = 1;
+		    tuple->LinkOffset += function * 5;
+		}
+		break;
+	    case CISTPL_NO_LINK:
+		HAS_LINK(tuple->Flags) = 0;
+		break;
+	    }
+	    if ((tuple->Attributes & TUPLE_RETURN_LINK) &&
+		(tuple->DesiredTuple == RETURN_FIRST_TUPLE))
+		break;
+	} else
+	    if (tuple->DesiredTuple == RETURN_FIRST_TUPLE)
+		break;
+	
+	if (link[0] == tuple->DesiredTuple)
+	    break;
+	ofs += link[1] + 2;
+    }
+    if (i == MAX_TUPLES) {
+	cs_dbg(s, 1, "cs: overrun in pcmcia_get_next_tuple\n");
+	return CS_NO_MORE_ITEMS;
+    }
+    
+    tuple->TupleCode = link[0];
+    tuple->TupleLink = link[1];
+    tuple->CISOffset = ofs + 2;
+    return CS_SUCCESS;
+}
+EXPORT_SYMBOL(pccard_get_next_tuple);
+
+/*====================================================================*/
+
+#define _MIN(a, b)		(((a) < (b)) ? (a) : (b))
+
+int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
+{
+    u_int len;
+
+    if (!s)
+	return CS_BAD_HANDLE;
+
+    if (tuple->TupleLink < tuple->TupleOffset)
+	return CS_NO_MORE_ITEMS;
+    len = tuple->TupleLink - tuple->TupleOffset;
+    tuple->TupleDataLen = tuple->TupleLink;
+    if (len == 0)
+	return CS_SUCCESS;
+    read_cis_cache(s, SPACE(tuple->Flags),
+		   tuple->CISOffset + tuple->TupleOffset,
+		   _MIN(len, tuple->TupleDataMax), tuple->TupleData);
+    return CS_SUCCESS;
+}
+EXPORT_SYMBOL(pccard_get_tuple_data);
+
+
+/*======================================================================
+
+    Parsing routines for individual tuples
+    
+======================================================================*/
+
+static int parse_device(tuple_t *tuple, cistpl_device_t *device)
+{
+    int i;
+    u_char scale;
+    u_char *p, *q;
+
+    p = (u_char *)tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+
+    device->ndev = 0;
+    for (i = 0; i < CISTPL_MAX_DEVICES; i++) {
+	
+	if (*p == 0xff) break;
+	device->dev[i].type = (*p >> 4);
+	device->dev[i].wp = (*p & 0x08) ? 1 : 0;
+	switch (*p & 0x07) {
+	case 0: device->dev[i].speed = 0;   break;
+	case 1: device->dev[i].speed = 250; break;
+	case 2: device->dev[i].speed = 200; break;
+	case 3: device->dev[i].speed = 150; break;
+	case 4: device->dev[i].speed = 100; break;
+	case 7:
+	    if (++p == q) return CS_BAD_TUPLE;
+	    device->dev[i].speed = SPEED_CVT(*p);
+	    while (*p & 0x80)
+		if (++p == q) return CS_BAD_TUPLE;
+	    break;
+	default:
+	    return CS_BAD_TUPLE;
+	}
+
+	if (++p == q) return CS_BAD_TUPLE;
+	if (*p == 0xff) break;
+	scale = *p & 7;
+	if (scale == 7) return CS_BAD_TUPLE;
+	device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
+	device->ndev++;
+	if (++p == q) break;
+    }
+    
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum)
+{
+    u_char *p;
+    if (tuple->TupleDataLen < 5)
+	return CS_BAD_TUPLE;
+    p = (u_char *)tuple->TupleData;
+    csum->addr = tuple->CISOffset+(short)le16_to_cpu(*(u_short *)p)-2;
+    csum->len = le16_to_cpu(*(u_short *)(p + 2));
+    csum->sum = *(p+4);
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
+{
+    if (tuple->TupleDataLen < 4)
+	return CS_BAD_TUPLE;
+    link->addr = le32_to_cpu(*(u_int *)tuple->TupleData);
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_longlink_mfc(tuple_t *tuple,
+			      cistpl_longlink_mfc_t *link)
+{
+    u_char *p;
+    int i;
+    
+    p = (u_char *)tuple->TupleData;
+    
+    link->nfn = *p; p++;
+    if (tuple->TupleDataLen <= link->nfn*5)
+	return CS_BAD_TUPLE;
+    for (i = 0; i < link->nfn; i++) {
+	link->fn[i].space = *p; p++;
+	link->fn[i].addr = le32_to_cpu(*(u_int *)p); p += 4;
+    }
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_strings(u_char *p, u_char *q, int max,
+			 char *s, u_char *ofs, u_char *found)
+{
+    int i, j, ns;
+
+    if (p == q) return CS_BAD_TUPLE;
+    ns = 0; j = 0;
+    for (i = 0; i < max; i++) {
+	if (*p == 0xff) break;
+	ofs[i] = j;
+	ns++;
+	for (;;) {
+	    s[j++] = (*p == 0xff) ? '\0' : *p;
+	    if ((*p == '\0') || (*p == 0xff)) break;
+	    if (++p == q) return CS_BAD_TUPLE;
+	}
+	if ((*p == 0xff) || (++p == q)) break;
+    }
+    if (found) {
+	*found = ns;
+	return CS_SUCCESS;
+    } else {
+	return (ns == max) ? CS_SUCCESS : CS_BAD_TUPLE;
+    }
+}
+
+/*====================================================================*/
+
+static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1)
+{
+    u_char *p, *q;
+    
+    p = (u_char *)tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+    
+    vers_1->major = *p; p++;
+    vers_1->minor = *p; p++;
+    if (p >= q) return CS_BAD_TUPLE;
+
+    return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
+			 vers_1->str, vers_1->ofs, &vers_1->ns);
+}
+
+/*====================================================================*/
+
+static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr)
+{
+    u_char *p, *q;
+    
+    p = (u_char *)tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+    
+    return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS,
+			 altstr->str, altstr->ofs, &altstr->ns);
+}
+
+/*====================================================================*/
+
+static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec)
+{
+    u_char *p, *q;
+    int nid;
+
+    p = (u_char *)tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+
+    for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) {
+	if (p > q-2) break;
+	jedec->id[nid].mfr = p[0];
+	jedec->id[nid].info = p[1];
+	p += 2;
+    }
+    jedec->nid = nid;
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
+{
+    u_short *p;
+    if (tuple->TupleDataLen < 4)
+	return CS_BAD_TUPLE;
+    p = (u_short *)tuple->TupleData;
+    m->manf = le16_to_cpu(p[0]);
+    m->card = le16_to_cpu(p[1]);
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f)
+{
+    u_char *p;
+    if (tuple->TupleDataLen < 2)
+	return CS_BAD_TUPLE;
+    p = (u_char *)tuple->TupleData;
+    f->func = p[0];
+    f->sysinit = p[1];
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_funce(tuple_t *tuple, cistpl_funce_t *f)
+{
+    u_char *p;
+    int i;
+    if (tuple->TupleDataLen < 1)
+	return CS_BAD_TUPLE;
+    p = (u_char *)tuple->TupleData;
+    f->type = p[0];
+    for (i = 1; i < tuple->TupleDataLen; i++)
+	f->data[i-1] = p[i];
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_config(tuple_t *tuple, cistpl_config_t *config)
+{
+    int rasz, rmsz, i;
+    u_char *p;
+
+    p = (u_char *)tuple->TupleData;
+    rasz = *p & 0x03;
+    rmsz = (*p & 0x3c) >> 2;
+    if (tuple->TupleDataLen < rasz+rmsz+4)
+	return CS_BAD_TUPLE;
+    config->last_idx = *(++p);
+    p++;
+    config->base = 0;
+    for (i = 0; i <= rasz; i++)
+	config->base += p[i] << (8*i);
+    p += rasz+1;
+    for (i = 0; i < 4; i++)
+	config->rmask[i] = 0;
+    for (i = 0; i <= rmsz; i++)
+	config->rmask[i>>2] += p[i] << (8*(i%4));
+    config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
+    return CS_SUCCESS;
+}
+
+/*======================================================================
+
+    The following routines are all used to parse the nightmarish
+    config table entries.
+    
+======================================================================*/
+
+static u_char *parse_power(u_char *p, u_char *q,
+			   cistpl_power_t *pwr)
+{
+    int i;
+    u_int scale;
+
+    if (p == q) return NULL;
+    pwr->present = *p;
+    pwr->flags = 0;
+    p++;
+    for (i = 0; i < 7; i++)
+	if (pwr->present & (1<<i)) {
+	    if (p == q) return NULL;
+	    pwr->param[i] = POWER_CVT(*p);
+	    scale = POWER_SCALE(*p);
+	    while (*p & 0x80) {
+		if (++p == q) return NULL;
+		if ((*p & 0x7f) < 100)
+		    pwr->param[i] += (*p & 0x7f) * scale / 100;
+		else if (*p == 0x7d)
+		    pwr->flags |= CISTPL_POWER_HIGHZ_OK;
+		else if (*p == 0x7e)
+		    pwr->param[i] = 0;
+		else if (*p == 0x7f)
+		    pwr->flags |= CISTPL_POWER_HIGHZ_REQ;
+		else
+		    return NULL;
+	    }
+	    p++;
+	}
+    return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_timing(u_char *p, u_char *q,
+			    cistpl_timing_t *timing)
+{
+    u_char scale;
+
+    if (p == q) return NULL;
+    scale = *p;
+    if ((scale & 3) != 3) {
+	if (++p == q) return NULL;
+	timing->wait = SPEED_CVT(*p);
+	timing->waitscale = exponent[scale & 3];
+    } else
+	timing->wait = 0;
+    scale >>= 2;
+    if ((scale & 7) != 7) {
+	if (++p == q) return NULL;
+	timing->ready = SPEED_CVT(*p);
+	timing->rdyscale = exponent[scale & 7];
+    } else
+	timing->ready = 0;
+    scale >>= 3;
+    if (scale != 7) {
+	if (++p == q) return NULL;
+	timing->reserved = SPEED_CVT(*p);
+	timing->rsvscale = exponent[scale];
+    } else
+	timing->reserved = 0;
+    p++;
+    return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io)
+{
+    int i, j, bsz, lsz;
+
+    if (p == q) return NULL;
+    io->flags = *p;
+
+    if (!(*p & 0x80)) {
+	io->nwin = 1;
+	io->win[0].base = 0;
+	io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK));
+	return p+1;
+    }
+    
+    if (++p == q) return NULL;
+    io->nwin = (*p & 0x0f) + 1;
+    bsz = (*p & 0x30) >> 4;
+    if (bsz == 3) bsz++;
+    lsz = (*p & 0xc0) >> 6;
+    if (lsz == 3) lsz++;
+    p++;
+    
+    for (i = 0; i < io->nwin; i++) {
+	io->win[i].base = 0;
+	io->win[i].len = 1;
+	for (j = 0; j < bsz; j++, p++) {
+	    if (p == q) return NULL;
+	    io->win[i].base += *p << (j*8);
+	}
+	for (j = 0; j < lsz; j++, p++) {
+	    if (p == q) return NULL;
+	    io->win[i].len += *p << (j*8);
+	}
+    }
+    return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem)
+{
+    int i, j, asz, lsz, has_ha;
+    u_int len, ca, ha;
+
+    if (p == q) return NULL;
+
+    mem->nwin = (*p & 0x07) + 1;
+    lsz = (*p & 0x18) >> 3;
+    asz = (*p & 0x60) >> 5;
+    has_ha = (*p & 0x80);
+    if (++p == q) return NULL;
+    
+    for (i = 0; i < mem->nwin; i++) {
+	len = ca = ha = 0;
+	for (j = 0; j < lsz; j++, p++) {
+	    if (p == q) return NULL;
+	    len += *p << (j*8);
+	}
+	for (j = 0; j < asz; j++, p++) {
+	    if (p == q) return NULL;
+	    ca += *p << (j*8);
+	}
+	if (has_ha)
+	    for (j = 0; j < asz; j++, p++) {
+		if (p == q) return NULL;
+		ha += *p << (j*8);
+	    }
+	mem->win[i].len = len << 8;
+	mem->win[i].card_addr = ca << 8;
+	mem->win[i].host_addr = ha << 8;
+    }
+    return p;
+}
+
+/*====================================================================*/
+
+static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
+{
+    if (p == q) return NULL;
+    irq->IRQInfo1 = *p; p++;
+    if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
+	if (p+2 > q) return NULL;
+	irq->IRQInfo2 = (p[1]<<8) + p[0];
+	p += 2;
+    }
+    return p;
+}
+
+/*====================================================================*/
+
+static int parse_cftable_entry(tuple_t *tuple,
+			       cistpl_cftable_entry_t *entry)
+{
+    u_char *p, *q, features;
+
+    p = tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+    entry->index = *p & 0x3f;
+    entry->flags = 0;
+    if (*p & 0x40)
+	entry->flags |= CISTPL_CFTABLE_DEFAULT;
+    if (*p & 0x80) {
+	if (++p == q) return CS_BAD_TUPLE;
+	if (*p & 0x10)
+	    entry->flags |= CISTPL_CFTABLE_BVDS;
+	if (*p & 0x20)
+	    entry->flags |= CISTPL_CFTABLE_WP;
+	if (*p & 0x40)
+	    entry->flags |= CISTPL_CFTABLE_RDYBSY;
+	if (*p & 0x80)
+	    entry->flags |= CISTPL_CFTABLE_MWAIT;
+	entry->interface = *p & 0x0f;
+    } else
+	entry->interface = 0;
+
+    /* Process optional features */
+    if (++p == q) return CS_BAD_TUPLE;
+    features = *p; p++;
+
+    /* Power options */
+    if ((features & 3) > 0) {
+	p = parse_power(p, q, &entry->vcc);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vcc.present = 0;
+    if ((features & 3) > 1) {
+	p = parse_power(p, q, &entry->vpp1);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vpp1.present = 0;
+    if ((features & 3) > 2) {
+	p = parse_power(p, q, &entry->vpp2);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vpp2.present = 0;
+
+    /* Timing options */
+    if (features & 0x04) {
+	p = parse_timing(p, q, &entry->timing);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else {
+	entry->timing.wait = 0;
+	entry->timing.ready = 0;
+	entry->timing.reserved = 0;
+    }
+    
+    /* I/O window options */
+    if (features & 0x08) {
+	p = parse_io(p, q, &entry->io);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->io.nwin = 0;
+    
+    /* Interrupt options */
+    if (features & 0x10) {
+	p = parse_irq(p, q, &entry->irq);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->irq.IRQInfo1 = 0;
+
+    switch (features & 0x60) {
+    case 0x00:
+	entry->mem.nwin = 0;
+	break;
+    case 0x20:
+	entry->mem.nwin = 1;
+	entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8;
+	entry->mem.win[0].card_addr = 0;
+	entry->mem.win[0].host_addr = 0;
+	p += 2;
+	if (p > q) return CS_BAD_TUPLE;
+	break;
+    case 0x40:
+	entry->mem.nwin = 1;
+	entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8;
+	entry->mem.win[0].card_addr =
+	    le16_to_cpu(*(u_short *)(p+2)) << 8;
+	entry->mem.win[0].host_addr = 0;
+	p += 4;
+	if (p > q) return CS_BAD_TUPLE;
+	break;
+    case 0x60:
+	p = parse_mem(p, q, &entry->mem);
+	if (p == NULL) return CS_BAD_TUPLE;
+	break;
+    }
+
+    /* Misc features */
+    if (features & 0x80) {
+	if (p == q) return CS_BAD_TUPLE;
+	entry->flags |= (*p << 8);
+	while (*p & 0x80)
+	    if (++p == q) return CS_BAD_TUPLE;
+	p++;
+    }
+
+    entry->subtuples = q-p;
+    
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+#ifdef CONFIG_CARDBUS
+
+static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
+{
+    u_char *p;
+    if (tuple->TupleDataLen < 6)
+	return CS_BAD_TUPLE;
+    p = (u_char *)tuple->TupleData;
+    bar->attr = *p;
+    p += 2;
+    bar->size = le32_to_cpu(*(u_int *)p);
+    return CS_SUCCESS;
+}
+
+static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
+{
+    u_char *p;
+    
+    p = (u_char *)tuple->TupleData;
+    if ((*p != 3) || (tuple->TupleDataLen < 6))
+	return CS_BAD_TUPLE;
+    config->last_idx = *(++p);
+    p++;
+    config->base = le32_to_cpu(*(u_int *)p);
+    config->subtuples = tuple->TupleDataLen - 6;
+    return CS_SUCCESS;
+}
+
+static int parse_cftable_entry_cb(tuple_t *tuple,
+				  cistpl_cftable_entry_cb_t *entry)
+{
+    u_char *p, *q, features;
+
+    p = tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+    entry->index = *p & 0x3f;
+    entry->flags = 0;
+    if (*p & 0x40)
+	entry->flags |= CISTPL_CFTABLE_DEFAULT;
+
+    /* Process optional features */
+    if (++p == q) return CS_BAD_TUPLE;
+    features = *p; p++;
+
+    /* Power options */
+    if ((features & 3) > 0) {
+	p = parse_power(p, q, &entry->vcc);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vcc.present = 0;
+    if ((features & 3) > 1) {
+	p = parse_power(p, q, &entry->vpp1);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vpp1.present = 0;
+    if ((features & 3) > 2) {
+	p = parse_power(p, q, &entry->vpp2);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->vpp2.present = 0;
+
+    /* I/O window options */
+    if (features & 0x08) {
+	if (p == q) return CS_BAD_TUPLE;
+	entry->io = *p; p++;
+    } else
+	entry->io = 0;
+    
+    /* Interrupt options */
+    if (features & 0x10) {
+	p = parse_irq(p, q, &entry->irq);
+	if (p == NULL) return CS_BAD_TUPLE;
+    } else
+	entry->irq.IRQInfo1 = 0;
+
+    if (features & 0x20) {
+	if (p == q) return CS_BAD_TUPLE;
+	entry->mem = *p; p++;
+    } else
+	entry->mem = 0;
+
+    /* Misc features */
+    if (features & 0x80) {
+	if (p == q) return CS_BAD_TUPLE;
+	entry->flags |= (*p << 8);
+	if (*p & 0x80) {
+	    if (++p == q) return CS_BAD_TUPLE;
+	    entry->flags |= (*p << 16);
+	}
+	while (*p & 0x80)
+	    if (++p == q) return CS_BAD_TUPLE;
+	p++;
+    }
+
+    entry->subtuples = q-p;
+    
+    return CS_SUCCESS;
+}
+
+#endif
+
+/*====================================================================*/
+
+static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
+{
+    u_char *p, *q;
+    int n;
+
+    p = (u_char *)tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+
+    for (n = 0; n < CISTPL_MAX_DEVICES; n++) {
+	if (p > q-6) break;
+	geo->geo[n].buswidth = p[0];
+	geo->geo[n].erase_block = 1 << (p[1]-1);
+	geo->geo[n].read_block  = 1 << (p[2]-1);
+	geo->geo[n].write_block = 1 << (p[3]-1);
+	geo->geo[n].partition   = 1 << (p[4]-1);
+	geo->geo[n].interleave  = 1 << (p[5]-1);
+	p += 6;
+    }
+    geo->ngeo = n;
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2)
+{
+    u_char *p, *q;
+
+    if (tuple->TupleDataLen < 10)
+	return CS_BAD_TUPLE;
+    
+    p = tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+
+    v2->vers = p[0];
+    v2->comply = p[1];
+    v2->dindex = le16_to_cpu(*(u_short *)(p+2));
+    v2->vspec8 = p[6];
+    v2->vspec9 = p[7];
+    v2->nhdr = p[8];
+    p += 9;
+    return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL);
+}
+
+/*====================================================================*/
+
+static int parse_org(tuple_t *tuple, cistpl_org_t *org)
+{
+    u_char *p, *q;
+    int i;
+    
+    p = tuple->TupleData;
+    q = p + tuple->TupleDataLen;
+    if (p == q) return CS_BAD_TUPLE;
+    org->data_org = *p;
+    if (++p == q) return CS_BAD_TUPLE;
+    for (i = 0; i < 30; i++) {
+	org->desc[i] = *p;
+	if (*p == '\0') break;
+	if (++p == q) return CS_BAD_TUPLE;
+    }
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int parse_format(tuple_t *tuple, cistpl_format_t *fmt)
+{
+    u_char *p;
+
+    if (tuple->TupleDataLen < 10)
+	return CS_BAD_TUPLE;
+
+    p = tuple->TupleData;
+
+    fmt->type = p[0];
+    fmt->edc = p[1];
+    fmt->offset = le32_to_cpu(*(u_int *)(p+2));
+    fmt->length = le32_to_cpu(*(u_int *)(p+6));
+
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse)
+{
+    int ret = CS_SUCCESS;
+    
+    if (tuple->TupleDataLen > tuple->TupleDataMax)
+	return CS_BAD_TUPLE;
+    switch (tuple->TupleCode) {
+    case CISTPL_DEVICE:
+    case CISTPL_DEVICE_A:
+	ret = parse_device(tuple, &parse->device);
+	break;
+#ifdef CONFIG_CARDBUS
+    case CISTPL_BAR:
+	ret = parse_bar(tuple, &parse->bar);
+	break;
+    case CISTPL_CONFIG_CB:
+	ret = parse_config_cb(tuple, &parse->config);
+	break;
+    case CISTPL_CFTABLE_ENTRY_CB:
+	ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
+	break;
+#endif
+    case CISTPL_CHECKSUM:
+	ret = parse_checksum(tuple, &parse->checksum);
+	break;
+    case CISTPL_LONGLINK_A:
+    case CISTPL_LONGLINK_C:
+	ret = parse_longlink(tuple, &parse->longlink);
+	break;
+    case CISTPL_LONGLINK_MFC:
+	ret = parse_longlink_mfc(tuple, &parse->longlink_mfc);
+	break;
+    case CISTPL_VERS_1:
+	ret = parse_vers_1(tuple, &parse->version_1);
+	break;
+    case CISTPL_ALTSTR:
+	ret = parse_altstr(tuple, &parse->altstr);
+	break;
+    case CISTPL_JEDEC_A:
+    case CISTPL_JEDEC_C:
+	ret = parse_jedec(tuple, &parse->jedec);
+	break;
+    case CISTPL_MANFID:
+	ret = parse_manfid(tuple, &parse->manfid);
+	break;
+    case CISTPL_FUNCID:
+	ret = parse_funcid(tuple, &parse->funcid);
+	break;
+    case CISTPL_FUNCE:
+	ret = parse_funce(tuple, &parse->funce);
+	break;
+    case CISTPL_CONFIG:
+	ret = parse_config(tuple, &parse->config);
+	break;
+    case CISTPL_CFTABLE_ENTRY:
+	ret = parse_cftable_entry(tuple, &parse->cftable_entry);
+	break;
+    case CISTPL_DEVICE_GEO:
+    case CISTPL_DEVICE_GEO_A:
+	ret = parse_device_geo(tuple, &parse->device_geo);
+	break;
+    case CISTPL_VERS_2:
+	ret = parse_vers_2(tuple, &parse->vers_2);
+	break;
+    case CISTPL_ORG:
+	ret = parse_org(tuple, &parse->org);
+	break;
+    case CISTPL_FORMAT:
+    case CISTPL_FORMAT_A:
+	ret = parse_format(tuple, &parse->format);
+	break;
+    case CISTPL_NO_LINK:
+    case CISTPL_LINKTARGET:
+	ret = CS_SUCCESS;
+	break;
+    default:
+	ret = CS_UNSUPPORTED_FUNCTION;
+	break;
+    }
+    return ret;
+}
+EXPORT_SYMBOL(pccard_parse_tuple);
+
+/*======================================================================
+
+    This is used internally by Card Services to look up CIS stuff.
+    
+======================================================================*/
+
+int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse)
+{
+    tuple_t tuple;
+    cisdata_t *buf;
+    int ret;
+
+    buf = kmalloc(256, GFP_KERNEL);
+    if (buf == NULL)
+	return CS_OUT_OF_RESOURCE;
+    tuple.DesiredTuple = code;
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    ret = pccard_get_first_tuple(s, function, &tuple);
+    if (ret != CS_SUCCESS) goto done;
+    tuple.TupleData = buf;
+    tuple.TupleOffset = 0;
+    tuple.TupleDataMax = 255;
+    ret = pccard_get_tuple_data(s, &tuple);
+    if (ret != CS_SUCCESS) goto done;
+    ret = pccard_parse_tuple(&tuple, parse);
+done:
+    kfree(buf);
+    return ret;
+}
+EXPORT_SYMBOL(pccard_read_tuple);
+
+/*======================================================================
+
+    This tries to determine if a card has a sensible CIS.  It returns
+    the number of tuples in the CIS, or 0 if the CIS looks bad.  The
+    checks include making sure several critical tuples are present and
+    valid; seeing if the total number of tuples is reasonable; and
+    looking for tuples that use reserved codes.
+    
+======================================================================*/
+
+int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info)
+{
+    tuple_t *tuple;
+    cisparse_t *p;
+    int ret, reserved, dev_ok = 0, ident_ok = 0;
+
+    if (!s)
+	return CS_BAD_HANDLE;
+
+    tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
+    if (tuple == NULL)
+	return CS_OUT_OF_RESOURCE;
+    p = kmalloc(sizeof(*p), GFP_KERNEL);
+    if (p == NULL) {
+	kfree(tuple);
+	return CS_OUT_OF_RESOURCE;
+    }
+
+    info->Chains = reserved = 0;
+    tuple->DesiredTuple = RETURN_FIRST_TUPLE;
+    tuple->Attributes = TUPLE_RETURN_COMMON;
+    ret = pccard_get_first_tuple(s, function, tuple);
+    if (ret != CS_SUCCESS)
+	goto done;
+
+    /* First tuple should be DEVICE; we should really have either that
+       or a CFTABLE_ENTRY of some sort */
+    if ((tuple->TupleCode == CISTPL_DEVICE) ||
+	(pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY, p) == CS_SUCCESS) ||
+	(pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY_CB, p) == CS_SUCCESS))
+	dev_ok++;
+
+    /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
+       tuple, for card identification.  Certain old D-Link and Linksys
+       cards have only a broken VERS_2 tuple; hence the bogus test. */
+    if ((pccard_read_tuple(s, function, CISTPL_MANFID, p) == CS_SUCCESS) ||
+	(pccard_read_tuple(s, function, CISTPL_VERS_1, p) == CS_SUCCESS) ||
+	(pccard_read_tuple(s, function, CISTPL_VERS_2, p) != CS_NO_MORE_ITEMS))
+	ident_ok++;
+
+    if (!dev_ok && !ident_ok)
+	goto done;
+
+    for (info->Chains = 1; info->Chains < MAX_TUPLES; info->Chains++) {
+	ret = pccard_get_next_tuple(s, function, tuple);
+	if (ret != CS_SUCCESS) break;
+	if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
+	    ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
+	    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
+	    reserved++;
+    }
+    if ((info->Chains == MAX_TUPLES) || (reserved > 5) ||
+	((!dev_ok || !ident_ok) && (info->Chains > 10)))
+	info->Chains = 0;
+
+done:
+    kfree(tuple);
+    kfree(p);
+    return CS_SUCCESS;
+}
+EXPORT_SYMBOL(pccard_validate_cis);
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
new file mode 100644
index 0000000..03fc885
--- /dev/null
+++ b/drivers/pcmcia/cs.c
@@ -0,0 +1,1917 @@
+/*
+ * cs.c -- Kernel Card Services - core services
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "cs_internal.h"
+
+#ifdef CONFIG_PCI
+#define PCI_OPT " [pci]"
+#else
+#define PCI_OPT ""
+#endif
+#ifdef CONFIG_CARDBUS
+#define CB_OPT " [cardbus]"
+#else
+#define CB_OPT ""
+#endif
+#ifdef CONFIG_PM
+#define PM_OPT " [pm]"
+#else
+#define PM_OPT ""
+#endif
+#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && !defined(CONFIG_PM)
+#define OPTIONS " none"
+#else
+#define OPTIONS PCI_OPT CB_OPT PM_OPT
+#endif
+
+static const char *release = "Linux Kernel Card Services";
+static const char *options = "options: " OPTIONS;
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Linux Kernel Card Services\noptions:" OPTIONS);
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+INT_MODULE_PARM(setup_delay,	10);		/* centiseconds */
+INT_MODULE_PARM(resume_delay,	20);		/* centiseconds */
+INT_MODULE_PARM(shutdown_delay,	3);		/* centiseconds */
+INT_MODULE_PARM(vcc_settle,	40);		/* centiseconds */
+INT_MODULE_PARM(reset_time,	10);		/* usecs */
+INT_MODULE_PARM(unreset_delay,	10);		/* centiseconds */
+INT_MODULE_PARM(unreset_check,	10);		/* centiseconds */
+INT_MODULE_PARM(unreset_limit,	30);		/* unreset_check's */
+
+/* Access speed for attribute memory windows */
+INT_MODULE_PARM(cis_speed,	300);		/* ns */
+
+/* Access speed for IO windows */
+INT_MODULE_PARM(io_speed,	0);		/* ns */
+
+#ifdef DEBUG
+static int pc_debug;
+
+module_param(pc_debug, int, 0644);
+
+int cs_debug_level(int level)
+{
+	return pc_debug > level;
+}
+#endif
+
+/*====================================================================*/
+
+socket_state_t dead_socket = {
+	.csc_mask	= SS_DETECT,
+};
+
+
+/* List of all sockets, protected by a rwsem */
+LIST_HEAD(pcmcia_socket_list);
+DECLARE_RWSEM(pcmcia_socket_list_rwsem);
+EXPORT_SYMBOL(pcmcia_socket_list);
+EXPORT_SYMBOL(pcmcia_socket_list_rwsem);
+
+
+#ifdef CONFIG_PCMCIA_PROBE
+/* mask ofIRQs already reserved by other cards, we should avoid using them */
+static u8 pcmcia_used_irq[NR_IRQS];
+#endif
+
+/*====================================================================
+
+    Low-level PC Card interface drivers need to register with Card
+    Services using these calls.
+    
+======================================================================*/
+
+/**
+ * socket drivers are expected to use the following callbacks in their 
+ * .drv struct:
+ *  - pcmcia_socket_dev_suspend
+ *  - pcmcia_socket_dev_resume
+ * These functions check for the appropriate struct pcmcia_soket arrays,
+ * and pass them to the low-level functions pcmcia_{suspend,resume}_socket
+ */
+static int socket_resume(struct pcmcia_socket *skt);
+static int socket_suspend(struct pcmcia_socket *skt);
+
+int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state)
+{
+	struct pcmcia_socket *socket;
+
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
+		if (socket->dev.dev != dev)
+			continue;
+		down(&socket->skt_sem);
+		socket_suspend(socket);
+		up(&socket->skt_sem);
+	}
+	up_read(&pcmcia_socket_list_rwsem);
+
+	return 0;
+}
+EXPORT_SYMBOL(pcmcia_socket_dev_suspend);
+
+int pcmcia_socket_dev_resume(struct device *dev)
+{
+	struct pcmcia_socket *socket;
+
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
+		if (socket->dev.dev != dev)
+			continue;
+		down(&socket->skt_sem);
+		socket_resume(socket);
+		up(&socket->skt_sem);
+	}
+	up_read(&pcmcia_socket_list_rwsem);
+
+	return 0;
+}
+EXPORT_SYMBOL(pcmcia_socket_dev_resume);
+
+
+struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt)
+{
+	struct class_device *cl_dev = class_device_get(&skt->dev);
+	if (!cl_dev)
+		return NULL;
+	skt = class_get_devdata(cl_dev);
+	if (!try_module_get(skt->owner)) {
+		class_device_put(&skt->dev);
+		return NULL;
+	}
+	return (skt);
+}
+EXPORT_SYMBOL(pcmcia_get_socket);
+
+
+void pcmcia_put_socket(struct pcmcia_socket *skt)
+{
+	module_put(skt->owner);
+	class_device_put(&skt->dev);
+}
+EXPORT_SYMBOL(pcmcia_put_socket);
+
+
+static void pcmcia_release_socket(struct class_device *class_dev)
+{
+	struct pcmcia_socket *socket = class_get_devdata(class_dev);
+
+	complete(&socket->socket_released);
+}
+
+static int pccardd(void *__skt);
+
+/**
+ * pcmcia_register_socket - add a new pcmcia socket device
+ */
+int pcmcia_register_socket(struct pcmcia_socket *socket)
+{
+	int ret;
+
+	if (!socket || !socket->ops || !socket->dev.dev || !socket->resource_ops)
+		return -EINVAL;
+
+	cs_dbg(socket, 0, "pcmcia_register_socket(0x%p)\n", socket->ops);
+
+	spin_lock_init(&socket->lock);
+
+	if (socket->resource_ops->init) {
+		ret = socket->resource_ops->init(socket);
+		if (ret)
+			return (ret);
+	}
+
+	/* try to obtain a socket number [yes, it gets ugly if we
+	 * register more than 2^sizeof(unsigned int) pcmcia 
+	 * sockets... but the socket number is deprecated 
+	 * anyways, so I don't care] */
+	down_write(&pcmcia_socket_list_rwsem);
+	if (list_empty(&pcmcia_socket_list))
+		socket->sock = 0;
+	else {
+		unsigned int found, i = 1;
+		struct pcmcia_socket *tmp;
+		do {
+			found = 1;
+			list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) {
+				if (tmp->sock == i)
+					found = 0;
+			}
+			i++;
+		} while (!found);
+		socket->sock = i - 1;
+	}
+	list_add_tail(&socket->socket_list, &pcmcia_socket_list);
+	up_write(&pcmcia_socket_list_rwsem);
+
+
+	/* set proper values in socket->dev */
+	socket->dev.class_data = socket;
+	socket->dev.class = &pcmcia_socket_class;
+	snprintf(socket->dev.class_id, BUS_ID_SIZE, "pcmcia_socket%u", socket->sock);
+
+	/* base address = 0, map = 0 */
+	socket->cis_mem.flags = 0;
+	socket->cis_mem.speed = cis_speed;
+
+	INIT_LIST_HEAD(&socket->cis_cache);
+
+	init_completion(&socket->socket_released);
+	init_completion(&socket->thread_done);
+	init_waitqueue_head(&socket->thread_wait);
+	init_MUTEX(&socket->skt_sem);
+	spin_lock_init(&socket->thread_lock);
+
+	ret = kernel_thread(pccardd, socket, CLONE_KERNEL);
+	if (ret < 0)
+		goto err;
+
+	wait_for_completion(&socket->thread_done);
+	if(!socket->thread) {
+		printk(KERN_WARNING "PCMCIA: warning: socket thread for socket %p did not start\n", socket);
+		return -EIO;
+	}
+	pcmcia_parse_events(socket, SS_DETECT);
+
+	return 0;
+
+ err:
+	down_write(&pcmcia_socket_list_rwsem);
+	list_del(&socket->socket_list);
+	up_write(&pcmcia_socket_list_rwsem);
+	return ret;
+} /* pcmcia_register_socket */
+EXPORT_SYMBOL(pcmcia_register_socket);
+
+
+/**
+ * pcmcia_unregister_socket - remove a pcmcia socket device
+ */
+void pcmcia_unregister_socket(struct pcmcia_socket *socket)
+{
+	if (!socket)
+		return;
+
+	cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops);
+
+	if (socket->thread) {
+		init_completion(&socket->thread_done);
+		socket->thread = NULL;
+		wake_up(&socket->thread_wait);
+		wait_for_completion(&socket->thread_done);
+	}
+	release_cis_mem(socket);
+
+	/* remove from our own list */
+	down_write(&pcmcia_socket_list_rwsem);
+	list_del(&socket->socket_list);
+	up_write(&pcmcia_socket_list_rwsem);
+
+	/* wait for sysfs to drop all references */
+	release_resource_db(socket);
+	wait_for_completion(&socket->socket_released);
+} /* pcmcia_unregister_socket */
+EXPORT_SYMBOL(pcmcia_unregister_socket);
+
+
+struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr)
+{
+	struct pcmcia_socket *s;
+
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(s, &pcmcia_socket_list, socket_list)
+		if (s->sock == nr) {
+			up_read(&pcmcia_socket_list_rwsem);
+			return s;
+		}
+	up_read(&pcmcia_socket_list_rwsem);
+
+	return NULL;
+
+}
+EXPORT_SYMBOL(pcmcia_get_socket_by_nr);
+
+
+/*======================================================================
+
+    socket_setup() and shutdown_socket() are called by the main event
+    handler when card insertion and removal events are received.
+    socket_setup() turns on socket power and resets the socket, in two stages.
+    shutdown_socket() unconfigures a socket and turns off socket power.
+
+======================================================================*/
+
+static void shutdown_socket(struct pcmcia_socket *s)
+{
+    cs_dbg(s, 1, "shutdown_socket\n");
+
+    /* Blank out the socket state */
+    s->socket = dead_socket;
+    s->ops->init(s);
+    s->ops->set_socket(s, &s->socket);
+    s->irq.AssignedIRQ = s->irq.Config = 0;
+    s->lock_count = 0;
+    destroy_cis_cache(s);
+#ifdef CONFIG_CARDBUS
+    cb_free(s);
+#endif
+    s->functions = 0;
+    if (s->config) {
+	kfree(s->config);
+	s->config = NULL;
+    }
+
+    {
+	int status;
+	s->ops->get_status(s, &status);
+	if (status & SS_POWERON) {
+		printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s);
+	}
+    }
+} /* shutdown_socket */
+
+/*======================================================================
+
+    The central event handler.  Send_event() sends an event to the
+    16-bit subsystem, which then calls the relevant device drivers.
+    Parse_events() interprets the event bits from
+    a card status change report.  Do_shutdown() handles the high
+    priority stuff associated with a card removal.
+    
+======================================================================*/
+
+
+/* NOTE: send_event needs to be called with skt->sem held. */
+
+static int send_event(struct pcmcia_socket *s, event_t event, int priority)
+{
+	int ret;
+
+	if (s->state & SOCKET_CARDBUS)
+		return 0;
+
+	cs_dbg(s, 1, "send_event(event %d, pri %d, callback 0x%p)\n",
+	   event, priority, s->callback);
+
+	if (!s->callback)
+		return 0;
+	if (!try_module_get(s->callback->owner))
+		return 0;
+
+	ret = s->callback->event(s, event, priority);
+
+	module_put(s->callback->owner);
+
+	return ret;
+}
+
+static void socket_remove_drivers(struct pcmcia_socket *skt)
+{
+	cs_dbg(skt, 4, "remove_drivers\n");
+
+	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+}
+
+static void socket_shutdown(struct pcmcia_socket *skt)
+{
+	cs_dbg(skt, 4, "shutdown\n");
+
+	socket_remove_drivers(skt);
+	skt->state &= SOCKET_INUSE|SOCKET_PRESENT;
+	msleep(shutdown_delay * 10);
+	skt->state &= SOCKET_INUSE;
+	shutdown_socket(skt);
+}
+
+static int socket_reset(struct pcmcia_socket *skt)
+{
+	int status, i;
+
+	cs_dbg(skt, 4, "reset\n");
+
+	skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
+	skt->ops->set_socket(skt, &skt->socket);
+	udelay((long)reset_time);
+
+	skt->socket.flags &= ~SS_RESET;
+	skt->ops->set_socket(skt, &skt->socket);
+
+	msleep(unreset_delay * 10);
+	for (i = 0; i < unreset_limit; i++) {
+		skt->ops->get_status(skt, &status);
+
+		if (!(status & SS_DETECT))
+			return CS_NO_CARD;
+
+		if (status & SS_READY)
+			return CS_SUCCESS;
+
+		msleep(unreset_check * 10);
+	}
+
+	cs_err(skt, "time out after reset.\n");
+	return CS_GENERAL_FAILURE;
+}
+
+static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
+{
+	int status, i;
+
+	cs_dbg(skt, 4, "setup\n");
+
+	skt->ops->get_status(skt, &status);
+	if (!(status & SS_DETECT))
+		return CS_NO_CARD;
+
+	msleep(initial_delay * 10);
+
+	for (i = 0; i < 100; i++) {
+		skt->ops->get_status(skt, &status);
+		if (!(status & SS_DETECT))
+			return CS_NO_CARD;
+
+		if (!(status & SS_PENDING))
+			break;
+
+		msleep(100);
+	}
+
+	if (status & SS_PENDING) {
+		cs_err(skt, "voltage interrogation timed out.\n");
+		return CS_GENERAL_FAILURE;
+	}
+
+	if (status & SS_CARDBUS) {
+		skt->state |= SOCKET_CARDBUS;
+#ifndef CONFIG_CARDBUS
+		cs_err(skt, "cardbus cards are not supported.\n");
+		return CS_BAD_TYPE;
+#endif
+	}
+
+	/*
+	 * Decode the card voltage requirements, and apply power to the card.
+	 */
+	if (status & SS_3VCARD)
+		skt->socket.Vcc = skt->socket.Vpp = 33;
+	else if (!(status & SS_XVCARD))
+		skt->socket.Vcc = skt->socket.Vpp = 50;
+	else {
+		cs_err(skt, "unsupported voltage key.\n");
+		return CS_BAD_TYPE;
+	}
+	skt->socket.flags = 0;
+	skt->ops->set_socket(skt, &skt->socket);
+
+	/*
+	 * Wait "vcc_settle" for the supply to stabilise.
+	 */
+	msleep(vcc_settle * 10);
+
+	skt->ops->get_status(skt, &status);
+	if (!(status & SS_POWERON)) {
+		cs_err(skt, "unable to apply power.\n");
+		return CS_BAD_TYPE;
+	}
+
+	return socket_reset(skt);
+}
+
+/*
+ * Handle card insertion.  Setup the socket, reset the card,
+ * and then tell the rest of PCMCIA that a card is present.
+ */
+static int socket_insert(struct pcmcia_socket *skt)
+{
+	int ret;
+
+	cs_dbg(skt, 4, "insert\n");
+
+	if (!cs_socket_get(skt))
+		return CS_NO_CARD;
+
+	ret = socket_setup(skt, setup_delay);
+	if (ret == CS_SUCCESS) {
+		skt->state |= SOCKET_PRESENT;
+#ifdef CONFIG_CARDBUS
+		if (skt->state & SOCKET_CARDBUS) {
+			cb_alloc(skt);
+			skt->state |= SOCKET_CARDBUS_CONFIG;
+		}
+#endif
+		cs_dbg(skt, 4, "insert done\n");
+
+		send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+	} else {
+		socket_shutdown(skt);
+		cs_socket_put(skt);
+	}
+
+	return ret;
+}
+
+static int socket_suspend(struct pcmcia_socket *skt)
+{
+	if (skt->state & SOCKET_SUSPEND)
+		return CS_IN_USE;
+
+	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+	skt->socket = dead_socket;
+	skt->ops->set_socket(skt, &skt->socket);
+	if (skt->ops->suspend)
+		skt->ops->suspend(skt);
+	skt->state |= SOCKET_SUSPEND;
+
+	return CS_SUCCESS;
+}
+
+/*
+ * Resume a socket.  If a card is present, verify its CIS against
+ * our cached copy.  If they are different, the card has been
+ * replaced, and we need to tell the drivers.
+ */
+static int socket_resume(struct pcmcia_socket *skt)
+{
+	int ret;
+
+	if (!(skt->state & SOCKET_SUSPEND))
+		return CS_IN_USE;
+
+	skt->socket = dead_socket;
+	skt->ops->init(skt);
+	skt->ops->set_socket(skt, &skt->socket);
+
+	if (!(skt->state & SOCKET_PRESENT)) {
+		skt->state &= ~SOCKET_SUSPEND;
+		return socket_insert(skt);
+	}
+
+	ret = socket_setup(skt, resume_delay);
+	if (ret == CS_SUCCESS) {
+		/*
+		 * FIXME: need a better check here for cardbus cards.
+		 */
+		if (verify_cis_cache(skt) != 0) {
+			cs_dbg(skt, 4, "cis mismatch - different card\n");
+			socket_remove_drivers(skt);
+			destroy_cis_cache(skt);
+			/*
+			 * Workaround: give DS time to schedule removal.
+			 * Remove me once the 100ms delay is eliminated
+			 * in ds.c
+			 */
+			msleep(200);
+			send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+		} else {
+			cs_dbg(skt, 4, "cis matches cache\n");
+			send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+		}
+	} else {
+		socket_shutdown(skt);
+		cs_socket_put(skt);
+	}
+
+	skt->state &= ~SOCKET_SUSPEND;
+
+	return CS_SUCCESS;
+}
+
+static void socket_remove(struct pcmcia_socket *skt)
+{
+	socket_shutdown(skt);
+	cs_socket_put(skt);
+}
+
+/*
+ * Process a socket card detect status change.
+ *
+ * If we don't have a card already present, delay the detect event for
+ * about 20ms (to be on the safe side) before reading the socket status.
+ *
+ * Some i82365-based systems send multiple SS_DETECT events during card
+ * insertion, and the "card present" status bit seems to bounce.  This
+ * will probably be true with GPIO-based card detection systems after
+ * the product has aged.
+ */
+static void socket_detect_change(struct pcmcia_socket *skt)
+{
+	if (!(skt->state & SOCKET_SUSPEND)) {
+		int status;
+
+		if (!(skt->state & SOCKET_PRESENT))
+			msleep(20);
+
+		skt->ops->get_status(skt, &status);
+		if ((skt->state & SOCKET_PRESENT) &&
+		     !(status & SS_DETECT))
+			socket_remove(skt);
+		if (!(skt->state & SOCKET_PRESENT) &&
+		    (status & SS_DETECT))
+			socket_insert(skt);
+	}
+}
+
+static int pccardd(void *__skt)
+{
+	struct pcmcia_socket *skt = __skt;
+	DECLARE_WAITQUEUE(wait, current);
+	int ret;
+
+	daemonize("pccardd");
+
+	skt->thread = current;
+	skt->socket = dead_socket;
+	skt->ops->init(skt);
+	skt->ops->set_socket(skt, &skt->socket);
+
+	/* register with the device core */
+	ret = class_device_register(&skt->dev);
+	if (ret) {
+		printk(KERN_WARNING "PCMCIA: unable to register socket 0x%p\n",
+			skt);
+		skt->thread = NULL;
+		complete_and_exit(&skt->thread_done, 0);
+	}
+	complete(&skt->thread_done);
+
+	add_wait_queue(&skt->thread_wait, &wait);
+	for (;;) {
+		unsigned long flags;
+		unsigned int events;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&skt->thread_lock, flags);
+		events = skt->thread_events;
+		skt->thread_events = 0;
+		spin_unlock_irqrestore(&skt->thread_lock, flags);
+
+		if (events) {
+			down(&skt->skt_sem);
+			if (events & SS_DETECT)
+				socket_detect_change(skt);
+			if (events & SS_BATDEAD)
+				send_event(skt, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
+			if (events & SS_BATWARN)
+				send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
+			if (events & SS_READY)
+				send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
+			up(&skt->skt_sem);
+			continue;
+		}
+
+		schedule();
+		try_to_freeze(PF_FREEZE);
+
+		if (!skt->thread)
+			break;
+	}
+	remove_wait_queue(&skt->thread_wait, &wait);
+
+	/* remove from the device core */
+	class_device_unregister(&skt->dev);
+
+	complete_and_exit(&skt->thread_done, 0);
+}
+
+/*
+ * Yenta (at least) probes interrupts before registering the socket and
+ * starting the handler thread.
+ */
+void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
+{
+	cs_dbg(s, 4, "parse_events: events %08x\n", events);
+	if (s->thread) {
+		spin_lock(&s->thread_lock);
+		s->thread_events |= events;
+		spin_unlock(&s->thread_lock);
+
+		wake_up(&s->thread_wait);
+	}
+} /* pcmcia_parse_events */
+
+
+/*======================================================================
+
+    Special stuff for managing IO windows, because they are scarce.
+    
+======================================================================*/
+
+static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base,
+			  ioaddr_t num, u_int lines)
+{
+    int i;
+    kio_addr_t try, align;
+
+    align = (*base) ? (lines ? 1<<lines : 0) : 1;
+    if (align && (align < num)) {
+	if (*base) {
+	    cs_dbg(s, 0, "odd IO request: num %#x align %#lx\n",
+		   num, align);
+	    align = 0;
+	} else
+	    while (align && (align < num)) align <<= 1;
+    }
+    if (*base & ~(align-1)) {
+	cs_dbg(s, 0, "odd IO request: base %#x align %#lx\n",
+	       *base, align);
+	align = 0;
+    }
+    if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) {
+	*base = s->io_offset | (*base & 0x0fff);
+	return 0;
+    }
+    /* Check for an already-allocated window that must conflict with
+       what was asked for.  It is a hack because it does not catch all
+       potential conflicts, just the most obvious ones. */
+    for (i = 0; i < MAX_IO_WIN; i++)
+	if ((s->io[i].NumPorts != 0) &&
+	    ((s->io[i].BasePort & (align-1)) == *base))
+	    return 1;
+    for (i = 0; i < MAX_IO_WIN; i++) {
+	if (s->io[i].NumPorts == 0) {
+	    s->io[i].res = find_io_region(*base, num, align, s);
+	    if (s->io[i].res) {
+		s->io[i].Attributes = attr;
+		s->io[i].BasePort = *base = s->io[i].res->start;
+		s->io[i].NumPorts = s->io[i].InUse = num;
+		break;
+	    } else
+		return 1;
+	} else if (s->io[i].Attributes != attr)
+	    continue;
+	/* Try to extend top of window */
+	try = s->io[i].BasePort + s->io[i].NumPorts;
+	if ((*base == 0) || (*base == try))
+	    if (adjust_io_region(s->io[i].res, s->io[i].res->start,
+				 s->io[i].res->end + num, s) == 0) {
+		*base = try;
+		s->io[i].NumPorts += num;
+		s->io[i].InUse += num;
+		break;
+	    }
+	/* Try to extend bottom of window */
+	try = s->io[i].BasePort - num;
+	if ((*base == 0) || (*base == try))
+	    if (adjust_io_region(s->io[i].res, s->io[i].res->start - num,
+				 s->io[i].res->end, s) == 0) {
+		s->io[i].BasePort = *base = try;
+		s->io[i].NumPorts += num;
+		s->io[i].InUse += num;
+		break;
+	    }
+    }
+    return (i == MAX_IO_WIN);
+} /* alloc_io_space */
+
+static void release_io_space(struct pcmcia_socket *s, ioaddr_t base,
+			     ioaddr_t num)
+{
+    int i;
+
+    for (i = 0; i < MAX_IO_WIN; i++) {
+	if ((s->io[i].BasePort <= base) &&
+	    (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) {
+	    s->io[i].InUse -= num;
+	    /* Free the window if no one else is using it */
+	    if (s->io[i].InUse == 0) {
+		s->io[i].NumPorts = 0;
+		release_resource(s->io[i].res);
+		kfree(s->io[i].res);
+		s->io[i].res = NULL;
+	    }
+	}
+    }
+}
+
+/*======================================================================
+
+    Access_configuration_register() reads and writes configuration
+    registers in attribute memory.  Memory window 0 is reserved for
+    this and the tuple reading services.
+    
+======================================================================*/
+
+int pccard_access_configuration_register(struct pcmcia_socket *s,
+					 unsigned int function,
+					 conf_reg_t *reg)
+{
+    config_t *c;
+    int addr;
+    u_char val;
+
+    if (!s || !s->config)
+	return CS_NO_CARD;    
+
+    c = &s->config[function];
+
+    if (c == NULL)
+	return CS_NO_CARD;
+
+    if (!(c->state & CONFIG_LOCKED))
+	return CS_CONFIGURATION_LOCKED;
+
+    addr = (c->ConfigBase + reg->Offset) >> 1;
+    
+    switch (reg->Action) {
+    case CS_READ:
+	read_cis_mem(s, 1, addr, 1, &val);
+	reg->Value = val;
+	break;
+    case CS_WRITE:
+	val = reg->Value;
+	write_cis_mem(s, 1, addr, 1, &val);
+	break;
+    default:
+	return CS_BAD_ARGS;
+	break;
+    }
+    return CS_SUCCESS;
+} /* access_configuration_register */
+EXPORT_SYMBOL(pccard_access_configuration_register);
+
+
+/*====================================================================*/
+
+int pccard_get_configuration_info(struct pcmcia_socket *s,
+				  unsigned int function,
+				  config_info_t *config)
+{
+    config_t *c;
+    
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+
+    config->Function = function;
+
+#ifdef CONFIG_CARDBUS
+    if (s->state & SOCKET_CARDBUS) {
+	memset(config, 0, sizeof(config_info_t));
+	config->Vcc = s->socket.Vcc;
+	config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+	config->Option = s->cb_dev->subordinate->number;
+	if (s->state & SOCKET_CARDBUS_CONFIG) {
+	    config->Attributes = CONF_VALID_CLIENT;
+	    config->IntType = INT_CARDBUS;
+	    config->AssignedIRQ = s->irq.AssignedIRQ;
+	    if (config->AssignedIRQ)
+		config->Attributes |= CONF_ENABLE_IRQ;
+	    config->BasePort1 = s->io[0].BasePort;
+	    config->NumPorts1 = s->io[0].NumPorts;
+	}
+	return CS_SUCCESS;
+    }
+#endif
+    
+    c = (s->config != NULL) ? &s->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;
+    config->CardValues = c->CardValues;
+    config->IRQAttributes = c->irq.Attributes;
+    config->AssignedIRQ = s->irq.AssignedIRQ;
+    config->BasePort1 = c->io.BasePort1;
+    config->NumPorts1 = c->io.NumPorts1;
+    config->Attributes1 = c->io.Attributes1;
+    config->BasePort2 = c->io.BasePort2;
+    config->NumPorts2 = c->io.NumPorts2;
+    config->Attributes2 = c->io.Attributes2;
+    config->IOAddrLines = c->io.IOAddrLines;
+    
+    return CS_SUCCESS;
+} /* get_configuration_info */
+EXPORT_SYMBOL(pccard_get_configuration_info);
+
+/*======================================================================
+
+    Return information about this version of Card Services.
+    
+======================================================================*/
+
+int pcmcia_get_card_services_info(servinfo_t *info)
+{
+    unsigned int socket_count = 0;
+    struct list_head *tmp;
+    info->Signature[0] = 'C';
+    info->Signature[1] = 'S';
+    down_read(&pcmcia_socket_list_rwsem);
+    list_for_each(tmp, &pcmcia_socket_list)
+	    socket_count++;
+    up_read(&pcmcia_socket_list_rwsem);
+    info->Count = socket_count;
+    info->Revision = CS_RELEASE_CODE;
+    info->CSLevel = 0x0210;
+    info->VendorString = (char *)release;
+    return CS_SUCCESS;
+} /* get_card_services_info */
+
+
+/*====================================================================*/
+
+int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req)
+{
+    window_t *win;
+    int w;
+
+    if (!s || !(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+    for (w = idx; w < MAX_WIN; w++)
+	if (s->state & SOCKET_WIN_REQ(w)) break;
+    if (w == MAX_WIN)
+	return CS_NO_MORE_ITEMS;
+    win = &s->win[w];
+    req->Base = win->ctl.res->start;
+    req->Size = win->ctl.res->end - win->ctl.res->start + 1;
+    req->AccessSpeed = win->ctl.speed;
+    req->Attributes = 0;
+    if (win->ctl.flags & MAP_ATTRIB)
+	req->Attributes |= WIN_MEMORY_TYPE_AM;
+    if (win->ctl.flags & MAP_ACTIVE)
+	req->Attributes |= WIN_ENABLE;
+    if (win->ctl.flags & MAP_16BIT)
+	req->Attributes |= WIN_DATA_WIDTH_16;
+    if (win->ctl.flags & MAP_USE_WAIT)
+	req->Attributes |= WIN_USE_WAIT;
+    *handle = win;
+    return CS_SUCCESS;
+} /* get_window */
+EXPORT_SYMBOL(pcmcia_get_window);
+
+/*=====================================================================
+
+    Return the PCI device associated with a card..
+
+======================================================================*/
+
+#ifdef CONFIG_CARDBUS
+
+struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
+{
+	if (!s || !(s->state & SOCKET_CARDBUS))
+		return NULL;
+
+	return s->cb_dev->subordinate;
+}
+
+EXPORT_SYMBOL(pcmcia_lookup_bus);
+
+#endif
+
+/*======================================================================
+
+    Get the current socket state bits.  We don't support the latched
+    SocketState yet: I haven't seen any point for it.
+    
+======================================================================*/
+
+int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status)
+{
+    config_t *c;
+    int val;
+    
+    s->ops->get_status(s, &val);
+    status->CardState = status->SocketState = 0;
+    status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
+    status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
+    status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
+    status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
+    if (s->state & SOCKET_SUSPEND)
+	status->CardState |= CS_EVENT_PM_SUSPEND;
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+    
+    c = (s->config != NULL) ? &s->config[function] : NULL;
+    if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
+	(c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
+	u_char reg;
+	if (c->Present & PRESENT_PIN_REPLACE) {
+	    read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
+	    status->CardState |=
+		(reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
+	    status->CardState |=
+		(reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
+	    status->CardState |=
+		(reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
+	    status->CardState |=
+		(reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
+	} else {
+	    /* No PRR?  Then assume we're always ready */
+	    status->CardState |= CS_EVENT_READY_CHANGE;
+	}
+	if (c->Present & PRESENT_EXT_STATUS) {
+	    read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
+	    status->CardState |=
+		(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
+	}
+	return CS_SUCCESS;
+    }
+    status->CardState |=
+	(val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
+    status->CardState |=
+	(val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
+    status->CardState |=
+	(val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
+    status->CardState |=
+	(val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
+    return CS_SUCCESS;
+} /* get_status */
+EXPORT_SYMBOL(pccard_get_status);
+
+/*======================================================================
+
+    Change the card address of an already open memory window.
+    
+======================================================================*/
+
+int pcmcia_get_mem_page(window_handle_t win, memreq_t *req)
+{
+    if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+	return CS_BAD_HANDLE;
+    req->Page = 0;
+    req->CardOffset = win->ctl.card_start;
+    return CS_SUCCESS;
+} /* get_mem_page */
+
+int pcmcia_map_mem_page(window_handle_t win, memreq_t *req)
+{
+    struct pcmcia_socket *s;
+    if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+	return CS_BAD_HANDLE;
+    if (req->Page != 0)
+	return CS_BAD_PAGE;
+    s = win->sock;
+    win->ctl.card_start = req->CardOffset;
+    if (s->ops->set_mem_map(s, &win->ctl) != 0)
+	return CS_BAD_OFFSET;
+    return CS_SUCCESS;
+} /* map_mem_page */
+
+/*======================================================================
+
+    Modify a locked socket configuration
+    
+======================================================================*/
+
+int pcmcia_modify_configuration(client_handle_t handle,
+				modconf_t *mod)
+{
+    struct pcmcia_socket *s;
+    config_t *c;
+    
+    if (CHECK_HANDLE(handle))
+	return CS_BAD_HANDLE;
+    s = SOCKET(handle); c = CONFIG(handle);
+    if (!(s->state & SOCKET_PRESENT))
+	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;
+	    s->socket.io_irq = s->irq.AssignedIRQ;
+	} else {
+	    c->Attributes &= ~CONF_ENABLE_IRQ;
+	    s->socket.io_irq = 0;
+	}
+	s->ops->set_socket(s, &s->socket);
+    }
+
+    if (mod->Attributes & CONF_VCC_CHANGE_VALID)
+	return CS_BAD_VCC;
+
+    /* We only allow changing Vpp1 and Vpp2 to the same value */
+    if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) &&
+	(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
+	if (mod->Vpp1 != mod->Vpp2)
+	    return CS_BAD_VPP;
+	c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1;
+	if (s->ops->set_socket(s, &s->socket))
+	    return CS_BAD_VPP;
+    } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
+	       (mod->Attributes & CONF_VPP2_CHANGE_VALID))
+	return CS_BAD_VPP;
+
+    return CS_SUCCESS;
+} /* modify_configuration */
+
+/* register pcmcia_callback */
+int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
+{
+        int ret = 0;
+
+	/* s->skt_sem also protects s->callback */
+	down(&s->skt_sem);
+
+	if (c) {
+		/* registration */
+		if (s->callback) {
+			ret = -EBUSY;
+			goto err;
+		}
+
+		s->callback = c;
+
+		if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT)
+			send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+	} else
+		s->callback = NULL;
+ err:
+	up(&s->skt_sem);
+
+	return ret;
+}
+EXPORT_SYMBOL(pccard_register_pcmcia);
+
+/*====================================================================*/
+
+int pcmcia_release_configuration(client_handle_t handle)
+{
+    pccard_io_map io = { 0, 0, 0, 0, 1 };
+    struct pcmcia_socket *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)
+	return CS_SUCCESS;
+#endif
+    
+    if (!(handle->state & CLIENT_STALE)) {
+	config_t *c = CONFIG(handle);
+	if (--(s->lock_count) == 0) {
+	    s->socket.flags = SS_OUTPUT_ENA;   /* Is this correct? */
+	    s->socket.Vpp = 0;
+	    s->socket.io_irq = 0;
+	    s->ops->set_socket(s, &s->socket);
+	}
+	if (c->state & CONFIG_IO_REQ)
+	    for (i = 0; i < MAX_IO_WIN; i++) {
+		if (s->io[i].NumPorts == 0)
+		    continue;
+		s->io[i].Config--;
+		if (s->io[i].Config != 0)
+		    continue;
+		io.map = i;
+		s->ops->set_io_map(s, &io);
+	    }
+	c->state &= ~CONFIG_LOCKED;
+    }
+    
+    return CS_SUCCESS;
+} /* release_configuration */
+
+/*======================================================================
+
+    Release_io() releases the I/O ranges allocated by a client.  This
+    may be invoked some time after a card ejection has already dumped
+    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)
+{
+    struct pcmcia_socket *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)
+	return CS_SUCCESS;
+#endif
+    
+    if (!(handle->state & CLIENT_STALE)) {
+	config_t *c = CONFIG(handle);
+	if (c->state & CONFIG_LOCKED)
+	    return CS_CONFIGURATION_LOCKED;
+	if ((c->io.BasePort1 != req->BasePort1) ||
+	    (c->io.NumPorts1 != req->NumPorts1) ||
+	    (c->io.BasePort2 != req->BasePort2) ||
+	    (c->io.NumPorts2 != req->NumPorts2))
+	    return CS_BAD_ARGS;
+	c->state &= ~CONFIG_IO_REQ;
+    }
+
+    release_io_space(s, req->BasePort1, req->NumPorts1);
+    if (req->NumPorts2)
+	release_io_space(s, req->BasePort2, req->NumPorts2);
+    
+    return CS_SUCCESS;
+} /* release_io */
+
+/*====================================================================*/
+
+int pcmcia_release_irq(client_handle_t handle, irq_req_t *req)
+{
+    struct pcmcia_socket *s;
+    if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ))
+	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)
+	    return CS_CONFIGURATION_LOCKED;
+	if (c->irq.Attributes != req->Attributes)
+	    return CS_BAD_ATTRIBUTE;
+	if (s->irq.AssignedIRQ != req->AssignedIRQ)
+	    return CS_BAD_IRQ;
+	if (--s->irq.Config == 0) {
+	    c->state &= ~CONFIG_IRQ_REQ;
+	    s->irq.AssignedIRQ = 0;
+	}
+    }
+    
+    if (req->Attributes & IRQ_HANDLE_PRESENT) {
+	free_irq(req->AssignedIRQ, req->Instance);
+    }
+
+#ifdef CONFIG_PCMCIA_PROBE
+    pcmcia_used_irq[req->AssignedIRQ]--;
+#endif
+
+    return CS_SUCCESS;
+} /* cs_release_irq */
+
+/*====================================================================*/
+
+int pcmcia_release_window(window_handle_t win)
+{
+    struct pcmcia_socket *s;
+    
+    if ((win == NULL) || (win->magic != WINDOW_MAGIC))
+	return CS_BAD_HANDLE;
+    s = win->sock;
+    if (!(win->handle->state & CLIENT_WIN_REQ(win->index)))
+	return CS_BAD_HANDLE;
+
+    /* Shut down memory window */
+    win->ctl.flags &= ~MAP_ACTIVE;
+    s->ops->set_mem_map(s, &win->ctl);
+    s->state &= ~SOCKET_WIN_REQ(win->index);
+
+    /* Release system memory */
+    if (win->ctl.res) {
+	release_resource(win->ctl.res);
+	kfree(win->ctl.res);
+	win->ctl.res = NULL;
+    }
+    win->handle->state &= ~CLIENT_WIN_REQ(win->index);
+
+    win->magic = 0;
+    
+    return CS_SUCCESS;
+} /* release_window */
+
+/*====================================================================*/
+
+int pcmcia_request_configuration(client_handle_t handle,
+				 config_req_t *req)
+{
+    int i;
+    u_int base;
+    struct pcmcia_socket *s;
+    config_t *c;
+    pccard_io_map iomap;
+    
+    if (CHECK_HANDLE(handle))
+	return CS_BAD_HANDLE;
+    s = SOCKET(handle);
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+    
+#ifdef CONFIG_CARDBUS
+    if (handle->state & CLIENT_CARDBUS)
+	return CS_UNSUPPORTED_MODE;
+#endif
+    
+    if (req->IntType & INT_CARDBUS)
+	return CS_UNSUPPORTED_MODE;
+    c = CONFIG(handle);
+    if (c->state & CONFIG_LOCKED)
+	return CS_CONFIGURATION_LOCKED;
+
+    /* Do power control.  We don't allow changes in Vcc. */
+    if (s->socket.Vcc != req->Vcc)
+	return CS_BAD_VCC;
+    if (req->Vpp1 != req->Vpp2)
+	return CS_BAD_VPP;
+    s->socket.Vpp = req->Vpp1;
+    if (s->ops->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;
+    if (req->IntType & INT_MEMORY_AND_IO)
+	s->socket.flags |= SS_IOCARD;
+    if (req->IntType & INT_ZOOMED_VIDEO)
+	s->socket.flags |= SS_ZVCARD | SS_IOCARD;
+    if (req->Attributes & CONF_ENABLE_DMA)
+	s->socket.flags |= SS_DMA_MODE;
+    if (req->Attributes & CONF_ENABLE_SPKR)
+	s->socket.flags |= SS_SPKR_ENA;
+    if (req->Attributes & CONF_ENABLE_IRQ)
+	s->socket.io_irq = s->irq.AssignedIRQ;
+    else
+	s->socket.io_irq = 0;
+    s->ops->set_socket(s, &s->socket);
+    s->lock_count++;
+    
+    /* Set up CIS configuration registers */
+    base = c->ConfigBase = req->ConfigBase;
+    c->Present = c->CardValues = req->Present;
+    if (req->Present & PRESENT_COPY) {
+	c->Copy = req->Copy;
+	write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy);
+    }
+    if (req->Present & PRESENT_OPTION) {
+	if (s->functions == 1) {
+	    c->Option = req->ConfigIndex & COR_CONFIG_MASK;
+	} else {
+	    c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK;
+	    c->Option |= COR_FUNC_ENA|COR_IREQ_ENA;
+	    if (req->Present & PRESENT_IOBASE_0)
+		c->Option |= COR_ADDR_DECODE;
+	}
+	if (c->state & CONFIG_IRQ_REQ)
+	    if (!(c->irq.Attributes & IRQ_FORCED_PULSE))
+		c->Option |= COR_LEVEL_REQ;
+	write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option);
+	mdelay(40);
+    }
+    if (req->Present & PRESENT_STATUS) {
+	c->Status = req->Status;
+	write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status);
+    }
+    if (req->Present & PRESENT_PIN_REPLACE) {
+	c->Pin = req->Pin;
+	write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin);
+    }
+    if (req->Present & PRESENT_EXT_STATUS) {
+	c->ExtStatus = req->ExtStatus;
+	write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus);
+    }
+    if (req->Present & PRESENT_IOBASE_0) {
+	u_char b = c->io.BasePort1 & 0xff;
+	write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b);
+	b = (c->io.BasePort1 >> 8) & 0xff;
+	write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b);
+    }
+    if (req->Present & PRESENT_IOSIZE) {
+	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;
+	for (i = 0; i < MAX_IO_WIN; i++)
+	    if (s->io[i].NumPorts != 0) {
+		iomap.map = i;
+		iomap.flags = MAP_ACTIVE;
+		switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) {
+		case IO_DATA_PATH_WIDTH_16:
+		    iomap.flags |= MAP_16BIT; break;
+		case IO_DATA_PATH_WIDTH_AUTO:
+		    iomap.flags |= MAP_AUTOSZ; break;
+		default:
+		    break;
+		}
+		iomap.start = s->io[i].BasePort;
+		iomap.stop = iomap.start + s->io[i].NumPorts - 1;
+		s->ops->set_io_map(s, &iomap);
+		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)
+{
+    struct pcmcia_socket *s;
+    config_t *c;
+    
+    if (CHECK_HANDLE(handle))
+	return CS_BAD_HANDLE;
+    s = SOCKET(handle);
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+
+    if (handle->state & CLIENT_CARDBUS) {
+#ifdef CONFIG_CARDBUS
+	handle->state |= CLIENT_IO_REQ;
+	return CS_SUCCESS;
+#else
+	return CS_UNSUPPORTED_FUNCTION;
+#endif
+    }
+
+    if (!req)
+	return CS_UNSUPPORTED_MODE;
+    c = CONFIG(handle);
+    if (c->state & CONFIG_LOCKED)
+	return CS_CONFIGURATION_LOCKED;
+    if (c->state & CONFIG_IO_REQ)
+	return CS_IN_USE;
+    if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))
+	return CS_BAD_ATTRIBUTE;
+    if ((req->NumPorts2 > 0) &&
+	(req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)))
+	return CS_BAD_ATTRIBUTE;
+
+    if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
+		       req->NumPorts1, req->IOAddrLines))
+	return CS_IN_USE;
+
+    if (req->NumPorts2) {
+	if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
+			   req->NumPorts2, req->IOAddrLines)) {
+	    release_io_space(s, req->BasePort1, req->NumPorts1);
+	    return CS_IN_USE;
+	}
+    }
+
+    c->io = *req;
+    c->state |= CONFIG_IO_REQ;
+    handle->state |= CLIENT_IO_REQ;
+    return CS_SUCCESS;
+} /* request_io */
+
+/*======================================================================
+
+    Request_irq() reserves an irq for this client.
+
+    Also, since Linux only reserves irq's when they are actually
+    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.
+    
+======================================================================*/
+
+#ifdef CONFIG_PCMCIA_PROBE
+static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+	return IRQ_NONE;
+}
+#endif
+
+int pcmcia_request_irq(client_handle_t handle, irq_req_t *req)
+{
+	struct pcmcia_socket *s;
+	config_t *c;
+	int ret = CS_IN_USE, irq = 0;
+	struct pcmcia_device *p_dev = handle_to_pdev(handle);
+
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	if (!(s->state & SOCKET_PRESENT))
+		return CS_NO_CARD;
+	c = CONFIG(handle);
+	if (c->state & CONFIG_LOCKED)
+		return CS_CONFIGURATION_LOCKED;
+	if (c->state & CONFIG_IRQ_REQ)
+		return CS_IN_USE;
+
+#ifdef CONFIG_PCMCIA_PROBE
+	if (s->irq.AssignedIRQ != 0) {
+		/* If the interrupt is already assigned, it must be the same */
+		irq = s->irq.AssignedIRQ;
+	} else {
+		int try;
+		u32 mask = s->irq_mask;
+		void *data = NULL;
+
+		for (try = 0; try < 64; try++) {
+			irq = try % 32;
+
+			/* marked as available by driver, and not blocked by userspace? */
+			if (!((mask >> irq) & 1))
+				continue;
+
+			/* avoid an IRQ which is already used by a PCMCIA card */
+			if ((try < 32) && pcmcia_used_irq[irq])
+				continue;
+
+			/* register the correct driver, if possible, of check whether
+			 * registering a dummy handle works, i.e. if the IRQ isn't
+			 * marked as used by the kernel resource management core */
+			ret = request_irq(irq,
+					  (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action,
+					  ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+					   (s->functions > 1) ||
+					   (irq == s->pci_irq)) ? SA_SHIRQ : 0,
+					  p_dev->dev.bus_id,
+					  (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data);
+			if (!ret) {
+				if (!(req->Attributes & IRQ_HANDLE_PRESENT))
+					free_irq(irq, data);
+				break;
+			}
+		}
+	}
+#endif
+	if (ret) {
+		if (!s->pci_irq)
+			return ret;
+		irq = s->pci_irq;
+	}
+
+	if (ret && req->Attributes & IRQ_HANDLE_PRESENT) {
+		if (request_irq(irq, req->Handler,
+				((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) ||
+				 (s->functions > 1) ||
+				 (irq == s->pci_irq)) ? SA_SHIRQ : 0,
+				p_dev->dev.bus_id, req->Instance))
+			return CS_IN_USE;
+	}
+
+	c->irq.Attributes = req->Attributes;
+	s->irq.AssignedIRQ = req->AssignedIRQ = irq;
+	s->irq.Config++;
+
+	c->state |= CONFIG_IRQ_REQ;
+	handle->state |= CLIENT_IRQ_REQ;
+
+#ifdef CONFIG_PCMCIA_PROBE
+	pcmcia_used_irq[irq]++;
+#endif
+
+	return CS_SUCCESS;
+} /* pcmcia_request_irq */
+
+/*======================================================================
+
+    Request_window() establishes a mapping between card memory space
+    and system memory space.
+
+======================================================================*/
+
+int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh)
+{
+    struct pcmcia_socket *s;
+    window_t *win;
+    u_long align;
+    int w;
+    
+    if (CHECK_HANDLE(*handle))
+	return CS_BAD_HANDLE;
+    s = (*handle)->Socket;
+    if (!(s->state & SOCKET_PRESENT))
+	return CS_NO_CARD;
+    if (req->Attributes & (WIN_PAGED | WIN_SHARED))
+	return CS_BAD_ATTRIBUTE;
+
+    /* Window size defaults to smallest available */
+    if (req->Size == 0)
+	req->Size = s->map_size;
+    align = (((s->features & SS_CAP_MEM_ALIGN) ||
+	      (req->Attributes & WIN_STRICT_ALIGN)) ?
+	     req->Size : s->map_size);
+    if (req->Size & (s->map_size-1))
+	return CS_BAD_SIZE;
+    if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
+	(req->Base & (align-1)))
+	return CS_BAD_BASE;
+    if (req->Base)
+	align = 0;
+
+    /* Allocate system memory window */
+    for (w = 0; w < MAX_WIN; w++)
+	if (!(s->state & SOCKET_WIN_REQ(w))) break;
+    if (w == MAX_WIN)
+	return CS_OUT_OF_RESOURCE;
+
+    win = &s->win[w];
+    win->magic = WINDOW_MAGIC;
+    win->index = w;
+    win->handle = *handle;
+    win->sock = s;
+
+    if (!(s->features & SS_CAP_STATIC_MAP)) {
+	win->ctl.res = find_mem_region(req->Base, req->Size, align,
+				       (req->Attributes & WIN_MAP_BELOW_1MB), s);
+	if (!win->ctl.res)
+	    return CS_IN_USE;
+    }
+    (*handle)->state |= CLIENT_WIN_REQ(w);
+
+    /* Configure the socket controller */
+    win->ctl.map = w+1;
+    win->ctl.flags = 0;
+    win->ctl.speed = req->AccessSpeed;
+    if (req->Attributes & WIN_MEMORY_TYPE)
+	win->ctl.flags |= MAP_ATTRIB;
+    if (req->Attributes & WIN_ENABLE)
+	win->ctl.flags |= MAP_ACTIVE;
+    if (req->Attributes & WIN_DATA_WIDTH_16)
+	win->ctl.flags |= MAP_16BIT;
+    if (req->Attributes & WIN_USE_WAIT)
+	win->ctl.flags |= MAP_USE_WAIT;
+    win->ctl.card_start = 0;
+    if (s->ops->set_mem_map(s, &win->ctl) != 0)
+	return CS_BAD_ARGS;
+    s->state |= SOCKET_WIN_REQ(w);
+
+    /* Return window handle */
+    if (s->features & SS_CAP_STATIC_MAP) {
+	req->Base = win->ctl.static_start;
+    } else {
+	req->Base = win->ctl.res->start;
+    }
+    *wh = win;
+    
+    return CS_SUCCESS;
+} /* request_window */
+
+/*======================================================================
+
+    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 pccard_reset_card(struct pcmcia_socket *skt)
+{
+	int ret;
+    
+	cs_dbg(skt, 1, "resetting socket\n");
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_SUSPEND) {
+			ret = CS_IN_USE;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
+
+		ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
+		if (ret == 0) {
+			send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
+			if (socket_reset(skt) == CS_SUCCESS)
+				send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+		}
+
+		ret = CS_SUCCESS;
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
+} /* reset_card */
+EXPORT_SYMBOL(pccard_reset_card);
+
+/*======================================================================
+
+    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(struct pcmcia_socket *skt)
+{
+	int ret;
+    
+	cs_dbg(skt, 1, "suspending socket\n");
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
+		ret = socket_suspend(skt);
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
+} /* suspend_card */
+
+int pcmcia_resume_card(struct pcmcia_socket *skt)
+{
+	int ret;
+    
+	cs_dbg(skt, 1, "waking up socket\n");
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
+		ret = socket_resume(skt);
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
+} /* resume_card */
+
+/*======================================================================
+
+    These handle user requests to eject or insert a card.
+    
+======================================================================*/
+
+int pcmcia_eject_card(struct pcmcia_socket *skt)
+{
+	int ret;
+    
+	cs_dbg(skt, 1, "user eject request\n");
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = -ENODEV;
+			break;
+		}
+
+		ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
+		if (ret != 0) {
+			ret = -EINVAL;
+			break;
+		}
+
+		socket_remove(skt);
+		ret = 0;
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
+} /* eject_card */
+
+int pcmcia_insert_card(struct pcmcia_socket *skt)
+{
+	int ret;
+
+	cs_dbg(skt, 1, "user insert request\n");
+
+	down(&skt->skt_sem);
+	do {
+		if (skt->state & SOCKET_PRESENT) {
+			ret = -EBUSY;
+			break;
+		}
+		if (socket_insert(skt) == CS_NO_CARD) {
+			ret = -ENODEV;
+			break;
+		}
+		ret = 0;
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
+} /* insert_card */
+
+/*======================================================================
+
+    OS-specific module glue goes here
+    
+======================================================================*/
+/* in alpha order */
+EXPORT_SYMBOL(pcmcia_eject_card);
+EXPORT_SYMBOL(pcmcia_get_card_services_info);
+EXPORT_SYMBOL(pcmcia_get_mem_page);
+EXPORT_SYMBOL(pcmcia_insert_card);
+EXPORT_SYMBOL(pcmcia_map_mem_page);
+EXPORT_SYMBOL(pcmcia_modify_configuration);
+EXPORT_SYMBOL(pcmcia_release_configuration);
+EXPORT_SYMBOL(pcmcia_release_io);
+EXPORT_SYMBOL(pcmcia_release_irq);
+EXPORT_SYMBOL(pcmcia_release_window);
+EXPORT_SYMBOL(pcmcia_replace_cis);
+EXPORT_SYMBOL(pcmcia_request_configuration);
+EXPORT_SYMBOL(pcmcia_request_io);
+EXPORT_SYMBOL(pcmcia_request_irq);
+EXPORT_SYMBOL(pcmcia_request_window);
+EXPORT_SYMBOL(pcmcia_resume_card);
+EXPORT_SYMBOL(pcmcia_suspend_card);
+
+EXPORT_SYMBOL(dead_socket);
+EXPORT_SYMBOL(pcmcia_parse_events);
+
+struct class pcmcia_socket_class = {
+	.name = "pcmcia_socket",
+	.release = pcmcia_release_socket,
+};
+EXPORT_SYMBOL(pcmcia_socket_class);
+
+
+static int __init init_pcmcia_cs(void)
+{
+	int ret;
+	printk(KERN_INFO "%s\n", release);
+	printk(KERN_INFO "  %s\n", options);
+
+	ret = class_register(&pcmcia_socket_class);
+	if (ret)
+		return (ret);
+	return class_interface_register(&pccard_sysfs_interface);
+}
+
+static void __exit exit_pcmcia_cs(void)
+{
+    printk(KERN_INFO "unloading Kernel Card Services\n");
+    class_interface_unregister(&pccard_sysfs_interface);
+    class_unregister(&pcmcia_socket_class);
+}
+
+subsys_initcall(init_pcmcia_cs);
+module_exit(exit_pcmcia_cs);
+
+/*====================================================================*/
+
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
new file mode 100644
index 0000000..7933a7d
--- /dev/null
+++ b/drivers/pcmcia/cs_internal.h
@@ -0,0 +1,185 @@
+/*
+ * cs_internal.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.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+#ifndef _LINUX_CS_INTERNAL_H
+#define _LINUX_CS_INTERNAL_H
+
+#include <linux/config.h>
+
+#define CLIENT_MAGIC 	0x51E6
+typedef struct client_t client_t;
+
+/* Flags in client state */
+#define CLIENT_CONFIG_LOCKED	0x0001
+#define CLIENT_IRQ_REQ		0x0002
+#define CLIENT_IO_REQ		0x0004
+#define CLIENT_UNBOUND		0x0008
+#define CLIENT_STALE		0x0010
+#define CLIENT_WIN_REQ(i)	(0x20<<(i))
+#define CLIENT_CARDBUS		0x8000
+
+#define REGION_MAGIC	0xE3C9
+typedef struct region_t {
+    u_short		region_magic;
+    u_short		state;
+    dev_info_t		dev_info;
+    client_handle_t	mtd;
+    u_int		MediaID;
+    region_info_t	info;
+} region_t;
+
+#define REGION_STALE	0x01
+
+/* Each card function gets one of these guys */
+typedef struct config_t {
+    u_int		state;
+    u_int		Attributes;
+    u_int		Vcc, Vpp1, Vpp2;
+    u_int		IntType;
+    u_int		ConfigBase;
+    u_char		Status, Pin, Copy, Option, ExtStatus;
+    u_int		Present;
+    u_int		CardValues;
+    io_req_t		io;
+    struct {
+	u_int		Attributes;
+    } irq;
+} config_t;
+
+struct cis_cache_entry {
+	struct list_head	node;
+	unsigned int		addr;
+	unsigned int		len;
+	unsigned int		attr;
+	unsigned char		cache[0];
+};
+
+/* Flags in config state */
+#define CONFIG_LOCKED		0x01
+#define CONFIG_IRQ_REQ		0x02
+#define CONFIG_IO_REQ		0x04
+
+/* Flags in socket state */
+#define SOCKET_PRESENT		0x0008
+#define SOCKET_INUSE		0x0010
+#define SOCKET_SUSPEND		0x0080
+#define SOCKET_WIN_REQ(i)	(0x0100<<(i))
+#define SOCKET_REGION_INFO	0x4000
+#define SOCKET_CARDBUS		0x8000
+#define SOCKET_CARDBUS_CONFIG	0x10000
+
+static inline int cs_socket_get(struct pcmcia_socket *skt)
+{
+	int ret;
+
+	WARN_ON(skt->state & SOCKET_INUSE);
+
+	ret = try_module_get(skt->owner);
+	if (ret)
+		skt->state |= SOCKET_INUSE;
+	return ret;
+}
+
+static inline void cs_socket_put(struct pcmcia_socket *skt)
+{
+	if (skt->state & SOCKET_INUSE) {
+		skt->state &= ~SOCKET_INUSE;
+		module_put(skt->owner);
+	}
+}
+
+#define CHECK_HANDLE(h) \
+    (((h) == NULL) || ((h)->client_magic != CLIENT_MAGIC))
+
+#define CHECK_SOCKET(s) \
+    (((s) >= sockets) || (socket_table[s]->ops == NULL))
+
+#define SOCKET(h) (h->Socket)
+#define CONFIG(h) (&SOCKET(h)->config[(h)->Function])
+
+#define CHECK_REGION(r) \
+    (((r) == NULL) || ((r)->region_magic != REGION_MAGIC))
+
+#define CHECK_ERASEQ(q) \
+    (((q) == NULL) || ((q)->eraseq_magic != ERASEQ_MAGIC))
+
+#define EVENT(h, e, p) \
+    ((h)->event_handler((e), (p), &(h)->event_callback_args))
+
+/* In cardbus.c */
+int cb_alloc(struct pcmcia_socket *s);
+void cb_free(struct pcmcia_socket *s);
+int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, void *ptr);
+
+/* In cistpl.c */
+int read_cis_mem(struct pcmcia_socket *s, int attr,
+		 u_int addr, u_int len, void *ptr);
+void write_cis_mem(struct pcmcia_socket *s, int attr,
+		   u_int addr, u_int len, void *ptr);
+void release_cis_mem(struct pcmcia_socket *s);
+void destroy_cis_cache(struct pcmcia_socket *s);
+int verify_cis_cache(struct pcmcia_socket *s);
+int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse);
+
+/* In rsrc_mgr */
+void pcmcia_validate_mem(struct pcmcia_socket *s);
+struct resource *find_io_region(unsigned long base, int num, unsigned long align,
+		   struct pcmcia_socket *s);
+int adjust_io_region(struct resource *res, unsigned long r_start,
+		     unsigned long r_end, struct pcmcia_socket *s);
+struct resource *find_mem_region(u_long base, u_long num, u_long align,
+		    int low, struct pcmcia_socket *s);
+int adjust_resource_info(client_handle_t handle, adjust_t *adj);
+void release_resource_db(struct pcmcia_socket *s);
+
+/* In socket_sysfs.c */
+extern struct class_interface pccard_sysfs_interface;
+
+/* In cs.c */
+extern struct rw_semaphore pcmcia_socket_list_rwsem;
+extern struct list_head pcmcia_socket_list;
+int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req);
+int pccard_get_configuration_info(struct pcmcia_socket *s, unsigned int function, config_info_t *config);
+int pccard_reset_card(struct pcmcia_socket *skt);
+int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status);
+int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int function, conf_reg_t *reg);
+
+
+struct pcmcia_callback{
+	struct module	*owner;
+	int		(*event) (struct pcmcia_socket *s, event_t event, int priority);
+	int		(*resources_done) (struct pcmcia_socket *s);
+};
+
+int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
+
+#define cs_socket_name(skt)	((skt)->dev.class_id)
+
+#ifdef DEBUG
+extern int cs_debug_level(int);
+
+#define cs_dbg(skt, lvl, fmt, arg...) do {		\
+	if (cs_debug_level(lvl))			\
+		printk(KERN_DEBUG "cs: %s: " fmt, 	\
+		       cs_socket_name(skt) , ## arg);	\
+} while (0)
+
+#else
+#define cs_dbg(skt, lvl, fmt, arg...) do { } while (0)
+#endif
+
+#define cs_err(skt, fmt, arg...) \
+	printk(KERN_ERR "cs: %s: " fmt, (skt)->dev.class_id , ## arg)
+
+#endif /* _LINUX_CS_INTERNAL_H */
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
new file mode 100644
index 0000000..66150d0
--- /dev/null
+++ b/drivers/pcmcia/ds.c
@@ -0,0 +1,1659 @@
+/*
+ * ds.c -- 16-bit PCMCIA core support
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ * (C) 2003 - 2004	Dominik Brodowski
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/ioctl.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/kref.h>
+#include <linux/workqueue.h>
+
+#include <asm/atomic.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+
+#include "cs_internal.h"
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("PCMCIA Driver Services");
+MODULE_LICENSE("GPL");
+
+#ifdef DEBUG
+int ds_pc_debug;
+
+module_param_named(pc_debug, ds_pc_debug, int, 0644);
+
+#define ds_dbg(lvl, fmt, arg...) do {				\
+	if (ds_pc_debug > (lvl))					\
+		printk(KERN_DEBUG "ds: " fmt , ## arg);		\
+} while (0)
+#else
+#define ds_dbg(lvl, fmt, arg...) do { } while (0)
+#endif
+
+/*====================================================================*/
+
+/* Device user information */
+#define MAX_EVENTS	32
+#define USER_MAGIC	0x7ea4
+#define CHECK_USER(u) \
+    (((u) == NULL) || ((u)->user_magic != USER_MAGIC))
+typedef struct user_info_t {
+    u_int		user_magic;
+    int			event_head, event_tail;
+    event_t		event[MAX_EVENTS];
+    struct user_info_t	*next;
+    struct pcmcia_bus_socket *socket;
+} user_info_t;
+
+/* Socket state information */
+struct pcmcia_bus_socket {
+	struct kref		refcount;
+	struct pcmcia_callback	callback;
+	int			state;
+	user_info_t		*user;
+	wait_queue_head_t	queue;
+	struct pcmcia_socket	*parent;
+
+	/* the PCMCIA devices connected to this socket (normally one, more
+	 * for multifunction devices: */
+	struct list_head	devices_list;
+	u8			device_count; /* the number of devices, used
+					       * only internally and subject
+					       * to incorrectness and change */
+};
+static spinlock_t pcmcia_dev_list_lock;
+
+#define DS_SOCKET_PRESENT		0x01
+#define DS_SOCKET_BUSY			0x02
+#define DS_SOCKET_REMOVAL_PENDING	0x10
+#define DS_SOCKET_DEAD			0x80
+
+/*====================================================================*/
+
+static int major_dev = -1;
+
+static int unbind_request(struct pcmcia_bus_socket *s);
+
+/*====================================================================*/
+
+/* code which was in cs.c before */
+
+/* String tables for error messages */
+
+typedef struct lookup_t {
+    int key;
+    char *msg;
+} lookup_t;
+
+static const lookup_t error_table[] = {
+    { CS_SUCCESS,		"Operation succeeded" },
+    { CS_BAD_ADAPTER,		"Bad adapter" },
+    { CS_BAD_ATTRIBUTE, 	"Bad attribute", },
+    { CS_BAD_BASE,		"Bad base address" },
+    { CS_BAD_EDC,		"Bad EDC" },
+    { CS_BAD_IRQ,		"Bad IRQ" },
+    { CS_BAD_OFFSET,		"Bad offset" },
+    { CS_BAD_PAGE,		"Bad page number" },
+    { CS_READ_FAILURE,		"Read failure" },
+    { CS_BAD_SIZE,		"Bad size" },
+    { CS_BAD_SOCKET,		"Bad socket" },
+    { CS_BAD_TYPE,		"Bad type" },
+    { CS_BAD_VCC,		"Bad Vcc" },
+    { CS_BAD_VPP,		"Bad Vpp" },
+    { CS_BAD_WINDOW,		"Bad window" },
+    { CS_WRITE_FAILURE,		"Write failure" },
+    { CS_NO_CARD,		"No card present" },
+    { CS_UNSUPPORTED_FUNCTION,	"Usupported function" },
+    { CS_UNSUPPORTED_MODE,	"Unsupported mode" },
+    { CS_BAD_SPEED,		"Bad speed" },
+    { CS_BUSY,			"Resource busy" },
+    { CS_GENERAL_FAILURE,	"General failure" },
+    { CS_WRITE_PROTECTED,	"Write protected" },
+    { CS_BAD_ARG_LENGTH,	"Bad argument length" },
+    { CS_BAD_ARGS,		"Bad arguments" },
+    { CS_CONFIGURATION_LOCKED,	"Configuration locked" },
+    { CS_IN_USE,		"Resource in use" },
+    { CS_NO_MORE_ITEMS,		"No more items" },
+    { CS_OUT_OF_RESOURCE,	"Out of resource" },
+    { CS_BAD_HANDLE,		"Bad handle" },
+    { CS_BAD_TUPLE,		"Bad CIS tuple" }
+};
+
+
+static const lookup_t service_table[] = {
+    { AccessConfigurationRegister,	"AccessConfigurationRegister" },
+    { AddSocketServices,		"AddSocketServices" },
+    { AdjustResourceInfo,		"AdjustResourceInfo" },
+    { CheckEraseQueue,			"CheckEraseQueue" },
+    { CloseMemory,			"CloseMemory" },
+    { DeregisterClient,			"DeregisterClient" },
+    { DeregisterEraseQueue,		"DeregisterEraseQueue" },
+    { GetCardServicesInfo,		"GetCardServicesInfo" },
+    { GetClientInfo,			"GetClientInfo" },
+    { GetConfigurationInfo,		"GetConfigurationInfo" },
+    { GetEventMask,			"GetEventMask" },
+    { GetFirstClient,			"GetFirstClient" },
+    { GetFirstRegion,			"GetFirstRegion" },
+    { GetFirstTuple,			"GetFirstTuple" },
+    { GetNextClient,			"GetNextClient" },
+    { GetNextRegion,			"GetNextRegion" },
+    { GetNextTuple,			"GetNextTuple" },
+    { GetStatus,			"GetStatus" },
+    { GetTupleData,			"GetTupleData" },
+    { MapMemPage,			"MapMemPage" },
+    { ModifyConfiguration,		"ModifyConfiguration" },
+    { ModifyWindow,			"ModifyWindow" },
+    { OpenMemory,			"OpenMemory" },
+    { ParseTuple,			"ParseTuple" },
+    { ReadMemory,			"ReadMemory" },
+    { RegisterClient,			"RegisterClient" },
+    { RegisterEraseQueue,		"RegisterEraseQueue" },
+    { RegisterMTD,			"RegisterMTD" },
+    { ReleaseConfiguration,		"ReleaseConfiguration" },
+    { ReleaseIO,			"ReleaseIO" },
+    { ReleaseIRQ,			"ReleaseIRQ" },
+    { ReleaseWindow,			"ReleaseWindow" },
+    { RequestConfiguration,		"RequestConfiguration" },
+    { RequestIO,			"RequestIO" },
+    { RequestIRQ,			"RequestIRQ" },
+    { RequestSocketMask,		"RequestSocketMask" },
+    { RequestWindow,			"RequestWindow" },
+    { ResetCard,			"ResetCard" },
+    { SetEventMask,			"SetEventMask" },
+    { ValidateCIS,			"ValidateCIS" },
+    { WriteMemory,			"WriteMemory" },
+    { BindDevice,			"BindDevice" },
+    { BindMTD,				"BindMTD" },
+    { ReportError,			"ReportError" },
+    { SuspendCard,			"SuspendCard" },
+    { ResumeCard,			"ResumeCard" },
+    { EjectCard,			"EjectCard" },
+    { InsertCard,			"InsertCard" },
+    { ReplaceCIS,			"ReplaceCIS" }
+};
+
+
+int pcmcia_report_error(client_handle_t handle, error_info_t *err)
+{
+	int i;
+	char *serv;
+
+	if (CHECK_HANDLE(handle))
+		printk(KERN_NOTICE);
+	else {
+		struct pcmcia_device *p_dev = handle_to_pdev(handle);
+		printk(KERN_NOTICE "%s: ", p_dev->dev.bus_id);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(service_table); i++)
+		if (service_table[i].key == err->func)
+			break;
+	if (i < ARRAY_SIZE(service_table))
+		serv = service_table[i].msg;
+	else
+		serv = "Unknown service number";
+
+	for (i = 0; i < ARRAY_SIZE(error_table); i++)
+		if (error_table[i].key == err->retcode)
+			break;
+	if (i < ARRAY_SIZE(error_table))
+		printk("%s: %s\n", serv, error_table[i].msg);
+	else
+		printk("%s: Unknown error code %#x\n", serv, err->retcode);
+
+	return CS_SUCCESS;
+} /* report_error */
+EXPORT_SYMBOL(pcmcia_report_error);
+
+/* end of code which was in cs.c before */
+
+/*======================================================================*/
+
+void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+	pcmcia_report_error(handle, &err);
+}
+EXPORT_SYMBOL(cs_error);
+
+/*======================================================================*/
+
+static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
+static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr);
+
+static void pcmcia_release_bus_socket(struct kref *refcount)
+{
+	struct pcmcia_bus_socket *s = container_of(refcount, struct pcmcia_bus_socket, refcount);
+	pcmcia_put_socket(s->parent);
+	kfree(s);
+}
+
+static void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s)
+{
+	kref_put(&s->refcount, pcmcia_release_bus_socket);
+}
+
+static struct pcmcia_bus_socket *pcmcia_get_bus_socket(struct pcmcia_bus_socket *s)
+{
+	kref_get(&s->refcount);
+	return (s);
+}
+
+/**
+ * pcmcia_register_driver - register a PCMCIA driver with the bus core
+ *
+ * Registers a PCMCIA driver with the PCMCIA bus core.
+ */
+static int pcmcia_device_probe(struct device *dev);
+static int pcmcia_device_remove(struct device * dev);
+
+int pcmcia_register_driver(struct pcmcia_driver *driver)
+{
+	if (!driver)
+		return -EINVAL;
+
+	/* initialize common fields */
+	driver->drv.bus = &pcmcia_bus_type;
+	driver->drv.owner = driver->owner;
+	driver->drv.probe = pcmcia_device_probe;
+	driver->drv.remove = pcmcia_device_remove;
+
+	return driver_register(&driver->drv);
+}
+EXPORT_SYMBOL(pcmcia_register_driver);
+
+/**
+ * pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core
+ */
+void pcmcia_unregister_driver(struct pcmcia_driver *driver)
+{
+	driver_unregister(&driver->drv);
+}
+EXPORT_SYMBOL(pcmcia_unregister_driver);
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_pccard = NULL;
+
+static int proc_read_drivers_callback(struct device_driver *driver, void *d)
+{
+	char **p = d;
+	struct pcmcia_driver *p_drv = container_of(driver,
+						   struct pcmcia_driver, drv);
+
+	*p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name,
+#ifdef CONFIG_MODULE_UNLOAD
+		      (p_drv->owner) ? module_refcount(p_drv->owner) : 1
+#else
+		      1
+#endif
+	);
+	d = (void *) p;
+
+	return 0;
+}
+
+static int proc_read_drivers(char *buf, char **start, off_t pos,
+			     int count, int *eof, void *data)
+{
+	char *p = buf;
+
+	bus_for_each_drv(&pcmcia_bus_type, NULL, 
+			 (void *) &p, proc_read_drivers_callback);
+
+	return (p - buf);
+}
+#endif
+
+/* pcmcia_device handling */
+
+static struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev)
+{
+	struct device *tmp_dev;
+	tmp_dev = get_device(&p_dev->dev);
+	if (!tmp_dev)
+		return NULL;
+	return to_pcmcia_dev(tmp_dev);
+}
+
+static void pcmcia_put_dev(struct pcmcia_device *p_dev)
+{
+	if (p_dev)
+		put_device(&p_dev->dev);
+}
+
+static void pcmcia_release_dev(struct device *dev)
+{
+	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+	ds_dbg(1, "releasing dev %p\n", p_dev);
+	pcmcia_put_bus_socket(p_dev->socket->pcmcia);
+	kfree(p_dev);
+}
+
+
+static int pcmcia_device_probe(struct device * dev)
+{
+	struct pcmcia_device *p_dev;
+	struct pcmcia_driver *p_drv;
+	int ret = 0;
+
+	dev = get_device(dev);
+	if (!dev)
+		return -ENODEV;
+
+	p_dev = to_pcmcia_dev(dev);
+	p_drv = to_pcmcia_drv(dev->driver);
+
+	if (!try_module_get(p_drv->owner)) {
+		ret = -EINVAL;
+		goto put_dev;
+	}
+
+	if (p_drv->attach) {
+		p_dev->instance = p_drv->attach();
+		if ((!p_dev->instance) || (p_dev->client.state & CLIENT_UNBOUND)) {
+			printk(KERN_NOTICE "ds: unable to create instance "
+			       "of '%s'!\n", p_drv->drv.name);
+			ret = -EINVAL;
+		}
+	}
+
+	if (ret)
+		module_put(p_drv->owner);
+ put_dev:
+	if ((ret) || !(p_drv->attach))
+		put_device(dev);
+	return (ret);
+}
+
+
+static int pcmcia_device_remove(struct device * dev)
+{
+	struct pcmcia_device *p_dev;
+	struct pcmcia_driver *p_drv;
+
+	/* detach the "instance" */
+	p_dev = to_pcmcia_dev(dev);
+	p_drv = to_pcmcia_drv(dev->driver);
+
+	if (p_drv) {
+		if ((p_drv->detach) && (p_dev->instance)) {
+			p_drv->detach(p_dev->instance);
+			/* from pcmcia_probe_device */
+			put_device(&p_dev->dev);
+		}
+		module_put(p_drv->owner);
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * pcmcia_device_query -- determine information about a pcmcia device
+ */
+static int pcmcia_device_query(struct pcmcia_device *p_dev)
+{
+	cistpl_manfid_t manf_id;
+	cistpl_funcid_t func_id;
+	cistpl_vers_1_t	vers1;
+	unsigned int i;
+
+	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
+			       CISTPL_MANFID, &manf_id)) {
+		p_dev->manf_id = manf_id.manf;
+		p_dev->card_id = manf_id.card;
+		p_dev->has_manf_id = 1;
+		p_dev->has_card_id = 1;
+	}
+
+	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
+			       CISTPL_FUNCID, &func_id)) {
+		p_dev->func_id = func_id.func;
+		p_dev->has_func_id = 1;
+	} else {
+		/* rule of thumb: cards with no FUNCID, but with
+		 * common memory device geometry information, are
+		 * probably memory cards (from pcmcia-cs) */
+		cistpl_device_geo_t devgeo;
+		if (!pccard_read_tuple(p_dev->socket, p_dev->func,
+				      CISTPL_DEVICE_GEO, &devgeo)) {
+			ds_dbg(0, "mem device geometry probably means "
+			       "FUNCID_MEMORY\n");
+			p_dev->func_id = CISTPL_FUNCID_MEMORY;
+			p_dev->has_func_id = 1;
+		}
+	}
+
+	if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_VERS_1,
+			       &vers1)) {
+		for (i=0; i < vers1.ns; i++) {
+			char *tmp;
+			unsigned int length;
+
+			tmp = vers1.str + vers1.ofs[i];
+
+			length = strlen(tmp) + 1;
+			if ((length < 3) || (length > 255))
+				continue;
+
+			p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
+						    GFP_KERNEL);
+			if (!p_dev->prod_id[i])
+				continue;
+
+			p_dev->prod_id[i] = strncpy(p_dev->prod_id[i],
+						    tmp, length);
+		}
+	}
+
+	return 0;
+}
+
+
+/* device_add_lock is needed to avoid double registration by cardmgr and kernel.
+ * Serializes pcmcia_device_add; will most likely be removed in future.
+ *
+ * While it has the caveat that adding new PCMCIA devices inside(!) device_register()
+ * won't work, this doesn't matter much at the moment: the driver core doesn't
+ * support it either.
+ */
+static DECLARE_MUTEX(device_add_lock);
+
+static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function)
+{
+	struct pcmcia_device *p_dev;
+	unsigned long flags;
+
+	s = pcmcia_get_bus_socket(s);
+	if (!s)
+		return NULL;
+
+	down(&device_add_lock);
+
+	p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
+	if (!p_dev)
+		goto err_put;
+	memset(p_dev, 0, sizeof(struct pcmcia_device));
+
+	p_dev->socket = s->parent;
+	p_dev->device_no = (s->device_count++);
+	p_dev->func   = function;
+
+	p_dev->dev.bus = &pcmcia_bus_type;
+	p_dev->dev.parent = s->parent->dev.dev;
+	p_dev->dev.release = pcmcia_release_dev;
+	sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no);
+
+	/* compat */
+	p_dev->client.client_magic = CLIENT_MAGIC;
+	p_dev->client.Socket = s->parent;
+	p_dev->client.Function = function;
+	p_dev->client.state = CLIENT_UNBOUND;
+
+	/* Add to the list in pcmcia_bus_socket */
+	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	list_add_tail(&p_dev->socket_device_list, &s->devices_list);
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+	if (device_register(&p_dev->dev)) {
+		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		list_del(&p_dev->socket_device_list);
+		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+		goto err_free;
+       }
+
+	up(&device_add_lock);
+
+	return p_dev;
+
+ err_free:
+	kfree(p_dev);
+	s->device_count--;
+ err_put:
+	up(&device_add_lock);
+	pcmcia_put_bus_socket(s);
+
+	return NULL;
+}
+
+
+static int pcmcia_card_add(struct pcmcia_socket *s)
+{
+	cisinfo_t cisinfo;
+	cistpl_longlink_mfc_t mfc;
+	unsigned int no_funcs, i;
+	int ret = 0;
+
+	if (!(s->resource_setup_done))
+		return -EAGAIN; /* try again, but later... */
+
+	pcmcia_validate_mem(s);
+	ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo);
+	if (ret || !cisinfo.Chains) {
+		ds_dbg(0, "invalid CIS or invalid resources\n");
+		return -ENODEV;
+	}
+
+	if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
+		no_funcs = mfc.nfn;
+	else
+		no_funcs = 1;
+
+	/* this doesn't handle multifunction devices on one pcmcia function
+	 * yet. */
+	for (i=0; i < no_funcs; i++)
+		pcmcia_device_add(s->pcmcia, i);
+
+	return (ret);
+}
+
+
+static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) {
+	struct pcmcia_device * p_dev = to_pcmcia_dev(dev);
+	struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
+
+	/* matching by cardmgr */
+	if (p_dev->cardmgr == p_drv)
+		return 1;
+
+	return 0;
+}
+
+/************************ per-device sysfs output ***************************/
+
+#define pcmcia_device_attr(field, test, format)				\
+static ssize_t field##_show (struct device *dev, char *buf)		\
+{									\
+	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
+	return p_dev->test ? sprintf (buf, format, p_dev->field) : -ENODEV; \
+}
+
+#define pcmcia_device_stringattr(name, field)					\
+static ssize_t name##_show (struct device *dev, char *buf)		\
+{									\
+	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);		\
+	return p_dev->field ? sprintf (buf, "%s\n", p_dev->field) : -ENODEV; \
+}
+
+pcmcia_device_attr(func, socket, "0x%02x\n");
+pcmcia_device_attr(func_id, has_func_id, "0x%02x\n");
+pcmcia_device_attr(manf_id, has_manf_id, "0x%04x\n");
+pcmcia_device_attr(card_id, has_card_id, "0x%04x\n");
+pcmcia_device_stringattr(prod_id1, prod_id[0]);
+pcmcia_device_stringattr(prod_id2, prod_id[1]);
+pcmcia_device_stringattr(prod_id3, prod_id[2]);
+pcmcia_device_stringattr(prod_id4, prod_id[3]);
+
+static struct device_attribute pcmcia_dev_attrs[] = {
+	__ATTR(function, 0444, func_show, NULL),
+	__ATTR_RO(func_id),
+	__ATTR_RO(manf_id),
+	__ATTR_RO(card_id),
+	__ATTR_RO(prod_id1),
+	__ATTR_RO(prod_id2),
+	__ATTR_RO(prod_id3),
+	__ATTR_RO(prod_id4),
+	__ATTR_NULL,
+};
+
+
+/*======================================================================
+
+    These manage a ring buffer of events pending for one user process
+    
+======================================================================*/
+
+static int queue_empty(user_info_t *user)
+{
+    return (user->event_head == user->event_tail);
+}
+
+static event_t get_queued_event(user_info_t *user)
+{
+    user->event_tail = (user->event_tail+1) % MAX_EVENTS;
+    return user->event[user->event_tail];
+}
+
+static void queue_event(user_info_t *user, event_t event)
+{
+    user->event_head = (user->event_head+1) % MAX_EVENTS;
+    if (user->event_head == user->event_tail)
+	user->event_tail = (user->event_tail+1) % MAX_EVENTS;
+    user->event[user->event_head] = event;
+}
+
+static void handle_event(struct pcmcia_bus_socket *s, event_t event)
+{
+    user_info_t *user;
+    for (user = s->user; user; user = user->next)
+	queue_event(user, event);
+    wake_up_interruptible(&s->queue);
+}
+
+
+/*======================================================================
+
+    The card status event handler.
+    
+======================================================================*/
+
+struct send_event_data {
+	struct pcmcia_socket *skt;
+	event_t event;
+	int priority;
+};
+
+static int send_event_callback(struct device *dev, void * _data)
+{
+	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+	struct send_event_data *data = _data;
+
+	/* we get called for all sockets, but may only pass the event
+	 * for drivers _on the affected socket_ */
+	if (p_dev->socket != data->skt)
+		return 0;
+
+	if (p_dev->client.state & (CLIENT_UNBOUND|CLIENT_STALE))
+		return 0;
+
+	if (p_dev->client.EventMask & data->event)
+		return EVENT(&p_dev->client, data->event, data->priority);
+
+	return 0;
+}
+
+static int send_event(struct pcmcia_socket *s, event_t event, int priority)
+{
+	int ret = 0;
+	struct send_event_data private;
+	struct pcmcia_bus_socket *skt = pcmcia_get_bus_socket(s->pcmcia);
+
+	if (!skt)
+		return 0;
+
+	private.skt = s;
+	private.event = event;
+	private.priority = priority;
+
+	ret = bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback);
+
+	pcmcia_put_bus_socket(skt);
+	return ret;
+} /* send_event */
+
+
+/* Normally, the event is passed to individual drivers after
+ * informing userspace. Only for CS_EVENT_CARD_REMOVAL this
+ * is inversed to maintain historic compatibility.
+ */
+
+static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
+{
+	struct pcmcia_bus_socket *s = skt->pcmcia;
+	int ret = 0;
+
+	ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
+	       event, priority, s);
+    
+	switch (event) {
+
+	case CS_EVENT_CARD_REMOVAL:
+		s->state &= ~DS_SOCKET_PRESENT;
+	    	send_event(skt, event, priority);
+		unbind_request(s);
+		handle_event(s, event);
+		break;
+	
+	case CS_EVENT_CARD_INSERTION:
+		s->state |= DS_SOCKET_PRESENT;
+		pcmcia_card_add(skt);
+		handle_event(s, event);
+		break;
+
+	case CS_EVENT_EJECTION_REQUEST:
+		ret = send_event(skt, event, priority);
+		break;
+
+	default:
+		handle_event(s, event);
+		send_event(skt, event, priority);
+		break;
+    }
+
+    return 0;
+} /* ds_event */
+
+
+/*======================================================================
+
+    bind_request() and bind_device() are merged by now. Register_client()
+    is called right at the end of bind_request(), during the driver's
+    ->attach() call. Individual descriptions:
+
+    bind_request() connects a socket to a particular client driver.
+    It looks up the specified device ID in the list of registered
+    drivers, binds it to the socket, and tries to create an instance
+    of the device.  unbind_request() deletes a driver instance.
+    
+    Bind_device() associates a device driver with a particular socket.
+    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.
+
+    Register_client() uses the dev_info_t handle to match the
+    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.
+
+======================================================================*/
+
+static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
+{
+	struct pcmcia_driver *p_drv;
+	struct pcmcia_device *p_dev;
+	int ret = 0;
+	unsigned long flags;
+
+	s = pcmcia_get_bus_socket(s);
+	if (!s)
+		return -EINVAL;
+
+	ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->sock,
+	       (char *)bind_info->dev_info);
+
+	p_drv = get_pcmcia_driver(&bind_info->dev_info);
+	if (!p_drv) {
+		ret = -EINVAL;
+		goto err_put;
+	}
+
+	if (!try_module_get(p_drv->owner)) {
+		ret = -EINVAL;
+		goto err_put_driver;
+	}
+
+	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+        list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
+		if (p_dev->func == bind_info->function) {
+			if ((p_dev->dev.driver == &p_drv->drv)) {
+				if (p_dev->cardmgr) {
+					/* if there's already a device
+					 * registered, and it was registered
+					 * by userspace before, we need to
+					 * return the "instance". */
+					spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+					bind_info->instance = p_dev->instance;
+					ret = -EBUSY;
+					goto err_put_module;
+				} else {
+					/* the correct driver managed to bind
+					 * itself magically to the correct
+					 * device. */
+					spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+					p_dev->cardmgr = p_drv;
+					ret = 0;
+					goto err_put_module;
+				}
+			} else if (!p_dev->dev.driver) {
+				/* there's already a device available where
+				 * no device has been bound to yet. So we don't
+				 * need to register a device! */
+				spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+				goto rescan;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+	p_dev = pcmcia_device_add(s, bind_info->function);
+	if (!p_dev) {
+		ret = -EIO;
+		goto err_put_module;
+	}
+
+rescan:
+	p_dev->cardmgr = p_drv;
+
+	pcmcia_device_query(p_dev);
+
+	/*
+	 * Prevent this racing with a card insertion.
+	 */
+	down(&s->parent->skt_sem);
+	bus_rescan_devices(&pcmcia_bus_type);
+	up(&s->parent->skt_sem);
+
+	/* check whether the driver indeed matched. I don't care if this
+	 * is racy or not, because it can only happen on cardmgr access
+	 * paths...
+	 */
+	if (!(p_dev->dev.driver == &p_drv->drv))
+		p_dev->cardmgr = NULL;
+
+ err_put_module:
+	module_put(p_drv->owner);
+ err_put_driver:
+	put_driver(&p_drv->drv);
+ err_put:
+	pcmcia_put_bus_socket(s);
+
+	return (ret);
+} /* bind_request */
+
+
+int pcmcia_register_client(client_handle_t *handle, client_reg_t *req)
+{
+	client_t *client = NULL;
+	struct pcmcia_socket *s;
+	struct pcmcia_bus_socket *skt = NULL;
+	struct pcmcia_device *p_dev = NULL;
+
+	/* Look for unbound client with matching dev_info */
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
+		unsigned long flags;
+
+		if (s->state & SOCKET_CARDBUS)
+			continue;
+
+		skt = s->pcmcia;
+		if (!skt)
+			continue;
+		skt = pcmcia_get_bus_socket(skt);
+		if (!skt)
+			continue;
+		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		list_for_each_entry(p_dev, &skt->devices_list, socket_device_list) {
+			struct pcmcia_driver *p_drv;
+			p_dev = pcmcia_get_dev(p_dev);
+			if (!p_dev)
+				continue;
+			if (!(p_dev->client.state & CLIENT_UNBOUND) ||
+			    (!p_dev->dev.driver)) {
+				pcmcia_put_dev(p_dev);
+				continue;
+			}
+			p_drv = to_pcmcia_drv(p_dev->dev.driver);
+			if (!strncmp(p_drv->drv.name, (char *)req->dev_info, DEV_NAME_LEN)) {
+				client = &p_dev->client;
+				spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+				goto found;
+			}
+			pcmcia_put_dev(p_dev);
+		}
+		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+		pcmcia_put_bus_socket(skt);
+	}
+ found:
+	up_read(&pcmcia_socket_list_rwsem);
+	if (!p_dev || !client)
+		return -ENODEV;
+
+	pcmcia_put_bus_socket(skt); /* safe, as we already hold a reference from bind_device */
+
+	*handle = client;
+	client->state &= ~CLIENT_UNBOUND;
+	client->Socket = s;
+	client->EventMask = req->EventMask;
+	client->event_handler = req->event_handler;
+	client->event_callback_args = req->event_callback_args;
+	client->event_callback_args.client_handle = client;
+
+	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;
+		if (pccard_read_tuple(s, client->Function, CISTPL_LONGLINK_MFC, &mfc)
+		    == CS_SUCCESS)
+			s->functions = mfc.nfn;
+		else
+			s->functions = 1;
+		s->config = kmalloc(sizeof(config_t) * s->functions,
+				    GFP_KERNEL);
+		if (!s->config)
+			goto out_no_resource;
+		memset(s->config, 0, sizeof(config_t) * s->functions);
+	}
+
+	ds_dbg(1, "register_client(): client 0x%p, dev %s\n",
+	       client, p_dev->dev.bus_id);
+	if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE)
+		EVENT(client, CS_EVENT_REGISTRATION_COMPLETE, CS_EVENT_PRI_LOW);
+
+	if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT) {
+		if (client->EventMask & CS_EVENT_CARD_INSERTION)
+			EVENT(client, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+	}
+
+	return CS_SUCCESS;
+
+ out_no_resource:
+	pcmcia_put_dev(p_dev);
+	return CS_OUT_OF_RESOURCE;
+} /* register_client */
+EXPORT_SYMBOL(pcmcia_register_client);
+
+
+/*====================================================================*/
+
+extern struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s);
+
+static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first)
+{
+	dev_node_t *node;
+	struct pcmcia_device *p_dev;
+	unsigned long flags;
+	int ret = 0;
+
+#ifdef CONFIG_CARDBUS
+	/*
+	 * Some unbelievably ugly code to associate the PCI cardbus
+	 * device and its driver with the PCMCIA "bind" information.
+	 */
+	{
+		struct pci_bus *bus;
+
+		bus = pcmcia_lookup_bus(s->parent);
+		if (bus) {
+			struct list_head *list;
+			struct pci_dev *dev = NULL;
+
+			list = bus->devices.next;
+			while (list != &bus->devices) {
+				struct pci_dev *pdev = pci_dev_b(list);
+				list = list->next;
+
+				if (first) {
+					dev = pdev;
+					break;
+				}
+
+				/* Try to handle "next" here some way? */
+			}
+			if (dev && dev->driver) {
+				strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
+				bind_info->major = 0;
+				bind_info->minor = 0;
+				bind_info->next = NULL;
+				return 0;
+			}
+		}
+	}
+#endif
+
+	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
+		if (p_dev->func == bind_info->function) {
+			p_dev = pcmcia_get_dev(p_dev);
+			if (!p_dev)
+				continue;
+			goto found;
+		}
+	}
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	return -ENODEV;
+
+ found:
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+	if ((!p_dev->instance) ||
+	    (p_dev->instance->state & DEV_CONFIG_PENDING)) {
+		ret = -EAGAIN;
+		goto err_put;
+	}
+
+	if (first)
+		node = p_dev->instance->dev;
+	else
+		for (node = p_dev->instance->dev; node; node = node->next)
+			if (node == bind_info->next)
+				break;
+	if (!node) {
+		ret = -ENODEV;
+		goto err_put;
+	}
+
+	strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
+	bind_info->major = node->major;
+	bind_info->minor = node->minor;
+	bind_info->next = node->next;
+
+ err_put:
+	pcmcia_put_dev(p_dev);
+	return (ret);
+} /* get_device_info */
+
+/*====================================================================*/
+
+/* unbind _all_ devices attached to a given pcmcia_bus_socket. The
+ * drivers have been called with EVENT_CARD_REMOVAL before.
+ */
+static int unbind_request(struct pcmcia_bus_socket *s)
+{
+	struct pcmcia_device	*p_dev;
+	unsigned long		flags;
+
+	ds_dbg(2, "unbind_request(%d)\n", s->parent->sock);
+
+	s->device_count = 0;
+
+	for (;;) {
+		/* unregister all pcmcia_devices registered with this socket*/
+		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		if (list_empty(&s->devices_list)) {
+			spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ 			return 0;
+		}
+		p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
+		list_del(&p_dev->socket_device_list);
+		p_dev->client.state |= CLIENT_STALE;
+		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+		device_unregister(&p_dev->dev);
+	}
+
+	return 0;
+} /* unbind_request */
+
+int pcmcia_deregister_client(client_handle_t handle)
+{
+	struct pcmcia_socket *s;
+	int i;
+	struct pcmcia_device *p_dev = handle_to_pdev(handle);
+
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+
+	s = SOCKET(handle);
+	ds_dbg(1, "deregister_client(%p)\n", handle);
+
+	if (handle->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED))
+		goto warn_out;
+	for (i = 0; i < MAX_WIN; i++)
+		if (handle->state & CLIENT_WIN_REQ(i))
+			goto warn_out;
+
+	if (handle->state & CLIENT_STALE) {
+		handle->client_magic = 0;
+		handle->state &= ~CLIENT_STALE;
+		pcmcia_put_dev(p_dev);
+	} else {
+		handle->state = CLIENT_UNBOUND;
+		handle->event_handler = NULL;
+	}
+
+	return CS_SUCCESS;
+ warn_out:
+	printk(KERN_WARNING "ds: deregister_client was called too early.\n");
+	return CS_IN_USE;
+} /* deregister_client */
+EXPORT_SYMBOL(pcmcia_deregister_client);
+
+
+/*======================================================================
+
+    The user-mode PC Card device interface
+
+======================================================================*/
+
+static int ds_open(struct inode *inode, struct file *file)
+{
+    socket_t i = iminor(inode);
+    struct pcmcia_bus_socket *s;
+    user_info_t *user;
+
+    ds_dbg(0, "ds_open(socket %d)\n", i);
+
+    s = get_socket_info_by_nr(i);
+    if (!s)
+	    return -ENODEV;
+    s = pcmcia_get_bus_socket(s);
+    if (!s)
+	    return -ENODEV;
+
+    if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+	    if (s->state & DS_SOCKET_BUSY) {
+		    pcmcia_put_bus_socket(s);
+		    return -EBUSY;
+	    }
+	else
+	    s->state |= DS_SOCKET_BUSY;
+    }
+    
+    user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
+    if (!user) {
+	    pcmcia_put_bus_socket(s);
+	    return -ENOMEM;
+    }
+    user->event_tail = user->event_head = 0;
+    user->next = s->user;
+    user->user_magic = USER_MAGIC;
+    user->socket = s;
+    s->user = user;
+    file->private_data = user;
+    
+    if (s->state & DS_SOCKET_PRESENT)
+	queue_event(user, CS_EVENT_CARD_INSERTION);
+    return 0;
+} /* ds_open */
+
+/*====================================================================*/
+
+static int ds_release(struct inode *inode, struct file *file)
+{
+    struct pcmcia_bus_socket *s;
+    user_info_t *user, **link;
+
+    ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
+
+    user = file->private_data;
+    if (CHECK_USER(user))
+	goto out;
+
+    s = user->socket;
+
+    /* Unlink user data structure */
+    if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+	s->state &= ~DS_SOCKET_BUSY;
+    }
+    file->private_data = NULL;
+    for (link = &s->user; *link; link = &(*link)->next)
+	if (*link == user) break;
+    if (link == NULL)
+	goto out;
+    *link = user->next;
+    user->user_magic = 0;
+    kfree(user);
+    pcmcia_put_bus_socket(s);
+out:
+    return 0;
+} /* ds_release */
+
+/*====================================================================*/
+
+static ssize_t ds_read(struct file *file, char __user *buf,
+		       size_t count, loff_t *ppos)
+{
+    struct pcmcia_bus_socket *s;
+    user_info_t *user;
+    int ret;
+
+    ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode));
+    
+    if (count < 4)
+	return -EINVAL;
+
+    user = file->private_data;
+    if (CHECK_USER(user))
+	return -EIO;
+    
+    s = user->socket;
+    if (s->state & DS_SOCKET_DEAD)
+        return -EIO;
+
+    ret = wait_event_interruptible(s->queue, !queue_empty(user));
+    if (ret == 0)
+	ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
+
+    return ret;
+} /* ds_read */
+
+/*====================================================================*/
+
+static ssize_t ds_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+    ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode));
+
+    if (count != 4)
+	return -EINVAL;
+    if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+	return -EBADF;
+
+    return -EIO;
+} /* ds_write */
+
+/*====================================================================*/
+
+/* No kernel lock - fine */
+static u_int ds_poll(struct file *file, poll_table *wait)
+{
+    struct pcmcia_bus_socket *s;
+    user_info_t *user;
+
+    ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode));
+    
+    user = file->private_data;
+    if (CHECK_USER(user))
+	return POLLERR;
+    s = user->socket;
+    /*
+     * We don't check for a dead socket here since that
+     * will send cardmgr into an endless spin.
+     */
+    poll_wait(file, &s->queue, wait);
+    if (!queue_empty(user))
+	return POLLIN | POLLRDNORM;
+    return 0;
+} /* ds_poll */
+
+/*====================================================================*/
+
+extern int pcmcia_adjust_resource_info(adjust_t *adj);
+
+static int ds_ioctl(struct inode * inode, struct file * file,
+		    u_int cmd, u_long arg)
+{
+    struct pcmcia_bus_socket *s;
+    void __user *uarg = (char __user *)arg;
+    u_int size;
+    int ret, err;
+    ds_ioctl_arg_t *buf;
+    user_info_t *user;
+
+    ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
+    
+    user = file->private_data;
+    if (CHECK_USER(user))
+	return -EIO;
+
+    s = user->socket;
+    if (s->state & DS_SOCKET_DEAD)
+        return -EIO;
+    
+    size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
+    if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
+
+    /* Permission check */
+    if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
+	return -EPERM;
+	
+    if (cmd & IOC_IN) {
+	if (!access_ok(VERIFY_READ, uarg, size)) {
+	    ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
+	    return -EFAULT;
+	}
+    }
+    if (cmd & IOC_OUT) {
+	if (!access_ok(VERIFY_WRITE, uarg, size)) {
+	    ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
+	    return -EFAULT;
+	}
+    }
+    buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
+    if (!buf)
+	return -ENOMEM;
+    
+    err = ret = 0;
+    
+    if (cmd & IOC_IN) __copy_from_user((char *)buf, uarg, size);
+    
+    switch (cmd) {
+    case DS_ADJUST_RESOURCE_INFO:
+	ret = pcmcia_adjust_resource_info(&buf->adjust);
+	break;
+    case DS_GET_CARD_SERVICES_INFO:
+	ret = pcmcia_get_card_services_info(&buf->servinfo);
+	break;
+    case DS_GET_CONFIGURATION_INFO:
+	if (buf->config.Function &&
+	   (buf->config.Function >= s->parent->functions))
+	    ret = CS_BAD_ARGS;
+	else
+	    ret = pccard_get_configuration_info(s->parent,
+			buf->config.Function, &buf->config);
+	break;
+    case DS_GET_FIRST_TUPLE:
+	down(&s->parent->skt_sem);
+	pcmcia_validate_mem(s->parent);
+	up(&s->parent->skt_sem);
+	ret = pccard_get_first_tuple(s->parent, BIND_FN_ALL, &buf->tuple);
+	break;
+    case DS_GET_NEXT_TUPLE:
+	ret = pccard_get_next_tuple(s->parent, BIND_FN_ALL, &buf->tuple);
+	break;
+    case DS_GET_TUPLE_DATA:
+	buf->tuple.TupleData = buf->tuple_parse.data;
+	buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
+	ret = pccard_get_tuple_data(s->parent, &buf->tuple);
+	break;
+    case DS_PARSE_TUPLE:
+	buf->tuple.TupleData = buf->tuple_parse.data;
+	ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
+	break;
+    case DS_RESET_CARD:
+	ret = pccard_reset_card(s->parent);
+	break;
+    case DS_GET_STATUS:
+	if (buf->status.Function &&
+	   (buf->status.Function >= s->parent->functions))
+	    ret = CS_BAD_ARGS;
+	else
+	ret = pccard_get_status(s->parent, buf->status.Function, &buf->status);
+	break;
+    case DS_VALIDATE_CIS:
+	down(&s->parent->skt_sem);
+	pcmcia_validate_mem(s->parent);
+	up(&s->parent->skt_sem);
+	ret = pccard_validate_cis(s->parent, BIND_FN_ALL, &buf->cisinfo);
+	break;
+    case DS_SUSPEND_CARD:
+	ret = pcmcia_suspend_card(s->parent);
+	break;
+    case DS_RESUME_CARD:
+	ret = pcmcia_resume_card(s->parent);
+	break;
+    case DS_EJECT_CARD:
+	err = pcmcia_eject_card(s->parent);
+	break;
+    case DS_INSERT_CARD:
+	err = pcmcia_insert_card(s->parent);
+	break;
+    case DS_ACCESS_CONFIGURATION_REGISTER:
+	if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
+	    err = -EPERM;
+	    goto free_out;
+	}
+	if (buf->conf_reg.Function &&
+	   (buf->conf_reg.Function >= s->parent->functions))
+	    ret = CS_BAD_ARGS;
+	else
+	    ret = pccard_access_configuration_register(s->parent,
+			buf->conf_reg.Function, &buf->conf_reg);
+	break;
+    case DS_GET_FIRST_REGION:
+    case DS_GET_NEXT_REGION:
+    case DS_BIND_MTD:
+	if (!capable(CAP_SYS_ADMIN)) {
+		err = -EPERM;
+		goto free_out;
+	} else {
+		static int printed = 0;
+		if (!printed) {
+			printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
+			printk(KERN_WARNING "MTD handling any more.\n");
+			printed++;
+		}
+	}
+	err = -EINVAL;
+	goto free_out;
+	break;
+    case DS_GET_FIRST_WINDOW:
+	ret = pcmcia_get_window(s->parent, &buf->win_info.handle, 0,
+			&buf->win_info.window);
+	break;
+    case DS_GET_NEXT_WINDOW:
+	ret = pcmcia_get_window(s->parent, &buf->win_info.handle,
+			buf->win_info.handle->index + 1, &buf->win_info.window);
+	break;
+    case DS_GET_MEM_PAGE:
+	ret = pcmcia_get_mem_page(buf->win_info.handle,
+			   &buf->win_info.map);
+	break;
+    case DS_REPLACE_CIS:
+	ret = pcmcia_replace_cis(s->parent, &buf->cisdump);
+	break;
+    case DS_BIND_REQUEST:
+	if (!capable(CAP_SYS_ADMIN)) {
+		err = -EPERM;
+		goto free_out;
+	}
+	err = bind_request(s, &buf->bind_info);
+	break;
+    case DS_GET_DEVICE_INFO:
+	err = get_device_info(s, &buf->bind_info, 1);
+	break;
+    case DS_GET_NEXT_DEVICE:
+	err = get_device_info(s, &buf->bind_info, 0);
+	break;
+    case DS_UNBIND_REQUEST:
+	err = 0;
+	break;
+    default:
+	err = -EINVAL;
+    }
+    
+    if ((err == 0) && (ret != CS_SUCCESS)) {
+	ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
+	switch (ret) {
+	case CS_BAD_SOCKET: case CS_NO_CARD:
+	    err = -ENODEV; break;
+	case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
+	case CS_BAD_TUPLE:
+	    err = -EINVAL; break;
+	case CS_IN_USE:
+	    err = -EBUSY; break;
+	case CS_OUT_OF_RESOURCE:
+	    err = -ENOSPC; break;
+	case CS_NO_MORE_ITEMS:
+	    err = -ENODATA; break;
+	case CS_UNSUPPORTED_FUNCTION:
+	    err = -ENOSYS; break;
+	default:
+	    err = -EIO; break;
+	}
+    }
+
+    if (cmd & IOC_OUT) {
+        if (__copy_to_user(uarg, (char *)buf, size))
+            err = -EFAULT;
+    }
+
+free_out:
+    kfree(buf);
+    return err;
+} /* ds_ioctl */
+
+/*====================================================================*/
+
+static struct file_operations ds_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ds_open,
+	.release	= ds_release,
+	.ioctl		= ds_ioctl,
+	.read		= ds_read,
+	.write		= ds_write,
+	.poll		= ds_poll,
+};
+
+static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev)
+{
+	struct pcmcia_socket *socket = class_get_devdata(class_dev);
+	struct pcmcia_bus_socket *s;
+	int ret;
+
+	s = kmalloc(sizeof(struct pcmcia_bus_socket), GFP_KERNEL);
+	if(!s)
+		return -ENOMEM;
+	memset(s, 0, sizeof(struct pcmcia_bus_socket));
+
+	/* get reference to parent socket */
+	s->parent = pcmcia_get_socket(socket);
+	if (!s->parent) {
+		printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket);
+		kfree (s);
+		return -ENODEV;
+	}
+
+	kref_init(&s->refcount);
+    
+	/*
+	 * Ugly. But we want to wait for the socket threads to have started up.
+	 * We really should let the drivers themselves drive some of this..
+	 */
+	msleep(250);
+
+	init_waitqueue_head(&s->queue);
+	INIT_LIST_HEAD(&s->devices_list);
+
+	/* Set up hotline to Card Services */
+	s->callback.owner = THIS_MODULE;
+	s->callback.event = &ds_event;
+	s->callback.resources_done = &pcmcia_card_add;
+	socket->pcmcia = s;
+
+	ret = pccard_register_pcmcia(socket, &s->callback);
+	if (ret) {
+		printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket);
+		pcmcia_put_bus_socket(s);
+		socket->pcmcia = NULL;
+		return (ret);
+	}
+
+	return 0;
+}
+
+
+static void pcmcia_bus_remove_socket(struct class_device *class_dev)
+{
+	struct pcmcia_socket *socket = class_get_devdata(class_dev);
+
+	if (!socket || !socket->pcmcia)
+		return;
+
+	pccard_register_pcmcia(socket, NULL);
+
+	socket->pcmcia->state |= DS_SOCKET_DEAD;
+	pcmcia_put_bus_socket(socket->pcmcia);
+	socket->pcmcia = NULL;
+
+	return;
+}
+
+
+/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
+static struct class_interface pcmcia_bus_interface = {
+	.class = &pcmcia_socket_class,
+	.add = &pcmcia_bus_add_socket,
+	.remove = &pcmcia_bus_remove_socket,
+};
+
+
+struct bus_type pcmcia_bus_type = {
+	.name = "pcmcia",
+	.match = pcmcia_bus_match,
+	.dev_attrs = pcmcia_dev_attrs,
+};
+EXPORT_SYMBOL(pcmcia_bus_type);
+
+
+static int __init init_pcmcia_bus(void)
+{
+	int i;
+
+	spin_lock_init(&pcmcia_dev_list_lock);
+
+	bus_register(&pcmcia_bus_type);
+	class_interface_register(&pcmcia_bus_interface);
+
+	/* Set up character device for user mode clients */
+	i = register_chrdev(0, "pcmcia", &ds_fops);
+	if (i == -EBUSY)
+		printk(KERN_NOTICE "unable to find a free device # for "
+		       "Driver Services\n");
+	else
+		major_dev = i;
+
+#ifdef CONFIG_PROC_FS
+	proc_pccard = proc_mkdir("pccard", proc_bus);
+	if (proc_pccard)
+		create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
+#endif
+
+	return 0;
+}
+fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that 
+			       * pcmcia_socket_class is already registered */
+
+
+static void __exit exit_pcmcia_bus(void)
+{
+	class_interface_unregister(&pcmcia_bus_interface);
+
+#ifdef CONFIG_PROC_FS
+	if (proc_pccard) {
+		remove_proc_entry("drivers", proc_pccard);
+		remove_proc_entry("pccard", proc_bus);
+	}
+#endif
+	if (major_dev != -1)
+		unregister_chrdev(major_dev, "pcmcia");
+
+	bus_unregister(&pcmcia_bus_type);
+}
+module_exit(exit_pcmcia_bus);
+
+
+
+/* helpers for backwards-compatible functions */
+
+static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr)
+{
+	struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr);
+	if (s && s->pcmcia)
+		return s->pcmcia;
+	else
+		return NULL;
+}
+
+/* backwards-compatible accessing of driver --- by name! */
+
+static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info)
+{
+	struct device_driver *drv;
+	struct pcmcia_driver *p_drv;
+
+	drv = driver_find((char *) dev_info, &pcmcia_bus_type);
+	if (!drv)
+		return NULL;
+
+	p_drv = container_of(drv, struct pcmcia_driver, drv);
+
+	return (p_drv);
+}
+
+MODULE_ALIAS("ds");
diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c
new file mode 100644
index 0000000..5e6c4ba
--- /dev/null
+++ b/drivers/pcmcia/hd64465_ss.c
@@ -0,0 +1,968 @@
+/*
+ * $Id: hd64465_ss.c,v 1.7 2003/07/06 14:42:50 lethal Exp $
+ *
+ * Device driver for the PCMCIA controller module of the
+ * Hitachi HD64465 handheld companion chip.
+ *
+ * Note that the HD64465 provides a very thin PCMCIA host bridge
+ * layer, requiring a lot of the work of supporting cards to be
+ * performed by the processor.  For example: mapping of card
+ * interrupts to processor IRQs is done by IRQ demuxing software;
+ * IO and memory mappings are fixed; setting voltages according
+ * to card Voltage Select pins etc is done in software.
+ *
+ * Note also that this driver uses only the simple, fixed,
+ * 16MB, 16-bit wide mappings to PCMCIA spaces defined by the
+ * HD64465.  Larger mappings, smaller mappings, or mappings of
+ * different width to the same socket, are all possible only by
+ * involving the SH7750's MMU, which is considered unnecessary here.
+ * The downside is that it may be possible for some drivers to
+ * break because they need or expect 8-bit mappings.
+ *
+ * This driver currently supports only the following configuration:
+ * SH7750 CPU, HD64465, TPS2206 voltage control chip.
+ *
+ * by Greg Banks <gbanks@pocketpenguins.com>
+ * (c) 2000 PocketPenguins Inc
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <asm/errno.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/hd64465/hd64465.h>
+#include <asm/hd64465/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include "cs_internal.h"
+
+#define MODNAME "hd64465_ss"
+
+/* #define HD64465_DEBUG 1 */
+
+#if HD64465_DEBUG
+#define DPRINTK(args...)	printk(MODNAME ": " args)
+#else
+#define DPRINTK(args...)
+#endif
+
+extern int hd64465_io_debug;
+extern void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
+extern void p3_iounmap(void *addr);
+
+/*============================================================*/
+
+#define HS_IO_MAP_SIZE 	(64*1024)
+
+typedef struct hs_socket_t
+{
+    unsigned int	number;
+    u_int   	    	irq;
+    u_long  	    	mem_base;
+    void		*io_base;
+    u_long  	    	mem_length;
+    u_int   	    	ctrl_base;
+    socket_state_t  	state;
+    pccard_io_map     	io_maps[MAX_IO_WIN];
+    pccard_mem_map  	mem_maps[MAX_WIN];
+    struct pcmcia_socket	socket;
+} hs_socket_t;
+
+
+
+#define HS_MAX_SOCKETS 2
+static hs_socket_t hs_sockets[HS_MAX_SOCKETS];
+
+#define hs_in(sp, r)	    inb((sp)->ctrl_base + (r))
+#define hs_out(sp, v, r)    outb(v, (sp)->ctrl_base + (r))
+
+
+/* translate a boolean value to a bit in a register */
+#define bool_to_regbit(sp, r, bi, bo)	    	\
+    do {    	    	    	    	    	\
+    	unsigned short v = hs_in(sp, r);    	\
+	if (bo)     	    	    	    	\
+	    v |= (bi);	    	    	    	\
+	else	    	    	    	    	\
+	    v &= ~(bi);     	    	    	\
+	hs_out(sp, v, r);   	    	    	\
+    } while(0)
+    
+/* register offsets from HD64465_REG_PCC[01]ISR */
+#define ISR 	0x0
+#define GCR 	0x2
+#define CSCR 	0x4
+#define CSCIER 	0x6
+#define SCR 	0x8
+
+
+/* Mask and values for CSCIER register */
+#define IER_MASK    0x80
+#define IER_ON	    0x3f    	/* interrupts on */
+#define IER_OFF     0x00    	/* interrupts off */
+
+/*============================================================*/
+
+#if HD64465_DEBUG > 10
+
+static void cis_hex_dump(const unsigned char *x, int len)
+{
+    	int i;
+	
+    	for (i=0 ; i<len ; i++)
+	{
+	    if (!(i & 0xf))
+	    	printk("\n%08x", (unsigned)(x + i));
+	    printk(" %02x", *(volatile unsigned short*)x);
+	    x += 2;
+	}
+	printk("\n");
+}
+
+#endif
+/*============================================================*/
+
+/*
+ * This code helps create the illusion that the IREQ line from
+ * the PC card is mapped to one of the CPU's IRQ lines by the
+ * host bridge hardware (which is how every host bridge *except*
+ * the HD64465 works).  In particular, it supports enabling
+ * and disabling the IREQ line by code which knows nothing
+ * about the host bridge (e.g. device drivers, IDE code) using
+ * the request_irq(), free_irq(), probe_irq_on() and probe_irq_off()
+ * functions.  Also, it supports sharing the mapped IRQ with
+ * real hardware IRQs from the -IRL0-3 lines.
+ */
+
+#define HS_NUM_MAPPED_IRQS  16	/* Limitation of the PCMCIA code */
+static struct
+{
+    /* index is mapped irq number */
+    hs_socket_t *sock;
+    hw_irq_controller *old_handler;
+} hs_mapped_irq[HS_NUM_MAPPED_IRQS];
+
+static void hs_socket_enable_ireq(hs_socket_t *sp)
+{
+    	unsigned short cscier;
+	
+    	DPRINTK("hs_socket_enable_ireq(sock=%d)\n", sp->number);
+
+    	cscier = hs_in(sp, CSCIER);
+	cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK;
+    	cscier |= HD64465_PCCCSCIER_PIREQE_LEVEL;
+	hs_out(sp, cscier, CSCIER);
+}
+
+static void hs_socket_disable_ireq(hs_socket_t *sp)
+{
+    	unsigned short cscier;
+	
+    	DPRINTK("hs_socket_disable_ireq(sock=%d)\n", sp->number);
+	
+    	cscier = hs_in(sp, CSCIER);
+	cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK;
+	hs_out(sp, cscier, CSCIER);
+}
+
+static unsigned int hs_startup_irq(unsigned int irq)
+{
+	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
+	hs_mapped_irq[irq].old_handler->startup(irq);
+	return 0;
+}
+
+static void hs_shutdown_irq(unsigned int irq)
+{
+	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
+	hs_mapped_irq[irq].old_handler->shutdown(irq);
+}
+
+static void hs_enable_irq(unsigned int irq)
+{
+	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
+	hs_mapped_irq[irq].old_handler->enable(irq);
+}
+
+static void hs_disable_irq(unsigned int irq)
+{
+	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
+	hs_mapped_irq[irq].old_handler->disable(irq);
+}
+
+extern struct hw_interrupt_type no_irq_type;
+
+static void hs_mask_and_ack_irq(unsigned int irq)
+{
+	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);
+	/* ack_none() spuriously complains about an unexpected IRQ */
+	if (hs_mapped_irq[irq].old_handler != &no_irq_type)
+	    hs_mapped_irq[irq].old_handler->ack(irq);
+}
+
+static void hs_end_irq(unsigned int irq)
+{
+	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);
+	hs_mapped_irq[irq].old_handler->end(irq);
+}
+
+
+static struct hw_interrupt_type hd64465_ss_irq_type = {
+	.typename	= "PCMCIA-IRQ",
+	.startup	= hs_startup_irq,
+	.shutdown	= hs_shutdown_irq,
+	.enable		= hs_enable_irq,
+	.disable	= hs_disable_irq,
+	.ack		= hs_mask_and_ack_irq,
+	.end		= hs_end_irq
+};
+
+/* 
+ * This function should only ever be called with interrupts disabled.
+ */
+static void hs_map_irq(hs_socket_t *sp, unsigned int irq)
+{
+    	DPRINTK("hs_map_irq(sock=%d irq=%d)\n", sp->number, irq);
+	
+	if (irq >= HS_NUM_MAPPED_IRQS)
+	    return;
+
+    	hs_mapped_irq[irq].sock = sp;
+	/* insert ourselves as the irq controller */
+	hs_mapped_irq[irq].old_handler = irq_desc[irq].handler;
+	irq_desc[irq].handler = &hd64465_ss_irq_type;
+}
+
+
+/* 
+ * This function should only ever be called with interrupts disabled.
+ */
+static void hs_unmap_irq(hs_socket_t *sp, unsigned int irq)
+{
+    	DPRINTK("hs_unmap_irq(sock=%d irq=%d)\n", sp->number, irq);
+	
+	if (irq >= HS_NUM_MAPPED_IRQS)
+	    return;
+		
+	/* restore the original irq controller */
+	irq_desc[irq].handler = hs_mapped_irq[irq].old_handler;
+}
+
+/*============================================================*/
+
+
+/*
+ * Set Vpp and Vcc (in tenths of a Volt).  Does not
+ * support the hi-Z state.
+ *
+ * Note, this assumes the board uses a TPS2206 chip to control
+ * the Vcc and Vpp voltages to the hs_sockets.  If your board
+ * uses the MIC2563 (also supported by the HD64465) then you
+ * will have to modify this function.
+ */
+    	    	    	    	         /* 0V   3.3V  5.5V */
+static const u_char hs_tps2206_avcc[3] = { 0x00, 0x04, 0x08 };
+static const u_char hs_tps2206_bvcc[3] = { 0x00, 0x80, 0x40 };
+
+static int hs_set_voltages(hs_socket_t *sp, int Vcc, int Vpp)
+{
+    	u_int psr;
+	u_int vcci = 0;
+	u_int sock = sp->number;
+	
+    	DPRINTK("hs_set_voltage(%d, %d, %d)\n", sock, Vcc, Vpp);
+
+    	switch (Vcc)
+	{
+	case 0:  vcci = 0; break;
+	case 33: vcci = 1; break;
+	case 50: vcci = 2; break;
+	default: return 0;
+	}
+
+    	/* Note: Vpp = 120 not supported -- Greg Banks */
+	if (Vpp != 0 && Vpp != Vcc)
+	    return 0;
+	
+	/* The PSR register holds 8 of the 9 bits which control
+	 * the TPS2206 via its serial interface.
+	 */
+	psr = inw(HD64465_REG_PCCPSR);
+	switch (sock)
+	{
+	case 0:
+	    psr &= 0x0f;
+	    psr |= hs_tps2206_avcc[vcci];
+	    psr |= (Vpp == 0 ? 0x00 : 0x02);
+	    break;
+	case 1:
+	    psr &= 0xf0;
+	    psr |= hs_tps2206_bvcc[vcci];
+	    psr |= (Vpp == 0 ? 0x00 : 0x20);
+	    break;
+	};
+	outw(psr, HD64465_REG_PCCPSR);
+	
+	return 1;
+}
+
+
+/*============================================================*/
+
+/*
+ * Drive the RESET line to the card.
+ */
+static void hs_reset_socket(hs_socket_t *sp, int on)
+{
+    	unsigned short v;
+	
+	v = hs_in(sp, GCR);
+	if (on)
+	    v |= HD64465_PCCGCR_PCCR;
+	else
+	    v &= ~HD64465_PCCGCR_PCCR;
+	hs_out(sp, v, GCR);
+}
+
+/*============================================================*/
+
+static int hs_init(struct pcmcia_socket *s)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+	
+    	DPRINTK("hs_init(%d)\n", sp->number);
+
+	return 0;
+}
+
+/*============================================================*/
+
+
+static int hs_get_status(struct pcmcia_socket *s, u_int *value)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+    	unsigned int isr;
+	u_int status = 0;
+	
+	
+	isr = hs_in(sp, ISR);
+
+    	/* Card is seated and powered when *both* CD pins are low */
+	if ((isr & HD64465_PCCISR_PCD_MASK) == 0)
+    	{
+	    status |= SS_DETECT;    /* card present */
+
+	    switch (isr & HD64465_PCCISR_PBVD_MASK)
+	    {
+	    case HD64465_PCCISR_PBVD_BATGOOD:   
+		break;
+	    case HD64465_PCCISR_PBVD_BATWARN:
+		status |= SS_BATWARN;
+		break;
+	    default:
+		status |= SS_BATDEAD;
+		break;
+	    }
+
+	    if (isr & HD64465_PCCISR_PREADY)
+		status |= SS_READY;
+
+	    if (isr & HD64465_PCCISR_PMWP)
+		status |= SS_WRPROT;
+		
+	    /* Voltage Select pins interpreted as per Table 4-5 of the std.
+	     * Assuming we have the TPS2206, the socket is a "Low Voltage
+	     * key, 3.3V and 5V available, no X.XV available".
+	     */
+	    switch (isr & (HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1))
+	    {
+	    case HD64465_PCCISR_PVS1:
+	    	printk(KERN_NOTICE MODNAME ": cannot handle X.XV card, ignored\n");
+		status = 0;
+	    	break;
+	    case 0:
+	    case HD64465_PCCISR_PVS2:
+    	    	/* 3.3V */
+    	    	status |= SS_3VCARD;
+	    	break;
+	    case HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1:
+	    	/* 5V */
+	    	break;
+	    }
+		
+	    /* TODO: SS_POWERON */
+	    /* TODO: SS_STSCHG */
+    	}	
+	
+    	DPRINTK("hs_get_status(%d) = %x\n", sock, status);
+	
+	*value = status;
+	return 0;
+}
+
+/*============================================================*/
+
+static int hs_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+
+    	DPRINTK("hs_get_socket(%d)\n", sock);
+	
+	*state = sp->state;
+	return 0;
+}
+
+/*============================================================*/
+
+static int hs_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+    	u_long flags;
+	u_int changed;
+	unsigned short cscier;
+
+    	DPRINTK("hs_set_socket(sock=%d, flags=%x, csc_mask=%x, Vcc=%d, Vpp=%d, io_irq=%d)\n",
+	    sock, state->flags, state->csc_mask, state->Vcc, state->Vpp, state->io_irq);
+	
+	local_irq_save(flags);	/* Don't want interrupts happening here */
+
+	if (state->Vpp != sp->state.Vpp ||
+	    state->Vcc != sp->state.Vcc) {
+	    if (!hs_set_voltages(sp, state->Vcc, state->Vpp)) {
+	    	local_irq_restore(flags);
+	    	return -EINVAL;
+	    }
+	}
+
+/*    	hd64465_io_debug = 1; */
+    	/*
+	 * Handle changes in the Card Status Change mask,
+	 * by propagating to the CSCR register
+	 */	
+	changed = sp->state.csc_mask ^ state->csc_mask;
+	cscier = hs_in(sp, CSCIER);
+	    
+	if (changed & SS_DETECT) {
+	    if (state->csc_mask & SS_DETECT)
+		cscier |= HD64465_PCCCSCIER_PCDE;
+	    else
+		cscier &= ~HD64465_PCCCSCIER_PCDE;
+	}
+
+	if (changed & SS_READY) {
+	    if (state->csc_mask & SS_READY)
+		cscier |= HD64465_PCCCSCIER_PRE;
+	    else
+		cscier &= ~HD64465_PCCCSCIER_PRE;
+	}
+
+	if (changed & SS_BATDEAD) {
+	    if (state->csc_mask & SS_BATDEAD)
+		cscier |= HD64465_PCCCSCIER_PBDE;
+	    else
+		cscier &= ~HD64465_PCCCSCIER_PBDE;
+	}
+
+	if (changed & SS_BATWARN) {
+	    if (state->csc_mask & SS_BATWARN)
+		cscier |= HD64465_PCCCSCIER_PBWE;
+	    else
+		cscier &= ~HD64465_PCCCSCIER_PBWE;
+	}
+
+	if (changed & SS_STSCHG) {
+	    if (state->csc_mask & SS_STSCHG)
+		cscier |= HD64465_PCCCSCIER_PSCE;
+	    else
+		cscier &= ~HD64465_PCCCSCIER_PSCE;
+	}
+
+    	hs_out(sp, cscier, CSCIER);
+
+	if (sp->state.io_irq && !state->io_irq)
+	    hs_unmap_irq(sp, sp->state.io_irq);
+	else if (!sp->state.io_irq && state->io_irq)
+	    hs_map_irq(sp, state->io_irq);
+
+
+    	/*
+	 * Handle changes in the flags field,
+	 * by propagating to config registers.
+	 */	
+	changed = sp->state.flags ^ state->flags;
+
+	if (changed & SS_IOCARD) {
+	    DPRINTK("card type: %s\n",
+		    (state->flags & SS_IOCARD ? "i/o" : "memory" ));
+	    bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCT,
+		state->flags & SS_IOCARD);
+	}
+
+	if (changed & SS_RESET) {
+	    DPRINTK("%s reset card\n",
+		(state->flags & SS_RESET ? "start" : "stop"));
+	    bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCR,
+		state->flags & SS_RESET);
+	}
+
+	if (changed & SS_OUTPUT_ENA) {
+	    DPRINTK("%sabling card output\n",
+		(state->flags & SS_OUTPUT_ENA ? "en" : "dis"));
+	    bool_to_regbit(sp, GCR, HD64465_PCCGCR_PDRV,
+		state->flags & SS_OUTPUT_ENA);
+	}
+
+    	/* TODO: SS_SPKR_ENA */
+	    
+/*    	hd64465_io_debug = 0; */
+	sp->state = *state;
+	    
+	local_irq_restore(flags);
+
+#if HD64465_DEBUG > 10
+	if (state->flags & SS_OUTPUT_ENA)   
+	    cis_hex_dump((const unsigned char*)sp->mem_base, 0x100);
+#endif
+	return 0;
+}
+
+/*============================================================*/
+
+static int hs_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+	int map = io->map;
+	int sock = sp->number;
+	struct pccard_io_map *sio;
+	pgprot_t prot;
+
+    	DPRINTK("hs_set_io_map(sock=%d, map=%d, flags=0x%x, speed=%dns, start=%#lx, stop=%#lx)\n",
+	    sock, map, io->flags, io->speed, io->start, io->stop);
+	if (map >= MAX_IO_WIN)
+	    return -EINVAL;
+	sio = &sp->io_maps[map];
+
+    	/* check for null changes */	
+    	if (io->flags == sio->flags &&
+	    io->start == sio->start &&
+	    io->stop == sio->stop)
+	    return 0;
+	
+	if (io->flags & MAP_AUTOSZ)
+	    prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IODYN);
+	else if (io->flags & MAP_16BIT)
+	    prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO16);
+	else
+	    prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO8);
+
+	/* TODO: handle MAP_USE_WAIT */
+	if (io->flags & MAP_USE_WAIT)
+	    printk(KERN_INFO MODNAME ": MAP_USE_WAIT unimplemented\n");
+	/* TODO: handle MAP_PREFETCH */
+	if (io->flags & MAP_PREFETCH)
+	    printk(KERN_INFO MODNAME ": MAP_PREFETCH unimplemented\n");
+	/* TODO: handle MAP_WRPROT */
+	if (io->flags & MAP_WRPROT)
+	    printk(KERN_INFO MODNAME ": MAP_WRPROT unimplemented\n");
+	/* TODO: handle MAP_0WS */
+	if (io->flags & MAP_0WS)
+	    printk(KERN_INFO MODNAME ": MAP_0WS unimplemented\n");
+
+	if (io->flags & MAP_ACTIVE) {
+	    unsigned long pstart, psize, paddrbase;
+	    
+	    paddrbase = virt_to_phys((void*)(sp->mem_base + 2 * HD64465_PCC_WINDOW));
+	    pstart = io->start & PAGE_MASK;
+	    psize = ((io->stop + PAGE_SIZE) & PAGE_MASK) - pstart;
+
+    	    /*
+	     * Change PTEs in only that portion of the mapping requested
+	     * by the caller.  This means that most of the time, most of
+	     * the PTEs in the io_vma will be unmapped and only the bottom
+	     * page will be mapped.  But the code allows for weird cards
+	     * that might want IO ports > 4K.
+	     */
+	    sp->io_base = p3_ioremap(paddrbase + pstart, psize, pgprot_val(prot));
+	    
+	    /*
+	     * Change the mapping used by inb() outb() etc
+	     */
+	    hd64465_port_map(io->start,
+		io->stop - io->start + 1,
+	    	(unsigned long)sp->io_base + io->start, 0);
+	} else {
+	    hd64465_port_unmap(sio->start, sio->stop - sio->start + 1);
+	    p3_iounmap(sp->io_base);
+	}
+	
+	*sio = *io;
+	return 0;
+}
+
+/*============================================================*/
+
+static int hs_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+    	hs_socket_t *sp = container_of(s, struct hs_socket_t, socket);
+	struct pccard_mem_map *smem;
+	int map = mem->map;
+	unsigned long paddr;
+
+#if 0
+    	DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, card_start=0x%08x)\n",
+	    sock, map, mem->flags, mem->card_start);
+#endif
+
+	if (map >= MAX_WIN)
+	    return -EINVAL;
+	smem = &sp->mem_maps[map];
+	
+	paddr = sp->mem_base;	    	    /* base of Attribute mapping */
+	if (!(mem->flags & MAP_ATTRIB))
+	    paddr += HD64465_PCC_WINDOW;    /* base of Common mapping */
+	paddr += mem->card_start;
+
+    	/* Because we specified SS_CAP_STATIC_MAP, we are obliged
+	 * at this time to report the system address corresponding
+	 * to the card address requested.  This is how Socket Services
+	 * queries our fixed mapping.  I wish this fact had been
+	 * documented - Greg Banks.
+	 */
+    	mem->static_start = paddr;
+	
+	*smem = *mem;
+	
+    	return 0;
+}
+
+/* TODO: do we need to use the MMU to access Common memory ??? */
+
+/*============================================================*/
+
+/*
+ * This function is registered with the HD64465 glue code to do a
+ * secondary demux step on the PCMCIA interrupts.  It handles 
+ * mapping the IREQ request from the card to a standard Linux
+ * IRQ, as requested by SocketServices.
+ */
+static int hs_irq_demux(int irq, void *dev)
+{
+    	hs_socket_t *sp = (hs_socket_t *)dev;
+	u_int cscr;
+    	
+    	DPRINTK("hs_irq_demux(irq=%d)\n", irq);
+
+    	if (sp->state.io_irq &&
+	    (cscr = hs_in(sp, CSCR)) & HD64465_PCCCSCR_PIREQ) {
+	    cscr &= ~HD64465_PCCCSCR_PIREQ;
+	    hs_out(sp, cscr, CSCR);
+	    return sp->state.io_irq;
+	}
+	    
+	return irq;
+}
+
+/*============================================================*/
+
+/*
+ * Interrupt handling routine.
+ */
+ 
+static irqreturn_t hs_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+    	hs_socket_t *sp = (hs_socket_t *)dev;
+	u_int events = 0;
+	u_int cscr;
+	
+	
+	cscr = hs_in(sp, CSCR);
+	
+	DPRINTK("hs_interrupt, cscr=%04x\n", cscr);
+
+	/* check for bus-related changes to be reported to Socket Services */
+	if (cscr & HD64465_PCCCSCR_PCDC) {
+	    /* double-check for a 16-bit card, as we don't support CardBus */
+	    if ((hs_in(sp, ISR) & HD64465_PCCISR_PCD_MASK) != 0) {
+	    	printk(KERN_NOTICE MODNAME
+		    ": socket %d, card not a supported card type or not inserted correctly\n",
+		    sp->number);
+		/* Don't do the rest unless a card is present */
+		cscr &= ~(HD64465_PCCCSCR_PCDC|
+		    	  HD64465_PCCCSCR_PRC|
+			  HD64465_PCCCSCR_PBW|
+		    	  HD64465_PCCCSCR_PBD|
+			  HD64465_PCCCSCR_PSC);
+	    } else {
+	    	cscr &= ~HD64465_PCCCSCR_PCDC;
+		events |= SS_DETECT;    	/* card insertion or removal */
+    	    }
+	}
+	if (cscr & HD64465_PCCCSCR_PRC) {
+	    cscr &= ~HD64465_PCCCSCR_PRC;
+	    events |= SS_READY;     	/* ready signal changed */
+	}
+	if (cscr & HD64465_PCCCSCR_PBW) {
+	    cscr &= ~HD64465_PCCCSCR_PSC;
+	    events |= SS_BATWARN;     	/* battery warning */
+	}
+	if (cscr & HD64465_PCCCSCR_PBD) {
+	    cscr &= ~HD64465_PCCCSCR_PSC;
+	    events |= SS_BATDEAD;     	/* battery dead */
+	}
+	if (cscr & HD64465_PCCCSCR_PSC) {
+	    cscr &= ~HD64465_PCCCSCR_PSC;
+	    events |= SS_STSCHG;     	/* STSCHG (status changed) signal */
+	}
+	
+	if (cscr & HD64465_PCCCSCR_PIREQ) {
+	    cscr &= ~HD64465_PCCCSCR_PIREQ;
+
+    	    /* This should have been dealt with during irq demux */	    
+	    printk(KERN_NOTICE MODNAME ": unexpected IREQ from card\n");
+	}
+
+	hs_out(sp, cscr, CSCR);
+
+	if (events)
+		pcmcia_parse_events(&sp->socket, events);
+
+	return IRQ_HANDLED;
+}
+
+/*============================================================*/
+
+static struct pccard_operations hs_operations = {
+	.init			= hs_init,
+	.get_status		= hs_get_status,
+	.get_socket		= hs_get_socket,
+	.set_socket		= hs_set_socket,
+	.set_io_map		= hs_set_io_map,
+	.set_mem_map		= hs_set_mem_map,
+};
+
+static int hs_init_socket(hs_socket_t *sp, int irq, unsigned long mem_base,
+    	    unsigned int ctrl_base)
+{
+    	unsigned short v;
+    	int i, err;
+
+    	memset(sp, 0, sizeof(*sp));
+	sp->irq = irq;
+	sp->mem_base = mem_base;
+	sp->mem_length = 4*HD64465_PCC_WINDOW;	/* 16MB */
+	sp->ctrl_base = ctrl_base;
+	
+	for (i=0 ; i<MAX_IO_WIN ; i++)
+	    sp->io_maps[i].map = i;
+	for (i=0 ; i<MAX_WIN ; i++)
+	    sp->mem_maps[i].map = i;
+	
+	hd64465_register_irq_demux(sp->irq, hs_irq_demux, sp);
+	
+    	if ((err = request_irq(sp->irq, hs_interrupt, SA_INTERRUPT, MODNAME, sp)) < 0)
+	    return err;
+    	if (request_mem_region(sp->mem_base, sp->mem_length, MODNAME) == 0) {
+    	    sp->mem_base = 0;
+	    return -ENOMEM;
+	}
+
+
+	/* According to section 3.2 of the PCMCIA standard, low-voltage
+	 * capable cards must implement cold insertion, i.e. Vpp and
+	 * Vcc set to 0 before card is inserted.
+	 */
+	/*hs_set_voltages(sp, 0, 0);*/
+	
+	/* hi-Z the outputs to the card and set 16MB map mode */
+	v = hs_in(sp, GCR);
+	v &= ~HD64465_PCCGCR_PCCT;  	/* memory-only card */
+	hs_out(sp, v, GCR);
+
+	v = hs_in(sp, GCR);
+	v |= HD64465_PCCGCR_PDRV;   	/* enable outputs to card */
+	hs_out(sp, v, GCR);
+
+	v = hs_in(sp, GCR);
+	v |= HD64465_PCCGCR_PMMOD; 	/* 16MB mapping mode */
+	hs_out(sp, v, GCR);
+
+	v = hs_in(sp, GCR);
+	/* lowest 16MB of Common */
+	v &= ~(HD64465_PCCGCR_PPA25|HD64465_PCCGCR_PPA24); 
+	hs_out(sp, v, GCR);
+	
+	hs_reset_socket(sp, 1);
+
+	printk(KERN_INFO "HD64465 PCMCIA bridge socket %d at 0x%08lx irq %d\n",
+	    	i, sp->mem_base, sp->irq);
+
+    	return 0;
+}
+
+static void hs_exit_socket(hs_socket_t *sp)
+{
+    	unsigned short cscier, gcr;
+	unsigned long flags;
+	
+	local_irq_save(flags);
+
+	/* turn off interrupts in hardware */
+    	cscier = hs_in(sp, CSCIER);
+	cscier = (cscier & IER_MASK) | IER_OFF;
+    	hs_out(sp, cscier, CSCIER);
+	
+	/* hi-Z the outputs to the card */
+    	gcr = hs_in(sp, GCR);
+	gcr &= HD64465_PCCGCR_PDRV;
+	hs_out(sp, gcr, GCR);
+
+    	/* power the card down */
+	hs_set_voltages(sp, 0, 0);
+
+    	if (sp->mem_base != 0)
+	    release_mem_region(sp->mem_base, sp->mem_length);
+	if (sp->irq != 0) {
+	    free_irq(sp->irq, hs_interrupt);
+    	    hd64465_unregister_irq_demux(sp->irq);
+	}
+
+	local_irq_restore(flags);
+}
+
+static int hd64465_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int hd64465_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+static struct device_driver hd64465_driver = {
+	.name = "hd64465-pcmcia",
+	.bus = &platform_bus_type,
+	.suspend = hd64465_suspend,
+	.resume = hd64465_resume,
+};
+
+static struct platform_device hd64465_device = {
+	.name = "hd64465-pcmcia",
+	.id = 0,
+};
+
+static int __init init_hs(void)
+{
+	int i;
+	unsigned short v;
+
+/*	hd64465_io_debug = 1; */
+	if (driver_register(&hd64465_driver))
+		return -EINVAL;
+	
+	/* Wake both sockets out of STANDBY mode */
+	/* TODO: wait 15ms */
+	v = inw(HD64465_REG_SMSCR);
+	v &= ~(HD64465_SMSCR_PC0ST|HD64465_SMSCR_PC1ST);
+	outw(v, HD64465_REG_SMSCR);
+
+	/* keep power controller out of shutdown mode */
+	v = inb(HD64465_REG_PCC0SCR);
+	v |= HD64465_PCCSCR_SHDN;
+	outb(v, HD64465_REG_PCC0SCR);
+
+    	/* use serial (TPS2206) power controller */
+	v = inb(HD64465_REG_PCC0CSCR);
+	v |= HD64465_PCCCSCR_PSWSEL;
+	outb(v, HD64465_REG_PCC0CSCR);
+
+	/*
+	 * Setup hs_sockets[] structures and request system resources.
+	 * TODO: on memory allocation failure, power down the socket
+	 *       before quitting.
+	 */
+	for (i=0; i<HS_MAX_SOCKETS; i++) {
+		hs_set_voltages(&hs_sockets[i], 0, 0);
+
+		hs_sockets[i].socket.features |=  SS_CAP_PCCARD | SS_CAP_STATIC_MAP;      /* mappings are fixed in host memory */
+		hs_sockets[i].socket.resource_ops = &pccard_static_ops;
+		hs_sockets[i].socket.irq_mask =  0xffde;/*0xffff*/	    /* IRQs mapped in s/w so can do any, really */
+		hs_sockets[i].socket.map_size = HD64465_PCC_WINDOW;     /* 16MB fixed window size */
+
+		hs_sockets[i].socket.owner = THIS_MODULE;
+		hs_sockets[i].socket.ss_entry = &hs_operations;
+	}
+
+	i = hs_init_socket(&hs_sockets[0],
+	    HD64465_IRQ_PCMCIA0,
+	    HD64465_PCC0_BASE,
+	    HD64465_REG_PCC0ISR);
+	if (i < 0) {
+		unregister_driver(&hd64465_driver);
+		return i;
+	}
+	i = hs_init_socket(&hs_sockets[1],
+	    HD64465_IRQ_PCMCIA1,
+	    HD64465_PCC1_BASE,
+	    HD64465_REG_PCC1ISR);
+	if (i < 0) {
+		unregister_driver(&hd64465_driver);
+		return i;
+	}
+
+/*	hd64465_io_debug = 0; */
+
+	platform_device_register(&hd64465_device);
+
+	for (i=0; i<HS_MAX_SOCKETS; i++) {
+		unsigned int ret;
+		hs_sockets[i].socket.dev.dev = &hd64465_device.dev;		
+		hs_sockets[i].number = i;
+		ret = pcmcia_register_socket(&hs_sockets[i].socket);
+		if (ret && i)
+			pcmcia_unregister_socket(&hs_sockets[0].socket);
+	}
+
+    	return 0;
+}
+
+static void __exit exit_hs(void)
+{
+	int i;
+
+	for (i=0 ; i<HS_MAX_SOCKETS ; i++) {
+		pcmcia_unregister_socket(&hs_sockets[i].socket);
+		hs_exit_socket(&hs_sockets[i]);
+	}
+
+	platform_device_unregister(&hd64465_device);
+	unregister_driver(&hd64465_driver);
+}
+
+module_init(init_hs);
+module_exit(exit_hs);
+
+/*============================================================*/
+/*END*/
diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c
new file mode 100644
index 0000000..f3fdc74
--- /dev/null
+++ b/drivers/pcmcia/i82092.c
@@ -0,0 +1,799 @@
+/* 
+ * Driver for Intel I82092AA PCI-PCMCIA bridge.
+ *
+ * (C) 2001 Red Hat, Inc.
+ *
+ * Author: Arjan Van De Ven <arjanv@redhat.com>
+ * Loosly based on i82365.c from the pcmcia-cs package
+ *
+ * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "i82092aa.h"
+#include "i82365.h"
+
+MODULE_LICENSE("GPL");
+
+/* PCI core routines */
+static struct pci_device_id i82092aa_pci_ids[] = {
+	{
+	      .vendor = PCI_VENDOR_ID_INTEL,
+	      .device = PCI_DEVICE_ID_INTEL_82092AA_0,
+	      .subvendor = PCI_ANY_ID,
+	      .subdevice = PCI_ANY_ID,
+	 },
+	 {} 
+};
+MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
+
+static int i82092aa_socket_suspend (struct pci_dev *dev, pm_message_t state)
+{
+	return pcmcia_socket_dev_suspend(&dev->dev, state);
+}
+
+static int i82092aa_socket_resume (struct pci_dev *dev)
+{
+	return pcmcia_socket_dev_resume(&dev->dev);
+}
+
+static struct pci_driver i82092aa_pci_drv = {
+	.name           = "i82092aa",
+	.id_table       = i82092aa_pci_ids,
+	.probe          = i82092aa_pci_probe,
+	.remove         = __devexit_p(i82092aa_pci_remove),
+	.suspend        = i82092aa_socket_suspend,
+	.resume         = i82092aa_socket_resume,
+};
+
+
+/* the pccard structure and its functions */
+static struct pccard_operations i82092aa_operations = {
+	.init 		 	= i82092aa_init,
+	.get_status		= i82092aa_get_status,
+	.get_socket		= i82092aa_get_socket,
+	.set_socket		= i82092aa_set_socket,
+	.set_io_map		= i82092aa_set_io_map,
+	.set_mem_map		= i82092aa_set_mem_map,
+};
+
+/* The card can do upto 4 sockets, allocate a structure for each of them */
+
+struct socket_info {
+	int	number;
+	int	card_state; 	/*  0 = no socket,
+				    1 = empty socket, 
+				    2 = card but not initialized,
+				    3 = operational card */
+	kio_addr_t io_base; 	/* base io address of the socket */
+	
+	struct pcmcia_socket socket;
+	struct pci_dev *dev;	/* The PCI device for the socket */
+};
+
+#define MAX_SOCKETS 4
+static struct socket_info sockets[MAX_SOCKETS];
+static int socket_count;  /* shortcut */                                  	                                	
+
+
+static int __devinit i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	unsigned char configbyte;
+	int i, ret;
+	
+	enter("i82092aa_pci_probe");
+	
+	if ((ret = pci_enable_device(dev)))
+		return ret;
+		
+	pci_read_config_byte(dev, 0x40, &configbyte);  /* PCI Configuration Control */
+	switch(configbyte&6) {
+		case 0:
+			socket_count = 2;
+			break;
+		case 2:
+			socket_count = 1;
+			break;
+		case 4:
+		case 6:
+			socket_count = 4;
+			break;
+			
+		default:
+			printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n");
+			ret = -EIO;
+			goto err_out_disable;
+	}
+	printk(KERN_INFO "i82092aa: configured as a %d socket device.\n", socket_count);
+
+	if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) {
+		ret = -EBUSY;
+		goto err_out_disable;
+	}
+	
+	for (i = 0;i<socket_count;i++) {
+		sockets[i].card_state = 1; /* 1 = present but empty */
+		sockets[i].io_base = pci_resource_start(dev, 0);
+		sockets[i].socket.features |= SS_CAP_PCCARD;
+		sockets[i].socket.map_size = 0x1000;
+		sockets[i].socket.irq_mask = 0;
+		sockets[i].socket.pci_irq  = dev->irq;
+		sockets[i].socket.owner = THIS_MODULE;
+
+		sockets[i].number = i;
+		
+		if (card_present(i)) {
+			sockets[i].card_state = 3;
+			dprintk(KERN_DEBUG "i82092aa: slot %i is occupied\n",i);
+		} else {
+			dprintk(KERN_DEBUG "i82092aa: slot %i is vacant\n",i);
+		}
+	}
+		
+	/* Now, specifiy that all interrupts are to be done as PCI interrupts */
+	configbyte = 0xFF; /* bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt */
+	pci_write_config_byte(dev, 0x50, configbyte); /* PCI Interrupt Routing Register */
+
+	/* Register the interrupt handler */
+	dprintk(KERN_DEBUG "Requesting interrupt %i \n",dev->irq);
+	if ((ret = request_irq(dev->irq, i82092aa_interrupt, SA_SHIRQ, "i82092aa", i82092aa_interrupt))) {
+		printk(KERN_ERR "i82092aa: Failed to register IRQ %d, aborting\n", dev->irq);
+		goto err_out_free_res;
+	}
+
+	pci_set_drvdata(dev, &sockets[i].socket);
+
+	for (i = 0; i<socket_count; i++) {
+		sockets[i].socket.dev.dev = &dev->dev;
+		sockets[i].socket.ops = &i82092aa_operations;
+		sockets[i].socket.resource_ops = &pccard_nonstatic_ops;
+		ret = pcmcia_register_socket(&sockets[i].socket);
+		if (ret) {
+			goto err_out_free_sockets;
+		}
+	}
+
+	leave("i82092aa_pci_probe");
+	return 0;
+
+err_out_free_sockets:
+	if (i) {
+		for (i--;i>=0;i--) {
+			pcmcia_unregister_socket(&sockets[i].socket);
+		}
+	}
+	free_irq(dev->irq, i82092aa_interrupt);
+err_out_free_res:
+	release_region(pci_resource_start(dev, 0), 2);
+err_out_disable:
+	pci_disable_device(dev);
+	return ret;			
+}
+
+static void __devexit i82092aa_pci_remove(struct pci_dev *dev)
+{
+	struct pcmcia_socket *socket = pci_get_drvdata(dev);
+
+	enter("i82092aa_pci_remove");
+	
+	free_irq(dev->irq, i82092aa_interrupt);
+
+	if (socket)
+		pcmcia_unregister_socket(socket);
+
+	leave("i82092aa_pci_remove");
+}
+
+static DEFINE_SPINLOCK(port_lock);
+
+/* basic value read/write functions */
+
+static unsigned char indirect_read(int socket, unsigned short reg)
+{
+	unsigned short int port;
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg += socket * 0x40;
+	port = sockets[socket].io_base;
+	outb(reg,port);
+	val = inb(port+1);
+	spin_unlock_irqrestore(&port_lock,flags);
+	return val;
+}
+
+#if 0
+static unsigned short indirect_read16(int socket, unsigned short reg)
+{
+	unsigned short int port;
+	unsigned short tmp;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg  = reg + socket * 0x40;
+	port = sockets[socket].io_base;
+	outb(reg,port);
+	tmp = inb(port+1);
+	reg++;
+	outb(reg,port);
+	tmp = tmp | (inb(port+1)<<8);
+	spin_unlock_irqrestore(&port_lock,flags);
+	return tmp;
+}
+#endif
+
+static void indirect_write(int socket, unsigned short reg, unsigned char value)
+{
+	unsigned short int port;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg = reg + socket * 0x40;
+	port = sockets[socket].io_base; 
+	outb(reg,port);
+	outb(value,port+1);
+	spin_unlock_irqrestore(&port_lock,flags);
+}
+
+static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
+{
+	unsigned short int port;
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg = reg + socket * 0x40;
+	port = sockets[socket].io_base; 
+	outb(reg,port);
+	val = inb(port+1);
+	val |= mask;
+	outb(reg,port);
+	outb(val,port+1);
+	spin_unlock_irqrestore(&port_lock,flags);
+}
+
+
+static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask)
+{
+	unsigned short int port;
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg = reg + socket * 0x40;
+	port = sockets[socket].io_base; 
+	outb(reg,port);
+	val = inb(port+1);
+	val &= ~mask;
+	outb(reg,port);
+	outb(val,port+1);
+	spin_unlock_irqrestore(&port_lock,flags);
+}
+
+static void indirect_write16(int socket, unsigned short reg, unsigned short value)
+{
+	unsigned short int port;
+	unsigned char val;
+	unsigned long flags;
+	spin_lock_irqsave(&port_lock,flags);
+	reg = reg + socket * 0x40;
+	port = sockets[socket].io_base; 
+	
+	outb(reg,port);
+	val = value & 255;
+	outb(val,port+1);
+	
+	reg++;
+	
+	outb(reg,port);
+	val = value>>8;
+	outb(val,port+1);
+	spin_unlock_irqrestore(&port_lock,flags);
+}
+
+/* simple helper functions */
+/* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
+static int cycle_time = 120;
+
+static int to_cycles(int ns)
+{
+	if (cycle_time!=0)
+		return ns/cycle_time;
+	else
+		return 0;
+}
+    
+
+/* Interrupt handler functionality */
+
+static irqreturn_t i82092aa_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	int i;
+	int loopcount = 0;
+	int handled = 0;
+
+	unsigned int events, active=0;
+	
+/*	enter("i82092aa_interrupt");*/
+	
+	while (1) {
+		loopcount++;
+		if (loopcount>20) {
+			printk(KERN_ERR "i82092aa: infinite eventloop in interrupt \n");
+			break;
+		}
+		
+		active = 0;
+		
+		for (i=0;i<socket_count;i++) {
+			int csc;
+			if (sockets[i].card_state==0) /* Inactive socket, should not happen */
+				continue;
+			
+			csc = indirect_read(i,I365_CSC); /* card status change register */
+			
+			if (csc==0)  /* no events on this socket */
+			   	continue;
+			handled = 1;
+			events = 0;
+			 
+			if (csc & I365_CSC_DETECT) {
+				events |= SS_DETECT;
+				printk("Card detected in socket %i!\n",i);
+			 }
+			
+			if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) { 
+				/* For IO/CARDS, bit 0 means "read the card" */
+				events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; 
+			} else {
+				/* Check for battery/ready events */
+				events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+				events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+				events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+			}
+			
+			if (events) {
+				pcmcia_parse_events(&sockets[i].socket, events);
+			}
+			active |= events;
+		}
+				
+		if (active==0) /* no more events to handle */
+			break;				
+		
+	}
+	return IRQ_RETVAL(handled);
+/*	leave("i82092aa_interrupt");*/
+}
+
+
+
+/* socket functions */
+
+static int card_present(int socketno)
+{	
+	unsigned int val;
+	enter("card_present");
+	
+	if ((socketno<0) || (socketno >= MAX_SOCKETS))
+		return 0;
+	if (sockets[socketno].io_base == 0)
+		return 0;
+
+		
+	val = indirect_read(socketno, 1); /* Interface status register */
+	if ((val&12)==12) {
+		leave("card_present 1");
+		return 1;
+	}
+		
+	leave("card_present 0");
+	return 0;
+}
+
+static void set_bridge_state(int sock)
+{
+	enter("set_bridge_state");
+	indirect_write(sock, I365_GBLCTL,0x00);
+	indirect_write(sock, I365_GENCTL,0x00);
+	
+	indirect_setbit(sock, I365_INTCTL,0x08);
+	leave("set_bridge_state");
+}
+
+
+
+
+
+      
+static int i82092aa_init(struct pcmcia_socket *sock)
+{
+	int i;
+	struct resource res = { .start = 0, .end = 0x0fff };
+        pccard_io_map io = { 0, 0, 0, 0, 1 };
+	pccard_mem_map mem = { .res = &res, };
+        
+        enter("i82092aa_init");
+                        
+        for (i = 0; i < 2; i++) {
+        	io.map = i;
+                i82092aa_set_io_map(sock, &io);
+	}
+        for (i = 0; i < 5; i++) {
+        	mem.map = i;
+                i82092aa_set_mem_map(sock, &mem);
+	}
+	
+	leave("i82092aa_init");
+	return 0;
+}
+                                                                                                                                                                                                                                              
+static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value)
+{
+	unsigned int sock = container_of(socket, struct socket_info, socket)->number;
+	unsigned int status;
+	
+	enter("i82092aa_get_status");
+	
+	status = indirect_read(sock,I365_STATUS); /* Interface Status Register */
+	*value = 0;
+	
+	if ((status & I365_CS_DETECT) == I365_CS_DETECT) {
+		*value |= SS_DETECT;
+	}
+		
+	/* IO cards have a different meaning of bits 0,1 */
+	/* Also notice the inverse-logic on the bits */
+	 if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD)	{
+	 	/* IO card */
+	 	if (!(status & I365_CS_STSCHG))
+	 		*value |= SS_STSCHG;
+	 } else { /* non I/O card */
+	 	if (!(status & I365_CS_BVD1))
+	 		*value |= SS_BATDEAD;
+	 	if (!(status & I365_CS_BVD2))
+	 		*value |= SS_BATWARN;
+	 		
+	 }
+	 
+	 if (status & I365_CS_WRPROT)
+	 	(*value) |= SS_WRPROT;	/* card is write protected */
+	 
+	 if (status & I365_CS_READY)
+	 	(*value) |= SS_READY;    /* card is not busy */
+	 	
+	 if (status & I365_CS_POWERON)
+	 	(*value) |= SS_POWERON;  /* power is applied to the card */
+
+
+	leave("i82092aa_get_status");
+	return 0;
+}
+
+
+static int i82092aa_get_socket(struct pcmcia_socket *socket, socket_state_t *state) 
+{
+	unsigned int sock = container_of(socket, struct socket_info, socket)->number;
+	unsigned char reg,vcc,vpp;
+	
+	enter("i82092aa_get_socket");
+	state->flags    = 0;
+	state->Vcc      = 0;
+	state->Vpp      = 0;
+	state->io_irq   = 0;
+	state->csc_mask = 0;
+
+	/* First the power status of the socket */
+	reg = indirect_read(sock,I365_POWER); /* PCTRL - Power Control Register */
+
+	if (reg & I365_PWR_AUTO)
+		state->flags |= SS_PWR_AUTO;  /* Automatic Power Switch */
+		
+	if (reg & I365_PWR_OUT)
+		state->flags |= SS_OUTPUT_ENA; /* Output signals are enabled */
+		
+	vcc = reg & I365_VCC_MASK;    vpp = reg & I365_VPP1_MASK;
+	
+	if (reg & I365_VCC_5V) { /* Can still be 3.3V, in this case the Vcc value will be overwritten later */
+		state->Vcc = 50;
+		
+		if (vpp == I365_VPP1_5V)
+			state->Vpp = 50;
+		if (vpp == I365_VPP1_12V)
+			state->Vpp = 120;
+			
+	}
+	
+	if ((reg & I365_VCC_3V)==I365_VCC_3V)
+		state->Vcc = 33;
+	
+	
+	/* Now the IO card, RESET flags and IO interrupt */
+	
+	reg = indirect_read(sock, I365_INTCTL); /* IGENC, Interrupt and General Control */
+	
+	if ((reg & I365_PC_RESET)==0)
+		state->flags |= SS_RESET;
+	if (reg & I365_PC_IOCARD) 
+		state->flags |= SS_IOCARD; /* This is an IO card */
+	
+	/* Set the IRQ number */
+	if (sockets[sock].dev!=NULL)
+		state->io_irq = sockets[sock].dev->irq;
+	
+	/* Card status change */
+	reg = indirect_read(sock, I365_CSCINT); /* CSCICR, Card Status Change Interrupt Configuration */
+	
+	if (reg & I365_CSC_DETECT) 
+		state->csc_mask |= SS_DETECT; /* Card detect is enabled */
+	
+	if (state->flags & SS_IOCARD) {/* IO Cards behave different */
+		if (reg & I365_CSC_STSCHG)
+			state->csc_mask |= SS_STSCHG;
+	} else {
+		if (reg & I365_CSC_BVD1) 
+			state->csc_mask |= SS_BATDEAD;
+		if (reg & I365_CSC_BVD2) 
+			state->csc_mask |= SS_BATWARN;
+		if (reg & I365_CSC_READY) 
+			state->csc_mask |= SS_READY;
+	}
+		
+	leave("i82092aa_get_socket");
+	return 0;
+}
+
+static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state) 
+{
+	unsigned int sock = container_of(socket, struct socket_info, socket)->number;
+	unsigned char reg;
+	
+	enter("i82092aa_set_socket");
+	
+	/* First, set the global controller options */
+	
+	set_bridge_state(sock);
+	
+	/* Values for the IGENC register */
+	
+	reg = 0;
+	if (!(state->flags & SS_RESET)) 	/* The reset bit has "inverse" logic */
+		reg = reg | I365_PC_RESET;  
+	if (state->flags & SS_IOCARD) 
+		reg = reg | I365_PC_IOCARD;
+		
+	indirect_write(sock,I365_INTCTL,reg); /* IGENC, Interrupt and General Control Register */
+	
+	/* Power registers */
+	
+	reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
+	
+	if (state->flags & SS_PWR_AUTO) {
+		printk("Auto power\n");
+		reg |= I365_PWR_AUTO;	/* automatic power mngmnt */
+	}
+	if (state->flags & SS_OUTPUT_ENA) {
+		printk("Power Enabled \n");
+		reg |= I365_PWR_OUT;	/* enable power */
+	}
+	
+	switch (state->Vcc) {
+		case 0:	
+			break;
+		case 50: 
+			printk("setting voltage to Vcc to 5V on socket %i\n",sock);
+			reg |= I365_VCC_5V;
+			break;
+		default:
+			printk("i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc);
+			leave("i82092aa_set_socket");
+			return -EINVAL;
+	}
+	
+	
+	switch (state->Vpp) {
+		case 0:	
+			printk("not setting Vpp on socket %i\n",sock);
+			break;
+		case 50: 
+			printk("setting Vpp to 5.0 for socket %i\n",sock);
+			reg |= I365_VPP1_5V | I365_VPP2_5V;
+			break;
+		case 120: 
+			printk("setting Vpp to 12.0\n");
+			reg |= I365_VPP1_12V | I365_VPP2_12V;
+			break;
+		default:
+			printk("i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc);
+			leave("i82092aa_set_socket");
+			return -EINVAL;
+	}
+	
+	if (reg != indirect_read(sock,I365_POWER)) /* only write if changed */
+		indirect_write(sock,I365_POWER,reg);
+		
+	/* Enable specific interrupt events */
+	
+	reg = 0x00;
+	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;
+	} else {
+		if (state->csc_mask & SS_BATDEAD) 
+			reg |= I365_CSC_BVD1;
+		if (state->csc_mask & SS_BATWARN) 
+			reg |= I365_CSC_BVD2;
+		if (state->csc_mask & SS_READY) 
+			reg |= I365_CSC_READY; 
+		                        
+	}
+	
+	/* now write the value and clear the (probably bogus) pending stuff by doing a dummy read*/
+	
+	indirect_write(sock,I365_CSCINT,reg);
+	(void)indirect_read(sock,I365_CSC);
+
+	leave("i82092aa_set_socket");
+	return 0;
+}
+
+static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io)
+{
+	unsigned int sock = container_of(socket, struct socket_info, socket)->number;
+	unsigned char map, ioctl;
+	
+	enter("i82092aa_set_io_map");
+	
+	map = io->map;
+	
+	/* Check error conditions */	
+	if (map > 1) {
+		leave("i82092aa_set_io_map with invalid map");
+		return -EINVAL;
+	}
+	if ((io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)){
+		leave("i82092aa_set_io_map with invalid io");
+		return -EINVAL;
+	}
+
+	/* Turn off the window before changing anything */ 
+	if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+		indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
+
+/*	printk("set_io_map: Setting range to %x - %x \n",io->start,io->stop);  */
+	
+	/* write the new values */
+	indirect_write16(sock,I365_IO(map)+I365_W_START,io->start);            	
+	indirect_write16(sock,I365_IO(map)+I365_W_STOP,io->stop);            	
+	            	
+	ioctl = indirect_read(sock,I365_IOCTL) & ~I365_IOCTL_MASK(map);
+	
+	if (io->flags & (MAP_16BIT|MAP_AUTOSZ))
+		ioctl |= I365_IOCTL_16BIT(map);
+		
+	indirect_write(sock,I365_IOCTL,ioctl);
+	
+	/* Turn the window back on if needed */
+	if (io->flags & MAP_ACTIVE)
+		indirect_setbit(sock,I365_ADDRWIN,I365_ENA_IO(map));
+			
+	leave("i82092aa_set_io_map");	
+	return 0;
+}
+
+static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem)
+{
+	struct socket_info *sock_info = container_of(socket, struct socket_info, socket);
+	unsigned int sock = sock_info->number;
+	struct pci_bus_region region;
+	unsigned short base, i;
+	unsigned char map;
+	
+	enter("i82092aa_set_mem_map");
+
+	pcibios_resource_to_bus(sock_info->dev, &region, mem->res);
+	
+	map = mem->map;
+	if (map > 4) {
+		leave("i82092aa_set_mem_map: invalid map");
+		return -EINVAL;
+	}
+	
+	
+	if ( (mem->card_start > 0x3ffffff) || (region.start > region.end) ||
+	     (mem->speed > 1000) ) {
+		leave("i82092aa_set_mem_map: invalid address / speed");
+		printk("invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,region.start, region.end, mem->card_start);
+		return -EINVAL;
+	}
+	
+	/* Turn off the window before changing anything */
+	if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
+	              indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+	                 
+	                 
+/* 	printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, region.start,region.end,sock,mem->speed,mem->flags & MAP_ACTIVE);  */
+
+	/* write the start address */
+	base = I365_MEM(map);
+	i = (region.start >> 12) & 0x0fff;
+	if (mem->flags & MAP_16BIT) 
+		i |= I365_MEM_16BIT;
+	if (mem->flags & MAP_0WS)
+		i |= I365_MEM_0WS;	
+	indirect_write16(sock,base+I365_W_START,i);
+		               
+	/* write the stop address */
+	
+	i= (region.end >> 12) & 0x0fff;
+	switch (to_cycles(mem->speed)) {
+		case 0:
+			break;
+		case 1:
+			i |= I365_MEM_WS0;
+			break;
+		case 2:
+			i |= I365_MEM_WS1;
+			break;
+		default:
+			i |= I365_MEM_WS1 | I365_MEM_WS0;
+			break;
+	}
+	
+	indirect_write16(sock,base+I365_W_STOP,i);
+	
+	/* card start */
+	
+	i = ((mem->card_start - region.start) >> 12) & 0x3fff;
+	if (mem->flags & MAP_WRPROT)
+		i |= I365_MEM_WRPROT;
+	if (mem->flags & MAP_ATTRIB) {
+/*		printk("requesting attribute memory for socket %i\n",sock);*/
+		i |= I365_MEM_REG;
+	} else {
+/*		printk("requesting normal memory for socket %i\n",sock);*/
+	}
+	indirect_write16(sock,base+I365_W_OFF,i);
+	
+	/* Enable the window if necessary */
+	if (mem->flags & MAP_ACTIVE)
+		indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
+	            
+	leave("i82092aa_set_mem_map");
+	return 0;
+}
+
+static int i82092aa_module_init(void)
+{
+	enter("i82092aa_module_init");
+	pci_register_driver(&i82092aa_pci_drv);
+	leave("i82092aa_module_init");
+	return 0;
+}
+
+static void i82092aa_module_exit(void)
+{
+	enter("i82092aa_module_exit");
+	pci_unregister_driver(&i82092aa_pci_drv);
+	if (sockets[0].io_base>0)
+			 release_region(sockets[0].io_base, 2);
+	leave("i82092aa_module_exit");
+}
+
+module_init(i82092aa_module_init);
+module_exit(i82092aa_module_exit);
+
diff --git a/drivers/pcmcia/i82092aa.h b/drivers/pcmcia/i82092aa.h
new file mode 100644
index 0000000..b98cac7
--- /dev/null
+++ b/drivers/pcmcia/i82092aa.h
@@ -0,0 +1,39 @@
+#ifndef _INCLUDE_GUARD_i82092aa_H_
+#define _INCLUDE_GUARD_i82092aa_H_
+
+#include <linux/interrupt.h>
+
+/* $Id: i82092aa.h,v 1.1.1.1 2001/09/19 14:53:15 dwmw2 Exp $ */
+
+/* Debuging defines */
+#ifdef NOTRACE
+#define enter(x)   printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__)
+#define leave(x)   printk("Leave: %s, %s line %i\n",x,__FILE__,__LINE__)
+#define dprintk(fmt, args...) printk(fmt , ## args)
+#else
+#define enter(x)   do {} while (0)
+#define leave(x)   do {} while (0)
+#define dprintk(fmt, args...) do {} while (0)
+#endif
+
+
+
+/* prototypes */
+
+static int  i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id);
+static void i82092aa_pci_remove(struct pci_dev *dev);
+static int card_present(int socketno);
+static irqreturn_t i82092aa_interrupt(int irq, void *dev, struct pt_regs *regs);
+
+
+
+
+static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value);
+static int i82092aa_get_socket(struct pcmcia_socket *socket, socket_state_t *state);
+static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state);
+static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io);
+static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem);
+static int i82092aa_init(struct pcmcia_socket *socket);
+
+#endif
+
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
new file mode 100644
index 0000000..90a335a
--- /dev/null
+++ b/drivers/pcmcia/i82365.c
@@ -0,0 +1,1454 @@
+/*======================================================================
+
+    Device driver for Intel 82365 and compatible PC Card controllers.
+
+    i82365.c 1.265 1999/11/10 18:36:21
+
+    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 David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#include <linux/isapnp.h>
+
+/* ISA-bus controllers */
+#include "i82365.h"
+#include "cirrus.h"
+#include "vg468.h"
+#include "ricoh.h"
+
+#ifdef DEBUG
+static const char version[] =
+"i82365.c 1.265 1999/11/10 18:36:21 (David Hinds)";
+
+static int pc_debug;
+
+module_param(pc_debug, int, 0644);
+
+#define debug(lvl, fmt, arg...) do {				\
+	if (pc_debug > (lvl))					\
+		printk(KERN_DEBUG "i82365: " fmt , ## arg);	\
+} while (0)
+#else
+#define debug(lvl, fmt, arg...) do { } while (0)
+#endif
+
+static irqreturn_t i365_count_irq(int, void *, struct pt_regs *);
+static inline int _check_irq(int irq, int flags)
+{
+    if (request_irq(irq, i365_count_irq, flags, "x", i365_count_irq) != 0)
+	return -1;
+    free_irq(irq, i365_count_irq);
+    return 0;
+}
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Default base address for i82365sl and other ISA chips */
+static unsigned long i365_base = 0x3e0;
+/* Should we probe at 0x3e2 for an extra ISA controller? */
+static int extra_sockets = 0;
+/* Specify a socket number to ignore */
+static int ignore = -1;
+/* Bit map or list of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[16];
+static int irq_list_count;
+/* The card status change interrupt -- 0 means autoselect */
+static int cs_irq = 0;
+
+/* Probe for safe interrupts? */
+static int do_scan = 1;
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+/* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
+static int cycle_time = 120;
+
+/* Cirrus options */
+static int has_dma = -1;
+static int has_led = -1;
+static int has_ring = -1;
+static int dynamic_mode = 0;
+static int freq_bypass = -1;
+static int setup_time = -1;
+static int cmd_time = -1;
+static int recov_time = -1;
+
+/* Vadem options */
+static int async_clock = -1;
+static int cable_mode = -1;
+static int wakeup = 0;
+
+module_param(i365_base, ulong, 0444);
+module_param(ignore, int, 0444);
+module_param(extra_sockets, int, 0444);
+module_param(irq_mask, int, 0444);
+module_param_array(irq_list, int, &irq_list_count, 0444);
+module_param(cs_irq, int, 0444);
+module_param(async_clock, int, 0444);
+module_param(cable_mode, int, 0444);
+module_param(wakeup, int, 0444);
+
+module_param(do_scan, int, 0444);
+module_param(poll_interval, int, 0444);
+module_param(cycle_time, int, 0444);
+module_param(has_dma, int, 0444);
+module_param(has_led, int, 0444);
+module_param(has_ring, int, 0444);
+module_param(dynamic_mode, int, 0444);
+module_param(freq_bypass, int, 0444);
+module_param(setup_time, int, 0444);
+module_param(cmd_time, int, 0444);
+module_param(recov_time, int, 0444);
+
+/*====================================================================*/
+
+typedef struct cirrus_state_t {
+    u_char		misc1, misc2;
+    u_char		timer[6];
+} cirrus_state_t;
+
+typedef struct vg46x_state_t {
+    u_char		ctl, ema;
+} vg46x_state_t;
+
+struct i82365_socket {
+    u_short		type, flags;
+    struct pcmcia_socket	socket;
+    unsigned int	number;
+    kio_addr_t		ioaddr;
+    u_short		psock;
+    u_char		cs_irq, intr;
+    union {
+	cirrus_state_t		cirrus;
+	vg46x_state_t		vg46x;
+    } state;
+};
+
+/* Where we keep track of our sockets... */
+static int sockets = 0;
+static struct i82365_socket socket[8] = {
+    { 0, }, /* ... */
+};
+
+/* Default ISA interrupt mask */
+#define I365_MASK	0xdeb8	/* irq 15,14,12,11,10,9,7,5,4,3 */
+
+static int grab_irq;
+static DEFINE_SPINLOCK(isa_lock);
+#define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f)
+#define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f)
+
+static struct timer_list poll_timer;
+
+/*====================================================================*/
+
+/* These definitions must match the pcic table! */
+typedef enum pcic_id {
+    IS_I82365A, IS_I82365B, IS_I82365DF,
+    IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
+    IS_PD6710, IS_PD672X, IS_VT83C469,
+} pcic_id;
+
+/* Flags for classifying groups of controllers */
+#define IS_VADEM	0x0001
+#define IS_CIRRUS	0x0002
+#define IS_VIA		0x0010
+#define IS_UNKNOWN	0x0400
+#define IS_VG_PWR	0x0800
+#define IS_DF_PWR	0x1000
+#define IS_REGISTERED	0x2000
+#define IS_ALIVE	0x8000
+
+typedef struct pcic_t {
+    char		*name;
+    u_short		flags;
+} pcic_t;
+
+static pcic_t pcic[] = {
+    { "Intel i82365sl A step", 0 },
+    { "Intel i82365sl B step", 0 },
+    { "Intel i82365sl DF", IS_DF_PWR },
+    { "IBM Clone", 0 },
+    { "Ricoh RF5C296/396", 0 },
+    { "VLSI 82C146", 0 },
+    { "Vadem VG-468", IS_VADEM },
+    { "Vadem VG-469", IS_VADEM|IS_VG_PWR },
+    { "Cirrus PD6710", IS_CIRRUS },
+    { "Cirrus PD672x", IS_CIRRUS },
+    { "VIA VT83C469", IS_CIRRUS|IS_VIA },
+};
+
+#define PCIC_COUNT	(sizeof(pcic)/sizeof(pcic_t))
+
+/*====================================================================*/
+
+static DEFINE_SPINLOCK(bus_lock);
+
+static u_char i365_get(u_short sock, u_short reg)
+{
+    unsigned long flags;
+    spin_lock_irqsave(&bus_lock,flags);
+    {
+	kio_addr_t port = socket[sock].ioaddr;
+	u_char val;
+	reg = I365_REG(socket[sock].psock, reg);
+	outb(reg, port); val = inb(port+1);
+	spin_unlock_irqrestore(&bus_lock,flags);
+	return val;
+    }
+}
+
+static void i365_set(u_short sock, u_short reg, u_char data)
+{
+    unsigned long flags;
+    spin_lock_irqsave(&bus_lock,flags);
+    {
+	kio_addr_t port = socket[sock].ioaddr;
+	u_char val = I365_REG(socket[sock].psock, reg);
+	outb(val, port); outb(data, port+1);
+	spin_unlock_irqrestore(&bus_lock,flags);
+    }
+}
+
+static void i365_bset(u_short sock, u_short reg, u_char mask)
+{
+    u_char d = i365_get(sock, reg);
+    d |= mask;
+    i365_set(sock, reg, d);
+}
+
+static void i365_bclr(u_short sock, u_short reg, u_char mask)
+{
+    u_char d = i365_get(sock, reg);
+    d &= ~mask;
+    i365_set(sock, reg, d);
+}
+
+static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
+{
+    u_char d = i365_get(sock, reg);
+    if (b)
+	d |= mask;
+    else
+	d &= ~mask;
+    i365_set(sock, reg, d);
+}
+
+static u_short i365_get_pair(u_short sock, u_short reg)
+{
+    u_short a, b;
+    a = i365_get(sock, reg);
+    b = i365_get(sock, reg+1);
+    return (a + (b<<8));
+}
+
+static void i365_set_pair(u_short sock, u_short reg, u_short data)
+{
+    i365_set(sock, reg, data & 0xff);
+    i365_set(sock, reg+1, data >> 8);
+}
+
+/*======================================================================
+
+    Code to save and restore global state information for Cirrus
+    PD67xx controllers, and to set and report global configuration
+    options.
+
+    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))))
+
+static void cirrus_get_state(u_short s)
+{
+    int i;
+    cirrus_state_t *p = &socket[s].state.cirrus;
+    p->misc1 = i365_get(s, PD67_MISC_CTL_1);
+    p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
+    p->misc2 = i365_get(s, PD67_MISC_CTL_2);
+    for (i = 0; i < 6; i++)
+	p->timer[i] = i365_get(s, PD67_TIME_SETUP(0)+i);
+}
+
+static void cirrus_set_state(u_short s)
+{
+    int i;
+    u_char misc;
+    cirrus_state_t *p = &socket[s].state.cirrus;
+
+    misc = i365_get(s, PD67_MISC_CTL_2);
+    i365_set(s, PD67_MISC_CTL_2, p->misc2);
+    if (misc & PD67_MC2_SUSPEND) mdelay(50);
+    misc = i365_get(s, PD67_MISC_CTL_1);
+    misc &= ~(PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
+    i365_set(s, PD67_MISC_CTL_1, misc | p->misc1);
+    for (i = 0; i < 6; i++)
+	i365_set(s, PD67_TIME_SETUP(0)+i, p->timer[i]);
+}
+
+static u_int __init cirrus_set_opts(u_short s, char *buf)
+{
+    struct i82365_socket *t = &socket[s];
+    cirrus_state_t *p = &socket[s].state.cirrus;
+    u_int mask = 0xffff;
+
+    if (has_ring == -1) has_ring = 1;
+    flip(p->misc2, PD67_MC2_IRQ15_RI, has_ring);
+    flip(p->misc2, PD67_MC2_DYNAMIC_MODE, dynamic_mode);
+    flip(p->misc2, PD67_MC2_FREQ_BYPASS, freq_bypass);
+    if (p->misc2 & PD67_MC2_IRQ15_RI)
+	strcat(buf, " [ring]");
+    if (p->misc2 & PD67_MC2_DYNAMIC_MODE)
+	strcat(buf, " [dyn mode]");
+    if (p->misc2 & PD67_MC2_FREQ_BYPASS)
+	strcat(buf, " [freq bypass]");
+    if (p->misc1 & PD67_MC1_INPACK_ENA)
+	strcat(buf, " [inpack]");
+    if (p->misc2 & PD67_MC2_IRQ15_RI)
+	mask &= ~0x8000;
+    if (has_led > 0) {
+	strcat(buf, " [led]");
+	mask &= ~0x1000;
+    }
+    if (has_dma > 0) {
+	strcat(buf, " [dma]");
+	mask &= ~0x0600;
+    }
+    if (!(t->flags & IS_VIA)) {
+	if (setup_time >= 0)
+	    p->timer[0] = p->timer[3] = setup_time;
+	if (cmd_time > 0) {
+	    p->timer[1] = cmd_time;
+	    p->timer[4] = cmd_time*2+4;
+	}
+	if (p->timer[1] == 0) {
+	    p->timer[1] = 6; p->timer[4] = 16;
+	    if (p->timer[0] == 0)
+		p->timer[0] = p->timer[3] = 1;
+	}
+	if (recov_time >= 0)
+	    p->timer[2] = p->timer[5] = recov_time;
+	buf += strlen(buf);
+	sprintf(buf, " [%d/%d/%d] [%d/%d/%d]", p->timer[0], p->timer[1],
+		p->timer[2], p->timer[3], p->timer[4], p->timer[5]);
+    }
+    return mask;
+}
+
+/*======================================================================
+
+    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)
+{
+    vg46x_state_t *p = &socket[s].state.vg46x;
+    p->ctl = i365_get(s, VG468_CTL);
+    if (socket[s].type == IS_VG469)
+	p->ema = i365_get(s, VG469_EXT_MODE);
+}
+
+static void vg46x_set_state(u_short s)
+{
+    vg46x_state_t *p = &socket[s].state.vg46x;
+    i365_set(s, VG468_CTL, p->ctl);
+    if (socket[s].type == IS_VG469)
+	i365_set(s, VG469_EXT_MODE, p->ema);
+}
+
+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)
+	strcat(buf, " [async]");
+    if (p->ctl & VG468_CTL_INPACK)
+	strcat(buf, " [inpack]");
+    if (socket[s].type == IS_VG469) {
+	u_char vsel = i365_get(s, VG469_VSELECT);
+	if (vsel & VG469_VSEL_EXT_STAT) {
+	    strcat(buf, " [ext mode]");
+	    if (vsel & VG469_VSEL_EXT_BUS)
+		strcat(buf, " [isa buf]");
+	}
+	if (p->ema & VG469_MODE_CABLE)
+	    strcat(buf, " [cable]");
+	if (p->ema & VG469_MODE_COMPAT)
+	    strcat(buf, " [c step]");
+    }
+    return 0xffff;
+}
+
+/*======================================================================
+
+    Generic routines to get and set controller options
+    
+======================================================================*/
+
+static void get_bridge_state(u_short s)
+{
+    struct i82365_socket *t = &socket[s];
+    if (t->flags & IS_CIRRUS)
+	cirrus_get_state(s);
+    else if (t->flags & IS_VADEM)
+	vg46x_get_state(s);
+}
+
+static void set_bridge_state(u_short s)
+{
+    struct i82365_socket *t = &socket[s];
+    if (t->flags & IS_CIRRUS)
+	cirrus_set_state(s);
+    else {
+	i365_set(s, I365_GBLCTL, 0x00);
+	i365_set(s, I365_GENCTL, 0x00);
+    }
+    i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr);
+    if (t->flags & IS_VADEM)
+	vg46x_set_state(s);
+}
+
+static u_int __init set_bridge_opts(u_short s, u_short ns)
+{
+    u_short i;
+    u_int m = 0xffff;
+    char buf[128];
+
+    for (i = s; i < s+ns; i++) {
+	if (socket[i].flags & IS_ALIVE) {
+	    printk(KERN_INFO "    host opts [%d]: already alive!\n", i);
+	    continue;
+	}
+	buf[0] = '\0';
+	get_bridge_state(i);
+	if (socket[i].flags & IS_CIRRUS)
+	    m = cirrus_set_opts(i, buf);
+	else if (socket[i].flags & IS_VADEM)
+	    m = vg46x_set_opts(i, buf);
+	set_bridge_state(i);
+	printk(KERN_INFO "    host opts [%d]:%s\n", i,
+	       (*buf) ? buf : " none");
+    }
+    return m;
+}
+
+/*======================================================================
+
+    Interrupt testing code, for ISA and PCI interrupts
+    
+======================================================================*/
+
+static volatile u_int irq_hits;
+static u_short irq_sock;
+
+static irqreturn_t i365_count_irq(int irq, void *dev, struct pt_regs *regs)
+{
+    i365_get(irq_sock, I365_CSC);
+    irq_hits++;
+    debug(2, "-> hit on irq %d\n", irq);
+    return IRQ_HANDLED;
+}
+
+static u_int __init test_irq(u_short sock, int irq)
+{
+    debug(2, "  testing ISA irq %d\n", irq);
+    if (request_irq(irq, i365_count_irq, 0, "scan", i365_count_irq) != 0)
+	return 1;
+    irq_hits = 0; irq_sock = sock;
+    msleep(10);
+    if (irq_hits) {
+	free_irq(irq, i365_count_irq);
+	debug(2, "    spurious hit!\n");
+	return 1;
+    }
+
+    /* Generate one interrupt */
+    i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4));
+    i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ);
+    udelay(1000);
+
+    free_irq(irq, i365_count_irq);
+
+    /* mask all interrupts */
+    i365_set(sock, I365_CSCINT, 0);
+    debug(2, "    hits = %d\n", irq_hits);
+    
+    return (irq_hits != 1);
+}
+
+static u_int __init isa_scan(u_short sock, u_int mask0)
+{
+    u_int mask1 = 0;
+    int i;
+
+#ifdef __alpha__
+#define PIC 0x4d0
+    /* 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);
+	for (i = 0; i < 16; i++)
+	    if ((mask0 & (1 << i)) && (test_irq(sock, i) == 0))
+		mask1 |= (1 << i);
+	for (i = 0; i < 16; i++)
+	    if ((mask1 & (1 << i)) && (test_irq(sock, i) != 0))
+		mask1 ^= (1 << i);
+    }
+    
+    printk(KERN_INFO "    ISA irqs (");
+    if (mask1) {
+	printk("scanned");
+    } else {
+	/* Fallback: just find interrupts that aren't in use */
+	for (i = 0; i < 16; i++)
+	    if ((mask0 & (1 << i)) && (_check_irq(i, 0) == 0))
+		mask1 |= (1 << i);
+	printk("default");
+	/* If scan failed, default to polled status */
+	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;
+}
+
+/*====================================================================*/
+
+/* Time conversion functions */
+
+static int to_cycles(int ns)
+{
+    return ns/cycle_time;
+}
+
+/*====================================================================*/
+
+static int __init identify(kio_addr_t port, u_short sock)
+{
+    u_char val;
+    int type = -1;
+
+    /* 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) {
+    case 0x82:
+	type = IS_I82365A; break;
+    case 0x83:
+	type = IS_I82365B; break;
+    case 0x84:
+	type = IS_I82365DF; break;
+    case 0x88: case 0x89: case 0x8a:
+	type = IS_IBM; break;
+    }
+    
+    /* Check for Vadem VG-468 chips */
+    outb(0x0e, port);
+    outb(0x37, port);
+    i365_bset(sockets, VG468_MISC, VG468_MISC_VADEMREV);
+    val = i365_get(sockets, I365_IDENT);
+    if (val & I365_IDENT_VADEM) {
+	i365_bclr(sockets, VG468_MISC, VG468_MISC_VADEMREV);
+	type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
+    }
+
+    /* Check for Ricoh chips */
+    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);
+    if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
+	val = i365_get(sockets, PD67_CHIP_INFO);
+	if ((val & PD67_INFO_CHIP_ID) == 0) {
+	    type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
+	    i365_set(sockets, PD67_EXT_INDEX, 0xe5);
+	    if (i365_get(sockets, PD67_EXT_INDEX) != 0xe5)
+		type = IS_VT83C469;
+	}
+    }
+    return type;
+} /* identify */
+
+/*======================================================================
+
+    See if a card is present, powered up, in IO mode, and already
+    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;
+    kio_addr_t 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);
+    if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) &&
+	(i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) &&
+	(i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) &&
+	(check_region(start, stop-start+1) != 0) &&
+	((start & 0xfeef) != 0x02e8))
+	return 1;
+    else
+	return 0;
+}
+
+/*====================================================================*/
+
+static void __init add_socket(kio_addr_t port, int psock, int type)
+{
+    socket[sockets].ioaddr = port;
+    socket[sockets].psock = psock;
+    socket[sockets].type = type;
+    socket[sockets].flags = pcic[type].flags;
+    if (is_alive(sockets))
+	socket[sockets].flags |= IS_ALIVE;
+    sockets++;
+}
+
+static void __init add_pcic(int ns, int type)
+{
+    u_int mask = 0, i, base;
+    int isa_irq = 0;
+    struct i82365_socket *t = &socket[sockets-ns];
+
+    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 %#lx ofs 0x%02x",
+	       t->ioaddr, t->psock*0x40);
+    printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : ""));
+
+    /* Set host options, build basic interrupt mask */
+    if (irq_list_count == 0)
+	mask = irq_mask;
+    else
+	for (i = mask = 0; i < irq_list_count; i++)
+	    mask |= (1<<irq_list[i]);
+    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);
+	tmp = tmp & (tmp-1);
+	if ((tmp & (tmp-1)) == 0)
+	    poll_interval = HZ;
+    }
+    /* Only try an ISA cs_irq if this is the first controller */
+    if (!grab_irq && (cs_irq || !poll_interval)) {
+	/* Avoid irq 12 unless it is explicitly requested */
+	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
+	for (cs_irq = 15; cs_irq > 0; cs_irq--)
+	    if ((cs_mask & (1 << cs_irq)) &&
+		(_check_irq(cs_irq, 0) == 0))
+		break;
+	if (cs_irq) {
+	    grab_irq = 1;
+	    isa_irq = cs_irq;
+	    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].socket.features |= SS_CAP_PCCARD;
+	t[i].socket.map_size = 0x1000;
+	t[i].socket.irq_mask = mask;
+	t[i].cs_irq = isa_irq;
+    }
+
+} /* add_pcic */
+
+/*====================================================================*/
+
+#ifdef CONFIG_PNP
+static struct isapnp_device_id id_table[] __initdata = {
+	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
+		ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" },
+	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
+		ISAPNP_FUNCTION(0x0e01), (unsigned long) "Cirrus Logic CL-PD6720" },
+	{ 	ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
+		ISAPNP_FUNCTION(0x0e02), (unsigned long) "VLSI VL82C146" },
+	{	0 }
+};
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static struct pnp_dev *i82365_pnpdev;
+#endif
+
+static void __init isa_probe(void)
+{
+    int i, j, sock, k, ns, id;
+    kio_addr_t port;
+#ifdef CONFIG_PNP
+    struct isapnp_device_id *devid;
+    struct pnp_dev *dev;
+
+    for (devid = id_table; devid->vendor; devid++) {
+	if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
+	
+	    if (pnp_device_attach(dev) < 0)
+	    	continue;
+
+	    if (pnp_activate_dev(dev) < 0) {
+		printk("activate failed\n");
+		pnp_device_detach(dev);
+		break;
+	    }
+
+	    if (!pnp_port_valid(dev, 0)) {
+		printk("invalid resources ?\n");
+		pnp_device_detach(dev);
+		break;
+	    }
+	    i365_base = pnp_port_start(dev, 0);
+	    i82365_pnpdev = dev;
+	    break;
+	}
+    }
+#endif
+
+    if (check_region(i365_base, 2) != 0) {
+	if (sockets == 0)
+	    printk("port conflict at %#lx\n", i365_base);
+	return;
+    }
+
+    id = identify(i365_base, 0);
+    if ((id == IS_I82365DF) && (identify(i365_base, 1) != id)) {
+	for (i = 0; i < 4; i++) {
+	    if (i == ignore) continue;
+	    port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
+	    sock = (i & 1) << 1;
+	    if (identify(port, sock) == IS_I82365DF) {
+		add_socket(port, sock, IS_VLSI);
+		add_pcic(1, IS_VLSI);
+	    }
+	}
+    } else {
+	for (i = 0; i < 8; i += 2) {
+	    if (sockets && !extra_sockets && (i == 4))
+		break;
+	    port = i365_base + 2*(i>>2);
+	    sock = (i & 3);
+	    id = identify(port, sock);
+	    if (id < 0) continue;
+
+	    for (j = ns = 0; j < 2; j++) {
+		/* Does the socket exist? */
+		if ((ignore == i+j) || (identify(port, sock+j) < 0))
+		    continue;
+		/* Check for bad socket decode */
+		for (k = 0; k <= sockets; k++)
+		    i365_set(k, I365_MEM(0)+I365_W_OFF, k);
+		for (k = 0; k <= sockets; k++)
+		    if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
+			break;
+		if (k <= sockets) break;
+		add_socket(port, sock+j, id); ns++;
+	    }
+	    if (ns != 0) add_pcic(ns, id);
+	}
+    }
+}
+
+/*====================================================================*/
+
+static irqreturn_t pcic_interrupt(int irq, void *dev,
+				    struct pt_regs *regs)
+{
+    int i, j, csc;
+    u_int events, active;
+    u_long flags = 0;
+    int handled = 0;
+
+    debug(4, "pcic_interrupt(%d)\n", irq);
+
+    for (j = 0; j < 20; j++) {
+	active = 0;
+	for (i = 0; i < sockets; i++) {
+	    if (socket[i].cs_irq != irq)
+		continue;
+	    handled = 1;
+	    ISA_LOCK(i, flags);
+	    csc = i365_get(i, I365_CSC);
+	    if ((csc == 0) || (i365_get(i, I365_IDENT) & 0x70)) {
+		ISA_UNLOCK(i, flags);
+		continue;
+	    }
+	    events = (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+
+	    if (i365_get(i, I365_INTCTL) & I365_PC_IOCARD)
+		events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+	    else {
+		events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+		events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+		events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+	    }
+	    ISA_UNLOCK(i, flags);
+	    debug(2, "socket %d event 0x%02x\n", i, events);
+
+	    if (events)
+		pcmcia_parse_events(&socket[i].socket, events);
+
+	    active |= events;
+	}
+	if (!active) break;
+    }
+    if (j == 20)
+	printk(KERN_NOTICE "i82365: infinite loop in interrupt handler\n");
+
+    debug(4, "interrupt done\n");
+    return IRQ_RETVAL(handled);
+} /* pcic_interrupt */
+
+static void pcic_interrupt_wrapper(u_long data)
+{
+    pcic_interrupt(0, NULL, NULL);
+    poll_timer.expires = jiffies + poll_interval;
+    add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+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 {
+	*value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
+	*value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
+    }
+    *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+    *value |= (status & I365_CS_READY) ? SS_READY : 0;
+    *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+
+    if (socket[sock].type == IS_VG469) {
+	status = i365_get(sock, VG469_VSENSE);
+	if (socket[sock].psock & 1) {
+	    *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
+	    *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
+	} else {
+	    *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
+	    *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
+	}
+    }
+    
+    debug(1, "GetStatus(%d) = %#4.4x\n", sock, *value);
+    return 0;
+} /* i365_get_status */
+
+/*====================================================================*/
+
+static int i365_get_socket(u_short sock, socket_state_t *state)
+{
+    struct i82365_socket *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;
+    vcc = reg & I365_VCC_MASK; vpp = reg & I365_VPP1_MASK;
+    state->Vcc = state->Vpp = 0;
+    if (t->flags & IS_CIRRUS) {
+	if (i365_get(sock, PD67_MISC_CTL_1) & PD67_MC1_VCC_3V) {
+	    if (reg & I365_VCC_5V) state->Vcc = 33;
+	    if (vpp == I365_VPP1_5V) state->Vpp = 33;
+	} else {
+	    if (reg & I365_VCC_5V) state->Vcc = 50;
+	    if (vpp == I365_VPP1_5V) state->Vpp = 50;
+	}
+	if (vpp == I365_VPP1_12V) state->Vpp = 120;
+    } else if (t->flags & IS_VG_PWR) {
+	if (i365_get(sock, VG469_VSELECT) & VG469_VSEL_VCC) {
+	    if (reg & I365_VCC_5V) state->Vcc = 33;
+	    if (vpp == I365_VPP1_5V) state->Vpp = 33;
+	} else {
+	    if (reg & I365_VCC_5V) state->Vcc = 50;
+	    if (vpp == I365_VPP1_5V) state->Vpp = 50;
+	}
+	if (vpp == I365_VPP1_12V) state->Vpp = 120;
+    } else if (t->flags & IS_DF_PWR) {
+	if (vcc == I365_VCC_3V) state->Vcc = 33;
+	if (vcc == I365_VCC_5V) state->Vcc = 50;
+	if (vpp == I365_VPP1_5V) state->Vpp = 50;
+	if (vpp == I365_VPP1_12V) state->Vpp = 120;
+    } else {
+	if (reg & I365_VCC_5V) {
+	    state->Vcc = 50;
+	    if (vpp == I365_VPP1_5V) state->Vpp = 50;
+	    if (vpp == I365_VPP1_12V) state->Vpp = 120;
+	}
+    }
+
+    /* IO card, RESET flags, IO interrupt */
+    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;
+    
+    /* 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;
+    if (state->flags & SS_IOCARD)
+	state->csc_mask |= (reg & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+    else {
+	state->csc_mask |= (reg & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+	state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0;
+	state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0;
+    }
+    
+    debug(1, "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);
+    return 0;
+} /* i365_get_socket */
+
+/*====================================================================*/
+
+static int i365_set_socket(u_short sock, socket_state_t *state)
+{
+    struct i82365_socket *t = &socket[sock];
+    u_char reg;
+    
+    debug(1, "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 |= (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;
+
+    if (t->flags & IS_CIRRUS) {
+	if (state->Vpp != 0) {
+	    if (state->Vpp == 120)
+		reg |= I365_VPP1_12V;
+	    else if (state->Vpp == state->Vcc)
+		reg |= I365_VPP1_5V;
+	    else return -EINVAL;
+	}
+	if (state->Vcc != 0) {
+	    reg |= I365_VCC_5V;
+	    if (state->Vcc == 33)
+		i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+	    else if (state->Vcc == 50)
+		i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+	    else return -EINVAL;
+	}
+    } else if (t->flags & IS_VG_PWR) {
+	if (state->Vpp != 0) {
+	    if (state->Vpp == 120)
+		reg |= I365_VPP1_12V;
+	    else if (state->Vpp == state->Vcc)
+		reg |= I365_VPP1_5V;
+	    else return -EINVAL;
+	}
+	if (state->Vcc != 0) {
+	    reg |= I365_VCC_5V;
+	    if (state->Vcc == 33)
+		i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
+	    else if (state->Vcc == 50)
+		i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
+	    else return -EINVAL;
+	}
+    } else if (t->flags & IS_DF_PWR) {
+	switch (state->Vcc) {
+	case 0:		break;
+	case 33:   	reg |= I365_VCC_3V; break;
+	case 50:	reg |= I365_VCC_5V; break;
+	default:	return -EINVAL;
+	}
+	switch (state->Vpp) {
+	case 0:		break;
+	case 50:   	reg |= I365_VPP1_5V; break;
+	case 120:	reg |= I365_VPP1_12V; break;
+	default:	return -EINVAL;
+	}
+    } else {
+	switch (state->Vcc) {
+	case 0:		break;
+	case 50:	reg |= I365_VCC_5V; break;
+	default:	return -EINVAL;
+	}
+	switch (state->Vpp) {
+	case 0:		break;
+	case 50:	reg |= I365_VPP1_5V | I365_VPP2_5V; break;
+	case 120:	reg |= I365_VPP1_12V | I365_VPP2_12V; break;
+	default:	return -EINVAL;
+	}
+    }
+    
+    if (reg != i365_get(sock, I365_POWER))
+	i365_set(sock, I365_POWER, reg);
+
+    /* Chipset-specific functions */
+    if (t->flags & IS_CIRRUS) {
+	/* Speaker control */
+	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;
+    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;
+    } else {
+	if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
+	if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
+	if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+    }
+    i365_set(sock, I365_CSCINT, reg);
+    i365_get(sock, I365_CSC);
+    
+    return 0;
+} /* i365_set_socket */
+
+/*====================================================================*/
+
+static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+    u_char map, ioctl;
+    
+    debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, "
+	  "%#lx-%#lx)\n", sock, io->map, io->flags,
+	  io->speed, io->start, io->stop);
+    map = io->map;
+    if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+	(io->stop < io->start)) return -EINVAL;
+    /* Turn off the window before changing anything */
+    if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
+	i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
+    i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
+    i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
+    ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+    if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
+    if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+    if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+    if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+    i365_set(sock, I365_IOCTL, ioctl);
+    /* Turn on the window if necessary */
+    if (io->flags & MAP_ACTIVE)
+	i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
+    return 0;
+} /* i365_set_io_map */
+
+/*====================================================================*/
+
+static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+    u_short base, i;
+    u_char map;
+    
+    debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#lx-%#lx, "
+	  "%#x)\n", sock, mem->map, mem->flags, mem->speed,
+	  mem->res->start, mem->res->end, mem->card_start);
+
+    map = mem->map;
+    if ((map > 4) || (mem->card_start > 0x3ffffff) ||
+	(mem->res->start > mem->res->end) || (mem->speed > 1000))
+	return -EINVAL;
+    if ((mem->res->start > 0xffffff) || (mem->res->end > 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->res->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->res->end >> 12) & 0x0fff;
+    switch (to_cycles(mem->speed)) {
+    case 0:	break;
+    case 1:	i |= I365_MEM_WS0; break;
+    case 2:	i |= I365_MEM_WS1; break;
+    default:	i |= I365_MEM_WS1 | I365_MEM_WS0; break;
+    }
+    i365_set_pair(sock, base+I365_W_STOP, i);
+    
+    i = ((mem->card_start - mem->res->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));
+    return 0;
+} /* i365_set_mem_map */
+
+#if 0 /* driver model ordering issue */
+/*======================================================================
+
+    Routines for accessing socket information and register dumps via
+    /sys/class/pcmcia_socket/...
+    
+======================================================================*/
+
+static ssize_t show_info(struct class_device *class_dev, char *buf)
+{
+	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
+	return sprintf(buf, "type:     %s\npsock:    %d\n",
+		       pcic[s->type].name, s->psock);
+}
+
+static ssize_t show_exca(struct class_device *class_dev, char *buf)
+{
+	struct i82365_socket *s = container_of(class_dev, struct i82365_socket, socket.dev);
+	unsigned short sock;
+	int i;
+	ssize_t ret = 0;
+	unsigned long flags = 0;
+
+	sock = s->number;
+
+	ISA_LOCK(sock, flags);
+	for (i = 0; i < 0x40; i += 4) {
+		ret += sprintf(buf, "%02x %02x %02x %02x%s",
+			       i365_get(sock,i), i365_get(sock,i+1),
+			       i365_get(sock,i+2), i365_get(sock,i+3),
+			       ((i % 16) == 12) ? "\n" : " ");
+		buf += ret;
+	}
+	ISA_UNLOCK(sock, flags);
+
+	return ret;
+}
+
+static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
+static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
+#endif
+
+/*====================================================================*/
+
+/* this is horribly ugly... proper locking needs to be done here at 
+ * some time... */
+#define LOCKED(x) do { \
+	int retval; \
+	unsigned long flags; \
+	spin_lock_irqsave(&isa_lock, flags); \
+	retval = x; \
+	spin_unlock_irqrestore(&isa_lock, flags); \
+	return retval; \
+} while (0)
+	
+
+static int pcic_get_status(struct pcmcia_socket *s, u_int *value)
+{
+	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		*value = 0;
+		return -EINVAL;
+	}
+
+	LOCKED(i365_get_status(sock, value));
+}
+
+static int pcic_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+
+	LOCKED(i365_get_socket(sock, state));
+}
+
+static int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+
+	LOCKED(i365_set_socket(sock, state));
+}
+
+static int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+
+	LOCKED(i365_set_io_map(sock, io));
+}
+
+static int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+	unsigned int sock = container_of(s, struct i82365_socket, socket)->number;
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+
+	LOCKED(i365_set_mem_map(sock, mem));
+}
+
+static int pcic_init(struct pcmcia_socket *s)
+{
+	int i;
+	struct resource res = { .start = 0, .end = 0x1000 };
+	pccard_io_map io = { 0, 0, 0, 0, 1 };
+	pccard_mem_map mem = { .res = &res, };
+
+	for (i = 0; i < 2; i++) {
+		io.map = i;
+		pcic_set_io_map(s, &io);
+	}
+	for (i = 0; i < 5; i++) {
+		mem.map = i;
+		pcic_set_mem_map(s, &mem);
+	}
+	return 0;
+}
+
+static struct pccard_operations pcic_operations = {
+	.init			= pcic_init,
+	.get_status		= pcic_get_status,
+	.get_socket		= pcic_get_socket,
+	.set_socket		= pcic_set_socket,
+	.set_io_map		= pcic_set_io_map,
+	.set_mem_map		= pcic_set_mem_map,
+};
+
+/*====================================================================*/
+
+static int i82365_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int i82365_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+static struct device_driver i82365_driver = {
+	.name = "i82365",
+	.bus = &platform_bus_type,
+	.suspend = i82365_suspend,
+	.resume = i82365_resume,
+};
+
+static struct platform_device i82365_device = {
+	.name = "i82365",
+	.id = 0,
+};
+
+static int __init init_i82365(void)
+{
+    int i, ret;
+
+    ret = driver_register(&i82365_driver);
+    if (ret)
+	return ret;
+
+    ret = platform_device_register(&i82365_device);
+    if (ret) {
+	driver_unregister(&i82365_driver);
+	return ret;
+    }
+
+    printk(KERN_INFO "Intel ISA PCIC probe: ");
+    sockets = 0;
+
+    isa_probe();
+
+    if (sockets == 0) {
+	printk("not found.\n");
+	platform_device_unregister(&i82365_device);
+	driver_unregister(&i82365_driver);
+	return -ENODEV;
+    }
+
+    /* Set up interrupt handler(s) */
+    if (grab_irq != 0)
+	request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt);
+    
+    /* register sockets with the pcmcia core */
+    for (i = 0; i < sockets; i++) {
+	    socket[i].socket.dev.dev = &i82365_device.dev;
+	    socket[i].socket.ops = &pcic_operations;
+	    socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+	    socket[i].socket.owner = THIS_MODULE;
+	    socket[i].number = i;
+	    ret = pcmcia_register_socket(&socket[i].socket);
+	    if (!ret)
+		    socket[i].flags |= IS_REGISTERED;
+
+#if 0 /* driver model ordering issue */
+	   class_device_create_file(&socket[i].socket.dev,
+			   	    &class_device_attr_info);
+	   class_device_create_file(&socket[i].socket.dev,
+			   	    &class_device_attr_exca);
+#endif
+    }
+
+    /* Finally, schedule a polling interrupt */
+    if (poll_interval != 0) {
+	poll_timer.function = pcic_interrupt_wrapper;
+	poll_timer.data = 0;
+	init_timer(&poll_timer);
+    	poll_timer.expires = jiffies + poll_interval;
+	add_timer(&poll_timer);
+    }
+    
+    return 0;
+    
+} /* init_i82365 */
+
+static void __exit exit_i82365(void)
+{
+    int i;
+
+    for (i = 0; i < sockets; i++) {
+	    if (socket[i].flags & IS_REGISTERED)
+		    pcmcia_unregister_socket(&socket[i].socket);
+    }
+    platform_device_unregister(&i82365_device);
+    if (poll_interval != 0)
+	del_timer_sync(&poll_timer);
+    if (grab_irq != 0)
+	free_irq(cs_irq, pcic_interrupt);
+    for (i = 0; i < sockets; i++) {
+	/* Turn off all interrupt sources! */
+	i365_set(i, I365_CSCINT, 0);
+	release_region(socket[i].ioaddr, 2);
+    }
+#ifdef CONFIG_PNP
+    if (i82365_pnpdev)
+    		pnp_disable_dev(i82365_pnpdev);
+#endif
+    driver_unregister(&i82365_driver);
+} /* exit_i82365 */
+
+module_init(init_i82365);
+module_exit(exit_i82365);
+MODULE_LICENSE("Dual MPL/GPL");
+/*====================================================================*/
diff --git a/drivers/pcmcia/i82365.h b/drivers/pcmcia/i82365.h
new file mode 100644
index 0000000..622860c
--- /dev/null
+++ b/drivers/pcmcia/i82365.h
@@ -0,0 +1,135 @@
+/*
+ * i82365.h 1.15 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_I82365_H
+#define _LINUX_I82365_H
+
+/* register definitions for the Intel 82365SL PCMCIA controller */
+
+/* Offsets for PCIC registers */
+#define I365_IDENT	0x00	/* Identification and revision */
+#define I365_STATUS	0x01	/* Interface status */
+#define I365_POWER	0x02	/* Power and RESETDRV control */
+#define I365_INTCTL	0x03	/* Interrupt and general control */
+#define I365_CSC	0x04	/* Card status change */
+#define I365_CSCINT	0x05	/* Card status change interrupt control */
+#define I365_ADDRWIN	0x06	/* Address window enable */
+#define I365_IOCTL	0x07	/* I/O control */
+#define I365_GENCTL	0x16	/* Card detect and general control */
+#define I365_GBLCTL	0x1E	/* Global control register */
+
+/* Offsets for I/O and memory window registers */
+#define I365_IO(map)	(0x08+((map)<<2))
+#define I365_MEM(map)	(0x10+((map)<<3))
+#define I365_W_START	0
+#define I365_W_STOP	2
+#define I365_W_OFF	4
+
+/* Flags for I365_STATUS */
+#define I365_CS_BVD1	0x01
+#define I365_CS_STSCHG	0x01
+#define I365_CS_BVD2	0x02
+#define I365_CS_SPKR	0x02
+#define I365_CS_DETECT	0x0C
+#define I365_CS_WRPROT	0x10
+#define I365_CS_READY	0x20	/* Inverted */
+#define I365_CS_POWERON	0x40
+#define I365_CS_GPI	0x80
+
+/* Flags for I365_POWER */
+#define I365_PWR_OFF	0x00	/* Turn off the socket */
+#define I365_PWR_OUT	0x80	/* Output enable */
+#define I365_PWR_NORESET 0x40	/* Disable RESETDRV on resume */
+#define I365_PWR_AUTO	0x20	/* Auto pwr switch enable */
+#define I365_VCC_MASK	0x18	/* Mask for turning off Vcc */
+/* There are different layouts for B-step and DF-step chips: the B
+   step has independent Vpp1/Vpp2 control, and the DF step has only
+   Vpp1 control, plus 3V control */
+#define I365_VCC_5V	0x10	/* Vcc = 5.0v */
+#define I365_VCC_3V	0x18	/* Vcc = 3.3v */
+#define I365_VPP2_MASK	0x0c	/* Mask for turning off Vpp2 */
+#define I365_VPP2_5V	0x04	/* Vpp2 = 5.0v */
+#define I365_VPP2_12V	0x08	/* Vpp2 = 12.0v */
+#define I365_VPP1_MASK	0x03	/* Mask for turning off Vpp1 */
+#define I365_VPP1_5V	0x01	/* Vpp2 = 5.0v */
+#define I365_VPP1_12V	0x02	/* Vpp2 = 12.0v */
+
+/* Flags for I365_INTCTL */
+#define I365_RING_ENA	0x80
+#define I365_PC_RESET	0x40
+#define I365_PC_IOCARD	0x20
+#define I365_INTR_ENA	0x10
+#define I365_IRQ_MASK	0x0F
+
+/* Flags for I365_CSC and I365_CSCINT*/
+#define I365_CSC_BVD1	0x01
+#define I365_CSC_STSCHG	0x01
+#define I365_CSC_BVD2	0x02
+#define I365_CSC_READY	0x04
+#define I365_CSC_DETECT	0x08
+#define I365_CSC_ANY	0x0F
+#define I365_CSC_GPI	0x10
+
+/* Flags for I365_ADDRWIN */
+#define I365_ENA_IO(map)	(0x40 << (map))
+#define I365_ENA_MEM(map)	(0x01 << (map))
+
+/* Flags for I365_IOCTL */
+#define I365_IOCTL_MASK(map)	(0x0F << (map<<2))
+#define I365_IOCTL_WAIT(map)	(0x08 << (map<<2))
+#define I365_IOCTL_0WS(map)	(0x04 << (map<<2))
+#define I365_IOCTL_IOCS16(map)	(0x02 << (map<<2))
+#define I365_IOCTL_16BIT(map)	(0x01 << (map<<2))
+
+/* Flags for I365_GENCTL */
+#define I365_CTL_16DELAY	0x01
+#define I365_CTL_RESET		0x02
+#define I365_CTL_GPI_ENA	0x04
+#define I365_CTL_GPI_CTL	0x08
+#define I365_CTL_RESUME		0x10
+#define I365_CTL_SW_IRQ		0x20
+
+/* Flags for I365_GBLCTL */
+#define I365_GBL_PWRDOWN	0x01
+#define I365_GBL_CSC_LEV	0x02
+#define I365_GBL_WRBACK		0x04
+#define I365_GBL_IRQ_0_LEV	0x08
+#define I365_GBL_IRQ_1_LEV	0x10
+
+/* Flags for memory window registers */
+#define I365_MEM_16BIT	0x8000	/* In memory start high byte */
+#define I365_MEM_0WS	0x4000
+#define I365_MEM_WS1	0x8000	/* In memory stop high byte */
+#define I365_MEM_WS0	0x4000
+#define I365_MEM_WRPROT	0x8000	/* In offset high byte */
+#define I365_MEM_REG	0x4000
+
+#define I365_REG(slot, reg)	(((slot) << 6) + reg)
+
+#endif /* _LINUX_I82365_H */
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
new file mode 100644
index 0000000..b0b7a7a
--- /dev/null
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -0,0 +1,873 @@
+/*
+ *  drivers/pcmcia/m32r_cfc.c
+ *
+ *  Device driver for the CFC functionality of M32R.
+ *
+ *  Copyright (c) 2001, 2002, 2003, 2004
+ *    Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#undef MAX_IO_WIN	/* FIXME */
+#define MAX_IO_WIN 1
+#undef MAX_WIN		/* FIXME */
+#define MAX_WIN 1
+
+#include "m32r_cfc.h"
+
+#ifdef DEBUG
+static int m32r_cfc_debug;
+module_param(m32r_cfc_debug, int, 0644);
+#define debug(lvl, fmt, arg...) do {				\
+	if (m32r_cfc_debug > (lvl))				\
+		printk(KERN_DEBUG "m32r_cfc: " fmt , ## arg);	\
+} while (0)
+#else
+#define debug(n, args...) do { } while (0)
+#endif
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+
+typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
+
+typedef struct pcc_socket {
+	u_short			type, flags;
+	struct pcmcia_socket	socket;
+	unsigned int		number;
+ 	kio_addr_t		ioaddr;
+	u_long			mapaddr;
+	u_long			base;	/* PCC register base */
+	u_char			cs_irq1, cs_irq2, intr;
+	pccard_io_map		io_map[MAX_IO_WIN];
+	pccard_mem_map		mem_map[MAX_WIN];
+	u_char			io_win;
+	u_char			mem_win;
+	pcc_as_t		current_space;
+	u_char			last_iodbex;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *proc;
+#endif
+} pcc_socket_t;
+
+static int pcc_sockets = 0;
+static pcc_socket_t socket[M32R_MAX_PCC] = {
+	{ 0, }, /* ... */
+};
+
+/*====================================================================*/
+
+static unsigned int pcc_get(u_short, unsigned int);
+static void pcc_set(u_short, unsigned int , unsigned int );
+
+static DEFINE_SPINLOCK(pcc_lock);
+
+#if !defined(CONFIG_PLAT_USRV)
+static inline u_long pcc_port2addr(unsigned long port, int size) {
+	u_long addr = 0;
+	u_long odd;
+
+	if (size == 1) {	/* byte access */
+		odd = (port&1) << 11;
+		port -= port & 1;
+		addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port;
+	} else if (size == 2)
+		addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port;
+
+	return addr;
+}
+#else	/* CONFIG_PLAT_USRV */
+static inline u_long pcc_port2addr(unsigned long port, int size) {
+	u_long odd;
+	u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8;
+
+	if (size == 1) {	/* byte access */
+		odd = port & 1;
+		port -= odd;
+		odd <<= 11;
+		addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff);
+	} else if (size == 2)	/* word access */
+		addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff);
+
+	return addr;
+}
+#endif	/* CONFIG_PLAT_USRV */
+
+void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
+	size_t nmemb, int flag)
+{
+	u_long addr;
+	unsigned char *bp = (unsigned char *)buf;
+	unsigned long flags;
+
+	debug(3, "m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
+		 "size=%u, nmemb=%d, flag=%d\n",
+		  sock, port, buf, size, nmemb, flag);
+
+	addr = pcc_port2addr(port, 1);
+	if (!addr) {
+		printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
+		return;
+	}
+	debug(3, "m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
+
+	spin_lock_irqsave(&pcc_lock, flags);
+	/* read Byte */
+	while (nmemb--)
+		*bp++ = readb(addr);
+	spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
+	size_t nmemb, int flag)
+{
+	u_long addr;
+	unsigned short *bp = (unsigned short *)buf;
+	unsigned long flags;
+
+	debug(3, "m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
+		 "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+		 sock, port, buf, size, nmemb, flag);
+
+	if (size != 2)
+		printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size,
+			port);
+	if (size == 9)
+		printk("m32r_cfc: ioread_word :insw \n");
+
+	addr = pcc_port2addr(port, 2);
+	if (!addr) {
+		printk("m32r_cfc:ioread_word null port :%#lx\n",port);
+		return;
+	}
+	debug(3, "m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
+
+	spin_lock_irqsave(&pcc_lock, flags);
+	/* read Word */
+	while (nmemb--)
+		*bp++ = readw(addr);
+	spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
+	size_t nmemb, int flag)
+{
+	u_long addr;
+	unsigned char *bp = (unsigned char *)buf;
+	unsigned long flags;
+
+	debug(3, "m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
+		 "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+		 sock, port, buf, size, nmemb, flag);
+
+	/* write Byte */
+	addr = pcc_port2addr(port, 1);
+	if (!addr) {
+		printk("m32r_cfc:iowrite_byte null port:%#lx\n",port);
+		return;
+	}
+	debug(3, "m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
+
+	spin_lock_irqsave(&pcc_lock, flags);
+	while (nmemb--)
+		writeb(*bp++, addr);
+	spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
+	size_t nmemb, int flag)
+{
+	u_long addr;
+	unsigned short *bp = (unsigned short *)buf;
+	unsigned long flags;
+
+	debug(3, "m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
+		 "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+		 sock, port, buf, size, nmemb, flag);
+
+	if(size != 2)
+		printk("m32r_cfc: iowrite_word :illigal size %u : %#lx\n",
+			size, port);
+	if(size == 9)
+		printk("m32r_cfc: iowrite_word :outsw \n");
+
+	addr = pcc_port2addr(port, 2);
+	if (!addr) {
+		printk("m32r_cfc:iowrite_word null addr :%#lx\n",port);
+		return;
+	}
+#if 1
+	if (addr & 1) {
+		printk("m32r_cfc:iowrite_word port addr (%#lx):%#lx\n", port,
+			addr);
+		return;
+	}
+#endif
+	debug(3, "m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
+
+	spin_lock_irqsave(&pcc_lock, flags);
+	while (nmemb--)
+		writew(*bp++, addr);
+	spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+/*====================================================================*/
+
+#define IS_REGISTERED		0x2000
+#define IS_ALIVE		0x8000
+
+typedef struct pcc_t {
+	char			*name;
+	u_short			flags;
+} pcc_t;
+
+static pcc_t pcc[] = {
+#if !defined(CONFIG_PLAT_USRV)
+	{ "m32r_cfc", 0 }, { "", 0 },
+#else	/* CONFIG_PLAT_USRV */
+	{ "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "m32r_cfc", 0 },
+	{ "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "", 0 },
+#endif	/* CONFIG_PLAT_USRV */
+};
+
+static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *);
+
+/*====================================================================*/
+
+static struct timer_list poll_timer;
+
+static unsigned int pcc_get(u_short sock, unsigned int reg)
+{
+	unsigned int val = inw(reg);
+	debug(3, "m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
+	return val;
+}
+
+
+static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
+{
+	outw(data, reg);
+	debug(3, "m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
+}
+
+/*======================================================================
+
+	See if a card is present, powered up, in IO mode, and already
+	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)
+{
+	unsigned int stat;
+
+	debug(3, "m32r_cfc: is_alive:\n");
+
+	printk("CF: ");
+	stat = pcc_get(sock, (unsigned int)PLD_CFSTS);
+	if (!stat)
+		printk("No ");
+	printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat);
+	debug(3, "m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
+
+	return 0;
+}
+
+static void add_pcc_socket(ulong base, int irq, ulong mapaddr, kio_addr_t ioaddr)
+{
+	pcc_socket_t *t = &socket[pcc_sockets];
+
+	debug(3, "m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
+		 "mapaddr=%#lx, ioaddr=%08x\n",
+		 base, irq, mapaddr, ioaddr);
+
+	/* add sockets */
+	t->ioaddr = ioaddr;
+	t->mapaddr = mapaddr;
+#if !defined(CONFIG_PLAT_USRV)
+	t->base = 0;
+	t->flags = 0;
+	t->cs_irq1 = irq;		// insert irq
+	t->cs_irq2 = irq + 1;		// eject irq
+#else	/* CONFIG_PLAT_USRV */
+	t->base = base;
+	t->flags = 0;
+	t->cs_irq1 = 0;			// insert irq
+	t->cs_irq2 = 0;			// eject irq
+#endif	/* CONFIG_PLAT_USRV */
+
+	if (is_alive(pcc_sockets))
+		t->flags |= IS_ALIVE;
+
+	/* add pcc */
+#if !defined(CONFIG_PLAT_USRV)
+	request_region((unsigned int)PLD_CFRSTCR, 0x20, "m32r_cfc");
+#else	/* CONFIG_PLAT_USRV */
+	{
+		unsigned int reg_base;
+
+		reg_base = (unsigned int)PLD_CFRSTCR;
+		reg_base |= pcc_sockets << 8;
+		request_region(reg_base, 0x20, "m32r_cfc");
+	}
+#endif	/* CONFIG_PLAT_USRV */
+	printk(KERN_INFO "  %s ", pcc[pcc_sockets].name);
+	printk("pcc at 0x%08lx\n", t->base);
+
+	/* Update socket interrupt information, capabilities */
+	t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
+	t->socket.map_size = M32R_PCC_MAPSIZE;
+	t->socket.io_offset = ioaddr;	/* use for io access offset */
+	t->socket.irq_mask = 0;
+#if !defined(CONFIG_PLAT_USRV)
+	t->socket.pci_irq = PLD_IRQ_CFIREQ ;	/* card interrupt */
+#else	/* CONFIG_PLAT_USRV */
+	t->socket.pci_irq = PLD_IRQ_CF0 + pcc_sockets;
+#endif	/* CONFIG_PLAT_USRV */
+
+#ifndef CONFIG_PLAT_USRV
+	/* insert interrupt */
+	request_irq(irq, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
+	/* eject interrupt */
+	request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
+
+	debug(3, "m32r_cfc: enable CFMSK, RDYSEL\n");
+	pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01);
+#endif	/* CONFIG_PLAT_USRV */
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+	pcc_set(pcc_sockets, (unsigned int)PLD_CFCR1, 0x0200);
+#endif
+	pcc_sockets++;
+
+	return;
+}
+
+
+/*====================================================================*/
+
+static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	int i;
+	u_int events = 0;
+	int handled = 0;
+
+	debug(3, "m32r_cfc: pcc_interrupt: irq=%d, dev=%p, regs=%p\n",
+		irq, dev, regs);
+	for (i = 0; i < pcc_sockets; i++) {
+		if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq)
+			continue;
+
+		handled = 1;
+		debug(3, "m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
+			i, irq);
+		events |= SS_DETECT;	/* insert or eject */
+		if (events)
+			pcmcia_parse_events(&socket[i].socket, events);
+	}
+	debug(3, "m32r_cfc: pcc_interrupt: done\n");
+
+	return IRQ_RETVAL(handled);
+} /* pcc_interrupt */
+
+static void pcc_interrupt_wrapper(u_long data)
+{
+	debug(3, "m32r_cfc: pcc_interrupt_wrapper:\n");
+	pcc_interrupt(0, NULL, NULL);
+	init_timer(&poll_timer);
+	poll_timer.expires = jiffies + poll_interval;
+	add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+static int _pcc_get_status(u_short sock, u_int *value)
+{
+	u_int status;
+
+	debug(3, "m32r_cfc: _pcc_get_status:\n");
+	status = pcc_get(sock, (unsigned int)PLD_CFSTS);
+	*value = (status) ? SS_DETECT : 0;
+ 	debug(3, "m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+	if ( status ) {
+		/* enable CF power */
+		status = inw((unsigned int)PLD_CPCR);
+		if (!(status & PLD_CPCR_CF)) {
+			debug(3, "m32r_cfc: _pcc_get_status: "
+				 "power on (CPCR=0x%08x)\n", status);
+			status |= PLD_CPCR_CF;
+			outw(status, (unsigned int)PLD_CPCR);
+			udelay(100);
+		}
+		*value |= SS_POWERON;
+
+		pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);/* enable buffer */
+		udelay(100);
+
+		*value |= SS_READY; 		/* always ready */
+		*value |= SS_3VCARD;
+	} else {
+		/* disable CF power */
+		status = inw((unsigned int)PLD_CPCR);
+		status &= ~PLD_CPCR_CF;
+		outw(status, (unsigned int)PLD_CPCR);
+		udelay(100);
+		debug(3, "m32r_cfc: _pcc_get_status: "
+			 "power off (CPCR=0x%08x)\n", status);
+	}
+#elif defined(CONFIG_PLAT_MAPPI2)
+	if ( status ) {
+		status = pcc_get(sock, (unsigned int)PLD_CPCR);
+		if (status == 0) { /* power off */
+			pcc_set(sock, (unsigned int)PLD_CPCR, 1);
+			pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); /* force buffer off for ZA-36 */
+			udelay(50);
+		}
+		status = pcc_get(sock, (unsigned int)PLD_CFBUFCR);
+		if (status != 0) { /* buffer off */
+			pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);
+			udelay(50);
+			pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0101);
+			udelay(25); /* for IDE reset */
+			pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0100);
+			mdelay(2);  /* for IDE reset */
+		} else {
+			*value |= SS_POWERON;
+			*value |= SS_READY;
+		}
+	}
+#else
+#error no platform configuration
+#endif
+	debug(3, "m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
+		 sock, *value);
+	return 0;
+} /* _get_status */
+
+/*====================================================================*/
+
+static int _pcc_get_socket(u_short sock, socket_state_t *state)
+{
+//	pcc_socket_t *t = &socket[sock];
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+	state->flags = 0;
+	state->csc_mask = SS_DETECT;
+	state->csc_mask |= SS_READY;
+	state->io_irq = 0;
+	state->Vcc = 33;	/* 3.3V fixed */
+	state->Vpp = 33;
+#endif
+	debug(3, "m32r_cfc: 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);
+	return 0;
+} /* _get_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_socket(u_short sock, socket_state_t *state)
+{
+#if defined(CONFIG_PLAT_MAPPI2)
+	u_long reg = 0;
+#endif
+	debug(3, "m32r_cfc: 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);
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+	if (state->Vcc) {
+		if ((state->Vcc != 50) && (state->Vcc != 33))
+			return -EINVAL;
+		/* accept 5V and 3.3V */
+	}
+#elif defined(CONFIG_PLAT_MAPPI2)
+	if (state->Vcc) {
+		/*
+		 * 5V only
+		 */
+		if (state->Vcc == 50) {
+			reg |= PCCSIGCR_VEN;
+		} else {
+			return -EINVAL;
+		}
+	}
+#endif
+
+	if (state->flags & SS_RESET) {
+		debug(3, ":RESET\n");
+		pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101);
+	}else{
+		pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100);
+	}
+	if (state->flags & SS_OUTPUT_ENA){
+		debug(3, ":OUTPUT_ENA\n");
+		/* bit clear */
+		pcc_set(sock,(unsigned int)PLD_CFBUFCR,0);
+	} else {
+		pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
+	}
+
+#ifdef DEBUG
+	if(state->flags & SS_IOCARD){
+		debug(3, ":IOCARD");
+	}
+	if (state->flags & SS_PWR_AUTO) {
+		debug(3, ":PWR_AUTO");
+	}
+	if (state->csc_mask & SS_DETECT)
+		debug(3, ":csc-SS_DETECT");
+	if (state->flags & SS_IOCARD) {
+		if (state->csc_mask & SS_STSCHG)
+			debug(3, ":STSCHG");
+	} else {
+		if (state->csc_mask & SS_BATDEAD)
+			debug(3, ":BATDEAD");
+		if (state->csc_mask & SS_BATWARN)
+			debug(3, ":BATWARN");
+		if (state->csc_mask & SS_READY)
+			debug(3, ":READY");
+	}
+	debug(3, "\n");
+#endif
+	return 0;
+} /* _set_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+	u_char map;
+
+	debug(3, "m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+		  "%#lx-%#lx)\n", sock, io->map, io->flags,
+		  io->speed, io->start, io->stop);
+	map = io->map;
+
+	return 0;
+} /* _set_io_map */
+
+/*====================================================================*/
+
+static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+
+	u_char map = mem->map;
+	u_long addr;
+	pcc_socket_t *t = &socket[sock];
+
+	debug(3, "m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+		 "%#lx, %#x)\n", sock, map, mem->flags,
+		 mem->speed, mem->static_start, mem->card_start);
+
+	/*
+	 * sanity check
+	 */
+	if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
+		return -EINVAL;
+	}
+
+	/*
+	 * de-activate
+	 */
+	if ((mem->flags & MAP_ACTIVE) == 0) {
+		t->current_space = as_none;
+		return 0;
+	}
+
+	/*
+	 * Set mode
+	 */
+	if (mem->flags & MAP_ATTRIB) {
+		t->current_space = as_attr;
+	} else {
+		t->current_space = as_comm;
+	}
+
+	/*
+	 * Set address
+	 */
+	addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
+	mem->static_start = addr + mem->card_start;
+
+	return 0;
+
+} /* _set_mem_map */
+
+#if 0 /* driver model ordering issue */
+/*======================================================================
+
+	Routines for accessing socket information and register dumps via
+	/proc/bus/pccard/...
+
+======================================================================*/
+
+static ssize_t show_info(struct class_device *class_dev, char *buf)
+{
+	pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
+		socket.dev);
+
+	return sprintf(buf, "type:     %s\nbase addr:    0x%08lx\n",
+		pcc[s->type].name, s->base);
+}
+
+static ssize_t show_exca(struct class_device *class_dev, char *buf)
+{
+	/* FIXME */
+
+	return 0;
+}
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
+static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
+#endif
+
+/*====================================================================*/
+
+/* this is horribly ugly... proper locking needs to be done here at
+ * some time... */
+#define LOCKED(x) do {					\
+	int retval;					\
+	unsigned long flags;				\
+	spin_lock_irqsave(&pcc_lock, flags);		\
+	retval = x;					\
+	spin_unlock_irqrestore(&pcc_lock, flags);	\
+	return retval;					\
+} while (0)
+
+
+static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		debug(3, "m32r_cfc: pcc_get_status: sock(%d) -EINVAL\n", sock);
+		*value = 0;
+		return -EINVAL;
+	}
+	debug(3, "m32r_cfc: pcc_get_status: sock(%d)\n", sock);
+	LOCKED(_pcc_get_status(sock, value));
+}
+
+static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		debug(3, "m32r_cfc: pcc_get_socket: sock(%d) -EINVAL\n", sock);
+		return -EINVAL;
+	}
+	debug(3, "m32r_cfc: pcc_get_socket: sock(%d)\n", sock);
+	LOCKED(_pcc_get_socket(sock, state));
+}
+
+static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		debug(3, "m32r_cfc: pcc_set_socket: sock(%d) -EINVAL\n", sock);
+		return -EINVAL;
+	}
+	debug(3, "m32r_cfc: pcc_set_socket: sock(%d)\n", sock);
+	LOCKED(_pcc_set_socket(sock, state));
+}
+
+static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		debug(3, "m32r_cfc: pcc_set_io_map: sock(%d) -EINVAL\n", sock);
+		return -EINVAL;
+	}
+	debug(3, "m32r_cfc: pcc_set_io_map: sock(%d)\n", sock);
+	LOCKED(_pcc_set_io_map(sock, io));
+}
+
+static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
+		return -EINVAL;
+	}
+	debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d)\n", sock);
+	LOCKED(_pcc_set_mem_map(sock, mem));
+}
+
+static int pcc_init(struct pcmcia_socket *s)
+{
+	debug(3, "m32r_cfc: pcc_init()\n");
+	return 0;
+}
+
+static struct pccard_operations pcc_operations = {
+	.init			= pcc_init,
+	.get_status		= pcc_get_status,
+	.get_socket		= pcc_get_socket,
+	.set_socket		= pcc_set_socket,
+	.set_io_map		= pcc_set_io_map,
+	.set_mem_map		= pcc_set_mem_map,
+};
+
+/*====================================================================*/
+
+static int m32r_pcc_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int m32r_pcc_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+
+static struct device_driver pcc_driver = {
+	.name = "cfc",
+	.bus = &platform_bus_type,
+	.suspend = m32r_pcc_suspend,
+	.resume = m32r_pcc_resume,
+};
+
+static struct platform_device pcc_device = {
+	.name = "cfc",
+	.id = 0,
+};
+
+/*====================================================================*/
+
+static int __init init_m32r_pcc(void)
+{
+	int i, ret;
+
+	ret = driver_register(&pcc_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_device_register(&pcc_device);
+	if (ret){
+		driver_unregister(&pcc_driver);
+		return ret;
+	}
+
+#if defined(CONFIG_PLAT_MAPPI2)
+	pcc_set(0, (unsigned int)PLD_CFCR0, 0x0f0f);
+	pcc_set(0, (unsigned int)PLD_CFCR1, 0x0200);
+#endif
+
+	pcc_sockets = 0;
+
+#if !defined(CONFIG_PLAT_USRV)
+	add_pcc_socket(M32R_PCC0_BASE, PLD_IRQ_CFC_INSERT, CFC_ATTR_MAPBASE,
+		       CFC_IOPORT_BASE);
+#else	/* CONFIG_PLAT_USRV */
+	{
+		ulong base, mapaddr;
+		kio_addr_t ioaddr;
+
+		for (i = 0 ; i < M32R_MAX_PCC ; i++) {
+			base = (ulong)PLD_CFRSTCR;
+			base = base | (i << 8);
+			ioaddr = (i + 1) << 12;
+			mapaddr = CFC_ATTR_MAPBASE | (i << 20);
+			add_pcc_socket(base, 0, mapaddr, ioaddr);
+		}
+	}
+#endif	/* CONFIG_PLAT_USRV */
+
+	if (pcc_sockets == 0) {
+		printk("socket is not found.\n");
+		platform_device_unregister(&pcc_device);
+		driver_unregister(&pcc_driver);
+		return -ENODEV;
+	}
+
+	/* Set up interrupt handler(s) */
+
+	for (i = 0 ; i < pcc_sockets ; i++) {
+		socket[i].socket.dev.dev = &pcc_device.dev;
+		socket[i].socket.ops = &pcc_operations;
+		socket[i].socket.resource_ops = &pccard_static_ops;
+		socket[i].socket.owner = THIS_MODULE;
+		socket[i].number = i;
+		ret = pcmcia_register_socket(&socket[i].socket);
+		if (!ret)
+			socket[i].flags |= IS_REGISTERED;
+
+#if 0	/* driver model ordering issue */
+		class_device_create_file(&socket[i].socket.dev,
+					 &class_device_attr_info);
+		class_device_create_file(&socket[i].socket.dev,
+					 &class_device_attr_exca);
+#endif
+	}
+
+	/* Finally, schedule a polling interrupt */
+	if (poll_interval != 0) {
+		poll_timer.function = pcc_interrupt_wrapper;
+		poll_timer.data = 0;
+		init_timer(&poll_timer);
+		poll_timer.expires = jiffies + poll_interval;
+		add_timer(&poll_timer);
+	}
+
+	return 0;
+} /* init_m32r_pcc */
+
+static void __exit exit_m32r_pcc(void)
+{
+	int i;
+
+	for (i = 0; i < pcc_sockets; i++)
+		if (socket[i].flags & IS_REGISTERED)
+			pcmcia_unregister_socket(&socket[i].socket);
+
+	platform_device_unregister(&pcc_device);
+	if (poll_interval != 0)
+		del_timer_sync(&poll_timer);
+
+	driver_unregister(&pcc_driver);
+} /* exit_m32r_pcc */
+
+module_init(init_m32r_pcc);
+module_exit(exit_m32r_pcc);
+MODULE_LICENSE("Dual MPL/GPL");
+/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_cfc.h b/drivers/pcmcia/m32r_cfc.h
new file mode 100644
index 0000000..17c1db7
--- /dev/null
+++ b/drivers/pcmcia/m32r_cfc.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2001 by Hiroyuki Kondo
+ */
+
+#if !defined(CONFIG_M32R_CFC_NUM)
+#define M32R_MAX_PCC	2
+#else
+#define M32R_MAX_PCC	CONFIG_M32R_CFC_NUM
+#endif
+
+/*
+ * M32R PC Card Controler
+ */
+#define M32R_PCC0_BASE        0x00ef7000
+#define M32R_PCC1_BASE        0x00ef7020
+
+/*
+ * Register offsets
+ */
+#define PCCR            0x00
+#define PCADR           0x04
+#define PCMOD           0x08
+#define PCIRC           0x0c
+#define PCCSIGCR        0x10
+#define PCATCR          0x14
+
+/*
+ * PCCR
+ */
+#define PCCR_PCEN       (1UL<<(31-31))
+
+/*
+ * PCIRC
+ */
+#define PCIRC_BWERR     (1UL<<(31-7))
+#define PCIRC_CDIN1     (1UL<<(31-14))
+#define PCIRC_CDIN2     (1UL<<(31-15))
+#define PCIRC_BEIEN     (1UL<<(31-23))
+#define PCIRC_CIIEN     (1UL<<(31-30))
+#define PCIRC_COIEN     (1UL<<(31-31))
+
+/*
+ * PCCSIGCR
+ */
+#define PCCSIGCR_SEN    (1UL<<(31-3))
+#define PCCSIGCR_VEN    (1UL<<(31-7))
+#define PCCSIGCR_CRST   (1UL<<(31-15))
+#define PCCSIGCR_COCR   (1UL<<(31-31))
+
+/*
+ *
+ */
+#define PCMOD_AS_ATTRIB	(1UL<<(31-19))
+#define PCMOD_AS_IO	(1UL<<(31-18))
+
+#define PCMOD_CBSZ	(1UL<<(31-23)) /* set for 8bit */
+
+#define PCMOD_DBEX	(1UL<<(31-31)) /* set for excahnge */
+
+/*
+ * M32R PCC Map addr
+ */
+
+#define M32R_PCC0_MAPBASE        0x14000000
+#define M32R_PCC1_MAPBASE        0x16000000
+
+#define M32R_PCC_MAPMAX		 0x02000000
+
+#define M32R_PCC_MAPSIZE	 0x00001000 /* XXX */
+#define M32R_PCC_MAPMASK     	(~(M32R_PCC_MAPMAX-1))
+
+#define CFC_IOPORT_BASE		0x1000
+
+#if !defined(CONFIG_PLAT_USRV)
+#define CFC_ATTR_MAPBASE        0x0c014000
+#define CFC_IO_MAPBASE_BYTE     0xac012000
+#define CFC_IO_MAPBASE_WORD     0xac002000
+#else	/* CONFIG_PLAT_USRV */
+#define CFC_ATTR_MAPBASE	0x04014000
+#define CFC_IO_MAPBASE_BYTE	0xa4012000
+#define CFC_IO_MAPBASE_WORD	0xa4002000
+#endif	/* CONFIG_PLAT_USRV */
+
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
new file mode 100644
index 0000000..cafba6f
--- /dev/null
+++ b/drivers/pcmcia/m32r_pcc.c
@@ -0,0 +1,811 @@
+/*
+ *  drivers/pcmcia/m32r_pcc.c
+ *
+ *  Device driver for the PCMCIA functionality of M32R.
+ *
+ *  Copyright (c) 2001, 2002, 2003, 2004
+ *    Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/addrspace.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+/* XXX: should be moved into asm/irq.h */
+#define PCC0_IRQ 24
+#define PCC1_IRQ 25
+
+#include "m32r_pcc.h"
+
+#define CHAOS_PCC_DEBUG
+#ifdef CHAOS_PCC_DEBUG
+	static volatile u_short dummy_readbuf;
+#endif
+
+#define PCC_DEBUG_DBEX
+
+#ifdef DEBUG
+static int m32r_pcc_debug;
+module_param(m32r_pcc_debug, int, 0644);
+#define debug(lvl, fmt, arg...) do {				\
+	if (m32r_pcc_debug > (lvl))				\
+		printk(KERN_DEBUG "m32r_pcc: " fmt , ## arg);	\
+} while (0)
+#else
+#define debug(n, args...) do { } while (0)
+#endif
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+
+typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
+
+typedef struct pcc_socket {
+	u_short			type, flags;
+	struct pcmcia_socket	socket;
+	unsigned int		number;
+ 	kio_addr_t		ioaddr;
+	u_long			mapaddr;
+	u_long			base;	/* PCC register base */
+	u_char			cs_irq, intr;
+	pccard_io_map		io_map[MAX_IO_WIN];
+	pccard_mem_map		mem_map[MAX_WIN];
+	u_char			io_win;
+	u_char			mem_win;
+	pcc_as_t		current_space;
+	u_char			last_iodbex;
+#ifdef CHAOS_PCC_DEBUG
+	u_char			last_iosize;
+#endif
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *proc;
+#endif
+} pcc_socket_t;
+
+static int pcc_sockets = 0;
+static pcc_socket_t socket[M32R_MAX_PCC] = {
+	{ 0, }, /* ... */
+};
+
+/*====================================================================*/
+
+static unsigned int pcc_get(u_short, unsigned int);
+static void pcc_set(u_short, unsigned int , unsigned int );
+
+static DEFINE_SPINLOCK(pcc_lock);
+
+void pcc_iorw(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int wr, int flag)
+{
+	u_long addr;
+	u_long flags;
+	int need_ex;
+#ifdef PCC_DEBUG_DBEX
+	int _dbex;
+#endif
+	pcc_socket_t *t = &socket[sock];
+#ifdef CHAOS_PCC_DEBUG
+	int map_changed = 0;
+#endif
+
+	/* Need lock ? */
+	spin_lock_irqsave(&pcc_lock, flags);
+
+	/*
+	 * Check if need dbex
+	 */
+	need_ex = (size > 1 && flag == 0) ? PCMOD_DBEX : 0;
+#ifdef PCC_DEBUG_DBEX
+	_dbex = need_ex;
+	need_ex = 0;
+#endif
+
+	/*
+	 * calculate access address
+	 */
+	addr = t->mapaddr + port - t->ioaddr + KSEG1; /* XXX */
+
+	/*
+	 * Check current mapping
+	 */
+	if (t->current_space != as_io || t->last_iodbex != need_ex) {
+
+		u_long cbsz;
+
+		/*
+		 * Disable first
+		 */
+		pcc_set(sock, PCCR, 0);
+
+		/*
+		 * Set mode and io address
+		 */
+		cbsz = (t->flags & MAP_16BIT) ? 0 : PCMOD_CBSZ;
+		pcc_set(sock, PCMOD, PCMOD_AS_IO | cbsz | need_ex);
+		pcc_set(sock, PCADR, addr & 0x1ff00000);
+
+		/*
+		 * Enable and read it
+		 */
+		pcc_set(sock, PCCR, 1);
+
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+		map_changed = (t->current_space == as_attr && size == 2); /* XXX */
+#else
+		map_changed = 1;
+#endif
+#endif
+		t->current_space = as_io;
+	}
+
+	/*
+	 * access to IO space
+	 */
+	if (size == 1) {
+		/* Byte */
+		unsigned char *bp = (unsigned char *)buf;
+
+#ifdef CHAOS_DEBUG
+		if (map_changed) {
+			dummy_readbuf = readb(addr);
+		}
+#endif
+		if (wr) {
+			/* write Byte */
+			while (nmemb--) {
+				writeb(*bp++, addr);
+			}
+		} else {
+			/* read Byte */
+			while (nmemb--) {
+	    		*bp++ = readb(addr);
+			}
+		}
+	} else {
+		/* Word */
+		unsigned short *bp = (unsigned short *)buf;
+
+#ifdef CHAOS_PCC_DEBUG
+		if (map_changed) {
+			dummy_readbuf = readw(addr);
+		}
+#endif
+		if (wr) {
+			/* write Word */
+			while (nmemb--) {
+#ifdef PCC_DEBUG_DBEX
+				if (_dbex) {
+					unsigned char *cp = (unsigned char *)bp;
+					unsigned short tmp;
+					tmp = cp[1] << 8 | cp[0];
+					writew(tmp, addr);
+					bp++;
+				} else
+#endif
+				writew(*bp++, addr);
+	    	}
+	    } else {
+	    	/* read Word */
+	    	while (nmemb--) {
+#ifdef  PCC_DEBUG_DBEX
+				if (_dbex) {
+					unsigned char *cp = (unsigned char *)bp;
+					unsigned short tmp;
+					tmp = readw(addr);
+					cp[0] = tmp & 0xff;
+					cp[1] = (tmp >> 8) & 0xff;
+					bp++;
+				} else
+#endif
+				*bp++ = readw(addr);
+	    	}
+	    }
+	}
+
+#if 1
+	/* addr is no longer used */
+	if ((addr = pcc_get(sock, PCIRC)) & PCIRC_BWERR) {
+	  printk("m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit\n",
+			 port, size * 8);
+	  pcc_set(sock, PCIRC, addr);
+	}
+#endif
+	/*
+	 * save state
+	 */
+	t->last_iosize = size;
+	t->last_iodbex = need_ex;
+
+	/* Need lock ? */
+
+	spin_unlock_irqrestore(&pcc_lock,flags);
+
+	return;
+}
+
+void pcc_ioread(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
+	pcc_iorw(sock, port, buf, size, nmemb, 0, flag);
+}
+
+void pcc_iowrite(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
+    pcc_iorw(sock, port, buf, size, nmemb, 1, flag);
+}
+
+/*====================================================================*/
+
+#define IS_REGISTERED		0x2000
+#define IS_ALIVE		0x8000
+
+typedef struct pcc_t {
+	char			*name;
+	u_short			flags;
+} pcc_t;
+
+static pcc_t pcc[] = {
+	{ "xnux2", 0 }, { "xnux2", 0 },
+};
+
+static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *);
+
+/*====================================================================*/
+
+static struct timer_list poll_timer;
+
+static unsigned int pcc_get(u_short sock, unsigned int reg)
+{
+	return inl(socket[sock].base + reg);
+}
+
+
+static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
+{
+  	outl(data, socket[sock].base + reg);
+}
+
+/*======================================================================
+
+	See if a card is present, powered up, in IO mode, and already
+	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)
+{
+	unsigned int stat;
+	unsigned int f;
+
+	stat = pcc_get(sock, PCIRC);
+	f = (stat & (PCIRC_CDIN1 | PCIRC_CDIN2)) >> 16;
+	if(!f){
+		printk("m32r_pcc: No Card is detected at socket %d : stat = 0x%08x\n",stat,sock);
+		return 0;
+	}
+	if(f!=3)
+		printk("m32r_pcc: Insertion fail (%.8x) at socket %d\n",stat,sock);
+	else
+		printk("m32r_pcc: Card is Inserted at socket %d(%.8x)\n",sock,stat);
+	return 0;
+}
+
+static void add_pcc_socket(ulong base, int irq, ulong mapaddr, kio_addr_t ioaddr)
+{
+  	pcc_socket_t *t = &socket[pcc_sockets];
+
+	/* add sockets */
+	t->ioaddr = ioaddr;
+	t->mapaddr = mapaddr;
+	t->base = base;
+#ifdef CHAOS_PCC_DEBUG
+	t->flags = MAP_16BIT;
+#else
+	t->flags = 0;
+#endif
+	if (is_alive(pcc_sockets))
+		t->flags |= IS_ALIVE;
+
+	/* add pcc */
+	if (t->base > 0) {
+		request_region(t->base, 0x20, "m32r-pcc");
+	}
+
+	printk(KERN_INFO "  %s ", pcc[pcc_sockets].name);
+	printk("pcc at 0x%08lx\n", t->base);
+
+	/* Update socket interrupt information, capabilities */
+	t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
+	t->socket.map_size = M32R_PCC_MAPSIZE;
+	t->socket.io_offset = ioaddr;	/* use for io access offset */
+	t->socket.irq_mask = 0;
+	t->socket.pci_irq = 2 + pcc_sockets; /* XXX */
+
+	request_irq(irq, pcc_interrupt, 0, "m32r-pcc", pcc_interrupt);
+
+	pcc_sockets++;
+
+	return;
+}
+
+
+/*====================================================================*/
+
+static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	int i, j, irc;
+	u_int events, active;
+	int handled = 0;
+
+	debug(4, "m32r: pcc_interrupt(%d)\n", irq);
+
+	for (j = 0; j < 20; j++) {
+		active = 0;
+		for (i = 0; i < pcc_sockets; i++) {
+			if ((socket[i].cs_irq != irq) &&
+				(socket[i].socket.pci_irq != irq))
+				continue;
+			handled = 1;
+			irc = pcc_get(i, PCIRC);
+			irc >>=16;
+			debug(2, "m32r-pcc:interrput: socket %d pcirc 0x%02x ", i, irc);
+			if (!irc)
+				continue;
+
+			events = (irc) ? SS_DETECT : 0;
+			events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0;
+			debug(2, " event 0x%02x\n", events);
+
+			if (events)
+				pcmcia_parse_events(&socket[i].socket, events);
+
+			active |= events;
+			active = 0;
+		}
+		if (!active) break;
+	}
+	if (j == 20)
+		printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n");
+
+	debug(4, "m32r-pcc: interrupt done\n");
+
+	return IRQ_RETVAL(handled);
+} /* pcc_interrupt */
+
+static void pcc_interrupt_wrapper(u_long data)
+{
+	pcc_interrupt(0, NULL, NULL);
+	init_timer(&poll_timer);
+	poll_timer.expires = jiffies + poll_interval;
+	add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+static int _pcc_get_status(u_short sock, u_int *value)
+{
+	u_int status;
+
+	status = pcc_get(sock,PCIRC);
+	*value = ((status & PCIRC_CDIN1) && (status & PCIRC_CDIN2))
+		? SS_DETECT : 0;
+
+	status = pcc_get(sock,PCCR);
+
+#if 0
+	*value |= (status & PCCR_PCEN) ? SS_READY : 0;
+#else
+	*value |= SS_READY; /* XXX: always */
+#endif
+
+	status = pcc_get(sock,PCCSIGCR);
+	*value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0;
+
+	debug(3, "m32r-pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
+	return 0;
+} /* _get_status */
+
+/*====================================================================*/
+
+static int _pcc_get_socket(u_short sock, socket_state_t *state)
+{
+	debug(3, "m32r-pcc: 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);
+	return 0;
+} /* _get_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_socket(u_short sock, socket_state_t *state)
+{
+	u_long reg = 0;
+
+	debug(3, "m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+		  "io_irq %d, csc_mask %#2.2x)", sock, state->flags,
+		  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+	if (state->Vcc) {
+		/*
+		 * 5V only
+		 */
+		if (state->Vcc == 50) {
+			reg |= PCCSIGCR_VEN;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	if (state->flags & SS_RESET) {
+		debug(3, ":RESET\n");
+		reg |= PCCSIGCR_CRST;
+	}
+	if (state->flags & SS_OUTPUT_ENA){
+		debug(3, ":OUTPUT_ENA\n");
+		/* bit clear */
+	} else {
+		reg |= PCCSIGCR_SEN;
+	}
+
+	pcc_set(sock,PCCSIGCR,reg);
+
+#ifdef DEBUG
+	if(state->flags & SS_IOCARD){
+		debug(3, ":IOCARD");
+	}
+	if (state->flags & SS_PWR_AUTO) {
+		debug(3, ":PWR_AUTO");
+	}
+	if (state->csc_mask & SS_DETECT)
+		debug(3, ":csc-SS_DETECT");
+	if (state->flags & SS_IOCARD) {
+		if (state->csc_mask & SS_STSCHG)
+			debug(3, ":STSCHG");
+	} else {
+		if (state->csc_mask & SS_BATDEAD)
+			debug(3, ":BATDEAD");
+		if (state->csc_mask & SS_BATWARN)
+			debug(3, ":BATWARN");
+		if (state->csc_mask & SS_READY)
+			debug(3, ":READY");
+	}
+	debug(3, "\n");
+#endif
+	return 0;
+} /* _set_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+	u_char map;
+
+	debug(3, "m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+		  "%#lx-%#lx)\n", sock, io->map, io->flags,
+		  io->speed, io->start, io->stop);
+	map = io->map;
+
+	return 0;
+} /* _set_io_map */
+
+/*====================================================================*/
+
+static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+
+	u_char map = mem->map;
+	u_long mode;
+	u_long addr;
+	pcc_socket_t *t = &socket[sock];
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+	pcc_as_t last = t->current_space;
+#endif
+#endif
+
+	debug(3, "m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+		 "%#lx,  %#x)\n", sock, map, mem->flags,
+		 mem->speed, mem->static_start, mem->card_start);
+
+	/*
+	 * sanity check
+	 */
+	if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
+		return -EINVAL;
+	}
+
+	/*
+	 * de-activate
+	 */
+	if ((mem->flags & MAP_ACTIVE) == 0) {
+		t->current_space = as_none;
+		return 0;
+	}
+
+	/*
+	 * Disable first
+	 */
+	pcc_set(sock, PCCR, 0);
+
+	/*
+	 * Set mode
+	 */
+	if (mem->flags & MAP_ATTRIB) {
+		mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ;
+		t->current_space = as_attr;
+	} else {
+		mode = 0; /* common memory */
+		t->current_space = as_comm;
+	}
+	pcc_set(sock, PCMOD, mode);
+
+	/*
+	 * Set address
+	 */
+	addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
+	pcc_set(sock, PCADR, addr);
+
+	mem->static_start = addr + mem->card_start;
+
+	/*
+	 * Enable again
+	 */
+	pcc_set(sock, PCCR, 1);
+
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+	if (last != as_attr) {
+#else
+	if (1) {
+#endif
+		dummy_readbuf = *(u_char *)(addr + KSEG1);
+	}
+#endif
+
+	return 0;
+
+} /* _set_mem_map */
+
+#if 0 /* driver model ordering issue */
+/*======================================================================
+
+	Routines for accessing socket information and register dumps via
+	/proc/bus/pccard/...
+
+======================================================================*/
+
+static ssize_t show_info(struct class_device *class_dev, char *buf)
+{
+	pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
+		socket.dev);
+
+	return sprintf(buf, "type:     %s\nbase addr:    0x%08lx\n",
+		pcc[s->type].name, s->base);
+}
+
+static ssize_t show_exca(struct class_device *class_dev, char *buf)
+{
+	/* FIXME */
+
+	return 0;
+}
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
+static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
+#endif
+
+/*====================================================================*/
+
+/* this is horribly ugly... proper locking needs to be done here at
+ * some time... */
+#define LOCKED(x) do {					\
+	int retval;					\
+	unsigned long flags;				\
+	spin_lock_irqsave(&pcc_lock, flags);		\
+	retval = x;					\
+	spin_unlock_irqrestore(&pcc_lock, flags);	\
+	return retval;					\
+} while (0)
+
+
+static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE) {
+		*value = 0;
+		return -EINVAL;
+	}
+	LOCKED(_pcc_get_status(sock, value));
+}
+
+static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+	LOCKED(_pcc_get_socket(sock, state));
+}
+
+static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+
+	LOCKED(_pcc_set_socket(sock, state));
+}
+
+static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+	LOCKED(_pcc_set_io_map(sock, io));
+}
+
+static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+	unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+	if (socket[sock].flags & IS_ALIVE)
+		return -EINVAL;
+	LOCKED(_pcc_set_mem_map(sock, mem));
+}
+
+static int pcc_init(struct pcmcia_socket *s)
+{
+	debug(4, "m32r-pcc: init call\n");
+	return 0;
+}
+
+static struct pccard_operations pcc_operations = {
+	.init			= pcc_init,
+	.get_status		= pcc_get_status,
+	.get_socket		= pcc_get_socket,
+	.set_socket		= pcc_set_socket,
+	.set_io_map		= pcc_set_io_map,
+	.set_mem_map		= pcc_set_mem_map,
+};
+
+/*====================================================================*/
+
+static int m32r_pcc_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int m32r_pcc_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+
+static struct device_driver pcc_driver = {
+	.name = "pcc",
+	.bus = &platform_bus_type,
+	.suspend = m32r_pcc_suspend,
+	.resume = m32r_pcc_resume,
+};
+
+static struct platform_device pcc_device = {
+	.name = "pcc",
+	.id = 0,
+};
+
+/*====================================================================*/
+
+static int __init init_m32r_pcc(void)
+{
+	int i, ret;
+
+	ret = driver_register(&pcc_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_device_register(&pcc_device);
+	if (ret){
+		driver_unregister(&pcc_driver);
+		return ret;
+	}
+
+	printk(KERN_INFO "m32r PCC probe:\n");
+
+	pcc_sockets = 0;
+
+	add_pcc_socket(M32R_PCC0_BASE, PCC0_IRQ, M32R_PCC0_MAPBASE, 0x1000);
+
+#ifdef CONFIG_M32RPCC_SLOT2
+	add_pcc_socket(M32R_PCC1_BASE, PCC1_IRQ, M32R_PCC1_MAPBASE, 0x2000);
+#endif
+
+	if (pcc_sockets == 0) {
+		printk("socket is not found.\n");
+		platform_device_unregister(&pcc_device);
+		driver_unregister(&pcc_driver);
+		return -ENODEV;
+	}
+
+	/* Set up interrupt handler(s) */
+
+	for (i = 0 ; i < pcc_sockets ; i++) {
+		socket[i].socket.dev.dev = &pcc_device.dev;
+		socket[i].socket.ops = &pcc_operations;
+		socket[i].socket.resource_ops = &pccard_static_ops;
+		socket[i].socket.owner = THIS_MODULE;
+		socket[i].number = i;
+		ret = pcmcia_register_socket(&socket[i].socket);
+		if (!ret)
+			socket[i].flags |= IS_REGISTERED;
+
+#if 0	/* driver model ordering issue */
+		class_device_create_file(&socket[i].socket.dev,
+					 &class_device_attr_info);
+		class_device_create_file(&socket[i].socket.dev,
+					 &class_device_attr_exca);
+#endif
+	}
+
+	/* Finally, schedule a polling interrupt */
+	if (poll_interval != 0) {
+		poll_timer.function = pcc_interrupt_wrapper;
+		poll_timer.data = 0;
+		init_timer(&poll_timer);
+		poll_timer.expires = jiffies + poll_interval;
+		add_timer(&poll_timer);
+	}
+
+	return 0;
+} /* init_m32r_pcc */
+
+static void __exit exit_m32r_pcc(void)
+{
+	int i;
+
+	for (i = 0; i < pcc_sockets; i++)
+		if (socket[i].flags & IS_REGISTERED)
+			pcmcia_unregister_socket(&socket[i].socket);
+
+	platform_device_unregister(&pcc_device);
+	if (poll_interval != 0)
+		del_timer_sync(&poll_timer);
+
+	driver_unregister(&pcc_driver);
+} /* exit_m32r_pcc */
+
+module_init(init_m32r_pcc);
+module_exit(exit_m32r_pcc);
+MODULE_LICENSE("Dual MPL/GPL");
+/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_pcc.h b/drivers/pcmcia/m32r_pcc.h
new file mode 100644
index 0000000..e4fffe4
--- /dev/null
+++ b/drivers/pcmcia/m32r_pcc.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2001 by Hiroyuki Kondo
+ */
+
+#define M32R_MAX_PCC	2
+
+/*
+ * M32R PC Card Controler
+ */
+#define M32R_PCC0_BASE        0x00ef7000
+#define M32R_PCC1_BASE        0x00ef7020
+
+/*
+ * Register offsets
+ */
+#define PCCR            0x00
+#define PCADR           0x04
+#define PCMOD           0x08
+#define PCIRC           0x0c
+#define PCCSIGCR        0x10
+#define PCATCR          0x14
+
+/*
+ * PCCR
+ */
+#define PCCR_PCEN       (1UL<<(31-31))
+
+/*
+ * PCIRC
+ */
+#define PCIRC_BWERR     (1UL<<(31-7))
+#define PCIRC_CDIN1     (1UL<<(31-14))
+#define PCIRC_CDIN2     (1UL<<(31-15))
+#define PCIRC_BEIEN     (1UL<<(31-23))
+#define PCIRC_CIIEN     (1UL<<(31-30))
+#define PCIRC_COIEN     (1UL<<(31-31))
+
+/*
+ * PCCSIGCR
+ */
+#define PCCSIGCR_SEN    (1UL<<(31-3))
+#define PCCSIGCR_VEN    (1UL<<(31-7))
+#define PCCSIGCR_CRST   (1UL<<(31-15))
+#define PCCSIGCR_COCR   (1UL<<(31-31))
+
+/*
+ *
+ */
+#define PCMOD_AS_ATTRIB	(1UL<<(31-19))
+#define PCMOD_AS_IO	(1UL<<(31-18))
+
+#define PCMOD_CBSZ	(1UL<<(31-23)) /* set for 8bit */
+
+#define PCMOD_DBEX	(1UL<<(31-31)) /* set for excahnge */
+
+/*
+ * M32R PCC Map addr
+ */
+#define M32R_PCC0_MAPBASE        0x14000000
+#define M32R_PCC1_MAPBASE        0x16000000
+
+#define M32R_PCC_MAPMAX		 0x02000000
+
+#define M32R_PCC_MAPSIZE	 0x00001000 /* XXX */
+#define M32R_PCC_MAPMASK     	(~(M32R_PCC_MAPMAX-1))
diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h
new file mode 100644
index 0000000..b1f6e3d
--- /dev/null
+++ b/drivers/pcmcia/o2micro.h
@@ -0,0 +1,164 @@
+/*
+ * o2micro.h 1.13 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_O2MICRO_H
+#define _LINUX_O2MICRO_H
+
+#ifndef PCI_VENDOR_ID_O2
+#define PCI_VENDOR_ID_O2		0x1217
+#endif
+#ifndef PCI_DEVICE_ID_O2_6729
+#define PCI_DEVICE_ID_O2_6729		0x6729
+#endif
+#ifndef PCI_DEVICE_ID_O2_6730
+#define PCI_DEVICE_ID_O2_6730		0x673a
+#endif
+#ifndef PCI_DEVICE_ID_O2_6832
+#define PCI_DEVICE_ID_O2_6832		0x6832
+#endif
+#ifndef PCI_DEVICE_ID_O2_6836
+#define PCI_DEVICE_ID_O2_6836		0x6836
+#endif
+#ifndef PCI_DEVICE_ID_O2_6812
+#define PCI_DEVICE_ID_O2_6812		0x6872
+#endif
+
+/* Additional PCI configuration registers */
+
+#define O2_MUX_CONTROL		0x90	/* 32 bit */
+#define  O2_MUX_RING_OUT	0x0000000f
+#define  O2_MUX_SKTB_ACTV	0x000000f0
+#define  O2_MUX_SCTA_ACTV_ENA	0x00000100
+#define  O2_MUX_SCTB_ACTV_ENA	0x00000200
+#define  O2_MUX_SER_IRQ_ROUTE	0x0000e000
+#define  O2_MUX_SER_PCI		0x00010000
+
+#define  O2_MUX_SKTA_TURBO	0x000c0000	/* for 6833, 6860 */
+#define  O2_MUX_SKTB_TURBO	0x00300000
+#define  O2_MUX_AUX_VCC_3V	0x00400000
+#define  O2_MUX_PCI_VCC_5V	0x00800000
+#define  O2_MUX_PME_MUX		0x0f000000
+
+/* Additional ExCA registers */
+
+#define O2_MODE_A		0x38
+#define O2_MODE_A_2		0x26	/* for 6833B, 6860C */
+#define  O2_MODE_A_CD_PULSE	0x04
+#define  O2_MODE_A_SUSP_EDGE	0x08
+#define  O2_MODE_A_HOST_SUSP	0x10
+#define  O2_MODE_A_PWR_MASK	0x60
+#define  O2_MODE_A_QUIET	0x80
+
+#define O2_MODE_B		0x39
+#define O2_MODE_B_2		0x2e	/* for 6833B, 6860C */
+#define  O2_MODE_B_IDENT	0x03
+#define  O2_MODE_B_ID_BSTEP	0x00
+#define  O2_MODE_B_ID_CSTEP	0x01
+#define  O2_MODE_B_ID_O2	0x02
+#define  O2_MODE_B_VS1		0x04
+#define  O2_MODE_B_VS2		0x08
+#define  O2_MODE_B_IRQ15_RI	0x80
+
+#define O2_MODE_C		0x3a
+#define  O2_MODE_C_DREQ_MASK	0x03
+#define  O2_MODE_C_DREQ_INPACK	0x01
+#define  O2_MODE_C_DREQ_WP	0x02
+#define  O2_MODE_C_DREQ_BVD2	0x03
+#define  O2_MODE_C_ZVIDEO	0x08
+#define  O2_MODE_C_IREQ_SEL	0x30
+#define  O2_MODE_C_MGMT_SEL	0xc0
+
+#define O2_MODE_D		0x3b
+#define  O2_MODE_D_IRQ_MODE	0x03
+#define  O2_MODE_D_PCI_CLKRUN	0x04
+#define  O2_MODE_D_CB_CLKRUN	0x08
+#define  O2_MODE_D_SKT_ACTV	0x20
+#define  O2_MODE_D_PCI_FIFO	0x40	/* for OZ6729, OZ6730 */
+#define  O2_MODE_D_W97_IRQ	0x40
+#define  O2_MODE_D_ISA_IRQ	0x80
+
+#define O2_MHPG_DMA		0x3c
+#define  O2_MHPG_CHANNEL	0x07
+#define  O2_MHPG_CINT_ENA	0x08
+#define  O2_MHPG_CSC_ENA	0x10
+
+#define O2_FIFO_ENA		0x3d
+#define  O2_FIFO_ZVIDEO_3	0x08
+#define  O2_FIFO_PCI_FIFO	0x10
+#define  O2_FIFO_POSTWR		0x40
+#define  O2_FIFO_BUFFER		0x80
+
+#define O2_MODE_E		0x3e
+#define  O2_MODE_E_MHPG_DMA	0x01
+#define  O2_MODE_E_SPKR_OUT	0x02
+#define  O2_MODE_E_LED_OUT	0x08
+#define  O2_MODE_E_SKTA_ACTV	0x10
+
+static int o2micro_override(struct yenta_socket *socket)
+{
+	/*
+	 * 'reserved' register at 0x94/D4. chaning it to 0xCA (8 bit) enables
+	 * read prefetching which for example makes the RME Hammerfall DSP
+	 * working. for some bridges it is at 0x94, for others at 0xD4. it's
+	 * ok to write to both registers on all O2 bridges.
+	 * from Eric Still, 02Micro.
+	 */
+	u8 a, b;
+
+	if (PCI_FUNC(socket->dev->devfn) == 0) {
+		a = config_readb(socket, 0x94);
+		b = config_readb(socket, 0xD4);
+
+		printk(KERN_INFO "Yenta O2: res at 0x94/0xD4: %02x/%02x\n", a, b);
+
+		switch (socket->dev->device) {
+		case PCI_DEVICE_ID_O2_6832:
+			printk(KERN_INFO "Yenta O2: old bridge, not enabling read prefetch / write burst\n");
+			break;
+
+		default:
+			printk(KERN_INFO "Yenta O2: enabling read prefetch/write burst\n");
+			config_writeb(socket, 0x94, a | 0x0a);
+			config_writeb(socket, 0xD4, b | 0x0a);
+		}
+	}
+
+	return 0;
+}
+
+static void o2micro_restore_state(struct yenta_socket *socket)
+{
+	/*
+	 * as long as read prefetch is the only thing in
+	 * o2micro_override, it's safe to call it from here
+	 */
+	o2micro_override(socket);
+}
+
+#endif /* _LINUX_O2MICRO_H */
diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c
new file mode 100644
index 0000000..68b8008
--- /dev/null
+++ b/drivers/pcmcia/pcmcia_compat.c
@@ -0,0 +1,125 @@
+/*
+ * PCMCIA 16-bit compatibility functions
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Copyright (C) 2004 Dominik Brodowski
+ *
+ * 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>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+
+#include "cs_internal.h"
+
+int pcmcia_get_first_tuple(client_handle_t handle, tuple_t *tuple)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_get_first_tuple(s, handle->Function, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_first_tuple);
+
+int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_get_next_tuple(s, handle->Function, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_next_tuple);
+
+int pcmcia_get_tuple_data(client_handle_t handle, tuple_t *tuple)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_get_tuple_data(s, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_tuple_data);
+
+int pcmcia_parse_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	return pccard_parse_tuple(tuple, parse);
+}
+EXPORT_SYMBOL(pcmcia_parse_tuple);
+
+int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_validate_cis(s, handle->Function, info);
+}
+EXPORT_SYMBOL(pcmcia_validate_cis);
+
+int pcmcia_get_configuration_info(client_handle_t handle,
+				  config_info_t *config)
+{
+	struct pcmcia_socket *s;
+
+	if ((CHECK_HANDLE(handle)) || !config)
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	if (!s)
+		return CS_BAD_HANDLE;
+	return pccard_get_configuration_info(s, handle->Function, config);
+}
+EXPORT_SYMBOL(pcmcia_get_configuration_info);
+
+int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
+{
+	struct pcmcia_socket *skt;
+    
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	skt = SOCKET(handle);
+	if (!skt)
+		return CS_BAD_HANDLE;
+
+	return pccard_reset_card(skt);
+}
+EXPORT_SYMBOL(pcmcia_reset_card);
+
+int pcmcia_get_status(client_handle_t handle, cs_status_t *status)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_get_status(s, handle->Function, status);
+}
+EXPORT_SYMBOL(pcmcia_get_status);
+
+int pcmcia_access_configuration_register(client_handle_t handle,
+					 conf_reg_t *reg)
+{
+	struct pcmcia_socket *s;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	s = SOCKET(handle);
+	return pccard_access_configuration_register(s, handle->Function, reg);
+}
+EXPORT_SYMBOL(pcmcia_access_configuration_register);
+
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c
new file mode 100644
index 0000000..3f43643
--- /dev/null
+++ b/drivers/pcmcia/pd6729.c
@@ -0,0 +1,871 @@
+/*
+ * Driver for the Cirrus PD6729 PCI-PCMCIA bridge.
+ *
+ * Based on the i82092.c driver.
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "pd6729.h"
+#include "i82365.h"
+#include "cirrus.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for the Cirrus PD6729 PCI-PCMCIA bridge");
+MODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>");
+
+#define MAX_SOCKETS 2
+
+/*
+ * simple helper functions
+ * External clock time, in nanoseconds.  120 ns = 8.33 MHz
+ */
+#define to_cycles(ns)	((ns)/120)
+
+#ifndef NO_IRQ
+#define NO_IRQ	((unsigned int)(0))
+#endif
+
+/*
+ * PARAMETERS
+ *  irq_mode=n
+ *     Specifies the interrupt delivery mode.  The default (1) is to use PCI
+ *     interrupts; a value of 0 selects ISA interrupts. This must be set for
+ *     correct operation of PCI card readers.
+ *
+ *  irq_list=i,j,...
+ *     This list limits the set of interrupts that can be used by PCMCIA
+ *     cards.
+ *     The default list is 3,4,5,7,9,10,11.
+ *     (irq_list parameter is not used, if irq_mode = 1)
+ */
+
+static int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */
+static int irq_list[16];
+static int irq_list_count = 0;
+
+module_param(irq_mode, int, 0444);
+module_param_array(irq_list, int, &irq_list_count, 0444);
+MODULE_PARM_DESC(irq_mode,
+		"interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1");
+MODULE_PARM_DESC(irq_list, "interrupts that can be used by PCMCIA cards");
+
+static DEFINE_SPINLOCK(port_lock);
+
+/* basic value read/write functions */
+
+static unsigned char indirect_read(struct pd6729_socket *socket,
+				   unsigned short reg)
+{
+	unsigned long port;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg += socket->number * 0x40;
+	port = socket->io_base;
+	outb(reg, port);
+	val = inb(port + 1);
+	spin_unlock_irqrestore(&port_lock, flags);
+
+	return val;
+}
+
+static unsigned short indirect_read16(struct pd6729_socket *socket,
+				      unsigned short reg)
+{
+	unsigned long port;
+	unsigned short tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg  = reg + socket->number * 0x40;
+	port = socket->io_base;
+	outb(reg, port);
+	tmp = inb(port + 1);
+	reg++;
+	outb(reg, port);
+	tmp = tmp | (inb(port + 1) << 8);
+	spin_unlock_irqrestore(&port_lock, flags);
+
+	return tmp;
+}
+
+static void indirect_write(struct pd6729_socket *socket, unsigned short reg,
+			   unsigned char value)
+{
+	unsigned long port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg = reg + socket->number * 0x40;
+	port = socket->io_base;
+	outb(reg, port);
+	outb(value, port + 1);
+	spin_unlock_irqrestore(&port_lock, flags);
+}
+
+static void indirect_setbit(struct pd6729_socket *socket, unsigned short reg,
+			    unsigned char mask)
+{
+	unsigned long port;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg = reg + socket->number * 0x40;
+	port = socket->io_base;
+	outb(reg, port);
+	val = inb(port + 1);
+	val |= mask;
+	outb(reg, port);
+	outb(val, port + 1);
+	spin_unlock_irqrestore(&port_lock, flags);
+}
+
+static void indirect_resetbit(struct pd6729_socket *socket, unsigned short reg,
+			      unsigned char mask)
+{
+	unsigned long port;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg = reg + socket->number * 0x40;
+	port = socket->io_base;
+	outb(reg, port);
+	val = inb(port + 1);
+	val &= ~mask;
+	outb(reg, port);
+	outb(val, port + 1);
+	spin_unlock_irqrestore(&port_lock, flags);
+}
+
+static void indirect_write16(struct pd6729_socket *socket, unsigned short reg,
+			     unsigned short value)
+{
+	unsigned long port;
+	unsigned char val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port_lock, flags);
+	reg = reg + socket->number * 0x40;
+	port = socket->io_base;
+
+	outb(reg, port);
+	val = value & 255;
+	outb(val, port + 1);
+
+	reg++;
+
+	outb(reg, port);
+	val = value >> 8;
+	outb(val, port + 1);
+	spin_unlock_irqrestore(&port_lock, flags);
+}
+
+/* Interrupt handler functionality */
+
+static irqreturn_t pd6729_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	struct pd6729_socket *socket = (struct pd6729_socket *)dev;
+	int i;
+	int loopcount = 0;
+	int handled = 0;
+	unsigned int events, active = 0;
+
+	while (1) {
+		loopcount++;
+		if (loopcount > 20) {
+			printk(KERN_ERR "pd6729: infinite eventloop "
+			       "in interrupt\n");
+			break;
+		}
+
+		active = 0;
+
+		for (i = 0; i < MAX_SOCKETS; i++) {
+			unsigned int csc;
+
+			/* card status change register */
+			csc = indirect_read(&socket[i], I365_CSC);
+			if (csc == 0)  /* no events on this socket */
+				continue;
+
+			handled = 1;
+			events = 0;
+
+			if (csc & I365_CSC_DETECT) {
+				events |= SS_DETECT;
+				dprintk("Card detected in socket %i!\n", i);
+			}
+
+			if (indirect_read(&socket[i], I365_INTCTL)
+						& I365_PC_IOCARD) {
+				/* For IO/CARDS, bit 0 means "read the card" */
+				events |= (csc & I365_CSC_STSCHG)
+						? SS_STSCHG : 0;
+			} else {
+				/* Check for battery/ready events */
+				events |= (csc & I365_CSC_BVD1)
+						? SS_BATDEAD : 0;
+				events |= (csc & I365_CSC_BVD2)
+						? SS_BATWARN : 0;
+				events |= (csc & I365_CSC_READY)
+						? SS_READY : 0;
+			}
+
+			if (events) {
+				pcmcia_parse_events(&socket[i].socket, events);
+			}
+			active |= events;
+		}
+
+		if (active == 0) /* no more events to handle */
+			break;
+	}
+	return IRQ_RETVAL(handled);
+}
+
+/* socket functions */
+
+static void pd6729_interrupt_wrapper(unsigned long data)
+{
+	struct pd6729_socket *socket = (struct pd6729_socket *) data;
+
+	pd6729_interrupt(0, (void *)socket, NULL);
+	mod_timer(&socket->poll_timer, jiffies + HZ);
+}
+
+static int pd6729_get_status(struct pcmcia_socket *sock, u_int *value)
+{
+	struct pd6729_socket *socket
+			= container_of(sock, struct pd6729_socket, socket);
+	unsigned int status;
+	unsigned int data;
+	struct pd6729_socket *t;
+
+	/* Interface Status Register */
+	status = indirect_read(socket, I365_STATUS);
+	*value = 0;
+
+	if ((status & I365_CS_DETECT) == I365_CS_DETECT) {
+		*value |= SS_DETECT;
+	}
+
+	/*
+	 * IO cards have a different meaning of bits 0,1
+	 * Also notice the inverse-logic on the bits
+	 */
+	if (indirect_read(socket, I365_INTCTL) & I365_PC_IOCARD) {
+		/* IO card */
+		if (!(status & I365_CS_STSCHG))
+			*value |= SS_STSCHG;
+	} else {
+		/* non I/O card */
+		if (!(status & I365_CS_BVD1))
+			*value |= SS_BATDEAD;
+		if (!(status & I365_CS_BVD2))
+			*value |= SS_BATWARN;
+	}
+
+	if (status & I365_CS_WRPROT)
+		*value |= SS_WRPROT;	/* card is write protected */
+
+	if (status & I365_CS_READY)
+		*value |= SS_READY;	/* card is not busy */
+
+	if (status & I365_CS_POWERON)
+		*value |= SS_POWERON;	/* power is applied to the card */
+
+	t = (socket->number) ? socket : socket + 1;
+	indirect_write(t, PD67_EXT_INDEX, PD67_EXTERN_DATA);
+	data = indirect_read16(t, PD67_EXT_DATA);
+	*value |= (data & PD67_EXD_VS1(socket->number)) ? 0 : SS_3VCARD;
+
+	return 0;
+}
+
+
+static int pd6729_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct pd6729_socket *socket
+			= container_of(sock, struct pd6729_socket, socket);
+	unsigned char reg, vcc, vpp;
+
+	state->flags    = 0;
+	state->Vcc      = 0;
+	state->Vpp      = 0;
+	state->io_irq   = 0;
+	state->csc_mask = 0;
+
+	/* First the power status of the socket */
+	reg = indirect_read(socket, I365_POWER);
+
+	if (reg & I365_PWR_AUTO)
+		state->flags |= SS_PWR_AUTO;  /* Automatic Power Switch */
+
+	if (reg & I365_PWR_OUT)
+		state->flags |= SS_OUTPUT_ENA; /* Output signals are enabled */
+
+	vcc = reg & I365_VCC_MASK;    vpp = reg & I365_VPP1_MASK;
+
+	if (reg & I365_VCC_5V) {
+		state->Vcc = (indirect_read(socket, PD67_MISC_CTL_1) &
+			PD67_MC1_VCC_3V) ? 33 : 50;
+
+		if (vpp == I365_VPP1_5V) {
+			if (state->Vcc == 50)
+				state->Vpp = 50;
+			else
+				state->Vpp = 33;
+		}
+		if (vpp == I365_VPP1_12V)
+			state->Vpp = 120;
+	}
+
+	/* Now the IO card, RESET flags and IO interrupt */
+	reg = indirect_read(socket, I365_INTCTL);
+
+	if ((reg & I365_PC_RESET) == 0)
+		state->flags |= SS_RESET;
+	if (reg & I365_PC_IOCARD)
+		state->flags |= SS_IOCARD; /* This is an IO card */
+
+	/* Set the IRQ number */
+	state->io_irq = socket->card_irq;
+
+	/* Card status change */
+	reg = indirect_read(socket, I365_CSCINT);
+
+	if (reg & I365_CSC_DETECT)
+		state->csc_mask |= SS_DETECT; /* Card detect is enabled */
+
+	if (state->flags & SS_IOCARD) {/* IO Cards behave different */
+		if (reg & I365_CSC_STSCHG)
+			state->csc_mask |= SS_STSCHG;
+	} else {
+		if (reg & I365_CSC_BVD1)
+			state->csc_mask |= SS_BATDEAD;
+		if (reg & I365_CSC_BVD2)
+			state->csc_mask |= SS_BATWARN;
+		if (reg & I365_CSC_READY)
+			state->csc_mask |= SS_READY;
+	}
+
+	return 0;
+}
+
+static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct pd6729_socket *socket
+			= container_of(sock, struct pd6729_socket, socket);
+	unsigned char reg, data;
+
+	/* First, set the global controller options */
+	indirect_write(socket, I365_GBLCTL, 0x00);
+	indirect_write(socket, I365_GENCTL, 0x00);
+
+	/* Values for the IGENC register */
+	socket->card_irq = state->io_irq;
+
+	reg = 0;
+ 	/* The reset bit has "inverse" logic */
+	if (!(state->flags & SS_RESET))
+		reg |= I365_PC_RESET;
+	if (state->flags & SS_IOCARD)
+		reg |= I365_PC_IOCARD;
+
+	/* IGENC, Interrupt and General Control Register */
+	indirect_write(socket, I365_INTCTL, reg);
+
+	/* Power registers */
+
+	reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
+
+	if (state->flags & SS_PWR_AUTO) {
+		dprintk("Auto power\n");
+		reg |= I365_PWR_AUTO;	/* automatic power mngmnt */
+	}
+	if (state->flags & SS_OUTPUT_ENA) {
+		dprintk("Power Enabled\n");
+		reg |= I365_PWR_OUT;	/* enable power */
+	}
+
+	switch (state->Vcc) {
+	case 0:
+		break;
+	case 33:
+		dprintk("setting voltage to Vcc to 3.3V on socket %i\n",
+			socket->number);
+		reg |= I365_VCC_5V;
+		indirect_setbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+		break;
+	case 50:
+		dprintk("setting voltage to Vcc to 5V on socket %i\n",
+			socket->number);
+		reg |= I365_VCC_5V;
+		indirect_resetbit(socket, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
+		break;
+	default:
+		dprintk("pd6729: pd6729_set_socket called with "
+				"invalid VCC power value: %i\n",
+			state->Vcc);
+		return -EINVAL;
+	}
+
+	switch (state->Vpp) {
+	case 0:
+		dprintk("not setting Vpp on socket %i\n", socket->number);
+		break;
+	case 33:
+	case 50:
+		dprintk("setting Vpp to Vcc for socket %i\n", socket->number);
+		reg |= I365_VPP1_5V;
+		break;
+	case 120:
+		dprintk("setting Vpp to 12.0\n");
+		reg |= I365_VPP1_12V;
+		break;
+	default:
+		dprintk("pd6729: pd6729_set_socket called with invalid VPP power value: %i\n",
+			state->Vpp);
+		return -EINVAL;
+	}
+
+	/* only write if changed */
+	if (reg != indirect_read(socket, I365_POWER))
+		indirect_write(socket, I365_POWER, reg);
+
+	if (irq_mode == 1) {
+		 /* all interrupts are to be done as PCI interrupts */
+		data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ;
+	} else
+		data = 0;
+
+	indirect_write(socket, PD67_EXT_INDEX, PD67_EXT_CTL_1);
+	indirect_write(socket, PD67_EXT_DATA, data);
+
+	/* Enable specific interrupt events */
+
+	reg = 0x00;
+	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;
+	} else {
+		if (state->csc_mask & SS_BATDEAD)
+			reg |= I365_CSC_BVD1;
+		if (state->csc_mask & SS_BATWARN)
+			reg |= I365_CSC_BVD2;
+		if (state->csc_mask & SS_READY)
+			reg |= I365_CSC_READY;
+	}
+	if (irq_mode == 1)
+		reg |= 0x30;	/* management IRQ: PCI INTA# = "irq 3" */
+	indirect_write(socket, I365_CSCINT, reg);
+
+	reg = indirect_read(socket, I365_INTCTL);
+	if (irq_mode == 1)
+		reg |= 0x03;	/* card IRQ: PCI INTA# = "irq 3" */
+	else
+		reg |= socket->card_irq;
+	indirect_write(socket, I365_INTCTL, reg);
+
+	/* now clear the (probably bogus) pending stuff by doing a dummy read */
+	(void)indirect_read(socket, I365_CSC);
+
+	return 0;
+}
+
+static int pd6729_set_io_map(struct pcmcia_socket *sock,
+			     struct pccard_io_map *io)
+{
+	struct pd6729_socket *socket
+			= container_of(sock, struct pd6729_socket, socket);
+	unsigned char map, ioctl;
+
+	map = io->map;
+
+	/* Check error conditions */
+	if (map > 1) {
+		dprintk("pd6729_set_io_map with invalid map");
+		return -EINVAL;
+	}
+
+	/* Turn off the window before changing anything */
+	if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_IO(map))
+		indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
+
+	/* dprintk("set_io_map: Setting range to %x - %x\n",
+	   io->start, io->stop);*/
+
+	/* write the new values */
+	indirect_write16(socket, I365_IO(map)+I365_W_START, io->start);
+	indirect_write16(socket, I365_IO(map)+I365_W_STOP, io->stop);
+
+	ioctl = indirect_read(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+
+	if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+	if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+	if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+
+	indirect_write(socket, I365_IOCTL, ioctl);
+
+	/* Turn the window back on if needed */
+	if (io->flags & MAP_ACTIVE)
+		indirect_setbit(socket, I365_ADDRWIN, I365_ENA_IO(map));
+
+	return 0;
+}
+
+static int pd6729_set_mem_map(struct pcmcia_socket *sock,
+			      struct pccard_mem_map *mem)
+{
+	struct pd6729_socket *socket
+			 = container_of(sock, struct pd6729_socket, socket);
+	unsigned short base, i;
+	unsigned char map;
+
+	map = mem->map;
+	if (map > 4) {
+		printk("pd6729_set_mem_map: invalid map");
+		return -EINVAL;
+	}
+
+	if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) {
+		printk("pd6729_set_mem_map: invalid address / speed");
+		return -EINVAL;
+	}
+
+	/* Turn off the window before changing anything */
+	if (indirect_read(socket, I365_ADDRWIN) & I365_ENA_MEM(map))
+		indirect_resetbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
+
+	/* write the start address */
+	base = I365_MEM(map);
+	i = (mem->res->start >> 12) & 0x0fff;
+	if (mem->flags & MAP_16BIT)
+		i |= I365_MEM_16BIT;
+	if (mem->flags & MAP_0WS)
+		i |= I365_MEM_0WS;
+	indirect_write16(socket, base + I365_W_START, i);
+
+	/* write the stop address */
+
+	i= (mem->res->end >> 12) & 0x0fff;
+	switch (to_cycles(mem->speed)) {
+	case 0:
+		break;
+	case 1:
+		i |= I365_MEM_WS0;
+		break;
+	case 2:
+		i |= I365_MEM_WS1;
+		break;
+	default:
+		i |= I365_MEM_WS1 | I365_MEM_WS0;
+		break;
+	}
+
+	indirect_write16(socket, base + I365_W_STOP, i);
+
+	/* Take care of high byte */
+	indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map));
+	indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24);
+
+	/* card start */
+
+	i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff;
+	if (mem->flags & MAP_WRPROT)
+		i |= I365_MEM_WRPROT;
+	if (mem->flags & MAP_ATTRIB) {
+		/* dprintk("requesting attribute memory for socket %i\n",
+			socket->number);*/
+		i |= I365_MEM_REG;
+	} else {
+		/* dprintk("requesting normal memory for socket %i\n",
+			socket->number);*/
+	}
+	indirect_write16(socket, base + I365_W_OFF, i);
+
+	/* Enable the window if necessary */
+	if (mem->flags & MAP_ACTIVE)
+		indirect_setbit(socket, I365_ADDRWIN, I365_ENA_MEM(map));
+
+	return 0;
+}
+
+static int pd6729_init(struct pcmcia_socket *sock)
+{
+	int i;
+	struct resource res = { .end = 0x0fff };
+	pccard_io_map io = { 0, 0, 0, 0, 1 };
+	pccard_mem_map mem = { .res = &res, };
+
+	pd6729_set_socket(sock, &dead_socket);
+	for (i = 0; i < 2; i++) {
+		io.map = i;
+		pd6729_set_io_map(sock, &io);
+	}
+	for (i = 0; i < 5; i++) {
+		mem.map = i;
+		pd6729_set_mem_map(sock, &mem);
+	}
+
+	return 0;
+}
+
+
+/* the pccard structure and its functions */
+static struct pccard_operations pd6729_operations = {
+	.init 			= pd6729_init,
+	.get_status		= pd6729_get_status,
+	.get_socket		= pd6729_get_socket,
+	.set_socket		= pd6729_set_socket,
+	.set_io_map		= pd6729_set_io_map,
+	.set_mem_map		= pd6729_set_mem_map,
+};
+
+static irqreturn_t pd6729_test(int irq, void *dev, struct pt_regs *regs)
+{
+	dprintk("-> hit on irq %d\n", irq);
+	return IRQ_HANDLED;
+}
+
+static int pd6729_check_irq(int irq, int flags)
+{
+	if (request_irq(irq, pd6729_test, flags, "x", pd6729_test) != 0)
+		return -1;
+	free_irq(irq, pd6729_test);
+	return 0;
+}
+
+static u_int __init pd6729_isa_scan(void)
+{
+	u_int mask0, mask = 0;
+	int i;
+
+	if (irq_mode == 1) {
+		printk(KERN_INFO "pd6729: PCI card interrupts, "
+						"PCI status changes\n");
+		return 0;
+	}
+
+	if (irq_list_count == 0)
+		mask0 = 0xffff;
+	else
+		for (i = mask0 = 0; i < irq_list_count; i++)
+			mask0 |= (1<<irq_list[i]);
+
+	mask0 &= PD67_MASK;
+
+	/* just find interrupts that aren't in use */
+	for (i = 0; i < 16; i++)
+		if ((mask0 & (1 << i)) && (pd6729_check_irq(i, 0) == 0))
+			mask |= (1 << i);
+
+	printk(KERN_INFO "pd6729: ISA irqs = ");
+	for (i = 0; i < 16; i++)
+		if (mask & (1<<i))
+			printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i);
+
+	if (mask == 0) printk("none!");
+
+	printk("  polling status changes.\n");
+
+	return mask;
+}
+
+static int __devinit pd6729_pci_probe(struct pci_dev *dev,
+				      const struct pci_device_id *id)
+{
+	int i, j, ret;
+	u_int mask;
+	char configbyte;
+	struct pd6729_socket *socket;
+
+	socket = kmalloc(sizeof(struct pd6729_socket) * MAX_SOCKETS,
+			 GFP_KERNEL);
+	if (!socket)
+		return -ENOMEM;
+
+	memset(socket, 0, sizeof(struct pd6729_socket) * MAX_SOCKETS);
+
+	if ((ret = pci_enable_device(dev)))
+		goto err_out_free_mem;
+
+	printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge "
+		"at 0x%lx on irq %d\n",	pci_resource_start(dev, 0), dev->irq);
+ 	/*
+	 * Since we have no memory BARs some firmware may not
+	 * have had PCI_COMMAND_MEMORY enabled, yet the device needs it.
+	 */
+	pci_read_config_byte(dev, PCI_COMMAND, &configbyte);
+	if (!(configbyte & PCI_COMMAND_MEMORY)) {
+		printk(KERN_DEBUG "pd6729: Enabling PCI_COMMAND_MEMORY.\n");
+		configbyte |= PCI_COMMAND_MEMORY;
+		pci_write_config_byte(dev, PCI_COMMAND, configbyte);
+	}
+
+	ret = pci_request_regions(dev, "pd6729");
+	if (ret) {
+		printk(KERN_INFO "pd6729: pci request region failed.\n");
+		goto err_out_disable;
+	}
+
+	if (dev->irq == NO_IRQ)
+		irq_mode = 0;	/* fall back to ISA interrupt mode */
+
+	mask = pd6729_isa_scan();
+	if (irq_mode == 0 && mask == 0) {
+		printk(KERN_INFO "pd6729: no ISA interrupt is available.\n");
+		goto err_out_free_res;
+	}
+
+	for (i = 0; i < MAX_SOCKETS; i++) {
+		socket[i].io_base = pci_resource_start(dev, 0);
+		socket[i].socket.features |= SS_CAP_PCCARD;
+		socket[i].socket.map_size = 0x1000;
+		socket[i].socket.irq_mask = mask;
+		socket[i].socket.pci_irq  = dev->irq;
+		socket[i].socket.owner = THIS_MODULE;
+
+		socket[i].number = i;
+
+		socket[i].socket.ops = &pd6729_operations;
+		socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+		socket[i].socket.dev.dev = &dev->dev;
+		socket[i].socket.driver_data = &socket[i];
+	}
+
+	pci_set_drvdata(dev, socket);
+	if (irq_mode == 1) {
+		/* Register the interrupt handler */
+		if ((ret = request_irq(dev->irq, pd6729_interrupt, SA_SHIRQ,
+							"pd6729", socket))) {
+			printk(KERN_ERR "pd6729: Failed to register irq %d, "
+							"aborting\n", dev->irq);
+			goto err_out_free_res;
+		}
+	} else {
+		/* poll Card status change */
+		init_timer(&socket->poll_timer);
+		socket->poll_timer.function = pd6729_interrupt_wrapper;
+		socket->poll_timer.data = (unsigned long)socket;
+		socket->poll_timer.expires = jiffies + HZ;
+		add_timer(&socket->poll_timer);
+	}
+
+	for (i = 0; i < MAX_SOCKETS; i++) {
+		ret = pcmcia_register_socket(&socket[i].socket);
+		if (ret) {
+			printk(KERN_INFO "pd6729: pcmcia_register_socket "
+					       "failed.\n");
+			for (j = 0; j < i ; j++)
+				pcmcia_unregister_socket(&socket[j].socket);
+			goto err_out_free_res2;
+		}
+	}
+
+	return 0;
+
+ err_out_free_res2:
+	if (irq_mode == 1)
+		free_irq(dev->irq, socket);
+	else
+		del_timer_sync(&socket->poll_timer);
+ err_out_free_res:
+	pci_release_regions(dev);
+ err_out_disable:
+	pci_disable_device(dev);
+
+ err_out_free_mem:
+	kfree(socket);
+	return ret;
+}
+
+static void __devexit pd6729_pci_remove(struct pci_dev *dev)
+{
+	int i;
+	struct pd6729_socket *socket = pci_get_drvdata(dev);
+
+	for (i = 0; i < MAX_SOCKETS; i++) {
+		/* Turn off all interrupt sources */
+		indirect_write(&socket[i], I365_CSCINT, 0);
+		indirect_write(&socket[i], I365_INTCTL, 0);
+
+		pcmcia_unregister_socket(&socket[i].socket);
+	}
+
+	if (irq_mode == 1)
+		free_irq(dev->irq, socket);
+	else
+		del_timer_sync(&socket->poll_timer);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+
+	kfree(socket);
+}
+
+static int pd6729_socket_suspend(struct pci_dev *dev, pm_message_t state)
+{
+	return pcmcia_socket_dev_suspend(&dev->dev, state);
+}
+
+static int pd6729_socket_resume(struct pci_dev *dev)
+{
+	return pcmcia_socket_dev_resume(&dev->dev);
+}
+
+static struct pci_device_id pd6729_pci_ids[] = {
+	{
+		.vendor		= PCI_VENDOR_ID_CIRRUS,
+		.device		= PCI_DEVICE_ID_CIRRUS_6729,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, pd6729_pci_ids);
+
+static struct pci_driver pd6729_pci_drv = {
+	.name		= "pd6729",
+	.id_table	= pd6729_pci_ids,
+	.probe		= pd6729_pci_probe,
+	.remove		= __devexit_p(pd6729_pci_remove),
+	.suspend	= pd6729_socket_suspend,
+	.resume		= pd6729_socket_resume,
+};
+
+static int pd6729_module_init(void)
+{
+	return pci_register_driver(&pd6729_pci_drv);
+}
+
+static void pd6729_module_exit(void)
+{
+	pci_unregister_driver(&pd6729_pci_drv);
+}
+
+module_init(pd6729_module_init);
+module_exit(pd6729_module_exit);
diff --git a/drivers/pcmcia/pd6729.h b/drivers/pcmcia/pd6729.h
new file mode 100644
index 0000000..f392e45
--- /dev/null
+++ b/drivers/pcmcia/pd6729.h
@@ -0,0 +1,30 @@
+#ifndef _INCLUDE_GUARD_PD6729_H_
+#define _INCLUDE_GUARD_PD6729_H_
+
+/* Debuging defines */
+#ifdef NOTRACE
+#define dprintk(fmt, args...) printk(fmt , ## args)
+#else
+#define dprintk(fmt, args...) do {} while (0)
+#endif
+
+/* Flags for I365_GENCTL */
+#define I365_DF_VS1		0x40	/* DF-step Voltage Sense */
+#define I365_DF_VS2		0x80
+
+/* Fields in PD67_EXTERN_DATA */
+#define PD67_EXD_VS1(s)		(0x01 << ((s) << 1))
+#define PD67_EXD_VS2(s)		(0x02 << ((s) << 1))
+
+/* Default ISA interrupt mask */
+#define PD67_MASK	0x0eb8	/* irq 11,10,9,7,5,4,3 */
+
+struct pd6729_socket {
+	int	number;
+	int	card_irq;
+	unsigned long io_base; 	/* base io address of the socket */
+	struct pcmcia_socket socket;
+	struct timer_list poll_timer;
+};
+
+#endif
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
new file mode 100644
index 0000000..a6936a7
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -0,0 +1,254 @@
+/*======================================================================
+
+  Device driver for the PCMCIA control functionality of PXA2xx
+  microprocessors.
+
+    The contents of this file may be used under the
+    terms of the GNU Public License version 2 (the "GPL")
+
+    (c) Ian Molton (spyro@f2s.com) 2003
+    (c) Stefan Eletzhofer (stefan.eletzhofer@inquant.de) 2003,4
+
+    derived from sa11xx_base.c
+
+     Portions created by John G. Dorsey are
+     Copyright (C) 1999 John G. Dorsey.
+
+  ======================================================================*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+
+#include "cs_internal.h"
+#include "soc_common.h"
+#include "pxa2xx_base.h"
+
+
+#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)
+
+static inline u_int pxa2xx_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) - 1;
+}
+
+static inline u_int pxa2xx_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) - 1;
+}
+
+static inline u_int pxa2xx_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 (approximate) command assertion period, in
+ * nanoseconds, for a given CPU clock frequency and MCXX_ASST value:
+ */
+static inline u_int pxa2xx_pcmcia_cmd_time(u_int mem_clk_10khz,
+					   u_int pcmcia_mcxx_asst)
+{
+	return (300000 * (pcmcia_mcxx_asst + 1) / mem_clk_10khz);
+}
+
+static int pxa2xx_pcmcia_set_mcmem( int sock, int speed, int clock )
+{
+	MCMEM(sock) = ((pxa2xx_mcxx_setup(speed, clock)
+		& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
+		| ((pxa2xx_mcxx_asst(speed, clock)
+		& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
+		| ((pxa2xx_mcxx_hold(speed, clock)
+		& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
+
+	return 0;
+}
+
+static int pxa2xx_pcmcia_set_mcio( int sock, int speed, int clock )
+{
+	MCIO(sock) = ((pxa2xx_mcxx_setup(speed, clock)
+		& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
+		| ((pxa2xx_mcxx_asst(speed, clock)
+		& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
+		| ((pxa2xx_mcxx_hold(speed, clock)
+		& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
+
+	return 0;
+}
+
+static int pxa2xx_pcmcia_set_mcatt( int sock, int speed, int clock )
+{
+	MCATT(sock) = ((pxa2xx_mcxx_setup(speed, clock)
+		& MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT)
+		| ((pxa2xx_mcxx_asst(speed, clock)
+		& MCXX_ASST_MASK) << MCXX_ASST_SHIFT)
+		| ((pxa2xx_mcxx_hold(speed, clock)
+		& MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT);
+
+	return 0;
+}
+
+static int pxa2xx_pcmcia_set_mcxx(struct soc_pcmcia_socket *skt, unsigned int clk)
+{
+	struct soc_pcmcia_timing timing;
+	int sock = skt->nr;
+
+	soc_common_pcmcia_get_timing(skt, &timing);
+
+	pxa2xx_pcmcia_set_mcmem(sock, timing.mem, clk);
+	pxa2xx_pcmcia_set_mcatt(sock, timing.attr, clk);
+	pxa2xx_pcmcia_set_mcio(sock, timing.io, clk);
+
+	return 0;
+}
+
+static int pxa2xx_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
+{
+	unsigned int clk = get_memclk_frequency_10khz();
+	return pxa2xx_pcmcia_set_mcxx(skt, clk);
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+static int
+pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
+			       unsigned long val,
+			       struct cpufreq_freqs *freqs)
+{
+#warning "it's not clear if this is right since the core CPU (N) clock has no effect on the memory (L) clock"
+	switch (val) {
+	case CPUFREQ_PRECHANGE:
+		if (freqs->new > freqs->old) {
+			debug(skt, 2, "new frequency %u.%uMHz > %u.%uMHz, "
+			       "pre-updating\n",
+			       freqs->new / 1000, (freqs->new / 100) % 10,
+			       freqs->old / 1000, (freqs->old / 100) % 10);
+			pxa2xx_pcmcia_set_mcxx(skt, freqs->new);
+		}
+		break;
+
+	case CPUFREQ_POSTCHANGE:
+		if (freqs->new < freqs->old) {
+			debug(skt, 2, "new frequency %u.%uMHz < %u.%uMHz, "
+			       "post-updating\n",
+			       freqs->new / 1000, (freqs->new / 100) % 10,
+			       freqs->old / 1000, (freqs->old / 100) % 10);
+			pxa2xx_pcmcia_set_mcxx(skt, freqs->new);
+		}
+		break;
+	}
+	return 0;
+}
+#endif
+
+int pxa2xx_drv_pcmcia_probe(struct device *dev)
+{
+	int ret;
+	struct pcmcia_low_level *ops;
+	int first, nr;
+
+	if (!dev || !dev->platform_data)
+		return -ENODEV;
+
+	ops = (struct pcmcia_low_level *)dev->platform_data;
+	first = ops->first;
+	nr = ops->nr;
+
+	/* Provide our PXA2xx specific timing routines. */
+	ops->set_timing  = pxa2xx_pcmcia_set_timing;
+#ifdef CONFIG_CPU_FREQ
+	ops->frequency_change = pxa2xx_pcmcia_frequency_change;
+#endif
+
+	ret = soc_common_drv_pcmcia_probe(dev, ops, first, nr);
+
+	if (ret == 0) {
+		/*
+		 * We have at least one socket, so set MECR:CIT
+		 * (Card Is There)
+		 */
+		MECR |= MECR_CIT;
+
+		/* Set MECR:NOS (Number Of Sockets) */
+		if (nr > 1)
+			MECR |= MECR_NOS;
+		else
+			MECR &= ~MECR_NOS;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(pxa2xx_drv_pcmcia_probe);
+
+static int pxa2xx_drv_pcmcia_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int pxa2xx_drv_pcmcia_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+	{
+		struct pcmcia_low_level *ops = dev->platform_data;
+		int nr = ops ? ops->nr : 0;
+
+		MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0);
+		ret = pcmcia_socket_dev_resume(dev);
+	}
+	return ret;
+}
+
+static struct device_driver pxa2xx_pcmcia_driver = {
+	.probe		= pxa2xx_drv_pcmcia_probe,
+	.remove		= soc_common_drv_pcmcia_remove,
+	.suspend 	= pxa2xx_drv_pcmcia_suspend,
+	.resume 	= pxa2xx_drv_pcmcia_resume,
+	.name		= "pxa2xx-pcmcia",
+	.bus		= &platform_bus_type,
+};
+
+static int __init pxa2xx_pcmcia_init(void)
+{
+	return driver_register(&pxa2xx_pcmcia_driver);
+}
+
+static void __exit pxa2xx_pcmcia_exit(void)
+{
+	driver_unregister(&pxa2xx_pcmcia_driver);
+}
+
+module_init(pxa2xx_pcmcia_init);
+module_exit(pxa2xx_pcmcia_exit);
+
+MODULE_AUTHOR("Stefan Eletzhofer <stefan.eletzhofer@inquant.de> and Ian Molton <spyro@f2s.com>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: PXA2xx core socket driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/pxa2xx_base.h b/drivers/pcmcia/pxa2xx_base.h
new file mode 100644
index 0000000..e46cff3
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_base.h
@@ -0,0 +1,3 @@
+/* temporary measure */
+extern int pxa2xx_drv_pcmcia_probe(struct device *);
+
diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/pxa2xx_lubbock.c
new file mode 100644
index 0000000..fd1f691
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_lubbock.c
@@ -0,0 +1,269 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_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/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/mach-types.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/lubbock.h>
+
+#include "sa1111_generic.h"
+
+static int
+lubbock_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	/*
+	 * 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);
+
+	pxa_gpio_mode(GPIO48_nPOE_MD);
+	pxa_gpio_mode(GPIO49_nPWE_MD);
+	pxa_gpio_mode(GPIO50_nPIOR_MD);
+	pxa_gpio_mode(GPIO51_nPIOW_MD);
+	pxa_gpio_mode(GPIO52_nPCE_1_MD);
+	pxa_gpio_mode(GPIO53_nPCE_2_MD);
+	pxa_gpio_mode(GPIO54_pSKTSEL_MD);
+	pxa_gpio_mode(GPIO55_nPREG_MD);
+	pxa_gpio_mode(GPIO56_nPWAIT_MD);
+	pxa_gpio_mode(GPIO57_nIOIS16_MD);
+
+	return sa1111_pcmcia_hw_init(skt);
+}
+
+static int
+lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+				const socket_state_t *state)
+{
+	unsigned int pa_dwr_mask, pa_dwr_set, misc_mask, misc_set;
+	int ret = 0;
+
+	pa_dwr_mask = pa_dwr_set = misc_mask = misc_set = 0;
+
+	/* 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_PWR1		MISC_WR<15>
+	 *	VX	VCC
+	 *	VY	+3.3V
+	 *	12IN	GND		VPP is not connected
+	 *	CODE	+3.3V		Cirrus  Code, CODE = High (VY)
+	 *
+	 */
+
+ again:
+	switch (skt->nr) {
+	case 0:
+		pa_dwr_mask = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3;
+
+		switch (state->Vcc) {
+		case 0: /* Hi-Z */
+			break;
+
+		case 33: /* VY */
+			pa_dwr_set |= GPIO_A3;
+			break;
+
+		case 50: /* VX */
+			pa_dwr_set |= GPIO_A2;
+			break;
+
+		default:
+			printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
+			       __FUNCTION__, state->Vcc);
+			ret = -1;
+		}
+
+		switch (state->Vpp) {
+		case 0: /* Hi-Z */
+			break;
+
+		case 120: /* 12IN */
+			pa_dwr_set |= GPIO_A1;
+			break;
+
+		default: /* VCC */
+			if (state->Vpp == state->Vcc)
+				pa_dwr_set |= GPIO_A0;
+			else {
+				printk(KERN_ERR "%s(): unrecognized Vpp %u\n",
+				       __FUNCTION__, state->Vpp);
+				ret = -1;
+				break;
+			}
+		}
+		break;
+
+	case 1:
+		misc_mask = (1 << 15) | (1 << 14);
+
+		switch (state->Vcc) {
+		case 0: /* Hi-Z */
+			break;
+
+		case 33: /* VY */
+			misc_set |= 1 << 15;
+			break;
+
+		case 50: /* VX */
+			misc_set |= 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;
+		}
+		break;
+
+	default:
+		ret = -1;
+	}
+
+	if (ret == 0)
+		ret = sa1111_pcmcia_configure_socket(skt, state);
+
+	if (ret == 0) {
+		lubbock_set_misc_wr(misc_mask, misc_set);
+		sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, pa_dwr_set);
+	}
+
+#if 1
+	if (ret == 0 && state->Vcc == 33) {
+		struct pcmcia_state new_state;
+
+		/*
+		 * 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.
+		 *
+		 * Note: It takes about 2.5ms for the MAX1602 VCC output
+		 * to rise.
+		 */
+		mdelay(3);
+
+		sa1111_pcmcia_socket_state(skt, &new_state);
+
+		if (!new_state.vs_3v && !new_state.vs_Xv) {
+			/*
+			 * Switch to 5V,  Configure socket with 5V voltage
+			 */
+			lubbock_set_misc_wr(misc_mask, 0);
+			sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, 0);
+
+			/*
+			 * It takes about 100ms to turn off Vcc.
+			 */
+			mdelay(100);
+
+			/*
+			 * We need to hack around the const qualifier as
+			 * well to keep this ugly workaround localized and
+			 * not force it to the rest of the code. Barf bags
+			 * avaliable in the seat pocket in front of you!
+			 */
+			((socket_state_t *)state)->Vcc = 50;
+			((socket_state_t *)state)->Vpp = 50;
+			goto again;
+		}
+	}
+#endif
+
+	return ret;
+}
+
+static struct pcmcia_low_level lubbock_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+	.hw_init		= lubbock_pcmcia_hw_init,
+	.hw_shutdown		= sa1111_pcmcia_hw_shutdown,
+	.socket_state		= sa1111_pcmcia_socket_state,
+	.configure_socket	= lubbock_pcmcia_configure_socket,
+	.socket_init		= sa1111_pcmcia_socket_init,
+	.socket_suspend		= sa1111_pcmcia_socket_suspend,
+	.first			= 0,
+	.nr			= 2,
+};
+
+#include "pxa2xx_base.h"
+
+int __init pcmcia_lubbock_init(struct sa1111_dev *sadev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_lubbock()) {
+		/*
+		 * Set GPIO_A<3:0> to be outputs for the MAX1600,
+		 * and switch to standby mode.
+		 */
+		sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0);
+		sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
+		sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
+
+		/* Set CF Socket 1 power to standby mode. */
+		lubbock_set_misc_wr((1 << 15) | (1 << 14), 0);
+
+		sadev->dev.platform_data = &lubbock_pcmcia_ops;
+		ret = pxa2xx_drv_pcmcia_probe(&sadev->dev);
+	}
+
+	return ret;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c
new file mode 100644
index 0000000..5309734
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_mainstone.c
@@ -0,0 +1,202 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_mainstone.c
+ *
+ * Mainstone PCMCIA specific routines.
+ *
+ * Created:	May 12, 2004
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/mainstone.h>
+
+#include "soc_common.h"
+
+
+static struct pcmcia_irqs irqs[] = {
+	{ 0, MAINSTONE_S0_CD_IRQ, "PCMCIA0 CD" },
+	{ 1, MAINSTONE_S1_CD_IRQ, "PCMCIA1 CD" },
+	{ 0, MAINSTONE_S0_STSCHG_IRQ, "PCMCIA0 STSCHG" },
+	{ 1, MAINSTONE_S1_STSCHG_IRQ, "PCMCIA1 STSCHG" },
+};
+
+static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	/*
+	 * 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(GPIO85_nPCE_1) |
+		GPIO_bit(GPIO54_nPCE_2);
+
+	pxa_gpio_mode(GPIO48_nPOE_MD);
+	pxa_gpio_mode(GPIO49_nPWE_MD);
+	pxa_gpio_mode(GPIO50_nPIOR_MD);
+	pxa_gpio_mode(GPIO51_nPIOW_MD);
+	pxa_gpio_mode(GPIO85_nPCE_1_MD);
+	pxa_gpio_mode(GPIO54_nPCE_2_MD);
+	pxa_gpio_mode(GPIO79_pSKTSEL_MD);
+	pxa_gpio_mode(GPIO55_nPREG_MD);
+	pxa_gpio_mode(GPIO56_nPWAIT_MD);
+	pxa_gpio_mode(GPIO57_nIOIS16_MD);
+
+	skt->irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ;
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void mst_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static unsigned long mst_pcmcia_status[2];
+
+static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+				    struct pcmcia_state *state)
+{
+	unsigned long status, flip;
+
+	status = (skt->nr == 0) ? MST_PCMCIA0 : MST_PCMCIA1;
+	flip = (status ^ mst_pcmcia_status[skt->nr]) & MST_PCMCIA_nSTSCHG_BVD1;
+
+	/*
+	 * Workaround for STSCHG which can't be deasserted:
+	 * We therefore disable/enable corresponding IRQs
+	 * as needed to avoid IRQ locks.
+	 */
+	if (flip) {
+		mst_pcmcia_status[skt->nr] = status;
+		if (status & MST_PCMCIA_nSTSCHG_BVD1)
+			enable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ
+						   : MAINSTONE_S1_STSCHG_IRQ );
+		else
+			disable_irq( (skt->nr == 0) ? MAINSTONE_S0_STSCHG_IRQ
+						    : MAINSTONE_S1_STSCHG_IRQ );
+	}
+
+	state->detect = (status & MST_PCMCIA_nCD) ? 0 : 1;
+	state->ready  = (status & MST_PCMCIA_nIRQ) ? 1 : 0;
+	state->bvd1   = (status & MST_PCMCIA_nSTSCHG_BVD1) ? 1 : 0;
+	state->bvd2   = (status & MST_PCMCIA_nSPKR_BVD2) ? 1 : 0;
+	state->vs_3v  = (status & MST_PCMCIA_nVS1) ? 0 : 1;
+	state->vs_Xv  = (status & MST_PCMCIA_nVS2) ? 0 : 1;
+	state->wrprot = 0;  /* not available */
+}
+
+static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+				       const socket_state_t *state)
+{
+	unsigned long power = 0;
+	int ret = 0;
+
+	switch (state->Vcc) {
+	case 0:  power |= MST_PCMCIA_PWR_VCC_0;  break;
+	case 33: power |= MST_PCMCIA_PWR_VCC_33; break;
+	case 50: power |= MST_PCMCIA_PWR_VCC_50; break;
+	default:
+		 printk(KERN_ERR "%s(): bad Vcc %u\n",
+				 __FUNCTION__, state->Vcc);
+		 ret = -1;
+	}
+
+	switch (state->Vpp) {
+	case 0:   power |= MST_PCMCIA_PWR_VPP_0;   break;
+	case 120: power |= MST_PCMCIA_PWR_VPP_120; break;
+	default:
+		  if(state->Vpp == state->Vcc) {
+			  power |= MST_PCMCIA_PWR_VPP_VCC;
+		  } else {
+			  printk(KERN_ERR "%s(): bad Vpp %u\n",
+					  __FUNCTION__, state->Vpp);
+			  ret = -1;
+		  }
+	}
+
+	if (state->flags & SS_RESET)
+	       power |= MST_PCMCIA_RESET;
+
+	switch (skt->nr) {
+	case 0:  MST_PCMCIA0 = power; break;
+	case 1:  MST_PCMCIA1 = power; break;
+	default: ret = -1;
+	}
+
+	return ret;
+}
+
+static void mst_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void mst_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level mst_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+	.hw_init		= mst_pcmcia_hw_init,
+	.hw_shutdown		= mst_pcmcia_hw_shutdown,
+	.socket_state		= mst_pcmcia_socket_state,
+	.configure_socket	= mst_pcmcia_configure_socket,
+	.socket_init		= mst_pcmcia_socket_init,
+	.socket_suspend		= mst_pcmcia_socket_suspend,
+	.nr			= 2,
+};
+
+static struct platform_device *mst_pcmcia_device;
+
+static int __init mst_pcmcia_init(void)
+{
+	int ret;
+
+	mst_pcmcia_device = kmalloc(sizeof(*mst_pcmcia_device), GFP_KERNEL);
+	if (!mst_pcmcia_device)
+		return -ENOMEM;
+	memset(mst_pcmcia_device, 0, sizeof(*mst_pcmcia_device));
+	mst_pcmcia_device->name = "pxa2xx-pcmcia";
+	mst_pcmcia_device->dev.platform_data = &mst_pcmcia_ops;
+
+	ret = platform_device_register(mst_pcmcia_device);
+	if (ret)
+		kfree(mst_pcmcia_device);
+
+	return ret;
+}
+
+static void __exit mst_pcmcia_exit(void)
+{
+	/*
+	 * This call is supposed to free our mst_pcmcia_device.
+	 * Unfortunately platform_device don't have a free method, and
+	 * we can't assume it's free of any reference at this point so we
+	 * can't free it either.
+	 */
+	platform_device_unregister(mst_pcmcia_device);
+}
+
+module_init(mst_pcmcia_init);
+module_exit(mst_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c
new file mode 100644
index 0000000..42efe21
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_sharpsl.c
@@ -0,0 +1,264 @@
+/*
+ * Sharp SL-C7xx Series PCMCIA routines
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on Sharp's 2.4 kernel patches and pxa2xx_mainstone.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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/hardware/scoop.h>
+#include <asm/arch/corgi.h>
+#include <asm/arch/pxa-regs.h>
+
+#include "soc_common.h"
+
+#define	NO_KEEP_VS 0x0001
+
+static unsigned char keep_vs;
+static unsigned char keep_rd;
+
+static struct pcmcia_irqs irqs[] = {
+	{ 0, CORGI_IRQ_GPIO_CF_CD, "PCMCIA0 CD"},
+};
+
+static void sharpsl_pcmcia_init_reset(void)
+{
+	reset_scoop(&corgiscoop_device.dev);
+	keep_vs = NO_KEEP_VS;
+	keep_rd = 0;
+}
+
+static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	int ret;
+
+	/*
+	 * 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);
+
+	pxa_gpio_mode(GPIO48_nPOE_MD);
+	pxa_gpio_mode(GPIO49_nPWE_MD);
+	pxa_gpio_mode(GPIO50_nPIOR_MD);
+	pxa_gpio_mode(GPIO51_nPIOW_MD);
+	pxa_gpio_mode(GPIO52_nPCE_1_MD);
+	pxa_gpio_mode(GPIO53_nPCE_2_MD);
+	pxa_gpio_mode(GPIO54_pSKTSEL_MD);
+	pxa_gpio_mode(GPIO55_nPREG_MD);
+	pxa_gpio_mode(GPIO56_nPWAIT_MD);
+	pxa_gpio_mode(GPIO57_nIOIS16_MD);
+
+	/* Register interrupts */
+	ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+	if (ret) {
+		printk(KERN_ERR "Request for Compact Flash IRQ failed\n");
+		return ret;
+	}
+
+	/* Enable interrupt */
+	write_scoop_reg(&corgiscoop_device.dev, SCOOP_IMR, 0x00C0);
+	write_scoop_reg(&corgiscoop_device.dev, SCOOP_MCR, 0x0101);
+	keep_vs = NO_KEEP_VS;
+
+	skt->irq = CORGI_IRQ_GPIO_CF_IRQ;
+
+	return 0;
+}
+
+static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+	/* CF_BUS_OFF */
+	sharpsl_pcmcia_init_reset();
+}
+
+
+static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+				    struct pcmcia_state *state)
+{
+	unsigned short cpr, csr;
+
+	cpr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_CPR);
+
+	write_scoop_reg(&corgiscoop_device.dev, SCOOP_IRM, 0x00FF);
+	write_scoop_reg(&corgiscoop_device.dev, SCOOP_ISR, 0x0000);
+	write_scoop_reg(&corgiscoop_device.dev, SCOOP_IRM, 0x0000);
+	csr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_CSR);
+	if (csr & 0x0004) {
+		/* card eject */
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CDR, 0x0000);
+		keep_vs = NO_KEEP_VS;
+	}
+	else if (!(keep_vs & NO_KEEP_VS)) {
+		/* keep vs1,vs2 */
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CDR, 0x0000);
+		csr |= keep_vs;
+	}
+	else if (cpr & 0x0003) {
+		/* power on */
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CDR, 0x0000);
+		keep_vs = (csr & 0x00C0);
+	}
+	else {
+		/* card detect */
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CDR, 0x0002);
+	}
+
+	state->detect = (csr & 0x0004) ? 0 : 1;
+	state->ready  = (csr & 0x0002) ? 1 : 0;
+	state->bvd1   = (csr & 0x0010) ? 1 : 0;
+	state->bvd2   = (csr & 0x0020) ? 1 : 0;
+	state->wrprot = (csr & 0x0008) ? 1 : 0;
+	state->vs_3v  = (csr & 0x0040) ? 0 : 1;
+	state->vs_Xv  = (csr & 0x0080) ? 0 : 1;
+
+	if ((cpr & 0x0080) && ((cpr & 0x8040) != 0x8040)) {
+		printk(KERN_ERR "sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage!\n", cpr);
+	}
+
+}
+
+
+static int sharpsl_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+				       const socket_state_t *state)
+{
+	unsigned long flags;
+
+	unsigned short cpr, ncpr, ccr, nccr, mcr, nmcr, imr, nimr;
+
+	switch (state->Vcc) {
+	case	0:  	break;
+	case 	33: 	break;
+	case	50: 	break;
+	default:
+		 printk(KERN_ERR "sharpsl_pcmcia_configure_socket(): bad Vcc %u\n", state->Vcc);
+		 return -1;
+	}
+
+	if ((state->Vpp!=state->Vcc) && (state->Vpp!=0)) {
+		printk(KERN_ERR "CF slot cannot support Vpp %u\n", state->Vpp);
+		return -1;
+	}
+
+	local_irq_save(flags);
+
+	nmcr = (mcr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_MCR)) & ~0x0010;
+	ncpr = (cpr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_CPR)) & ~0x0083;
+	nccr = (ccr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_CCR)) & ~0x0080;
+	nimr = (imr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_IMR)) & ~0x003E;
+
+	ncpr |= (state->Vcc == 33) ? 0x0001 :
+				(state->Vcc == 50) ? 0x0002 : 0;
+	nmcr |= (state->flags&SS_IOCARD) ? 0x0010 : 0;
+	ncpr |= (state->flags&SS_OUTPUT_ENA) ? 0x0080 : 0;
+	nccr |= (state->flags&SS_RESET)? 0x0080: 0;
+	nimr |=	((skt->status&SS_DETECT) ? 0x0004 : 0)|
+			((skt->status&SS_READY)  ? 0x0002 : 0)|
+			((skt->status&SS_BATDEAD)? 0x0010 : 0)|
+			((skt->status&SS_BATWARN)? 0x0020 : 0)|
+			((skt->status&SS_STSCHG) ? 0x0010 : 0)|
+			((skt->status&SS_WRPROT) ? 0x0008 : 0);
+
+	if (!(ncpr & 0x0003)) {
+		keep_rd = 0;
+	} else if (!keep_rd) {
+		if (nccr & 0x0080)
+			keep_rd = 1;
+		else
+			nccr |= 0x0080;
+	}
+
+	if (mcr != nmcr)
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_MCR, nmcr);
+	if (cpr != ncpr)
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CPR, ncpr);
+	if (ccr != nccr)
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_CCR, nccr);
+	if (imr != nimr)
+		write_scoop_reg(&corgiscoop_device.dev, SCOOP_IMR, nimr);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void sharpsl_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level sharpsl_pcmcia_ops = {
+	.owner				= THIS_MODULE,
+	.hw_init			= sharpsl_pcmcia_hw_init,
+	.hw_shutdown		= sharpsl_pcmcia_hw_shutdown,
+	.socket_state		= sharpsl_pcmcia_socket_state,
+	.configure_socket	= sharpsl_pcmcia_configure_socket,
+	.socket_init		= sharpsl_pcmcia_socket_init,
+	.socket_suspend		= sharpsl_pcmcia_socket_suspend,
+	.first				= 0,
+	.nr					= 1,
+};
+
+static struct platform_device *sharpsl_pcmcia_device;
+
+static int __init sharpsl_pcmcia_init(void)
+{
+	int ret;
+
+	sharpsl_pcmcia_device = kmalloc(sizeof(*sharpsl_pcmcia_device), GFP_KERNEL);
+	if (!sharpsl_pcmcia_device)
+		return -ENOMEM;
+	memset(sharpsl_pcmcia_device, 0, sizeof(*sharpsl_pcmcia_device));
+	sharpsl_pcmcia_device->name = "pxa2xx-pcmcia";
+	sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops;
+
+	ret = platform_device_register(sharpsl_pcmcia_device);
+	if (ret)
+		kfree(sharpsl_pcmcia_device);
+
+	return ret;
+}
+
+static void __exit sharpsl_pcmcia_exit(void)
+{
+	/*
+	 * This call is supposed to free our sharpsl_pcmcia_device.
+	 * Unfortunately platform_device don't have a free method, and
+	 * we can't assume it's free of any reference at this point so we
+	 * can't free it either.
+	 */
+	platform_device_unregister(sharpsl_pcmcia_device);
+}
+
+module_init(sharpsl_pcmcia_init);
+module_exit(sharpsl_pcmcia_exit);
+
+MODULE_DESCRIPTION("Sharp SL Series PCMCIA Support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/ricoh.h b/drivers/pcmcia/ricoh.h
new file mode 100644
index 0000000..01098c8
--- /dev/null
+++ b/drivers/pcmcia/ricoh.h
@@ -0,0 +1,206 @@
+/*
+ * ricoh.h 1.9 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_RICOH_H
+#define _LINUX_RICOH_H
+
+
+#define RF5C_MODE_CTL		0x1f	/* Mode control */
+#define RF5C_PWR_CTL		0x2f	/* Mixed voltage control */
+#define RF5C_CHIP_ID		0x3a	/* Chip identification */
+#define RF5C_MODE_CTL_3		0x3b	/* Mode control 3 */
+
+/* I/O window address offset */
+#define RF5C_IO_OFF(w)		(0x36+((w)<<1))
+
+/* Flags for RF5C_MODE_CTL */
+#define RF5C_MODE_ATA		0x01	/* ATA mode */
+#define RF5C_MODE_LED_ENA	0x02	/* IRQ 12 is LED */
+#define RF5C_MODE_CA21		0x04
+#define RF5C_MODE_CA22		0x08
+#define RF5C_MODE_CA23		0x10
+#define RF5C_MODE_CA24		0x20
+#define RF5C_MODE_CA25		0x40
+#define RF5C_MODE_3STATE_BIT7	0x80
+
+/* Flags for RF5C_PWR_CTL */
+#define RF5C_PWR_VCC_3V		0x01
+#define RF5C_PWR_IREQ_HIGH	0x02
+#define RF5C_PWR_INPACK_ENA	0x04
+#define RF5C_PWR_5V_DET		0x08
+#define RF5C_PWR_TC_SEL		0x10	/* Terminal Count: irq 11 or 15 */
+#define RF5C_PWR_DREQ_LOW	0x20
+#define RF5C_PWR_DREQ_OFF	0x00	/* DREQ steering control */
+#define RF5C_PWR_DREQ_INPACK	0x40
+#define RF5C_PWR_DREQ_SPKR	0x80
+#define RF5C_PWR_DREQ_IOIS16	0xc0
+
+/* Values for RF5C_CHIP_ID */
+#define RF5C_CHIP_RF5C296	0x32
+#define RF5C_CHIP_RF5C396	0xb2
+
+/* Flags for RF5C_MODE_CTL_3 */
+#define RF5C_MCTL3_DISABLE	0x01	/* Disable PCMCIA interface */
+#define RF5C_MCTL3_DMA_ENA	0x02
+
+/* Register definitions for Ricoh PCI-to-CardBus bridges */
+
+/* Extra bits in CB_BRIDGE_CONTROL */
+#define RL5C46X_BCR_3E0_ENA		0x0800
+#define RL5C46X_BCR_3E2_ENA		0x1000
+
+/* Bridge Configuration Register */
+#define RL5C4XX_CONFIG			0x80	/* 16 bit */
+#define  RL5C4XX_CONFIG_IO_1_MODE	0x0200
+#define  RL5C4XX_CONFIG_IO_0_MODE	0x0100
+#define  RL5C4XX_CONFIG_PREFETCH	0x0001
+
+/* Misc Control Register */
+#define RL5C4XX_MISC			0x0082	/* 16 bit */
+#define  RL5C4XX_MISC_HW_SUSPEND_ENA	0x0002
+#define  RL5C4XX_MISC_VCCEN_POL		0x0100
+#define  RL5C4XX_MISC_VPPEN_POL		0x0200
+#define  RL5C46X_MISC_SUSPEND		0x0001
+#define  RL5C46X_MISC_PWR_SAVE_2	0x0004
+#define  RL5C46X_MISC_IFACE_BUSY	0x0008
+#define  RL5C46X_MISC_B_LOCK		0x0010
+#define  RL5C46X_MISC_A_LOCK		0x0020
+#define  RL5C46X_MISC_PCI_LOCK		0x0040
+#define  RL5C47X_MISC_IFACE_BUSY	0x0004
+#define  RL5C47X_MISC_PCI_INT_MASK	0x0018
+#define  RL5C47X_MISC_PCI_INT_DIS	0x0020
+#define  RL5C47X_MISC_SUBSYS_WR		0x0040
+#define  RL5C47X_MISC_SRIRQ_ENA		0x0080
+#define  RL5C47X_MISC_5V_DISABLE	0x0400
+#define  RL5C47X_MISC_LED_POL		0x0800
+
+/* 16-bit Interface Control Register */
+#define RL5C4XX_16BIT_CTL		0x0084	/* 16 bit */
+#define  RL5C4XX_16CTL_IO_TIMING	0x0100
+#define  RL5C4XX_16CTL_MEM_TIMING	0x0200
+#define  RL5C46X_16CTL_LEVEL_1		0x0010
+#define  RL5C46X_16CTL_LEVEL_2		0x0020
+
+/* 16-bit IO and memory timing registers */
+#define RL5C4XX_16BIT_IO_0		0x0088	/* 16 bit */
+#define RL5C4XX_16BIT_MEM_0		0x008a	/* 16 bit */
+#define  RL5C4XX_SETUP_MASK		0x0007
+#define  RL5C4XX_SETUP_SHIFT		0
+#define  RL5C4XX_CMD_MASK		0x01f0
+#define  RL5C4XX_CMD_SHIFT		4
+#define  RL5C4XX_HOLD_MASK		0x1c00
+#define  RL5C4XX_HOLD_SHIFT		10
+#define  RL5C4XX_MISC_CONTROL           0x2F /* 8 bit */
+#define  RL5C4XX_ZV_ENABLE              0x08
+
+#ifdef __YENTA_H
+
+#define rl_misc(socket)		((socket)->private[0])
+#define rl_ctl(socket)		((socket)->private[1])
+#define rl_io(socket)		((socket)->private[2])
+#define rl_mem(socket)		((socket)->private[3])
+#define rl_config(socket)	((socket)->private[4])
+
+static void ricoh_zoom_video(struct pcmcia_socket *sock, int onoff)
+{
+        u8 reg;
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+
+        reg = config_readb(socket, RL5C4XX_MISC_CONTROL);
+        if (onoff)
+                /* Zoom zoom, we will all go together, zoom zoom, zoom zoom */
+                reg |=  RL5C4XX_ZV_ENABLE;
+        else
+                reg &= ~RL5C4XX_ZV_ENABLE;
+	
+        config_writeb(socket, RL5C4XX_MISC_CONTROL, reg);
+}
+
+static void ricoh_set_zv(struct yenta_socket *socket)
+{
+        if(socket->dev->vendor == PCI_VENDOR_ID_RICOH)
+        {
+                switch(socket->dev->device)
+                {
+                        /* There may be more .. */
+		case  PCI_DEVICE_ID_RICOH_RL5C478:
+			socket->socket.zoom_video = ricoh_zoom_video;
+			break;  
+                }
+        }
+}
+
+static void ricoh_save_state(struct yenta_socket *socket)
+{
+	rl_misc(socket) = config_readw(socket, RL5C4XX_MISC);
+	rl_ctl(socket) = config_readw(socket, RL5C4XX_16BIT_CTL);
+	rl_io(socket) = config_readw(socket, RL5C4XX_16BIT_IO_0);
+	rl_mem(socket) = config_readw(socket, RL5C4XX_16BIT_MEM_0);
+	rl_config(socket) = config_readw(socket, RL5C4XX_CONFIG);
+}
+
+static void ricoh_restore_state(struct yenta_socket *socket)
+{
+	config_writew(socket, RL5C4XX_MISC, rl_misc(socket));
+	config_writew(socket, RL5C4XX_16BIT_CTL, rl_ctl(socket));
+	config_writew(socket, RL5C4XX_16BIT_IO_0, rl_io(socket));
+	config_writew(socket, RL5C4XX_16BIT_MEM_0, rl_mem(socket));
+	config_writew(socket, RL5C4XX_CONFIG, rl_config(socket));
+}
+
+
+/*
+ * Magic Ricoh initialization code..
+ */
+static int ricoh_override(struct yenta_socket *socket)
+{
+	u16 config, ctl;
+
+	config = config_readw(socket, RL5C4XX_CONFIG);
+
+	/* Set the default timings, don't trust the original values */
+	ctl = RL5C4XX_16CTL_IO_TIMING | RL5C4XX_16CTL_MEM_TIMING;
+
+	if(socket->dev->device < PCI_DEVICE_ID_RICOH_RL5C475) {
+		ctl |= RL5C46X_16CTL_LEVEL_1 | RL5C46X_16CTL_LEVEL_2;
+	} else {
+		config |= RL5C4XX_CONFIG_PREFETCH;
+	}
+
+	config_writew(socket, RL5C4XX_16BIT_CTL, ctl);
+	config_writew(socket, RL5C4XX_CONFIG, config);
+
+	ricoh_set_zv(socket);
+
+	return 0;
+}
+
+#endif /* CONFIG_CARDBUS */
+
+#endif /* _LINUX_RICOH_H */
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
new file mode 100644
index 0000000..b6843f8
--- /dev/null
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -0,0 +1,163 @@
+/*
+ * rsrc_mgr.c -- Resource management routines and/or wrappers
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include "cs_internal.h"
+
+
+#ifdef CONFIG_PCMCIA_PROBE
+
+static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
+{
+	int irq;
+	u32 mask;
+
+	irq = adj->resource.irq.IRQ;
+	if ((irq < 0) || (irq > 15))
+		return CS_BAD_IRQ;
+
+	if (adj->Action != REMOVE_MANAGED_RESOURCE)
+		return 0;
+
+	mask = 1 << irq;
+
+	if (!(s->irq_mask & mask))
+		return 0;
+
+	s->irq_mask &= ~mask;
+
+	return 0;
+}
+
+#else
+
+static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
+	return CS_SUCCESS;
+}
+
+#endif
+
+
+int pcmcia_adjust_resource_info(adjust_t *adj)
+{
+	struct pcmcia_socket *s;
+	int ret = CS_UNSUPPORTED_FUNCTION;
+	unsigned long flags;
+
+	down_read(&pcmcia_socket_list_rwsem);
+	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
+
+		if (adj->Resource == RES_IRQ)
+			ret = adjust_irq(s, adj);
+
+		else if (s->resource_ops->adjust_resource) {
+
+			/* you can't use the old interface if the new
+			 * one was used before */
+			spin_lock_irqsave(&s->lock, flags);
+			if ((s->resource_setup_done) &&
+			    !(s->resource_setup_old)) {
+				spin_unlock_irqrestore(&s->lock, flags);
+				continue;
+			} else if (!(s->resource_setup_old))
+				s->resource_setup_old = 1;
+			spin_unlock_irqrestore(&s->lock, flags);
+
+			ret = s->resource_ops->adjust_resource(s, adj);
+			if (!ret) {
+				/* as there's no way we know this is the
+				 * last call to adjust_resource_info, we
+				 * always need to assume this is the latest
+				 * one... */
+				spin_lock_irqsave(&s->lock, flags);
+				s->resource_setup_done = 1;
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+	}
+	up_read(&pcmcia_socket_list_rwsem);
+
+	return (ret);
+}
+EXPORT_SYMBOL(pcmcia_adjust_resource_info);
+
+void pcmcia_validate_mem(struct pcmcia_socket *s)
+{
+	if (s->resource_ops->validate_mem)
+		s->resource_ops->validate_mem(s);
+}
+EXPORT_SYMBOL(pcmcia_validate_mem);
+
+int adjust_io_region(struct resource *res, unsigned long r_start,
+		     unsigned long r_end, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->adjust_io_region)
+		return s->resource_ops->adjust_io_region(res, r_start, r_end, s);
+	return -ENOMEM;
+}
+
+struct resource *find_io_region(unsigned long base, int num,
+		   unsigned long align, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_io)
+		return s->resource_ops->find_io(base, num, align, s);
+	return NULL;
+}
+
+struct resource *find_mem_region(u_long base, u_long num, u_long align,
+				 int low, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_mem)
+		return s->resource_ops->find_mem(base, num, align, low, s);
+	return NULL;
+}
+
+void release_resource_db(struct pcmcia_socket *s)
+{
+	if (s->resource_ops->exit)
+		s->resource_ops->exit(s);
+}
+
+
+static int static_init(struct pcmcia_socket *s)
+{
+	unsigned long flags;
+
+	/* the good thing about SS_CAP_STATIC_MAP sockets is
+	 * that they don't need a resource database */
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->resource_setup_done = 1;
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	return 0;
+}
+
+
+struct pccard_resource_ops pccard_static_ops = {
+	.validate_mem = NULL,
+	.adjust_io_region = NULL,
+	.find_io = NULL,
+	.find_mem = NULL,
+	.adjust_resource = NULL,
+	.init = static_init,
+	.exit = NULL,
+};
+EXPORT_SYMBOL(pccard_static_ops);
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
new file mode 100644
index 0000000..5876bab
--- /dev/null
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -0,0 +1,985 @@
+/*
+ * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
+ *
+ * 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 initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999		David A. Hinds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
+MODULE_LICENSE("GPL");
+
+/* Parameters that can be set with 'insmod' */
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+INT_MODULE_PARM(probe_mem,	1);		/* memory probe? */
+#ifdef CONFIG_PCMCIA_PROBE
+INT_MODULE_PARM(probe_io,	1);		/* IO port probe? */
+INT_MODULE_PARM(mem_limit,	0x10000);
+#endif
+
+/* for io_db and mem_db */
+struct resource_map {
+	u_long			base, num;
+	struct resource_map	*next;
+};
+
+struct socket_data {
+	struct resource_map		mem_db;
+	struct resource_map		io_db;
+	unsigned int			rsrc_mem_probe;
+};
+
+static DECLARE_MUTEX(rsrc_sem);
+#define MEM_PROBE_LOW	(1 << 0)
+#define MEM_PROBE_HIGH	(1 << 1)
+
+
+/*======================================================================
+
+    Linux resource management extensions
+
+======================================================================*/
+
+static struct resource *
+make_resource(unsigned long b, unsigned long n, int flags, char *name)
+{
+	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
+
+	if (res) {
+		memset(res, 0, sizeof(*res));
+		res->name = name;
+		res->start = b;
+		res->end = b + n - 1;
+		res->flags = flags;
+	}
+	return res;
+}
+
+static struct resource *
+claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size,
+	     int type, char *name)
+{
+	struct resource *res, *parent;
+
+	parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
+	res = make_resource(base, size, type | IORESOURCE_BUSY, name);
+
+	if (res) {
+#ifdef CONFIG_PCI
+		if (s && s->cb_dev)
+			parent = pci_find_parent_resource(s->cb_dev, res);
+#endif
+		if (!parent || request_resource(parent, res)) {
+			kfree(res);
+			res = NULL;
+		}
+	}
+	return res;
+}
+
+static void free_region(struct resource *res)
+{
+	if (res) {
+		release_resource(res);
+		kfree(res);
+	}
+}
+
+/*======================================================================
+
+    These manage the internal databases of available resources.
+
+======================================================================*/
+
+static int add_interval(struct resource_map *map, u_long base, u_long num)
+{
+    struct resource_map *p, *q;
+
+    for (p = map; ; p = p->next) {
+	if ((p != map) && (p->base+p->num-1 >= base))
+	    return -1;
+	if ((p->next == map) || (p->next->base > base+num-1))
+	    break;
+    }
+    q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
+    if (!q) return CS_OUT_OF_RESOURCE;
+    q->base = base; q->num = num;
+    q->next = p->next; p->next = q;
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int sub_interval(struct resource_map *map, u_long base, u_long num)
+{
+    struct resource_map *p, *q;
+
+    for (p = map; ; p = q) {
+	q = p->next;
+	if (q == map)
+	    break;
+	if ((q->base+q->num > base) && (base+num > q->base)) {
+	    if (q->base >= base) {
+		if (q->base+q->num <= base+num) {
+		    /* Delete whole block */
+		    p->next = q->next;
+		    kfree(q);
+		    /* don't advance the pointer yet */
+		    q = p;
+		} else {
+		    /* Cut off bit from the front */
+		    q->num = q->base + q->num - base - num;
+		    q->base = base + num;
+		}
+	    } else if (q->base+q->num <= base+num) {
+		/* Cut off bit from the end */
+		q->num = base - q->base;
+	    } else {
+		/* Split the block into two pieces */
+		p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
+		if (!p) return CS_OUT_OF_RESOURCE;
+		p->base = base+num;
+		p->num = q->base+q->num - p->base;
+		q->num = base - q->base;
+		p->next = q->next ; q->next = p;
+	    }
+	}
+    }
+    return CS_SUCCESS;
+}
+
+/*======================================================================
+
+    These routines examine a region of IO or memory addresses to
+    determine what ranges might be genuinely available.
+
+======================================================================*/
+
+#ifdef CONFIG_PCMCIA_PROBE
+static void do_io_probe(struct pcmcia_socket *s, kio_addr_t base, kio_addr_t num)
+{
+    struct resource *res;
+    struct socket_data *s_data = s->resource_data;
+    kio_addr_t i, j, bad;
+    int any;
+    u_char *b, hole, most;
+
+    printk(KERN_INFO "cs: IO port probe %#lx-%#lx:",
+	   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) {
+	res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
+	if (!res)
+	    continue;
+	hole = inb(i);
+	for (j = 1; j < 8; j++)
+	    if (inb(i+j) != hole) break;
+	free_region(res);
+	if ((j == 8) && (++b[hole] > b[most]))
+	    most = hole;
+	if (b[most] == 127) break;
+    }
+    kfree(b);
+
+    bad = any = 0;
+    for (i = base; i < base+num; i += 8) {
+	res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
+	if (!res)
+	    continue;
+	for (j = 0; j < 8; j++)
+	    if (inb(i+j) != most) break;
+	free_region(res);
+	if (j < 8) {
+	    if (!any)
+		printk(" excluding");
+	    if (!bad)
+		bad = any = i;
+	} else {
+	    if (bad) {
+		sub_interval(&s_data->io_db, bad, i-bad);
+		printk(" %#lx-%#lx", bad, i-1);
+		bad = 0;
+	    }
+	}
+    }
+    if (bad) {
+	if ((num > 16) && (bad == base) && (i == base+num)) {
+	    printk(" nothing: probe failed.\n");
+	    return;
+	} else {
+	    sub_interval(&s_data->io_db, bad, i-bad);
+	    printk(" %#lx-%#lx", bad, i-1);
+	}
+    }
+
+    printk(any ? "\n" : " clean.\n");
+}
+#endif
+
+/*======================================================================
+
+    This is tricky... when we set up CIS memory, we try to validate
+    the memory window space allocations.
+
+======================================================================*/
+
+/* Validation function for cards with a valid CIS */
+static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info)
+{
+	int ret = -1;
+
+	s->cis_mem.res = res;
+	s->cis_virt = ioremap(res->start, s->map_size);
+	if (s->cis_virt) {
+		ret = pccard_validate_cis(s, BIND_FN_ALL, info);
+		/* invalidate mapping and CIS cache */
+		iounmap(s->cis_virt);
+		s->cis_virt = NULL;
+		destroy_cis_cache(s);
+	}
+	s->cis_mem.res = NULL;
+	if ((ret != 0) || (info->Chains == 0))
+		return 0;
+	return 1;
+}
+
+/* Validation function for simple memory cards */
+static int checksum(struct pcmcia_socket *s, struct resource *res)
+{
+	pccard_mem_map map;
+	int i, a = 0, b = -1, d;
+	void __iomem *virt;
+
+	virt = ioremap(res->start, s->map_size);
+	if (virt) {
+		map.map = 0;
+		map.flags = MAP_ACTIVE;
+		map.speed = 0;
+		map.res = res;
+		map.card_start = 0;
+		s->ops->set_mem_map(s, &map);
+
+		/* Don't bother checking every word... */
+		for (i = 0; i < s->map_size; i += 44) {
+			d = readl(virt+i);
+			a += d;
+			b &= d;
+		}
+
+		map.flags = 0;
+		s->ops->set_mem_map(s, &map);
+
+		iounmap(virt);
+	}
+
+	return (b == -1) ? -1 : (a>>1);
+}
+
+static int
+cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+{
+	struct resource *res1, *res2;
+	cisinfo_t info1, info2;
+	int ret = 0;
+
+	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
+	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+
+	if (res1 && res2) {
+		ret = readable(s, res1, &info1);
+		ret += readable(s, res2, &info2);
+	}
+
+	free_region(res2);
+	free_region(res1);
+
+	return (ret == 2) && (info1.Chains == info2.Chains);
+}
+
+static int
+checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+{
+	struct resource *res1, *res2;
+	int a = -1, b = -1;
+
+	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
+	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+
+	if (res1 && res2) {
+		a = checksum(s, res1);
+		b = checksum(s, res2);
+	}
+
+	free_region(res2);
+	free_region(res1);
+
+	return (a == b) && (a >= 0);
+}
+
+/*======================================================================
+
+    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, struct pcmcia_socket *s)
+{
+    struct socket_data *s_data = s->resource_data;
+    u_long i, j, bad, fail, step;
+
+    printk(KERN_INFO "cs: memory probe 0x%06lx-0x%06lx:",
+	   base, base+num-1);
+    bad = fail = 0;
+    step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
+    /* cis_readable wants to map 2x map_size */
+    if (step < 2 * s->map_size)
+	step = 2 * s->map_size;
+    for (i = j = base; i < base+num; i = j + step) {
+	if (!fail) {
+	    for (j = i; j < base+num; j += step) {
+		if (cis_readable(s, j, step))
+		    break;
+	    }
+	    fail = ((i == base) && (j == base+num));
+	}
+	if (fail) {
+	    for (j = i; j < base+num; j += 2*step)
+		if (checksum_match(s, j, step) &&
+		    checksum_match(s, j + step, step))
+		    break;
+	}
+	if (i != j) {
+	    if (!bad) printk(" excluding");
+	    printk(" %#05lx-%#05lx", i, j-1);
+	    sub_interval(&s_data->mem_db, i, j-i);
+	    bad += j-i;
+	}
+    }
+    printk(bad ? "\n" : " clean.\n");
+    return (num - bad);
+}
+
+#ifdef CONFIG_PCMCIA_PROBE
+
+static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
+{
+    struct socket_data *s_data = s->resource_data;
+    u_long ok;
+    if (m == &s_data->mem_db)
+	return 0;
+    ok = inv_probe(m->next, s);
+    if (ok) {
+	if (m->base >= 0x100000)
+	    sub_interval(&s_data->mem_db, m->base, m->num);
+	return ok;
+    }
+    if (m->base < 0x100000)
+	return 0;
+    return do_mem_probe(m->base, m->num, s);
+}
+
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
+{
+    struct resource_map *m, mm;
+    static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
+    u_long b, i, ok = 0;
+    struct socket_data *s_data = s->resource_data;
+
+    /* We do up to four passes through the list */
+    if (probe_mask & MEM_PROBE_HIGH) {
+	if (inv_probe(s_data->mem_db.next, s) > 0)
+	    return;
+	printk(KERN_NOTICE "cs: warning: no high memory space "
+	       "available!\n");
+    }
+    if ((probe_mask & MEM_PROBE_LOW) == 0)
+	return;
+    for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
+	mm = *m;
+	/* Only probe < 1 MB */
+	if (mm.base >= 0x100000) continue;
+	if ((mm.base | mm.num) & 0xffff) {
+	    ok += do_mem_probe(mm.base, mm.num, s);
+	    continue;
+	}
+	/* Special probe for 64K-aligned block */
+	for (i = 0; i < 4; i++) {
+	    b = order[i] << 12;
+	    if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
+		if (ok >= mem_limit)
+		    sub_interval(&s_data->mem_db, b, 0x10000);
+		else
+		    ok += do_mem_probe(b, 0x10000, s);
+	    }
+	}
+    }
+}
+
+#else /* CONFIG_PCMCIA_PROBE */
+
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
+{
+	struct resource_map *m, mm;
+	struct socket_data *s_data = s->resource_data;
+
+	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
+		mm = *m;
+		if (do_mem_probe(mm.base, mm.num, s))
+			break;
+	}
+}
+
+#endif /* CONFIG_PCMCIA_PROBE */
+
+
+/*
+ * Locking note: Must be called with skt_sem held!
+ */
+static void pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
+{
+	struct socket_data *s_data = s->resource_data;
+	if (probe_mem) {
+		unsigned int probe_mask;
+
+		down(&rsrc_sem);
+
+		probe_mask = MEM_PROBE_LOW;
+		if (s->features & SS_CAP_PAGE_REGS)
+			probe_mask = MEM_PROBE_HIGH;
+
+		if (probe_mask & ~s_data->rsrc_mem_probe) {
+			s_data->rsrc_mem_probe |= probe_mask;
+
+			if (s->state & SOCKET_PRESENT)
+				validate_mem(s, probe_mask);
+		}
+
+		up(&rsrc_sem);
+	}
+}
+
+struct pcmcia_align_data {
+	unsigned long	mask;
+	unsigned long	offset;
+	struct resource_map	*map;
+};
+
+static void
+pcmcia_common_align(void *align_data, struct resource *res,
+		    unsigned long size, unsigned long align)
+{
+	struct pcmcia_align_data *data = align_data;
+	unsigned long start;
+	/*
+	 * Ensure that we have the correct start address
+	 */
+	start = (res->start & ~data->mask) + data->offset;
+	if (start < res->start)
+		start += data->mask + 1;
+	res->start = start;
+}
+
+static void
+pcmcia_align(void *align_data, struct resource *res,
+	     unsigned long size, unsigned long align)
+{
+	struct pcmcia_align_data *data = align_data;
+	struct resource_map *m;
+
+	pcmcia_common_align(data, res, size, align);
+
+	for (m = data->map->next; m != data->map; m = m->next) {
+		unsigned long start = m->base;
+		unsigned long end = m->base + m->num - 1;
+
+		/*
+		 * If the lower resources are not available, try aligning
+		 * to this entry of the resource database to see if it'll
+		 * fit here.
+		 */
+		if (res->start < start) {
+			res->start = start;
+			pcmcia_common_align(data, res, size, align);
+		}
+
+		/*
+		 * If we're above the area which was passed in, there's
+		 * no point proceeding.
+		 */
+		if (res->start >= res->end)
+			break;
+
+		if ((res->start + size - 1) <= end)
+			break;
+	}
+
+	/*
+	 * If we failed to find something suitable, ensure we fail.
+	 */
+	if (m == data->map)
+		res->start = res->end;
+}
+
+/*
+ * Adjust an existing IO region allocation, but making sure that we don't
+ * encroach outside the resources which the user supplied.
+ */
+static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
+				      unsigned long r_end, struct pcmcia_socket *s)
+{
+	struct resource_map *m;
+	struct socket_data *s_data = s->resource_data;
+	int ret = -ENOMEM;
+
+	down(&rsrc_sem);
+	for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
+		unsigned long start = m->base;
+		unsigned long end = m->base + m->num - 1;
+
+		if (start > r_start || r_end > end)
+			continue;
+
+		ret = adjust_resource(res, r_start, r_end - r_start + 1);
+		break;
+	}
+	up(&rsrc_sem);
+
+	return ret;
+}
+
+/*======================================================================
+
+    These find ranges of I/O ports or memory addresses that are not
+    currently allocated by other devices.
+
+    The 'align' field should reflect the number of bits of address
+    that need to be preserved from the initial value of *base.  It
+    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'.
+
+======================================================================*/
+
+struct resource *nonstatic_find_io_region(unsigned long base, int num,
+		   unsigned long align, struct pcmcia_socket *s)
+{
+	struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id);
+	struct socket_data *s_data = s->resource_data;
+	struct pcmcia_align_data data;
+	unsigned long min = base;
+	int ret;
+
+	if (align == 0)
+		align = 0x10000;
+
+	data.mask = align - 1;
+	data.offset = base & data.mask;
+	data.map = &s_data->io_db;
+
+	down(&rsrc_sem);
+#ifdef CONFIG_PCI
+	if (s->cb_dev) {
+		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
+					     min, 0, pcmcia_align, &data);
+	} else
+#endif
+		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
+					1, pcmcia_align, &data);
+	up(&rsrc_sem);
+
+	if (ret != 0) {
+		kfree(res);
+		res = NULL;
+	}
+	return res;
+}
+
+struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align,
+				 int low, struct pcmcia_socket *s)
+{
+	struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id);
+	struct socket_data *s_data = s->resource_data;
+	struct pcmcia_align_data data;
+	unsigned long min, max;
+	int ret, i;
+
+	low = low || !(s->features & SS_CAP_PAGE_REGS);
+
+	data.mask = align - 1;
+	data.offset = base & data.mask;
+	data.map = &s_data->mem_db;
+
+	for (i = 0; i < 2; i++) {
+		if (low) {
+			max = 0x100000UL;
+			min = base < max ? base : 0;
+		} else {
+			max = ~0UL;
+			min = 0x100000UL + base;
+		}
+
+		down(&rsrc_sem);
+#ifdef CONFIG_PCI
+		if (s->cb_dev) {
+			ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
+						     1, min, 0,
+						     pcmcia_align, &data);
+		} else
+#endif
+			ret = allocate_resource(&iomem_resource, res, num, min,
+						max, 1, pcmcia_align, &data);
+		up(&rsrc_sem);
+		if (ret == 0 || low)
+			break;
+		low = 1;
+	}
+
+	if (ret != 0) {
+		kfree(res);
+		res = NULL;
+	}
+	return res;
+}
+
+
+static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj)
+{
+	u_long base, num;
+	struct socket_data *data = s->resource_data;
+	int ret;
+
+	base = adj->resource.memory.Base;
+	num = adj->resource.memory.Size;
+	if ((num == 0) || (base+num-1 < base))
+		return CS_BAD_SIZE;
+
+	ret = CS_SUCCESS;
+
+	down(&rsrc_sem);
+	switch (adj->Action) {
+	case ADD_MANAGED_RESOURCE:
+		ret = add_interval(&data->mem_db, base, num);
+		break;
+	case REMOVE_MANAGED_RESOURCE:
+		ret = sub_interval(&data->mem_db, base, num);
+		if (ret == CS_SUCCESS) {
+			struct pcmcia_socket *socket;
+			down_read(&pcmcia_socket_list_rwsem);
+			list_for_each_entry(socket, &pcmcia_socket_list, socket_list)
+				release_cis_mem(socket);
+			up_read(&pcmcia_socket_list_rwsem);
+		}
+		break;
+	default:
+		ret = CS_UNSUPPORTED_FUNCTION;
+	}
+	up(&rsrc_sem);
+
+	return ret;
+}
+
+
+static int adjust_io(struct pcmcia_socket *s, adjust_t *adj)
+{
+	struct socket_data *data = s->resource_data;
+	kio_addr_t base, num;
+	int ret = CS_SUCCESS;
+
+	base = adj->resource.io.BasePort;
+	num = adj->resource.io.NumPorts;
+	if ((base < 0) || (base > 0xffff))
+		return CS_BAD_BASE;
+	if ((num <= 0) || (base+num > 0x10000) || (base+num <= base))
+		return CS_BAD_SIZE;
+
+	down(&rsrc_sem);
+	switch (adj->Action) {
+	case ADD_MANAGED_RESOURCE:
+		if (add_interval(&data->io_db, base, num) != 0) {
+			ret = CS_IN_USE;
+			break;
+		}
+#ifdef CONFIG_PCMCIA_PROBE
+		if (probe_io)
+			do_io_probe(s, base, num);
+#endif
+		break;
+	case REMOVE_MANAGED_RESOURCE:
+		sub_interval(&data->io_db, base, num);
+		break;
+	default:
+		ret = CS_UNSUPPORTED_FUNCTION;
+		break;
+	}
+	up(&rsrc_sem);
+
+	return ret;
+}
+
+
+static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj)
+{
+	switch (adj->Resource) {
+	case RES_MEMORY_RANGE:
+		return adjust_memory(s, adj);
+	case RES_IO_RANGE:
+		return adjust_io(s, adj);
+	}
+	return CS_UNSUPPORTED_FUNCTION;
+}
+
+static int nonstatic_init(struct pcmcia_socket *s)
+{
+	struct socket_data *data;
+
+	data = kmalloc(sizeof(struct socket_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	memset(data, 0, sizeof(struct socket_data));
+
+	data->mem_db.next = &data->mem_db;
+	data->io_db.next = &data->io_db;
+
+	s->resource_data = (void *) data;
+
+	return 0;
+}
+
+static void nonstatic_release_resource_db(struct pcmcia_socket *s)
+{
+	struct socket_data *data = s->resource_data;
+	struct resource_map *p, *q;
+
+	down(&rsrc_sem);
+	for (p = data->mem_db.next; p != &data->mem_db; p = q) {
+		q = p->next;
+		kfree(p);
+	}
+	for (p = data->io_db.next; p != &data->io_db; p = q) {
+		q = p->next;
+		kfree(p);
+	}
+	up(&rsrc_sem);
+}
+
+
+struct pccard_resource_ops pccard_nonstatic_ops = {
+	.validate_mem = pcmcia_nonstatic_validate_mem,
+	.adjust_io_region = nonstatic_adjust_io_region,
+	.find_io = nonstatic_find_io_region,
+	.find_mem = nonstatic_find_mem_region,
+	.adjust_resource = nonstatic_adjust_resource_info,
+	.init = nonstatic_init,
+	.exit = nonstatic_release_resource_db,
+};
+EXPORT_SYMBOL(pccard_nonstatic_ops);
+
+
+/* sysfs interface to the resource database */
+
+static ssize_t show_io_db(struct class_device *class_dev, char *buf)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	struct socket_data *data;
+	struct resource_map *p;
+	ssize_t ret = 0;
+
+	down(&rsrc_sem);
+	data = s->resource_data;
+
+	for (p = data->io_db.next; p != &data->io_db; p = p->next) {
+		if (ret > (PAGE_SIZE - 10))
+			continue;
+		ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
+				 "0x%08lx - 0x%08lx\n",
+				 ((unsigned long) p->base),
+				 ((unsigned long) p->base + p->num - 1));
+	}
+
+	up(&rsrc_sem);
+	return (ret);
+}
+
+static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size_t count)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	unsigned long start_addr, end_addr;
+	unsigned int add = 1;
+	adjust_t adj;
+	ssize_t ret = 0;
+
+	ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
+	if (ret != 2) {
+		ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
+		add = 0;
+		if (ret != 2) {
+			ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
+			add = 1;
+			if (ret != 2)
+				return -EINVAL;
+		}
+	}
+	if (end_addr <= start_addr)
+		return -EINVAL;
+
+	adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE;
+	adj.Resource = RES_IO_RANGE;
+	adj.resource.io.BasePort = start_addr;
+	adj.resource.io.NumPorts = end_addr - start_addr + 1;
+
+	ret = adjust_io(s, &adj);
+
+	return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db);
+
+static ssize_t show_mem_db(struct class_device *class_dev, char *buf)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	struct socket_data *data;
+	struct resource_map *p;
+	ssize_t ret = 0;
+
+	down(&rsrc_sem);
+	data = s->resource_data;
+
+	for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
+		if (ret > (PAGE_SIZE - 10))
+			continue;
+		ret += snprintf (&buf[ret], (PAGE_SIZE - ret - 1),
+				 "0x%08lx - 0x%08lx\n",
+				 ((unsigned long) p->base),
+				 ((unsigned long) p->base + p->num - 1));
+	}
+
+	up(&rsrc_sem);
+	return (ret);
+}
+
+static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, size_t count)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	unsigned long start_addr, end_addr;
+	unsigned int add = 1;
+	adjust_t adj;
+	ssize_t ret = 0;
+
+	ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr);
+	if (ret != 2) {
+		ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr);
+		add = 0;
+		if (ret != 2) {
+			ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr);
+			add = 1;
+			if (ret != 2)
+				return -EINVAL;
+		}
+	}
+	if (end_addr <= start_addr)
+		return -EINVAL;
+
+	adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE;
+	adj.Resource = RES_MEMORY_RANGE;
+	adj.resource.memory.Base = start_addr;
+	adj.resource.memory.Size = end_addr - start_addr + 1;
+
+	ret = adjust_memory(s, &adj);
+
+	return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db);
+
+static struct class_device_attribute *pccard_rsrc_attributes[] = {
+	&class_device_attr_available_resources_io,
+	&class_device_attr_available_resources_mem,
+	NULL,
+};
+
+static int __devinit pccard_sysfs_add_rsrc(struct class_device *class_dev)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	struct class_device_attribute **attr;
+	int ret = 0;
+	if (s->resource_ops != &pccard_nonstatic_ops)
+		return 0;
+
+	for (attr = pccard_rsrc_attributes; *attr; attr++) {
+		ret = class_device_create_file(class_dev, *attr);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static void __devexit pccard_sysfs_remove_rsrc(struct class_device *class_dev)
+{
+	struct pcmcia_socket *s = class_get_devdata(class_dev);
+	struct class_device_attribute **attr;
+
+	if (s->resource_ops != &pccard_nonstatic_ops)
+		return;
+
+	for (attr = pccard_rsrc_attributes; *attr; attr++)
+		class_device_remove_file(class_dev, *attr);
+}
+
+static struct class_interface pccard_rsrc_interface = {
+	.class = &pcmcia_socket_class,
+	.add = &pccard_sysfs_add_rsrc,
+	.remove = __devexit_p(&pccard_sysfs_remove_rsrc),
+};
+
+static int __init nonstatic_sysfs_init(void)
+{
+	return class_interface_register(&pccard_rsrc_interface);
+}
+
+static void __exit nonstatic_sysfs_exit(void)
+{
+	class_interface_unregister(&pccard_rsrc_interface);
+}
+
+module_init(nonstatic_sysfs_init);
+module_exit(nonstatic_sysfs_exit);
diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c
new file mode 100644
index 0000000..7c57fdd
--- /dev/null
+++ b/drivers/pcmcia/sa1100_assabet.c
@@ -0,0 +1,141 @@
+/*
+ * drivers/pcmcia/sa1100_assabet.c
+ *
+ * PCMCIA implementation routines for Assabet
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/signal.h>
+#include <asm/arch/assabet.h>
+
+#include "sa1100_generic.h"
+
+static struct pcmcia_irqs irqs[] = {
+	{ 1, ASSABET_IRQ_GPIO_CF_CD,   "CF CD"   },
+	{ 1, ASSABET_IRQ_GPIO_CF_BVD2, "CF BVD2" },
+	{ 1, ASSABET_IRQ_GPIO_CF_BVD1, "CF BVD1" },
+};
+
+static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	skt->irq = ASSABET_IRQ_GPIO_CF_IRQ;
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+/*
+ * Release all resources.
+ */
+static void assabet_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void
+assabet_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+	unsigned long levels = GPLR;
+
+	state->detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1;
+	state->ready  = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0;
+	state->bvd1   = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0;
+	state->bvd2   = (levels & ASSABET_GPIO_CF_BVD2) ? 1 : 0;
+	state->wrprot = 0; /* Not available on Assabet. */
+	state->vs_3v  = 1; /* Can only apply 3.3V on Assabet. */
+	state->vs_Xv  = 0;
+}
+
+static int
+assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+	unsigned int mask;
+
+	switch (state->Vcc) {
+	case 0:
+		mask = 0;
+		break;
+
+	case 50:
+		printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V...\n",
+			__FUNCTION__);
+
+	case 33:  /* Can only apply 3.3V to the CF slot. */
+		mask = ASSABET_BCR_CF_PWR;
+		break;
+
+	default:
+		printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
+			state->Vcc);
+		return -1;
+	}
+
+	/* Silently ignore Vpp, output enable, speaker enable. */
+
+	if (state->flags & SS_RESET)
+		mask |= ASSABET_BCR_CF_RST;
+
+	ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask);
+
+	return 0;
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation.  This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+static void assabet_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	/*
+	 * Enable CF bus
+	 */
+	ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF);
+
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+/*
+ * Disable card status IRQs on suspend.
+ */
+static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+	/*
+	 * Tristate the CF bus signals.  Also assert CF
+	 * reset as per user guide page 4-11.
+	 */
+	ASSABET_BCR_set(ASSABET_BCR_CF_BUS_OFF | ASSABET_BCR_CF_RST);
+}
+
+static struct pcmcia_low_level assabet_pcmcia_ops = { 
+	.owner			= THIS_MODULE,
+
+	.hw_init		= assabet_pcmcia_hw_init,
+	.hw_shutdown		= assabet_pcmcia_hw_shutdown,
+
+	.socket_state		= assabet_pcmcia_socket_state,
+	.configure_socket	= assabet_pcmcia_configure_socket,
+
+	.socket_init		= assabet_pcmcia_socket_init,
+	.socket_suspend		= assabet_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_assabet_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_assabet() && !machine_has_neponset())
+		ret = sa11xx_drv_pcmcia_probe(dev, &assabet_pcmcia_ops, 1, 1);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1100_badge4.c
new file mode 100644
index 0000000..c6b262b
--- /dev/null
+++ b/drivers/pcmcia/sa1100_badge4.c
@@ -0,0 +1,169 @@
+/*
+ * linux/drivers/pcmcia/sa1100_badge4.c
+ *
+ * BadgePAD 4 PCMCIA specific routines
+ *
+ *   Christopher Hoover <ch@hpl.hp.com>
+ *
+ * Copyright (C) 2002 Hewlett-Packard Company
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/badge4.h>
+#include <asm/hardware/sa1111.h>
+
+#include "sa1111_generic.h"
+
+/*
+ * BadgePAD 4 Details
+ *
+ * PCM Vcc:
+ *
+ *  PCM Vcc on BadgePAD 4 can be jumpered for 3v3 (short pins 1 and 3
+ *  on JP6) or 5v0 (short pins 3 and 5 on JP6).
+ *
+ * PCM Vpp:
+ *
+ *  PCM Vpp on BadgePAD 4 can be jumpered for 12v0 (short pins 4 and 6
+ *  on JP6) or tied to PCM Vcc (short pins 2 and 4 on JP6).  N.B.,
+ *  12v0 operation requires that the power supply actually supply 12v0
+ *  via pin 7 of JP7.
+ *
+ * CF Vcc:
+ *
+ *  CF Vcc on BadgePAD 4 can be jumpered either for 3v3 (short pins 1
+ *  and 2 on JP10) or 5v0 (short pins 2 and 3 on JP10).
+ *
+ * Unfortunately there's no way programmatically to determine how a
+ * given board is jumpered.  This code assumes a default jumpering
+ * as described below.
+ *
+ * If the defaults aren't correct, you may override them with a pcmv
+ * setup argument: pcmv=<pcm vcc>,<pcm vpp>,<cf vcc>.  The units are
+ * tenths of volts; e.g. pcmv=33,120,50 indicates 3v3 PCM Vcc, 12v0
+ * PCM Vpp, and 5v0 CF Vcc.
+ *
+ */
+
+static int badge4_pcmvcc = 50;  /* pins 3 and 5 jumpered on JP6 */
+static int badge4_pcmvpp = 50;  /* pins 2 and 4 jumpered on JP6 */
+static int badge4_cfvcc = 33;   /* pins 1 and 2 jumpered on JP10 */
+
+static void complain_about_jumpering(const char *whom,
+				     const char *supply,
+				     int given, int wanted)
+{
+	printk(KERN_ERR
+	 "%s: %s %d.%dV wanted but board is jumpered for %s %d.%dV operation"
+	 "; re-jumper the board and/or use pcmv=xx,xx,xx\n",
+	       whom, supply,
+	       wanted / 10, wanted % 10,
+	       supply,
+	       given / 10, given % 10);
+}
+
+static int
+badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+	int ret;
+
+	switch (skt->nr) {
+	case 0:
+		if ((state->Vcc != 0) &&
+		    (state->Vcc != badge4_pcmvcc)) {
+			complain_about_jumpering(__FUNCTION__, "pcmvcc",
+						 badge4_pcmvcc, state->Vcc);
+			// Apply power regardless of the jumpering.
+			// return -1;
+		}
+		if ((state->Vpp != 0) &&
+		    (state->Vpp != badge4_pcmvpp)) {
+			complain_about_jumpering(__FUNCTION__, "pcmvpp",
+						 badge4_pcmvpp, state->Vpp);
+			return -1;
+		}
+		break;
+
+	case 1:
+		if ((state->Vcc != 0) &&
+		    (state->Vcc != badge4_cfvcc)) {
+			complain_about_jumpering(__FUNCTION__, "cfvcc",
+						 badge4_cfvcc, state->Vcc);
+			return -1;
+		}
+		break;
+
+	default:
+		return -1;
+	}
+
+	ret = sa1111_pcmcia_configure_socket(skt, state);
+	if (ret == 0) {
+		unsigned long flags;
+		int need5V;
+
+		local_irq_save(flags);
+
+		need5V = ((state->Vcc == 50) || (state->Vpp == 50));
+
+		badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(skt->nr), need5V);
+
+		local_irq_restore(flags);
+	}
+
+	return 0;
+}
+
+static struct pcmcia_low_level badge4_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+	.init			= sa1111_pcmcia_hw_init,
+	.shutdown		= sa1111_pcmcia_hw_shutdown,
+	.socket_state		= sa1111_pcmcia_socket_state,
+	.configure_socket	= badge4_pcmcia_configure_socket,
+
+	.socket_init		= sa1111_pcmcia_socket_init,
+	.socket_suspend		= sa1111_pcmcia_socket_suspend,
+};
+
+int pcmcia_badge4_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_badge4()) {
+		printk(KERN_INFO
+		       "%s: badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n",
+		       __FUNCTION__,
+		       badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc);
+
+		ret = sa11xx_drv_pcmcia_probe(dev, &badge4_pcmcia_ops, 0, 2);
+	}
+
+	return ret;
+}
+
+static int __init pcmv_setup(char *s)
+{
+	int v[4];
+
+	s = get_options(s, ARRAY_SIZE(v), v);
+
+	if (v[0] >= 1) badge4_pcmvcc = v[1];
+	if (v[0] >= 2) badge4_pcmvpp = v[2];
+	if (v[0] >= 3) badge4_cfvcc = v[3];
+
+	return 1;
+}
+
+__setup("pcmv=", pcmv_setup);
diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c
new file mode 100644
index 0000000..2b3c289
--- /dev/null
+++ b/drivers/pcmcia/sa1100_cerf.c
@@ -0,0 +1,110 @@
+/*
+ * drivers/pcmcia/sa1100_cerf.c
+ *
+ * PCMCIA implementation routines for CerfBoard
+ * Based off the Assabet.
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/arch/cerf.h>
+#include "sa1100_generic.h"
+
+#define CERF_SOCKET	1
+
+static struct pcmcia_irqs irqs[] = {
+	{ CERF_SOCKET, CERF_IRQ_GPIO_CF_CD,   "CF_CD"   },
+	{ CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD2, "CF_BVD2" },
+	{ CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD1, "CF_BVD1" }
+};
+
+static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	skt->irq = CERF_IRQ_GPIO_CF_IRQ;
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void
+cerf_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+	unsigned long levels = GPLR;
+
+	state->detect	= (levels & CERF_GPIO_CF_CD)  ?0:1;
+	state->ready	= (levels & CERF_GPIO_CF_IRQ) ?1:0;
+	state->bvd1	= (levels & CERF_GPIO_CF_BVD1)?1:0;
+	state->bvd2	= (levels & CERF_GPIO_CF_BVD2)?1:0;
+	state->wrprot	= 0;
+	state->vs_3v	= 1;
+	state->vs_Xv	= 0;
+}
+
+static int
+cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+			     const socket_state_t *state)
+{
+	switch (state->Vcc) {
+	case 0:
+	case 50:
+	case 33:
+		break;
+
+	default:
+		printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
+			__FUNCTION__, state->Vcc);
+		return -1;
+	}
+
+	if (state->flags & SS_RESET) {
+		GPSR = CERF_GPIO_CF_RESET;
+	} else {
+		GPCR = CERF_GPIO_CF_RESET;
+	}
+
+	return 0;
+}
+
+static void cerf_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void cerf_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static struct pcmcia_low_level cerf_pcmcia_ops = { 
+	.owner			= THIS_MODULE,
+	.hw_init		= cerf_pcmcia_hw_init,
+	.hw_shutdown		= cerf_pcmcia_hw_shutdown,
+	.socket_state		= cerf_pcmcia_socket_state,
+	.configure_socket	= cerf_pcmcia_configure_socket,
+
+	.socket_init		= cerf_pcmcia_socket_init,
+	.socket_suspend		= cerf_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_cerf_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_cerf())
+		ret = sa11xx_drv_pcmcia_probe(dev, &cerf_pcmcia_ops, CERF_SOCKET, 1);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
new file mode 100644
index 0000000..874990e
--- /dev/null
+++ b/drivers/pcmcia/sa1100_generic.c
@@ -0,0 +1,131 @@
+/*======================================================================
+
+    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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+
+#include "sa1100_generic.h"
+
+static int (*sa11x0_pcmcia_hw_init[])(struct device *dev) = {
+#ifdef CONFIG_SA1100_ASSABET
+	pcmcia_assabet_init,
+#endif
+#ifdef CONFIG_SA1100_CERF
+	pcmcia_cerf_init,
+#endif
+#ifdef CONFIG_SA1100_H3600
+	pcmcia_h3600_init,
+#endif
+#ifdef CONFIG_SA1100_SHANNON
+	pcmcia_shannon_init,
+#endif
+#ifdef CONFIG_SA1100_SIMPAD
+	pcmcia_simpad_init,
+#endif
+};
+
+static int sa11x0_drv_pcmcia_probe(struct device *dev)
+{
+	int i, ret = -ENODEV;
+
+	/*
+	 * Initialise any "on-board" PCMCIA sockets.
+	 */
+	for (i = 0; i < ARRAY_SIZE(sa11x0_pcmcia_hw_init); i++) {
+		ret = sa11x0_pcmcia_hw_init[i](dev);
+		if (ret == 0)
+			break;
+	}
+
+	return ret;
+}
+
+static int sa11x0_drv_pcmcia_suspend(struct device *dev, u32 state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int sa11x0_drv_pcmcia_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+static struct device_driver sa11x0_pcmcia_driver = {
+	.probe		= sa11x0_drv_pcmcia_probe,
+	.remove		= soc_common_drv_pcmcia_remove,
+	.name		= "sa11x0-pcmcia",
+	.bus		= &platform_bus_type,
+	.suspend 	= sa11x0_drv_pcmcia_suspend,
+	.resume 	= sa11x0_drv_pcmcia_resume,
+};
+
+/* sa11x0_pcmcia_init()
+ * ^^^^^^^^^^^^^^^^^^^^
+ *
+ * This routine performs low-level PCMCIA initialization and then
+ * registers this socket driver with Card Services.
+ *
+ * Returns: 0 on success, -ve error code on failure
+ */
+static int __init sa11x0_pcmcia_init(void)
+{
+	return driver_register(&sa11x0_pcmcia_driver);
+}
+
+/* sa11x0_pcmcia_exit()
+ * ^^^^^^^^^^^^^^^^^^^^
+ * Invokes the low-level kernel service to free IRQs associated with this
+ * socket controller and reset GPIO edge detection.
+ */
+static void __exit sa11x0_pcmcia_exit(void)
+{
+	driver_unregister(&sa11x0_pcmcia_driver);
+}
+
+MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11x0 Socket Controller");
+MODULE_LICENSE("Dual MPL/GPL");
+
+module_init(sa11x0_pcmcia_init);
+module_exit(sa11x0_pcmcia_exit);
diff --git a/drivers/pcmcia/sa1100_generic.h b/drivers/pcmcia/sa1100_generic.h
new file mode 100644
index 0000000..794f96a
--- /dev/null
+++ b/drivers/pcmcia/sa1100_generic.h
@@ -0,0 +1,24 @@
+#include "soc_common.h"
+#include "sa11xx_base.h"
+
+/*
+ * Declaration for all machine specific init/exit functions.
+ */
+extern int pcmcia_adsbitsy_init(struct device *);
+extern int pcmcia_assabet_init(struct device *);
+extern int pcmcia_badge4_init(struct device *);
+extern int pcmcia_cerf_init(struct device *);
+extern int pcmcia_flexanet_init(struct device *);
+extern int pcmcia_freebird_init(struct device *);
+extern int pcmcia_gcplus_init(struct device *);
+extern int pcmcia_graphicsmaster_init(struct device *);
+extern int pcmcia_h3600_init(struct device *);
+extern int pcmcia_pangolin_init(struct device *);
+extern int pcmcia_pfs168_init(struct device *);
+extern int pcmcia_shannon_init(struct device *);
+extern int pcmcia_simpad_init(struct device *);
+extern int pcmcia_stork_init(struct device *);
+extern int pcmcia_system3_init(struct device *);
+extern int pcmcia_trizeps_init(struct device *);
+extern int pcmcia_xp860_init(struct device *);
+extern int pcmcia_yopy_init(struct device *);
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c
new file mode 100644
index 0000000..64fd5e3
--- /dev/null
+++ b/drivers/pcmcia/sa1100_h3600.c
@@ -0,0 +1,142 @@
+/*
+ * drivers/pcmcia/sa1100_h3600.c
+ *
+ * PCMCIA implementation routines for H3600
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/arch/h3600.h>
+
+#include "sa1100_generic.h"
+
+static struct pcmcia_irqs irqs[] = {
+	{ 0, IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" },
+	{ 1, IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" }
+};
+
+static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	skt->irq = skt->nr ? IRQ_GPIO_H3600_PCMCIA_IRQ1
+			   : IRQ_GPIO_H3600_PCMCIA_IRQ0;
+
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+  
+	/* Disable CF bus: */
+	clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
+	clr_h3600_egpio(IPAQ_EGPIO_OPT_ON);
+	set_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
+}
+
+static void
+h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+	unsigned long levels = GPLR;
+
+	switch (skt->nr) {
+	case 0:
+		state->detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1;
+		state->ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0;
+		state->bvd1 = 0;
+		state->bvd2 = 0;
+		state->wrprot = 0; /* Not available on H3600. */
+		state->vs_3v = 0;
+		state->vs_Xv = 0;
+		break;
+
+	case 1:
+		state->detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1;
+		state->ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0;
+		state->bvd1 = 0;
+		state->bvd2 = 0;
+		state->wrprot = 0; /* Not available on H3600. */
+		state->vs_3v = 0;
+		state->vs_Xv = 0;
+		break;
+	}
+}
+
+static int
+h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+	if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) {
+		printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n",
+		       state->Vcc / 10, state->Vcc % 10);
+		return -1;
+	}
+
+	if (state->flags & SS_RESET)
+		set_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
+	else
+		clr_h3600_egpio(IPAQ_EGPIO_CARD_RESET);
+
+	/* Silently ignore Vpp, output enable, speaker enable. */
+
+	return 0;
+}
+
+static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	/* Enable CF bus: */
+	set_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
+	set_h3600_egpio(IPAQ_EGPIO_OPT_ON);
+	clr_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
+
+	msleep(10);
+
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+	/*
+	 * FIXME:  This doesn't fit well.  We don't have the mechanism in
+	 * the generic PCMCIA layer to deal with the idea of two sockets
+	 * on one bus.  We rely on the cs.c behaviour shutting down
+	 * socket 0 then socket 1.
+	 */
+	if (skt->nr == 1) {
+		clr_h3600_egpio(IPAQ_EGPIO_OPT_ON);
+		clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON);
+		/* hmm, does this suck power? */
+		set_h3600_egpio(IPAQ_EGPIO_OPT_RESET);
+	}
+}
+
+struct pcmcia_low_level h3600_pcmcia_ops = { 
+	.owner			= THIS_MODULE,
+	.hw_init		= h3600_pcmcia_hw_init,
+	.hw_shutdown		= h3600_pcmcia_hw_shutdown,
+	.socket_state		= h3600_pcmcia_socket_state,
+	.configure_socket	= h3600_pcmcia_configure_socket,
+
+	.socket_init		= h3600_pcmcia_socket_init,
+	.socket_suspend		= h3600_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_h3600_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_h3600())
+		ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1100_jornada720.c
new file mode 100644
index 0000000..0a38710
--- /dev/null
+++ b/drivers/pcmcia/sa1100_jornada720.c
@@ -0,0 +1,124 @@
+/*
+ * drivers/pcmcia/sa1100_jornada720.c
+ *
+ * Jornada720 PCMCIA specific routines
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/mach-types.h>
+
+#include "sa1111_generic.h"
+
+#define SOCKET0_POWER   GPIO_GPIO0
+#define SOCKET0_3V      GPIO_GPIO2
+#define SOCKET1_POWER   (GPIO_GPIO1 | GPIO_GPIO3)
+#warning *** Does SOCKET1_3V actually do anything?
+#define SOCKET1_3V	GPIO_GPIO3
+
+static int jornada720_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+  /*
+   * What is all this crap for?
+   */
+  GRER |= 0x00000002;
+  /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */
+  PA_DDR = 0;
+  PA_DWR = 0;
+  PA_SDR = 0;
+  PA_SSR = 0;
+
+  PB_DDR = 0;
+  PB_DWR = 0x01;
+  PB_SDR = 0;
+  PB_SSR = 0;
+
+  PC_DDR = 0x88;
+  PC_DWR = 0x20;
+  PC_SDR = 0;
+  PC_SSR = 0;
+
+  return sa1111_pcmcia_hw_init(skt);
+}
+
+static int
+jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+  unsigned int pa_dwr_mask, pa_dwr_set;
+  int ret;
+
+printk("%s(): config socket %d vcc %d vpp %d\n", __FUNCTION__,
+	skt->nr, state->Vcc, state->Vpp);
+
+  switch (skt->nr) {
+  case 0:
+    pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V;
+
+    switch (state->Vcc) {
+    default:
+    case 0:	pa_dwr_set = 0;					break;
+    case 33:	pa_dwr_set = SOCKET0_POWER | SOCKET0_3V;	break;
+    case 50:	pa_dwr_set = SOCKET0_POWER;			break;
+    }
+    break;
+
+  case 1:
+    pa_dwr_mask = SOCKET1_POWER;
+
+    switch (state->Vcc) {
+    default:
+    case 0:	pa_dwr_set = 0;					break;
+    case 33:	pa_dwr_set = SOCKET1_POWER;			break;
+    case 50:	pa_dwr_set = SOCKET1_POWER;			break;
+    }
+    break;
+
+  default:
+    return -1;
+  }
+
+  if (state->Vpp != state->Vcc && state->Vpp != 0) {
+    printk(KERN_ERR "%s(): slot cannot support VPP %u\n",
+	   __FUNCTION__, state->Vpp);
+    return -1;
+  }
+
+  ret = sa1111_pcmcia_configure_socket(skt, state);
+  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;
+}
+
+static struct pcmcia_low_level jornada720_pcmcia_ops = {
+  .owner		= THIS_MODULE,
+  .hw_init		= jornada720_pcmcia_hw_init,
+  .hw_shutdown		= sa1111_pcmcia_hw_shutdown,
+  .socket_state		= sa1111_pcmcia_socket_state,
+  .configure_socket	= jornada720_pcmcia_configure_socket,
+
+  .socket_init		= sa1111_pcmcia_socket_init,
+  .socket_suspend	= sa1111_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_jornada720_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_jornada720())
+		ret = sa11xx_drv_pcmcia_probe(dev, &jornada720_pcmcia_ops, 0, 2);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c
new file mode 100644
index 0000000..5e34b3e
--- /dev/null
+++ b/drivers/pcmcia/sa1100_neponset.c
@@ -0,0 +1,143 @@
+/*
+ * linux/drivers/pcmcia/sa1100_neponset.c
+ *
+ * Neponset PCMCIA specific routines
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/neponset.h>
+#include <asm/hardware/sa1111.h>
+
+#include "sa1111_generic.h"
+
+/*
+ * Neponset uses the Maxim MAX1600, with the following connections:
+ *
+ *   MAX1600      Neponset
+ *
+ *    A0VCC        SA-1111 GPIO A<1>
+ *    A1VCC        SA-1111 GPIO A<0>
+ *    A0VPP        CPLD NCR A0VPP
+ *    A1VPP        CPLD NCR A1VPP
+ *    B0VCC        SA-1111 GPIO A<2>
+ *    B1VCC        SA-1111 GPIO A<3>
+ *    B0VPP        ground (slot B is CF)
+ *    B1VPP        ground (slot B is CF)
+ *
+ *     VX          VCC (5V)
+ *     VY          VCC3_3 (3.3V)
+ *     12INA       12V
+ *     12INB       ground (slot B is CF)
+ *
+ * The MAX1600 CODE pin is tied to ground, placing the device in 
+ * "Standard Intel code" mode. Refer to the Maxim data sheet for
+ * the corresponding truth table.
+ */
+
+static int
+neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+	unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set;
+	int ret;
+
+	switch (skt->nr) {
+	case 0:
+		pa_dwr_mask = GPIO_A0 | GPIO_A1;
+		ncr_mask = NCR_A0VPP | NCR_A1VPP;
+
+		if (state->Vpp == 0)
+			ncr_set = 0;
+		else if (state->Vpp == 120)
+			ncr_set = NCR_A1VPP;
+		else if (state->Vpp == state->Vcc)
+			ncr_set = NCR_A0VPP;
+		else {
+			printk(KERN_ERR "%s(): unrecognized VPP %u\n",
+			       __FUNCTION__, state->Vpp);
+			return -1;
+		}
+		break;
+
+	case 1:
+		pa_dwr_mask = GPIO_A2 | GPIO_A3;
+		ncr_mask = 0;
+		ncr_set = 0;
+
+		if (state->Vpp != state->Vcc && state->Vpp != 0) {
+			printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n",
+			       __FUNCTION__, state->Vpp);
+			return -1;
+		}
+		break;
+
+	default:
+		return -1;
+	}
+
+	/*
+	 * pa_dwr_set is the mask for selecting Vcc on both sockets.
+	 * pa_dwr_mask selects which bits (and therefore socket) we change.
+	 */
+	switch (state->Vcc) {
+	default:
+	case 0:  pa_dwr_set = 0;		break;
+	case 33: pa_dwr_set = GPIO_A1|GPIO_A2;	break;
+	case 50: pa_dwr_set = GPIO_A0|GPIO_A3;	break;
+	}
+
+	ret = sa1111_pcmcia_configure_socket(skt, state);
+	if (ret == 0) {
+		unsigned long flags;
+
+		local_irq_save(flags);
+		NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set;
+
+		local_irq_restore(flags);
+		sa1111_set_io(SA1111_DEV(skt->dev), pa_dwr_mask, pa_dwr_set);
+	}
+
+	return 0;
+}
+
+static void neponset_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	if (skt->nr == 0)
+		NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
+
+	sa1111_pcmcia_socket_init(skt);
+}
+
+static struct pcmcia_low_level neponset_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+	.hw_init		= sa1111_pcmcia_hw_init,
+	.hw_shutdown		= sa1111_pcmcia_hw_shutdown,
+	.socket_state		= sa1111_pcmcia_socket_state,
+	.configure_socket	= neponset_pcmcia_configure_socket,
+	.socket_init		= neponset_pcmcia_socket_init,
+	.socket_suspend 	= sa1111_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_neponset_init(struct sa1111_dev *sadev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_assabet()) {
+		/*
+		 * Set GPIO_A<3:0> to be outputs for the MAX1600,
+		 * and switch to standby mode.
+		 */
+		sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0);
+		sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
+		sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0);
+		ret = sa11xx_drv_pcmcia_probe(&sadev->dev, &neponset_pcmcia_ops, 0, 2);
+	}
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c
new file mode 100644
index 0000000..7bc9e59
--- /dev/null
+++ b/drivers/pcmcia/sa1100_shannon.c
@@ -0,0 +1,125 @@
+/*
+ * drivers/pcmcia/sa1100_shannon.c
+ *
+ * PCMCIA implementation routines for Shannon
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/arch/shannon.h>
+#include <asm/irq.h>
+#include "sa1100_generic.h"
+
+static struct pcmcia_irqs irqs[] = {
+	{ 0, SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" },
+	{ 1, SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" },
+};
+
+static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	/* All those are inputs */
+	GPDR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | 
+		  SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
+	GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 | 
+		  SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
+
+	skt->irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0;
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void shannon_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void
+shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+			    struct pcmcia_state *state)
+{
+	unsigned long levels = GPLR;
+
+	switch (skt->nr) {
+	case 0:
+		state->detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1;
+		state->ready  = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0;
+		state->wrprot = 0; /* Not available on Shannon. */
+		state->bvd1   = 1; 
+		state->bvd2   = 1; 
+		state->vs_3v  = 1; /* FIXME Can only apply 3.3V on Shannon. */
+		state->vs_Xv  = 0;
+		break;
+
+	case 1:
+		state->detect = (levels & SHANNON_GPIO_EJECT_1) ? 0 : 1;
+		state->ready  = (levels & SHANNON_GPIO_RDY_1) ? 1 : 0;
+		state->wrprot = 0; /* Not available on Shannon. */
+		state->bvd1   = 1; 
+		state->bvd2   = 1; 
+		state->vs_3v  = 1; /* FIXME Can only apply 3.3V on Shannon. */
+		state->vs_Xv  = 0;
+		break;
+	}
+}
+
+static int
+shannon_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+				const socket_state_t *state)
+{
+	switch (state->Vcc) {
+	case 0:	/* power off */
+		printk(KERN_WARNING "%s(): CS asked for 0V, still applying 3.3V..\n", __FUNCTION__);
+		break;
+	case 50:
+		printk(KERN_WARNING "%s(): CS asked for 5V, applying 3.3V..\n", __FUNCTION__);
+	case 33:
+		break;
+	default:
+		printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
+		       __FUNCTION__, state->Vcc);
+		return -1;
+	}
+
+	printk(KERN_WARNING "%s(): Warning, Can't perform reset\n", __FUNCTION__);
+	
+	/* Silently ignore Vpp, output enable, speaker enable. */
+
+	return 0;
+}
+
+static void shannon_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void shannon_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static struct pcmcia_low_level shannon_pcmcia_ops = {
+	.owner			= THIS_MODULE,
+	.hw_init		= shannon_pcmcia_hw_init,
+	.hw_shutdown		= shannon_pcmcia_hw_shutdown,
+	.socket_state		= shannon_pcmcia_socket_state,
+	.configure_socket	= shannon_pcmcia_configure_socket,
+
+	.socket_init		= shannon_pcmcia_socket_init,
+	.socket_suspend		= shannon_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_shannon_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_shannon())
+		ret = sa11xx_drv_pcmcia_probe(dev, &shannon_pcmcia_ops, 0, 2);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c
new file mode 100644
index 0000000..c2ecf11
--- /dev/null
+++ b/drivers/pcmcia/sa1100_simpad.c
@@ -0,0 +1,135 @@
+/*
+ * drivers/pcmcia/sa1100_simpad.c
+ *
+ * PCMCIA implementation routines for simpad
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.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 clear_cs3_bit(int value);
+
+static struct pcmcia_irqs irqs[] = {
+	{ 1, IRQ_GPIO_CF_CD, "CF_CD" },
+};
+
+static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+
+	clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+
+	skt->irq = IRQ_GPIO_CF_IRQ;
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+	/* Disable CF bus: */
+	//set_cs3_bit(PCMCIA_BUFF_DIS);
+	clear_cs3_bit(PCMCIA_RESET);       
+}
+
+static void
+simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+			   struct pcmcia_state *state)
+{
+	unsigned long levels = GPLR;
+	long cs3reg = get_cs3_shadow();
+
+	state->detect=((levels & GPIO_CF_CD)==0)?1:0;
+	state->ready=(levels & GPIO_CF_IRQ)?1:0;
+	state->bvd1=1; /* Not available on Simpad. */
+	state->bvd2=1; /* Not available on Simpad. */
+	state->wrprot=0; /* Not available on Simpad. */
+  
+	if((cs3reg & 0x0c) == 0x0c) {
+		state->vs_3v=0;
+		state->vs_Xv=0;
+	} else {
+		state->vs_3v=1;
+		state->vs_Xv=0;
+	}
+}
+
+static int
+simpad_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+			       const socket_state_t *state)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	/* Murphy: see table of MIC2562a-1 */
+	switch (state->Vcc) {
+	case 0:
+		clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+		break;
+
+	case 33:  
+		clear_cs3_bit(VCC_3V_EN|EN1);
+		set_cs3_bit(VCC_5V_EN|EN0);
+		break;
+
+	case 50:
+		clear_cs3_bit(VCC_5V_EN|EN1);
+		set_cs3_bit(VCC_3V_EN|EN0);
+		break;
+
+	default:
+		printk(KERN_ERR "%s(): unrecognized Vcc %u\n",
+			__FUNCTION__, state->Vcc);
+		clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
+		local_irq_restore(flags);
+		return -1;
+	}
+
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void simpad_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+	set_cs3_bit(PCMCIA_RESET);
+}
+
+static struct pcmcia_low_level simpad_pcmcia_ops = { 
+	.owner			= THIS_MODULE,
+	.hw_init		= simpad_pcmcia_hw_init,
+	.hw_shutdown		= simpad_pcmcia_hw_shutdown,
+	.socket_state		= simpad_pcmcia_socket_state,
+	.configure_socket	= simpad_pcmcia_configure_socket,
+	.socket_init		= simpad_pcmcia_socket_init,
+	.socket_suspend		= simpad_pcmcia_socket_suspend,
+};
+
+int __init pcmcia_simpad_init(struct device *dev)
+{
+	int ret = -ENODEV;
+
+	if (machine_is_simpad())
+		ret = sa11xx_drv_pcmcia_probe(dev, &simpad_pcmcia_ops, 1, 1);
+
+	return ret;
+}
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
new file mode 100644
index 0000000..bd9d6b2
--- /dev/null
+++ b/drivers/pcmcia/sa1111_generic.c
@@ -0,0 +1,196 @@
+/*
+ * linux/drivers/pcmcia/sa1111_generic.c
+ *
+ * We implement the generic parts of a SA1111 PCMCIA driver.  This
+ * basically means we handle everything except controlling the
+ * power.  Power is machine specific...
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/sa1111.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "sa1111_generic.h"
+
+static struct pcmcia_irqs irqs[] = {
+	{ 0, IRQ_S0_CD_VALID,    "SA1111 PCMCIA card detect" },
+	{ 0, IRQ_S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1"        },
+	{ 1, IRQ_S1_CD_VALID,    "SA1111 CF card detect"     },
+	{ 1, IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1"            },
+};
+
+int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+	if (skt->irq == NO_IRQ)
+		skt->irq = skt->nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT;
+
+	return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
+	unsigned long status = sa1111_readl(sadev->mapbase + SA1111_PCSR);
+
+	switch (skt->nr) {
+	case 0:
+		state->detect = status & PCSR_S0_DETECT ? 0 : 1;
+		state->ready  = status & PCSR_S0_READY  ? 1 : 0;
+		state->bvd1   = status & PCSR_S0_BVD1   ? 1 : 0;
+		state->bvd2   = status & PCSR_S0_BVD2   ? 1 : 0;
+		state->wrprot = status & PCSR_S0_WP     ? 1 : 0;
+		state->vs_3v  = status & PCSR_S0_VS1    ? 0 : 1;
+		state->vs_Xv  = status & PCSR_S0_VS2    ? 0 : 1;
+		break;
+
+	case 1:
+		state->detect = status & PCSR_S1_DETECT ? 0 : 1;
+		state->ready  = status & PCSR_S1_READY  ? 1 : 0;
+		state->bvd1   = status & PCSR_S1_BVD1   ? 1 : 0;
+		state->bvd2   = status & PCSR_S1_BVD2   ? 1 : 0;
+		state->wrprot = status & PCSR_S1_WP     ? 1 : 0;
+		state->vs_3v  = status & PCSR_S1_VS1    ? 0 : 1;
+		state->vs_Xv  = status & PCSR_S1_VS2    ? 0 : 1;
+		break;
+	}
+}
+
+int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state)
+{
+	struct sa1111_dev *sadev = SA1111_DEV(skt->dev);
+	unsigned int pccr_skt_mask, pccr_set_mask, val;
+	unsigned long flags;
+
+	switch (skt->nr) {
+	case 0:
+		pccr_skt_mask = PCCR_S0_RST|PCCR_S0_FLT|PCCR_S0_PWAITEN|PCCR_S0_PSE;
+		break;
+
+	case 1:
+		pccr_skt_mask = PCCR_S1_RST|PCCR_S1_FLT|PCCR_S1_PWAITEN|PCCR_S1_PSE;
+		break;
+
+	default:
+		return -1;
+	}
+
+	pccr_set_mask = 0;
+
+	if (state->Vcc != 0)
+		pccr_set_mask |= PCCR_S0_PWAITEN|PCCR_S1_PWAITEN;
+	if (state->Vcc == 50)
+		pccr_set_mask |= PCCR_S0_PSE|PCCR_S1_PSE;
+	if (state->flags & SS_RESET)
+		pccr_set_mask |= PCCR_S0_RST|PCCR_S1_RST;
+	if (state->flags & SS_OUTPUT_ENA)
+		pccr_set_mask |= PCCR_S0_FLT|PCCR_S1_FLT;
+
+	local_irq_save(flags);
+	val = sa1111_readl(sadev->mapbase + SA1111_PCCR);
+	val &= ~pccr_skt_mask;
+	val |= pccr_set_mask & pccr_skt_mask;
+	sa1111_writel(val, sadev->mapbase + SA1111_PCCR);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+	soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static int pcmcia_probe(struct sa1111_dev *dev)
+{
+	char *base;
+
+	if (!request_mem_region(dev->res.start, 512,
+				SA1111_DRIVER_NAME(dev)))
+		return -EBUSY;
+
+	base = dev->mapbase;
+
+	/*
+	 * Initialise the suspend state.
+	 */
+	sa1111_writel(PCSSR_S0_SLEEP | PCSSR_S1_SLEEP, base + SA1111_PCSSR);
+	sa1111_writel(PCCR_S0_FLT | PCCR_S1_FLT, base + SA1111_PCCR);
+
+#ifdef CONFIG_SA1100_BADGE4
+	pcmcia_badge4_init(&dev->dev);
+#endif
+#ifdef CONFIG_SA1100_JORNADA720
+	pcmcia_jornada720_init(&dev->dev);
+#endif
+#ifdef CONFIG_ARCH_LUBBOCK
+	pcmcia_lubbock_init(dev);
+#endif
+#ifdef CONFIG_ASSABET_NEPONSET
+	pcmcia_neponset_init(dev);
+#endif
+	return 0;
+}
+
+static int __devexit pcmcia_remove(struct sa1111_dev *dev)
+{
+	soc_common_drv_pcmcia_remove(&dev->dev);
+	release_mem_region(dev->res.start, 512);
+	return 0;
+}
+
+static int pcmcia_suspend(struct sa1111_dev *dev, u32 state)
+{
+	return pcmcia_socket_dev_suspend(&dev->dev, state);
+}
+
+static int pcmcia_resume(struct sa1111_dev *dev)
+{
+	return pcmcia_socket_dev_resume(&dev->dev);
+}
+
+static struct sa1111_driver pcmcia_driver = {
+	.drv = {
+		.name	= "sa1111-pcmcia",
+	},
+	.devid		= SA1111_DEVID_PCMCIA,
+	.probe		= pcmcia_probe,
+	.remove		= __devexit_p(pcmcia_remove),
+	.suspend	= pcmcia_suspend,
+	.resume		= pcmcia_resume,
+};
+
+static int __init sa1111_drv_pcmcia_init(void)
+{
+	return sa1111_driver_register(&pcmcia_driver);
+}
+
+static void __exit sa1111_drv_pcmcia_exit(void)
+{
+	sa1111_driver_unregister(&pcmcia_driver);
+}
+
+module_init(sa1111_drv_pcmcia_init);
+module_exit(sa1111_drv_pcmcia_exit);
+
+MODULE_DESCRIPTION("SA1111 PCMCIA card socket driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/sa1111_generic.h b/drivers/pcmcia/sa1111_generic.h
new file mode 100644
index 0000000..10ced4a
--- /dev/null
+++ b/drivers/pcmcia/sa1111_generic.h
@@ -0,0 +1,15 @@
+#include "soc_common.h"
+#include "sa11xx_base.h"
+
+extern int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *);
+extern void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *);
+extern void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *, struct pcmcia_state *);
+extern int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *, const socket_state_t *);
+extern void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *);
+extern void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *);
+
+extern int pcmcia_badge4_init(struct device *);
+extern int pcmcia_jornada720_init(struct device *);
+extern int pcmcia_lubbock_init(struct sa1111_dev *);
+extern int pcmcia_neponset_init(struct sa1111_dev *);
+
diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c
new file mode 100644
index 0000000..db04ffb
--- /dev/null
+++ b/drivers/pcmcia/sa11xx_base.c
@@ -0,0 +1,200 @@
+/*======================================================================
+
+    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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/cpufreq.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "soc_common.h"
+#include "sa11xx_base.h"
+
+
+/*
+ * sa1100_pcmcia_default_mecr_timing
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Calculate MECR clock wait states for given CPU clock
+ * speed and command wait state. This function can be over-
+ * written by a board specific version.
+ *
+ * The default is to simply calculate the BS values as specified in
+ * the INTEL SA1100 development manual
+ * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
+ * that's section 10.2.5 in _my_ version of the manual ;)
+ */
+static unsigned int
+sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
+				  unsigned int cpu_speed,
+				  unsigned int cmd_time)
+{
+	return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
+}
+
+/* sa1100_pcmcia_set_mecr()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * set MECR value for socket <sock> based on this sockets
+ * io, mem and attribute space access speed.
+ * Call board specific BS value calculation to allow boards
+ * to tweak the BS values.
+ */
+static int
+sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
+{
+	struct soc_pcmcia_timing timing;
+	u32 mecr, old_mecr;
+	unsigned long flags;
+	unsigned int bs_io, bs_mem, bs_attr;
+
+	soc_common_pcmcia_get_timing(skt, &timing);
+
+	bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
+	bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
+	bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
+
+	local_irq_save(flags);
+
+	old_mecr = mecr = MECR;
+	MECR_FAST_SET(mecr, skt->nr, 0);
+	MECR_BSIO_SET(mecr, skt->nr, bs_io);
+	MECR_BSA_SET(mecr, skt->nr, bs_attr);
+	MECR_BSM_SET(mecr, skt->nr, bs_mem);
+	if (old_mecr != mecr)
+		MECR = mecr;
+
+	local_irq_restore(flags);
+
+	debug(skt, 2, "FAST %X  BSM %X  BSA %X  BSIO %X\n",
+	      MECR_FAST_GET(mecr, skt->nr),
+	      MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
+	      MECR_BSIO_GET(mecr, skt->nr));
+
+	return 0;
+}
+
+#ifdef CONFIG_CPU_FREQ
+static int
+sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
+			       unsigned long val,
+			       struct cpufreq_freqs *freqs)
+{
+	switch (val) {
+	case CPUFREQ_PRECHANGE:
+		if (freqs->new > freqs->old)
+			sa1100_pcmcia_set_mecr(skt, freqs->new);
+		break;
+
+	case CPUFREQ_POSTCHANGE:
+		if (freqs->new < freqs->old)
+			sa1100_pcmcia_set_mecr(skt, freqs->new);
+		break;
+	case CPUFREQ_RESUMECHANGE:
+		sa1100_pcmcia_set_mecr(skt, freqs->new);
+		break;
+	}
+
+	return 0;
+}
+
+#endif
+
+static int
+sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
+{
+	return sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
+}
+
+static int
+sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
+{
+	struct soc_pcmcia_timing timing;
+	unsigned int clock = cpufreq_get(0);
+	unsigned long mecr = MECR;
+	char *p = buf;
+
+	soc_common_pcmcia_get_timing(skt, &timing);
+
+	p+=sprintf(p, "I/O      : %u (%u)\n", timing.io,
+		   sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
+
+	p+=sprintf(p, "attribute: %u (%u)\n", timing.attr,
+		   sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
+
+	p+=sprintf(p, "common   : %u (%u)\n", timing.mem,
+		   sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
+
+	return p - buf;
+}
+
+int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
+			    int first, int nr)
+{
+	/*
+	 * set default MECR calculation if the board specific
+	 * code did not specify one...
+	 */
+	if (!ops->get_timing)
+		ops->get_timing = sa1100_pcmcia_default_mecr_timing;
+
+	/* Provide our SA11x0 specific timing routines. */
+	ops->set_timing  = sa1100_pcmcia_set_timing;
+	ops->show_timing = sa1100_pcmcia_show_timing;
+#ifdef CONFIG_CPU_FREQ
+	ops->frequency_change = sa1100_pcmcia_frequency_change;
+#endif
+
+	return soc_common_drv_pcmcia_probe(dev, ops, first, nr);
+}
+EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
+
+static int __init sa11xx_pcmcia_init(void)
+{
+	return 0;
+}
+module_init(sa11xx_pcmcia_init);
+
+static void __exit sa11xx_pcmcia_exit(void) {}
+
+module_exit(sa11xx_pcmcia_exit);
+
+MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
+MODULE_LICENSE("Dual MPL/GPL");
diff --git a/drivers/pcmcia/sa11xx_base.h b/drivers/pcmcia/sa11xx_base.h
new file mode 100644
index 0000000..7bc2082
--- /dev/null
+++ b/drivers/pcmcia/sa11xx_base.h
@@ -0,0 +1,123 @@
+/*======================================================================
+
+    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(_PCMCIA_SA1100_H)
+# define _PCMCIA_SA1100_H
+
+/* 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."
+ */
+
+/* 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 MECR_SOCKET_0_SHIFT (0)
+#define MECR_SOCKET_1_SHIFT (16)
+
+#define MECR_BS_MASK        (0x1f)
+#define MECR_FAST_MODE_MASK (0x01)
+
+#define MECR_BSIO_SHIFT (0)
+#define MECR_BSA_SHIFT  (5)
+#define MECR_BSM_SHIFT  (10)
+#define MECR_FAST_SHIFT (15)
+
+#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 sa1100_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);
+}
+
+/* This function returns the (approximate) command assertion period, in
+ * nanoseconds, for a given CPU clock frequency and MECR BS value:
+ */
+static inline unsigned int sa1100_pcmcia_cmd_time(unsigned int cpu_clock_khz,
+						  unsigned int pcmcia_mecr_bs){
+  return (((10000000 * 2) / cpu_clock_khz) * (3 * (pcmcia_mecr_bs + 1))) / 10;
+}
+
+
+extern int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr);
+
+#endif  /* !defined(_PCMCIA_SA1100_H) */
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
new file mode 100644
index 0000000..888b70e
--- /dev/null
+++ b/drivers/pcmcia/soc_common.c
@@ -0,0 +1,850 @@
+/*======================================================================
+
+    Common support code for the PCMCIA control functionality of
+    integrated SOCs like the SA-11x0 and PXA2xx 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/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/cpufreq.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "soc_common.h"
+
+/* FIXME: platform dependent resource declaration has to move out of this file */
+#ifdef CONFIG_ARCH_PXA
+#include <asm/arch/pxa-regs.h>
+#endif
+
+#ifdef DEBUG
+
+static int pc_debug;
+module_param(pc_debug, int, 0644);
+
+void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func,
+		      int lvl, const char *fmt, ...)
+{
+	va_list args;
+	if (pc_debug > lvl) {
+		printk(KERN_DEBUG "skt%u: %s: ", skt->nr, func);
+		va_start(args, fmt);
+		printk(fmt, args);
+		va_end(args);
+	}
+}
+
+#endif
+
+#define to_soc_pcmcia_socket(x)	container_of(x, struct soc_pcmcia_socket, socket)
+
+static unsigned short
+calc_speed(unsigned short *spds, int num, unsigned short dflt)
+{
+	unsigned short speed = 0;
+	int i;
+
+	for (i = 0; i < num; i++)
+		if (speed < spds[i])
+			speed = spds[i];
+	if (speed == 0)
+		speed = dflt;
+
+	return speed;
+}
+
+void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt, struct soc_pcmcia_timing *timing)
+{
+	timing->io = calc_speed(skt->spd_io, MAX_IO_WIN, SOC_PCMCIA_IO_ACCESS);
+	timing->mem = calc_speed(skt->spd_mem, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS);
+	timing->attr = calc_speed(skt->spd_attr, MAX_WIN, SOC_PCMCIA_3V_MEM_ACCESS);
+}
+EXPORT_SYMBOL(soc_common_pcmcia_get_timing);
+
+static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
+{
+	struct pcmcia_state state;
+	unsigned int stat;
+
+	memset(&state, 0, sizeof(struct pcmcia_state));
+
+	skt->ops->socket_state(skt, &state);
+
+	stat = state.detect  ? SS_DETECT : 0;
+	stat |= state.ready  ? SS_READY  : 0;
+	stat |= state.wrprot ? SS_WRPROT : 0;
+	stat |= state.vs_3v  ? SS_3VCARD : 0;
+	stat |= state.vs_Xv  ? SS_XVCARD : 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:
+	 */
+	stat |= skt->cs_state.Vcc ? SS_POWERON : 0;
+
+	if (skt->cs_state.flags & SS_IOCARD)
+		stat |= state.bvd1 ? SS_STSCHG : 0;
+	else {
+		if (state.bvd1 == 0)
+			stat |= SS_BATDEAD;
+		else if (state.bvd2 == 0)
+			stat |= SS_BATWARN;
+	}
+	return stat;
+}
+
+/*
+ * soc_common_pcmcia_config_skt
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Convert PCMCIA socket state to our socket configure structure.
+ */
+static int
+soc_common_pcmcia_config_skt(struct soc_pcmcia_socket *skt, socket_state_t *state)
+{
+	int ret;
+
+	ret = skt->ops->configure_socket(skt, state);
+	if (ret == 0) {
+		/*
+		 * This really needs a better solution.  The IRQ
+		 * may or may not be claimed by the driver.
+		 */
+		if (skt->irq_state != 1 && state->io_irq) {
+			skt->irq_state = 1;
+			set_irq_type(skt->irq, IRQT_FALLING);
+		} else if (skt->irq_state == 1 && state->io_irq == 0) {
+			skt->irq_state = 0;
+			set_irq_type(skt->irq, IRQT_NOEDGE);
+		}
+
+		skt->cs_state = *state;
+	}
+
+	if (ret < 0)
+		printk(KERN_ERR "soc_common_pcmcia: unable to configure "
+		       "socket %d\n", skt->nr);
+
+	return ret;
+}
+
+/* soc_common_pcmcia_sock_init()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * (Re-)Initialise the socket, turning on status interrupts
+ * and PCMCIA bus.  This must wait for power to stabilise
+ * so that the card status signals report correctly.
+ *
+ * Returns: 0
+ */
+static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+
+	debug(skt, 2, "initializing socket\n");
+
+	skt->ops->socket_init(skt);
+	return 0;
+}
+
+
+/*
+ * soc_common_pcmcia_suspend()
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * Remove power on the socket, disable IRQs from the card.
+ * Turn off status interrupts, and disable the PCMCIA bus.
+ *
+ * Returns: 0
+ */
+static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+
+	debug(skt, 2, "suspending socket\n");
+
+	skt->ops->socket_suspend(skt);
+
+	return 0;
+}
+
+static DEFINE_SPINLOCK(status_lock);
+
+static void soc_common_check_status(struct soc_pcmcia_socket *skt)
+{
+	unsigned int events;
+
+	debug(skt, 4, "entering PCMCIA monitoring thread\n");
+
+	do {
+		unsigned int status;
+		unsigned long flags;
+
+		status = soc_common_pcmcia_skt_state(skt);
+
+		spin_lock_irqsave(&status_lock, flags);
+		events = (status ^ skt->status) & skt->cs_state.csc_mask;
+		skt->status = status;
+		spin_unlock_irqrestore(&status_lock, flags);
+
+		debug(skt, 4, "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 "  : "");
+
+		if (events)
+			pcmcia_parse_events(&skt->socket, events);
+	} while (events);
+}
+
+/* Let's poll for events in addition to IRQs since IRQ only is unreliable... */
+static void soc_common_pcmcia_poll_event(unsigned long dummy)
+{
+	struct soc_pcmcia_socket *skt = (struct soc_pcmcia_socket *)dummy;
+	debug(skt, 4, "polling for events\n");
+
+	mod_timer(&skt->poll_timer, jiffies + SOC_PCMCIA_POLL_PERIOD);
+
+	soc_common_check_status(skt);
+}
+
+
+/*
+ * Service routine for socket driver interrupts (requested by the
+ * low-level PCMCIA init() operation via soc_common_pcmcia_thread()).
+ * The actual interrupt-servicing work is performed by
+ * soc_common_pcmcia_thread(), largely because the Card Services event-
+ * handling code performs scheduling operations which cannot be
+ * executed from within an interrupt context.
+ */
+static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	struct soc_pcmcia_socket *skt = dev;
+
+	debug(skt, 3, "servicing IRQ %d\n", irq);
+
+	soc_common_check_status(skt);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ *  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
+soc_common_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+
+	skt->status = soc_common_pcmcia_skt_state(skt);
+	*status = skt->status;
+
+	return 0;
+}
+
+
+/*
+ * 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
+soc_common_pcmcia_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+
+	debug(skt, 2, "\n");
+
+	*state = skt->cs_state;
+
+	return 0;
+}
+
+/*
+ * 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
+soc_common_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+
+	debug(skt, 2, "mask: %s%s%s%s%s%sflags: %s%s%s%s%s%sVcc %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);
+
+	return soc_common_pcmcia_config_skt(skt, state);
+}
+
+
+/*
+ * 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
+soc_common_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+	unsigned short speed = map->speed;
+
+	debug(skt, 2, "map %u  speed %u start 0x%08x stop 0x%08x\n",
+		map->map, map->speed, map->start, map->stop);
+	debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n",
+		(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) {
+		if (speed == 0)
+			speed = SOC_PCMCIA_IO_ACCESS;
+	} else {
+		speed = 0;
+	}
+
+	skt->spd_io[map->map] = speed;
+	skt->ops->set_timing(skt);
+
+	if (map->stop == 1)
+		map->stop = PAGE_SIZE-1;
+
+	map->stop -= map->start;
+	map->stop += skt->socket.io_offset;
+	map->start = skt->socket.io_offset;
+
+	return 0;
+}
+
+
+/*
+ * 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
+soc_common_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map)
+{
+	struct soc_pcmcia_socket *skt = to_soc_pcmcia_socket(sock);
+	struct resource *res;
+	unsigned short speed = map->speed;
+
+	debug(skt, 2, "map %u speed %u card_start %08x\n",
+		map->map, map->speed, map->card_start);
+	debug(skt, 2, "flags: %s%s%s%s%s%s%s%s\n",
+		(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)
+		return -EINVAL;
+
+	if (map->flags & MAP_ACTIVE) {
+		if (speed == 0)
+			speed = 300;
+	} else {
+		speed = 0;
+	}
+
+	if (map->flags & MAP_ATTRIB) {
+		res = &skt->res_attr;
+		skt->spd_attr[map->map] = speed;
+		skt->spd_mem[map->map] = 0;
+	} else {
+		res = &skt->res_mem;
+		skt->spd_attr[map->map] = 0;
+		skt->spd_mem[map->map] = speed;
+	}
+
+	skt->ops->set_timing(skt);
+
+	map->static_start = res->start + map->card_start;
+
+	return 0;
+}
+
+struct bittbl {
+	unsigned int mask;
+	const char *name;
+};
+
+static struct bittbl status_bits[] = {
+	{ SS_WRPROT,		"SS_WRPROT"	},
+	{ SS_BATDEAD,		"SS_BATDEAD"	},
+	{ SS_BATWARN,		"SS_BATWARN"	},
+	{ SS_READY,		"SS_READY"	},
+	{ SS_DETECT,		"SS_DETECT"	},
+	{ SS_POWERON,		"SS_POWERON"	},
+	{ SS_STSCHG,		"SS_STSCHG"	},
+	{ SS_3VCARD,		"SS_3VCARD"	},
+	{ SS_XVCARD,		"SS_XVCARD"	},
+};
+
+static struct bittbl conf_bits[] = {
+	{ SS_PWR_AUTO,		"SS_PWR_AUTO"	},
+	{ SS_IOCARD,		"SS_IOCARD"	},
+	{ SS_RESET,		"SS_RESET"	},
+	{ SS_DMA_MODE,		"SS_DMA_MODE"	},
+	{ SS_SPKR_ENA,		"SS_SPKR_ENA"	},
+	{ SS_OUTPUT_ENA,	"SS_OUTPUT_ENA"	},
+};
+
+static void
+dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, int sz)
+{
+	char *b = *p;
+	int i;
+
+	b += sprintf(b, "%-9s:", prefix);
+	for (i = 0; i < sz; i++)
+		if (val & bits[i].mask)
+			b += sprintf(b, " %s", bits[i].name);
+	*b++ = '\n';
+	*p = b;
+}
+
+/*
+ * Implements the /sys/class/pcmcia_socket/??/status file.
+ *
+ * Returns: the number of characters added to the buffer
+ */
+static ssize_t show_status(struct class_device *class_dev, char *buf)
+{
+	struct soc_pcmcia_socket *skt =
+		container_of(class_dev, struct soc_pcmcia_socket, socket.dev);
+	char *p = buf;
+
+	p+=sprintf(p, "slot     : %d\n", skt->nr);
+
+	dump_bits(&p, "status", skt->status,
+		  status_bits, ARRAY_SIZE(status_bits));
+	dump_bits(&p, "csc_mask", skt->cs_state.csc_mask,
+		  status_bits, ARRAY_SIZE(status_bits));
+	dump_bits(&p, "cs_flags", skt->cs_state.flags,
+		  conf_bits, ARRAY_SIZE(conf_bits));
+
+	p+=sprintf(p, "Vcc      : %d\n", skt->cs_state.Vcc);
+	p+=sprintf(p, "Vpp      : %d\n", skt->cs_state.Vpp);
+	p+=sprintf(p, "IRQ      : %d (%d)\n", skt->cs_state.io_irq, skt->irq);
+	if (skt->ops->show_timing)
+		p+=skt->ops->show_timing(skt, p);
+
+	return p-buf;
+}
+static CLASS_DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
+
+
+static struct pccard_operations soc_common_pcmcia_operations = {
+	.init			= soc_common_pcmcia_sock_init,
+	.suspend		= soc_common_pcmcia_suspend,
+	.get_status		= soc_common_pcmcia_get_status,
+	.get_socket		= soc_common_pcmcia_get_socket,
+	.set_socket		= soc_common_pcmcia_set_socket,
+	.set_io_map		= soc_common_pcmcia_set_io_map,
+	.set_mem_map		= soc_common_pcmcia_set_mem_map,
+};
+
+
+int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt,
+			    struct pcmcia_irqs *irqs, int nr)
+{
+	int i, res = 0;
+
+	for (i = 0; i < nr; i++) {
+		if (irqs[i].sock != skt->nr)
+			continue;
+		res = request_irq(irqs[i].irq, soc_common_pcmcia_interrupt,
+				  SA_INTERRUPT, irqs[i].str, skt);
+		if (res)
+			break;
+		set_irq_type(irqs[i].irq, IRQT_NOEDGE);
+	}
+
+	if (res) {
+		printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n",
+			irqs[i].irq, res);
+
+		while (i--)
+			if (irqs[i].sock == skt->nr)
+				free_irq(irqs[i].irq, skt);
+	}
+	return res;
+}
+EXPORT_SYMBOL(soc_pcmcia_request_irqs);
+
+void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt,
+			  struct pcmcia_irqs *irqs, int nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		if (irqs[i].sock == skt->nr)
+			free_irq(irqs[i].irq, skt);
+}
+EXPORT_SYMBOL(soc_pcmcia_free_irqs);
+
+void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt,
+			     struct pcmcia_irqs *irqs, int nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		if (irqs[i].sock == skt->nr)
+			set_irq_type(irqs[i].irq, IRQT_NOEDGE);
+}
+EXPORT_SYMBOL(soc_pcmcia_disable_irqs);
+
+void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt,
+			    struct pcmcia_irqs *irqs, int nr)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		if (irqs[i].sock == skt->nr) {
+			set_irq_type(irqs[i].irq, IRQT_RISING);
+			set_irq_type(irqs[i].irq, IRQT_BOTHEDGE);
+		}
+}
+EXPORT_SYMBOL(soc_pcmcia_enable_irqs);
+
+
+LIST_HEAD(soc_pcmcia_sockets);
+DECLARE_MUTEX(soc_pcmcia_sockets_lock);
+
+static const char *skt_names[] = {
+	"PCMCIA socket 0",
+	"PCMCIA socket 1",
+};
+
+struct skt_dev_info {
+	int nskt;
+	struct soc_pcmcia_socket skt[0];
+};
+
+#define SKT_DEV_INFO_SIZE(n) \
+	(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
+
+#ifdef CONFIG_CPU_FREQ
+static int
+soc_pcmcia_notifier(struct notifier_block *nb, unsigned long val, void *data)
+{
+	struct soc_pcmcia_socket *skt;
+	struct cpufreq_freqs *freqs = data;
+	int ret = 0;
+
+	down(&soc_pcmcia_sockets_lock);
+	list_for_each_entry(skt, &soc_pcmcia_sockets, node)
+		if ( skt->ops->frequency_change )
+			ret += skt->ops->frequency_change(skt, val, freqs);
+	up(&soc_pcmcia_sockets_lock);
+
+	return ret;
+}
+
+static struct notifier_block soc_pcmcia_notifier_block = {
+	.notifier_call	= soc_pcmcia_notifier
+};
+
+static int soc_pcmcia_cpufreq_register(void)
+{
+	int ret;
+
+	ret = cpufreq_register_notifier(&soc_pcmcia_notifier_block,
+					CPUFREQ_TRANSITION_NOTIFIER);
+	if (ret < 0)
+		printk(KERN_ERR "Unable to register CPU frequency change "
+				"notifier for PCMCIA (%d)\n", ret);
+	return ret;
+}
+
+static void soc_pcmcia_cpufreq_unregister(void)
+{
+	cpufreq_unregister_notifier(&soc_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+#define soc_pcmcia_cpufreq_register()
+#define soc_pcmcia_cpufreq_unregister()
+#endif
+
+int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr)
+{
+	struct skt_dev_info *sinfo;
+	struct soc_pcmcia_socket *skt;
+	int ret, i;
+
+	down(&soc_pcmcia_sockets_lock);
+
+	sinfo = kmalloc(SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
+	if (!sinfo) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(sinfo, 0, SKT_DEV_INFO_SIZE(nr));
+	sinfo->nskt = nr;
+
+	/*
+	 * Initialise the per-socket structure.
+	 */
+	for (i = 0; i < nr; i++) {
+		skt = &sinfo->skt[i];
+
+		skt->socket.ops = &soc_common_pcmcia_operations;
+		skt->socket.owner = ops->owner;
+		skt->socket.dev.dev = dev;
+
+		init_timer(&skt->poll_timer);
+		skt->poll_timer.function = soc_common_pcmcia_poll_event;
+		skt->poll_timer.data = (unsigned long)skt;
+		skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
+
+		skt->nr		= first + i;
+		skt->irq	= NO_IRQ;
+		skt->dev	= dev;
+		skt->ops	= ops;
+
+		skt->res_skt.start	= _PCMCIA(skt->nr);
+		skt->res_skt.end	= _PCMCIA(skt->nr) + PCMCIASp - 1;
+		skt->res_skt.name	= skt_names[skt->nr];
+		skt->res_skt.flags	= IORESOURCE_MEM;
+
+		ret = request_resource(&iomem_resource, &skt->res_skt);
+		if (ret)
+			goto out_err_1;
+
+		skt->res_io.start	= _PCMCIAIO(skt->nr);
+		skt->res_io.end		= _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
+		skt->res_io.name	= "io";
+		skt->res_io.flags	= IORESOURCE_MEM | IORESOURCE_BUSY;
+
+		ret = request_resource(&skt->res_skt, &skt->res_io);
+		if (ret)
+			goto out_err_2;
+
+		skt->res_mem.start	= _PCMCIAMem(skt->nr);
+		skt->res_mem.end	= _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
+		skt->res_mem.name	= "memory";
+		skt->res_mem.flags	= IORESOURCE_MEM;
+
+		ret = request_resource(&skt->res_skt, &skt->res_mem);
+		if (ret)
+			goto out_err_3;
+
+		skt->res_attr.start	= _PCMCIAAttr(skt->nr);
+		skt->res_attr.end	= _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
+		skt->res_attr.name	= "attribute";
+		skt->res_attr.flags	= IORESOURCE_MEM;
+
+		ret = request_resource(&skt->res_skt, &skt->res_attr);
+		if (ret)
+			goto out_err_4;
+
+		skt->virt_io = ioremap(skt->res_io.start, 0x10000);
+		if (skt->virt_io == NULL) {
+			ret = -ENOMEM;
+			goto out_err_5;
+		}
+
+		if ( list_empty(&soc_pcmcia_sockets) )
+			soc_pcmcia_cpufreq_register();
+
+		list_add(&skt->node, &soc_pcmcia_sockets);
+
+		/*
+		 * We initialize default socket timing here, because
+		 * we are not guaranteed to see a SetIOMap operation at
+		 * runtime.
+		 */
+		ops->set_timing(skt);
+
+		ret = ops->hw_init(skt);
+		if (ret)
+			goto out_err_6;
+
+		skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD;
+		skt->socket.resource_ops = &pccard_static_ops;
+		skt->socket.irq_mask = 0;
+		skt->socket.map_size = PAGE_SIZE;
+		skt->socket.pci_irq = skt->irq;
+		skt->socket.io_offset = (unsigned long)skt->virt_io;
+
+		skt->status = soc_common_pcmcia_skt_state(skt);
+
+		ret = pcmcia_register_socket(&skt->socket);
+		if (ret)
+			goto out_err_7;
+
+		WARN_ON(skt->socket.sock != i);
+
+		add_timer(&skt->poll_timer);
+
+		class_device_create_file(&skt->socket.dev, &class_device_attr_status);
+	}
+
+	dev_set_drvdata(dev, sinfo);
+	ret = 0;
+	goto out;
+
+	do {
+		skt = &sinfo->skt[i];
+
+		del_timer_sync(&skt->poll_timer);
+		pcmcia_unregister_socket(&skt->socket);
+
+ out_err_7:
+		flush_scheduled_work();
+
+		ops->hw_shutdown(skt);
+ out_err_6:
+ 		list_del(&skt->node);
+		iounmap(skt->virt_io);
+ out_err_5:
+		release_resource(&skt->res_attr);
+ out_err_4:
+		release_resource(&skt->res_mem);
+ out_err_3:
+		release_resource(&skt->res_io);
+ out_err_2:
+		release_resource(&skt->res_skt);
+ out_err_1:
+		i--;
+	} while (i > 0);
+
+	kfree(sinfo);
+
+ out:
+	up(&soc_pcmcia_sockets_lock);
+	return ret;
+}
+
+int soc_common_drv_pcmcia_remove(struct device *dev)
+{
+	struct skt_dev_info *sinfo = dev_get_drvdata(dev);
+	int i;
+
+	dev_set_drvdata(dev, NULL);
+
+	down(&soc_pcmcia_sockets_lock);
+	for (i = 0; i < sinfo->nskt; i++) {
+		struct soc_pcmcia_socket *skt = &sinfo->skt[i];
+
+		del_timer_sync(&skt->poll_timer);
+
+		pcmcia_unregister_socket(&skt->socket);
+
+		flush_scheduled_work();
+
+		skt->ops->hw_shutdown(skt);
+
+		soc_common_pcmcia_config_skt(skt, &dead_socket);
+
+		list_del(&skt->node);
+		iounmap(skt->virt_io);
+		skt->virt_io = NULL;
+		release_resource(&skt->res_attr);
+		release_resource(&skt->res_mem);
+		release_resource(&skt->res_io);
+		release_resource(&skt->res_skt);
+	}
+	if ( list_empty(&soc_pcmcia_sockets) )
+		soc_pcmcia_cpufreq_unregister();
+
+	up(&soc_pcmcia_sockets_lock);
+
+	kfree(sinfo);
+
+	return 0;
+}
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
new file mode 100644
index 0000000..700a155
--- /dev/null
+++ b/drivers/pcmcia/soc_common.h
@@ -0,0 +1,194 @@
+/*
+ * linux/drivers/pcmcia/soc_common.h
+ *
+ * Copyright (C) 2000 John G Dorsey <john+@cs.cmu.edu>
+ *
+ * This file contains definitions for the PCMCIA support code common to
+ * integrated SOCs like the SA-11x0 and PXA2xx microprocessors.
+ */
+#ifndef _ASM_ARCH_PCMCIA
+#define _ASM_ARCH_PCMCIA
+
+/* include the world */
+#include <linux/cpufreq.h>
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+
+struct device;
+struct pcmcia_low_level;
+
+/*
+ * This structure encapsulates per-socket state which we might need to
+ * use when responding to a Card Services query of some kind.
+ */
+struct soc_pcmcia_socket {
+	struct pcmcia_socket	socket;
+
+	/*
+	 * Info from low level handler
+	 */
+	struct device		*dev;
+	unsigned int		nr;
+	unsigned int		irq;
+
+	/*
+	 * Core PCMCIA state
+	 */
+	struct pcmcia_low_level *ops;
+
+	unsigned int		status;
+	socket_state_t		cs_state;
+
+	unsigned short		spd_io[MAX_IO_WIN];
+	unsigned short		spd_mem[MAX_WIN];
+	unsigned short		spd_attr[MAX_WIN];
+
+	struct resource		res_skt;
+	struct resource		res_io;
+	struct resource		res_mem;
+	struct resource		res_attr;
+	void __iomem		*virt_io;
+
+	unsigned int		irq_state;
+
+	struct timer_list	poll_timer;
+	struct list_head	node;
+};
+
+struct pcmcia_state {
+  unsigned detect: 1,
+            ready: 1,
+             bvd1: 1,
+             bvd2: 1,
+           wrprot: 1,
+            vs_3v: 1,
+            vs_Xv: 1;
+};
+
+struct pcmcia_low_level {
+	struct module *owner;
+
+	/* first socket in system */
+	int first;
+	/* nr of sockets */
+	int nr;
+
+	int (*hw_init)(struct soc_pcmcia_socket *);
+	void (*hw_shutdown)(struct soc_pcmcia_socket *);
+
+	void (*socket_state)(struct soc_pcmcia_socket *, struct pcmcia_state *);
+	int (*configure_socket)(struct soc_pcmcia_socket *, const socket_state_t *);
+
+	/*
+	 * Enable card status IRQs on (re-)initialisation.  This can
+	 * be called at initialisation, power management event, or
+	 * pcmcia event.
+	 */
+	void (*socket_init)(struct soc_pcmcia_socket *);
+
+	/*
+	 * Disable card status IRQs and PCMCIA bus on suspend.
+	 */
+	void (*socket_suspend)(struct soc_pcmcia_socket *);
+
+	/*
+	 * Hardware specific timing routines.
+	 * If provided, the get_timing routine overrides the SOC default.
+	 */
+	unsigned int (*get_timing)(struct soc_pcmcia_socket *, unsigned int, unsigned int);
+	int (*set_timing)(struct soc_pcmcia_socket *);
+	int (*show_timing)(struct soc_pcmcia_socket *, char *);
+
+#ifdef CONFIG_CPU_FREQ
+	/*
+	 * CPUFREQ support.
+	 */
+	int (*frequency_change)(struct soc_pcmcia_socket *, unsigned long, struct cpufreq_freqs *);
+#endif
+};
+
+
+struct pcmcia_irqs {
+	int sock;
+	int irq;
+	const char *str;
+};
+
+struct soc_pcmcia_timing {
+	unsigned short io;
+	unsigned short mem;
+	unsigned short attr;
+};
+
+extern int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
+extern void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
+extern void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
+extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
+extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *);
+
+
+extern struct list_head soc_pcmcia_sockets;
+extern struct semaphore soc_pcmcia_sockets_lock;
+
+extern int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr);
+extern int soc_common_drv_pcmcia_remove(struct device *dev);
+
+
+#ifdef DEBUG
+
+extern void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func,
+			     int lvl, const char *fmt, ...);
+
+#define debug(skt, lvl, fmt, arg...) \
+	soc_pcmcia_debug(skt, __func__, lvl, fmt , ## arg)
+
+#else
+#define debug(skt, lvl, fmt, arg...) do { } while (0)
+#endif
+
+
+/*
+ * 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.
+ */
+#define SOC_PCMCIA_IO_ACCESS		(165)
+#define SOC_PCMCIA_5V_MEM_ACCESS	(150)
+#define SOC_PCMCIA_3V_MEM_ACCESS	(300)
+#define SOC_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 SOC_PCMCIA_POLL_PERIOD    (2*HZ)
+
+
+/* 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
+
+#endif
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
new file mode 100644
index 0000000..8eed039
--- /dev/null
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -0,0 +1,228 @@
+/*
+ * socket_sysfs.c -- most of socket-related sysfs output
+ *
+ * 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.
+ *
+ * (C) 2003 - 2004		Dominik Brodowski
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "cs_internal.h"
+
+#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
+
+static ssize_t pccard_show_type(struct class_device *dev, char *buf)
+{
+	int val;
+	struct pcmcia_socket *s = to_socket(dev);
+
+	if (!(s->state & SOCKET_PRESENT))
+		return -ENODEV;
+	s->ops->get_status(s, &val);
+	if (val & SS_CARDBUS)
+		return sprintf(buf, "32-bit\n");
+	if (val & SS_DETECT)
+		return sprintf(buf, "16-bit\n");
+	return sprintf(buf, "invalid\n");
+}
+static CLASS_DEVICE_ATTR(card_type, 0400, pccard_show_type, NULL);
+
+static ssize_t pccard_show_voltage(struct class_device *dev, char *buf)
+{
+	int val;
+	struct pcmcia_socket *s = to_socket(dev);
+
+	if (!(s->state & SOCKET_PRESENT))
+		return -ENODEV;
+	s->ops->get_status(s, &val);
+	if (val & SS_3VCARD)
+		return sprintf(buf, "3.3V\n");
+	if (val & SS_XVCARD)
+		return sprintf(buf, "X.XV\n");
+	return sprintf(buf, "5.0V\n");
+}
+static CLASS_DEVICE_ATTR(card_voltage, 0400, pccard_show_voltage, NULL);
+
+static ssize_t pccard_show_vpp(struct class_device *dev, char *buf)
+{
+	struct pcmcia_socket *s = to_socket(dev);
+	if (!(s->state & SOCKET_PRESENT))
+		return -ENODEV;
+	return sprintf(buf, "%d.%dV\n", s->socket.Vpp / 10, s->socket.Vpp % 10);
+}
+static CLASS_DEVICE_ATTR(card_vpp, 0400, pccard_show_vpp, NULL);
+
+static ssize_t pccard_show_vcc(struct class_device *dev, char *buf)
+{
+	struct pcmcia_socket *s = to_socket(dev);
+	if (!(s->state & SOCKET_PRESENT))
+		return -ENODEV;
+	return sprintf(buf, "%d.%dV\n", s->socket.Vcc / 10, s->socket.Vcc % 10);
+}
+static CLASS_DEVICE_ATTR(card_vcc, 0400, pccard_show_vcc, NULL);
+
+
+static ssize_t pccard_store_insert(struct class_device *dev, const char *buf, size_t count)
+{
+	ssize_t ret;
+	struct pcmcia_socket *s = to_socket(dev);
+
+	if (!count)
+		return -EINVAL;
+
+	ret = pcmcia_insert_card(s);
+
+	return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
+
+static ssize_t pccard_store_eject(struct class_device *dev, const char *buf, size_t count)
+{
+	ssize_t ret;
+	struct pcmcia_socket *s = to_socket(dev);
+
+	if (!count)
+		return -EINVAL;
+
+	ret = pcmcia_eject_card(s);
+
+	return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
+
+
+static ssize_t pccard_show_irq_mask(struct class_device *dev, char *buf)
+{
+	struct pcmcia_socket *s = to_socket(dev);
+	return sprintf(buf, "0x%04x\n", s->irq_mask);
+}
+
+static ssize_t pccard_store_irq_mask(struct class_device *dev, const char *buf, size_t count)
+{
+	ssize_t ret;
+	struct pcmcia_socket *s = to_socket(dev);
+	u32 mask;
+
+	if (!count)
+		return -EINVAL;
+
+	ret = sscanf (buf, "0x%x\n", &mask);
+
+	if (ret == 1) {
+		s->irq_mask &= mask;
+		ret = 0;
+	}
+
+	return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(card_irq_mask, 0600, pccard_show_irq_mask, pccard_store_irq_mask);
+
+
+static ssize_t pccard_show_resource(struct class_device *dev, char *buf)
+{
+	struct pcmcia_socket *s = to_socket(dev);
+	return sprintf(buf, "%s\n", s->resource_setup_done ? "yes" : "no");
+}
+
+static ssize_t pccard_store_resource(struct class_device *dev, const char *buf, size_t count)
+{
+	unsigned long flags;
+	struct pcmcia_socket *s = to_socket(dev);
+
+	if (!count)
+		return -EINVAL;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!s->resource_setup_done) {
+		s->resource_setup_done = 1;
+		spin_unlock_irqrestore(&s->lock, flags);
+
+		down(&s->skt_sem);
+		if ((s->callback) &&
+		    (s->state & SOCKET_PRESENT) &&
+		    !(s->state & SOCKET_CARDBUS)) {
+			if (try_module_get(s->callback->owner)) {
+				s->callback->resources_done(s);
+				module_put(s->callback->owner);
+			}
+		}
+		up(&s->skt_sem);
+
+		return count;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	return count;
+}
+static CLASS_DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
+
+
+static struct class_device_attribute *pccard_socket_attributes[] = {
+	&class_device_attr_card_type,
+	&class_device_attr_card_voltage,
+	&class_device_attr_card_vpp,
+	&class_device_attr_card_vcc,
+	&class_device_attr_card_insert,
+	&class_device_attr_card_eject,
+	&class_device_attr_card_irq_mask,
+	&class_device_attr_available_resources_setup_done,
+	NULL,
+};
+
+static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev)
+{
+	struct class_device_attribute **attr;
+	int ret = 0;
+
+	for (attr = pccard_socket_attributes; *attr; attr++) {
+		ret = class_device_create_file(class_dev, *attr);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static void __devexit pccard_sysfs_remove_socket(struct class_device *class_dev)
+{
+	struct class_device_attribute **attr;
+
+	for (attr = pccard_socket_attributes; *attr; attr++)
+		class_device_remove_file(class_dev, *attr);
+}
+
+struct class_interface pccard_sysfs_interface = {
+	.class = &pcmcia_socket_class,
+	.add = &pccard_sysfs_add_socket,
+	.remove = __devexit_p(&pccard_sysfs_remove_socket),
+};
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
new file mode 100644
index 0000000..aacbbb5
--- /dev/null
+++ b/drivers/pcmcia/tcic.c
@@ -0,0 +1,903 @@
+/*======================================================================
+
+    Device driver for Databook TCIC-2 PCMCIA controller
+
+    tcic.c 1.111 2000/02/15 04:13:12
+
+    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 David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include "tcic.h"
+
+#ifdef DEBUG
+static int pc_debug;
+
+module_param(pc_debug, int, 0644);
+static const char version[] =
+"tcic.c 1.111 2000/02/15 04:13:12 (David Hinds)";
+
+#define debug(lvl, fmt, arg...) do {				\
+	if (pc_debug > (lvl))					\
+		printk(KERN_DEBUG "tcic: " fmt , ## arg);	\
+} while (0)
+#else
+#define debug(lvl, fmt, arg...) do { } while (0)
+#endif
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Databook TCIC-2 PCMCIA socket driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* The base port address of the TCIC-2 chip */
+static unsigned long tcic_base = TCIC_BASE;
+
+/* Specify a socket number to ignore */
+static int ignore = -1;
+
+/* Probe for safe interrupts? */
+static int do_scan = 1;
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[16];
+static int irq_list_count;
+
+/* The card status change interrupt -- 0 means autoselect */
+static int cs_irq;
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval;
+
+/* Delay for card status double-checking */
+static int poll_quick = HZ/20;
+
+/* CCLK external clock time, in nanoseconds.  70 ns = 14.31818 MHz */
+static int cycle_time = 70;
+
+module_param(tcic_base, ulong, 0444);
+module_param(ignore, int, 0444);
+module_param(do_scan, int, 0444);
+module_param(irq_mask, int, 0444);
+module_param_array(irq_list, int, &irq_list_count, 0444);
+module_param(cs_irq, int, 0444);
+module_param(poll_interval, int, 0444);
+module_param(poll_quick, int, 0444);
+module_param(cycle_time, int, 0444);
+
+/*====================================================================*/
+
+static irqreturn_t tcic_interrupt(int irq, void *dev, struct pt_regs *regs);
+static void tcic_timer(u_long data);
+static struct pccard_operations tcic_operations;
+
+struct tcic_socket {
+    u_short	psock;
+    u_char	last_sstat;
+    u_char	id;
+    struct pcmcia_socket	socket;
+};
+
+static struct timer_list poll_timer;
+static int tcic_timer_pending;
+
+static int sockets;
+static struct tcic_socket socket_table[2];
+
+/*====================================================================*/
+
+/* Trick when selecting interrupts: the TCIC sktirq pin is supposed
+   to map to irq 11, but is coded as 0 or 1 in the irq registers. */
+#define TCIC_IRQ(x) ((x) ? (((x) == 11) ? 1 : (x)) : 15)
+
+#ifdef DEBUG_X
+static u_char tcic_getb(u_char reg)
+{
+    u_char val = inb(tcic_base+reg);
+    printk(KERN_DEBUG "tcic_getb(%#lx) = %#x\n", tcic_base+reg, val);
+    return val;
+}
+
+static u_short tcic_getw(u_char reg)
+{
+    u_short val = inw(tcic_base+reg);
+    printk(KERN_DEBUG "tcic_getw(%#lx) = %#x\n", tcic_base+reg, val);
+    return val;
+}
+
+static void tcic_setb(u_char reg, u_char data)
+{
+    printk(KERN_DEBUG "tcic_setb(%#lx, %#x)\n", tcic_base+reg, data);
+    outb(data, tcic_base+reg);
+}
+
+static void tcic_setw(u_char reg, u_short data)
+{
+    printk(KERN_DEBUG "tcic_setw(%#lx, %#x)\n", tcic_base+reg, data);
+    outw(data, tcic_base+reg);
+}
+#else
+#define tcic_getb(reg) inb(tcic_base+reg)
+#define tcic_getw(reg) inw(tcic_base+reg)
+#define tcic_setb(reg, data) outb(data, tcic_base+reg)
+#define tcic_setw(reg, data) outw(data, tcic_base+reg)
+#endif
+
+static void tcic_setl(u_char reg, u_int data)
+{
+#ifdef DEBUG_X
+    printk(KERN_DEBUG "tcic_setl(%#x, %#lx)\n", tcic_base+reg, data);
+#endif
+    outw(data & 0xffff, tcic_base+reg);
+    outw(data >> 16, tcic_base+reg+2);
+}
+
+static u_char tcic_aux_getb(u_short reg)
+{
+    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+    tcic_setb(TCIC_MODE, mode);
+    return tcic_getb(TCIC_AUX);
+}
+
+static void tcic_aux_setb(u_short reg, u_char data)
+{
+    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+    tcic_setb(TCIC_MODE, mode);
+    tcic_setb(TCIC_AUX, data);
+}
+
+static u_short tcic_aux_getw(u_short reg)
+{
+    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+    tcic_setb(TCIC_MODE, mode);
+    return tcic_getw(TCIC_AUX);
+}
+
+static void tcic_aux_setw(u_short reg, u_short data)
+{
+    u_char mode = (tcic_getb(TCIC_MODE) & TCIC_MODE_PGMMASK) | reg;
+    tcic_setb(TCIC_MODE, mode);
+    tcic_setw(TCIC_AUX, data);
+}
+
+/*====================================================================*/
+
+/* Time conversion functions */
+
+static int to_cycles(int ns)
+{
+    if (ns < 14)
+	return 0;
+    else
+	return 2*(ns-14)/cycle_time;
+}
+
+/*====================================================================*/
+
+static volatile u_int irq_hits;
+
+static irqreturn_t __init tcic_irq_count(int irq, void *dev, struct pt_regs *regs)
+{
+    irq_hits++;
+    return IRQ_HANDLED;
+}
+
+static u_int __init try_irq(int irq)
+{
+    u_short cfg;
+
+    irq_hits = 0;
+    if (request_irq(irq, tcic_irq_count, 0, "irq scan", tcic_irq_count) != 0)
+	return -1;
+    mdelay(10);
+    if (irq_hits) {
+	free_irq(irq, tcic_irq_count);
+	return -1;
+    }
+
+    /* Generate one interrupt */
+    cfg = TCIC_SYSCFG_AUTOBUSY | 0x0a00;
+    tcic_aux_setw(TCIC_AUX_SYSCFG, cfg | TCIC_IRQ(irq));
+    tcic_setb(TCIC_IENA, TCIC_IENA_ERR | TCIC_IENA_CFG_HIGH);
+    tcic_setb(TCIC_ICSR, TCIC_ICSR_ERR | TCIC_ICSR_JAM);
+
+    udelay(1000);
+    free_irq(irq, tcic_irq_count);
+
+    /* Turn off interrupts */
+    tcic_setb(TCIC_IENA, TCIC_IENA_CFG_OFF);
+    while (tcic_getb(TCIC_ICSR))
+	tcic_setb(TCIC_ICSR, TCIC_ICSR_JAM);
+    tcic_aux_setw(TCIC_AUX_SYSCFG, cfg);
+    
+    return (irq_hits != 1);
+}
+
+static u_int __init irq_scan(u_int mask0)
+{
+    u_int mask1;
+    int i;
+
+#ifdef __alpha__
+#define PIC 0x4d0
+    /* Don't probe level-triggered interrupts -- reserved for PCI */
+    int level_mask = inb_p(PIC) | (inb_p(PIC+1) << 8);
+    if (level_mask)
+	mask0 &= ~level_mask;
+#endif
+
+    mask1 = 0;
+    if (do_scan) {
+	for (i = 0; i < 16; i++)
+	    if ((mask0 & (1 << i)) && (try_irq(i) == 0))
+		mask1 |= (1 << i);
+	for (i = 0; i < 16; i++)
+	    if ((mask1 & (1 << i)) && (try_irq(i) != 0)) {
+		mask1 ^= (1 << i);
+	    }
+    }
+    
+    if (mask1) {
+	printk("scanned");
+    } else {
+	/* Fallback: just find interrupts that aren't in use */
+	for (i = 0; i < 16; i++)
+	    if ((mask0 & (1 << i)) &&
+		(request_irq(i, tcic_irq_count, 0, "x", tcic_irq_count) == 0)) {
+		mask1 |= (1 << i);
+		free_irq(i, tcic_irq_count);
+	    }
+	printk("default");
+    }
+    
+    printk(") = ");
+    for (i = 0; i < 16; i++)
+	if (mask1 & (1<<i))
+	    printk("%s%d", ((mask1 & ((1<<i)-1)) ? "," : ""), i);
+    printk(" ");
+    
+    return mask1;
+}
+
+/*======================================================================
+
+    See if a card is present, powered up, in IO mode, and already
+    bound to a (non-PCMCIA) Linux driver.
+
+    We make an exception for cards that look like serial devices.
+    
+======================================================================*/
+
+static int __init is_active(int s)
+{
+    u_short scf1, ioctl, base, num;
+    u_char pwr, sstat;
+    u_int addr;
+    
+    tcic_setl(TCIC_ADDR, (s << TCIC_ADDR_SS_SHFT)
+	      | TCIC_ADDR_INDREG | TCIC_SCF1(s));
+    scf1 = tcic_getw(TCIC_DATA);
+    pwr = tcic_getb(TCIC_PWR);
+    sstat = tcic_getb(TCIC_SSTAT);
+    addr = TCIC_IWIN(s, 0);
+    tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
+    base = tcic_getw(TCIC_DATA);
+    tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
+    ioctl = tcic_getw(TCIC_DATA);
+
+    if (ioctl & TCIC_ICTL_TINY)
+	num = 1;
+    else {
+	num = (base ^ (base-1));
+	base = base & (base-1);
+    }
+
+    if ((sstat & TCIC_SSTAT_CD) && (pwr & TCIC_PWR_VCC(s)) &&
+	(scf1 & TCIC_SCF1_IOSTS) && (ioctl & TCIC_ICTL_ENA) &&
+	((base & 0xfeef) != 0x02e8)) {
+	struct resource *res = request_region(base, num, "tcic-2");
+	if (!res) /* region is busy */
+	    return 1;
+	release_region(base, num);
+    }
+
+    return 0;
+}
+
+/*======================================================================
+
+    This returns the revision code for the specified socket.
+    
+======================================================================*/
+
+static int __init get_tcic_id(void)
+{
+    u_short id;
+    
+    tcic_aux_setw(TCIC_AUX_TEST, TCIC_TEST_DIAG);
+    id = tcic_aux_getw(TCIC_AUX_ILOCK);
+    id = (id & TCIC_ILOCKTEST_ID_MASK) >> TCIC_ILOCKTEST_ID_SH;
+    tcic_aux_setw(TCIC_AUX_TEST, 0);
+    return id;
+}
+
+/*====================================================================*/
+
+static int tcic_drv_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	int ret = 0;
+	if (level == SUSPEND_SAVE_STATE)
+		ret = pcmcia_socket_dev_suspend(dev, state);
+	return ret;
+}
+
+static int tcic_drv_resume(struct device *dev, u32 level)
+{
+	int ret = 0;
+	if (level == RESUME_RESTORE_STATE)
+		ret = pcmcia_socket_dev_resume(dev);
+	return ret;
+}
+
+static struct device_driver tcic_driver = {
+	.name = "tcic-pcmcia",
+	.bus = &platform_bus_type,
+	.suspend = tcic_drv_suspend,
+	.resume = tcic_drv_resume,
+};
+
+static struct platform_device tcic_device = {
+	.name = "tcic-pcmcia",
+	.id = 0,
+};
+
+
+static int __init init_tcic(void)
+{
+    int i, sock, ret = 0;
+    u_int mask, scan;
+
+    if (driver_register(&tcic_driver))
+	return -1;
+    
+    printk(KERN_INFO "Databook TCIC-2 PCMCIA probe: ");
+    sock = 0;
+
+    if (!request_region(tcic_base, 16, "tcic-2")) {
+	printk("could not allocate ports,\n ");
+	driver_unregister(&tcic_driver);
+	return -ENODEV;
+    }
+    else {
+	tcic_setw(TCIC_ADDR, 0);
+	if (tcic_getw(TCIC_ADDR) == 0) {
+	    tcic_setw(TCIC_ADDR, 0xc3a5);
+	    if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
+	}
+	if (sock == 0) {
+	    /* See if resetting the controller does any good */
+	    tcic_setb(TCIC_SCTRL, TCIC_SCTRL_RESET);
+	    tcic_setb(TCIC_SCTRL, 0);
+	    tcic_setw(TCIC_ADDR, 0);
+	    if (tcic_getw(TCIC_ADDR) == 0) {
+		tcic_setw(TCIC_ADDR, 0xc3a5);
+		if (tcic_getw(TCIC_ADDR) == 0xc3a5) sock = 2;
+	    }
+	}
+    }
+    if (sock == 0) {
+	printk("not found.\n");
+	release_region(tcic_base, 16);
+	driver_unregister(&tcic_driver);
+	return -ENODEV;
+    }
+
+    sockets = 0;
+    for (i = 0; i < sock; i++) {
+	if ((i == ignore) || is_active(i)) continue;
+	socket_table[sockets].psock = i;
+	socket_table[sockets].id = get_tcic_id();
+
+	socket_table[sockets].socket.owner = THIS_MODULE;
+	/* only 16-bit cards, memory windows must be size-aligned */
+	/* No PCI or CardBus support */
+	socket_table[sockets].socket.features = SS_CAP_PCCARD | SS_CAP_MEM_ALIGN;
+	/* irq 14, 11, 10, 7, 6, 5, 4, 3 */
+	socket_table[sockets].socket.irq_mask = 0x4cf8;
+	/* 4K minimum window size */
+	socket_table[sockets].socket.map_size = 0x1000;		
+	sockets++;
+    }
+
+    switch (socket_table[0].id) {
+    case TCIC_ID_DB86082:
+	printk("DB86082"); break;
+    case TCIC_ID_DB86082A:
+	printk("DB86082A"); break;
+    case TCIC_ID_DB86084:
+	printk("DB86084"); break;
+    case TCIC_ID_DB86084A:
+	printk("DB86084A"); break;
+    case TCIC_ID_DB86072:
+	printk("DB86072"); break;
+    case TCIC_ID_DB86184:
+	printk("DB86184"); break;
+    case TCIC_ID_DB86082B:
+	printk("DB86082B"); break;
+    default:
+	printk("Unknown ID 0x%02x", socket_table[0].id);
+    }
+    
+    /* Set up polling */
+    poll_timer.function = &tcic_timer;
+    poll_timer.data = 0;
+    init_timer(&poll_timer);
+
+    /* Build interrupt mask */
+    printk(", %d sockets\n" KERN_INFO "  irq list (", sockets);
+    if (irq_list_count == 0)
+	mask = irq_mask;
+    else
+	for (i = mask = 0; i < irq_list_count; i++)
+	    mask |= (1<<irq_list[i]);
+
+    /* irq 14, 11, 10, 7, 6, 5, 4, 3 */
+    mask &= 0x4cf8;
+    /* Scan interrupts */
+    mask = irq_scan(mask);
+    for (i=0;i<sockets;i++)
+	    socket_table[i].socket.irq_mask = mask;
+    
+    /* Check for only two interrupts available */
+    scan = (mask & (mask-1));
+    if (((scan & (scan-1)) == 0) && (poll_interval == 0))
+	poll_interval = HZ;
+    
+    if (poll_interval == 0) {
+	/* Avoid irq 12 unless it is explicitly requested */
+	u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12));
+	for (i = 15; i > 0; i--)
+	    if ((cs_mask & (1 << i)) &&
+		(request_irq(i, tcic_interrupt, 0, "tcic",
+			     tcic_interrupt) == 0))
+		break;
+	cs_irq = i;
+	if (cs_irq == 0) poll_interval = HZ;
+    }
+    
+    if (socket_table[0].socket.irq_mask & (1 << 11))
+	printk("sktirq is irq 11, ");
+    if (cs_irq != 0)
+	printk("status change on irq %d\n", cs_irq);
+    else
+	printk("polled status, interval = %d ms\n",
+	       poll_interval * 1000 / HZ);
+    
+    for (i = 0; i < sockets; i++) {
+	tcic_setw(TCIC_ADDR+2, socket_table[i].psock << TCIC_SS_SHFT);
+	socket_table[i].last_sstat = tcic_getb(TCIC_SSTAT);
+    }
+    
+    /* jump start interrupt handler, if needed */
+    tcic_interrupt(0, NULL, NULL);
+
+    platform_device_register(&tcic_device);
+
+    for (i = 0; i < sockets; i++) {
+	    socket_table[i].socket.ops = &tcic_operations;
+	    socket_table[i].socket.resource_ops = &pccard_nonstatic_ops;
+	    socket_table[i].socket.dev.dev = &tcic_device.dev;
+	    ret = pcmcia_register_socket(&socket_table[i].socket);
+	    if (ret && i)
+		    pcmcia_unregister_socket(&socket_table[0].socket);
+    }
+    
+    return ret;
+
+    return 0;
+    
+} /* init_tcic */
+
+/*====================================================================*/
+
+static void __exit exit_tcic(void)
+{
+    int i;
+
+    del_timer_sync(&poll_timer);
+    if (cs_irq != 0) {
+	tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00);
+	free_irq(cs_irq, tcic_interrupt);
+    }
+    release_region(tcic_base, 16);
+
+    for (i = 0; i < sockets; i++) {
+	    pcmcia_unregister_socket(&socket_table[i].socket);	    
+    }
+
+    platform_device_unregister(&tcic_device);
+    driver_unregister(&tcic_driver);
+} /* exit_tcic */
+
+/*====================================================================*/
+
+static irqreturn_t tcic_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+    int i, quick = 0;
+    u_char latch, sstat;
+    u_short psock;
+    u_int events;
+    static volatile int active = 0;
+
+    if (active) {
+	printk(KERN_NOTICE "tcic: reentered interrupt handler!\n");
+	return IRQ_NONE;
+    } else
+	active = 1;
+
+    debug(2, "tcic_interrupt()\n");
+    
+    for (i = 0; i < sockets; i++) {
+	psock = socket_table[i].psock;
+	tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+		  | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+	sstat = tcic_getb(TCIC_SSTAT);
+	latch = sstat ^ socket_table[psock].last_sstat;
+	socket_table[i].last_sstat = sstat;
+	if (tcic_getb(TCIC_ICSR) & TCIC_ICSR_CDCHG) {
+	    tcic_setb(TCIC_ICSR, TCIC_ICSR_CLEAR);
+	    quick = 1;
+	}
+	if (latch == 0)
+	    continue;
+	events = (latch & TCIC_SSTAT_CD) ? SS_DETECT : 0;
+	events |= (latch & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
+	if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
+	    events |= (latch & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
+	} else {
+	    events |= (latch & TCIC_SSTAT_RDY) ? SS_READY : 0;
+	    events |= (latch & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
+	    events |= (latch & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
+	}
+	if (events) {
+		pcmcia_parse_events(&socket_table[i].socket, events);
+	}
+    }
+
+    /* Schedule next poll, if needed */
+    if (((cs_irq == 0) || quick) && (!tcic_timer_pending)) {
+	poll_timer.expires = jiffies + (quick ? poll_quick : poll_interval);
+	add_timer(&poll_timer);
+	tcic_timer_pending = 1;
+    }
+    active = 0;
+    
+    debug(2, "interrupt done\n");
+    return IRQ_HANDLED;
+} /* tcic_interrupt */
+
+static void tcic_timer(u_long data)
+{
+    debug(2, "tcic_timer()\n");
+    tcic_timer_pending = 0;
+    tcic_interrupt(0, NULL, NULL);
+} /* tcic_timer */
+
+/*====================================================================*/
+
+static int tcic_get_status(struct pcmcia_socket *sock, u_int *value)
+{
+    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
+    u_char reg;
+
+    tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+	      | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+    reg = tcic_getb(TCIC_SSTAT);
+    *value  = (reg & TCIC_SSTAT_CD) ? SS_DETECT : 0;
+    *value |= (reg & TCIC_SSTAT_WP) ? SS_WRPROT : 0;
+    if (tcic_getw(TCIC_DATA) & TCIC_SCF1_IOSTS) {
+	*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_STSCHG : 0;
+    } else {
+	*value |= (reg & TCIC_SSTAT_RDY) ? SS_READY : 0;
+	*value |= (reg & TCIC_SSTAT_LBAT1) ? SS_BATDEAD : 0;
+	*value |= (reg & TCIC_SSTAT_LBAT2) ? SS_BATWARN : 0;
+    }
+    reg = tcic_getb(TCIC_PWR);
+    if (reg & (TCIC_PWR_VCC(psock)|TCIC_PWR_VPP(psock)))
+	*value |= SS_POWERON;
+    debug(1, "GetStatus(%d) = %#2.2x\n", psock, *value);
+    return 0;
+} /* tcic_get_status */
+  
+/*====================================================================*/
+
+static int tcic_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
+    u_char reg;
+    u_short scf1, scf2;
+    
+    tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT)
+	      | TCIC_ADDR_INDREG | TCIC_SCF1(psock));
+    scf1 = tcic_getw(TCIC_DATA);
+    state->flags = (scf1 & TCIC_SCF1_IOSTS) ? SS_IOCARD : 0;
+    state->flags |= (scf1 & TCIC_SCF1_DMA_MASK) ? SS_DMA_MODE : 0;
+    state->flags |= (scf1 & TCIC_SCF1_SPKR) ? SS_SPKR_ENA : 0;
+    if (tcic_getb(TCIC_SCTRL) & TCIC_SCTRL_ENA)
+	state->flags |= SS_OUTPUT_ENA;
+    state->io_irq = scf1 & TCIC_SCF1_IRQ_MASK;
+    if (state->io_irq == 1) state->io_irq = 11;
+
+    reg = tcic_getb(TCIC_PWR);
+    state->Vcc = state->Vpp = 0;
+    if (reg & TCIC_PWR_VCC(psock)) {
+	if (reg & TCIC_PWR_VPP(psock))
+	    state->Vcc = 50;
+	else
+	    state->Vcc = state->Vpp = 50;
+    } else {
+	if (reg & TCIC_PWR_VPP(psock)) {
+	    state->Vcc = 50;
+	    state->Vpp = 120;
+	}
+    }
+    reg = tcic_aux_getb(TCIC_AUX_ILOCK);
+    state->flags |= (reg & TCIC_ILOCK_CRESET) ? SS_RESET : 0;
+
+    /* Card status change interrupt mask */
+    tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
+    scf2 = tcic_getw(TCIC_DATA);
+    state->csc_mask = (scf2 & TCIC_SCF2_MCD) ? 0 : SS_DETECT;
+    if (state->flags & SS_IOCARD) {
+	state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_STSCHG;
+    } else {
+	state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT1) ? 0 : SS_BATDEAD;
+	state->csc_mask |= (scf2 & TCIC_SCF2_MLBAT2) ? 0 : SS_BATWARN;
+	state->csc_mask |= (scf2 & TCIC_SCF2_MRDY) ? 0 : SS_READY;
+    }
+
+    debug(1, "GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+	  "io_irq %d, csc_mask %#2.2x\n", psock, state->flags,
+	  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+    return 0;
+} /* tcic_get_socket */
+
+/*====================================================================*/
+
+static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
+    u_char reg;
+    u_short scf1, scf2;
+
+    debug(1, "SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+	  "io_irq %d, csc_mask %#2.2x)\n", psock, state->flags,
+	  state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+    tcic_setw(TCIC_ADDR+2, (psock << TCIC_SS_SHFT) | TCIC_ADR2_INDREG);
+
+    reg = tcic_getb(TCIC_PWR);
+    reg &= ~(TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock));
+
+    if (state->Vcc == 50) {
+	switch (state->Vpp) {
+	case 0:   reg |= TCIC_PWR_VCC(psock) | TCIC_PWR_VPP(psock); break;
+	case 50:  reg |= TCIC_PWR_VCC(psock); break;
+	case 120: reg |= TCIC_PWR_VPP(psock); break;
+	default:  return -EINVAL;
+	}
+    } else if (state->Vcc != 0)
+	return -EINVAL;
+
+    if (reg != tcic_getb(TCIC_PWR))
+	tcic_setb(TCIC_PWR, reg);
+
+    reg = TCIC_ILOCK_HOLD_CCLK | TCIC_ILOCK_CWAIT;
+    if (state->flags & SS_OUTPUT_ENA) {
+	tcic_setb(TCIC_SCTRL, TCIC_SCTRL_ENA);
+	reg |= TCIC_ILOCK_CRESENA;
+    } else
+	tcic_setb(TCIC_SCTRL, 0);
+    if (state->flags & SS_RESET)
+	reg |= TCIC_ILOCK_CRESET;
+    tcic_aux_setb(TCIC_AUX_ILOCK, reg);
+    
+    tcic_setw(TCIC_ADDR, TCIC_SCF1(psock));
+    scf1 = TCIC_SCF1_FINPACK;
+    scf1 |= TCIC_IRQ(state->io_irq);
+    if (state->flags & SS_IOCARD) {
+	scf1 |= TCIC_SCF1_IOSTS;
+	if (state->flags & SS_SPKR_ENA)
+	    scf1 |= TCIC_SCF1_SPKR;
+	if (state->flags & SS_DMA_MODE)
+	    scf1 |= TCIC_SCF1_DREQ2 << TCIC_SCF1_DMA_SHIFT;
+    }
+    tcic_setw(TCIC_DATA, scf1);
+
+    /* Some general setup stuff, and configure status interrupt */
+    reg = TCIC_WAIT_ASYNC | TCIC_WAIT_SENSE | to_cycles(250);
+    tcic_aux_setb(TCIC_AUX_WCTL, reg);
+    tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00|
+		  TCIC_IRQ(cs_irq));
+    
+    /* Card status change interrupt mask */
+    tcic_setw(TCIC_ADDR, TCIC_SCF2(psock));
+    scf2 = TCIC_SCF2_MALL;
+    if (state->csc_mask & SS_DETECT) scf2 &= ~TCIC_SCF2_MCD;
+    if (state->flags & SS_IOCARD) {
+	if (state->csc_mask & SS_STSCHG) reg &= ~TCIC_SCF2_MLBAT1;
+    } else {
+	if (state->csc_mask & SS_BATDEAD) reg &= ~TCIC_SCF2_MLBAT1;
+	if (state->csc_mask & SS_BATWARN) reg &= ~TCIC_SCF2_MLBAT2;
+	if (state->csc_mask & SS_READY) reg &= ~TCIC_SCF2_MRDY;
+    }
+    tcic_setw(TCIC_DATA, scf2);
+    /* For the ISA bus, the irq should be active-high totem-pole */
+    tcic_setb(TCIC_IENA, TCIC_IENA_CDCHG | TCIC_IENA_CFG_HIGH);
+
+    return 0;
+} /* tcic_set_socket */
+  
+/*====================================================================*/
+
+static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
+{
+    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
+    u_int addr;
+    u_short base, len, ioctl;
+    
+    debug(1, "SetIOMap(%d, %d, %#2.2x, %d ns, "
+	  "%#lx-%#lx)\n", psock, io->map, io->flags,
+	  io->speed, io->start, io->stop);
+    if ((io->map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
+	(io->stop < io->start)) return -EINVAL;
+    tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+    addr = TCIC_IWIN(psock, io->map);
+
+    base = io->start; len = io->stop - io->start;
+    /* Check to see that len+1 is power of two, etc */
+    if ((len & (len+1)) || (base & len)) return -EINVAL;
+    base |= (len+1)>>1;
+    tcic_setw(TCIC_ADDR, addr + TCIC_IBASE_X);
+    tcic_setw(TCIC_DATA, base);
+    
+    ioctl  = (psock << TCIC_ICTL_SS_SHFT);
+    ioctl |= (len == 0) ? TCIC_ICTL_TINY : 0;
+    ioctl |= (io->flags & MAP_ACTIVE) ? TCIC_ICTL_ENA : 0;
+    ioctl |= to_cycles(io->speed) & TCIC_ICTL_WSCNT_MASK;
+    if (!(io->flags & MAP_AUTOSZ)) {
+	ioctl |= TCIC_ICTL_QUIET;
+	ioctl |= (io->flags & MAP_16BIT) ? TCIC_ICTL_BW_16 : TCIC_ICTL_BW_8;
+    }
+    tcic_setw(TCIC_ADDR, addr + TCIC_ICTL_X);
+    tcic_setw(TCIC_DATA, ioctl);
+    
+    return 0;
+} /* tcic_set_io_map */
+
+/*====================================================================*/
+
+static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
+{
+    u_short psock = container_of(sock, struct tcic_socket, socket)->psock;
+    u_short addr, ctl;
+    u_long base, len, mmap;
+
+    debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, "
+	  "%#lx-%#lx, %#x)\n", psock, mem->map, mem->flags,
+	  mem->speed, mem->res->start, mem->res->end, mem->card_start);
+    if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
+	(mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
+	(mem->res->start > mem->res->end) || (mem->speed > 1000))
+	return -EINVAL;
+    tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT));
+    addr = TCIC_MWIN(psock, mem->map);
+
+    base = mem->res->start; len = mem->res->end - mem->res->start;
+    if ((len & (len+1)) || (base & len)) return -EINVAL;
+    if (len == 0x0fff)
+	base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT;
+    else
+	base = (base | (len+1)>>1) >> TCIC_MBASE_HA_SHFT;
+    tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X);
+    tcic_setw(TCIC_DATA, base);
+    
+    mmap = mem->card_start - mem->res->start;
+    mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK;
+    if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG;
+    tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X);
+    tcic_setw(TCIC_DATA, mmap);
+
+    ctl  = TCIC_MCTL_QUIET | (psock << TCIC_MCTL_SS_SHFT);
+    ctl |= to_cycles(mem->speed) & TCIC_MCTL_WSCNT_MASK;
+    ctl |= (mem->flags & MAP_16BIT) ? 0 : TCIC_MCTL_B8;
+    ctl |= (mem->flags & MAP_WRPROT) ? TCIC_MCTL_WP : 0;
+    ctl |= (mem->flags & MAP_ACTIVE) ? TCIC_MCTL_ENA : 0;
+    tcic_setw(TCIC_ADDR, addr + TCIC_MCTL_X);
+    tcic_setw(TCIC_DATA, ctl);
+    
+    return 0;
+} /* tcic_set_mem_map */
+
+/*====================================================================*/
+
+static int tcic_init(struct pcmcia_socket *s)
+{
+	int i;
+	struct resource res = { .start = 0, .end = 0x1000 };
+	pccard_io_map io = { 0, 0, 0, 0, 1 };
+	pccard_mem_map mem = { .res = &res, };
+
+	for (i = 0; i < 2; i++) {
+		io.map = i;
+		tcic_set_io_map(s, &io);
+	}
+	for (i = 0; i < 5; i++) {
+		mem.map = i;
+		tcic_set_mem_map(s, &mem);
+	}
+	return 0;
+}
+
+static struct pccard_operations tcic_operations = {
+	.init		   = tcic_init,
+	.get_status	   = tcic_get_status,
+	.get_socket	   = tcic_get_socket,
+	.set_socket	   = tcic_set_socket,
+	.set_io_map	   = tcic_set_io_map,
+	.set_mem_map	   = tcic_set_mem_map,
+};
+
+/*====================================================================*/
+
+module_init(init_tcic);
+module_exit(exit_tcic);
diff --git a/drivers/pcmcia/tcic.h b/drivers/pcmcia/tcic.h
new file mode 100644
index 0000000..2c0b8f6
--- /dev/null
+++ b/drivers/pcmcia/tcic.h
@@ -0,0 +1,266 @@
+/*
+ * tcic.h 1.13 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_TCIC_H
+#define _LINUX_TCIC_H
+
+#define TCIC_BASE		0x240
+
+/* offsets of registers from TCIC_BASE */
+#define TCIC_DATA		0x00
+#define TCIC_ADDR		0x02
+#define TCIC_SCTRL		0x06
+#define TCIC_SSTAT		0x07
+#define TCIC_MODE		0x08
+#define TCIC_PWR		0x09
+#define TCIC_EDC		0x0A
+#define TCIC_ICSR		0x0C
+#define TCIC_IENA		0x0D
+#define TCIC_AUX		0x0E
+
+#define TCIC_SS_SHFT		12
+#define TCIC_SS_MASK		0x7000
+
+/* Flags for TCIC_ADDR */
+#define TCIC_ADR2_REG		0x8000
+#define TCIC_ADR2_INDREG	0x0800
+
+#define TCIC_ADDR_REG		0x80000000
+#define TCIC_ADDR_SS_SHFT	(TCIC_SS_SHFT+16)
+#define TCIC_ADDR_SS_MASK	(TCIC_SS_MASK<<16)
+#define TCIC_ADDR_INDREG	0x08000000
+#define TCIC_ADDR_IO		0x04000000
+#define TCIC_ADDR_MASK		0x03ffffff
+
+/* Flags for TCIC_SCTRL */
+#define TCIC_SCTRL_ENA		0x01
+#define TCIC_SCTRL_INCMODE	0x18
+#define TCIC_SCTRL_INCMODE_HOLD	0x00
+#define TCIC_SCTRL_INCMODE_WORD	0x08
+#define TCIC_SCTRL_INCMODE_REG	0x10
+#define TCIC_SCTRL_INCMODE_AUTO	0x18
+#define TCIC_SCTRL_EDCSUM	0x20
+#define TCIC_SCTRL_RESET	0x80
+
+/* Flags for TCIC_SSTAT */
+#define TCIC_SSTAT_6US		0x01
+#define TCIC_SSTAT_10US		0x02
+#define TCIC_SSTAT_PROGTIME	0x04
+#define TCIC_SSTAT_LBAT1	0x08
+#define TCIC_SSTAT_LBAT2	0x10
+#define TCIC_SSTAT_RDY		0x20	/* Inverted */
+#define TCIC_SSTAT_WP		0x40
+#define TCIC_SSTAT_CD		0x80	/* Card detect */
+
+/* Flags for TCIC_MODE */
+#define TCIC_MODE_PGMMASK	0x1f
+#define TCIC_MODE_NORMAL	0x00
+#define TCIC_MODE_PGMWR		0x01
+#define TCIC_MODE_PGMRD		0x02
+#define TCIC_MODE_PGMCE		0x04
+#define TCIC_MODE_PGMDBW	0x08
+#define TCIC_MODE_PGMWORD	0x10
+#define TCIC_MODE_AUXSEL_MASK	0xe0
+
+/* Registers accessed through TCIC_AUX, by setting TCIC_MODE */
+#define TCIC_AUX_TCTL		(0<<5)
+#define TCIC_AUX_PCTL		(1<<5)
+#define TCIC_AUX_WCTL		(2<<5)
+#define TCIC_AUX_EXTERN		(3<<5)
+#define TCIC_AUX_PDATA		(4<<5)
+#define TCIC_AUX_SYSCFG		(5<<5)
+#define TCIC_AUX_ILOCK		(6<<5)
+#define TCIC_AUX_TEST		(7<<5)
+
+/* Flags for TCIC_PWR */
+#define TCIC_PWR_VCC(sock)	(0x01<<(sock))
+#define TCIC_PWR_VCC_MASK	0x03
+#define TCIC_PWR_VPP(sock)	(0x08<<(sock))
+#define TCIC_PWR_VPP_MASK	0x18
+#define TCIC_PWR_CLIMENA	0x40
+#define TCIC_PWR_CLIMSTAT	0x80
+
+/* Flags for TCIC_ICSR */
+#define TCIC_ICSR_CLEAR		0x01
+#define TCIC_ICSR_SET		0x02
+#define TCIC_ICSR_JAM		(TCIC_ICSR_CLEAR|TCIC_ICSR_SET)
+#define TCIC_ICSR_STOPCPU	0x04
+#define TCIC_ICSR_ILOCK		0x08
+#define TCIC_ICSR_PROGTIME	0x10
+#define TCIC_ICSR_ERR		0x20
+#define TCIC_ICSR_CDCHG		0x40
+#define TCIC_ICSR_IOCHK		0x80
+
+/* Flags for TCIC_IENA */
+#define TCIC_IENA_CFG_MASK	0x03
+#define TCIC_IENA_CFG_OFF	0x00	/* disabled */
+#define TCIC_IENA_CFG_OD	0x01	/* active low, open drain */
+#define TCIC_IENA_CFG_LOW	0x02	/* active low, totem pole */
+#define TCIC_IENA_CFG_HIGH	0x03	/* active high, totem pole */
+#define TCIC_IENA_ILOCK		0x08
+#define TCIC_IENA_PROGTIME	0x10
+#define TCIC_IENA_ERR		0x20	/* overcurrent or iochk */
+#define TCIC_IENA_CDCHG		0x40
+
+/* Flags for TCIC_AUX_WCTL */
+#define TCIC_WAIT_COUNT_MASK	0x001f
+#define TCIC_WAIT_ASYNC		0x0020
+#define TCIC_WAIT_SENSE		0x0040
+#define TCIC_WAIT_SRC		0x0080
+#define TCIC_WCTL_WR		0x0100
+#define TCIC_WCTL_RD		0x0200
+#define TCIC_WCTL_CE		0x0400
+#define TCIC_WCTL_LLBAT1	0x0800
+#define TCIC_WCTL_LLBAT2	0x1000
+#define TCIC_WCTL_LRDY		0x2000
+#define TCIC_WCTL_LWP		0x4000
+#define TCIC_WCTL_LCD		0x8000
+
+/* Flags for TCIC_AUX_SYSCFG */
+#define TCIC_SYSCFG_IRQ_MASK	0x000f
+#define TCIC_SYSCFG_MCSFULL	0x0010
+#define TCIC_SYSCFG_IO1723	0x0020
+#define TCIC_SYSCFG_MCSXB	0x0040
+#define TCIC_SYSCFG_ICSXB	0x0080
+#define TCIC_SYSCFG_NOPDN	0x0100
+#define TCIC_SYSCFG_MPSEL_SHFT	9
+#define TCIC_SYSCFG_MPSEL_MASK	0x0e00
+#define TCIC_SYSCFG_MPSENSE	0x2000
+#define TCIC_SYSCFG_AUTOBUSY	0x4000
+#define TCIC_SYSCFG_ACC		0x8000
+
+#define TCIC_ILOCK_OUT		0x01
+#define TCIC_ILOCK_SENSE	0x02
+#define TCIC_ILOCK_CRESET	0x04
+#define TCIC_ILOCK_CRESENA	0x08
+#define TCIC_ILOCK_CWAIT	0x10
+#define TCIC_ILOCK_CWAITSNS	0x20
+#define TCIC_ILOCK_HOLD_MASK	0xc0
+#define TCIC_ILOCK_HOLD_CCLK	0xc0
+
+#define TCIC_ILOCKTEST_ID_SH	8
+#define TCIC_ILOCKTEST_ID_MASK	0x7f00
+#define TCIC_ILOCKTEST_MCIC_1	0x8000
+
+#define TCIC_ID_DB86082		0x02
+#define TCIC_ID_DB86082A	0x03
+#define TCIC_ID_DB86084		0x04
+#define TCIC_ID_DB86084A	0x08
+#define TCIC_ID_DB86072		0x15
+#define TCIC_ID_DB86184		0x14
+#define TCIC_ID_DB86082B	0x17
+
+#define TCIC_TEST_DIAG		0x8000
+
+/*
+ * Indirectly addressed registers
+ */
+
+#define TCIC_SCF1(sock)	((sock)<<3)
+#define TCIC_SCF2(sock) (((sock)<<3)+2)
+
+/* Flags for SCF1 */
+#define TCIC_SCF1_IRQ_MASK	0x000f
+#define TCIC_SCF1_IRQ_OFF	0x0000
+#define TCIC_SCF1_IRQOC		0x0010
+#define TCIC_SCF1_PCVT		0x0020
+#define TCIC_SCF1_IRDY		0x0040
+#define TCIC_SCF1_ATA		0x0080
+#define TCIC_SCF1_DMA_SHIFT	8
+#define TCIC_SCF1_DMA_MASK	0x0700
+#define TCIC_SCF1_DMA_OFF	0
+#define TCIC_SCF1_DREQ2		2
+#define TCIC_SCF1_IOSTS		0x0800
+#define TCIC_SCF1_SPKR		0x1000
+#define TCIC_SCF1_FINPACK	0x2000
+#define TCIC_SCF1_DELWR		0x4000
+#define TCIC_SCF1_HD7IDE	0x8000
+
+/* Flags for SCF2 */
+#define TCIC_SCF2_RI		0x0001
+#define TCIC_SCF2_IDBR		0x0002
+#define TCIC_SCF2_MDBR		0x0004
+#define TCIC_SCF2_MLBAT1	0x0008
+#define TCIC_SCF2_MLBAT2	0x0010
+#define TCIC_SCF2_MRDY		0x0020
+#define TCIC_SCF2_MWP		0x0040
+#define TCIC_SCF2_MCD		0x0080
+#define TCIC_SCF2_MALL		0x00f8
+
+/* Indirect addresses for memory window registers */
+#define TCIC_MWIN(sock,map)	(0x100+(((map)+((sock)<<2))<<3))
+#define TCIC_MBASE_X		2
+#define TCIC_MMAP_X		4
+#define TCIC_MCTL_X		6
+
+#define TCIC_MBASE_4K_BIT	0x4000
+#define TCIC_MBASE_HA_SHFT	12
+#define TCIC_MBASE_HA_MASK	0x0fff
+
+#define TCIC_MMAP_REG		0x8000
+#define TCIC_MMAP_CA_SHFT	12
+#define TCIC_MMAP_CA_MASK	0x3fff
+
+#define TCIC_MCTL_WSCNT_MASK	0x001f
+#define TCIC_MCTL_WCLK		0x0020
+#define TCIC_MCTL_WCLK_CCLK	0x0000
+#define TCIC_MCTL_WCLK_BCLK	0x0020
+#define TCIC_MCTL_QUIET		0x0040
+#define TCIC_MCTL_WP		0x0080
+#define TCIC_MCTL_ACC		0x0100
+#define TCIC_MCTL_KE		0x0200
+#define TCIC_MCTL_EDC		0x0400
+#define TCIC_MCTL_B8		0x0800
+#define TCIC_MCTL_SS_SHFT	TCIC_SS_SHFT
+#define TCIC_MCTL_SS_MASK	TCIC_SS_MASK
+#define TCIC_MCTL_ENA		0x8000
+
+/* Indirect addresses for I/O window registers */
+#define TCIC_IWIN(sock,map)	(0x200+(((map)+((sock)<<1))<<2))
+#define TCIC_IBASE_X		0
+#define TCIC_ICTL_X		2
+
+#define TCIC_ICTL_WSCNT_MASK	TCIC_MCTL_WSCNT_MASK
+#define TCIC_ICTL_QUIET		TCIC_MCTL_QUIET
+#define TCIC_ICTL_1K		0x0080
+#define TCIC_ICTL_PASS16	0x0100
+#define TCIC_ICTL_ACC		TCIC_MCTL_ACC
+#define TCIC_ICTL_TINY		0x0200
+#define TCIC_ICTL_B16		0x0400
+#define TCIC_ICTL_B8		TCIC_MCTL_B8
+#define TCIC_ICTL_BW_MASK	(TCIC_ICTL_B16|TCIC_ICTL_B8)
+#define TCIC_ICTL_BW_DYN	0
+#define TCIC_ICTL_BW_8		TCIC_ICTL_B8
+#define TCIC_ICTL_BW_16		TCIC_ICTL_B16
+#define TCIC_ICTL_BW_ATA	(TCIC_ICTL_B16|TCIC_ICTL_B8)
+#define TCIC_ICTL_SS_SHFT	TCIC_SS_SHFT
+#define TCIC_ICTL_SS_MASK	TCIC_SS_MASK
+#define TCIC_ICTL_ENA		TCIC_MCTL_ENA
+
+#endif /* _LINUX_TCIC_H */
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
new file mode 100644
index 0000000..52c073a
--- /dev/null
+++ b/drivers/pcmcia/ti113x.h
@@ -0,0 +1,662 @@
+/*
+ * ti113x.h 1.16 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_TI113X_H
+#define _LINUX_TI113X_H
+
+#include <linux/config.h>
+
+/* Register definitions for TI 113X PCI-to-CardBus bridges */
+
+/* System Control Register */
+#define TI113X_SYSTEM_CONTROL		0x0080	/* 32 bit */
+#define  TI113X_SCR_SMIROUTE		0x04000000
+#define  TI113X_SCR_SMISTATUS		0x02000000
+#define  TI113X_SCR_SMIENB		0x01000000
+#define  TI113X_SCR_VCCPROT		0x00200000
+#define  TI113X_SCR_REDUCEZV		0x00100000
+#define  TI113X_SCR_CDREQEN		0x00080000
+#define  TI113X_SCR_CDMACHAN		0x00070000
+#define  TI113X_SCR_SOCACTIVE		0x00002000
+#define  TI113X_SCR_PWRSTREAM		0x00000800
+#define  TI113X_SCR_DELAYUP		0x00000400
+#define  TI113X_SCR_DELAYDOWN		0x00000200
+#define  TI113X_SCR_INTERROGATE		0x00000100
+#define  TI113X_SCR_CLKRUN_SEL		0x00000080
+#define  TI113X_SCR_PWRSAVINGS		0x00000040
+#define  TI113X_SCR_SUBSYSRW		0x00000020
+#define  TI113X_SCR_CB_DPAR		0x00000010
+#define  TI113X_SCR_CDMA_EN		0x00000008
+#define  TI113X_SCR_ASYNC_IRQ		0x00000004
+#define  TI113X_SCR_KEEPCLK		0x00000002
+#define  TI113X_SCR_CLKRUN_ENA		0x00000001  
+
+#define  TI122X_SCR_SER_STEP		0xc0000000
+#define  TI122X_SCR_INTRTIE		0x20000000
+#define  TI122X_SCR_CBRSVD		0x00400000
+#define  TI122X_SCR_MRBURSTDN		0x00008000
+#define  TI122X_SCR_MRBURSTUP		0x00004000
+#define  TI122X_SCR_RIMUX		0x00000001
+
+/* Multimedia Control Register */
+#define TI1250_MULTIMEDIA_CTL		0x0084	/* 8 bit */
+#define  TI1250_MMC_ZVOUTEN		0x80
+#define  TI1250_MMC_PORTSEL		0x40
+#define  TI1250_MMC_ZVEN1		0x02
+#define  TI1250_MMC_ZVEN0		0x01
+
+#define TI1250_GENERAL_STATUS		0x0085	/* 8 bit */
+#define TI1250_GPIO0_CONTROL		0x0088	/* 8 bit */
+#define TI1250_GPIO1_CONTROL		0x0089	/* 8 bit */
+#define TI1250_GPIO2_CONTROL		0x008a	/* 8 bit */
+#define TI1250_GPIO3_CONTROL		0x008b	/* 8 bit */
+#define TI1250_GPIO_MODE_MASK		0xc0
+
+/* IRQMUX/MFUNC Register */
+#define TI122X_MFUNC			0x008c	/* 32 bit */
+#define TI122X_MFUNC0_MASK		0x0000000f
+#define TI122X_MFUNC1_MASK		0x000000f0
+#define TI122X_MFUNC2_MASK		0x00000f00
+#define TI122X_MFUNC3_MASK		0x0000f000
+#define TI122X_MFUNC4_MASK		0x000f0000
+#define TI122X_MFUNC5_MASK		0x00f00000
+#define TI122X_MFUNC6_MASK		0x0f000000
+
+#define TI122X_MFUNC0_INTA		0x00000002
+#define TI125X_MFUNC0_INTB		0x00000001
+#define TI122X_MFUNC1_INTB		0x00000020
+#define TI122X_MFUNC3_IRQSER		0x00001000
+
+
+/* Retry Status Register */
+#define TI113X_RETRY_STATUS		0x0090	/* 8 bit */
+#define  TI113X_RSR_PCIRETRY		0x80
+#define  TI113X_RSR_CBRETRY		0x40
+#define  TI113X_RSR_TEXP_CBB		0x20
+#define  TI113X_RSR_MEXP_CBB		0x10
+#define  TI113X_RSR_TEXP_CBA		0x08
+#define  TI113X_RSR_MEXP_CBA		0x04
+#define  TI113X_RSR_TEXP_PCI		0x02
+#define  TI113X_RSR_MEXP_PCI		0x01
+
+/* Card Control Register */
+#define TI113X_CARD_CONTROL		0x0091	/* 8 bit */
+#define  TI113X_CCR_RIENB		0x80
+#define  TI113X_CCR_ZVENABLE		0x40
+#define  TI113X_CCR_PCI_IRQ_ENA		0x20
+#define  TI113X_CCR_PCI_IREQ		0x10
+#define  TI113X_CCR_PCI_CSC		0x08
+#define  TI113X_CCR_SPKROUTEN		0x02
+#define  TI113X_CCR_IFG			0x01
+
+#define  TI1220_CCR_PORT_SEL		0x20
+#define  TI122X_CCR_AUD2MUX		0x04
+
+/* Device Control Register */
+#define TI113X_DEVICE_CONTROL		0x0092	/* 8 bit */
+#define  TI113X_DCR_5V_FORCE		0x40
+#define  TI113X_DCR_3V_FORCE		0x20
+#define  TI113X_DCR_IMODE_MASK		0x06
+#define  TI113X_DCR_IMODE_ISA		0x02
+#define  TI113X_DCR_IMODE_SERIAL	0x04
+
+#define  TI12XX_DCR_IMODE_PCI_ONLY	0x00
+#define  TI12XX_DCR_IMODE_ALL_SERIAL	0x06
+
+/* Buffer Control Register */
+#define TI113X_BUFFER_CONTROL		0x0093	/* 8 bit */
+#define  TI113X_BCR_CB_READ_DEPTH	0x08
+#define  TI113X_BCR_CB_WRITE_DEPTH	0x04
+#define  TI113X_BCR_PCI_READ_DEPTH	0x02
+#define  TI113X_BCR_PCI_WRITE_DEPTH	0x01
+
+/* Diagnostic Register */
+#define TI1250_DIAGNOSTIC		0x0093	/* 8 bit */
+#define  TI1250_DIAG_TRUE_VALUE		0x80
+#define  TI1250_DIAG_PCI_IREQ		0x40
+#define  TI1250_DIAG_PCI_CSC		0x20
+#define  TI1250_DIAG_ASYNC_CSC		0x01
+
+/* DMA Registers */
+#define TI113X_DMA_0			0x0094	/* 32 bit */
+#define TI113X_DMA_1			0x0098	/* 32 bit */
+
+/* ExCA IO offset registers */
+#define TI113X_IO_OFFSET(map)		(0x36+((map)<<1))
+
+/* EnE test register */
+#define ENE_TEST_C9			0xc9	/* 8bit */
+#define ENE_TEST_C9_TLTENABLE		0x02
+
+#ifdef CONFIG_CARDBUS
+
+/*
+ * Texas Instruments CardBus controller overrides.
+ */
+#define ti_sysctl(socket)	((socket)->private[0])
+#define ti_cardctl(socket)	((socket)->private[1])
+#define ti_devctl(socket)	((socket)->private[2])
+#define ti_diag(socket)		((socket)->private[3])
+#define ti_mfunc(socket)	((socket)->private[4])
+#define ene_test_c9(socket)	((socket)->private[5])
+
+/*
+ * These are the TI specific power management handlers.
+ */
+static void ti_save_state(struct yenta_socket *socket)
+{
+	ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL);
+	ti_mfunc(socket) = config_readl(socket, TI122X_MFUNC);
+	ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL);
+	ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL);
+	ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC);
+
+	if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
+		ene_test_c9(socket) = config_readb(socket, ENE_TEST_C9);
+}
+
+static void ti_restore_state(struct yenta_socket *socket)
+{
+	config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket));
+	config_writel(socket, TI122X_MFUNC, ti_mfunc(socket));
+	config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket));
+	config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket));
+	config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket));
+
+	if (socket->dev->vendor == PCI_VENDOR_ID_ENE)
+		config_writeb(socket, ENE_TEST_C9, ene_test_c9(socket));
+}
+
+/*
+ *	Zoom video control for TI122x/113x chips
+ */
+
+static void ti_zoom_video(struct pcmcia_socket *sock, int onoff)
+{
+	u8 reg;
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+
+	/* If we don't have a Zoom Video switch this is harmless,
+	   we just tristate the unused (ZV) lines */
+	reg = config_readb(socket, TI113X_CARD_CONTROL);
+	if (onoff)
+		/* Zoom zoom, we will all go together, zoom zoom, zoom zoom */
+		reg |= TI113X_CCR_ZVENABLE;
+	else
+		reg &= ~TI113X_CCR_ZVENABLE;
+	config_writeb(socket, TI113X_CARD_CONTROL, reg);
+}
+
+/*
+ *	The 145x series can also use this. They have an additional
+ *	ZV autodetect mode we don't use but don't actually need.
+ *	FIXME: manual says its in func0 and func1 but disagrees with
+ *	itself about this - do we need to force func0, if so we need
+ *	to know a lot more about socket pairings in pcmcia_socket than
+ *	we do now.. uggh.
+ */
+ 
+static void ti1250_zoom_video(struct pcmcia_socket *sock, int onoff)
+{	
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	int shift = 0;
+	u8 reg;
+
+	ti_zoom_video(sock, onoff);
+
+	reg = config_readb(socket, TI1250_MULTIMEDIA_CTL);
+	reg |= TI1250_MMC_ZVOUTEN;	/* ZV bus enable */
+
+	if(PCI_FUNC(socket->dev->devfn)==1)
+		shift = 1;
+	
+	if(onoff)
+	{
+		reg &= ~(1<<6); 	/* Clear select bit */
+		reg |= shift<<6;	/* Favour our socket */
+		reg |= 1<<shift;	/* Socket zoom video on */
+	}
+	else
+	{
+		reg &= ~(1<<6); 	/* Clear select bit */
+		reg |= (1^shift)<<6;	/* Favour other socket */
+		reg &= ~(1<<shift);	/* Socket zoon video off */
+	}
+
+	config_writeb(socket, TI1250_MULTIMEDIA_CTL, reg);
+}
+
+static void ti_set_zv(struct yenta_socket *socket)
+{
+	if(socket->dev->vendor == PCI_VENDOR_ID_TI)
+	{
+		switch(socket->dev->device)
+		{
+			/* There may be more .. */
+			case PCI_DEVICE_ID_TI_1220:
+			case PCI_DEVICE_ID_TI_1221:
+			case PCI_DEVICE_ID_TI_1225:
+			case PCI_DEVICE_ID_TI_4510:
+				socket->socket.zoom_video = ti_zoom_video;
+				break;	
+			case PCI_DEVICE_ID_TI_1250:
+			case PCI_DEVICE_ID_TI_1251A:
+			case PCI_DEVICE_ID_TI_1251B:
+			case PCI_DEVICE_ID_TI_1450:
+				socket->socket.zoom_video = ti1250_zoom_video;
+		}
+	}
+}
+
+
+/*
+ * Generic TI init - TI has an extension for the
+ * INTCTL register that sets the PCI CSC interrupt.
+ * Make sure we set it correctly at open and init
+ * time
+ * - override: disable the PCI CSC interrupt. This makes
+ *   it possible to use the CSC interrupt to probe the
+ *   ISA interrupts.
+ * - init: set the interrupt to match our PCI state.
+ *   This makes us correctly get PCI CSC interrupt
+ *   events.
+ */
+static int ti_init(struct yenta_socket *socket)
+{
+	u8 new, reg = exca_readb(socket, I365_INTCTL);
+
+	new = reg & ~I365_INTR_ENA;
+	if (socket->cb_irq)
+		new |= I365_INTR_ENA;
+	if (new != reg)
+		exca_writeb(socket, I365_INTCTL, new);
+	return 0;
+}
+
+static int ti_override(struct yenta_socket *socket)
+{
+	u8 new, reg = exca_readb(socket, I365_INTCTL);
+
+	new = reg & ~I365_INTR_ENA;
+	if (new != reg)
+		exca_writeb(socket, I365_INTCTL, new);
+
+	ti_set_zv(socket);
+
+	return 0;
+}
+
+static int ti113x_override(struct yenta_socket *socket)
+{
+	u8 cardctl;
+
+	cardctl = config_readb(socket, TI113X_CARD_CONTROL);
+	cardctl &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC);
+	if (socket->cb_irq)
+		cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ;
+	config_writeb(socket, TI113X_CARD_CONTROL, cardctl);
+
+	return ti_override(socket);
+}
+
+
+/* irqrouting for func0, probes PCI interrupt and ISA interrupts */
+static void ti12xx_irqroute_func0(struct yenta_socket *socket)
+{
+	u32 mfunc, mfunc_old, devctl;
+	u8 gpio3, gpio3_old;
+	int pci_irq_status;
+
+	mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
+	devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
+	printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n",
+	       pci_name(socket->dev), mfunc, devctl);
+
+	/* make sure PCI interrupts are enabled before probing */
+	ti_init(socket);
+
+	/* test PCI interrupts first. only try fixing if return value is 0! */
+	pci_irq_status = yenta_probe_cb_irq(socket);
+	if (pci_irq_status)
+		goto out;
+
+	/*
+	 * We're here which means PCI interrupts are _not_ delivered. try to
+	 * find the right setting (all serial or parallel)
+	 */
+	printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n",
+	       pci_name(socket->dev));
+
+	/* for serial PCI make sure MFUNC3 is set to IRQSER */
+	if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
+		switch (socket->dev->device) {
+		case PCI_DEVICE_ID_TI_1250:
+		case PCI_DEVICE_ID_TI_1251A:
+		case PCI_DEVICE_ID_TI_1251B:
+		case PCI_DEVICE_ID_TI_1450:
+		case PCI_DEVICE_ID_TI_1451A:
+		case PCI_DEVICE_ID_TI_4450:
+		case PCI_DEVICE_ID_TI_4451:
+			/* these chips have no IRQSER setting in MFUNC3  */
+			break;
+
+		default:
+			mfunc = (mfunc & ~TI122X_MFUNC3_MASK) | TI122X_MFUNC3_IRQSER;
+
+			/* write down if changed, probe */
+			if (mfunc != mfunc_old) {
+				config_writel(socket, TI122X_MFUNC, mfunc);
+
+				pci_irq_status = yenta_probe_cb_irq(socket);
+				if (pci_irq_status == 1) {
+					printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts ok\n",
+					       pci_name(socket->dev));
+					mfunc_old = mfunc;
+					goto out;
+				}
+
+				/* not working, back to old value */
+				mfunc = mfunc_old;
+				config_writel(socket, TI122X_MFUNC, mfunc);
+
+				if (pci_irq_status == -1)
+					goto out;
+			}
+		}
+
+		/* serial PCI interrupts not working fall back to parallel */
+		printk(KERN_INFO "Yenta TI: socket %s falling back to parallel PCI interrupts\n",
+		       pci_name(socket->dev));
+		devctl &= ~TI113X_DCR_IMODE_MASK;
+		devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
+		config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
+	}
+
+	/* parallel PCI interrupts: route INTA */
+	switch (socket->dev->device) {
+	case PCI_DEVICE_ID_TI_1250:
+	case PCI_DEVICE_ID_TI_1251A:
+	case PCI_DEVICE_ID_TI_1251B:
+	case PCI_DEVICE_ID_TI_1450:
+		/* make sure GPIO3 is set to INTA */
+		gpio3 = gpio3_old = config_readb(socket, TI1250_GPIO3_CONTROL);
+		gpio3 &= ~TI1250_GPIO_MODE_MASK;
+		if (gpio3 != gpio3_old)
+			config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3);
+		break;
+
+	default:
+		gpio3 = gpio3_old = 0;
+
+		mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI122X_MFUNC0_INTA;
+		if (mfunc != mfunc_old)
+			config_writel(socket, TI122X_MFUNC, mfunc);
+	}
+
+	/* time to probe again */
+	pci_irq_status = yenta_probe_cb_irq(socket);
+	if (pci_irq_status == 1) {
+		mfunc_old = mfunc;
+		printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n",
+		       pci_name(socket->dev));
+	} else {
+		/* not working, back to old value */
+		mfunc = mfunc_old;
+		config_writel(socket, TI122X_MFUNC, mfunc);
+		if (gpio3 != gpio3_old)
+			config_writeb(socket, TI1250_GPIO3_CONTROL, gpio3_old);
+	}
+
+out:
+	if (pci_irq_status < 1) {
+		socket->cb_irq = 0;
+		printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n",
+		       pci_name(socket->dev));
+	}
+}
+
+
+/*
+ * ties INTA and INTB together. also changes the devices irq to that of
+ * the function 0 device. call from func1 only.
+ * returns 1 if INTRTIE changed, 0 otherwise.
+ */
+static int ti12xx_tie_interrupts(struct yenta_socket *socket, int *old_irq)
+{
+	struct pci_dev *func0;
+	u32 sysctl;
+
+	sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
+	if (sysctl & TI122X_SCR_INTRTIE)
+		return 0;
+
+	/* find func0 device */
+	func0 = pci_get_slot(socket->dev->bus, socket->dev->devfn & ~0x07);
+	if (!func0)
+		return 0;
+
+	/* change the interrupt to match func0, tie 'em up */
+	*old_irq = socket->cb_irq;
+	socket->cb_irq = socket->dev->irq = func0->irq;
+	sysctl |= TI122X_SCR_INTRTIE;
+	config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
+
+	pci_dev_put(func0);
+
+	return 1;
+}
+
+/* undo what ti12xx_tie_interrupts() did */
+static void ti12xx_untie_interrupts(struct yenta_socket *socket, int old_irq)
+{
+	u32 sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
+	sysctl &= ~TI122X_SCR_INTRTIE;
+	config_writel(socket, TI113X_SYSTEM_CONTROL, sysctl);
+
+	socket->cb_irq = socket->dev->irq = old_irq;
+}
+
+/* 
+ * irqrouting for func1, plays with INTB routing
+ * only touches MFUNC for INTB routing. all other bits are taken
+ * care of in func0 already.
+ */
+static void ti12xx_irqroute_func1(struct yenta_socket *socket)
+{
+	u32 mfunc, mfunc_old, devctl;
+	int pci_irq_status;
+
+	mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
+	devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
+	printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n",
+	       pci_name(socket->dev), mfunc, devctl);
+
+	/* make sure PCI interrupts are enabled before probing */
+	ti_init(socket);
+
+	/* test PCI interrupts first. only try fixing if return value is 0! */
+	pci_irq_status = yenta_probe_cb_irq(socket);
+	if (pci_irq_status)
+		goto out;
+
+	/*
+	 * We're here which means PCI interrupts are _not_ delivered. try to
+	 * find the right setting
+	 */
+	printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n",
+	       pci_name(socket->dev));
+
+
+	/* if all serial: set INTRTIE, probe again */
+	if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
+		int old_irq;
+
+		if (ti12xx_tie_interrupts(socket, &old_irq)) {
+			pci_irq_status = yenta_probe_cb_irq(socket);
+			if (pci_irq_status == 1) {
+				printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts, tied ok\n",
+				       pci_name(socket->dev));
+				goto out;
+			}
+
+			ti12xx_untie_interrupts(socket, old_irq);
+		}
+	}
+	/* parallel PCI: route INTB, probe again */
+	else {
+		int old_irq;
+
+		switch (socket->dev->device) {
+		case PCI_DEVICE_ID_TI_1250:
+			/* the 1250 has one pin for IRQSER/INTB depending on devctl */
+			break;
+
+		case PCI_DEVICE_ID_TI_1251A:
+		case PCI_DEVICE_ID_TI_1251B:
+		case PCI_DEVICE_ID_TI_1450:
+			/*
+			 *  those have a pin for IRQSER/INTB plus INTB in MFUNC0
+			 *  we alread probed the shared pin, now go for MFUNC0
+			 */
+			mfunc = (mfunc & ~TI122X_MFUNC0_MASK) | TI125X_MFUNC0_INTB;
+			break;
+
+		default:
+			mfunc = (mfunc & ~TI122X_MFUNC1_MASK) | TI122X_MFUNC1_INTB;
+			break;
+		}
+
+		/* write, probe */
+		if (mfunc != mfunc_old) {
+			config_writel(socket, TI122X_MFUNC, mfunc);
+
+			pci_irq_status = yenta_probe_cb_irq(socket);
+			if (pci_irq_status == 1) {
+				printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n",
+				       pci_name(socket->dev));
+				goto out;
+			}
+
+			mfunc = mfunc_old;
+			config_writel(socket, TI122X_MFUNC, mfunc);
+
+			if (pci_irq_status == -1)
+				goto out;
+		}
+		
+		/* still nothing: set INTRTIE */
+		if (ti12xx_tie_interrupts(socket, &old_irq)) {
+			pci_irq_status = yenta_probe_cb_irq(socket);
+			if (pci_irq_status == 1) {
+				printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts, tied ok\n",
+				       pci_name(socket->dev));
+				goto out;
+			}
+
+			ti12xx_untie_interrupts(socket, old_irq);
+		}
+	}
+
+out:
+	if (pci_irq_status < 1) {
+		socket->cb_irq = 0;
+		printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n",
+		       pci_name(socket->dev));
+	}
+}
+
+static int ti12xx_override(struct yenta_socket *socket)
+{
+	u32 val, val_orig;
+
+	/* make sure that memory burst is active */
+	val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
+	if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
+		printk(KERN_INFO "Yenta: Disabling CLKRUN feature\n");
+		val |= TI113X_SCR_KEEPCLK;
+	}
+	if (!(val & TI122X_SCR_MRBURSTUP)) {
+		printk(KERN_INFO "Yenta: Enabling burst memory read transactions\n");
+		val |= TI122X_SCR_MRBURSTUP;
+	}
+	if (val_orig != val)
+		config_writel(socket, TI113X_SYSTEM_CONTROL, val);
+
+	/*
+	 * for EnE bridges only: clear testbit TLTEnable. this makes the
+	 * RME Hammerfall DSP sound card working.
+	 */
+	if (socket->dev->vendor == PCI_VENDOR_ID_ENE) {
+		u8 test_c9 = config_readb(socket, ENE_TEST_C9);
+		test_c9 &= ~ENE_TEST_C9_TLTENABLE;
+		config_writeb(socket, ENE_TEST_C9, test_c9);
+	}
+
+	/*
+	 * Yenta expects controllers to use CSCINT to route
+	 * CSC interrupts to PCI rather than INTVAL.
+	 */
+	val = config_readb(socket, TI1250_DIAGNOSTIC);
+	printk(KERN_INFO "Yenta: Using %s to route CSC interrupts to PCI\n",
+		(val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
+	printk(KERN_INFO "Yenta: Routing CardBus interrupts to %s\n",
+		(val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
+
+	/* do irqrouting, depending on function */
+	if (PCI_FUNC(socket->dev->devfn) == 0)
+		ti12xx_irqroute_func0(socket);
+	else
+		ti12xx_irqroute_func1(socket);
+
+	return ti_override(socket);
+}
+
+
+static int ti1250_override(struct yenta_socket *socket)
+{
+	u8 old, diag;
+
+	old = config_readb(socket, TI1250_DIAGNOSTIC);
+	diag = old & ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ);
+	if (socket->cb_irq)
+		diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
+
+	if (diag != old) {
+		printk(KERN_INFO "Yenta: adjusting diagnostic: %02x -> %02x\n",
+			old, diag);
+		config_writeb(socket, TI1250_DIAGNOSTIC, diag);
+	}
+
+	return ti12xx_override(socket);
+}
+
+#endif /* CONFIG_CARDBUS */
+
+#endif /* _LINUX_TI113X_H */
+
diff --git a/drivers/pcmcia/topic.h b/drivers/pcmcia/topic.h
new file mode 100644
index 0000000..be420bb
--- /dev/null
+++ b/drivers/pcmcia/topic.h
@@ -0,0 +1,140 @@
+/*
+ * topic.h 1.8 1999/08/28 04:01:47
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ * topic.h $Release$ 1999/08/28 04:01:47
+ */
+
+#ifndef _LINUX_TOPIC_H
+#define _LINUX_TOPIC_H
+
+/* Register definitions for Toshiba ToPIC95/97/100 controllers */
+
+#define TOPIC_SOCKET_CONTROL		0x0090	/* 32 bit */
+#define  TOPIC_SCR_IRQSEL		0x00000001
+
+#define TOPIC_SLOT_CONTROL		0x00a0	/* 8 bit */
+#define  TOPIC_SLOT_SLOTON		0x80
+#define  TOPIC_SLOT_SLOTEN		0x40
+#define  TOPIC_SLOT_ID_LOCK		0x20
+#define  TOPIC_SLOT_ID_WP		0x10
+#define  TOPIC_SLOT_PORT_MASK		0x0c
+#define  TOPIC_SLOT_PORT_SHIFT		2
+#define  TOPIC_SLOT_OFS_MASK		0x03
+
+#define TOPIC_CARD_CONTROL		0x00a1	/* 8 bit */
+#define  TOPIC_CCR_INTB			0x20
+#define  TOPIC_CCR_INTA			0x10
+#define  TOPIC_CCR_CLOCK		0x0c
+#define  TOPIC_CCR_PCICLK		0x0c
+#define  TOPIC_CCR_PCICLK_2		0x08
+#define  TOPIC_CCR_CCLK			0x04
+
+#define TOPIC97_INT_CONTROL		0x00a1	/* 8 bit */
+#define  TOPIC97_ICR_INTB		0x20
+#define  TOPIC97_ICR_INTA		0x10
+#define  TOPIC97_ICR_STSIRQNP		0x04
+#define  TOPIC97_ICR_IRQNP		0x02
+#define  TOPIC97_ICR_IRQSEL		0x01
+
+#define TOPIC_CARD_DETECT		0x00a3	/* 8 bit */
+#define  TOPIC_CDR_MODE_PC32		0x80
+#define  TOPIC_CDR_VS1			0x04
+#define  TOPIC_CDR_VS2			0x02
+#define  TOPIC_CDR_SW_DETECT		0x01
+
+#define TOPIC_REGISTER_CONTROL		0x00a4	/* 32 bit */
+#define  TOPIC_RCR_RESUME_RESET		0x80000000
+#define  TOPIC_RCR_REMOVE_RESET		0x40000000
+#define  TOPIC97_RCR_CLKRUN_ENA		0x20000000
+#define  TOPIC97_RCR_TESTMODE		0x10000000
+#define  TOPIC97_RCR_IOPLUP		0x08000000
+#define  TOPIC_RCR_BUFOFF_PWROFF	0x02000000
+#define  TOPIC_RCR_BUFOFF_SIGOFF	0x01000000
+#define  TOPIC97_RCR_CB_DEV_MASK	0x0000f800
+#define  TOPIC97_RCR_CB_DEV_SHIFT	11
+#define  TOPIC97_RCR_RI_DISABLE		0x00000004
+#define  TOPIC97_RCR_CAUDIO_OFF		0x00000002
+#define  TOPIC_RCR_CAUDIO_INVERT	0x00000001
+
+#define TOPIC97_MISC1			0x00ad  /* 8bit */
+#define  TOPIC97_MISC1_CLOCKRUN_ENABLE	0x80
+#define  TOPIC97_MISC1_CLOCKRUN_MODE	0x40
+#define  TOPIC97_MISC1_DETECT_REQ_ENA	0x10
+#define  TOPIC97_MISC1_SCK_CLEAR_DIS	0x04
+#define  TOPIC97_MISC1_R2_LOW_ENABLE	0x10
+
+#define TOPIC97_MISC2			0x00ae  /* 8 bit */
+#define  TOPIC97_MISC2_SPWRCLK_MASK	0x70
+#define  TOPIC97_MISC2_SPWRMOD		0x08
+#define  TOPIC97_MISC2_SPWR_ENABLE	0x04
+#define  TOPIC97_MISC2_ZV_MODE		0x02
+#define  TOPIC97_MISC2_ZV_ENABLE	0x01
+
+#define TOPIC97_ZOOM_VIDEO_CONTROL	0x009c  /* 8 bit */
+#define  TOPIC97_ZV_CONTROL_ENABLE	0x01
+
+#define TOPIC97_AUDIO_VIDEO_SWITCH	0x003c  /* 8 bit */
+#define  TOPIC97_AVS_AUDIO_CONTROL	0x02
+#define  TOPIC97_AVS_VIDEO_CONTROL	0x01
+
+
+static void topic97_zoom_video(struct pcmcia_socket *sock, int onoff)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	u8 reg_zv, reg;
+
+	reg_zv = config_readb(socket, TOPIC97_ZOOM_VIDEO_CONTROL);
+	if (onoff) {
+		reg_zv |= TOPIC97_ZV_CONTROL_ENABLE;
+		config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
+
+		reg = config_readb(socket, TOPIC97_MISC2);
+		reg |= TOPIC97_MISC2_ZV_ENABLE;
+		config_writeb(socket, TOPIC97_MISC2, reg);
+
+		/* not sure this is needed, doc is unclear */
+#if 0
+		reg = config_readb(socket, TOPIC97_AUDIO_VIDEO_SWITCH);
+		reg |= TOPIC97_AVS_AUDIO_CONTROL | TOPIC97_AVS_VIDEO_CONTROL;
+		config_writeb(socket, TOPIC97_AUDIO_VIDEO_SWITCH, reg);
+#endif
+	}
+	else {
+		reg_zv &= ~TOPIC97_ZV_CONTROL_ENABLE;
+		config_writeb(socket, TOPIC97_ZOOM_VIDEO_CONTROL, reg_zv);
+	}
+
+}
+
+static int topic97_override(struct yenta_socket *socket)
+{
+	/* ToPIC97/100 support ZV */
+	socket->socket.zoom_video = topic97_zoom_video;
+	return 0;
+}
+
+#endif /* _LINUX_TOPIC_H */
diff --git a/drivers/pcmcia/vg468.h b/drivers/pcmcia/vg468.h
new file mode 100644
index 0000000..88c2b48
--- /dev/null
+++ b/drivers/pcmcia/vg468.h
@@ -0,0 +1,106 @@
+/*
+ * vg468.h 1.11 1999/10/25 20:03:34
+ *
+ * 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 David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of the
+ * above.  If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use
+ * your version of this file under the 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.
+ */
+
+#ifndef _LINUX_VG468_H
+#define _LINUX_VG468_H
+
+/* Special bit in I365_IDENT used for Vadem chip detection */
+#define I365_IDENT_VADEM	0x08
+
+/* Special definitions in I365_POWER */
+#define VG468_VPP2_MASK		0x0c
+#define VG468_VPP2_5V		0x04
+#define VG468_VPP2_12V		0x08
+
+/* Unique Vadem registers */
+#define VG469_VSENSE		0x1f	/* Card voltage sense */
+#define VG469_VSELECT		0x2f	/* Card voltage select */
+#define VG468_CTL		0x38	/* Control register */
+#define VG468_TIMER		0x39	/* Timer control */
+#define VG468_MISC		0x3a	/* Miscellaneous */
+#define VG468_GPIO_CFG		0x3b	/* GPIO configuration */
+#define VG469_EXT_MODE		0x3c	/* Extended mode register */
+#define VG468_SELECT		0x3d	/* Programmable chip select */
+#define VG468_SELECT_CFG	0x3e	/* Chip select configuration */
+#define VG468_ATA		0x3f	/* ATA control */
+
+/* Flags for VG469_VSENSE */
+#define VG469_VSENSE_A_VS1	0x01
+#define VG469_VSENSE_A_VS2	0x02
+#define VG469_VSENSE_B_VS1	0x04
+#define VG469_VSENSE_B_VS2	0x08
+
+/* Flags for VG469_VSELECT */
+#define VG469_VSEL_VCC		0x03
+#define VG469_VSEL_5V		0x00
+#define VG469_VSEL_3V		0x03
+#define VG469_VSEL_MAX		0x0c
+#define VG469_VSEL_EXT_STAT	0x10
+#define VG469_VSEL_EXT_BUS	0x20
+#define VG469_VSEL_MIXED	0x40
+#define VG469_VSEL_ISA		0x80
+
+/* Flags for VG468_CTL */
+#define VG468_CTL_SLOW		0x01	/* 600ns memory timing */
+#define VG468_CTL_ASYNC		0x02	/* Asynchronous bus clocking */
+#define VG468_CTL_TSSI		0x08	/* Tri-state some outputs */
+#define VG468_CTL_DELAY		0x10	/* Card detect debounce */
+#define VG468_CTL_INPACK	0x20	/* Obey INPACK signal? */
+#define VG468_CTL_POLARITY	0x40	/* VCCEN polarity */
+#define VG468_CTL_COMPAT	0x80	/* Compatibility stuff */
+
+#define VG469_CTL_WS_COMPAT	0x04	/* Wait state compatibility */
+#define VG469_CTL_STRETCH	0x10	/* LED stretch */
+
+/* Flags for VG468_TIMER */
+#define VG468_TIMER_ZEROPWR	0x10	/* Zero power control */
+#define VG468_TIMER_SIGEN	0x20	/* Power up */
+#define VG468_TIMER_STATUS	0x40	/* Activity timer status */
+#define VG468_TIMER_RES		0x80	/* Timer resolution */
+#define VG468_TIMER_MASK	0x0f	/* Activity timer timeout */
+
+/* Flags for VG468_MISC */
+#define VG468_MISC_GPIO		0x04	/* General-purpose IO */
+#define VG468_MISC_DMAWSB	0x08	/* DMA wait state control */
+#define VG469_MISC_LEDENA	0x10	/* LED enable */
+#define VG468_MISC_VADEMREV	0x40	/* Vadem revision control */
+#define VG468_MISC_UNLOCK	0x80	/* Unique register lock */
+
+/* Flags for VG469_EXT_MODE_A */
+#define VG469_MODE_VPPST	0x03	/* Vpp steering control */
+#define VG469_MODE_INT_SENSE	0x04	/* Internal voltage sense */
+#define VG469_MODE_CABLE	0x08
+#define VG469_MODE_COMPAT	0x10	/* i82365sl B or DF step */
+#define VG469_MODE_TEST		0x20
+#define VG469_MODE_RIO		0x40	/* Steer RIO to INTR? */
+
+/* Flags for VG469_EXT_MODE_B */
+#define VG469_MODE_B_3V		0x01	/* 3.3v for socket B */
+
+#endif /* _LINUX_VG468_H */
diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c
new file mode 100644
index 0000000..987bc21
--- /dev/null
+++ b/drivers/pcmcia/vrc4171_card.c
@@ -0,0 +1,846 @@
+/*
+ * vrc4171_card.c, NEC VRC4171 Card Controller driver for Socket Services.
+ *
+ * Copyright (C) 2003-2005  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  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/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <pcmcia/ss.h>
+
+#include "i82365.h"
+
+MODULE_DESCRIPTION("NEC VRC4171 Card Controllers driver for Socket Services");
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_LICENSE("GPL");
+
+#define CARD_MAX_SLOTS		2
+#define CARD_SLOTA		0
+#define CARD_SLOTB		1
+#define CARD_SLOTB_OFFSET	0x40
+
+#define CARD_MEM_START		0x10000000
+#define CARD_MEM_END		0x13ffffff
+#define CARD_MAX_MEM_OFFSET	0x3ffffff
+#define CARD_MAX_MEM_SPEED	1000
+
+#define CARD_CONTROLLER_INDEX	0x03e0
+#define CARD_CONTROLLER_DATA	0x03e1
+ /* Power register */
+  #define VPP_GET_VCC		0x01
+  #define POWER_ENABLE		0x10
+ #define CARD_VOLTAGE_SENSE	0x1f
+  #define VCC_3VORXV_CAPABLE	0x00
+  #define VCC_XV_ONLY		0x01
+  #define VCC_3V_CAPABLE	0x02
+  #define VCC_5V_ONLY		0x03
+ #define CARD_VOLTAGE_SELECT	0x2f
+  #define VCC_3V		0x01
+  #define VCC_5V		0x00
+  #define VCC_XV		0x02
+  #define VCC_STATUS_3V		0x02
+  #define VCC_STATUS_5V		0x01
+  #define VCC_STATUS_XV		0x03
+ #define GLOBAL_CONTROL		0x1e
+  #define EXWRBK		0x04
+  #define IRQPM_EN		0x08
+  #define CLRPMIRQ		0x10
+
+#define INTERRUPT_STATUS	0x05fa
+ #define IRQ_A			0x02
+ #define IRQ_B			0x04
+
+#define CONFIGURATION1		0x05fe
+ #define SLOTB_CONFIG		0xc000
+ #define SLOTB_NONE		0x0000
+ #define SLOTB_PCCARD		0x4000
+ #define SLOTB_CF		0x8000
+ #define SLOTB_FLASHROM		0xc000
+
+#define CARD_CONTROLLER_START	CARD_CONTROLLER_INDEX
+#define CARD_CONTROLLER_END	CARD_CONTROLLER_DATA
+
+#define IO_MAX_MAPS	2
+#define MEM_MAX_MAPS	5
+
+typedef enum {
+	SLOT_PROBE = 0,
+	SLOT_NOPROBE_IO,
+	SLOT_NOPROBE_MEM,
+	SLOT_NOPROBE_ALL,
+	SLOT_INITIALIZED,
+} vrc4171_slot_t;
+
+typedef enum {
+	SLOTB_IS_NONE,
+	SLOTB_IS_PCCARD,
+	SLOTB_IS_CF,
+	SLOTB_IS_FLASHROM,
+} vrc4171_slotb_t;
+
+typedef struct vrc4171_socket {
+	vrc4171_slot_t slot;
+	struct pcmcia_socket pcmcia_socket;
+	char name[24];
+	int csc_irq;
+	int io_irq;
+} vrc4171_socket_t;
+
+static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
+static vrc4171_slotb_t vrc4171_slotb = SLOTB_IS_NONE;
+static char vrc4171_card_name[] = "NEC VRC4171 Card Controller";
+static unsigned int vrc4171_irq;
+static uint16_t vrc4171_irq_mask = 0xdeb8;
+
+static struct resource vrc4171_card_resource[3] = {
+	{	.name		= vrc4171_card_name,
+		.start		= CARD_CONTROLLER_START,
+		.end		= CARD_CONTROLLER_END,
+		.flags		= IORESOURCE_IO,	},
+	{	.name		= vrc4171_card_name,
+		.start		= INTERRUPT_STATUS,
+		.end		= INTERRUPT_STATUS,
+		.flags		= IORESOURCE_IO,	},
+	{	.name		= vrc4171_card_name,
+		.start		= CONFIGURATION1,
+		.end		= CONFIGURATION1,
+		.flags		= IORESOURCE_IO,	},
+};
+
+static struct platform_device vrc4171_card_device = {
+	.name		= vrc4171_card_name,
+	.id		= 0,
+	.num_resources	= 3,
+	.resource	= vrc4171_card_resource,
+};
+
+static inline uint16_t vrc4171_get_irq_status(void)
+{
+	return inw(INTERRUPT_STATUS);
+}
+
+static inline void vrc4171_set_multifunction_pin(vrc4171_slotb_t config)
+{
+	uint16_t config1;
+
+	config1 = inw(CONFIGURATION1);
+	config1 &= ~SLOTB_CONFIG;
+
+	switch (config) {
+	case SLOTB_IS_NONE:
+		config1 |= SLOTB_NONE;
+		break;
+	case SLOTB_IS_PCCARD:
+		config1 |= SLOTB_PCCARD;
+		break;
+	case SLOTB_IS_CF:
+		config1 |= SLOTB_CF;
+		break;
+	case SLOTB_IS_FLASHROM:
+		config1 |= SLOTB_FLASHROM;
+		break;
+	default:
+		break;
+	}
+
+	outw(config1, CONFIGURATION1);
+}
+
+static inline uint8_t exca_read_byte(int slot, uint8_t index)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	return inb(CARD_CONTROLLER_DATA);
+}
+
+static inline uint16_t exca_read_word(int slot, uint8_t index)
+{
+	uint16_t data;
+
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index++, CARD_CONTROLLER_INDEX);
+	data = inb(CARD_CONTROLLER_DATA);
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	data |= ((uint16_t)inb(CARD_CONTROLLER_DATA)) << 8;
+
+	return data;
+}
+
+static inline uint8_t exca_write_byte(int slot, uint8_t index, uint8_t data)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	outb(data, CARD_CONTROLLER_DATA);
+
+	return data;
+}
+
+static inline uint16_t exca_write_word(int slot, uint8_t index, uint16_t data)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index++, CARD_CONTROLLER_INDEX);
+	outb(data, CARD_CONTROLLER_DATA);
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	outb((uint8_t)(data >> 8), CARD_CONTROLLER_DATA);
+
+	return data;
+}
+
+static inline int search_nonuse_irq(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (vrc4171_irq_mask & (1 << i)) {
+			vrc4171_irq_mask &= ~(1 << i);
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+static int pccard_init(struct pcmcia_socket *sock)
+{
+	vrc4171_socket_t *socket;
+	unsigned int slot;
+
+	sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
+	sock->irq_mask = 0;
+	sock->map_size = 0x1000;
+	sock->pci_irq = vrc4171_irq;
+
+	slot = sock->sock;
+	socket = &vrc4171_sockets[slot];
+	socket->csc_irq = search_nonuse_irq();
+	socket->io_irq = search_nonuse_irq();
+
+	return 0;
+}
+
+static int pccard_get_status(struct pcmcia_socket *sock, u_int *value)
+{
+	unsigned int slot;
+	uint8_t status, sense;
+	u_int val = 0;
+
+	if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || value == NULL)
+		return -EINVAL;
+
+	slot = sock->sock;
+
+	status = exca_read_byte(slot, I365_STATUS);
+	if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+		if (status & I365_CS_STSCHG)
+			val |= SS_STSCHG;
+	} else {
+		if (!(status & I365_CS_BVD1))
+			val |= SS_BATDEAD;
+		else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+			val |= SS_BATWARN;
+	}
+	if ((status & I365_CS_DETECT) == I365_CS_DETECT)
+		val |= SS_DETECT;
+	if (status & I365_CS_WRPROT)
+		val |= SS_WRPROT;
+	if (status & I365_CS_READY)
+		val |= SS_READY;
+	if (status & I365_CS_POWERON)
+		val |= SS_POWERON;
+
+	sense = exca_read_byte(slot, CARD_VOLTAGE_SENSE);
+	switch (sense) {
+	case VCC_3VORXV_CAPABLE:
+		val |= SS_3VCARD | SS_XVCARD;
+		break;
+	case VCC_XV_ONLY:
+		val |= SS_XVCARD;
+		break;
+	case VCC_3V_CAPABLE:
+		val |= SS_3VCARD;
+		break;
+	default:
+		/* 5V only */
+		break;
+	}
+
+	*value = val;
+
+	return 0;
+}
+
+static inline u_char get_Vcc_value(uint8_t voltage)
+{
+	switch (voltage) {
+	case VCC_STATUS_3V:
+		return 33;
+	case VCC_STATUS_5V:
+		return 50;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline u_char get_Vpp_value(uint8_t power, u_char Vcc)
+{
+	if ((power & 0x03) == 0x01 || (power & 0x03) == 0x02)
+		return Vcc;
+
+	return 0;
+}
+
+static int pccard_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	unsigned int slot;
+	uint8_t power, voltage, control, cscint;
+
+	if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || state == NULL)
+		return -EINVAL;
+
+	slot = sock->sock;
+
+	power = exca_read_byte(slot, I365_POWER);
+	voltage = exca_read_byte(slot, CARD_VOLTAGE_SELECT);
+
+	state->Vcc = get_Vcc_value(voltage);
+	state->Vpp = get_Vpp_value(power, state->Vcc);
+
+	state->flags = 0;
+	if (power & POWER_ENABLE)
+		state->flags |= SS_PWR_AUTO;
+	if (power & I365_PWR_OUT)
+		state->flags |= SS_OUTPUT_ENA;
+
+	control = exca_read_byte(slot, I365_INTCTL);
+	if (control & I365_PC_IOCARD)
+		state->flags |= SS_IOCARD;
+	if (!(control & I365_PC_RESET))
+		state->flags |= SS_RESET;
+
+        cscint = exca_read_byte(slot, I365_CSCINT);
+	state->csc_mask = 0;
+	if (state->flags & SS_IOCARD) {
+		if (cscint & I365_CSC_STSCHG)
+			state->flags |= SS_STSCHG;
+	} else {
+		if (cscint & I365_CSC_BVD1)
+			state->csc_mask |= SS_BATDEAD;
+		if (cscint & I365_CSC_BVD2)
+			state->csc_mask |= SS_BATWARN;
+	}
+	if (cscint & I365_CSC_READY)
+		state->csc_mask |= SS_READY;
+	if (cscint & I365_CSC_DETECT)
+		state->csc_mask |= SS_DETECT;
+
+	return 0;
+}
+
+static inline uint8_t set_Vcc_value(u_char Vcc)
+{
+	switch (Vcc) {
+	case 33:
+		return VCC_3V;
+	case 50:
+		return VCC_5V;
+	}
+
+	/* Small voltage is chosen for safety. */
+	return VCC_3V;
+}
+
+static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	vrc4171_socket_t *socket;
+	unsigned int slot;
+	uint8_t voltage, power, control, cscint;
+
+	if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+	    (state->Vpp != state->Vcc && state->Vpp != 0) ||
+	    (state->Vcc != 50 && state->Vcc != 33 && state->Vcc != 0))
+		return -EINVAL;
+
+	slot = sock->sock;
+	socket = &vrc4171_sockets[slot];
+
+	spin_lock_irq(&sock->lock);
+
+	voltage = set_Vcc_value(state->Vcc);
+	exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage);
+
+	power = POWER_ENABLE;
+	if (state->Vpp == state->Vcc)
+		power |= VPP_GET_VCC;
+	if (state->flags & SS_OUTPUT_ENA)
+		power |= I365_PWR_OUT;
+	exca_write_byte(slot, I365_POWER, power);
+
+	control = 0;
+	if (state->io_irq != 0)
+		control |= socket->io_irq;
+	if (state->flags & SS_IOCARD)
+		control |= I365_PC_IOCARD;
+	if (state->flags & SS_RESET)
+		control	&= ~I365_PC_RESET;
+	else
+		control |= I365_PC_RESET;
+	exca_write_byte(slot, I365_INTCTL, control);
+
+        cscint = 0;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+	exca_read_byte(slot, I365_CSC);	/* clear CardStatus change */
+	if (state->csc_mask != 0)
+		cscint |= socket->csc_irq << 8;
+	if (state->flags & SS_IOCARD) {
+		if (state->csc_mask & SS_STSCHG)
+			cscint |= I365_CSC_STSCHG;
+	} else {
+		if (state->csc_mask & SS_BATDEAD)
+			cscint |= I365_CSC_BVD1;
+		if (state->csc_mask & SS_BATWARN)
+			cscint |= I365_CSC_BVD2;
+	}
+	if (state->csc_mask & SS_READY)
+		cscint |= I365_CSC_READY;
+	if (state->csc_mask & SS_DETECT)
+		cscint |= I365_CSC_DETECT;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+
+	spin_unlock_irq(&sock->lock);
+
+	return 0;
+}
+
+static int pccard_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
+{
+	unsigned int slot;
+	uint8_t ioctl, addrwin;
+	u_char map;
+
+	if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+	    io == NULL || io->map >= IO_MAX_MAPS ||
+	    io->start > 0xffff || io->stop > 0xffff || io->start > io->stop)
+		return -EINVAL;
+
+	slot = sock->sock;
+	map = io->map;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_IO(map)) {
+		addrwin &= ~I365_ENA_IO(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	exca_write_word(slot, I365_IO(map)+I365_W_START, io->start);
+	exca_write_word(slot, I365_IO(map)+I365_W_STOP, io->stop);
+
+	ioctl = 0;
+	if (io->speed > 0)
+		ioctl |= I365_IOCTL_WAIT(map);
+	if (io->flags & MAP_16BIT)
+		ioctl |= I365_IOCTL_16BIT(map);
+	if (io->flags & MAP_AUTOSZ)
+		ioctl |= I365_IOCTL_IOCS16(map);
+	if (io->flags & MAP_0WS)
+		ioctl |= I365_IOCTL_0WS(map);
+	exca_write_byte(slot, I365_IOCTL, ioctl);
+
+	if (io->flags & MAP_ACTIVE) {
+		addrwin |= I365_ENA_IO(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	return 0;
+}
+
+static int pccard_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
+{
+	unsigned int slot;
+	uint16_t start, stop, offset;
+	uint8_t addrwin;
+	u_char map;
+
+	if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+	    mem == NULL || mem->map >= MEM_MAX_MAPS ||
+	    mem->res->start < CARD_MEM_START || mem->res->start > CARD_MEM_END ||
+	    mem->res->end < CARD_MEM_START || mem->res->end > CARD_MEM_END ||
+	    mem->res->start > mem->res->end ||
+	    mem->card_start > CARD_MAX_MEM_OFFSET ||
+	    mem->speed > CARD_MAX_MEM_SPEED)
+		return -EINVAL;
+
+	slot = sock->sock;
+	map = mem->map;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_MEM(map)) {
+		addrwin &= ~I365_ENA_MEM(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	start = (mem->res->start >> 12) & 0x3fff;
+	if (mem->flags & MAP_16BIT)
+		start |= I365_MEM_16BIT;
+	exca_write_word(slot, I365_MEM(map)+I365_W_START, start);
+
+	stop = (mem->res->end >> 12) & 0x3fff;
+	switch (mem->speed) {
+	case 0:
+		break;
+	case 1:
+		stop |= I365_MEM_WS0;
+		break;
+	case 2:
+		stop |= I365_MEM_WS1;
+		break;
+	default:
+		stop |= I365_MEM_WS0 | I365_MEM_WS1;
+		break;
+	}
+	exca_write_word(slot, I365_MEM(map)+I365_W_STOP, stop);
+
+	offset = (mem->card_start >> 12) & 0x3fff;
+	if (mem->flags & MAP_ATTRIB)
+		offset |= I365_MEM_REG;
+	if (mem->flags & MAP_WRPROT)
+		offset |= I365_MEM_WRPROT;
+	exca_write_word(slot, I365_MEM(map)+I365_W_OFF, offset);
+
+	if (mem->flags & MAP_ACTIVE) {
+		addrwin |= I365_ENA_MEM(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	return 0;
+}
+
+static struct pccard_operations vrc4171_pccard_operations = {
+	.init			= pccard_init,
+	.get_status		= pccard_get_status,
+	.get_socket		= pccard_get_socket,
+	.set_socket		= pccard_set_socket,
+	.set_io_map		= pccard_set_io_map,
+	.set_mem_map		= pccard_set_mem_map,
+};
+
+static inline unsigned int get_events(int slot)
+{
+	unsigned int events = 0;
+	uint8_t status, csc;
+
+	status = exca_read_byte(slot, I365_STATUS);
+	csc = exca_read_byte(slot, I365_CSC);
+
+	if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+		if ((csc & I365_CSC_STSCHG) && (status & I365_CS_STSCHG))
+			events |= SS_STSCHG;
+	} else {
+		if (csc & (I365_CSC_BVD1 | I365_CSC_BVD2)) {
+			if (!(status & I365_CS_BVD1))
+				events |= SS_BATDEAD;
+			else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+				events |= SS_BATWARN;
+		}
+	}
+	if ((csc & I365_CSC_READY) && (status & I365_CS_READY))
+		events |= SS_READY;
+	if ((csc & I365_CSC_DETECT) && ((status & I365_CS_DETECT) == I365_CS_DETECT))
+		events |= SS_DETECT;
+
+	return events;
+}
+
+static irqreturn_t pccard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vrc4171_socket_t *socket;
+	unsigned int events;
+	irqreturn_t retval = IRQ_NONE;
+	uint16_t status;
+
+	status = vrc4171_get_irq_status();
+	if (status & IRQ_A) {
+		socket = &vrc4171_sockets[CARD_SLOTA];
+		if (socket->slot == SLOT_INITIALIZED) {
+			if (status & (1 << socket->csc_irq)) {
+				events = get_events(CARD_SLOTA);
+				if (events != 0) {
+					pcmcia_parse_events(&socket->pcmcia_socket, events);
+					retval = IRQ_HANDLED;
+				}
+			}
+		}
+	}
+
+	if (status & IRQ_B) {
+		socket = &vrc4171_sockets[CARD_SLOTB];
+		if (socket->slot == SLOT_INITIALIZED) {
+			if (status & (1 << socket->csc_irq)) {
+				events = get_events(CARD_SLOTB);
+				if (events != 0) {
+					pcmcia_parse_events(&socket->pcmcia_socket, events);
+					retval = IRQ_HANDLED;
+				}
+			}
+		}
+	}
+
+	return retval;
+}
+
+static inline void reserve_using_irq(int slot)
+{
+	unsigned int irq;
+
+	irq = exca_read_byte(slot, I365_INTCTL);
+	irq &= 0x0f;
+	vrc4171_irq_mask &= ~(1 << irq);
+
+	irq = exca_read_byte(slot, I365_CSCINT);
+	irq = (irq & 0xf0) >> 4;
+	vrc4171_irq_mask &= ~(1 << irq);
+}
+
+static int __devinit vrc4171_add_sockets(void)
+{
+	vrc4171_socket_t *socket;
+	int slot, retval;
+
+	for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
+		if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
+			continue;
+
+		socket = &vrc4171_sockets[slot];
+		if (socket->slot != SLOT_PROBE) {
+			uint8_t addrwin;
+
+			switch (socket->slot) {
+			case SLOT_NOPROBE_MEM:
+				addrwin = exca_read_byte(slot, I365_ADDRWIN);
+				addrwin &= 0x1f;
+				exca_write_byte(slot, I365_ADDRWIN, addrwin);
+				break;
+			case SLOT_NOPROBE_IO:
+				addrwin = exca_read_byte(slot, I365_ADDRWIN);
+				addrwin &= 0xc0;
+				exca_write_byte(slot, I365_ADDRWIN, addrwin);
+				break;
+			default:
+				break;
+			}
+
+			reserve_using_irq(slot);
+			continue;
+		}
+
+		sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot);
+		socket->pcmcia_socket.dev.dev = &vrc4171_card_device.dev;
+		socket->pcmcia_socket.ops = &vrc4171_pccard_operations;
+		socket->pcmcia_socket.owner = THIS_MODULE;
+
+		retval = pcmcia_register_socket(&socket->pcmcia_socket);
+		if (retval < 0)
+			return retval;
+
+		exca_write_byte(slot, I365_ADDRWIN, 0);
+		exca_write_byte(slot, GLOBAL_CONTROL, 0);
+
+		socket->slot = SLOT_INITIALIZED;
+	}
+
+	return 0;
+}
+
+static void vrc4171_remove_sockets(void)
+{
+	vrc4171_socket_t *socket;
+	int slot;
+
+	for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
+		if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
+			continue;
+
+		socket = &vrc4171_sockets[slot];
+		if (socket->slot == SLOT_INITIALIZED)
+			pcmcia_unregister_socket(&socket->pcmcia_socket);
+
+		socket->slot = SLOT_PROBE;
+	}
+}
+
+static int __devinit vrc4171_card_setup(char *options)
+{
+	if (options == NULL || *options == '\0')
+		return 0;
+
+	if (strncmp(options, "irq:", 4) == 0) {
+		int irq;
+		options += 4;
+		irq = simple_strtoul(options, &options, 0);
+		if (irq >= 0 && irq < NR_IRQS)
+			vrc4171_irq = irq;
+
+		if (*options != ',')
+			return 0;
+		options++;
+	}
+
+	if (strncmp(options, "slota:", 6) == 0) {
+		options += 6;
+		if (*options != '\0') {
+			if (strncmp(options, "memnoprobe", 10) == 0) {
+				vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_MEM;
+				options += 10;
+			} else if (strncmp(options, "ionoprobe", 9) == 0) {
+				vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_IO;
+				options += 9;
+			} else if ( strncmp(options, "noprobe", 7) == 0) {
+				vrc4171_sockets[CARD_SLOTA].slot = SLOT_NOPROBE_ALL;
+				options += 7;
+			}
+
+			if (*options != ',')
+				return 0;
+			options++;
+		} else
+			return 0;
+
+	}
+
+	if (strncmp(options, "slotb:", 6) == 0) {
+		options += 6;
+		if (*options != '\0') {
+			if (strncmp(options, "pccard", 6) == 0) {
+				vrc4171_slotb = SLOTB_IS_PCCARD;
+				options += 6;
+			} else if (strncmp(options, "cf", 2) == 0) {
+				vrc4171_slotb = SLOTB_IS_CF;
+				options += 2;
+			} else if (strncmp(options, "flashrom", 8) == 0) {
+				vrc4171_slotb = SLOTB_IS_FLASHROM;
+				options += 8;
+			} else if (strncmp(options, "none", 4) == 0) {
+				vrc4171_slotb = SLOTB_IS_NONE;
+				options += 4;
+			}
+
+			if (*options != ',')
+				return 0;
+			options++;
+
+			if (strncmp(options, "memnoprobe", 10) == 0)
+				vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_MEM;
+			if (strncmp(options, "ionoprobe", 9) == 0)
+				vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_IO;
+			if (strncmp(options, "noprobe", 7) == 0)
+				vrc4171_sockets[CARD_SLOTB].slot = SLOT_NOPROBE_ALL;
+		}
+	}
+
+	return 0;
+}
+
+__setup("vrc4171_card=", vrc4171_card_setup);
+
+static int vrc4171_card_suspend(struct device *dev, u32 state, u32 level)
+{
+	int retval = 0;
+
+	if (level == SUSPEND_SAVE_STATE)
+		retval = pcmcia_socket_dev_suspend(dev, state);
+
+	return retval;
+}
+
+static int vrc4171_card_resume(struct device *dev, u32 level)
+{
+	int retval = 0;
+
+	if (level == RESUME_RESTORE_STATE)
+		retval = pcmcia_socket_dev_resume(dev);
+
+	return retval;
+}
+
+static struct device_driver vrc4171_card_driver = {
+	.name		= vrc4171_card_name,
+	.bus		= &platform_bus_type,
+	.suspend	= vrc4171_card_suspend,
+	.resume		= vrc4171_card_resume,
+};
+
+static int __devinit vrc4171_card_init(void)
+{
+	int retval;
+
+	retval = driver_register(&vrc4171_card_driver);
+	if (retval < 0)
+		return retval;
+
+	retval = platform_device_register(&vrc4171_card_device);
+	if (retval < 0) {
+		driver_unregister(&vrc4171_card_driver);
+		return retval;
+	}
+
+	vrc4171_set_multifunction_pin(vrc4171_slotb);
+
+	retval = vrc4171_add_sockets();
+	if (retval == 0)
+		retval = request_irq(vrc4171_irq, pccard_interrupt, SA_SHIRQ,
+		                     vrc4171_card_name, vrc4171_sockets);
+
+	if (retval < 0) {
+		vrc4171_remove_sockets();
+		platform_device_unregister(&vrc4171_card_device);
+		driver_unregister(&vrc4171_card_driver);
+		return retval;
+	}
+
+	printk(KERN_INFO "%s, connected to IRQ %d\n", vrc4171_card_driver.name, vrc4171_irq);
+
+	return 0;
+}
+
+static void __devexit vrc4171_card_exit(void)
+{
+	free_irq(vrc4171_irq, vrc4171_sockets);
+	vrc4171_remove_sockets();
+	platform_device_unregister(&vrc4171_card_device);
+	driver_unregister(&vrc4171_card_driver);
+}
+
+module_init(vrc4171_card_init);
+module_exit(vrc4171_card_exit);
diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c
new file mode 100644
index 0000000..db91259
--- /dev/null
+++ b/drivers/pcmcia/vrc4173_cardu.c
@@ -0,0 +1,617 @@
+/*
+ * FILE NAME
+ *	drivers/pcmcia/vrc4173_cardu.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ * 	NEC VRC4173 CARDU driver for Socket Services
+ *	(This device doesn't support CardBus. it is supporting only 16bit PC Card.)
+ *
+ * Copyright 2002,2003 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <pcmcia/ss.h>
+
+#include "vrc4173_cardu.h"
+
+MODULE_DESCRIPTION("NEC VRC4173 CARDU driver for Socket Services");
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_LICENSE("GPL");
+
+static int vrc4173_cardu_slots;
+
+static vrc4173_socket_t cardu_sockets[CARDU_MAX_SOCKETS];
+
+extern struct socket_info_t *pcmcia_register_socket (int slot,
+                                                     struct pccard_operations *vtable,
+                                                     int use_bus_pm);
+extern void pcmcia_unregister_socket(struct socket_info_t *s);
+
+static inline uint8_t exca_readb(vrc4173_socket_t *socket, uint16_t offset)
+{
+	return readb(socket->base + EXCA_REGS_BASE + offset);
+}
+
+static inline uint16_t exca_readw(vrc4173_socket_t *socket, uint16_t offset)
+{
+	uint16_t val;
+
+	val = readb(socket->base + EXCA_REGS_BASE + offset);
+	val |= (u16)readb(socket->base + EXCA_REGS_BASE + offset + 1) << 8;
+
+	return val;
+}
+
+static inline void exca_writeb(vrc4173_socket_t *socket, uint16_t offset, uint8_t val)
+{
+	writeb(val, socket->base + EXCA_REGS_BASE + offset);
+}
+
+static inline void exca_writew(vrc4173_socket_t *socket, uint8_t offset, uint16_t val)
+{
+	writeb((u8)val, socket->base + EXCA_REGS_BASE + offset);
+	writeb((u8)(val >> 8), socket->base + EXCA_REGS_BASE + offset + 1);
+}
+
+static inline uint32_t cardbus_socket_readl(vrc4173_socket_t *socket, u16 offset)
+{
+	return readl(socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
+}
+
+static inline void cardbus_socket_writel(vrc4173_socket_t *socket, u16 offset, uint32_t val)
+{
+	writel(val, socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
+}
+
+static void cardu_pciregs_init(struct pci_dev *dev)
+{
+	u32 syscnt;
+	u16 brgcnt;
+	u8 devcnt;
+
+	pci_write_config_dword(dev, 0x1c, 0x10000000);
+	pci_write_config_dword(dev, 0x20, 0x17fff000);
+	pci_write_config_dword(dev, 0x2c, 0);
+	pci_write_config_dword(dev, 0x30, 0xfffc);
+
+	pci_read_config_word(dev, BRGCNT, &brgcnt);
+	brgcnt &= ~IREQ_INT;
+	pci_write_config_word(dev, BRGCNT, brgcnt);
+
+	pci_read_config_dword(dev, SYSCNT, &syscnt);
+	syscnt &= ~(BAD_VCC_REQ_DISB|PCPCI_EN|CH_ASSIGN_MASK|SUB_ID_WR_EN|PCI_CLK_RIN);
+	syscnt |= (CH_ASSIGN_NODMA|ASYN_INT_MODE);
+	pci_write_config_dword(dev, SYSCNT, syscnt);
+
+	pci_read_config_byte(dev, DEVCNT, &devcnt);
+	devcnt &= ~(ZOOM_VIDEO_EN|SR_PCI_INT_SEL_MASK|PCI_INT_MODE|IRQ_MODE);
+	devcnt |= (SR_PCI_INT_SEL_NONE|IFG);
+	pci_write_config_byte(dev, DEVCNT, devcnt);
+
+	pci_write_config_byte(dev, CHIPCNT, S_PREF_DISB);
+
+	pci_write_config_byte(dev, SERRDIS, 0);
+}
+
+static int cardu_init(unsigned int slot)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[slot];
+
+	cardu_pciregs_init(socket->dev);
+
+	/* CARD_SC bits are cleared by reading CARD_SC. */
+	exca_writeb(socket, GLO_CNT, 0);
+
+	socket->cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
+	socket->cap.irq_mask = 0;
+	socket->cap.map_size = 0x1000;
+	socket->cap.pci_irq  = socket->dev->irq;
+	socket->events = 0;
+	spin_lock_init(socket->event_lock);
+
+	/* Enable PC Card status interrupts */
+	exca_writeb(socket, CARD_SCI, CARD_DT_EN|RDY_EN|BAT_WAR_EN|BAT_DEAD_EN);
+
+	return 0;
+}
+
+static int cardu_register_callback(unsigned int sock,
+                                           void (*handler)(void *, unsigned int),
+                                           void * info)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+
+	socket->handler = handler;
+	socket->info = info;
+
+	return 0;
+}
+
+static int cardu_inquire_socket(unsigned int sock, socket_cap_t *cap)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+
+	*cap = socket->cap;
+
+	return 0;
+}
+
+static int cardu_get_status(unsigned int sock, u_int *value)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint32_t state;
+	uint8_t status;
+	u_int val = 0;
+
+	status = exca_readb(socket, IF_STATUS);
+	if (status & CARD_PWR) val |= SS_POWERON;
+	if (status & READY) val |= SS_READY;
+	if (status & CARD_WP) val |= SS_WRPROT;
+	if ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2))
+		val |= SS_DETECT;
+	if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
+		if (status & STSCHG) val |= SS_STSCHG;
+	} else {
+		status &= BV_DETECT_MASK;
+		if (status != BV_DETECT_GOOD) {
+			if (status == BV_DETECT_WARN) val |= SS_BATWARN;
+			else val |= SS_BATDEAD;
+		}
+	}
+
+	state = cardbus_socket_readl(socket, SKT_PRE_STATE);
+	if (state & VOL_3V_CARD_DT) val |= SS_3VCARD;
+	if (state & VOL_XV_CARD_DT) val |= SS_XVCARD;
+	if (state & CB_CARD_DT) val |= SS_CARDBUS;
+	if (!(state &
+	      (VOL_YV_CARD_DT|VOL_XV_CARD_DT|VOL_3V_CARD_DT|VOL_5V_CARD_DT|CCD20|CCD10)))
+		val |= SS_PENDING;
+
+	*value = val;
+
+	return 0;
+}
+
+static inline u_char get_Vcc_value(uint8_t val)
+{
+	switch (val & VCC_MASK) {
+	case VCC_3V:
+		return 33;
+	case VCC_5V:
+		return 50;
+	}
+
+	return 0;
+}
+
+static inline u_char get_Vpp_value(uint8_t val)
+{
+	switch (val & VPP_MASK) {
+	case VPP_12V:
+		return 120;
+	case VPP_VCC:
+		return get_Vcc_value(val);
+	}
+
+	return 0;
+}
+
+static int cardu_get_socket(unsigned int sock, socket_state_t *state)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint8_t val;
+
+	val = exca_readb(socket, PWR_CNT);
+	state->Vcc = get_Vcc_value(val);
+	state->Vpp = get_Vpp_value(val);
+	state->flags = 0;
+	if (val & CARD_OUT_EN) state->flags |= SS_OUTPUT_ENA;
+
+	val = exca_readb(socket, INT_GEN_CNT);
+	if (!(val & CARD_REST0)) state->flags |= SS_RESET;
+	if (val & CARD_TYPE_IO) state->flags |= SS_IOCARD;
+
+	return 0;
+}
+
+static inline uint8_t set_Vcc_value(u_char Vcc)
+{
+	switch (Vcc) {
+	case 33:
+		return VCC_3V;
+	case 50:
+		return VCC_5V;
+	}
+
+	return VCC_0V;
+}
+
+static inline uint8_t set_Vpp_value(u_char Vpp)
+{
+	switch (Vpp) {
+	case 33:
+	case 50:
+		return VPP_VCC;
+	case 120:
+		return VPP_12V;
+	}
+
+	return VPP_0V;
+}
+
+static int cardu_set_socket(unsigned int sock, socket_state_t *state)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint8_t val;
+
+	if (((state->Vpp == 33) || (state->Vpp == 50)) && (state->Vpp != state->Vcc))
+			return -EINVAL;
+
+	val = set_Vcc_value(state->Vcc);
+	val |= set_Vpp_value(state->Vpp);
+	if (state->flags & SS_OUTPUT_ENA) val |= CARD_OUT_EN;
+	exca_writeb(socket, PWR_CNT, val);
+
+	val = exca_readb(socket, INT_GEN_CNT) & CARD_REST0;
+	if (state->flags & SS_RESET) val &= ~CARD_REST0;
+	else val |= CARD_REST0;
+	if (state->flags & SS_IOCARD) val |= CARD_TYPE_IO;
+	exca_writeb(socket, INT_GEN_CNT, val);
+
+	return 0;
+}
+
+static int cardu_get_io_map(unsigned int sock, struct pccard_io_map *io)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint8_t ioctl, window;
+	u_char map;
+
+	map = io->map;
+	if (map > 1)
+		return -EINVAL;
+
+	io->start = exca_readw(socket, IO_WIN_SA(map));
+	io->stop = exca_readw(socket, IO_WIN_EA(map));
+
+	ioctl = exca_readb(socket, IO_WIN_CNT);
+	window = exca_readb(socket, ADR_WIN_EN);
+	io->flags  = (window & IO_WIN_EN(map)) ? MAP_ACTIVE : 0;
+	if (ioctl & IO_WIN_DATA_AUTOSZ(map))
+		io->flags |= MAP_AUTOSZ;
+	else if (ioctl & IO_WIN_DATA_16BIT(map))
+		io->flags |= MAP_16BIT;
+
+	return 0;
+}
+
+static int cardu_set_io_map(unsigned int sock, struct pccard_io_map *io)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint16_t ioctl;
+	uint8_t window, enable;
+	u_char map;
+
+	map = io->map;
+	if (map > 1)
+		return -EINVAL;
+
+	window = exca_readb(socket, ADR_WIN_EN);
+	enable = IO_WIN_EN(map);
+
+	if (window & enable) {
+		window &= ~enable;
+		exca_writeb(socket, ADR_WIN_EN, window);
+	}
+
+	exca_writew(socket, IO_WIN_SA(map), io->start);
+	exca_writew(socket, IO_WIN_EA(map), io->stop);
+
+	ioctl = exca_readb(socket, IO_WIN_CNT) & ~IO_WIN_CNT_MASK(map);
+	if (io->flags & MAP_AUTOSZ) ioctl |= IO_WIN_DATA_AUTOSZ(map);
+	else if (io->flags & MAP_16BIT) ioctl |= IO_WIN_DATA_16BIT(map);
+	exca_writeb(socket, IO_WIN_CNT, ioctl);
+
+	if (io->flags & MAP_ACTIVE)
+		exca_writeb(socket, ADR_WIN_EN, window | enable);
+
+	return 0;
+}
+
+static int cardu_get_mem_map(unsigned int sock, struct pccard_mem_map *mem)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint32_t start, stop, offset, page;
+	uint8_t window;
+	u_char map;
+
+	map = mem->map;
+	if (map > 4)
+		return -EINVAL;
+
+	window = exca_readb(socket, ADR_WIN_EN);
+	mem->flags = (window & MEM_WIN_EN(map)) ? MAP_ACTIVE : 0;
+
+	start = exca_readw(socket, MEM_WIN_SA(map));
+	mem->flags |= (start & MEM_WIN_DSIZE) ? MAP_16BIT : 0;
+	start = (start & 0x0fff) << 12;
+
+	stop = exca_readw(socket, MEM_WIN_EA(map));
+	stop = ((stop & 0x0fff) << 12) + 0x0fff;
+
+	offset = exca_readw(socket, MEM_WIN_OA(map));
+	mem->flags |= (offset & MEM_WIN_WP) ? MAP_WRPROT : 0;
+	mem->flags |= (offset & MEM_WIN_REGSET) ? MAP_ATTRIB : 0;
+	offset = ((offset & 0x3fff) << 12) + start;
+	mem->card_start = offset & 0x03ffffff;
+
+	page = exca_readb(socket, MEM_WIN_SAU(map)) << 24;
+	mem->sys_start = start + page;
+	mem->sys_stop = start + page;
+
+	return 0;
+}
+
+static int cardu_set_mem_map(unsigned int sock, struct pccard_mem_map *mem)
+{
+	vrc4173_socket_t *socket = &cardu_sockets[sock];
+	uint16_t value;
+	uint8_t window, enable;
+	u_long sys_start, sys_stop, card_start;
+	u_char map;
+
+	map = mem->map;
+	sys_start = mem->sys_start;
+	sys_stop = mem->sys_stop;
+	card_start = mem->card_start;
+
+	if (map > 4 || sys_start > sys_stop || ((sys_start ^ sys_stop) >> 24) ||
+	    (card_start >> 26))
+		return -EINVAL;
+
+	window = exca_readb(socket, ADR_WIN_EN);
+	enable = MEM_WIN_EN(map);
+	if (window & enable) {
+		window &= ~enable;
+		exca_writeb(socket, ADR_WIN_EN, window);
+	}
+
+	exca_writeb(socket, MEM_WIN_SAU(map), sys_start >> 24);
+
+	value = (sys_start >> 12) & 0x0fff;
+	if (mem->flags & MAP_16BIT) value |= MEM_WIN_DSIZE;
+	exca_writew(socket, MEM_WIN_SA(map), value);
+
+	value = (sys_stop >> 12) & 0x0fff;
+	exca_writew(socket, MEM_WIN_EA(map), value);
+
+	value = ((card_start - sys_start) >> 12) & 0x3fff;
+	if (mem->flags & MAP_WRPROT) value |= MEM_WIN_WP;
+	if (mem->flags & MAP_ATTRIB) value |= MEM_WIN_REGSET;
+	exca_writew(socket, MEM_WIN_OA(map), value);
+
+	if (mem->flags & MAP_ACTIVE)
+		exca_writeb(socket, ADR_WIN_EN, window | enable);
+
+	return 0;
+}
+
+static void cardu_proc_setup(unsigned int sock, struct proc_dir_entry *base)
+{
+}
+
+static struct pccard_operations cardu_operations = {
+	.init			= cardu_init,
+	.register_callback	= cardu_register_callback,
+	.inquire_socket		= cardu_inquire_socket,
+	.get_status		= cardu_get_status,
+	.get_socket		= cardu_get_socket,
+	.set_socket		= cardu_set_socket,
+	.get_io_map		= cardu_get_io_map,
+	.set_io_map		= cardu_set_io_map,
+	.get_mem_map		= cardu_get_mem_map,
+	.set_mem_map		= cardu_set_mem_map,
+	.proc_setup		= cardu_proc_setup,
+};
+
+static void cardu_bh(void *data)
+{
+	vrc4173_socket_t *socket = (vrc4173_socket_t *)data;
+	uint16_t events;
+
+	spin_lock_irq(&socket->event_lock);
+	events = socket->events;
+	socket->events = 0;
+	spin_unlock_irq(&socket->event_lock);
+
+	if (socket->handler)
+		socket->handler(socket->info, events);
+}
+
+static uint16_t get_events(vrc4173_socket_t *socket)
+{
+	uint16_t events = 0;
+	uint8_t csc, status;
+
+	status = exca_readb(socket, IF_STATUS);
+	csc = exca_readb(socket, CARD_SC);
+	if ((csc & CARD_DT_CHG) &&
+	    ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2)))
+		events |= SS_DETECT;
+
+	if ((csc & RDY_CHG) && (status & READY))
+		events |= SS_READY;
+
+	if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
+		if ((csc & BAT_DEAD_ST_CHG) && (status & STSCHG))
+			events |= SS_STSCHG;
+	} else {
+		if (csc & (BAT_WAR_CHG|BAT_DEAD_ST_CHG)) {
+			if ((status & BV_DETECT_MASK) != BV_DETECT_GOOD) {
+				if (status == BV_DETECT_WARN) events |= SS_BATWARN;
+				else events |= SS_BATDEAD;
+			}
+		}
+	}
+
+	return events;
+}
+
+static void cardu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vrc4173_socket_t *socket = (vrc4173_socket_t *)dev_id;
+	uint16_t events;
+
+	INIT_WORK(&socket->tq_work, cardu_bh, socket);
+
+	events = get_events(socket);
+	if (events) {
+		spin_lock(&socket->event_lock);
+		socket->events |= events;
+		spin_unlock(&socket->event_lock);
+		schedule_work(&socket->tq_work);
+	}
+}
+
+static int __devinit vrc4173_cardu_probe(struct pci_dev *dev,
+                                         const struct pci_device_id *ent)
+{
+	vrc4173_socket_t *socket;
+	unsigned long start, len, flags;
+	int slot, err;
+
+	slot = vrc4173_cardu_slots++;
+	socket = &cardu_sockets[slot];
+	if (socket->noprobe != 0)
+		return -EBUSY;
+
+	sprintf(socket->name, "NEC VRC4173 CARDU%1d", slot+1);
+
+	if ((err = pci_enable_device(dev)) < 0)
+		return err;
+
+	start = pci_resource_start(dev, 0);
+	if (start == 0)
+		return -ENODEV;
+
+	len = pci_resource_len(dev, 0);
+	if (len == 0)
+		return -ENODEV;
+
+	if (((flags = pci_resource_flags(dev, 0)) & IORESOURCE_MEM) == 0)
+		return -EBUSY;
+
+	if ((err = pci_request_regions(dev, socket->name)) < 0)
+		return err;
+
+	socket->base = ioremap(start, len);
+	if (socket->base == NULL)
+		return -ENODEV;
+
+	socket->dev = dev;
+
+	socket->pcmcia_socket = pcmcia_register_socket(slot, &cardu_operations, 1);
+	if (socket->pcmcia_socket == NULL) {
+		iounmap(socket->base);
+		socket->base = NULL;
+		return -ENOMEM;
+	}
+
+	if (request_irq(dev->irq, cardu_interrupt, SA_SHIRQ, socket->name, socket) < 0) {
+		pcmcia_unregister_socket(socket->pcmcia_socket);
+		socket->pcmcia_socket = NULL;
+		iounmap(socket->base);
+		socket->base = NULL;
+		return -EBUSY;
+	}
+
+	printk(KERN_INFO "%s at %#08lx, IRQ %d\n", socket->name, start, dev->irq);
+
+	return 0;
+}
+
+static int __devinit vrc4173_cardu_setup(char *options)
+{
+	if (options == NULL || *options == '\0')
+		return 0;
+
+	if (strncmp(options, "cardu1:", 7) == 0) {
+		options += 7;
+		if (*options != '\0') {
+			if (strncmp(options, "noprobe", 7) == 0) {
+				cardu_sockets[CARDU1].noprobe = 1;
+				options += 7;
+			}
+
+			if (*options != ',')
+				return 0;
+		} else
+			return 0;
+	}
+
+	if (strncmp(options, "cardu2:", 7) == 0) {
+		options += 7;
+		if ((*options != '\0') && (strncmp(options, "noprobe", 7) == 0))
+			cardu_sockets[CARDU2].noprobe = 1;
+	}
+
+	return 0;
+}
+
+__setup("vrc4173_cardu=", vrc4173_cardu_setup);
+
+static struct pci_device_id vrc4173_cardu_id_table[] __devinitdata = {
+	{	.vendor		= PCI_VENDOR_ID_NEC,
+		.device		= PCI_DEVICE_ID_NEC_NAPCCARD,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID, },
+        {0, }
+};
+
+static struct pci_driver vrc4173_cardu_driver = {
+	.name		= "NEC VRC4173 CARDU",
+	.probe		= vrc4173_cardu_probe,
+	.id_table	= vrc4173_cardu_id_table,
+};
+
+static int __devinit vrc4173_cardu_init(void)
+{
+	vrc4173_cardu_slots = 0;
+
+	return pci_module_init(&vrc4173_cardu_driver);
+}
+
+static void __devexit vrc4173_cardu_exit(void)
+{
+	pci_unregister_driver(&vrc4173_cardu_driver);
+}
+
+module_init(vrc4173_cardu_init);
+module_exit(vrc4173_cardu_exit);
+MODULE_DEVICE_TABLE(pci, vrc4173_cardu_id_table);
diff --git a/drivers/pcmcia/vrc4173_cardu.h b/drivers/pcmcia/vrc4173_cardu.h
new file mode 100644
index 0000000..113726f
--- /dev/null
+++ b/drivers/pcmcia/vrc4173_cardu.h
@@ -0,0 +1,247 @@
+/*
+ * FILE NAME
+ *	drivers/pcmcia/vrc4173_cardu.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Include file for NEC VRC4173 CARDU.
+ *
+ * Copyright 2002 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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 _VRC4173_CARDU_H
+#define _VRC4173_CARDU_H
+
+#include <linux/pci.h>
+
+#include <pcmcia/ss.h>
+
+#define CARDU_MAX_SOCKETS	2
+#define CARDU1			0
+#define CARDU2			1
+
+/*
+ * PCI Configuration Registers
+ */
+#define BRGCNT			0x3e
+ #define POST_WR_EN		0x0400
+ #define MEM1_PREF_EN		0x0200
+ #define MEM0_PREF_EN		0x0100
+ #define IREQ_INT		0x0080
+ #define CARD_RST		0x0040
+ #define MABORT_MODE		0x0020
+ #define VGA_EN			0x0008
+ #define ISA_EN			0x0004
+ #define SERR_EN		0x0002
+ #define PERR_EN		0x0001
+
+#define SYSCNT			0x80
+ #define BAD_VCC_REQ_DISB	0x00200000
+ #define PCPCI_EN		0x00080000
+ #define CH_ASSIGN_MASK		0x00070000
+ #define CH_ASSIGN_NODMA	0x00040000
+ #define SUB_ID_WR_EN		0x00000008
+ #define ASYN_INT_MODE		0x00000004
+ #define PCI_CLK_RIN		0x00000002
+
+#define DEVCNT			0x91
+ #define ZOOM_VIDEO_EN		0x40
+ #define SR_PCI_INT_SEL_MASK	0x18
+ #define SR_PCI_INT_SEL_NONE	0x00
+ #define PCI_INT_MODE		0x04
+ #define IRQ_MODE		0x02
+ #define IFG			0x01
+
+#define CHIPCNT			0x9c
+ #define S_PREF_DISB		0x10
+
+#define SERRDIS			0x9f
+ #define SERR_DIS_MAB		0x10
+ #define SERR_DIS_TAB		0x08
+ #define SERR_DIS_DT_PERR	0x04
+
+/*
+ * ExCA Registers
+ */
+#define EXCA_REGS_BASE		0x800
+#define EXCA_REGS_SIZE		0x800
+
+#define ID_REV			0x000
+ #define IF_TYPE_16BIT		0x80
+
+#define IF_STATUS		0x001
+ #define CARD_PWR		0x40
+ #define READY			0x20
+ #define CARD_WP		0x10
+ #define CARD_DETECT2		0x08
+ #define CARD_DETECT1		0x04
+ #define BV_DETECT_MASK		0x03
+ #define BV_DETECT_GOOD		0x03	/* Memory card */
+ #define BV_DETECT_WARN		0x02
+ #define BV_DETECT_BAD1		0x01
+ #define BV_DETECT_BAD0		0x00
+ #define STSCHG			0x02	/* I/O card */
+ #define SPKR			0x01
+
+#define PWR_CNT			0x002
+ #define CARD_OUT_EN		0x80
+ #define VCC_MASK		0x18
+ #define VCC_3V			0x18
+ #define VCC_5V			0x10
+ #define VCC_0V			0x00
+ #define VPP_MASK		0x03
+ #define VPP_12V		0x02
+ #define VPP_VCC		0x01
+ #define VPP_0V			0x00
+
+#define INT_GEN_CNT		0x003
+ #define CARD_REST0		0x40
+ #define CARD_TYPE_MASK		0x20
+ #define CARD_TYPE_IO		0x20
+ #define CARD_TYPE_MEM		0x00
+
+#define CARD_SC			0x004
+ #define CARD_DT_CHG		0x08
+ #define RDY_CHG		0x04
+ #define BAT_WAR_CHG		0x02
+ #define BAT_DEAD_ST_CHG	0x01
+
+#define CARD_SCI		0x005
+ #define CARD_DT_EN		0x08
+ #define RDY_EN			0x04
+ #define BAT_WAR_EN		0x02
+ #define BAT_DEAD_EN		0x01
+
+#define ADR_WIN_EN		0x006
+ #define IO_WIN_EN(x)		(0x40 << (x))
+ #define MEM_WIN_EN(x)		(0x01 << (x))
+
+#define IO_WIN_CNT		0x007
+ #define IO_WIN_CNT_MASK(x)	(0x03 << ((x) << 2))
+ #define IO_WIN_DATA_AUTOSZ(x)	(0x02 << ((x) << 2))
+ #define IO_WIN_DATA_16BIT(x)	(0x01 << ((x) << 2))
+
+#define IO_WIN_SA(x)		(0x008 + ((x) << 2))
+#define IO_WIN_EA(x)		(0x00a + ((x) << 2))
+
+#define MEM_WIN_SA(x)		(0x010 + ((x) << 3))
+ #define MEM_WIN_DSIZE		0x8000
+
+#define MEM_WIN_EA(x)		(0x012 + ((x) << 3))
+
+#define MEM_WIN_OA(x)		(0x014 + ((x) << 3))
+ #define MEM_WIN_WP		0x8000
+ #define MEM_WIN_REGSET		0x4000
+
+#define GEN_CNT			0x016
+ #define VS2_STATUS		0x80
+ #define VS1_STATUS		0x40
+ #define EXCA_REG_RST_EN	0x02
+
+#define GLO_CNT			0x01e
+ #define FUN_INT_LEV		0x08
+ #define INT_WB_CLR		0x04
+ #define CSC_INT_LEV		0x02
+
+#define IO_WIN_OAL(x)		(0x036 + ((x) << 1))
+#define IO_WIN_OAH(x)		(0x037 + ((x) << 1))
+
+#define MEM_WIN_SAU(x)		(0x040 + (x))
+
+#define IO_SETUP_TIM		0x080
+#define IO_CMD_TIM		0x081
+#define IO_HOLD_TIM		0x082
+#define MEM_SETUP_TIM(x)	(0x084 + ((x) << 2))
+#define MEM_CMD_TIM(x)		(0x085 + ((x) << 2))
+#define MEM_HOLD_TIM(x)		(0x086 + ((x) << 2))
+ #define TIM_CLOCKS(x)		((x) - 1)
+
+#define MEM_TIM_SEL1		0x08c
+#define MEM_TIM_SEL2		0x08d
+ #define MEM_WIN_TIMSEL1(x)	(0x03 << (((x) & 3) << 1))
+
+#define MEM_WIN_PWEN		0x091
+ #define POSTWEN		0x01
+
+/*
+ * CardBus Socket Registers
+ */
+#define CARDBUS_SOCKET_REGS_BASE	0x000
+#define CARDBUS_SOCKET_REGS_SIZE	0x800
+
+#define SKT_EV			0x000
+ #define POW_CYC_EV		0x00000008
+ #define CCD2_EV		0x00000004
+ #define CCD1_EV		0x00000002
+ #define CSTSCHG_EV		0x00000001
+
+#define SKT_MASK		0x004
+ #define POW_CYC_MASK		0x00000008
+ #define CCD_MASK		0x00000006
+ #define CSC_MASK		0x00000001
+
+#define SKT_PRE_STATE		0x008
+#define SKT_FORCE_EV		0x00c
+ #define VOL_3V_SKT		0x20000000
+ #define VOL_5V_SKT		0x10000000
+ #define CVS_TEST		0x00004000
+ #define VOL_YV_CARD_DT		0x00002000
+ #define VOL_XV_CARD_DT		0x00001000
+ #define VOL_3V_CARD_DT		0x00000800
+ #define VOL_5V_CARD_DT		0x00000400
+ #define BAD_VCC_REQ		0x00000200
+ #define DATA_LOST		0x00000100
+ #define NOT_A_CARD		0x00000080
+ #define CREADY			0x00000040
+ #define CB_CARD_DT		0x00000020
+ #define R2_CARD_DT		0x00000010
+ #define POW_UP			0x00000008
+ #define CCD20			0x00000004
+ #define CCD10			0x00000002
+ #define CSTSCHG		0x00000001
+
+#define SKT_CNT			0x010
+ #define STP_CLK_EN		0x00000080
+ #define VCC_CNT_MASK		0x00000070
+ #define VCC_CNT_3V		0x00000030
+ #define VCC_CNT_5V		0x00000020
+ #define VCC_CNT_0V		0x00000000
+ #define VPP_CNT_MASK		0x00000007
+ #define VPP_CNT_3V		0x00000003
+ #define VPP_CNT_5V		0x00000002
+ #define VPP_CNT_12V		0x00000001
+ #define VPP_CNT_0V		0x00000000
+
+typedef struct vrc4173_socket {
+	int noprobe;
+	struct pci_dev *dev;
+	void *base;
+	void (*handler)(void *, unsigned int);
+	void *info;
+	socket_cap_t cap;
+	spinlock_t event_lock;
+	uint16_t events;
+	struct socket_info_t *pcmcia_socket;
+	struct work_struct tq_work;
+	char name[20];
+} vrc4173_socket_t;
+
+#endif /* _VRC4173_CARDU_H */
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
new file mode 100644
index 0000000..6404d97
--- /dev/null
+++ b/drivers/pcmcia/yenta_socket.c
@@ -0,0 +1,1160 @@
+/*
+ * Regular cardbus driver ("yenta_socket")
+ *
+ * (C) Copyright 1999, 2000 Linus Torvalds
+ *
+ * Changelog:
+ * Aug 2002: Manfred Spraul <manfred@colorfullife.com>
+ * 	Dynamically adjust the size of the bridge resource
+ * 	
+ * May 2003: Dominik Brodowski <linux@brodo.de>
+ * 	Merge pci_socket.c and yenta.c into one file
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#include <asm/io.h>
+
+#include "yenta_socket.h"
+#include "i82365.h"
+
+static int disable_clkrun;
+module_param(disable_clkrun, bool, 0444);
+MODULE_PARM_DESC(disable_clkrun, "If PC card doesn't function properly, please try this option");
+
+#if 0
+#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
+#else
+#define debug(x,args...)
+#endif
+
+/* Don't ask.. */
+#define to_cycles(ns)	((ns)/120)
+#define to_ns(cycles)	((cycles)*120)
+
+static int yenta_probe_cb_irq(struct yenta_socket *socket);
+
+
+static unsigned int override_bios;
+module_param(override_bios, uint, 0000);
+MODULE_PARM_DESC (override_bios, "yenta ignore bios resource allocation");
+
+/*
+ * Generate easy-to-use ways of reading a cardbus sockets
+ * regular memory space ("cb_xxx"), configuration space
+ * ("config_xxx") and compatibility space ("exca_xxxx")
+ */
+static inline u32 cb_readl(struct yenta_socket *socket, unsigned reg)
+{
+	u32 val = readl(socket->base + reg);
+	debug("%p %04x %08x\n", socket, reg, val);
+	return val;
+}
+
+static inline void cb_writel(struct yenta_socket *socket, unsigned reg, u32 val)
+{
+	debug("%p %04x %08x\n", socket, reg, val);
+	writel(val, socket->base + reg);
+}
+
+static inline u8 config_readb(struct yenta_socket *socket, unsigned offset)
+{
+	u8 val;
+	pci_read_config_byte(socket->dev, offset, &val);
+	debug("%p %04x %02x\n", socket, offset, val);
+	return val;
+}
+
+static inline u16 config_readw(struct yenta_socket *socket, unsigned offset)
+{
+	u16 val;
+	pci_read_config_word(socket->dev, offset, &val);
+	debug("%p %04x %04x\n", socket, offset, val);
+	return val;
+}
+
+static inline u32 config_readl(struct yenta_socket *socket, unsigned offset)
+{
+	u32 val;
+	pci_read_config_dword(socket->dev, offset, &val);
+	debug("%p %04x %08x\n", socket, offset, val);
+	return val;
+}
+
+static inline void config_writeb(struct yenta_socket *socket, unsigned offset, u8 val)
+{
+	debug("%p %04x %02x\n", socket, offset, val);
+	pci_write_config_byte(socket->dev, offset, val);
+}
+
+static inline void config_writew(struct yenta_socket *socket, unsigned offset, u16 val)
+{
+	debug("%p %04x %04x\n", socket, offset, val);
+	pci_write_config_word(socket->dev, offset, val);
+}
+
+static inline void config_writel(struct yenta_socket *socket, unsigned offset, u32 val)
+{
+	debug("%p %04x %08x\n", socket, offset, val);
+	pci_write_config_dword(socket->dev, offset, val);
+}
+
+static inline u8 exca_readb(struct yenta_socket *socket, unsigned reg)
+{
+	u8 val = readb(socket->base + 0x800 + reg);
+	debug("%p %04x %02x\n", socket, reg, val);
+	return val;
+}
+
+static inline u8 exca_readw(struct yenta_socket *socket, unsigned reg)
+{
+	u16 val;
+	val = readb(socket->base + 0x800 + reg);
+	val |= readb(socket->base + 0x800 + reg + 1) << 8;
+	debug("%p %04x %04x\n", socket, reg, val);
+	return val;
+}
+
+static inline void exca_writeb(struct yenta_socket *socket, unsigned reg, u8 val)
+{
+	debug("%p %04x %02x\n", socket, reg, val);
+	writeb(val, socket->base + 0x800 + reg);
+}
+
+static void exca_writew(struct yenta_socket *socket, unsigned reg, u16 val)
+{
+	debug("%p %04x %04x\n", socket, reg, val);
+	writeb(val, socket->base + 0x800 + reg);
+	writeb(val >> 8, socket->base + 0x800 + reg + 1);
+}
+
+/*
+ * Ugh, mixed-mode cardbus and 16-bit pccard state: things depend
+ * on what kind of card is inserted..
+ */
+static int yenta_get_status(struct pcmcia_socket *sock, unsigned int *value)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	unsigned int val;
+	u32 state = cb_readl(socket, CB_SOCKET_STATE);
+
+	val  = (state & CB_3VCARD) ? SS_3VCARD : 0;
+	val |= (state & CB_XVCARD) ? SS_XVCARD : 0;
+	val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD
+			 | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING;
+
+	if (state & CB_CBCARD) {
+		val |= SS_CARDBUS;	
+		val |= (state & CB_CARDSTS) ? SS_STSCHG : 0;
+		val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT;
+		val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0;
+	} else {
+		u8 status = exca_readb(socket, I365_STATUS);
+		val |= ((status & I365_CS_DETECT) == I365_CS_DETECT) ? SS_DETECT : 0;
+		if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) {
+			val |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
+		} else {
+			val |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
+			val |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
+		}
+		val |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
+		val |= (status & I365_CS_READY) ? SS_READY : 0;
+		val |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
+	}
+
+	*value = val;
+	return 0;
+}
+
+static int yenta_Vcc_power(u32 control)
+{
+	switch (control & CB_SC_VCC_MASK) {
+	case CB_SC_VCC_5V: return 50;
+	case CB_SC_VCC_3V: return 33;
+	default: return 0;
+	}
+}
+
+static int yenta_Vpp_power(u32 control)
+{
+	switch (control & CB_SC_VPP_MASK) {
+	case CB_SC_VPP_12V: return 120;
+	case CB_SC_VPP_5V: return 50;
+	case CB_SC_VPP_3V: return 33;
+	default: return 0;
+	}
+}
+
+static int yenta_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	u8 reg;
+	u32 control;
+
+	control = cb_readl(socket, CB_SOCKET_CONTROL);
+
+	state->Vcc = yenta_Vcc_power(control);
+	state->Vpp = yenta_Vpp_power(control);
+	state->io_irq = socket->io_irq;
+
+	if (cb_readl(socket, CB_SOCKET_STATE) & CB_CBCARD) {
+		u16 bridge = config_readw(socket, CB_BRIDGE_CONTROL);
+		if (bridge & CB_BRIDGE_CRST)
+			state->flags |= SS_RESET;
+		return 0;
+	}
+
+	/* 16-bit card state.. */
+	reg = exca_readb(socket, I365_POWER);
+	state->flags = (reg & I365_PWR_AUTO) ? SS_PWR_AUTO : 0;
+	state->flags |= (reg & I365_PWR_OUT) ? SS_OUTPUT_ENA : 0;
+
+	reg = exca_readb(socket, I365_INTCTL);
+	state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET;
+	state->flags |= (reg & I365_PC_IOCARD) ? SS_IOCARD : 0;
+
+	reg = exca_readb(socket, I365_CSCINT);
+	state->csc_mask = (reg & I365_CSC_DETECT) ? SS_DETECT : 0;
+	if (state->flags & SS_IOCARD) {
+		state->csc_mask |= (reg & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+	} else {
+		state->csc_mask |= (reg & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+		state->csc_mask |= (reg & I365_CSC_BVD2) ? SS_BATWARN : 0;
+		state->csc_mask |= (reg & I365_CSC_READY) ? SS_READY : 0;
+	}
+
+	return 0;
+}
+
+static void yenta_set_power(struct yenta_socket *socket, socket_state_t *state)
+{
+	u32 reg = 0;	/* CB_SC_STPCLK? */
+	switch (state->Vcc) {
+	case 33: reg = CB_SC_VCC_3V; break;
+	case 50: reg = CB_SC_VCC_5V; break;
+	default: reg = 0; break;
+	}
+	switch (state->Vpp) {
+	case 33:  reg |= CB_SC_VPP_3V; break;
+	case 50:  reg |= CB_SC_VPP_5V; break;
+	case 120: reg |= CB_SC_VPP_12V; break;
+	}
+	if (reg != cb_readl(socket, CB_SOCKET_CONTROL))
+		cb_writel(socket, CB_SOCKET_CONTROL, reg);
+}
+
+static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	u16 bridge;
+
+	yenta_set_power(socket, state);
+	socket->io_irq = state->io_irq;
+	bridge = config_readw(socket, CB_BRIDGE_CONTROL) & ~(CB_BRIDGE_CRST | CB_BRIDGE_INTR);
+	if (cb_readl(socket, CB_SOCKET_STATE) & CB_CBCARD) {
+		u8 intr;
+		bridge |= (state->flags & SS_RESET) ? CB_BRIDGE_CRST : 0;
+
+		/* ISA interrupt control? */
+		intr = exca_readb(socket, I365_INTCTL);
+		intr = (intr & ~0xf);
+		if (!socket->cb_irq) {
+			intr |= state->io_irq;
+			bridge |= CB_BRIDGE_INTR;
+		}
+		exca_writeb(socket, I365_INTCTL, intr);
+	}  else {
+		u8 reg;
+
+		reg = exca_readb(socket, I365_INTCTL) & (I365_RING_ENA | I365_INTR_ENA);
+		reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
+		reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
+		if (state->io_irq != socket->cb_irq) {
+			reg |= state->io_irq;
+			bridge |= CB_BRIDGE_INTR;
+		}
+		exca_writeb(socket, I365_INTCTL, reg);
+
+		reg = exca_readb(socket, I365_POWER) & (I365_VCC_MASK|I365_VPP1_MASK);
+		reg |= I365_PWR_NORESET;
+		if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
+		if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
+		if (exca_readb(socket, I365_POWER) != reg)
+			exca_writeb(socket, I365_POWER, reg);
+
+		/* CSC interrupt: no ISA irq for CSC */
+		reg = I365_CSC_DETECT;
+		if (state->flags & SS_IOCARD) {
+			if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
+		} else {
+			if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
+			if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
+			if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
+		}
+		exca_writeb(socket, I365_CSCINT, reg);
+		exca_readb(socket, I365_CSC);
+		if(sock->zoom_video)
+			sock->zoom_video(sock, state->flags & SS_ZVCARD);
+	}
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge);
+	/* Socket event mask: get card insert/remove events.. */
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	cb_writel(socket, CB_SOCKET_MASK, CB_CDMASK);
+	return 0;
+}
+
+static int yenta_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	int map;
+	unsigned char ioctl, addr, enable;
+
+	map = io->map;
+
+	if (map > 1)
+		return -EINVAL;
+
+	enable = I365_ENA_IO(map);
+	addr = exca_readb(socket, I365_ADDRWIN);
+
+	/* Disable the window before changing it.. */
+	if (addr & enable) {
+		addr &= ~enable;
+		exca_writeb(socket, I365_ADDRWIN, addr);
+	}
+
+	exca_writew(socket, I365_IO(map)+I365_W_START, io->start);
+	exca_writew(socket, I365_IO(map)+I365_W_STOP, io->stop);
+
+	ioctl = exca_readb(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map);
+	if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
+	if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
+	if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
+	exca_writeb(socket, I365_IOCTL, ioctl);
+
+	if (io->flags & MAP_ACTIVE)
+		exca_writeb(socket, I365_ADDRWIN, addr | enable);
+	return 0;
+}
+
+static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	struct pci_bus_region region;
+	int map;
+	unsigned char addr, enable;
+	unsigned int start, stop, card_start;
+	unsigned short word;
+
+	pcibios_resource_to_bus(socket->dev, &region, mem->res);
+
+	map = mem->map;
+	start = region.start;
+	stop = region.end;
+	card_start = mem->card_start;
+
+	if (map > 4 || start > stop || ((start ^ stop) >> 24) ||
+	    (card_start >> 26) || mem->speed > 1000)
+		return -EINVAL;
+
+	enable = I365_ENA_MEM(map);
+	addr = exca_readb(socket, I365_ADDRWIN);
+	if (addr & enable) {
+		addr &= ~enable;
+		exca_writeb(socket, I365_ADDRWIN, addr);
+	}
+
+	exca_writeb(socket, CB_MEM_PAGE(map), start >> 24);
+
+	word = (start >> 12) & 0x0fff;
+	if (mem->flags & MAP_16BIT)
+		word |= I365_MEM_16BIT;
+	if (mem->flags & MAP_0WS)
+		word |= I365_MEM_0WS;
+	exca_writew(socket, I365_MEM(map) + I365_W_START, word);
+
+	word = (stop >> 12) & 0x0fff;
+	switch (to_cycles(mem->speed)) {
+		case 0: break;
+		case 1:  word |= I365_MEM_WS0; break;
+		case 2:  word |= I365_MEM_WS1; break;
+		default: word |= I365_MEM_WS1 | I365_MEM_WS0; break;
+	}
+	exca_writew(socket, I365_MEM(map) + I365_W_STOP, word);
+
+	word = ((card_start - start) >> 12) & 0x3fff;
+	if (mem->flags & MAP_WRPROT)
+		word |= I365_MEM_WRPROT;
+	if (mem->flags & MAP_ATTRIB)
+		word |= I365_MEM_REG;
+	exca_writew(socket, I365_MEM(map) + I365_W_OFF, word);
+
+	if (mem->flags & MAP_ACTIVE)
+		exca_writeb(socket, I365_ADDRWIN, addr | enable);
+	return 0;
+}
+
+
+static unsigned int yenta_events(struct yenta_socket *socket)
+{
+	u8 csc;
+	u32 cb_event;
+	unsigned int events;
+
+	/* Clear interrupt status for the event */
+	cb_event = cb_readl(socket, CB_SOCKET_EVENT);
+	cb_writel(socket, CB_SOCKET_EVENT, cb_event);
+
+	csc = exca_readb(socket, I365_CSC);
+
+	events = (cb_event & (CB_CD1EVENT | CB_CD2EVENT)) ? SS_DETECT : 0 ;
+	events |= (csc & I365_CSC_DETECT) ? SS_DETECT : 0;
+	if (exca_readb(socket, I365_INTCTL) & I365_PC_IOCARD) {
+		events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0;
+	} else {
+		events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
+		events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
+		events |= (csc & I365_CSC_READY) ? SS_READY : 0;
+	}
+	return events;
+}
+
+
+static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int events;
+	struct yenta_socket *socket = (struct yenta_socket *) dev_id;
+
+	events = yenta_events(socket);
+	if (events) {
+		pcmcia_parse_events(&socket->socket, events);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
+static void yenta_interrupt_wrapper(unsigned long data)
+{
+	struct yenta_socket *socket = (struct yenta_socket *) data;
+
+	yenta_interrupt(0, (void *)socket, NULL);
+	socket->poll_timer.expires = jiffies + HZ;
+	add_timer(&socket->poll_timer);
+}
+
+static void yenta_clear_maps(struct yenta_socket *socket)
+{
+	int i;
+	struct resource res = { .start = 0, .end = 0x0fff };
+	pccard_io_map io = { 0, 0, 0, 0, 1 };
+	pccard_mem_map mem = { .res = &res, };
+
+	yenta_set_socket(&socket->socket, &dead_socket);
+	for (i = 0; i < 2; i++) {
+		io.map = i;
+		yenta_set_io_map(&socket->socket, &io);
+	}
+	for (i = 0; i < 5; i++) {
+		mem.map = i;
+		yenta_set_mem_map(&socket->socket, &mem);
+	}
+}
+
+/* Called at resume and initialization events */
+static int yenta_sock_init(struct pcmcia_socket *sock)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+	u32 state;
+	u16 bridge;
+
+	bridge = config_readw(socket, CB_BRIDGE_CONTROL) & ~CB_BRIDGE_INTR;
+	if (!socket->cb_irq)
+		bridge |= CB_BRIDGE_INTR;
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge);
+
+	exca_writeb(socket, I365_GBLCTL, 0x00);
+	exca_writeb(socket, I365_GENCTL, 0x00);
+
+	/* Redo card voltage interrogation */
+	state = cb_readl(socket, CB_SOCKET_STATE);
+	if (!(state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD |
+	               CB_3VCARD | CB_XVCARD | CB_YVCARD)))
+		cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST);
+
+	yenta_clear_maps(socket);
+
+	if (socket->type && socket->type->sock_init)
+		socket->type->sock_init(socket);
+
+	/* Re-enable CSC interrupts */
+	cb_writel(socket, CB_SOCKET_MASK, CB_CDMASK);
+
+	return 0;
+}
+
+static int yenta_sock_suspend(struct pcmcia_socket *sock)
+{
+	struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
+
+	/* Disable CSC interrupts */
+	cb_writel(socket, CB_SOCKET_MASK, 0x0);
+
+	return 0;
+}
+
+/*
+ * Use an adaptive allocation for the memory resource,
+ * sometimes the memory behind pci bridges is limited:
+ * 1/8 of the size of the io window of the parent.
+ * max 4 MB, min 16 kB.
+ */
+#define BRIDGE_MEM_MAX 4*1024*1024
+#define BRIDGE_MEM_MIN 16*1024
+
+#define BRIDGE_IO_MAX 256
+#define BRIDGE_IO_MIN 32
+
+#ifndef PCIBIOS_MIN_CARDBUS_IO
+#define PCIBIOS_MIN_CARDBUS_IO PCIBIOS_MIN_IO
+#endif
+
+static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type)
+{
+	struct pci_bus *bus;
+	struct resource *root, *res;
+	u32 start, end;
+	u32 align, size, min;
+	unsigned offset;
+	unsigned mask;
+
+	/* The granularity of the memory limit is 4kB, on IO it's 4 bytes */
+	mask = ~0xfff;
+	if (type & IORESOURCE_IO)
+		mask = ~3;
+
+	offset = 0x1c + 8*nr;
+	bus = socket->dev->subordinate;
+	res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr;
+	res->name = bus->name;
+	res->flags = type;
+	res->start = 0;
+	res->end = 0;
+	root = pci_find_parent_resource(socket->dev, res);
+
+	if (!root)
+		return;
+
+	start = config_readl(socket, offset) & mask;
+	end = config_readl(socket, offset+4) | ~mask;
+	if (start && end > start && !override_bios) {
+		res->start = start;
+		res->end = end;
+		if (request_resource(root, res) == 0)
+			return;
+		printk(KERN_INFO "yenta %s: Preassigned resource %d busy, reconfiguring...\n",
+				pci_name(socket->dev), nr);
+		res->start = res->end = 0;
+	}
+
+	if (type & IORESOURCE_IO) {
+		align = 1024;
+		size = BRIDGE_IO_MAX;
+		min = BRIDGE_IO_MIN;
+		start = PCIBIOS_MIN_CARDBUS_IO;
+		end = ~0U;
+	} else {
+		unsigned long avail = root->end - root->start;
+		int i;
+		size = BRIDGE_MEM_MAX;
+		if (size > avail/8) {
+			size=(avail+1)/8;
+			/* round size down to next power of 2 */
+			i = 0;
+			while ((size /= 2) != 0)
+				i++;
+			size = 1 << i;
+		}
+		if (size < BRIDGE_MEM_MIN)
+			size = BRIDGE_MEM_MIN;
+		min = BRIDGE_MEM_MIN;
+		align = size;
+		start = PCIBIOS_MIN_MEM;
+		end = ~0U;
+	}
+	
+	do {
+		if (allocate_resource(root, res, size, start, end, align, NULL, NULL)==0) {
+			config_writel(socket, offset, res->start);
+			config_writel(socket, offset+4, res->end);
+			return;
+		}
+		size = size/2;
+		align = size;
+	} while (size >= min);
+	printk(KERN_INFO "yenta %s: no resource of type %x available, trying to continue...\n",
+			pci_name(socket->dev), type);
+	res->start = res->end = 0;
+}
+
+/*
+ * Allocate the bridge mappings for the device..
+ */
+static void yenta_allocate_resources(struct yenta_socket *socket)
+{
+	yenta_allocate_res(socket, 0, IORESOURCE_MEM|IORESOURCE_PREFETCH);
+	yenta_allocate_res(socket, 1, IORESOURCE_MEM);
+	yenta_allocate_res(socket, 2, IORESOURCE_IO);
+	yenta_allocate_res(socket, 3, IORESOURCE_IO);	/* PCI isn't clever enough to use this one yet */
+}
+
+
+/*
+ * Free the bridge mappings for the device..
+ */
+static void yenta_free_resources(struct yenta_socket *socket)
+{
+	int i;
+	for (i=0;i<4;i++) {
+		struct resource *res;
+		res = socket->dev->resource + PCI_BRIDGE_RESOURCES + i;
+		if (res->start != 0 && res->end != 0)
+			release_resource(res);
+		res->start = res->end = 0;
+	}
+}
+
+
+/*
+ * Close it down - release our resources and go home..
+ */
+static void yenta_close(struct pci_dev *dev)
+{
+	struct yenta_socket *sock = pci_get_drvdata(dev);
+
+	/* we don't want a dying socket registered */
+	pcmcia_unregister_socket(&sock->socket);
+	
+	/* Disable all events so we don't die in an IRQ storm */
+	cb_writel(sock, CB_SOCKET_MASK, 0x0);
+	exca_writeb(sock, I365_CSCINT, 0);
+
+	if (sock->cb_irq)
+		free_irq(sock->cb_irq, sock);
+	else
+		del_timer_sync(&sock->poll_timer);
+
+	if (sock->base)
+		iounmap(sock->base);
+	yenta_free_resources(sock);
+
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	pci_set_drvdata(dev, NULL);
+}
+
+
+static struct pccard_operations yenta_socket_operations = {
+	.init			= yenta_sock_init,
+	.suspend		= yenta_sock_suspend,
+	.get_status		= yenta_get_status,
+	.get_socket		= yenta_get_socket,
+	.set_socket		= yenta_set_socket,
+	.set_io_map		= yenta_set_io_map,
+	.set_mem_map		= yenta_set_mem_map,
+};
+
+
+#include "ti113x.h"
+#include "ricoh.h"
+#include "topic.h"
+#include "o2micro.h"
+
+enum {
+	CARDBUS_TYPE_DEFAULT = -1,
+	CARDBUS_TYPE_TI,
+	CARDBUS_TYPE_TI113X,
+	CARDBUS_TYPE_TI12XX,
+	CARDBUS_TYPE_TI1250,
+	CARDBUS_TYPE_RICOH,
+	CARDBUS_TYPE_TOPIC97,
+	CARDBUS_TYPE_O2MICRO,
+};
+
+/*
+ * Different cardbus controllers have slightly different
+ * initialization sequences etc details. List them here..
+ */
+static struct cardbus_type cardbus_type[] = {
+	[CARDBUS_TYPE_TI]	= {
+		.override	= ti_override,
+		.save_state	= ti_save_state,
+		.restore_state	= ti_restore_state,
+		.sock_init	= ti_init,
+	},
+	[CARDBUS_TYPE_TI113X]	= {
+		.override	= ti113x_override,
+		.save_state	= ti_save_state,
+		.restore_state	= ti_restore_state,
+		.sock_init	= ti_init,
+	},
+	[CARDBUS_TYPE_TI12XX]	= {
+		.override	= ti12xx_override,
+		.save_state	= ti_save_state,
+		.restore_state	= ti_restore_state,
+		.sock_init	= ti_init,
+	},
+	[CARDBUS_TYPE_TI1250]	= {
+		.override	= ti1250_override,
+		.save_state	= ti_save_state,
+		.restore_state	= ti_restore_state,
+		.sock_init	= ti_init,
+	},
+	[CARDBUS_TYPE_RICOH]	= {
+		.override	= ricoh_override,
+		.save_state	= ricoh_save_state,
+		.restore_state	= ricoh_restore_state,
+	},
+	[CARDBUS_TYPE_TOPIC97]	= {
+		.override	= topic97_override,
+	},
+	[CARDBUS_TYPE_O2MICRO]	= {
+		.override	= o2micro_override,
+		.restore_state	= o2micro_restore_state,
+	},
+};
+
+
+/*
+ * Only probe "regular" interrupts, don't
+ * touch dangerous spots like the mouse irq,
+ * because there are mice that apparently
+ * get really confused if they get fondled
+ * too intimately.
+ *
+ * Default to 11, 10, 9, 7, 6, 5, 4, 3.
+ */
+static u32 isa_interrupts = 0x0ef8;
+
+static unsigned int yenta_probe_irq(struct yenta_socket *socket, u32 isa_irq_mask)
+{
+	int i;
+	unsigned long val;
+	u16 bridge_ctrl;
+	u32 mask;
+
+	/* Set up ISA irq routing to probe the ISA irqs.. */
+	bridge_ctrl = config_readw(socket, CB_BRIDGE_CONTROL);
+	if (!(bridge_ctrl & CB_BRIDGE_INTR)) {
+		bridge_ctrl |= CB_BRIDGE_INTR;
+		config_writew(socket, CB_BRIDGE_CONTROL, bridge_ctrl);
+	}
+
+	/*
+	 * Probe for usable interrupts using the force
+	 * register to generate bogus card status events.
+	 */
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+	exca_writeb(socket, I365_CSCINT, 0);
+	val = probe_irq_on() & isa_irq_mask;
+	for (i = 1; i < 16; i++) {
+		if (!((val >> i) & 1))
+			continue;
+		exca_writeb(socket, I365_CSCINT, I365_CSC_STSCHG | (i << 4));
+		cb_writel(socket, CB_SOCKET_FORCE, CB_FCARDSTS);
+		udelay(100);
+		cb_writel(socket, CB_SOCKET_EVENT, -1);
+	}
+	cb_writel(socket, CB_SOCKET_MASK, 0);
+	exca_writeb(socket, I365_CSCINT, 0);
+
+	mask = probe_irq_mask(val) & 0xffff;
+
+	bridge_ctrl &= ~CB_BRIDGE_INTR;
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge_ctrl);
+
+	return mask;
+}
+
+
+/* interrupt handler, only used during probing */
+static irqreturn_t yenta_probe_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct yenta_socket *socket = (struct yenta_socket *) dev_id;
+	u8 csc;
+        u32 cb_event;
+
+	/* Clear interrupt status for the event */
+	cb_event = cb_readl(socket, CB_SOCKET_EVENT);
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	csc = exca_readb(socket, I365_CSC);
+
+	if (cb_event || csc) {
+		socket->probe_status = 1;
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+/* probes the PCI interrupt, use only on override functions */
+static int yenta_probe_cb_irq(struct yenta_socket *socket)
+{
+	u16 bridge_ctrl;
+
+	if (!socket->cb_irq)
+		return -1;
+
+	socket->probe_status = 0;
+
+	/* disable ISA interrupts */
+	bridge_ctrl = config_readw(socket, CB_BRIDGE_CONTROL);
+	bridge_ctrl &= ~CB_BRIDGE_INTR;
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge_ctrl);
+
+	if (request_irq(socket->cb_irq, yenta_probe_handler, SA_SHIRQ, "yenta", socket)) {
+		printk(KERN_WARNING "Yenta: request_irq() in yenta_probe_cb_irq() failed!\n");
+		return -1;
+	}
+
+	/* generate interrupt, wait */
+	exca_writeb(socket, I365_CSCINT, I365_CSC_STSCHG);
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+	cb_writel(socket, CB_SOCKET_FORCE, CB_FCARDSTS);
+	
+	msleep(100);
+
+	/* disable interrupts */
+	cb_writel(socket, CB_SOCKET_MASK, 0);
+	exca_writeb(socket, I365_CSCINT, 0);
+	cb_writel(socket, CB_SOCKET_EVENT, -1);
+	exca_readb(socket, I365_CSC);
+
+	free_irq(socket->cb_irq, socket);
+
+	return (int) socket->probe_status;
+}
+
+
+
+/*
+ * Set static data that doesn't need re-initializing..
+ */
+static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_irq_mask)
+{
+	socket->socket.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS;
+	socket->socket.map_size = 0x1000;
+	socket->socket.pci_irq = socket->cb_irq;
+	socket->socket.irq_mask = yenta_probe_irq(socket, isa_irq_mask);
+	socket->socket.cb_dev = socket->dev;
+
+	printk(KERN_INFO "Yenta: ISA IRQ mask 0x%04x, PCI irq %d\n",
+	       socket->socket.irq_mask, socket->cb_irq);
+}
+
+/*
+ * Initialize the standard cardbus registers
+ */
+static void yenta_config_init(struct yenta_socket *socket)
+{
+	u16 bridge;
+	struct pci_dev *dev = socket->dev;
+
+	pci_set_power_state(socket->dev, 0);
+
+	config_writel(socket, CB_LEGACY_MODE_BASE, 0);
+	config_writel(socket, PCI_BASE_ADDRESS_0, dev->resource[0].start);
+	config_writew(socket, PCI_COMMAND,
+			PCI_COMMAND_IO |
+			PCI_COMMAND_MEMORY |
+			PCI_COMMAND_MASTER |
+			PCI_COMMAND_WAIT);
+
+	/* MAGIC NUMBERS! Fixme */
+	config_writeb(socket, PCI_CACHE_LINE_SIZE, L1_CACHE_BYTES / 4);
+	config_writeb(socket, PCI_LATENCY_TIMER, 168);
+	config_writel(socket, PCI_PRIMARY_BUS,
+		(176 << 24) |			   /* sec. latency timer */
+		(dev->subordinate->subordinate << 16) | /* subordinate bus */
+		(dev->subordinate->secondary << 8) |  /* secondary bus */
+		dev->subordinate->primary);		   /* primary bus */
+
+	/*
+	 * Set up the bridging state:
+	 *  - enable write posting.
+	 *  - memory window 0 prefetchable, window 1 non-prefetchable
+	 *  - PCI interrupts enabled if a PCI interrupt exists..
+	 */
+	bridge = config_readw(socket, CB_BRIDGE_CONTROL);
+	bridge &= ~(CB_BRIDGE_CRST | CB_BRIDGE_PREFETCH1 | CB_BRIDGE_INTR | CB_BRIDGE_ISAEN | CB_BRIDGE_VGAEN);
+	bridge |= CB_BRIDGE_PREFETCH0 | CB_BRIDGE_POSTEN | CB_BRIDGE_INTR;
+	config_writew(socket, CB_BRIDGE_CONTROL, bridge);
+}
+
+/*
+ * Initialize a cardbus controller. Make sure we have a usable
+ * interrupt, and that we can map the cardbus area. Fill in the
+ * socket information structure..
+ */
+static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct yenta_socket *socket;
+	int ret;
+	
+	socket = kmalloc(sizeof(struct yenta_socket), GFP_KERNEL);
+	if (!socket)
+		return -ENOMEM;
+	memset(socket, 0, sizeof(*socket));
+
+	/* prepare pcmcia_socket */
+	socket->socket.ops = &yenta_socket_operations;
+	socket->socket.resource_ops = &pccard_nonstatic_ops;
+	socket->socket.dev.dev = &dev->dev;
+	socket->socket.driver_data = socket;
+	socket->socket.owner = THIS_MODULE;
+
+	/* prepare struct yenta_socket */
+	socket->dev = dev;
+	pci_set_drvdata(dev, socket);
+
+	/*
+	 * Do some basic sanity checking..
+	 */
+	if (pci_enable_device(dev)) {
+		ret = -EBUSY;
+		goto free;
+	}
+
+	ret = pci_request_regions(dev, "yenta_socket");
+	if (ret)
+		goto disable;
+
+	if (!pci_resource_start(dev, 0)) {
+		printk(KERN_ERR "No cardbus resource!\n");
+		ret = -ENODEV;
+		goto release;
+	}
+
+	/*
+	 * Ok, start setup.. Map the cardbus registers,
+	 * and request the IRQ.
+	 */
+	socket->base = ioremap(pci_resource_start(dev, 0), 0x1000);
+	if (!socket->base) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	/*
+	 * report the subsystem vendor and device for help debugging
+	 * the irq stuff...
+	 */
+	printk(KERN_INFO "Yenta: CardBus bridge found at %s [%04x:%04x]\n",
+		pci_name(dev), dev->subsystem_vendor, dev->subsystem_device);
+
+	yenta_config_init(socket);
+
+	/* Disable all events */
+	cb_writel(socket, CB_SOCKET_MASK, 0x0);
+
+	/* Set up the bridge regions.. */
+	yenta_allocate_resources(socket);
+
+	socket->cb_irq = dev->irq;
+
+	/* Do we have special options for the device? */
+	if (id->driver_data != CARDBUS_TYPE_DEFAULT &&
+	    id->driver_data < ARRAY_SIZE(cardbus_type)) {
+		socket->type = &cardbus_type[id->driver_data];
+
+		ret = socket->type->override(socket);
+		if (ret < 0)
+			goto unmap;
+	}
+
+	/* We must finish initialization here */
+
+	if (!socket->cb_irq || request_irq(socket->cb_irq, yenta_interrupt, SA_SHIRQ, "yenta", socket)) {
+		/* No IRQ or request_irq failed. Poll */
+		socket->cb_irq = 0; /* But zero is a valid IRQ number. */
+		init_timer(&socket->poll_timer);
+		socket->poll_timer.function = yenta_interrupt_wrapper;
+		socket->poll_timer.data = (unsigned long)socket;
+		socket->poll_timer.expires = jiffies + HZ;
+		add_timer(&socket->poll_timer);
+	}
+
+	/* Figure out what the dang thing can do for the PCMCIA layer... */
+	yenta_get_socket_capabilities(socket, isa_interrupts);
+	printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
+
+	/* Register it with the pcmcia layer.. */
+	ret = pcmcia_register_socket(&socket->socket);
+	if (ret == 0)
+		goto out;
+
+ unmap:
+	iounmap(socket->base);
+ release:
+	pci_release_regions(dev);
+ disable:
+	pci_disable_device(dev);
+ free:
+	kfree(socket);
+ out:
+	return ret;
+}
+
+
+static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state)
+{
+	struct yenta_socket *socket = pci_get_drvdata(dev);
+	int ret;
+
+	ret = pcmcia_socket_dev_suspend(&dev->dev, state);
+
+	if (socket) {
+		if (socket->type && socket->type->save_state)
+			socket->type->save_state(socket);
+
+		/* FIXME: pci_save_state needs to have a better interface */
+		pci_save_state(dev);
+		pci_read_config_dword(dev, 16*4, &socket->saved_state[0]);
+		pci_read_config_dword(dev, 17*4, &socket->saved_state[1]);
+
+		/*
+		 * Some laptops (IBM T22) do not like us putting the Cardbus
+		 * bridge into D3.  At a guess, some other laptop will
+		 * probably require this, so leave it commented out for now.
+		 */
+		/* pci_set_power_state(dev, 3); */
+	}
+
+	return ret;
+}
+
+
+static int yenta_dev_resume (struct pci_dev *dev)
+{
+	struct yenta_socket *socket = pci_get_drvdata(dev);
+
+	if (socket) {
+		pci_set_power_state(dev, 0);
+		/* FIXME: pci_restore_state needs to have a better interface */
+		pci_restore_state(dev);
+		pci_write_config_dword(dev, 16*4, socket->saved_state[0]);
+		pci_write_config_dword(dev, 17*4, socket->saved_state[1]);
+
+		if (socket->type && socket->type->restore_state)
+			socket->type->restore_state(socket);
+	}
+
+	return pcmcia_socket_dev_resume(&dev->dev);
+}
+
+
+#define CB_ID(vend,dev,type)				\
+	{						\
+		.vendor		= vend,			\
+		.device		= dev,			\
+		.subvendor	= PCI_ANY_ID,		\
+		.subdevice	= PCI_ANY_ID,		\
+		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8, \
+		.class_mask	= ~0,			\
+		.driver_data	= CARDBUS_TYPE_##type,	\
+	}
+
+static struct pci_device_id yenta_table [] = {
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI),
+
+	/*
+	 * TBD: Check if these TI variants can use more
+	 * advanced overrides instead.  (I can't get the
+	 * data sheets for these devices. --rmk)
+	 */
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1210, TI),
+
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1130, TI113X),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1131, TI113X),
+
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1211, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1220, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1221, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1225, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251A, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1251B, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1450, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1451A, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1510, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1520, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1620, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4410, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4450, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4451, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4510, TI12XX),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_4520, TI12XX),
+
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1250, TI1250),
+	CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1410, TI1250),
+
+	CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1211, TI12XX),
+	CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1225, TI12XX),
+	CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1410, TI1250),
+	CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1420, TI12XX),
+
+	CB_ID(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C465, RICOH),
+	CB_ID(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C466, RICOH),
+	CB_ID(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C475, RICOH),
+	CB_ID(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476, RICOH),
+	CB_ID(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C478, RICOH),
+
+	CB_ID(PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_TOPIC97, TOPIC97),
+	CB_ID(PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_TOPIC100, TOPIC97),
+
+	CB_ID(PCI_VENDOR_ID_O2, PCI_ANY_ID, O2MICRO),
+
+	/* match any cardbus bridge */
+	CB_ID(PCI_ANY_ID, PCI_ANY_ID, DEFAULT),
+	{ /* all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, yenta_table);
+
+
+static struct pci_driver yenta_cardbus_driver = {
+	.name		= "yenta_cardbus",
+	.id_table	= yenta_table,
+	.probe		= yenta_probe,
+	.remove		= __devexit_p(yenta_close),
+	.suspend	= yenta_dev_suspend,
+	.resume		= yenta_dev_resume,
+};
+
+
+static int __init yenta_socket_init(void)
+{
+	return pci_register_driver (&yenta_cardbus_driver);
+}
+
+
+static void __exit yenta_socket_exit (void)
+{
+	pci_unregister_driver (&yenta_cardbus_driver);
+}
+
+
+module_init(yenta_socket_init);
+module_exit(yenta_socket_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/yenta_socket.h b/drivers/pcmcia/yenta_socket.h
new file mode 100644
index 0000000..4e637ee
--- /dev/null
+++ b/drivers/pcmcia/yenta_socket.h
@@ -0,0 +1,127 @@
+#ifndef __YENTA_H
+#define __YENTA_H
+
+#include <asm/io.h>
+
+#define CB_SOCKET_EVENT		0x00
+#define    CB_CSTSEVENT		0x00000001	/* Card status event */
+#define    CB_CD1EVENT		0x00000002	/* Card detect 1 change event */
+#define    CB_CD2EVENT		0x00000004	/* Card detect 2 change event */
+#define    CB_PWREVENT		0x00000008	/* PWRCYCLE change event */
+
+#define CB_SOCKET_MASK		0x04
+#define    CB_CSTSMASK		0x00000001	/* Card status mask */
+#define    CB_CDMASK		0x00000006	/* Card detect 1&2 mask */
+#define    CB_PWRMASK		0x00000008	/* PWRCYCLE change mask */
+
+#define CB_SOCKET_STATE		0x08
+#define    CB_CARDSTS		0x00000001	/* CSTSCHG status */
+#define    CB_CDETECT1		0x00000002	/* Card detect status 1 */
+#define    CB_CDETECT2		0x00000004	/* Card detect status 2 */
+#define    CB_PWRCYCLE		0x00000008	/* Socket powered */
+#define    CB_16BITCARD		0x00000010	/* 16-bit card detected */
+#define    CB_CBCARD		0x00000020	/* CardBus card detected */
+#define    CB_IREQCINT		0x00000040	/* READY(xIRQ)/xCINT high */
+#define    CB_NOTACARD		0x00000080	/* Unrecognizable PC card detected */
+#define    CB_DATALOST		0x00000100	/* Potential data loss due to card removal */
+#define    CB_BADVCCREQ		0x00000200	/* Invalid Vcc request by host software */
+#define    CB_5VCARD		0x00000400	/* Card Vcc at 5.0 volts? */
+#define    CB_3VCARD		0x00000800	/* Card Vcc at 3.3 volts? */
+#define    CB_XVCARD		0x00001000	/* Card Vcc at X.X volts? */
+#define    CB_YVCARD		0x00002000	/* Card Vcc at Y.Y volts? */
+#define    CB_5VSOCKET		0x10000000	/* Socket Vcc at 5.0 volts? */
+#define    CB_3VSOCKET		0x20000000	/* Socket Vcc at 3.3 volts? */
+#define    CB_XVSOCKET		0x40000000	/* Socket Vcc at X.X volts? */
+#define    CB_YVSOCKET		0x80000000	/* Socket Vcc at Y.Y volts? */
+
+#define CB_SOCKET_FORCE		0x0C
+#define    CB_FCARDSTS		0x00000001	/* Force CSTSCHG */
+#define    CB_FCDETECT1		0x00000002	/* Force CD1EVENT */
+#define    CB_FCDETECT2		0x00000004	/* Force CD2EVENT */
+#define    CB_FPWRCYCLE		0x00000008	/* Force PWREVENT */
+#define    CB_F16BITCARD	0x00000010	/* Force 16-bit PCMCIA card */
+#define    CB_FCBCARD		0x00000020	/* Force CardBus line */
+#define    CB_FNOTACARD		0x00000080	/* Force NOTACARD */
+#define    CB_FDATALOST		0x00000100	/* Force data lost */
+#define    CB_FBADVCCREQ	0x00000200	/* Force bad Vcc request */
+#define    CB_F5VCARD		0x00000400	/* Force 5.0 volt card */
+#define    CB_F3VCARD		0x00000800	/* Force 3.3 volt card */
+#define    CB_FXVCARD		0x00001000	/* Force X.X volt card */
+#define    CB_FYVCARD		0x00002000	/* Force Y.Y volt card */
+#define    CB_CVSTEST		0x00004000	/* Card VS test */
+
+#define CB_SOCKET_CONTROL	0x10
+#define  CB_SC_VPP_MASK		0x00000007
+#define   CB_SC_VPP_OFF		0x00000000
+#define   CB_SC_VPP_12V		0x00000001
+#define   CB_SC_VPP_5V		0x00000002
+#define   CB_SC_VPP_3V		0x00000003
+#define   CB_SC_VPP_XV		0x00000004
+#define   CB_SC_VPP_YV		0x00000005
+#define  CB_SC_VCC_MASK		0x00000070
+#define   CB_SC_VCC_OFF		0x00000000
+#define   CB_SC_VCC_5V		0x00000020
+#define   CB_SC_VCC_3V		0x00000030
+#define   CB_SC_VCC_XV		0x00000040
+#define   CB_SC_VCC_YV		0x00000050
+#define  CB_SC_CCLK_STOP	0x00000080
+
+#define CB_SOCKET_POWER		0x20
+#define    CB_SKTACCES		0x02000000	/* A PC card access has occurred (clear on read) */
+#define    CB_SKTMODE		0x01000000	/* Clock frequency has changed (clear on read) */
+#define    CB_CLKCTRLEN		0x00010000	/* Clock control enabled (RW) */
+#define    CB_CLKCTRL		0x00000001	/* Stop(0) or slow(1) CB clock (RW) */
+
+/*
+ * Cardbus configuration space
+ */
+#define CB_BRIDGE_BASE(m)	(0x1c + 8*(m))
+#define CB_BRIDGE_LIMIT(m)	(0x20 + 8*(m))
+#define CB_BRIDGE_CONTROL	0x3e
+#define   CB_BRIDGE_CPERREN	0x00000001
+#define   CB_BRIDGE_CSERREN	0x00000002
+#define   CB_BRIDGE_ISAEN	0x00000004
+#define   CB_BRIDGE_VGAEN	0x00000008
+#define   CB_BRIDGE_MABTMODE	0x00000020
+#define   CB_BRIDGE_CRST	0x00000040
+#define   CB_BRIDGE_INTR	0x00000080
+#define   CB_BRIDGE_PREFETCH0	0x00000100
+#define   CB_BRIDGE_PREFETCH1	0x00000200
+#define   CB_BRIDGE_POSTEN	0x00000400
+#define CB_LEGACY_MODE_BASE	0x44
+
+/*
+ * ExCA area extensions in Yenta
+ */
+#define CB_MEM_PAGE(map)	(0x40 + (map))
+
+struct yenta_socket;
+
+struct cardbus_type {
+	int	(*override)(struct yenta_socket *);
+	void	(*save_state)(struct yenta_socket *);
+	void	(*restore_state)(struct yenta_socket *);
+	int	(*sock_init)(struct yenta_socket *);
+};
+
+struct yenta_socket {
+	struct pci_dev *dev;
+	int cb_irq, io_irq;
+	void __iomem *base;
+	struct timer_list poll_timer;
+
+	struct pcmcia_socket socket;
+	struct cardbus_type *type;
+
+	/* for PCI interrupt probing */
+	unsigned int probe_status;
+
+	/* A few words of private data for special stuff of overrides... */
+	unsigned int private[8];
+
+	/* PCI saved state */
+	u32 saved_state[2];
+};
+
+
+#endif