summaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pcmcia
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.bz2
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r--drivers/pcmcia/Kconfig203
-rw-r--r--drivers/pcmcia/Makefile65
-rw-r--r--drivers/pcmcia/au1000_db1x00.c288
-rw-r--r--drivers/pcmcia/au1000_generic.c579
-rw-r--r--drivers/pcmcia/au1000_generic.h150
-rw-r--r--drivers/pcmcia/au1000_pb1x00.c419
-rw-r--r--drivers/pcmcia/au1000_xxs1500.c191
-rw-r--r--drivers/pcmcia/cardbus.c247
-rw-r--r--drivers/pcmcia/cirrus.h157
-rw-r--r--drivers/pcmcia/cistpl.c1490
-rw-r--r--drivers/pcmcia/cs.c1917
-rw-r--r--drivers/pcmcia/cs_internal.h185
-rw-r--r--drivers/pcmcia/ds.c1659
-rw-r--r--drivers/pcmcia/hd64465_ss.c968
-rw-r--r--drivers/pcmcia/i82092.c799
-rw-r--r--drivers/pcmcia/i82092aa.h39
-rw-r--r--drivers/pcmcia/i82365.c1454
-rw-r--r--drivers/pcmcia/i82365.h135
-rw-r--r--drivers/pcmcia/m32r_cfc.c873
-rw-r--r--drivers/pcmcia/m32r_cfc.h83
-rw-r--r--drivers/pcmcia/m32r_pcc.c811
-rw-r--r--drivers/pcmcia/m32r_pcc.h65
-rw-r--r--drivers/pcmcia/o2micro.h164
-rw-r--r--drivers/pcmcia/pcmcia_compat.c125
-rw-r--r--drivers/pcmcia/pd6729.c871
-rw-r--r--drivers/pcmcia/pd6729.h30
-rw-r--r--drivers/pcmcia/pxa2xx_base.c254
-rw-r--r--drivers/pcmcia/pxa2xx_base.h3
-rw-r--r--drivers/pcmcia/pxa2xx_lubbock.c269
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c202
-rw-r--r--drivers/pcmcia/pxa2xx_sharpsl.c264
-rw-r--r--drivers/pcmcia/ricoh.h206
-rw-r--r--drivers/pcmcia/rsrc_mgr.c163
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c985
-rw-r--r--drivers/pcmcia/sa1100_assabet.c141
-rw-r--r--drivers/pcmcia/sa1100_badge4.c169
-rw-r--r--drivers/pcmcia/sa1100_cerf.c110
-rw-r--r--drivers/pcmcia/sa1100_generic.c131
-rw-r--r--drivers/pcmcia/sa1100_generic.h24
-rw-r--r--drivers/pcmcia/sa1100_h3600.c142
-rw-r--r--drivers/pcmcia/sa1100_jornada720.c124
-rw-r--r--drivers/pcmcia/sa1100_neponset.c143
-rw-r--r--drivers/pcmcia/sa1100_shannon.c125
-rw-r--r--drivers/pcmcia/sa1100_simpad.c135
-rw-r--r--drivers/pcmcia/sa1111_generic.c196
-rw-r--r--drivers/pcmcia/sa1111_generic.h15
-rw-r--r--drivers/pcmcia/sa11xx_base.c200
-rw-r--r--drivers/pcmcia/sa11xx_base.h123
-rw-r--r--drivers/pcmcia/soc_common.c850
-rw-r--r--drivers/pcmcia/soc_common.h194
-rw-r--r--drivers/pcmcia/socket_sysfs.c228
-rw-r--r--drivers/pcmcia/tcic.c903
-rw-r--r--drivers/pcmcia/tcic.h266
-rw-r--r--drivers/pcmcia/ti113x.h662
-rw-r--r--drivers/pcmcia/topic.h140
-rw-r--r--drivers/pcmcia/vg468.h106
-rw-r--r--drivers/pcmcia/vrc4171_card.c846
-rw-r--r--drivers/pcmcia/vrc4173_cardu.c617
-rw-r--r--drivers/pcmcia/vrc4173_cardu.h247
-rw-r--r--drivers/pcmcia/yenta_socket.c1160
-rw-r--r--drivers/pcmcia/yenta_socket.h127
61 files changed, 24437 insertions, 0 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
new file mode 100644
index 000000000000..6375ebc85020
--- /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 000000000000..50c29361bc5f
--- /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 000000000000..42cf8bfbcc98
--- /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 000000000000..9fa7f15d89e5
--- /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 000000000000..417bc1500bad
--- /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 000000000000..df19ce1ea4f3
--- /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 000000000000..1dfc77653660
--- /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 000000000000..3ccb5247ec50
--- /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 000000000000..ecd4fc7f666f
--- /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 000000000000..e29a6ddf2fd7
--- /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 000000000000..03fc885db1c5
--- /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 000000000000..7933a7db49d3
--- /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 000000000000..66150d08b5c7
--- /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 000000000000..5e6c4ba7d995
--- /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 000000000000..f3fdc748659d
--- /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 000000000000..b98cac7bda9f
--- /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 000000000000..90a335a5d9fa
--- /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 000000000000..622860c689d9
--- /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 000000000000..b0b7a7a41120
--- /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 000000000000..17c1db7ae155
--- /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 000000000000..cafba6f45dfa
--- /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 000000000000..e4fffe417ba9
--- /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 000000000000..b1f6e3d9ee06
--- /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 000000000000..68b80084f83f
--- /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 000000000000..3f4364341d8d
--- /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 000000000000..f392e458cdfd
--- /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 000000000000..a6936a75a87e
--- /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 000000000000..e46cff345d47
--- /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 000000000000..fd1f691c7c2c
--- /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 000000000000..5309734e1687
--- /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 000000000000..42efe218867a
--- /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 000000000000..01098c841f87
--- /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 000000000000..b6843f8d300d
--- /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 000000000000..5876bab7c14c
--- /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 000000000000..7c57fdd3c8d7
--- /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 000000000000..c6b262b653d3
--- /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 000000000000..2b3c2895b43d
--- /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 000000000000..874990ebe147
--- /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 000000000000..794f96a35bba
--- /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 000000000000..64fd5e37f2d2
--- /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 000000000000..0a387106acb0
--- /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 000000000000..5e34b3e8e5db
--- /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 000000000000..7bc9e59c761f
--- /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 000000000000..c2ecf1185e9e
--- /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 000000000000..bd9d6b2fad68
--- /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 000000000000..10ced4a210d7
--- /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 000000000000..db04ffb6f68c
--- /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 000000000000..7bc208280527
--- /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 000000000000..888b70e6a484
--- /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 000000000000..700a155fbc78
--- /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 000000000000..8eed03938214
--- /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 000000000000..aacbbb5f055d
--- /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 000000000000..2c0b8f65ad6c
--- /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 000000000000..52c073a9d7e4
--- /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 000000000000..be420bb29113
--- /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 000000000000..88c2b487f675
--- /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 000000000000..987bc21ced42
--- /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 000000000000..db91259dc50e
--- /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 000000000000..113726f7cf92
--- /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 000000000000..6404d97a12eb
--- /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 000000000000..4e637eef2076
--- /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