Linux-2.6.12-rc2

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

Let it rip!
diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig
new file mode 100644
index 0000000..df52190
--- /dev/null
+++ b/drivers/scsi/pcmcia/Kconfig
@@ -0,0 +1,82 @@
+#
+# PCMCIA SCSI adapter configuration
+#
+
+menu "PCMCIA SCSI adapter support"
+	depends on SCSI!=n && PCMCIA!=n && MODULES
+
+config PCMCIA_AHA152X
+	tristate "Adaptec AHA152X PCMCIA support"
+	depends on m && !64BIT
+	help
+	  Say Y here if you intend to attach this type of PCMCIA SCSI host
+	  adapter to your computer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aha152x_cs.
+
+config PCMCIA_FDOMAIN
+	tristate "Future Domain PCMCIA support"
+	depends on m
+	help
+	  Say Y here if you intend to attach this type of PCMCIA SCSI host
+	  adapter to your computer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called fdomain_cs.
+
+config PCMCIA_NINJA_SCSI
+	tristate "NinjaSCSI-3 / NinjaSCSI-32Bi (16bit) PCMCIA support"
+	depends on m && !64BIT
+	help
+	  If you intend to attach this type of PCMCIA SCSI host adapter to
+	  your computer, say Y here and read
+	  <file:Documentation/scsi/NinjaSCSI.txt>.
+
+	  Supported cards:
+
+	  NinjaSCSI-3: (version string: "WBT","NinjaSCSI-3","R1.0")
+	    IO-DATA     PCSC-FP
+	    ALPHA DATA  AD-PCS201
+	    CyQ've      SFC-201  
+	    LOGITECH    LPM-SCSI2E
+	    Pioneer PCR-PR24's card
+	    I-O DATA CDPS-PX24's card (PCSC-F)
+	    Panasonic KXL-RW10AN CD-RW's card
+	    etc.
+
+	  NinjaSCSI-32Bit (in 16bit mode):
+	    [Workbit (version string: "WORKBIT","UltraNinja-16","1")]
+	    Jazz SCP050
+	    [I-O DATA (OEM) (version string: "IO DATA","CBSC16       ","1")]
+	    I-O DATA CBSC-II
+	    [Kyusyu Matsushita Kotobuki (OEM)
+               (version string: "KME    ","SCSI-CARD-001","1")]
+	    KME KXL-820AN's card
+	    HP M820e CDRW's card
+	    etc.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called nsp_cs.
+
+config PCMCIA_QLOGIC
+	tristate "Qlogic PCMCIA support"
+	depends on m
+	help
+	  Say Y here if you intend to attach this type of PCMCIA SCSI host
+	  adapter to your computer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qlogic_cs.
+
+config PCMCIA_SYM53C500
+	tristate "Symbios 53c500 PCMCIA support"
+	depends on m
+	help
+	  Say Y here if you have a New Media Bus Toaster or other PCMCIA
+	  SCSI adapter based on the Symbios 53c500 controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sym53c500_cs.
+
+endmenu
diff --git a/drivers/scsi/pcmcia/Makefile b/drivers/scsi/pcmcia/Makefile
new file mode 100644
index 0000000..eca3790
--- /dev/null
+++ b/drivers/scsi/pcmcia/Makefile
@@ -0,0 +1,13 @@
+
+EXTRA_CFLAGS		+= -Idrivers/scsi
+
+# 16-bit client drivers
+obj-$(CONFIG_PCMCIA_QLOGIC)	+= qlogic_cs.o
+obj-$(CONFIG_PCMCIA_FDOMAIN)	+= fdomain_cs.o
+obj-$(CONFIG_PCMCIA_AHA152X)	+= aha152x_cs.o
+obj-$(CONFIG_PCMCIA_NINJA_SCSI)	+= nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500)	+= sym53c500_cs.o
+
+aha152x_cs-objs	:= aha152x_stub.o aha152x_core.o
+fdomain_cs-objs	:= fdomain_stub.o fdomain_core.o
+qlogic_cs-objs	:= qlogic_stub.o
diff --git a/drivers/scsi/pcmcia/aha152x_core.c b/drivers/scsi/pcmcia/aha152x_core.c
new file mode 100644
index 0000000..dba3716
--- /dev/null
+++ b/drivers/scsi/pcmcia/aha152x_core.c
@@ -0,0 +1,3 @@
+#define PCMCIA	1
+#define AHA152X_STAT 1
+#include "aha152x.c"
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
new file mode 100644
index 0000000..e60b4c0
--- /dev/null
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -0,0 +1,343 @@
+/*======================================================================
+
+    A driver for Adaptec AHA152X-compatible PCMCIA SCSI cards.
+
+    This driver supports the Adaptec AHA-1460, the New Media Bus
+    Toaster, and the New Media Toast & Jam.
+    
+    aha152x_cs.c 1.54 2000/06/12 21:27:25
+
+    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/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "aha152x.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"aha152x_cs.c 1.54 2000/06/12 21:27:25 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* SCSI bus setup options */
+static int host_id = 7;
+static int reconnect = 1;
+static int parity = 1;
+static int synchronous = 1;
+static int reset_delay = 100;
+static int ext_trans = 0;
+
+module_param(host_id, int, 0);
+module_param(reconnect, int, 0);
+module_param(parity, int, 0);
+module_param(synchronous, int, 0);
+module_param(reset_delay, int, 0);
+module_param(ext_trans, int, 0);
+
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    struct Scsi_Host	*host;
+} scsi_info_t;
+
+static void aha152x_release_cs(dev_link_t *link);
+static int aha152x_event(event_t event, int priority,
+			 event_callback_args_t *args);
+
+static dev_link_t *aha152x_attach(void);
+static void aha152x_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+static dev_info_t dev_info = "aha152x_cs";
+
+static dev_link_t *aha152x_attach(void)
+{
+    scsi_info_t *info;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    int ret;
+    
+    DEBUG(0, "aha152x_attach()\n");
+
+    /* Create new SCSI device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+
+    link->io.NumPorts1 = 0x20;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 10;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.event_handler = &aha152x_event;
+    client_reg.EventMask =
+	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	aha152x_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* aha152x_attach */
+
+/*====================================================================*/
+
+static void aha152x_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "aha152x_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->state & DEV_CONFIG)
+	aha152x_release_cs(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* aha152x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void aha152x_config_cs(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    scsi_info_t *info = link->priv;
+    struct aha152x_setup s;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn;
+    u_char tuple_data[64];
+    struct Scsi_Host *host;
+    
+    DEBUG(0, "aha152x_config(0x%p)\n", link);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = tuple_data;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (1) {
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+	    goto next_entry;
+	/* For New Media T&J, look for a SCSI window */
+	if (parse.cftable_entry.io.win[0].len >= 0x20)
+	    link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+	else if ((parse.cftable_entry.io.nwin > 1) &&
+		 (parse.cftable_entry.io.win[1].len >= 0x20))
+	    link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
+	if ((parse.cftable_entry.io.nwin > 0) &&
+	    (link->io.BasePort1 < 0xffff)) {
+	    link->conf.ConfigIndex = parse.cftable_entry.index;
+	    i = pcmcia_request_io(handle, &link->io);
+	    if (i == CS_SUCCESS) break;
+	}
+    next_entry:
+	CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+    }
+    
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    
+    /* Set configuration options for the aha152x driver */
+    memset(&s, 0, sizeof(s));
+    s.conf        = "PCMCIA setup";
+    s.io_port     = link->io.BasePort1;
+    s.irq         = link->irq.AssignedIRQ;
+    s.scsiid      = host_id;
+    s.reconnect   = reconnect;
+    s.parity      = parity;
+    s.synchronous = synchronous;
+    s.delay       = reset_delay;
+    if (ext_trans)
+        s.ext_trans = ext_trans;
+
+    host = aha152x_probe_one(&s);
+    if (host == NULL) {
+	printk(KERN_INFO "aha152x_cs: no SCSI devices found\n");
+	goto cs_failed;
+    }
+
+    sprintf(info->node.dev_name, "scsi%d", host->host_no);
+    link->dev = &info->node;
+    info->host = host;
+
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+    
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    aha152x_release_cs(link);
+    return;
+}
+
+static void aha152x_release_cs(dev_link_t *link)
+{
+	scsi_info_t *info = link->priv;
+
+	aha152x_release(info->host);
+	link->dev = NULL;
+    
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+    
+	link->state &= ~DEV_CONFIG;
+}
+
+static int aha152x_event(event_t event, int priority,
+			 event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    scsi_info_t *info = link->priv;
+    
+    DEBUG(0, "aha152x_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    aha152x_release_cs(link);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	aha152x_config_cs(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_release_configuration(link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    Scsi_Cmnd tmp;
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    tmp.device->host = info->host;
+	    aha152x_host_reset(&tmp);
+	}
+	break;
+    }
+    return 0;
+}
+
+static struct pcmcia_driver aha152x_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "aha152x_cs",
+	},
+	.attach		= aha152x_attach,
+	.detach		= aha152x_detach,
+};
+
+static int __init init_aha152x_cs(void)
+{
+	return pcmcia_register_driver(&aha152x_cs_driver);
+}
+
+static void __exit exit_aha152x_cs(void)
+{
+	pcmcia_unregister_driver(&aha152x_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_aha152x_cs);
+module_exit(exit_aha152x_cs);
+ 
diff --git a/drivers/scsi/pcmcia/fdomain_core.c b/drivers/scsi/pcmcia/fdomain_core.c
new file mode 100644
index 0000000..a489137
--- /dev/null
+++ b/drivers/scsi/pcmcia/fdomain_core.c
@@ -0,0 +1,2 @@
+#define PCMCIA 1
+#include "fdomain.c"
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
new file mode 100644
index 0000000..3df7bc7
--- /dev/null
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -0,0 +1,323 @@
+/*======================================================================
+
+    A driver for Future Domain-compatible PCMCIA SCSI cards
+
+    fdomain_cs.c 1.47 2001/10/13 00:08:52
+
+    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/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "fdomain.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Future Domain PCMCIA SCSI driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"fdomain_cs.c 1.47 2001/10/13 00:08:52 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    struct Scsi_Host	*host;
+} scsi_info_t;
+
+
+static void fdomain_release(dev_link_t *link);
+static int fdomain_event(event_t event, int priority,
+			event_callback_args_t *args);
+
+static dev_link_t *fdomain_attach(void);
+static void fdomain_detach(dev_link_t *);
+
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "fdomain_cs";
+
+static dev_link_t *fdomain_attach(void)
+{
+    scsi_info_t *info;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    int ret;
+    
+    DEBUG(0, "fdomain_attach()\n");
+
+    /* Create new SCSI device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL);
+    if (!info) return NULL;
+    memset(info, 0, sizeof(*info));
+    link = &info->link; link->priv = info;
+    link->io.NumPorts1 = 0x10;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 10;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.event_handler = &fdomain_event;
+    client_reg.EventMask =
+	CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	fdomain_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* fdomain_attach */
+
+/*====================================================================*/
+
+static void fdomain_detach(dev_link_t *link)
+{
+    dev_link_t **linkp;
+
+    DEBUG(0, "fdomain_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->state & DEV_CONFIG)
+	fdomain_release(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    kfree(link->priv);
+    
+} /* fdomain_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void fdomain_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    scsi_info_t *info = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn;
+    u_char tuple_data[64];
+    char str[16];
+    struct Scsi_Host *host;
+
+    DEBUG(0, "fdomain_config(0x%p)\n", link);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    tuple.TupleData = tuple_data;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+    
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (1) {
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+	    goto next_entry;
+	link->conf.ConfigIndex = parse.cftable_entry.index;
+	link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+	i = pcmcia_request_io(handle, &link->io);
+	if (i == CS_SUCCESS) break;
+    next_entry:
+	CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+    }
+
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    
+    /* A bad hack... */
+    release_region(link->io.BasePort1, link->io.NumPorts1);
+
+    /* Set configuration options for the fdomain driver */
+    sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ);
+    fdomain_setup(str);
+    
+    host = __fdomain_16x0_detect(&fdomain_driver_template);
+    if (!host) {
+        printk(KERN_INFO "fdomain_cs: no SCSI devices found\n");
+	goto cs_failed;
+    }
+ 
+    scsi_add_host(host, NULL); /* XXX handle failure */
+    scsi_scan_host(host);
+
+    sprintf(info->node.dev_name, "scsi%d", host->host_no);
+    link->dev = &info->node;
+    info->host = host;
+    
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+    
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+    fdomain_release(link);
+    return;
+    
+} /* fdomain_config */
+
+/*====================================================================*/
+
+static void fdomain_release(dev_link_t *link)
+{
+    scsi_info_t *info = link->priv;
+
+    DEBUG(0, "fdomain_release(0x%p)\n", link);
+
+    scsi_remove_host(info->host);
+    link->dev = NULL;
+    
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+
+    scsi_unregister(info->host);
+
+    link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int fdomain_event(event_t event, int priority,
+			event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+
+    DEBUG(1, "fdomain_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    fdomain_release(link);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	fdomain_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG)
+	    pcmcia_release_configuration(link->handle);
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    fdomain_16x0_bus_reset(NULL);
+	}
+	break;
+    }
+    return 0;
+} /* fdomain_event */
+
+static struct pcmcia_driver fdomain_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "fdomain_cs",
+	},
+	.attach		= fdomain_attach,
+	.detach		= fdomain_detach,
+};
+
+static int __init init_fdomain_cs(void)
+{
+	return pcmcia_register_driver(&fdomain_cs_driver);
+}
+
+static void __exit exit_fdomain_cs(void)
+{
+	pcmcia_unregister_driver(&fdomain_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_fdomain_cs);
+module_exit(exit_fdomain_cs);
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
new file mode 100644
index 0000000..496c412
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -0,0 +1,2198 @@
+/*======================================================================
+
+    NinjaSCSI-3 / NinjaSCSI-32Bi PCMCIA SCSI host adapter card driver
+      By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+    Ver.2.8   Support 32bit MMIO mode
+              Support Synchronous Data Transfer Request (SDTR) mode
+    Ver.2.0   Support 32bit PIO mode
+    Ver.1.1.2 Fix for scatter list buffer exceeds
+    Ver.1.1   Support scatter list
+    Ver.0.1   Initial version
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License.
+
+======================================================================*/
+
+/***********************************************************************
+    This driver is for these PCcards.
+
+	I-O DATA PCSC-F	 (Workbit NinjaSCSI-3)
+			"WBT", "NinjaSCSI-3", "R1.0"
+	I-O DATA CBSC-II (Workbit NinjaSCSI-32Bi in 16bit mode)
+			"IO DATA", "CBSC16	 ", "1"
+
+***********************************************************************/
+
+/* $Id: nsp_cs.c,v 1.23 2003/08/18 11:09:19 elca Exp $ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <../drivers/scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include "nsp_cs.h"
+
+MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>");
+MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module $Revision: 1.23 $");
+MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#include "nsp_io.h"
+
+/*====================================================================*/
+/* Parameters that can be set with 'insmod' */
+
+static int       nsp_burst_mode = BURST_MEM32;
+module_param(nsp_burst_mode, int, 0);
+MODULE_PARM_DESC(nsp_burst_mode, "Burst transfer mode (0=io8, 1=io32, 2=mem32(default))");
+
+/* Release IO ports after configuration? */
+static int       free_ports = 0;
+module_param(free_ports, bool, 0);
+MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))");
+
+/* /usr/src/linux/drivers/scsi/hosts.h */
+static Scsi_Host_Template nsp_driver_template = {
+	.proc_name	         = "nsp_cs",
+	.proc_info		 = nsp_proc_info,
+	.name			 = "WorkBit NinjaSCSI-3/32Bi(16bit)",
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+	.detect			 = nsp_detect_old,
+	.release		 = nsp_release_old,
+#endif
+	.info			 = nsp_info,
+	.queuecommand		 = nsp_queuecommand,
+/*	.eh_strategy_handler	 = nsp_eh_strategy,*/
+/*	.eh_abort_handler	 = nsp_eh_abort,*/
+/*	.eh_device_reset_handler = nsp_eh_device_reset,*/
+	.eh_bus_reset_handler	 = nsp_eh_bus_reset,
+	.eh_host_reset_handler	 = nsp_eh_host_reset,
+	.can_queue		 = 1,
+	.this_id		 = NSP_INITIATOR_ID,
+	.sg_tablesize		 = SG_ALL,
+	.cmd_per_lun		 = 1,
+	.use_clustering		 = DISABLE_CLUSTERING,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
+	.use_new_eh_code	 = 1,
+#endif
+};
+
+static dev_link_t *dev_list = NULL;
+static dev_info_t dev_info  = {"nsp_cs"};
+
+static nsp_hw_data nsp_data_base; /* attach <-> detect glue */
+
+
+
+/*
+ * debug, error print
+ */
+#ifndef NSP_DEBUG
+# define NSP_DEBUG_MASK		0x000000
+# define nsp_msg(type, args...) nsp_cs_message("", 0, (type), args)
+# define nsp_dbg(mask, args...) /* */
+#else
+# define NSP_DEBUG_MASK		0xffffff
+# define nsp_msg(type, args...) \
+	nsp_cs_message (__FUNCTION__, __LINE__, (type), args)
+# define nsp_dbg(mask, args...) \
+	nsp_cs_dmessage(__FUNCTION__, __LINE__, (mask), args)
+#endif
+
+#define NSP_DEBUG_QUEUECOMMAND		BIT(0)
+#define NSP_DEBUG_REGISTER		BIT(1)
+#define NSP_DEBUG_AUTOSCSI		BIT(2)
+#define NSP_DEBUG_INTR			BIT(3)
+#define NSP_DEBUG_SGLIST		BIT(4)
+#define NSP_DEBUG_BUSFREE		BIT(5)
+#define NSP_DEBUG_CDB_CONTENTS		BIT(6)
+#define NSP_DEBUG_RESELECTION		BIT(7)
+#define NSP_DEBUG_MSGINOCCUR		BIT(8)
+#define NSP_DEBUG_EEPROM		BIT(9)
+#define NSP_DEBUG_MSGOUTOCCUR		BIT(10)
+#define NSP_DEBUG_BUSRESET		BIT(11)
+#define NSP_DEBUG_RESTART		BIT(12)
+#define NSP_DEBUG_SYNC			BIT(13)
+#define NSP_DEBUG_WAIT			BIT(14)
+#define NSP_DEBUG_TARGETFLAG		BIT(15)
+#define NSP_DEBUG_PROC			BIT(16)
+#define NSP_DEBUG_INIT			BIT(17)
+#define NSP_DEBUG_DATA_IO      		BIT(18)
+#define NSP_SPECIAL_PRINT_REGISTER	BIT(20)
+
+#define NSP_DEBUG_BUF_LEN		150
+
+static void nsp_cs_message(const char *func, int line, char *type, char *fmt, ...)
+{
+	va_list args;
+	char buf[NSP_DEBUG_BUF_LEN];
+
+	va_start(args, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+#ifndef NSP_DEBUG
+	printk("%snsp_cs: %s\n", type, buf);
+#else
+	printk("%snsp_cs: %s (%d): %s\n", type, func, line, buf);
+#endif
+}
+
+#ifdef NSP_DEBUG
+static void nsp_cs_dmessage(const char *func, int line, int mask, char *fmt, ...)
+{
+	va_list args;
+	char buf[NSP_DEBUG_BUF_LEN];
+
+	va_start(args, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+
+	if (mask & NSP_DEBUG_MASK) {
+		printk("nsp_cs-debug: 0x%x %s (%d): %s\n", mask, func, line, buf);
+	}
+}
+#endif
+
+/***********************************************************/
+
+/*====================================================
+ * Clenaup parameters and call done() functions.
+ * You must be set SCpnt->result before call this function.
+ */
+static void nsp_scsi_done(Scsi_Cmnd *SCpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+	data->CurrentSC = NULL;
+
+	SCpnt->scsi_done(SCpnt);
+}
+
+static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+#ifdef NSP_DEBUG
+	/*unsigned int host_id = SCpnt->device->host->this_id;*/
+	/*unsigned int base    = SCpnt->device->host->io_port;*/
+	unsigned char target = SCpnt->device->id;
+#endif
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+	nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "SCpnt=0x%p target=%d lun=%d buff=0x%p bufflen=%d use_sg=%d",
+		   SCpnt, target, SCpnt->device->lun, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->use_sg);
+	//nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "before CurrentSC=0x%p", data->CurrentSC);
+
+	SCpnt->scsi_done	= done;
+
+	if (data->CurrentSC != NULL) {
+		nsp_msg(KERN_DEBUG, "CurrentSC!=NULL this can't be happen");
+		SCpnt->result   = DID_BAD_TARGET << 16;
+		nsp_scsi_done(SCpnt);
+		return 0;
+	}
+
+#if 0
+	/* XXX: pcmcia-cs generates SCSI command with "scsi_info" utility.
+	        This makes kernel crash when suspending... */
+	if (data->ScsiInfo->stop != 0) {
+		nsp_msg(KERN_INFO, "suspending device. reject command.");
+		SCpnt->result  = DID_BAD_TARGET << 16;
+		nsp_scsi_done(SCpnt);
+		return SCSI_MLQUEUE_HOST_BUSY;
+	}
+#endif
+
+	show_command(SCpnt);
+
+	data->CurrentSC		= SCpnt;
+
+	SCpnt->SCp.Status	= CHECK_CONDITION;
+	SCpnt->SCp.Message	= 0;
+	SCpnt->SCp.have_data_in = IO_UNKNOWN;
+	SCpnt->SCp.sent_command = 0;
+	SCpnt->SCp.phase	= PH_UNDETERMINED;
+	SCpnt->resid	        = SCpnt->request_bufflen;
+
+	/* setup scratch area
+	   SCp.ptr		: buffer pointer
+	   SCp.this_residual	: buffer length
+	   SCp.buffer		: next buffer
+	   SCp.buffers_residual : left buffers in list
+	   SCp.phase		: current state of the command */
+	if (SCpnt->use_sg) {
+		SCpnt->SCp.buffer	    = (struct scatterlist *) SCpnt->request_buffer;
+		SCpnt->SCp.ptr		    = BUFFER_ADDR;
+		SCpnt->SCp.this_residual    = SCpnt->SCp.buffer->length;
+		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+	} else {
+		SCpnt->SCp.ptr		    = (char *) SCpnt->request_buffer;
+		SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
+		SCpnt->SCp.buffer	    = NULL;
+		SCpnt->SCp.buffers_residual = 0;
+	}
+
+	if (nsphw_start_selection(SCpnt) == FALSE) {
+		nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "selection fail");
+		SCpnt->result   = DID_BUS_BUSY << 16;
+		nsp_scsi_done(SCpnt);
+		return 0;
+	}
+
+
+	//nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "out");
+#ifdef NSP_DEBUG
+	data->CmdId++;
+#endif
+	return 0;
+}
+
+/*
+ * setup PIO FIFO transfer mode and enable/disable to data out
+ */
+static void nsp_setup_fifo(nsp_hw_data *data, int enabled)
+{
+	unsigned int  base = data->BaseAddress;
+	unsigned char transfer_mode_reg;
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "enabled=%d", enabled);
+
+	if (enabled != FALSE) {
+		transfer_mode_reg = TRANSFER_GO | BRAIND;
+	} else {
+		transfer_mode_reg = 0;
+	}
+
+	transfer_mode_reg |= data->TransferMode;
+
+	nsp_index_write(base, TRANSFERMODE, transfer_mode_reg);
+}
+
+static void nsphw_init_sync(nsp_hw_data *data)
+{
+	sync_data tmp_sync = { .SyncNegotiation = SYNC_NOT_YET,
+			       .SyncPeriod      = 0,
+			       .SyncOffset      = 0
+	};
+	int i;
+
+	/* setup sync data */
+	for ( i = 0; i < ARRAY_SIZE(data->Sync); i++ ) {
+		data->Sync[i] = tmp_sync;
+	}
+}
+
+/*
+ * Initialize Ninja hardware
+ */
+static int nsphw_init(nsp_hw_data *data)
+{
+	unsigned int base     = data->BaseAddress;
+
+	nsp_dbg(NSP_DEBUG_INIT, "in base=0x%x", base);
+
+	data->ScsiClockDiv = CLOCK_40M | FAST_20;
+	data->CurrentSC    = NULL;
+	data->FifoCount    = 0;
+	data->TransferMode = MODE_IO8;
+
+	nsphw_init_sync(data);
+
+	/* block all interrupts */
+	nsp_write(base,	      IRQCONTROL,   IRQCONTROL_ALLMASK);
+
+	/* setup SCSI interface */
+	nsp_write(base,	      IFSELECT,	    IF_IFSEL);
+
+	nsp_index_write(base, SCSIIRQMODE,  0);
+
+	nsp_index_write(base, TRANSFERMODE, MODE_IO8);
+	nsp_index_write(base, CLOCKDIV,	    data->ScsiClockDiv);
+
+	nsp_index_write(base, PARITYCTRL,   0);
+	nsp_index_write(base, POINTERCLR,   POINTER_CLEAR     |
+					    ACK_COUNTER_CLEAR |
+					    REQ_COUNTER_CLEAR |
+					    HOST_COUNTER_CLEAR);
+
+	/* setup fifo asic */
+	nsp_write(base,	      IFSELECT,	    IF_REGSEL);
+	nsp_index_write(base, TERMPWRCTRL,  0);
+	if ((nsp_index_read(base, OTHERCONTROL) & TPWR_SENSE) == 0) {
+		nsp_msg(KERN_INFO, "terminator power on");
+		nsp_index_write(base, TERMPWRCTRL, POWER_ON);
+	}
+
+	nsp_index_write(base, TIMERCOUNT,   0);
+	nsp_index_write(base, TIMERCOUNT,   0); /* requires 2 times!! */
+
+	nsp_index_write(base, SYNCREG,	    0);
+	nsp_index_write(base, ACKWIDTH,	    0);
+
+	/* enable interrupts and ack them */
+	nsp_index_write(base, SCSIIRQMODE,  SCSI_PHASE_CHANGE_EI |
+					    RESELECT_EI		 |
+					    SCSI_RESET_IRQ_EI	 );
+	nsp_write(base,	      IRQCONTROL,   IRQCONTROL_ALLCLEAR);
+
+	nsp_setup_fifo(data, FALSE);
+
+	return TRUE;
+}
+
+/*
+ * Start selection phase
+ */
+static int nsphw_start_selection(Scsi_Cmnd *SCpnt)
+{
+	unsigned int  host_id	 = SCpnt->device->host->this_id;
+	unsigned int  base	 = SCpnt->device->host->io_port;
+	unsigned char target	 = SCpnt->device->id;
+	nsp_hw_data  *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	int	      time_out;
+	unsigned char phase, arbit;
+
+	//nsp_dbg(NSP_DEBUG_RESELECTION, "in");
+
+	phase = nsp_index_read(base, SCSIBUSMON);
+	if(phase != BUSMON_BUS_FREE) {
+		//nsp_dbg(NSP_DEBUG_RESELECTION, "bus busy");
+		return FALSE;
+	}
+
+	/* start arbitration */
+	//nsp_dbg(NSP_DEBUG_RESELECTION, "start arbit");
+	SCpnt->SCp.phase = PH_ARBSTART;
+	nsp_index_write(base, SETARBIT, ARBIT_GO);
+
+	time_out = 1000;
+	do {
+		/* XXX: what a stupid chip! */
+		arbit = nsp_index_read(base, ARBITSTATUS);
+		//nsp_dbg(NSP_DEBUG_RESELECTION, "arbit=%d, wait_count=%d", arbit, wait_count);
+		udelay(1); /* hold 1.2us */
+	} while((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
+		(time_out-- != 0));
+
+	if (!(arbit & ARBIT_WIN)) {
+		//nsp_dbg(NSP_DEBUG_RESELECTION, "arbit fail");
+		nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR);
+		return FALSE;
+	}
+
+	/* assert select line */
+	//nsp_dbg(NSP_DEBUG_RESELECTION, "assert SEL line");
+	SCpnt->SCp.phase = PH_SELSTART;
+	udelay(3); /* wait 2.4us */
+	nsp_index_write(base, SCSIDATALATCH, BIT(host_id) | BIT(target));
+	nsp_index_write(base, SCSIBUSCTRL,   SCSI_SEL | SCSI_BSY                    | SCSI_ATN);
+	udelay(2); /* wait >1.2us */
+	nsp_index_write(base, SCSIBUSCTRL,   SCSI_SEL | SCSI_BSY | SCSI_DATAOUT_ENB | SCSI_ATN);
+	nsp_index_write(base, SETARBIT,	     ARBIT_FLAG_CLEAR);
+	/*udelay(1);*/ /* wait >90ns */
+	nsp_index_write(base, SCSIBUSCTRL,   SCSI_SEL            | SCSI_DATAOUT_ENB | SCSI_ATN);
+
+	/* check selection timeout */
+	nsp_start_timer(SCpnt, 1000/51);
+	data->SelectionTimeOut = 1;
+
+	return TRUE;
+}
+
+struct nsp_sync_table {
+	unsigned int min_period;
+	unsigned int max_period;
+	unsigned int chip_period;
+	unsigned int ack_width;
+};
+
+static struct nsp_sync_table nsp_sync_table_40M[] = {
+	{0x0c, 0x0c, 0x1, 0},	/* 20MB	  50ns*/
+	{0x19, 0x19, 0x3, 1},	/* 10MB	 100ns*/ 
+	{0x1a, 0x25, 0x5, 2},	/* 7.5MB 150ns*/ 
+	{0x26, 0x32, 0x7, 3},	/* 5MB	 200ns*/
+	{   0,    0,   0, 0},
+};
+
+static struct nsp_sync_table nsp_sync_table_20M[] = {
+	{0x19, 0x19, 0x1, 0},	/* 10MB	 100ns*/ 
+	{0x1a, 0x25, 0x2, 0},	/* 7.5MB 150ns*/ 
+	{0x26, 0x32, 0x3, 1},	/* 5MB	 200ns*/
+	{   0,    0,   0, 0},
+};
+
+/*
+ * setup synchronous data transfer mode
+ */
+static int nsp_analyze_sdtr(Scsi_Cmnd *SCpnt)
+{
+	unsigned char	       target = SCpnt->device->id;
+//	unsigned char	       lun    = SCpnt->device->lun;
+	nsp_hw_data           *data   = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	sync_data	      *sync   = &(data->Sync[target]);
+	struct nsp_sync_table *sync_table;
+	unsigned int	       period, offset;
+	int		       i;
+
+
+	nsp_dbg(NSP_DEBUG_SYNC, "in");
+
+	period = sync->SyncPeriod;
+	offset = sync->SyncOffset;
+
+	nsp_dbg(NSP_DEBUG_SYNC, "period=0x%x, offset=0x%x", period, offset);
+
+	if ((data->ScsiClockDiv & (BIT(0)|BIT(1))) == CLOCK_20M) {
+		sync_table = nsp_sync_table_20M;
+	} else {
+		sync_table = nsp_sync_table_40M;
+	}
+
+	for ( i = 0; sync_table->max_period != 0; i++, sync_table++) {
+		if ( period >= sync_table->min_period &&
+		     period <= sync_table->max_period	 ) {
+			break;
+		}
+	}
+
+	if (period != 0 && sync_table->max_period == 0) {
+		/*
+		 * No proper period/offset found
+		 */
+		nsp_dbg(NSP_DEBUG_SYNC, "no proper period/offset");
+
+		sync->SyncPeriod      = 0;
+		sync->SyncOffset      = 0;
+		sync->SyncRegister    = 0;
+		sync->AckWidth	      = 0;
+
+		return FALSE;
+	}
+
+	sync->SyncRegister    = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) |
+		                (offset & SYNCREG_OFFSET_MASK);
+	sync->AckWidth	      = sync_table->ack_width;
+
+	nsp_dbg(NSP_DEBUG_SYNC, "sync_reg=0x%x, ack_width=0x%x", sync->SyncRegister, sync->AckWidth);
+
+	return TRUE;
+}
+
+
+/*
+ * start ninja hardware timer
+ */
+static void nsp_start_timer(Scsi_Cmnd *SCpnt, int time)
+{
+	unsigned int base = SCpnt->device->host->io_port;
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+	//nsp_dbg(NSP_DEBUG_INTR, "in SCpnt=0x%p, time=%d", SCpnt, time);
+	data->TimerCount = time;
+	nsp_index_write(base, TIMERCOUNT, time);
+}
+
+/*
+ * wait for bus phase change
+ */
+static int nsp_negate_signal(Scsi_Cmnd *SCpnt, unsigned char mask, char *str)
+{
+	unsigned int  base = SCpnt->device->host->io_port;
+	unsigned char reg;
+	int	      time_out;
+
+	//nsp_dbg(NSP_DEBUG_INTR, "in");
+
+	time_out = 100;
+
+	do {
+		reg = nsp_index_read(base, SCSIBUSMON);
+		if (reg == 0xff) {
+			break;
+		}
+	} while ((time_out-- != 0) && (reg & mask) != 0);
+
+	if (time_out == 0) {
+		nsp_msg(KERN_DEBUG, " %s signal off timeut", str);
+	}
+
+	return 0;
+}
+
+/*
+ * expect Ninja Irq
+ */
+static int nsp_expect_signal(Scsi_Cmnd	   *SCpnt,
+			     unsigned char  current_phase,
+			     unsigned char  mask)
+{
+	unsigned int  base	 = SCpnt->device->host->io_port;
+	int	      time_out;
+	unsigned char phase, i_src;
+
+	//nsp_dbg(NSP_DEBUG_INTR, "current_phase=0x%x, mask=0x%x", current_phase, mask);
+
+	time_out = 100;
+	do {
+		phase = nsp_index_read(base, SCSIBUSMON);
+		if (phase == 0xff) {
+			//nsp_dbg(NSP_DEBUG_INTR, "ret -1");
+			return -1;
+		}
+		i_src = nsp_read(base, IRQSTATUS);
+		if (i_src & IRQSTATUS_SCSI) {
+			//nsp_dbg(NSP_DEBUG_INTR, "ret 0 found scsi signal");
+			return 0;
+		}
+		if ((phase & mask) != 0 && (phase & BUSMON_PHASE_MASK) == current_phase) {
+			//nsp_dbg(NSP_DEBUG_INTR, "ret 1 phase=0x%x", phase);
+			return 1;
+		}
+	} while(time_out-- != 0);
+
+	//nsp_dbg(NSP_DEBUG_INTR, "timeout");
+	return -1;
+}
+
+/*
+ * transfer SCSI message
+ */
+static int nsp_xfer(Scsi_Cmnd *SCpnt, int phase)
+{
+	unsigned int  base = SCpnt->device->host->io_port;
+	nsp_hw_data  *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	char	     *buf  = data->MsgBuffer;
+	int	      len  = min(MSGBUF_SIZE, data->MsgLen);
+	int	      ptr;
+	int	      ret;
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "in");
+	for (ptr = 0; len > 0; len--, ptr++) {
+
+		ret = nsp_expect_signal(SCpnt, phase, BUSMON_REQ);
+		if (ret <= 0) {
+			nsp_dbg(NSP_DEBUG_DATA_IO, "xfer quit");
+			return 0;
+		}
+
+		/* if last byte, negate ATN */
+		if (len == 1 && SCpnt->SCp.phase == PH_MSG_OUT) {
+			nsp_index_write(base, SCSIBUSCTRL, AUTODIRECTION | ACKENB);
+		}
+
+		/* read & write message */
+		if (phase & BUSMON_IO) {
+			nsp_dbg(NSP_DEBUG_DATA_IO, "read msg");
+			buf[ptr] = nsp_index_read(base, SCSIDATAWITHACK);
+		} else {
+			nsp_dbg(NSP_DEBUG_DATA_IO, "write msg");
+			nsp_index_write(base, SCSIDATAWITHACK, buf[ptr]);
+		}
+		nsp_negate_signal(SCpnt, BUSMON_ACK, "xfer<ack>");
+
+	}
+	return len;
+}
+
+/*
+ * get extra SCSI data from fifo
+ */
+static int nsp_dataphase_bypass(Scsi_Cmnd *SCpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	unsigned int count;
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "in");
+
+	if (SCpnt->SCp.have_data_in != IO_IN) {
+		return 0;
+	}
+
+	count = nsp_fifo_count(SCpnt);
+	if (data->FifoCount == count) {
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "not use bypass quirk");
+		return 0;
+	}
+
+	/*
+	 * XXX: NSP_QUIRK
+	 * data phase skip only occures in case of SCSI_LOW_READ
+	 */
+	nsp_dbg(NSP_DEBUG_DATA_IO, "use bypass quirk");
+	SCpnt->SCp.phase = PH_DATA;
+	nsp_pio_read(SCpnt);
+	nsp_setup_fifo(data, FALSE);
+
+	return 0;
+}
+
+/*
+ * accept reselection
+ */
+static int nsp_reselected(Scsi_Cmnd *SCpnt)
+{
+	unsigned int  base    = SCpnt->device->host->io_port;
+	unsigned int  host_id = SCpnt->device->host->this_id;
+	//nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	unsigned char bus_reg;
+	unsigned char id_reg, tmp;
+	int target;
+
+	nsp_dbg(NSP_DEBUG_RESELECTION, "in");
+
+	id_reg = nsp_index_read(base, RESELECTID);
+	tmp    = id_reg & (~BIT(host_id));
+	target = 0;
+	while(tmp != 0) {
+		if (tmp & BIT(0)) {
+			break;
+		}
+		tmp >>= 1;
+		target++;
+	}
+
+	if (SCpnt->device->id != target) {
+		nsp_msg(KERN_ERR, "XXX: reselect ID must be %d in this implementation.", target);
+	}
+
+	nsp_negate_signal(SCpnt, BUSMON_SEL, "reselect<SEL>");
+
+	nsp_nexus(SCpnt);
+	bus_reg = nsp_index_read(base, SCSIBUSCTRL) & ~(SCSI_BSY | SCSI_ATN);
+	nsp_index_write(base, SCSIBUSCTRL, bus_reg);
+	nsp_index_write(base, SCSIBUSCTRL, bus_reg | AUTODIRECTION | ACKENB);
+
+	return TRUE;
+}
+
+/*
+ * count how many data transferd
+ */
+static int nsp_fifo_count(Scsi_Cmnd *SCpnt)
+{
+	unsigned int base = SCpnt->device->host->io_port;
+	unsigned int count;
+	unsigned int l, m, h, dummy;
+
+	nsp_index_write(base, POINTERCLR, POINTER_CLEAR | ACK_COUNTER);
+
+	l     = nsp_index_read(base, TRANSFERCOUNT);
+	m     = nsp_index_read(base, TRANSFERCOUNT);
+	h     = nsp_index_read(base, TRANSFERCOUNT);
+	dummy = nsp_index_read(base, TRANSFERCOUNT); /* required this! */
+
+	count = (h << 16) | (m << 8) | (l << 0);
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "count=0x%x", count);
+
+	return count;
+}
+
+/* fifo size */
+#define RFIFO_CRIT 64
+#define WFIFO_CRIT 64
+
+/*
+ * read data in DATA IN phase
+ */
+static void nsp_pio_read(Scsi_Cmnd *SCpnt)
+{
+	unsigned int  base      = SCpnt->device->host->io_port;
+	unsigned long mmio_base = SCpnt->device->host->base;
+	nsp_hw_data  *data      = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	long	      time_out;
+	int	      ocount, res;
+	unsigned char stat, fifo_stat;
+
+	ocount = data->FifoCount;
+
+	nsp_dbg(NSP_DEBUG_DATA_IO, "in SCpnt=0x%p resid=%d ocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d",
+		SCpnt, SCpnt->resid, ocount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual);
+
+	time_out = 1000;
+
+	while ((time_out-- != 0) &&
+	       (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0 ) ) {
+
+		stat = nsp_index_read(base, SCSIBUSMON);
+		stat &= BUSMON_PHASE_MASK;
+
+
+		res = nsp_fifo_count(SCpnt) - ocount;
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x ocount=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount, res);
+		if (res == 0) { /* if some data avilable ? */
+			if (stat == BUSPHASE_DATA_IN) { /* phase changed? */
+				//nsp_dbg(NSP_DEBUG_DATA_IO, " wait for data this=%d", SCpnt->SCp.this_residual);
+				continue;
+			} else {
+				nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x", stat);
+				break;
+			}
+		}
+
+		fifo_stat = nsp_read(base, FIFOSTATUS);
+		if ((fifo_stat & FIFOSTATUS_FULL_EMPTY) == 0 &&
+		    stat                                == BUSPHASE_DATA_IN) {
+			continue;
+		}
+
+		res = min(res, SCpnt->SCp.this_residual);
+
+		switch (data->TransferMode) {
+		case MODE_IO32:
+			res &= ~(BIT(1)|BIT(0)); /* align 4 */
+			nsp_fifo32_read(base, SCpnt->SCp.ptr, res >> 2);
+			break;
+		case MODE_IO8:
+			nsp_fifo8_read (base, SCpnt->SCp.ptr, res     );
+			break;
+
+		case MODE_MEM32:
+			res &= ~(BIT(1)|BIT(0)); /* align 4 */
+			nsp_mmio_fifo32_read(mmio_base, SCpnt->SCp.ptr, res >> 2);
+			break;
+
+		default:
+			nsp_dbg(NSP_DEBUG_DATA_IO, "unknown read mode");
+			return;
+		}
+
+		SCpnt->resid	       	 -= res;
+		SCpnt->SCp.ptr		 += res;
+		SCpnt->SCp.this_residual -= res;
+		ocount			 += res;
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this_residual=0x%x ocount=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, ocount);
+
+		/* go to next scatter list if available */
+		if (SCpnt->SCp.this_residual	== 0 &&
+		    SCpnt->SCp.buffers_residual != 0 ) {
+			//nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next timeout=%d", time_out);
+			SCpnt->SCp.buffers_residual--;
+			SCpnt->SCp.buffer++;
+			SCpnt->SCp.ptr		 = BUFFER_ADDR;
+			SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+			time_out = 1000;
+
+			//nsp_dbg(NSP_DEBUG_DATA_IO, "page: 0x%p, off: 0x%x", SCpnt->SCp.buffer->page, SCpnt->SCp.buffer->offset);
+		}
+	}
+
+	data->FifoCount = ocount;
+
+	if (time_out == 0) {
+		nsp_msg(KERN_DEBUG, "pio read timeout resid=%d this_residual=%d buffers_residual=%d",
+			SCpnt->resid, SCpnt->SCp.this_residual, SCpnt->SCp.buffers_residual);
+	}
+	nsp_dbg(NSP_DEBUG_DATA_IO, "read ocount=0x%x", ocount);
+	nsp_dbg(NSP_DEBUG_DATA_IO, "r cmd=%d resid=0x%x\n", data->CmdId, SCpnt->resid);
+}
+
+/*
+ * write data in DATA OUT phase
+ */
+static void nsp_pio_write(Scsi_Cmnd *SCpnt)
+{
+	unsigned int  base      = SCpnt->device->host->io_port;
+	unsigned long mmio_base = SCpnt->device->host->base;
+	nsp_hw_data  *data      = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	int	      time_out;
+	int           ocount, res;
+	unsigned char stat;
+
+	ocount	 = data->FifoCount;
+
+	nsp_dbg(NSP_DEBUG_DATA_IO, "in fifocount=%d ptr=0x%p this_residual=%d buffers=0x%p nbuf=%d resid=0x%x",
+		data->FifoCount, SCpnt->SCp.ptr, SCpnt->SCp.this_residual, SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual, SCpnt->resid);
+
+	time_out = 1000;
+
+	while ((time_out-- != 0) &&
+	       (SCpnt->SCp.this_residual > 0 || SCpnt->SCp.buffers_residual > 0)) {
+		stat = nsp_index_read(base, SCSIBUSMON);
+		stat &= BUSMON_PHASE_MASK;
+
+		if (stat != BUSPHASE_DATA_OUT) {
+			res = ocount - nsp_fifo_count(SCpnt);
+
+			nsp_dbg(NSP_DEBUG_DATA_IO, "phase changed stat=0x%x, res=%d\n", stat, res);
+			/* Put back pointer */
+			SCpnt->resid	       	 += res;
+			SCpnt->SCp.ptr		 -= res;
+			SCpnt->SCp.this_residual += res;
+			ocount			 -= res;
+
+			break;
+		}
+
+		res = ocount - nsp_fifo_count(SCpnt);
+		if (res > 0) { /* write all data? */
+			nsp_dbg(NSP_DEBUG_DATA_IO, "wait for all data out. ocount=0x%x res=%d", ocount, res);
+			continue;
+		}
+
+		res = min(SCpnt->SCp.this_residual, WFIFO_CRIT);
+
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "ptr=0x%p this=0x%x res=0x%x", SCpnt->SCp.ptr, SCpnt->SCp.this_residual, res);
+		switch (data->TransferMode) {
+		case MODE_IO32:
+			res &= ~(BIT(1)|BIT(0)); /* align 4 */
+			nsp_fifo32_write(base, SCpnt->SCp.ptr, res >> 2);
+			break;
+		case MODE_IO8:
+			nsp_fifo8_write (base, SCpnt->SCp.ptr, res     );
+			break;
+
+		case MODE_MEM32:
+			res &= ~(BIT(1)|BIT(0)); /* align 4 */
+			nsp_mmio_fifo32_write(mmio_base, SCpnt->SCp.ptr, res >> 2);
+			break;
+
+		default:
+			nsp_dbg(NSP_DEBUG_DATA_IO, "unknown write mode");
+			break;
+		}
+
+		SCpnt->resid	       	 -= res;
+		SCpnt->SCp.ptr		 += res;
+		SCpnt->SCp.this_residual -= res;
+		ocount			 += res;
+
+		/* go to next scatter list if available */
+		if (SCpnt->SCp.this_residual	== 0 &&
+		    SCpnt->SCp.buffers_residual != 0 ) {
+			//nsp_dbg(NSP_DEBUG_DATA_IO, "scatterlist next");
+			SCpnt->SCp.buffers_residual--;
+			SCpnt->SCp.buffer++;
+			SCpnt->SCp.ptr		 = BUFFER_ADDR;
+			SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+			time_out = 1000;
+		}
+	}
+
+	data->FifoCount = ocount;
+
+	if (time_out == 0) {
+		nsp_msg(KERN_DEBUG, "pio write timeout resid=0x%x", SCpnt->resid);
+	}
+	nsp_dbg(NSP_DEBUG_DATA_IO, "write ocount=0x%x", ocount);
+	nsp_dbg(NSP_DEBUG_DATA_IO, "w cmd=%d resid=0x%x\n", data->CmdId, SCpnt->resid);
+}
+#undef RFIFO_CRIT
+#undef WFIFO_CRIT
+
+/*
+ * setup synchronous/asynchronous data transfer mode
+ */
+static int nsp_nexus(Scsi_Cmnd *SCpnt)
+{
+	unsigned int   base   = SCpnt->device->host->io_port;
+	unsigned char  target = SCpnt->device->id;
+//	unsigned char  lun    = SCpnt->device->lun;
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	sync_data     *sync   = &(data->Sync[target]);
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "in SCpnt=0x%p", SCpnt);
+
+	/* setup synch transfer registers */
+	nsp_index_write(base, SYNCREG,	sync->SyncRegister);
+	nsp_index_write(base, ACKWIDTH, sync->AckWidth);
+
+	if (SCpnt->use_sg    == 0        ||
+	    SCpnt->resid % 4 != 0        ||
+	    SCpnt->resid     <= PAGE_SIZE ) {
+		data->TransferMode = MODE_IO8;
+	} else if (nsp_burst_mode == BURST_MEM32) {
+		data->TransferMode = MODE_MEM32;
+	} else if (nsp_burst_mode == BURST_IO32) {
+		data->TransferMode = MODE_IO32;
+	} else {
+		data->TransferMode = MODE_IO8;
+	}
+
+	/* setup pdma fifo */
+	nsp_setup_fifo(data, TRUE);
+
+	/* clear ack counter */
+ 	data->FifoCount = 0;
+	nsp_index_write(base, POINTERCLR, POINTER_CLEAR	    |
+					  ACK_COUNTER_CLEAR |
+					  REQ_COUNTER_CLEAR |
+					  HOST_COUNTER_CLEAR);
+
+	return 0;
+}
+
+#include "nsp_message.c"
+/*
+ * interrupt handler
+ */
+static irqreturn_t nspintr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int   base;
+	unsigned char  irq_status, irq_phase, phase;
+	Scsi_Cmnd     *tmpSC;
+	unsigned char  target, lun;
+	unsigned int  *sync_neg;
+	int            i, tmp;
+	nsp_hw_data   *data;
+
+
+	//nsp_dbg(NSP_DEBUG_INTR, "dev_id=0x%p", dev_id);
+	//nsp_dbg(NSP_DEBUG_INTR, "host=0x%p", ((scsi_info_t *)dev_id)->host);
+
+	if (                dev_id        != NULL &&
+	    ((scsi_info_t *)dev_id)->host != NULL  ) {
+		scsi_info_t *info = (scsi_info_t *)dev_id;
+
+		data = (nsp_hw_data *)info->host->hostdata;
+	} else {
+		nsp_dbg(NSP_DEBUG_INTR, "host data wrong");
+		return IRQ_NONE;
+	}
+
+	//nsp_dbg(NSP_DEBUG_INTR, "&nsp_data_base=0x%p, dev_id=0x%p", &nsp_data_base, dev_id);
+
+	base = data->BaseAddress;
+	//nsp_dbg(NSP_DEBUG_INTR, "base=0x%x", base);
+
+	/*
+	 * interrupt check
+	 */
+	nsp_write(base, IRQCONTROL, IRQCONTROL_IRQDISABLE);
+	irq_status = nsp_read(base, IRQSTATUS);
+	//nsp_dbg(NSP_DEBUG_INTR, "irq_status=0x%x", irq_status);
+	if ((irq_status == 0xff) || ((irq_status & IRQSTATUS_MASK) == 0)) {
+		nsp_write(base, IRQCONTROL, 0);
+		//nsp_dbg(NSP_DEBUG_INTR, "no irq/shared irq");
+		return IRQ_NONE;
+	}
+
+	/* XXX: IMPORTANT
+	 * Do not read an irq_phase register if no scsi phase interrupt.
+	 * Unless, you should lose a scsi phase interrupt.
+	 */
+	phase = nsp_index_read(base, SCSIBUSMON);
+	if((irq_status & IRQSTATUS_SCSI) != 0) {
+		irq_phase = nsp_index_read(base, IRQPHASESENCE);
+	} else {
+		irq_phase = 0;
+	}
+
+	//nsp_dbg(NSP_DEBUG_INTR, "irq_phase=0x%x", irq_phase);
+
+	/*
+	 * timer interrupt handler (scsi vs timer interrupts)
+	 */
+	//nsp_dbg(NSP_DEBUG_INTR, "timercount=%d", data->TimerCount);
+	if (data->TimerCount != 0) {
+		//nsp_dbg(NSP_DEBUG_INTR, "stop timer");
+		nsp_index_write(base, TIMERCOUNT, 0);
+		nsp_index_write(base, TIMERCOUNT, 0);
+		data->TimerCount = 0;
+	}
+
+	if ((irq_status & IRQSTATUS_MASK) == IRQSTATUS_TIMER &&
+	    data->SelectionTimeOut == 0) {
+		//nsp_dbg(NSP_DEBUG_INTR, "timer start");
+		nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR);
+		return IRQ_HANDLED;
+	}
+
+	nsp_write(base, IRQCONTROL, IRQCONTROL_TIMER_CLEAR | IRQCONTROL_FIFO_CLEAR);
+
+	if ((irq_status & IRQSTATUS_SCSI) &&
+	    (irq_phase  & SCSI_RESET_IRQ)) {
+		nsp_msg(KERN_ERR, "bus reset (power off?)");
+
+		nsphw_init(data);
+		nsp_bus_reset(data);
+
+		if(data->CurrentSC != NULL) {
+			tmpSC = data->CurrentSC;
+			tmpSC->result  = (DID_RESET                   << 16) |
+				         ((tmpSC->SCp.Message & 0xff) <<  8) |
+				         ((tmpSC->SCp.Status  & 0xff) <<  0);
+			nsp_scsi_done(tmpSC);
+		}
+		return IRQ_HANDLED;
+	}
+
+	if (data->CurrentSC == NULL) {
+		nsp_msg(KERN_ERR, "CurrentSC==NULL irq_status=0x%x phase=0x%x irq_phase=0x%x this can't be happen. reset everything", irq_status, phase, irq_phase);
+		nsphw_init(data);
+		nsp_bus_reset(data);
+		return IRQ_HANDLED;
+	}
+
+	tmpSC    = data->CurrentSC;
+	target   = tmpSC->device->id;
+	lun      = tmpSC->device->lun;
+	sync_neg = &(data->Sync[target].SyncNegotiation);
+
+	/*
+	 * parse hardware SCSI irq reasons register
+	 */
+	if (irq_status & IRQSTATUS_SCSI) {
+		if (irq_phase & RESELECT_IRQ) {
+			nsp_dbg(NSP_DEBUG_INTR, "reselect");
+			nsp_write(base, IRQCONTROL, IRQCONTROL_RESELECT_CLEAR);
+			if (nsp_reselected(tmpSC) != FALSE) {
+				return IRQ_HANDLED;
+			}
+		}
+
+		if ((irq_phase & (PHASE_CHANGE_IRQ | LATCHED_BUS_FREE)) == 0) {
+			return IRQ_HANDLED;
+		}
+	}
+
+	//show_phase(tmpSC);
+
+	switch(tmpSC->SCp.phase) {
+	case PH_SELSTART:
+		// *sync_neg = SYNC_NOT_YET;
+		if ((phase & BUSMON_BSY) == 0) {
+			//nsp_dbg(NSP_DEBUG_INTR, "selection count=%d", data->SelectionTimeOut);
+			if (data->SelectionTimeOut >= NSP_SELTIMEOUT) {
+				nsp_dbg(NSP_DEBUG_INTR, "selection time out");
+				data->SelectionTimeOut = 0;
+				nsp_index_write(base, SCSIBUSCTRL, 0);
+
+				tmpSC->result   = DID_TIME_OUT << 16;
+				nsp_scsi_done(tmpSC);
+
+				return IRQ_HANDLED;
+			}
+			data->SelectionTimeOut += 1;
+			nsp_start_timer(tmpSC, 1000/51);
+			return IRQ_HANDLED;
+		}
+
+		/* attention assert */
+		//nsp_dbg(NSP_DEBUG_INTR, "attention assert");
+		data->SelectionTimeOut = 0;
+		tmpSC->SCp.phase       = PH_SELECTED;
+		nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN);
+		udelay(1);
+		nsp_index_write(base, SCSIBUSCTRL, SCSI_ATN | AUTODIRECTION | ACKENB);
+		return IRQ_HANDLED;
+
+		break;
+
+	case PH_RESELECT:
+		//nsp_dbg(NSP_DEBUG_INTR, "phase reselect");
+		// *sync_neg = SYNC_NOT_YET;
+		if ((phase & BUSMON_PHASE_MASK) != BUSPHASE_MESSAGE_IN) {
+
+			tmpSC->result	= DID_ABORT << 16;
+			nsp_scsi_done(tmpSC);
+			return IRQ_HANDLED;
+		}
+		/* fall thru */
+	default:
+		if ((irq_status & (IRQSTATUS_SCSI | IRQSTATUS_FIFO)) == 0) {
+			return IRQ_HANDLED;
+		}
+		break;
+	}
+
+	/*
+	 * SCSI sequencer
+	 */
+	//nsp_dbg(NSP_DEBUG_INTR, "start scsi seq");
+
+	/* normal disconnect */
+	if (((tmpSC->SCp.phase == PH_MSG_IN) || (tmpSC->SCp.phase == PH_MSG_OUT)) &&
+	    (irq_phase & LATCHED_BUS_FREE) != 0 ) {
+		nsp_dbg(NSP_DEBUG_INTR, "normal disconnect irq_status=0x%x, phase=0x%x, irq_phase=0x%x", irq_status, phase, irq_phase);
+
+		//*sync_neg       = SYNC_NOT_YET;
+
+		if ((tmpSC->SCp.Message == MSG_COMMAND_COMPLETE)) {     /* all command complete and return status */
+			tmpSC->result = (DID_OK		             << 16) |
+					((tmpSC->SCp.Message & 0xff) <<  8) |
+					((tmpSC->SCp.Status  & 0xff) <<  0);
+			nsp_dbg(NSP_DEBUG_INTR, "command complete result=0x%x", tmpSC->result);
+			nsp_scsi_done(tmpSC);
+
+			return IRQ_HANDLED;
+		}
+
+		return IRQ_HANDLED;
+	}
+
+
+	/* check unexpected bus free state */
+	if (phase == 0) {
+		nsp_msg(KERN_DEBUG, "unexpected bus free. irq_status=0x%x, phase=0x%x, irq_phase=0x%x", irq_status, phase, irq_phase);
+
+		*sync_neg       = SYNC_NG;
+		tmpSC->result   = DID_ERROR << 16;
+		nsp_scsi_done(tmpSC);
+		return IRQ_HANDLED;
+	}
+
+	switch (phase & BUSMON_PHASE_MASK) {
+	case BUSPHASE_COMMAND:
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_COMMAND");
+		if ((phase & BUSMON_REQ) == 0) {
+			nsp_dbg(NSP_DEBUG_INTR, "REQ == 0");
+			return IRQ_HANDLED;
+		}
+
+		tmpSC->SCp.phase = PH_COMMAND;
+
+		nsp_nexus(tmpSC);
+
+		/* write scsi command */
+		nsp_dbg(NSP_DEBUG_INTR, "cmd_len=%d", tmpSC->cmd_len);
+		nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER);
+		for (i = 0; i < tmpSC->cmd_len; i++) {
+			nsp_index_write(base, COMMANDDATA, tmpSC->cmnd[i]);
+		}
+		nsp_index_write(base, COMMANDCTRL, CLEAR_COMMAND_POINTER | AUTO_COMMAND_GO);
+		break;
+
+	case BUSPHASE_DATA_OUT:
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_OUT");
+
+		tmpSC->SCp.phase        = PH_DATA;
+		tmpSC->SCp.have_data_in = IO_OUT;
+
+		nsp_pio_write(tmpSC);
+
+		break;
+
+	case BUSPHASE_DATA_IN:
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_DATA_IN");
+
+		tmpSC->SCp.phase        = PH_DATA;
+		tmpSC->SCp.have_data_in = IO_IN;
+
+		nsp_pio_read(tmpSC);
+
+		break;
+
+	case BUSPHASE_STATUS:
+		nsp_dataphase_bypass(tmpSC);
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_STATUS");
+
+		tmpSC->SCp.phase = PH_STATUS;
+
+		tmpSC->SCp.Status = nsp_index_read(base, SCSIDATAWITHACK);
+		nsp_dbg(NSP_DEBUG_INTR, "message=0x%x status=0x%x", tmpSC->SCp.Message, tmpSC->SCp.Status);
+
+		break;
+
+	case BUSPHASE_MESSAGE_OUT:
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_MESSAGE_OUT");
+		if ((phase & BUSMON_REQ) == 0) {
+			goto timer_out;
+		}
+
+		tmpSC->SCp.phase = PH_MSG_OUT;
+
+		//*sync_neg = SYNC_NOT_YET;
+
+		data->MsgLen = i = 0;
+		data->MsgBuffer[i] = IDENTIFY(TRUE, lun); i++;
+
+		if (*sync_neg == SYNC_NOT_YET) {
+			data->Sync[target].SyncPeriod = 0;
+			data->Sync[target].SyncOffset = 0;
+
+			/**/
+			data->MsgBuffer[i] = MSG_EXTENDED; i++;
+			data->MsgBuffer[i] = 3;            i++;
+			data->MsgBuffer[i] = MSG_EXT_SDTR; i++;
+			data->MsgBuffer[i] = 0x0c;         i++;
+			data->MsgBuffer[i] = 15;           i++;
+			/**/
+		}
+		data->MsgLen = i;
+
+		nsp_analyze_sdtr(tmpSC);
+		show_message(data);
+		nsp_message_out(tmpSC);
+		break;
+
+	case BUSPHASE_MESSAGE_IN:
+		nsp_dataphase_bypass(tmpSC);
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE_MESSAGE_IN");
+		if ((phase & BUSMON_REQ) == 0) {
+			goto timer_out;
+		}
+
+		tmpSC->SCp.phase = PH_MSG_IN;
+		nsp_message_in(tmpSC);
+
+		/**/
+		if (*sync_neg == SYNC_NOT_YET) {
+			//nsp_dbg(NSP_DEBUG_INTR, "sync target=%d,lun=%d",target,lun);
+
+			if (data->MsgLen       >= 5            &&
+			    data->MsgBuffer[0] == MSG_EXTENDED &&
+			    data->MsgBuffer[1] == 3            &&
+			    data->MsgBuffer[2] == MSG_EXT_SDTR ) {
+				data->Sync[target].SyncPeriod = data->MsgBuffer[3];
+				data->Sync[target].SyncOffset = data->MsgBuffer[4];
+				//nsp_dbg(NSP_DEBUG_INTR, "sync ok, %d %d", data->MsgBuffer[3], data->MsgBuffer[4]);
+				*sync_neg = SYNC_OK;
+			} else {
+				data->Sync[target].SyncPeriod = 0;
+				data->Sync[target].SyncOffset = 0;
+				*sync_neg = SYNC_NG;
+			}
+			nsp_analyze_sdtr(tmpSC);
+		}
+		/**/
+
+		/* search last messeage byte */
+		tmp = -1;
+		for (i = 0; i < data->MsgLen; i++) {
+			tmp = data->MsgBuffer[i];
+			if (data->MsgBuffer[i] == MSG_EXTENDED) {
+				i += (1 + data->MsgBuffer[i+1]);
+			}
+		}
+		tmpSC->SCp.Message = tmp;
+
+		nsp_dbg(NSP_DEBUG_INTR, "message=0x%x len=%d", tmpSC->SCp.Message, data->MsgLen);
+		show_message(data);
+
+		break;
+
+	case BUSPHASE_SELECT:
+	default:
+		nsp_dbg(NSP_DEBUG_INTR, "BUSPHASE other");
+
+		break;
+	}
+
+	//nsp_dbg(NSP_DEBUG_INTR, "out");
+	return IRQ_HANDLED; 	
+
+timer_out:
+	nsp_start_timer(tmpSC, 1000/102);
+	return IRQ_HANDLED;
+}
+
+#ifdef NSP_DEBUG
+#include "nsp_debug.c"
+#endif	/* NSP_DEBUG */
+
+/*----------------------------------------------------------------*/
+/* look for ninja3 card and init if found			  */
+/*----------------------------------------------------------------*/
+static struct Scsi_Host *nsp_detect(Scsi_Host_Template *sht)
+{
+	struct Scsi_Host *host;	/* registered host structure */
+	nsp_hw_data *data_b = &nsp_data_base, *data;
+
+	nsp_dbg(NSP_DEBUG_INIT, "this_id=%d", sht->this_id);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	host = scsi_host_alloc(&nsp_driver_template, sizeof(nsp_hw_data));
+#else
+	host = scsi_register(sht, sizeof(nsp_hw_data));
+#endif
+	if (host == NULL) {
+		nsp_dbg(NSP_DEBUG_INIT, "host failed");
+		return NULL;
+	}
+
+	memcpy(host->hostdata, data_b, sizeof(nsp_hw_data));
+	data = (nsp_hw_data *)host->hostdata;
+	data->ScsiInfo->host = host;
+#ifdef NSP_DEBUG
+	data->CmdId = 0;
+#endif
+
+	nsp_dbg(NSP_DEBUG_INIT, "irq=%d,%d", data_b->IrqNumber, ((nsp_hw_data *)host->hostdata)->IrqNumber);
+
+	host->unique_id	  = data->BaseAddress;
+	host->io_port	  = data->BaseAddress;
+	host->n_io_port	  = data->NumAddress;
+	host->irq	  = data->IrqNumber;
+	host->base        = data->MmioAddress;
+
+	spin_lock_init(&(data->Lock));
+
+	snprintf(data->nspinfo,
+		 sizeof(data->nspinfo),
+		 "NinjaSCSI-3/32Bi Driver $Revision: 1.23 $ IO:0x%04lx-0x%04lx MMIO(virt addr):0x%04lx IRQ:%02d",
+		 host->io_port, host->io_port + host->n_io_port - 1,
+		 host->base,
+		 host->irq);
+	sht->name	  = data->nspinfo;
+
+	nsp_dbg(NSP_DEBUG_INIT, "end");
+
+
+	return host; /* detect done. */
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+static int nsp_detect_old(Scsi_Host_Template *sht)
+{
+	if (nsp_detect(sht) == NULL) {
+		return 0;
+	} else {
+		//MOD_INC_USE_COUNT;
+		return 1;
+	}
+}
+
+
+static int nsp_release_old(struct Scsi_Host *shpnt)
+{
+	//nsp_hw_data *data = (nsp_hw_data *)shpnt->hostdata;
+
+	/* PCMCIA Card Service dose same things below. */
+	/* So we do nothing.                           */
+	//if (shpnt->irq) {
+	//	free_irq(shpnt->irq, data->ScsiInfo);
+	//}
+	//if (shpnt->io_port) {
+	//	release_region(shpnt->io_port, shpnt->n_io_port);
+	//}
+
+	//MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* return info string						  */
+/*----------------------------------------------------------------*/
+static const char *nsp_info(struct Scsi_Host *shpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)shpnt->hostdata;
+
+	return data->nspinfo;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+        do { \
+		if(length > (pos - buffer)) { \
+			pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+			nsp_dbg(NSP_DEBUG_PROC, "buffer=0x%p pos=0x%p length=%d %d\n", buffer, pos, length,  length - (pos - buffer));\
+		} \
+	} while(0)
+static int
+nsp_proc_info(
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	struct Scsi_Host *host,
+#endif
+	char  *buffer,
+	char **start,
+	off_t  offset,
+	int    length,
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	int    hostno,
+#endif
+	int    inout)
+{
+	int id;
+	char *pos = buffer;
+	int thislength;
+	int speed;
+	unsigned long flags;
+	nsp_hw_data *data;
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	struct Scsi_Host *host;
+#else
+	int hostno;
+#endif
+	if (inout) {
+		return -EINVAL;
+	}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	hostno = host->host_no;
+#else
+	/* search this HBA host */
+	host = scsi_host_hn_get(hostno);
+	if (host == NULL) {
+		return -ESRCH;
+	}
+#endif
+	data = (nsp_hw_data *)host->hostdata;
+
+
+	SPRINTF("NinjaSCSI status\n\n");
+	SPRINTF("Driver version:        $Revision: 1.23 $\n");
+	SPRINTF("SCSI host No.:         %d\n",          hostno);
+	SPRINTF("IRQ:                   %d\n",          host->irq);
+	SPRINTF("IO:                    0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
+	SPRINTF("MMIO(virtual address): 0x%lx-0x%lx\n", host->base, host->base + data->MmioLength - 1);
+	SPRINTF("sg_tablesize:          %d\n",          host->sg_tablesize);
+
+	SPRINTF("burst transfer mode:   ");
+	switch (nsp_burst_mode) {
+	case BURST_IO8:
+		SPRINTF("io8");
+		break;
+	case BURST_IO32:
+		SPRINTF("io32");
+		break;
+	case BURST_MEM32:
+		SPRINTF("mem32");
+		break;
+	default:
+		SPRINTF("???");
+		break;
+	}
+	SPRINTF("\n");
+
+
+	spin_lock_irqsave(&(data->Lock), flags);
+	SPRINTF("CurrentSC:             0x%p\n\n",      data->CurrentSC);
+	spin_unlock_irqrestore(&(data->Lock), flags);
+
+	SPRINTF("SDTR status\n");
+	for(id = 0; id < ARRAY_SIZE(data->Sync); id++) {
+
+		SPRINTF("id %d: ", id);
+
+		if (id == host->this_id) {
+			SPRINTF("----- NinjaSCSI-3 host adapter\n");
+			continue;
+		}
+
+		switch(data->Sync[id].SyncNegotiation) {
+		case SYNC_OK:
+			SPRINTF(" sync");
+			break;
+		case SYNC_NG:
+			SPRINTF("async");
+			break;
+		case SYNC_NOT_YET:
+			SPRINTF(" none");
+			break;
+		default:
+			SPRINTF("?????");
+			break;
+		}
+
+		if (data->Sync[id].SyncPeriod != 0) {
+			speed = 1000000 / (data->Sync[id].SyncPeriod * 4);
+
+			SPRINTF(" transfer %d.%dMB/s, offset %d",
+				speed / 1000,
+				speed % 1000,
+				data->Sync[id].SyncOffset
+				);
+		}
+		SPRINTF("\n");
+	}
+
+	thislength = pos - (buffer + offset);
+
+	if(thislength < 0) {
+		*start = NULL;
+                return 0;
+        }
+
+
+	thislength = min(thislength, length);
+	*start = buffer + offset;
+
+	return thislength;
+}
+#undef SPRINTF
+
+/*---------------------------------------------------------------*/
+/* error handler                                                 */
+/*---------------------------------------------------------------*/
+
+/*static int nsp_eh_strategy(struct Scsi_Host *Shost)
+{
+	return FAILED;
+}*/
+
+/*
+static int nsp_eh_abort(Scsi_Cmnd *SCpnt)
+{
+	nsp_dbg(NSP_DEBUG_BUSRESET, "SCpnt=0x%p", SCpnt);
+
+	return nsp_eh_bus_reset(SCpnt);
+}*/
+
+/*
+static int nsp_eh_device_reset(Scsi_Cmnd *SCpnt)
+{
+	nsp_dbg(NSP_DEBUG_BUSRESET, "%s: SCpnt=0x%p", SCpnt);
+
+	return FAILED;
+}*/
+
+static int nsp_bus_reset(nsp_hw_data *data)
+{
+	unsigned int base = data->BaseAddress;
+	int	     i;
+
+	nsp_write(base, IRQCONTROL, IRQCONTROL_ALLMASK);
+
+	nsp_index_write(base, SCSIBUSCTRL, SCSI_RST);
+	mdelay(100); /* 100ms */
+	nsp_index_write(base, SCSIBUSCTRL, 0);
+	for(i = 0; i < 5; i++) {
+		nsp_index_read(base, IRQPHASESENCE); /* dummy read */
+	}
+
+	nsphw_init_sync(data);
+
+	nsp_write(base, IRQCONTROL, IRQCONTROL_ALLCLEAR);
+
+	return SUCCESS;
+}
+
+static int nsp_eh_bus_reset(Scsi_Cmnd *SCpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+	nsp_dbg(NSP_DEBUG_BUSRESET, "SCpnt=0x%p", SCpnt);
+
+	return nsp_bus_reset(data);
+}
+
+static int nsp_eh_host_reset(Scsi_Cmnd *SCpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+
+	nsp_dbg(NSP_DEBUG_BUSRESET, "in");
+
+	nsphw_init(data);
+
+	return SUCCESS;
+}
+
+
+/**********************************************************************
+  PCMCIA functions
+**********************************************************************/
+
+/*======================================================================
+    nsp_cs_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+    The dev_link structure is initialized, but we don't actually
+    configure the card at this point -- we wait until we receive a
+    card insertion event.
+======================================================================*/
+static dev_link_t *nsp_cs_attach(void)
+{
+	scsi_info_t  *info;
+	client_reg_t  client_reg;
+	dev_link_t   *link;
+	int	      ret;
+	nsp_hw_data  *data = &nsp_data_base;
+
+	nsp_dbg(NSP_DEBUG_INIT, "in");
+
+	/* Create new SCSI device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (info == NULL) { return NULL; }
+	memset(info, 0, sizeof(*info));
+	link = &info->link;
+	link->priv = info;
+	data->ScsiInfo = info;
+
+	nsp_dbg(NSP_DEBUG_INIT, "info=0x%p", info);
+
+	/* The io structure describes IO port mapping */
+	link->io.NumPorts1	 = 0x10;
+	link->io.Attributes1	 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.IOAddrLines	 = 10;	/* not used */
+
+	/* Interrupt setup */
+	link->irq.Attributes	 = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1	 = IRQ_LEVEL_ID;
+
+	/* Interrupt handler */
+	link->irq.Handler	 = &nspintr;
+	link->irq.Instance       = info;
+	link->irq.Attributes     |= (SA_SHIRQ | SA_SAMPLE_RANDOM);
+
+	/* General socket configuration */
+	link->conf.Attributes	 = CONF_ENABLE_IRQ;
+	link->conf.Vcc		 = 50;
+	link->conf.IntType	 = INT_MEMORY_AND_IO;
+	link->conf.Present	 = PRESENT_OPTION;
+
+
+	/* Register with Card Services */
+	link->next               = dev_list;
+	dev_list                 = link;
+	client_reg.dev_info	 = &dev_info;
+	client_reg.EventMask	 =
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+		CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET	|
+		CS_EVENT_PM_SUSPEND	| CS_EVENT_PM_RESUME	 ;
+	client_reg.event_handler = &nsp_cs_event;
+	client_reg.Version	 = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		nsp_cs_detach(link);
+		return NULL;
+	}
+
+
+	nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link);
+	return link;
+} /* nsp_cs_attach */
+
+
+/*======================================================================
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.	 If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+======================================================================*/
+static void nsp_cs_detach(dev_link_t *link)
+{
+	dev_link_t **linkp;
+
+	nsp_dbg(NSP_DEBUG_INIT, "in, link=0x%p", link);
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) {
+		if (*linkp == link) {
+			break;
+		}
+	}
+	if (*linkp == NULL) {
+		return;
+	}
+
+	if (link->state & DEV_CONFIG)
+		nsp_cs_release(link);
+
+	/* Break the link with Card Services */
+	if (link->handle) {
+		pcmcia_deregister_client(link->handle);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+	kfree(link->priv);
+	link->priv = NULL;
+
+} /* nsp_cs_detach */
+
+
+/*======================================================================
+    nsp_cs_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+======================================================================*/
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+/*====================================================================*/
+static void nsp_cs_config(dev_link_t *link)
+{
+	client_handle_t	  handle = link->handle;
+	scsi_info_t	 *info	 = link->priv;
+	tuple_t		  tuple;
+	cisparse_t	  parse;
+	int		  last_ret, last_fn;
+	unsigned char	  tuple_data[64];
+	config_info_t	  conf;
+	win_req_t         req;
+	memreq_t          map;
+	cistpl_cftable_entry_t dflt = { 0 };
+	struct Scsi_Host *host;
+	nsp_hw_data      *data = &nsp_data_base;
+#if !(LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+	Scsi_Device	 *dev;
+	dev_node_t	**tail, *node;
+#endif
+
+	nsp_dbg(NSP_DEBUG_INIT, "in");
+
+	tuple.DesiredTuple    = CISTPL_CONFIG;
+	tuple.Attributes      = 0;
+	tuple.TupleData	      = tuple_data;
+	tuple.TupleDataMax    = sizeof(tuple_data);
+	tuple.TupleOffset     = 0;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData,	pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple,	pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present    = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state	      |= DEV_CONFIG;
+
+	/* Look up the current Vcc */
+	CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+	link->conf.Vcc = conf.Vcc;
+
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	while (1) {
+		cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+
+		if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+				pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+			goto next_entry;
+
+		if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { dflt = *cfg; }
+		if (cfg->index == 0) { goto next_entry; }
+		link->conf.ConfigIndex = cfg->index;
+
+		/* Does this card need audio output? */
+		if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+			link->conf.Attributes |= CONF_ENABLE_SPKR;
+			link->conf.Status = CCSR_AUDIO_ENA;
+		}
+
+		/* Use power settings for Vcc and Vpp if present */
+		/*  Note that the CIS values need to be rescaled */
+		if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+			if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
+				goto next_entry;
+			}
+		} else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+			if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
+				goto next_entry;
+			}
+		}
+
+		if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+			link->conf.Vpp1 = link->conf.Vpp2 =
+				cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+		} else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+			link->conf.Vpp1 = link->conf.Vpp2 =
+				dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
+		}
+
+		/* Do we need to allocate an interrupt? */
+		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) {
+			link->conf.Attributes |= CONF_ENABLE_IRQ;
+		}
+
+		/* IO window settings */
+		link->io.NumPorts1 = link->io.NumPorts2 = 0;
+		if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+			cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+			link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+			if (!(io->flags & CISTPL_IO_8BIT))
+				link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+			if (!(io->flags & CISTPL_IO_16BIT))
+				link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+			link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+			link->io.BasePort1 = io->win[0].base;
+			link->io.NumPorts1 = io->win[0].len;
+			if (io->nwin > 1) {
+				link->io.Attributes2 = link->io.Attributes1;
+				link->io.BasePort2 = io->win[1].base;
+				link->io.NumPorts2 = io->win[1].len;
+			}
+			/* This reserves IO space but doesn't actually enable it */
+			if (pcmcia_request_io(link->handle, &link->io) != 0)
+				goto next_entry;
+		}
+
+		if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
+			cistpl_mem_t *mem =
+				(cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
+			req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+			req.Attributes |= WIN_ENABLE;
+			req.Base = mem->win[0].host_addr;
+			req.Size = mem->win[0].len;
+			if (req.Size < 0x1000) {
+				req.Size = 0x1000;
+			}
+			req.AccessSpeed = 0;
+			if (pcmcia_request_window(&link->handle, &req, &link->win) != 0)
+				goto next_entry;
+			map.Page = 0; map.CardOffset = mem->win[0].card_addr;
+			if (pcmcia_map_mem_page(link->win, &map) != 0)
+				goto next_entry;
+
+			data->MmioAddress = (unsigned long)ioremap_nocache(req.Base, req.Size);
+			data->MmioLength  = req.Size;
+		}
+		/* If we got this far, we're cool! */
+		break;
+
+	next_entry:
+		nsp_dbg(NSP_DEBUG_INIT, "next");
+
+		if (link->io.NumPorts1) {
+			pcmcia_release_io(link->handle, &link->io);
+		}
+		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+	}
+
+	if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+		CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	}
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+	if (free_ports) {
+		if (link->io.BasePort1) {
+			release_region(link->io.BasePort1, link->io.NumPorts1);
+		}
+		if (link->io.BasePort2) {
+			release_region(link->io.BasePort2, link->io.NumPorts2);
+		}
+	}
+
+	/* Set port and IRQ */
+	data->BaseAddress = link->io.BasePort1;
+	data->NumAddress  = link->io.NumPorts1;
+	data->IrqNumber   = link->irq.AssignedIRQ;
+
+	nsp_dbg(NSP_DEBUG_INIT, "I/O[0x%x+0x%x] IRQ %d",
+		data->BaseAddress, data->NumAddress, data->IrqNumber);
+
+	if(nsphw_init(data) == FALSE) {
+		goto cs_failed;
+	}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
+	host = nsp_detect(&nsp_driver_template);
+#else
+	scsi_register_host(&nsp_driver_template);
+	for (host = scsi_host_get_next(NULL); host != NULL;
+	     host = scsi_host_get_next(host)) {
+		if (host->hostt == &nsp_driver_template) {
+			break;
+		}
+	}
+#endif
+
+	if (host == NULL) {
+		nsp_dbg(NSP_DEBUG_INIT, "detect failed");
+		goto cs_failed;
+	}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+	scsi_add_host (host, NULL);
+	scsi_scan_host(host);
+
+	snprintf(info->node.dev_name, sizeof(info->node.dev_name), "scsi%d", host->host_no);
+	link->dev  = &info->node;
+	info->host = host;
+
+#else
+	nsp_dbg(NSP_DEBUG_INIT, "GET_SCSI_INFO");
+	tail = &link->dev;
+	info->ndev = 0;
+
+	nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host);
+
+	for (dev = host->host_queue; dev != NULL; dev = dev->next) {
+		unsigned long id;
+		id = (dev->id & 0x0f) + ((dev->lun & 0x0f) << 4) +
+			((dev->channel & 0x0f) << 8) +
+			((dev->host->host_no & 0x0f) << 12);
+		node = &info->node[info->ndev];
+		node->minor = 0;
+		switch (dev->type) {
+		case TYPE_TAPE:
+			node->major = SCSI_TAPE_MAJOR;
+			snprintf(node->dev_name, sizeof(node->dev_name), "st#%04lx", id);
+			break;
+		case TYPE_DISK:
+		case TYPE_MOD:
+			node->major = SCSI_DISK0_MAJOR;
+			snprintf(node->dev_name, sizeof(node->dev_name), "sd#%04lx", id);
+			break;
+		case TYPE_ROM:
+		case TYPE_WORM:
+			node->major = SCSI_CDROM_MAJOR;
+			snprintf(node->dev_name, sizeof(node->dev_name), "sr#%04lx", id);
+			break;
+		default:
+			node->major = SCSI_GENERIC_MAJOR;
+			snprintf(node->dev_name, sizeof(node->dev_name), "sg#%04lx", id);
+			break;
+		}
+		*tail = node; tail = &node->next;
+		info->ndev++;
+		info->host = dev->host;
+	}
+
+	*tail = NULL;
+	if (info->ndev == 0) {
+		nsp_msg(KERN_INFO, "no SCSI devices found");
+	}
+	nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host);
+#endif
+
+	/* Finally, report what we've done */
+	printk(KERN_INFO "nsp_cs: index 0x%02x: Vcc %d.%d",
+	       link->conf.ConfigIndex,
+	       link->conf.Vcc/10, link->conf.Vcc%10);
+	if (link->conf.Vpp1) {
+		printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+	}
+	if (link->conf.Attributes & CONF_ENABLE_IRQ) {
+		printk(", irq %d", link->irq.AssignedIRQ);
+	}
+	if (link->io.NumPorts1) {
+		printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+		       link->io.BasePort1+link->io.NumPorts1-1);
+	}
+	if (link->io.NumPorts2)
+		printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+		       link->io.BasePort2+link->io.NumPorts2-1);
+	if (link->win)
+		printk(", mem 0x%06lx-0x%06lx", req.Base,
+		       req.Base+req.Size-1);
+	printk("\n");
+
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+
+ cs_failed:
+	nsp_dbg(NSP_DEBUG_INIT, "config fail");
+	cs_error(link->handle, last_fn, last_ret);
+	nsp_cs_release(link);
+
+	return;
+} /* nsp_cs_config */
+#undef CS_CHECK
+
+
+/*======================================================================
+    After a card is removed, nsp_cs_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+======================================================================*/
+static void nsp_cs_release(dev_link_t *link)
+{
+	scsi_info_t *info = link->priv;
+	nsp_hw_data *data = NULL;
+
+	if (info->host == NULL) {
+		nsp_msg(KERN_DEBUG, "unexpected card release call.");
+	} else {
+		data = (nsp_hw_data *)info->host->hostdata;
+	}
+
+	nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link);
+
+	/* Unlink the device chain */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2))
+	if (info->host != NULL) {
+		scsi_remove_host(info->host);
+	}
+#else
+	scsi_unregister_host(&nsp_driver_template);
+#endif
+	link->dev = NULL;
+
+	if (link->win) {
+		if (data != NULL) {
+			iounmap((void *)(data->MmioAddress));
+		}
+		pcmcia_release_window(link->win);
+	}
+	pcmcia_release_configuration(link->handle);
+	if (link->io.NumPorts1) {
+		pcmcia_release_io(link->handle, &link->io);
+	}
+	if (link->irq.AssignedIRQ) {
+		pcmcia_release_irq(link->handle, &link->irq);
+	}
+	link->state &= ~DEV_CONFIG;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2))
+	if (info->host != NULL) {
+		scsi_host_put(info->host);
+	}
+#endif
+} /* nsp_cs_release */
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+    When a CARD_REMOVAL event is received, we immediately set a flag
+    to block future accesses to this device.  All the functions that
+    actually access the device should check this flag to make sure
+    the card is still present.
+
+======================================================================*/
+static int nsp_cs_event(event_t		       event,
+			int		       priority,
+			event_callback_args_t *args)
+{
+	dev_link_t  *link = args->client_data;
+	scsi_info_t *info = link->priv;
+	nsp_hw_data *data;
+
+	nsp_dbg(NSP_DEBUG_INIT, "in, event=0x%08x", event);
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		nsp_dbg(NSP_DEBUG_INIT, "event: remove");
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			((scsi_info_t *)link->priv)->stop = 1;
+			nsp_cs_release(link);
+		}
+		break;
+
+	case CS_EVENT_CARD_INSERTION:
+		nsp_dbg(NSP_DEBUG_INIT, "event: insert");
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,68))
+		info->bus    =  args->bus;
+#endif
+		nsp_cs_config(link);
+		break;
+
+	case CS_EVENT_PM_SUSPEND:
+		nsp_dbg(NSP_DEBUG_INIT, "event: suspend");
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		/* Mark the device as stopped, to block IO until later */
+		nsp_dbg(NSP_DEBUG_INIT, "event: reset physical");
+
+		if (info->host != NULL) {
+			nsp_msg(KERN_INFO, "clear SDTR status");
+
+			data = (nsp_hw_data *)info->host->hostdata;
+
+			nsphw_init_sync(data);
+		}
+
+		info->stop = 1;
+		if (link->state & DEV_CONFIG) {
+			pcmcia_release_configuration(link->handle);
+		}
+		break;
+
+	case CS_EVENT_PM_RESUME:
+		nsp_dbg(NSP_DEBUG_INIT, "event: resume");
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		nsp_dbg(NSP_DEBUG_INIT, "event: reset");
+		if (link->state & DEV_CONFIG) {
+			pcmcia_request_configuration(link->handle, &link->conf);
+		}
+		info->stop = 0;
+
+		if (info->host != NULL) {
+			nsp_msg(KERN_INFO, "reset host and bus");
+
+			data = (nsp_hw_data *)info->host->hostdata;
+
+			nsphw_init   (data);
+			nsp_bus_reset(data);
+		}
+
+		break;
+
+	default:
+		nsp_dbg(NSP_DEBUG_INIT, "event: unknown");
+		break;
+	}
+	nsp_dbg(NSP_DEBUG_INIT, "end");
+	return 0;
+} /* nsp_cs_event */
+
+/*======================================================================*
+ *	module entry point
+ *====================================================================*/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+static struct pcmcia_driver nsp_driver = {
+	.owner          = THIS_MODULE,
+	.drv            = {
+		.name   = "nsp_cs",
+	},
+	.attach         = nsp_cs_attach,
+	.detach         = nsp_cs_detach,
+};
+#endif
+
+static int __init nsp_cs_init(void)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+	nsp_msg(KERN_INFO, "loading...");
+
+	return pcmcia_register_driver(&nsp_driver);
+#else
+	servinfo_t serv;
+
+	nsp_msg(KERN_INFO, "loading...");
+	pcmcia_get_card_services_info(&serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		nsp_msg(KERN_DEBUG, "Card Services release does not match!");
+		return -EINVAL;
+	}
+	register_pcmcia_driver(&dev_info, &nsp_cs_attach, &nsp_cs_detach);
+
+	nsp_dbg(NSP_DEBUG_INIT, "out");
+	return 0;
+#endif
+}
+
+static void __exit nsp_cs_exit(void)
+{
+	nsp_msg(KERN_INFO, "unloading...");
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68))
+	pcmcia_unregister_driver(&nsp_driver);
+	BUG_ON(dev_list != NULL);
+#else
+	unregister_pcmcia_driver(&dev_info);
+	/* XXX: this really needs to move into generic code.. */
+	while (dev_list != NULL) {
+		if (dev_list->state & DEV_CONFIG) {
+			nsp_cs_release(dev_list);
+		}
+		nsp_cs_detach(dev_list);
+	}
+#endif
+}
+
+
+module_init(nsp_cs_init)
+module_exit(nsp_cs_exit)
+
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_cs.h b/drivers/scsi/pcmcia/nsp_cs.h
new file mode 100644
index 0000000..c201b52
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_cs.h
@@ -0,0 +1,472 @@
+/*=======================================================/
+  Header file for nsp_cs.c
+      By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+    Ver.1.0 : Cut unused lines.
+    Ver 0.1 : Initial version.
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License.
+
+=========================================================*/
+
+/* $Id: nsp_cs.h,v 1.19 2003/08/18 11:09:19 elca Exp $ */
+
+#ifndef  __nsp_cs__
+#define  __nsp_cs__
+
+/* for debugging */
+//#define NSP_DEBUG 9
+
+/*
+#define static
+#define inline
+*/
+
+/************************************
+ * Some useful macros...
+ */
+#define BIT(x)      (1L << (x))
+
+/* SCSI initiator must be ID 7 */
+#define NSP_INITIATOR_ID  7
+
+#define NSP_SELTIMEOUT 200
+
+/***************************************************************************
+ * register definitions
+ ***************************************************************************/
+/*========================================================================
+ * base register
+ ========================================================================*/
+#define	IRQCONTROL	0x00  /* R */
+#  define IRQCONTROL_RESELECT_CLEAR     BIT(0)
+#  define IRQCONTROL_PHASE_CHANGE_CLEAR BIT(1)
+#  define IRQCONTROL_TIMER_CLEAR        BIT(2)
+#  define IRQCONTROL_FIFO_CLEAR         BIT(3)
+#  define IRQCONTROL_ALLMASK            0xff
+#  define IRQCONTROL_ALLCLEAR           (IRQCONTROL_RESELECT_CLEAR     | \
+					 IRQCONTROL_PHASE_CHANGE_CLEAR | \
+					 IRQCONTROL_TIMER_CLEAR        | \
+					 IRQCONTROL_FIFO_CLEAR          )
+#  define IRQCONTROL_IRQDISABLE         0xf0
+
+#define	IRQSTATUS	0x00  /* W */
+#  define IRQSTATUS_SCSI  BIT(0)
+#  define IRQSTATUS_TIMER BIT(2)
+#  define IRQSTATUS_FIFO  BIT(3)
+#  define IRQSTATUS_MASK  0x0f
+
+#define	IFSELECT	0x01 /* W */
+#  define IF_IFSEL    BIT(0)
+#  define IF_REGSEL   BIT(2)
+
+#define	FIFOSTATUS	0x01 /* R */
+#  define FIFOSTATUS_CHIP_REVISION_MASK 0x0f
+#  define FIFOSTATUS_CHIP_ID_MASK       0x70
+#  define FIFOSTATUS_FULL_EMPTY         BIT(7)
+
+#define	INDEXREG	0x02 /* R/W */
+#define	DATAREG		0x03 /* R/W */
+#define	FIFODATA	0x04 /* R/W */
+#define	FIFODATA1	0x05 /* R/W */
+#define	FIFODATA2	0x06 /* R/W */
+#define	FIFODATA3	0x07 /* R/W */
+
+/*====================================================================
+ * indexed register
+ ====================================================================*/
+#define EXTBUSCTRL	0x10 /* R/W,deleted */
+
+#define CLOCKDIV	0x11 /* R/W */
+#  define CLOCK_40M 0x02
+#  define CLOCK_20M 0x01
+#  define FAST_20   BIT(2)
+
+#define TERMPWRCTRL	0x13 /* R/W */
+#  define POWER_ON BIT(0)
+
+#define SCSIIRQMODE	0x15 /* R/W */
+#  define SCSI_PHASE_CHANGE_EI BIT(0)
+#  define RESELECT_EI          BIT(4)
+#  define FIFO_IRQ_EI          BIT(5)
+#  define SCSI_RESET_IRQ_EI    BIT(6)
+
+#define IRQPHASESENCE	0x16 /* R */
+#  define LATCHED_MSG      BIT(0)
+#  define LATCHED_IO       BIT(1)
+#  define LATCHED_CD       BIT(2)
+#  define LATCHED_BUS_FREE BIT(3)
+#  define PHASE_CHANGE_IRQ BIT(4)
+#  define RESELECT_IRQ     BIT(5)
+#  define FIFO_IRQ         BIT(6)
+#  define SCSI_RESET_IRQ   BIT(7)
+
+#define TIMERCOUNT	0x17 /* R/W */
+
+#define SCSIBUSCTRL	0x18 /* R/W */
+#  define SCSI_SEL         BIT(0)
+#  define SCSI_RST         BIT(1)
+#  define SCSI_DATAOUT_ENB BIT(2)
+#  define SCSI_ATN         BIT(3)
+#  define SCSI_ACK         BIT(4)
+#  define SCSI_BSY         BIT(5)
+#  define AUTODIRECTION    BIT(6)
+#  define ACKENB           BIT(7)
+
+#define SCSIBUSMON	0x19 /* R */
+
+#define SETARBIT	0x1A /* W */
+#  define ARBIT_GO         BIT(0)
+#  define ARBIT_FLAG_CLEAR BIT(1)
+
+#define ARBITSTATUS	0x1A /* R */
+/*#  define ARBIT_GO        BIT(0)*/
+#  define ARBIT_WIN        BIT(1)
+#  define ARBIT_FAIL       BIT(2)
+#  define RESELECT_FLAG    BIT(3)
+
+#define PARITYCTRL	0x1B  /* W */
+#define PARITYSTATUS	0x1B  /* R */
+
+#define COMMANDCTRL	0x1C  /* W */
+#  define CLEAR_COMMAND_POINTER BIT(0)
+#  define AUTO_COMMAND_GO       BIT(1)
+
+#define RESELECTID	0x1C  /* R   */
+#define COMMANDDATA	0x1D  /* R/W */
+
+#define POINTERCLR	0x1E  /*   W */
+#  define POINTER_CLEAR      BIT(0)
+#  define ACK_COUNTER_CLEAR  BIT(1)
+#  define REQ_COUNTER_CLEAR  BIT(2)
+#  define HOST_COUNTER_CLEAR BIT(3)
+#  define READ_SOURCE        (BIT(4) | BIT(5))
+#    define ACK_COUNTER        (0)
+#    define REQ_COUNTER        (BIT(4))
+#    define HOST_COUNTER       (BIT(5))
+
+#define TRANSFERCOUNT	0x1E  /* R   */
+
+#define TRANSFERMODE	0x20  /* R/W */
+#  define MODE_MEM8   BIT(0)
+#  define MODE_MEM32  BIT(1)
+#  define MODE_ADR24  BIT(2)
+#  define MODE_ADR32  BIT(3)
+#  define MODE_IO8    BIT(4)
+#  define MODE_IO32   BIT(5)
+#  define TRANSFER_GO BIT(6)
+#  define BRAIND      BIT(7)
+
+#define SYNCREG		0x21 /* R/W */
+#  define SYNCREG_OFFSET_MASK  0x0f
+#  define SYNCREG_PERIOD_MASK  0xf0
+#  define SYNCREG_PERIOD_SHIFT 4
+
+#define SCSIDATALATCH	0x22 /*   W */
+#define SCSIDATAIN	0x22 /* R   */
+#define SCSIDATAWITHACK	0x23 /* R/W */
+#define SCAMCONTROL	0x24 /*   W */
+#define SCAMSTATUS	0x24 /* R   */
+#define SCAMDATA	0x25 /* R/W */
+
+#define OTHERCONTROL	0x26 /* R/W */
+#  define TPL_ROM_WRITE_EN BIT(0)
+#  define TPWR_OUT         BIT(1)
+#  define TPWR_SENSE       BIT(2)
+#  define RA8_CONTROL      BIT(3)
+
+#define ACKWIDTH	0x27 /* R/W */
+#define CLRTESTPNT	0x28 /*   W */
+#define ACKCNTLD	0x29 /*   W */
+#define REQCNTLD	0x2A /*   W */
+#define HSTCNTLD	0x2B /*   W */
+#define CHECKSUM	0x2C /* R/W */
+
+/************************************************************************
+ * Input status bit definitions.
+ ************************************************************************/
+#define S_MESSAGE	BIT(0)    /* Message line from SCSI bus      */
+#define S_IO		BIT(1)    /* Input/Output line from SCSI bus */
+#define S_CD		BIT(2)    /* Command/Data line from SCSI bus */
+#define S_BUSY		BIT(3)    /* Busy line from SCSI bus         */
+#define S_ACK		BIT(4)    /* Acknowlege line from SCSI bus   */
+#define S_REQUEST	BIT(5)    /* Request line from SCSI bus      */
+#define S_SELECT	BIT(6)	  /*                                 */
+#define S_ATN		BIT(7)	  /*                                 */
+
+/***********************************************************************
+ * Useful Bus Monitor status combinations.
+ ***********************************************************************/
+#define BUSMON_SEL         S_SELECT
+#define BUSMON_BSY         S_BUSY
+#define BUSMON_REQ         S_REQUEST
+#define BUSMON_IO          S_IO
+#define BUSMON_ACK         S_ACK
+#define BUSMON_BUS_FREE    0
+#define BUSMON_COMMAND     ( S_BUSY | S_CD |                    S_REQUEST )
+#define BUSMON_MESSAGE_IN  ( S_BUSY | S_CD | S_IO | S_MESSAGE | S_REQUEST )
+#define BUSMON_MESSAGE_OUT ( S_BUSY | S_CD |        S_MESSAGE | S_REQUEST )
+#define BUSMON_DATA_IN     ( S_BUSY |        S_IO |             S_REQUEST )
+#define BUSMON_DATA_OUT    ( S_BUSY |                           S_REQUEST )
+#define BUSMON_STATUS      ( S_BUSY | S_CD | S_IO |             S_REQUEST )
+#define BUSMON_SELECT      (                 S_IO |                        S_SELECT )
+#define BUSMON_RESELECT    (                 S_IO |                        S_SELECT )
+#define BUSMON_PHASE_MASK  (          S_CD | S_IO | S_MESSAGE |            S_SELECT )
+
+#define BUSPHASE_SELECT      ( BUSMON_SELECT      & BUSMON_PHASE_MASK )
+#define BUSPHASE_COMMAND     ( BUSMON_COMMAND     & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_IN  ( BUSMON_MESSAGE_IN  & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_IN     ( BUSMON_DATA_IN     & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_OUT    ( BUSMON_DATA_OUT    & BUSMON_PHASE_MASK )
+#define BUSPHASE_STATUS      ( BUSMON_STATUS      & BUSMON_PHASE_MASK )
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+	dev_link_t             link;
+	struct Scsi_Host      *host;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74))
+	dev_node_t             node;
+#else
+	int	               ndev;
+	dev_node_t             node[8];
+	struct bus_operations *bus;
+#endif
+	int                    stop;
+} scsi_info_t;
+
+
+/* synchronous transfer negotiation data */
+typedef struct _sync_data {
+	unsigned int SyncNegotiation;
+#define SYNC_NOT_YET 0
+#define SYNC_OK      1
+#define SYNC_NG      2
+
+	unsigned int  SyncPeriod;
+	unsigned int  SyncOffset;
+	unsigned char SyncRegister;
+	unsigned char AckWidth;
+} sync_data;
+
+typedef struct _nsp_hw_data {
+	unsigned int  BaseAddress;
+	unsigned int  NumAddress;
+	unsigned int  IrqNumber;
+
+	unsigned long MmioAddress;
+#define NSP_MMIO_OFFSET 0x0800
+	unsigned long MmioLength;
+
+	unsigned char ScsiClockDiv;
+
+	unsigned char TransferMode;
+
+	int           TimerCount;
+	int           SelectionTimeOut;
+	Scsi_Cmnd    *CurrentSC;
+	//int           CurrnetTarget;
+
+	int           FifoCount;
+
+#define MSGBUF_SIZE 20
+	unsigned char MsgBuffer[MSGBUF_SIZE];
+	int MsgLen;
+
+#define N_TARGET 8
+	sync_data     Sync[N_TARGET];
+
+	char nspinfo[110];     /* description */
+	spinlock_t Lock;
+
+	scsi_info_t   *ScsiInfo; /* attach <-> detect glue */
+
+
+#ifdef NSP_DEBUG
+	int CmdId; /* Accepted command serial number.
+		      Used for debugging.             */
+#endif
+} nsp_hw_data;
+
+
+/****************************************************************************
+ *
+ */
+
+/* Card service functions */
+static dev_link_t *nsp_cs_attach (void);
+static void        nsp_cs_detach (dev_link_t *link);
+static void        nsp_cs_release(dev_link_t *link);
+static void        nsp_cs_config (dev_link_t *link);
+static int         nsp_cs_event  (event_t event, int priority, event_callback_args_t *args);
+
+/* Linux SCSI subsystem specific functions */
+static struct Scsi_Host *nsp_detect     (Scsi_Host_Template *sht);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+static        int        nsp_detect_old (Scsi_Host_Template *sht);
+static        int        nsp_release_old(struct Scsi_Host *shpnt);
+#endif
+static const  char      *nsp_info       (struct Scsi_Host *shpnt);
+static        int        nsp_proc_info  (
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+	                                 struct Scsi_Host *host,
+#endif
+					 char   *buffer,
+					 char  **start,
+					 off_t   offset,
+					 int     length,
+#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+					 int     hostno,
+#endif
+					 int     inout);
+static        int        nsp_queuecommand(Scsi_Cmnd *SCpnt, void (* done)(Scsi_Cmnd *SCpnt));
+
+/* Error handler */
+/*static int nsp_eh_abort       (Scsi_Cmnd *SCpnt);*/
+/*static int nsp_eh_device_reset(Scsi_Cmnd *SCpnt);*/
+static int nsp_eh_bus_reset    (Scsi_Cmnd *SCpnt);
+static int nsp_eh_host_reset   (Scsi_Cmnd *SCpnt);
+static int nsp_bus_reset       (nsp_hw_data *data);
+
+/* */
+static int  nsphw_init           (nsp_hw_data *data);
+static int  nsphw_start_selection(Scsi_Cmnd *SCpnt);
+static void nsp_start_timer      (Scsi_Cmnd *SCpnt, int time);
+static int  nsp_fifo_count       (Scsi_Cmnd *SCpnt);
+static void nsp_pio_read         (Scsi_Cmnd *SCpnt);
+static void nsp_pio_write        (Scsi_Cmnd *SCpnt);
+static int  nsp_nexus            (Scsi_Cmnd *SCpnt);
+static void nsp_scsi_done        (Scsi_Cmnd *SCpnt);
+static int  nsp_analyze_sdtr     (Scsi_Cmnd *SCpnt);
+static int  nsp_negate_signal    (Scsi_Cmnd *SCpnt, unsigned char mask, char *str);
+static int  nsp_expect_signal    (Scsi_Cmnd *SCpnt, unsigned char current_phase, unsigned char  mask);
+static int  nsp_xfer             (Scsi_Cmnd *SCpnt, int phase);
+static int  nsp_dataphase_bypass (Scsi_Cmnd *SCpnt);
+static int  nsp_reselected       (Scsi_Cmnd *SCpnt);
+static struct Scsi_Host *nsp_detect(Scsi_Host_Template *sht);
+
+/* Interrupt handler */
+//static irqreturn_t nspintr(int irq, void *dev_id, struct pt_regs *regs);
+
+/* Module entry point*/
+static int  __init nsp_cs_init(void);
+static void __exit nsp_cs_exit(void);
+
+
+/* Debug */
+#ifdef NSP_DEBUG
+static void show_command (Scsi_Cmnd *SCpnt);
+static void show_phase   (Scsi_Cmnd *SCpnt);
+static void show_busphase(unsigned char stat);
+static void show_message (nsp_hw_data *data);
+#else
+# define show_command(ptr)   /* */
+# define show_phase(SCpnt)   /* */
+# define show_busphase(stat) /* */
+# define show_message(data)  /* */
+#endif
+
+/*
+ * SCSI phase
+ */
+enum _scsi_phase {
+	PH_UNDETERMINED ,
+	PH_ARBSTART     ,
+	PH_SELSTART     ,
+	PH_SELECTED     ,
+	PH_COMMAND      ,
+	PH_DATA         ,
+	PH_STATUS       ,
+	PH_MSG_IN       ,
+	PH_MSG_OUT      ,
+	PH_DISCONNECT   ,
+	PH_RESELECT     ,
+	PH_ABORT        ,
+	PH_RESET
+};
+
+enum _data_in_out {
+	IO_UNKNOWN,
+	IO_IN,
+	IO_OUT
+};
+
+enum _burst_mode {
+	BURST_IO8   = 0,
+	BURST_IO32  = 1,
+	BURST_MEM32 = 2,
+};
+
+
+/**************************************************************************
+ * SCSI messaage
+ */
+#define MSG_COMMAND_COMPLETE 0x00
+#define MSG_EXTENDED         0x01
+#define MSG_ABORT            0x06
+#define MSG_NO_OPERATION     0x08
+#define MSG_BUS_DEVICE_RESET 0x0c
+
+#define MSG_EXT_SDTR         0x01
+
+
+/**************************************************************************
+ * Compatibility functions
+ */
+
+/* for Kernel 2.4 */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+#  define scsi_register_host(template)   scsi_register_module(MODULE_SCSI_HA, template)
+#  define scsi_unregister_host(template) scsi_unregister_module(MODULE_SCSI_HA, template)
+#  define scsi_host_put(host)            scsi_unregister(host)
+
+typedef void irqreturn_t;
+#  define IRQ_NONE      /* */
+#  define IRQ_HANDLED   /* */
+#  define IRQ_RETVAL(x) /* */
+
+/* This is ad-hoc version of scsi_host_get_next() */
+static inline struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *host)
+{
+	if (host == NULL) {
+		return scsi_hostlist;
+	} else {
+		return host->next;
+	}
+}
+
+/* This is ad-hoc version of scsi_host_hn_get() */
+static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno)
+{
+	struct Scsi_Host *host;
+
+	for (host = scsi_host_get_next(NULL); host != NULL;
+	     host = scsi_host_get_next(host)) {
+		if (host->host_no == hostno) {
+			break;
+		}
+	}
+
+	return host;
+}
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+	pcmcia_report_error(handle, &err);
+}
+
+/* scatter-gather table */
+#  define BUFFER_ADDR (SCpnt->SCp.buffer->address)
+#endif
+
+/* for Kernel 2.6 */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+/* scatter-gather table */
+#  define BUFFER_ADDR ((char *)((unsigned int)(SCpnt->SCp.buffer->page) + SCpnt->SCp.buffer->offset))
+#endif
+
+#endif  /*__nsp_cs__*/
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_debug.c b/drivers/scsi/pcmcia/nsp_debug.c
new file mode 100644
index 0000000..62e5c60
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_debug.c
@@ -0,0 +1,215 @@
+/*========================================================================
+    Debug routines for nsp_cs
+      By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License.
+=========================================================================*/
+
+/* $Id: nsp_debug.c,v 1.3 2003/07/26 14:21:09 elca Exp $ */
+
+/*
+ * Show the command data of a command
+ */
+static const char unknown[] = "UNKNOWN";
+
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",  
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", 
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */  unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", 
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long",  "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same", 
+/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)", 
+/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP  0
+#define VENDOR_GROUP    1
+#define NOTEXT_GROUP    2
+
+static const char **commands[] = {
+    group_0_commands, group_1_commands, group_2_commands, 
+    (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, 
+    (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, 
+    (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcodek(unsigned char opcode)
+{
+	const char **table = commands[ group(opcode) ];
+
+	switch ((unsigned long) table) {
+	case RESERVED_GROUP:
+		printk("%s[%02x] ", reserved, opcode); 
+		break;
+	case NOTEXT_GROUP:
+		printk("%s(notext)[%02x] ", unknown, opcode); 
+		break;
+	case VENDOR_GROUP:
+		printk("%s[%02x] ", vendor, opcode); 
+		break;
+	default:
+		if (table[opcode & 0x1f] != unknown)
+			printk("%s[%02x] ", table[opcode & 0x1f], opcode);
+		else
+			printk("%s[%02x] ", unknown, opcode);
+		break;
+	}
+}
+
+static void print_commandk (unsigned char *command)
+{
+	int i, s;
+	printk(KERN_DEBUG);
+	print_opcodek(command[0]);
+	/*printk(KERN_DEBUG "%s ", __FUNCTION__);*/
+	if ((command[0] >> 5) == 6 ||
+	    (command[0] >> 5) == 7 ) {
+		s = 12; /* vender specific */
+	} else {
+		s = COMMAND_SIZE(command[0]);
+	}
+	for ( i = 1; i < s; ++i) {
+		printk("%02x ", command[i]);
+	}
+
+	switch (s) {
+	case 6:
+		printk("LBA=%d len=%d",
+		       (((unsigned int)command[1] & 0x0f) << 16) |
+		       ( (unsigned int)command[2]         <<  8) |
+		       ( (unsigned int)command[3]              ),
+		       (unsigned int)command[4]
+			);
+		break;
+	case 10:
+		printk("LBA=%d len=%d",
+		       ((unsigned int)command[2] << 24) |
+		       ((unsigned int)command[3] << 16) |
+		       ((unsigned int)command[4] <<  8) |
+		       ((unsigned int)command[5]      ),
+		       ((unsigned int)command[7] <<  8) |
+		       ((unsigned int)command[8]      )
+		       );
+		break;
+	case 12:
+		printk("LBA=%d len=%d",
+		       ((unsigned int)command[2] << 24) |
+		       ((unsigned int)command[3] << 16) |
+		       ((unsigned int)command[4] <<  8) |
+		       ((unsigned int)command[5]      ),
+		       ((unsigned int)command[6] << 24) |
+		       ((unsigned int)command[7] << 16) |
+		       ((unsigned int)command[8] <<  8) |
+		       ((unsigned int)command[9]      )
+		       );
+		break;
+	default:
+		break;
+	}
+	printk("\n");
+}
+
+static void show_command(Scsi_Cmnd *SCpnt)
+{
+	print_commandk(SCpnt->cmnd);
+}
+
+static void show_phase(Scsi_Cmnd *SCpnt)
+{
+	int i = SCpnt->SCp.phase;
+
+	char *ph[] = {
+		"PH_UNDETERMINED",
+		"PH_ARBSTART",
+		"PH_SELSTART",
+		"PH_SELECTED",
+		"PH_COMMAND",
+		"PH_DATA",
+		"PH_STATUS",
+		"PH_MSG_IN",
+		"PH_MSG_OUT",
+		"PH_DISCONNECT",
+		"PH_RESELECT"
+	};
+
+	if ( i < PH_UNDETERMINED || i > PH_RESELECT ) {
+		printk(KERN_DEBUG "scsi phase: unknown(%d)\n", i);
+		return;
+	}
+
+	printk(KERN_DEBUG "scsi phase: %s\n", ph[i]);
+
+	return;
+}
+
+static void show_busphase(unsigned char stat)
+{
+	switch(stat) {
+	case BUSPHASE_COMMAND:
+		printk(KERN_DEBUG "BUSPHASE_COMMAND\n");
+		break;
+	case BUSPHASE_MESSAGE_IN:
+		printk(KERN_DEBUG "BUSPHASE_MESSAGE_IN\n");
+		break;
+	case BUSPHASE_MESSAGE_OUT:
+		printk(KERN_DEBUG "BUSPHASE_MESSAGE_OUT\n");
+		break;
+	case BUSPHASE_DATA_IN:
+		printk(KERN_DEBUG "BUSPHASE_DATA_IN\n");
+		break;
+	case BUSPHASE_DATA_OUT:
+		printk(KERN_DEBUG "BUSPHASE_DATA_OUT\n");
+		break;
+	case BUSPHASE_STATUS:
+		printk(KERN_DEBUG "BUSPHASE_STATUS\n");
+		break;
+	case BUSPHASE_SELECT:
+		printk(KERN_DEBUG "BUSPHASE_SELECT\n");
+		break;
+	default:
+		printk(KERN_DEBUG "BUSPHASE_other\n");
+		break;
+	}
+}
+
+static void show_message(nsp_hw_data *data)
+{
+	int i;
+
+	printk(KERN_DEBUG "msg:");
+	for(i=0; i < data->MsgLen; i++) {
+		printk(" %02x", data->MsgBuffer[i]);
+	}
+	printk("\n");
+}
+
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_io.h b/drivers/scsi/pcmcia/nsp_io.h
new file mode 100644
index 0000000..3b8746f
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_io.h
@@ -0,0 +1,274 @@
+/*
+  NinjaSCSI I/O funtions 
+      By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+ 
+  This software may be used and distributed according to the terms of
+  the GNU General Public License.
+
+  */
+
+/* $Id: nsp_io.h,v 1.3 2003/08/04 21:15:26 elca Exp $ */
+
+#ifndef __NSP_IO_H__
+#define __NSP_IO_H__
+
+static inline          void nsp_write(unsigned int base,
+				      unsigned int index,
+				      unsigned char val);
+static inline unsigned char nsp_read(unsigned int base,
+				     unsigned int index);
+static inline          void nsp_index_write(unsigned int BaseAddr,
+					    unsigned int Register,
+					    unsigned char Value);
+static inline unsigned char nsp_index_read(unsigned int BaseAddr,
+					   unsigned int Register);
+
+/*******************************************************************
+ * Basic IO
+ */
+
+static inline void nsp_write(unsigned int  base,
+			     unsigned int  index,
+			     unsigned char val)
+{
+	outb(val, (base + index));
+}
+
+static inline unsigned char nsp_read(unsigned int base,
+				     unsigned int index)
+{
+	return inb(base + index);
+}
+
+
+/**********************************************************************
+ * Indexed IO
+ */
+static inline unsigned char nsp_index_read(unsigned int BaseAddr,
+					   unsigned int Register)
+{
+	outb(Register, BaseAddr + INDEXREG);
+	return inb(BaseAddr + DATAREG);
+}
+
+static inline void nsp_index_write(unsigned int  BaseAddr,
+				   unsigned int  Register,
+				   unsigned char Value)
+{
+	outb(Register, BaseAddr + INDEXREG);
+	outb(Value, BaseAddr + DATAREG);
+}
+
+/*********************************************************************
+ * fifo func
+ */
+
+/* read 8 bit FIFO */
+static inline void nsp_multi_read_1(unsigned int   BaseAddr,
+				    unsigned int   Register,
+				    void          *buf,
+				    unsigned long  count)
+{
+	insb(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo8_read(unsigned int   base,
+				  void          *buf,
+				  unsigned long  count)
+{
+	/*nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx", buf, count);*/
+	nsp_multi_read_1(base, FIFODATA, buf, count);
+}
+
+/*--------------------------------------------------------------*/
+
+/* read 16 bit FIFO */
+static inline void nsp_multi_read_2(unsigned int   BaseAddr,
+				    unsigned int   Register,
+				    void          *buf,
+				    unsigned long  count)
+{
+	insw(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo16_read(unsigned int   base,
+				   void          *buf,
+				   unsigned long  count)
+{
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*2", buf, count);
+	nsp_multi_read_2(base, FIFODATA, buf, count);
+}
+
+/*--------------------------------------------------------------*/
+
+/* read 32bit FIFO */
+static inline void nsp_multi_read_4(unsigned int   BaseAddr,
+				    unsigned int   Register,
+				    void          *buf,
+				    unsigned long  count)
+{
+	insl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo32_read(unsigned int   base,
+				   void          *buf,
+				   unsigned long  count)
+{
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+	nsp_multi_read_4(base, FIFODATA, buf, count);
+}
+
+/*----------------------------------------------------------*/
+
+/* write 8bit FIFO */
+static inline void nsp_multi_write_1(unsigned int   BaseAddr,
+				     unsigned int   Register,
+				     void          *buf,
+				     unsigned long  count)
+{
+	outsb(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo8_write(unsigned int   base,
+				   void          *buf,
+				   unsigned long  count)
+{
+	nsp_multi_write_1(base, FIFODATA, buf, count);
+}
+
+/*---------------------------------------------------------*/
+
+/* write 16bit FIFO */
+static inline void nsp_multi_write_2(unsigned int   BaseAddr,
+				     unsigned int   Register,
+				     void          *buf,
+				     unsigned long  count)
+{
+	outsw(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo16_write(unsigned int   base,
+				    void          *buf,
+				    unsigned long  count)
+{
+	nsp_multi_write_2(base, FIFODATA, buf, count);
+}
+
+/*---------------------------------------------------------*/
+
+/* write 32bit FIFO */
+static inline void nsp_multi_write_4(unsigned int   BaseAddr,
+				     unsigned int   Register,
+				     void          *buf,
+				     unsigned long  count)
+{
+	outsl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp_fifo32_write(unsigned int   base,
+				    void          *buf,
+				    unsigned long  count)
+{
+	nsp_multi_write_4(base, FIFODATA, buf, count);
+}
+
+
+/*====================================================================*/
+
+static inline void nsp_mmio_write(unsigned long base,
+				  unsigned int  index,
+				  unsigned char val)
+{
+	unsigned char *ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + index);
+
+	writeb(val, ptr);
+}
+
+static inline unsigned char nsp_mmio_read(unsigned long base,
+					  unsigned int  index)
+{
+	unsigned char *ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + index);
+
+	return readb(ptr);
+}
+
+/*-----------*/
+
+static inline unsigned char nsp_mmio_index_read(unsigned long base,
+						unsigned int  reg)
+{
+	unsigned char *index_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + INDEXREG);
+	unsigned char *data_ptr  = (unsigned char *)(base + NSP_MMIO_OFFSET + DATAREG);
+
+	writeb((unsigned char)reg, index_ptr);
+	return readb(data_ptr);
+}
+
+static inline void nsp_mmio_index_write(unsigned long base,
+					unsigned int  reg,
+					unsigned char val)
+{
+	unsigned char *index_ptr = (unsigned char *)(base + NSP_MMIO_OFFSET + INDEXREG);
+	unsigned char *data_ptr  = (unsigned char *)(base + NSP_MMIO_OFFSET + DATAREG);
+
+	writeb((unsigned char)reg, index_ptr);
+	writeb(val,                data_ptr);
+}
+
+/* read 32bit FIFO */
+static inline void nsp_mmio_multi_read_4(unsigned long  base,
+					 unsigned int   Register,
+					 void          *buf,
+					 unsigned long  count)
+{
+	unsigned long *ptr = (unsigned long *)(base + Register);
+	unsigned long *tmp = (unsigned long *)buf;
+	int i;
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "base 0x%0lx ptr 0x%p",base,ptr);
+
+	for (i = 0; i < count; i++) {
+		*tmp = readl(ptr);
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "<%d,%p,%p,%lx>", i, ptr, tmp, *tmp);
+		tmp++;
+	}
+}
+
+static inline void nsp_mmio_fifo32_read(unsigned int   base,
+					void          *buf,
+					unsigned long  count)
+{
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+	nsp_mmio_multi_read_4(base, FIFODATA, buf, count);
+}
+
+static inline void nsp_mmio_multi_write_4(unsigned long  base,
+					  unsigned int   Register,
+					  void          *buf,
+					  unsigned long  count)
+{
+	unsigned long *ptr = (unsigned long *)(base + Register);
+	unsigned long *tmp = (unsigned long *)buf;
+	int i;
+
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "base 0x%0lx ptr 0x%p",base,ptr);
+
+	for (i = 0; i < count; i++) {
+		writel(*tmp, ptr);
+		//nsp_dbg(NSP_DEBUG_DATA_IO, "<%d,%p,%p,%lx>", i, ptr, tmp, *tmp);
+		tmp++;
+	}
+}
+
+static inline void nsp_mmio_fifo32_write(unsigned int   base,
+					 void          *buf,
+					 unsigned long  count)
+{
+	//nsp_dbg(NSP_DEBUG_DATA_IO, "buf=0x%p, count=0x%lx*4", buf, count);
+	nsp_mmio_multi_write_4(base, FIFODATA, buf, count);
+}
+
+
+
+#endif
+/* end */
diff --git a/drivers/scsi/pcmcia/nsp_message.c b/drivers/scsi/pcmcia/nsp_message.c
new file mode 100644
index 0000000..d705773
--- /dev/null
+++ b/drivers/scsi/pcmcia/nsp_message.c
@@ -0,0 +1,78 @@
+/*==========================================================================
+  NinjaSCSI-3 message handler
+      By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+
+   This software may be used and distributed according to the terms of
+   the GNU General Public License.
+ */
+
+/* $Id: nsp_message.c,v 1.6 2003/07/26 14:21:09 elca Exp $ */
+
+static void nsp_message_in(Scsi_Cmnd *SCpnt)
+{
+	unsigned int  base = SCpnt->device->host->io_port;
+	nsp_hw_data  *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	unsigned char data_reg, control_reg;
+	int           ret, len;
+
+	/*
+	 * XXX: NSP QUIRK
+	 * NSP invoke interrupts only in the case of scsi phase changes,
+	 * therefore we should poll the scsi phase here to catch 
+	 * the next "msg in" if exists (no scsi phase changes).
+	 */
+	ret = 16;
+	len = 0;
+
+	nsp_dbg(NSP_DEBUG_MSGINOCCUR, "msgin loop");
+	do {
+		/* read data */
+		data_reg = nsp_index_read(base, SCSIDATAIN);
+
+		/* assert ACK */
+		control_reg = nsp_index_read(base, SCSIBUSCTRL);
+		control_reg |= SCSI_ACK;
+		nsp_index_write(base, SCSIBUSCTRL, control_reg);
+		nsp_negate_signal(SCpnt, BUSMON_REQ, "msgin<REQ>");
+
+		data->MsgBuffer[len] = data_reg; len++;
+
+		/* deassert ACK */
+		control_reg =  nsp_index_read(base, SCSIBUSCTRL);
+		control_reg &= ~SCSI_ACK;
+		nsp_index_write(base, SCSIBUSCTRL, control_reg);
+
+		/* catch a next signal */
+		ret = nsp_expect_signal(SCpnt, BUSPHASE_MESSAGE_IN, BUSMON_REQ);
+	} while (ret > 0 && MSGBUF_SIZE > len);
+
+	data->MsgLen = len;
+
+}
+
+static void nsp_message_out(Scsi_Cmnd *SCpnt)
+{
+	nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata;
+	int ret = 1;
+	int len = data->MsgLen;
+
+	/*
+	 * XXX: NSP QUIRK
+	 * NSP invoke interrupts only in the case of scsi phase changes,
+	 * therefore we should poll the scsi phase here to catch 
+	 * the next "msg out" if exists (no scsi phase changes).
+	 */
+
+	nsp_dbg(NSP_DEBUG_MSGOUTOCCUR, "msgout loop");
+	do {
+		if (nsp_xfer(SCpnt, BUSPHASE_MESSAGE_OUT)) {
+			nsp_msg(KERN_DEBUG, "msgout: xfer short");
+		}
+
+		/* catch a next signal */
+		ret = nsp_expect_signal(SCpnt, BUSPHASE_MESSAGE_OUT, BUSMON_REQ);
+	} while (ret > 0 && len-- > 0);
+
+}
+
+/* end */
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
new file mode 100644
index 0000000..4766bcd
--- /dev/null
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -0,0 +1,425 @@
+/*======================================================================
+
+    A driver for the Qlogic SCSI card
+
+    qlogic_cs.c 1.79 2000/06/12 21:27:26
+
+    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/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+#include <linux/interrupt.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include "../qlogicfas408.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ * tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ * drain
+ */
+#define INT_TYPE	0
+
+static char qlogic_name[] = "qlogic_cs";
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0644);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "qlogic_cs.c 1.79-ac 2002/10/26 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+static Scsi_Host_Template qlogicfas_driver_template = {
+	.module			= THIS_MODULE,
+	.name			= qlogic_name,
+	.proc_name		= qlogic_name,
+	.info			= qlogicfas408_info,
+	.queuecommand		= qlogicfas408_queuecommand,
+	.eh_abort_handler	= qlogicfas408_abort,
+	.eh_bus_reset_handler	= qlogicfas408_bus_reset,
+	.eh_device_reset_handler= qlogicfas408_device_reset,
+	.eh_host_reset_handler	= qlogicfas408_host_reset,
+	.bios_param		= qlogicfas408_biosparam,
+	.can_queue		= 1,
+	.this_id		= -1,
+	.sg_tablesize		= SG_ALL,
+	.cmd_per_lun		= 1,
+	.use_clustering		= DISABLE_CLUSTERING,
+};
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+	dev_link_t link;
+	dev_node_t node;
+	struct Scsi_Host *host;
+	unsigned short manf_id;
+} scsi_info_t;
+
+static void qlogic_release(dev_link_t *link);
+static int qlogic_event(event_t event, int priority, event_callback_args_t * args);
+
+static dev_link_t *qlogic_attach(void);
+static void qlogic_detach(dev_link_t *);
+
+
+static dev_link_t *dev_list = NULL;
+
+static dev_info_t dev_info = "qlogic_cs";
+
+static struct Scsi_Host *qlogic_detect(Scsi_Host_Template *host,
+				dev_link_t *link, int qbase, int qlirq)
+{
+	int qltyp;		/* type of chip */
+	int qinitid;
+	struct Scsi_Host *shost;	/* registered host structure */
+	struct qlogicfas408_priv *priv;
+
+	qltyp = qlogicfas408_get_chip_type(qbase, INT_TYPE);
+	qinitid = host->this_id;
+	if (qinitid < 0)
+		qinitid = 7;	/* if no ID, use 7 */
+
+	qlogicfas408_setup(qbase, qinitid, INT_TYPE);
+
+	host->name = qlogic_name;
+	shost = scsi_host_alloc(host, sizeof(struct qlogicfas408_priv));
+	if (!shost)
+		goto err;
+	shost->io_port = qbase;
+	shost->n_io_port = 16;
+	shost->dma_channel = -1;
+	if (qlirq != -1)
+		shost->irq = qlirq;
+
+	priv = get_priv_by_host(shost);
+	priv->qlirq = qlirq;
+	priv->qbase = qbase;
+	priv->qinitid = qinitid;
+	priv->shost = shost;
+	priv->int_type = INT_TYPE;					
+
+	if (request_irq(qlirq, qlogicfas408_ihandl, 0, qlogic_name, shost))
+		goto free_scsi_host;
+
+	sprintf(priv->qinfo,
+		"Qlogicfas Driver version 0.46, chip %02X at %03X, IRQ %d, TPdma:%d",
+		qltyp, qbase, qlirq, QL_TURBO_PDMA);
+
+	if (scsi_add_host(shost, NULL))
+		goto free_interrupt;
+
+	scsi_scan_host(shost);
+
+	return shost;
+
+free_interrupt:
+	free_irq(qlirq, shost);
+
+free_scsi_host:
+	scsi_host_put(shost);
+	
+err:
+	return NULL;
+}
+static dev_link_t *qlogic_attach(void)
+{
+	scsi_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int ret;
+
+	DEBUG(0, "qlogic_attach()\n");
+
+	/* Create new SCSI device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+	link = &info->link;
+	link->priv = info;
+	link->io.NumPorts1 = 16;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.IOAddrLines = 10;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.event_handler = &qlogic_event;
+	client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != 0) {
+		cs_error(link->handle, RegisterClient, ret);
+		qlogic_detach(link);
+		return NULL;
+	}
+
+	return link;
+}				/* qlogic_attach */
+
+/*====================================================================*/
+
+static void qlogic_detach(dev_link_t * link)
+{
+	dev_link_t **linkp;
+
+	DEBUG(0, "qlogic_detach(0x%p)\n", link);
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+	if (*linkp == NULL)
+		return;
+
+	if (link->state & DEV_CONFIG)
+		qlogic_release(link);
+
+	if (link->handle)
+		pcmcia_deregister_client(link->handle);
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+	kfree(link->priv);
+
+}				/* qlogic_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void qlogic_config(dev_link_t * link)
+{
+	client_handle_t handle = link->handle;
+	scsi_info_t *info = link->priv;
+	tuple_t tuple;
+	cisparse_t parse;
+	int i, last_ret, last_fn;
+	unsigned short tuple_data[32];
+	struct Scsi_Host *host;
+
+	DEBUG(0, "qlogic_config(0x%p)\n", link);
+
+	tuple.TupleData = (cisdata_t *) tuple_data;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigBase = parse.config.base;
+
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+		info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	while (1) {
+		if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+				pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+			goto next_entry;
+		link->conf.ConfigIndex = parse.cftable_entry.index;
+		link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+		link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+		if (link->io.BasePort1 != 0) {
+			i = pcmcia_request_io(handle, &link->io);
+			if (i == CS_SUCCESS)
+				break;
+		}
+	      next_entry:
+		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+	}
+
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+	if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
+		/* set ATAcmd */
+		outb(0xb4, link->io.BasePort1 + 0xd);
+		outb(0x24, link->io.BasePort1 + 0x9);
+		outb(0x04, link->io.BasePort1 + 0xd);
+	}
+
+	/* The KXL-810AN has a bigger IO port window */
+	if (link->io.NumPorts1 == 32)
+		host = qlogic_detect(&qlogicfas_driver_template, link,
+			link->io.BasePort1 + 16, link->irq.AssignedIRQ);
+	else
+		host = qlogic_detect(&qlogicfas_driver_template, link,
+			link->io.BasePort1, link->irq.AssignedIRQ);
+	
+	if (!host) {
+		printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name);
+		goto out;
+	}
+
+	sprintf(info->node.dev_name, "scsi%d", host->host_no);
+	link->dev = &info->node;
+	info->host = host;
+
+out:
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+	link->dev = NULL;
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+	link->state &= ~DEV_CONFIG;
+	return;
+
+}				/* qlogic_config */
+
+/*====================================================================*/
+
+static void qlogic_release(dev_link_t *link)
+{
+	scsi_info_t *info = link->priv;
+
+	DEBUG(0, "qlogic_release(0x%p)\n", link);
+
+	scsi_remove_host(info->host);
+	link->dev = NULL;
+
+	free_irq(link->irq.AssignedIRQ, info->host);
+
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+
+	scsi_host_put(info->host);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int qlogic_event(event_t event, int priority, event_callback_args_t * args)
+{
+	dev_link_t *link = args->client_data;
+
+	DEBUG(1, "qlogic_event(0x%06x)\n", event);
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG)
+			qlogic_release(link);
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		qlogic_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			pcmcia_release_configuration(link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (link->state & DEV_CONFIG) {
+			scsi_info_t *info = link->priv;
+			pcmcia_request_configuration(link->handle, &link->conf);
+			if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) {
+				outb(0x80, link->io.BasePort1 + 0xd);
+				outb(0x24, link->io.BasePort1 + 0x9);
+				outb(0x04, link->io.BasePort1 + 0xd);
+			}
+			/* Ugggglllyyyy!!! */
+			qlogicfas408_bus_reset(NULL);
+		}
+		break;
+	}
+	return 0;
+}				/* qlogic_event */
+
+
+static struct pcmcia_driver qlogic_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+	.name		= "qlogic_cs",
+	},
+	.attach		= qlogic_attach,
+	.detach		= qlogic_detach,
+};
+
+static int __init init_qlogic_cs(void)
+{
+	return pcmcia_register_driver(&qlogic_cs_driver);
+}
+
+static void __exit exit_qlogic_cs(void)
+{
+	pcmcia_unregister_driver(&qlogic_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
+MODULE_LICENSE("GPL");
+module_init(init_qlogic_cs);
+module_exit(exit_qlogic_cs);
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
new file mode 100644
index 0000000..8457d0d
--- /dev/null
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -0,0 +1,1022 @@
+/*
+*  sym53c500_cs.c	Bob Tracy (rct@frus.com)
+*
+*  A rewrite of the pcmcia-cs add-on driver for newer (circa 1997)
+*  New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic
+*  53c500 controller: intended for use with 2.6 and later kernels.
+*  The pcmcia-cs add-on version of this driver is not supported
+*  beyond 2.4.  It consisted of three files with history/copyright
+*  information as follows:
+*
+*  SYM53C500.h
+*	Bob Tracy (rct@frus.com)
+*	Original by Tom Corner (tcorner@via.at).
+*	Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+*	Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+*  SYM53C500.c
+*	Bob Tracy (rct@frus.com)
+*	Original driver by Tom Corner (tcorner@via.at) was adapted
+*	from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996 
+*	Normunds Saumanis (normunds@fi.ibm.com)
+*
+*  sym53c500.c
+*	Bob Tracy (rct@frus.com)
+*	Original by Tom Corner (tcorner@via.at) was adapted from a
+*	driver for the Qlogic SCSI card written by
+*	David Hinds (dhinds@allegro.stanford.edu).
+* 
+*  This program is free software; you can redistribute it and/or modify it
+*  under the terms of the GNU General Public License as published by the
+*  Free Software Foundation; either version 2, 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.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/*
+*  Set this to 0 if you encounter kernel lockups while transferring 
+*  data in PIO mode.  Note this can be changed via "sysfs".
+*/
+#define USE_FAST_PIO 1
+
+/* =============== End of user configurable parameters ============== */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* ================================================================== */
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.9c 2004/10/27 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ================================================================== */
+
+#define SYNC_MODE 0 		/* Synchronous transfer mode */
+
+/* Default configuration */
+#define C1_IMG   0x07		/* ID=7 */
+#define C2_IMG   0x48		/* FE SCSI2 */
+#define C3_IMG   0x20		/* CDB */
+#define C4_IMG   0x04		/* ANE */
+#define C5_IMG   0xa4		/* ? changed from b6= AA PI SIE POL */
+#define C7_IMG   0x80		/* added for SYM53C500 t. corner */
+
+/* Hardware Registers: offsets from io_port (base) */
+
+/* Control Register Set 0 */
+#define TC_LSB		0x00		/* transfer counter lsb */
+#define TC_MSB		0x01		/* transfer counter msb */
+#define SCSI_FIFO	0x02		/* scsi fifo register */
+#define CMD_REG		0x03		/* command register */
+#define STAT_REG	0x04		/* status register */
+#define DEST_ID		0x04		/* selection/reselection bus id */
+#define INT_REG		0x05		/* interrupt status register */
+#define SRTIMOUT	0x05		/* select/reselect timeout reg */
+#define SEQ_REG		0x06		/* sequence step register */
+#define SYNCPRD		0x06		/* synchronous transfer period */
+#define FIFO_FLAGS	0x07		/* indicates # of bytes in fifo */
+#define SYNCOFF		0x07		/* synchronous offset register */
+#define CONFIG1		0x08		/* configuration register */
+#define CLKCONV		0x09		/* clock conversion register */
+/* #define TESTREG	0x0A */		/* test mode register */
+#define CONFIG2		0x0B		/* configuration 2 register */
+#define CONFIG3		0x0C		/* configuration 3 register */
+#define CONFIG4		0x0D		/* configuration 4 register */
+#define TC_HIGH		0x0E		/* transfer counter high */
+/* #define FIFO_BOTTOM	0x0F */		/* reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/* #define JUMPER_SENSE	0x00 */		/* jumper sense port reg (r/w) */
+/* #define SRAM_PTR	0x01 */		/* SRAM address pointer reg (r/w) */
+/* #define SRAM_DATA	0x02 */		/* SRAM data register (r/w) */
+#define PIO_FIFO	0x04		/* PIO FIFO registers (r/w) */
+/* #define PIO_FIFO1	0x05 */		/*  */
+/* #define PIO_FIFO2	0x06 */		/*  */
+/* #define PIO_FIFO3	0x07 */		/*  */
+#define PIO_STATUS	0x08		/* PIO status (r/w) */
+/* #define ATA_CMD	0x09 */		/* ATA command/status reg (r/w) */
+/* #define ATA_ERR	0x0A */		/* ATA features/error reg (r/w) */
+#define PIO_FLAG	0x0B		/* PIO flag interrupt enable (r/w) */
+#define CONFIG5		0x09		/* configuration 5 register */
+/* #define SIGNATURE	0x0E */		/* signature register (r) */
+/* #define CONFIG6	0x0F */		/* configuration 6 register (r) */
+#define CONFIG7		0x0d
+
+/* select register set 0 */
+#define REG0(x)		(outb(C4_IMG, (x) + CONFIG4))
+/* select register set 1 */
+#define REG1(x)		outb(C7_IMG, (x) + CONFIG7); outb(C5_IMG, (x) + CONFIG5)
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(x, count) \
+  outb(count & 0xff, (x) + TC_LSB); \
+  outb((count >> 8) & 0xff, (x) + TC_MSB); \
+  outb((count >> 16) & 0xff, (x) + TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP               0x80
+
+#define SCSI_NOP             0x00
+#define FLUSH_FIFO           0x01
+#define CHIP_RESET           0x02
+#define SCSI_RESET           0x03
+#define RESELECT             0x40
+#define SELECT_NO_ATN        0x41
+#define SELECT_ATN           0x42
+#define SELECT_ATN_STOP      0x43
+#define ENABLE_SEL           0x44
+#define DISABLE_SEL          0x45
+#define SELECT_ATN3          0x46
+#define RESELECT3            0x47
+#define TRANSFER_INFO        0x10
+#define INIT_CMD_COMPLETE    0x11
+#define MSG_ACCEPT           0x12
+#define TRANSFER_PAD         0x18
+#define SET_ATN              0x1a
+#define RESET_ATN            0x1b
+#define SEND_MSG             0x20
+#define SEND_STATUS          0x21
+#define SEND_DATA            0x22
+#define DISCONN_SEQ          0x23
+#define TERMINATE_SEQ        0x24
+#define TARG_CMD_COMPLETE    0x25
+#define DISCONN              0x27
+#define RECV_MSG             0x28
+#define RECV_CMD             0x29
+#define RECV_DATA            0x2a
+#define RECV_CMD_SEQ         0x2b
+#define TARGET_ABORT_DMA     0x04
+
+/* ================================================================== */
+
+struct scsi_info_t {
+	dev_link_t link;
+	dev_node_t node;
+	struct Scsi_Host *host;
+	unsigned short manf_id;
+};
+
+/*
+*  Repository for per-instance host data.
+*/
+struct sym53c500_data {
+	struct scsi_cmnd *current_SC;
+	int fast_pio;
+};
+
+enum Phase {
+    idle,
+    data_out,
+    data_in,
+    command_ph,
+    status_ph,
+    message_out,
+    message_in
+};
+
+/* ================================================================== */
+
+/*
+*  Global (within this module) variables other than
+*  sym53c500_driver_template (the scsi_host_template).
+*/
+static dev_link_t *dev_list;
+static dev_info_t dev_info = "sym53c500_cs";
+
+/* ================================================================== */
+
+static void
+chip_init(int io_port)
+{
+	REG1(io_port);
+	outb(0x01, io_port + PIO_STATUS);
+	outb(0x00, io_port + PIO_FLAG);
+
+	outb(C4_IMG, io_port + CONFIG4);	/* REG0(io_port); */
+	outb(C3_IMG, io_port + CONFIG3);
+	outb(C2_IMG, io_port + CONFIG2);
+	outb(C1_IMG, io_port + CONFIG1);
+
+	outb(0x05, io_port + CLKCONV);	/* clock conversion factor */
+	outb(0x9C, io_port + SRTIMOUT);	/* Selection timeout */
+	outb(0x05, io_port + SYNCPRD);	/* Synchronous transfer period */
+	outb(SYNC_MODE, io_port + SYNCOFF);	/* synchronous mode */  
+}
+
+static void
+SYM53C500_int_host_reset(int io_port)
+{
+	outb(C4_IMG, io_port + CONFIG4);	/* REG0(io_port); */
+	outb(CHIP_RESET, io_port + CMD_REG);
+	outb(SCSI_NOP, io_port + CMD_REG);	/* required after reset */
+	outb(SCSI_RESET, io_port + CMD_REG);
+	chip_init(io_port);
+}
+
+static __inline__ int
+SYM53C500_pio_read(int fast_pio, int base, unsigned char *request, unsigned int reqlen)
+{
+	int i;
+	int len;	/* current scsi fifo size */
+
+	REG1(base);
+	while (reqlen) {
+		i = inb(base + PIO_STATUS);
+		/* VDEB(printk("pio_status=%x\n", i)); */
+		if (i & 0x80) 
+			return 0;
+
+		switch (i & 0x1e) {
+		default:
+		case 0x10:	/* fifo empty */
+			len = 0;
+			break;
+		case 0x0:
+			len = 1;
+			break; 
+		case 0x8:	/* fifo 1/3 full */
+			len = 42;
+			break;
+		case 0xc:	/* fifo 2/3 full */
+			len = 84;
+			break;
+		case 0xe:	/* fifo full */
+			len = 128;
+			break;
+		}
+
+		if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+			return 0;
+		}
+
+		if (len) {
+			if (len > reqlen) 
+				len = reqlen;
+
+			if (fast_pio && len > 3) {
+				insl(base + PIO_FIFO, request, len >> 2);
+				request += len & 0xfc; 
+				reqlen -= len & 0xfc; 
+			} else {
+				while (len--) {
+					*request++ = inb(base + PIO_FIFO);
+					reqlen--;
+				}
+			} 
+		}
+	}
+	return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(int fast_pio, int base, unsigned char *request, unsigned int reqlen)
+{
+	int i = 0;
+	int len;	/* current scsi fifo size */
+
+	REG1(base);
+	while (reqlen && !(i & 0x40)) {
+		i = inb(base + PIO_STATUS);
+		/* VDEB(printk("pio_status=%x\n", i)); */
+		if (i & 0x80)	/* error */
+			return 0;
+
+		switch (i & 0x1e) {
+		case 0x10:
+			len = 128;
+			break;
+		case 0x0:
+			len = 84;
+			break;
+		case 0x8:
+			len = 42;
+			break;
+		case 0xc:
+			len = 1;
+			break;
+		default:
+		case 0xe:
+			len = 0;
+			break;
+		}
+
+		if (len) {
+			if (len > reqlen)
+				len = reqlen;
+
+			if (fast_pio && len > 3) {
+				outsl(base + PIO_FIFO, request, len >> 2);
+				request += len & 0xfc;
+				reqlen -= len & 0xfc;
+			} else {
+				while (len--) {
+					outb(*request++, base + PIO_FIFO);
+					reqlen--;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static irqreturn_t
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+	struct Scsi_Host *dev = dev_id;
+	DEB(unsigned char fifo_size;)
+	DEB(unsigned char seq_reg;)
+	unsigned char status, int_reg;
+	unsigned char pio_status;
+	struct scatterlist *sglist;
+	unsigned int sgcount;
+	int port_base = dev->io_port;
+	struct sym53c500_data *data =
+	    (struct sym53c500_data *)dev->hostdata;
+	struct scsi_cmnd *curSC = data->current_SC;
+	int fast_pio = data->fast_pio;
+
+	spin_lock_irqsave(dev->host_lock, flags);
+
+	VDEB(printk("SYM53C500_intr called\n"));
+
+	REG1(port_base);
+	pio_status = inb(port_base + PIO_STATUS);
+	REG0(port_base);
+	status = inb(port_base + STAT_REG);
+	DEB(seq_reg = inb(port_base + SEQ_REG));
+	int_reg = inb(port_base + INT_REG);
+	DEB(fifo_size = inb(port_base + FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+	printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x", 
+	    status, seq_reg, int_reg, fifo_size);
+	printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+	if (int_reg & 0x80) {	/* SCSI reset intr */
+		DEB(printk("SYM53C500: reset intr received\n"));
+		curSC->result = DID_RESET << 16;
+		goto idle_out;
+	}
+
+	if (pio_status & 0x80) {
+		printk("SYM53C500: Warning: PIO error!\n");
+		curSC->result = DID_ERROR << 16;
+		goto idle_out;
+	}
+
+	if (status & 0x20) {		/* Parity error */
+		printk("SYM53C500: Warning: parity error!\n");
+		curSC->result = DID_PARITY << 16;
+		goto idle_out;
+	}
+
+	if (status & 0x40) {		/* Gross error */
+		printk("SYM53C500: Warning: gross error!\n");
+		curSC->result = DID_ERROR << 16;
+		goto idle_out;
+	}
+
+	if (int_reg & 0x20) {		/* Disconnect */
+		DEB(printk("SYM53C500: disconnect intr received\n"));
+		if (curSC->SCp.phase != message_in) {	/* Unexpected disconnect */
+			curSC->result = DID_NO_CONNECT << 16;
+		} else {	/* Command complete, return status and message */
+			curSC->result = (curSC->SCp.Status & 0xff)
+			    | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+		}
+		goto idle_out;
+	}
+
+	switch (status & 0x07) {	/* scsi phase */
+	case 0x00:			/* DATA-OUT */
+		if (int_reg & 0x10) {	/* Target requesting info transfer */
+			curSC->SCp.phase = data_out;
+			VDEB(printk("SYM53C500: Data-Out phase\n"));
+			outb(FLUSH_FIFO, port_base + CMD_REG);
+			LOAD_DMA_COUNT(port_base, curSC->request_bufflen);	/* Max transfer size */
+			outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+			if (!curSC->use_sg)	/* Don't use scatter-gather */
+				SYM53C500_pio_write(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen);
+			else {	/* use scatter-gather */
+				sgcount = curSC->use_sg;
+				sglist = curSC->request_buffer;
+				while (sgcount--) {
+					SYM53C500_pio_write(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+					sglist++;
+				}
+			}
+			REG0(port_base);
+		}
+		break;
+
+	case 0x01:		/* DATA-IN */
+		if (int_reg & 0x10) {	/* Target requesting info transfer */
+			curSC->SCp.phase = data_in;
+			VDEB(printk("SYM53C500: Data-In phase\n"));
+			outb(FLUSH_FIFO, port_base + CMD_REG);
+			LOAD_DMA_COUNT(port_base, curSC->request_bufflen);	/* Max transfer size */
+			outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+			if (!curSC->use_sg)	/* Don't use scatter-gather */
+				SYM53C500_pio_read(fast_pio, port_base, curSC->request_buffer, curSC->request_bufflen);
+			else {	/* Use scatter-gather */
+				sgcount = curSC->use_sg;
+				sglist = curSC->request_buffer;
+				while (sgcount--) {
+					SYM53C500_pio_read(fast_pio, port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+					sglist++;
+				}
+			}
+			REG0(port_base);
+		}
+		break;
+
+	case 0x02:		/* COMMAND */
+		curSC->SCp.phase = command_ph;
+		printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+		break;
+
+	case 0x03:		/* STATUS */
+		curSC->SCp.phase = status_ph;
+		VDEB(printk("SYM53C500: Status phase\n"));
+		outb(FLUSH_FIFO, port_base + CMD_REG);
+		outb(INIT_CMD_COMPLETE, port_base + CMD_REG);
+		break;
+
+	case 0x04:		/* Reserved */
+	case 0x05:		/* Reserved */
+		printk("SYM53C500: WARNING: Reserved phase!!!\n");
+		break;
+
+	case 0x06:		/* MESSAGE-OUT */
+		DEB(printk("SYM53C500: Message-Out phase\n"));
+		curSC->SCp.phase = message_out;
+		outb(SET_ATN, port_base + CMD_REG);	/* Reject the message */
+		outb(MSG_ACCEPT, port_base + CMD_REG);
+		break;
+
+	case 0x07:		/* MESSAGE-IN */
+		VDEB(printk("SYM53C500: Message-In phase\n"));
+		curSC->SCp.phase = message_in;
+
+		curSC->SCp.Status = inb(port_base + SCSI_FIFO);
+		curSC->SCp.Message = inb(port_base + SCSI_FIFO);
+
+		VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f));
+		DEB(printk("Status = %02x  Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message));
+
+		if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) {
+			outb(SET_ATN, port_base + CMD_REG);	/* Reject message */
+			DEB(printk("Discarding SAVE_POINTERS message\n"));
+		}
+		outb(MSG_ACCEPT, port_base + CMD_REG);
+		break;
+	}
+out:
+	spin_unlock_irqrestore(dev->host_lock, flags);
+	return IRQ_HANDLED;
+
+idle_out:
+	curSC->SCp.phase = idle;
+	curSC->scsi_done(curSC);
+	goto out;
+}
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+	struct scsi_info_t *info = link->priv;
+	struct Scsi_Host *shost = info->host;
+
+	DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+	/*
+	*  Do this before releasing/freeing resources.
+	*/
+	scsi_remove_host(shost);
+
+	/*
+	*  Interrupts getting hosed on card removal.  Try
+	*  the following code, mostly from qlogicfas.c.
+	*/
+	if (shost->irq)
+		free_irq(shost->irq, shost);
+	if (shost->dma_channel != 0xff)
+		free_dma(shost->dma_channel);
+	if (shost->io_port && shost->n_io_port)
+		release_region(shost->io_port, shost->n_io_port);
+
+	link->dev = NULL;
+
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+
+	scsi_host_put(shost);
+} /* SYM53C500_release */
+
+static const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+	static char info_msg[256];
+	struct sym53c500_data *data =
+	    (struct sym53c500_data *)SChost->hostdata;
+
+	DEB(printk("SYM53C500_info called\n"));
+	(void)snprintf(info_msg, sizeof(info_msg),
+	    "SYM53C500 at 0x%lx, IRQ %d, %s PIO mode.", 
+	    SChost->io_port, SChost->irq, data->fast_pio ? "fast" : "slow");
+	return (info_msg);
+}
+
+static int 
+SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+	int i;
+	int port_base = SCpnt->device->host->io_port;
+	struct sym53c500_data *data =
+	    (struct sym53c500_data *)SCpnt->device->host->hostdata;
+
+	VDEB(printk("SYM53C500_queue called\n"));
+
+	DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n", 
+	    SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id, 
+	    SCpnt->device->lun,  SCpnt->request_bufflen));
+
+	VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+	    printk("cmd[%d]=%02x  ", i, SCpnt->cmnd[i]));
+	VDEB(printk("\n"));
+
+	data->current_SC = SCpnt;
+	data->current_SC->scsi_done = done;
+	data->current_SC->SCp.phase = command_ph;
+	data->current_SC->SCp.Status = 0;
+	data->current_SC->SCp.Message = 0;
+
+	/* We are locked here already by the mid layer */
+	REG0(port_base);
+	outb(SCpnt->device->id, port_base + DEST_ID);	/* set destination */
+	outb(FLUSH_FIFO, port_base + CMD_REG);	/* reset the fifos */
+
+	for (i = 0; i < SCpnt->cmd_len; i++) {
+		outb(SCpnt->cmnd[i], port_base + SCSI_FIFO);
+	}
+	outb(SELECT_NO_ATN, port_base + CMD_REG);
+
+	return 0;
+}
+
+static int 
+SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
+{
+	int port_base = SCpnt->device->host->io_port;
+
+	DEB(printk("SYM53C500_host_reset called\n"));
+	SYM53C500_int_host_reset(port_base);
+
+	return SUCCESS;
+}
+
+static int 
+SYM53C500_biosparm(struct scsi_device *disk,
+    struct block_device *dev,
+    sector_t capacity, int *info_array)
+{
+	int size;
+
+	DEB(printk("SYM53C500_biosparm called\n"));
+
+	size = capacity;
+	info_array[0] = 64;		/* heads */
+	info_array[1] = 32;		/* sectors */
+	info_array[2] = size >> 11;	/* cylinders */
+	if (info_array[2] > 1024) {	/* big disk */
+		info_array[0] = 255;
+		info_array[1] = 63;
+		info_array[2] = size / (255 * 63);
+	}
+	return 0;
+}
+
+static ssize_t
+SYM53C500_show_pio(struct class_device *cdev, char *buf)
+{
+	struct Scsi_Host *SHp = class_to_shost(cdev);
+	struct sym53c500_data *data =
+	    (struct sym53c500_data *)SHp->hostdata;
+
+	return snprintf(buf, 4, "%d\n", data->fast_pio);
+}
+
+static ssize_t
+SYM53C500_store_pio(struct class_device *cdev, const char *buf, size_t count)
+{
+	int pio;
+	struct Scsi_Host *SHp = class_to_shost(cdev);
+	struct sym53c500_data *data =
+	    (struct sym53c500_data *)SHp->hostdata;
+
+	pio = simple_strtoul(buf, NULL, 0);
+	if (pio == 0 || pio == 1) {
+		data->fast_pio = pio;
+		return count;
+	}
+	else
+		return -EINVAL;
+}
+
+/*
+*  SCSI HBA device attributes we want to
+*  make available via sysfs.
+*/
+static struct class_device_attribute SYM53C500_pio_attr = {
+	.attr = {
+		.name = "fast_pio",
+		.mode = (S_IRUGO | S_IWUSR),
+	},
+	.show = SYM53C500_show_pio,
+	.store = SYM53C500_store_pio,
+};
+
+static struct class_device_attribute *SYM53C500_shost_attrs[] = {
+	&SYM53C500_pio_attr,
+	NULL,
+};
+
+/*
+*  scsi_host_template initializer
+*/
+static struct scsi_host_template sym53c500_driver_template = {
+     .module			= THIS_MODULE,
+     .name			= "SYM53C500",
+     .info			= SYM53C500_info,
+     .queuecommand		= SYM53C500_queue,
+     .eh_host_reset_handler	= SYM53C500_host_reset,
+     .bios_param		= SYM53C500_biosparm,
+     .proc_name			= "SYM53C500",
+     .can_queue			= 1,
+     .this_id			= 7,
+     .sg_tablesize		= 32,
+     .cmd_per_lun		= 1,
+     .use_clustering		= ENABLE_CLUSTERING,
+     .shost_attrs		= SYM53C500_shost_attrs
+};
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	struct scsi_info_t *info = link->priv;
+	tuple_t tuple;
+	cisparse_t parse;
+	int i, last_ret, last_fn;
+	int irq_level, port_base;
+	unsigned short tuple_data[32];
+	struct Scsi_Host *host;
+	struct scsi_host_template *tpnt = &sym53c500_driver_template;
+	struct sym53c500_data *data;
+
+	DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+	tuple.TupleData = (cisdata_t *)tuple_data;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigBase = parse.config.base;
+
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+	    (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+		info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	while (1) {
+		if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		    pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+			goto next_entry;
+		link->conf.ConfigIndex = parse.cftable_entry.index;
+		link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+		link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+		if (link->io.BasePort1 != 0) {
+			i = pcmcia_request_io(handle, &link->io);
+			if (i == CS_SUCCESS)
+				break;
+		}
+next_entry:
+		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+	}
+
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+	/*
+	*  That's the trouble with copying liberally from another driver.
+	*  Some things probably aren't relevant, and I suspect this entire
+	*  section dealing with manufacturer IDs can be scrapped.	--rct
+	*/
+	if ((info->manf_id == MANFID_MACNICA) ||
+	    (info->manf_id == MANFID_PIONEER) ||
+	    (info->manf_id == 0x0098)) {
+		/* set ATAcmd */
+		outb(0xb4, link->io.BasePort1 + 0xd);
+		outb(0x24, link->io.BasePort1 + 0x9);
+		outb(0x04, link->io.BasePort1 + 0xd);
+	}
+
+	/*
+	*  irq_level == 0 implies tpnt->can_queue == 0, which
+	*  is not supported in 2.6.  Thus, only irq_level > 0
+	*  will be allowed.
+	*
+	*  Possible port_base values are as follows:
+	*
+	*	0x130, 0x230, 0x280, 0x290,
+	*	0x320, 0x330, 0x340, 0x350
+	*/
+	port_base = link->io.BasePort1;
+	irq_level = link->irq.AssignedIRQ;
+
+	DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n",
+	    port_base, irq_level, USE_FAST_PIO);)
+
+	chip_init(port_base);
+
+	host = scsi_host_alloc(tpnt, sizeof(struct sym53c500_data));
+	if (!host) {
+		printk("SYM53C500: Unable to register host, giving up.\n");
+		goto err_release;
+	}
+
+	data = (struct sym53c500_data *)host->hostdata;
+
+	if (irq_level > 0) {
+		if (request_irq(irq_level, SYM53C500_intr, SA_SHIRQ, "SYM53C500", host)) {
+			printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+			goto err_free_scsi;
+		}
+		DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+	} else if (irq_level == 0) {
+		DEB(printk("SYM53C500: No interrupts detected\n"));
+		goto err_free_scsi;
+	} else {
+		DEB(printk("SYM53C500: Shouldn't get here!\n"));
+		goto err_free_scsi;
+	}
+
+	host->unique_id = port_base;
+	host->irq = irq_level;
+	host->io_port = port_base;
+	host->n_io_port = 0x10;
+	host->dma_channel = -1;
+
+	/*
+	*  Note fast_pio is set to USE_FAST_PIO by
+	*  default, but can be changed via "sysfs".
+	*/
+	data->fast_pio = USE_FAST_PIO;
+
+	sprintf(info->node.dev_name, "scsi%d", host->host_no);
+	link->dev = &info->node;
+	info->host = host;
+
+	if (scsi_add_host(host, NULL))
+		goto err_free_irq;
+
+	scsi_scan_host(host);
+
+	goto out;	/* SUCCESS */
+
+err_free_irq:
+	free_irq(irq_level, host);
+err_free_scsi:
+	scsi_host_put(host);
+err_release:
+	release_region(port_base, 0x10);
+	printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+
+out:
+	link->state &= ~DEV_CONFIG_PENDING;
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+	SYM53C500_release(link);
+	return;
+} /* SYM53C500_config */
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	struct scsi_info_t *info = link->priv;
+
+	DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG)
+			SYM53C500_release(link);
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		SYM53C500_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			pcmcia_release_configuration(link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (link->state & DEV_CONFIG) {
+			pcmcia_request_configuration(link->handle, &link->conf);
+			/* See earlier comment about manufacturer IDs. */
+			if ((info->manf_id == MANFID_MACNICA) ||
+			    (info->manf_id == MANFID_PIONEER) ||
+			    (info->manf_id == 0x0098)) {
+				outb(0x80, link->io.BasePort1 + 0xd);
+				outb(0x24, link->io.BasePort1 + 0x9);
+				outb(0x04, link->io.BasePort1 + 0xd);
+			}
+			/*
+			*  If things don't work after a "resume",
+			*  this is a good place to start looking.
+			*/
+			SYM53C500_int_host_reset(link->io.BasePort1);
+		}
+		break;
+	}
+	return 0;
+} /* SYM53C500_event */
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+	dev_link_t **linkp;
+
+	DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+	if (*linkp == NULL)
+		return;
+
+	if (link->state & DEV_CONFIG)
+		SYM53C500_release(link);
+
+	if (link->handle)
+		pcmcia_deregister_client(link->handle);
+
+	/* Unlink device structure, free bits. */
+	*linkp = link->next;
+	kfree(link->priv);
+	link->priv = NULL;
+} /* SYM53C500_detach */
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+	struct scsi_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int ret;
+
+	DEBUG(0, "SYM53C500_attach()\n");
+
+	/* Create new SCSI device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+	link = &info->link;
+	link->priv = info;
+	link->io.NumPorts1 = 16;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	link->io.IOAddrLines = 10;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.event_handler = &SYM53C500_event;
+	client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != 0) {
+		cs_error(link->handle, RegisterClient, ret);
+		SYM53C500_detach(link);
+		return NULL;
+	}
+
+	return link;
+} /* SYM53C500_attach */
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "sym53c500_cs",
+	},
+	.attach		= SYM53C500_attach,
+	.detach		= SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+	return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+	pcmcia_unregister_driver(&sym53c500_cs_driver);
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);