aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/pcmcia
diff options
context:
space:
mode:
authorLinus Torvalds2005-04-16 15:20:36 -0700
committerLinus Torvalds2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/pcmcia
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!
Diffstat (limited to 'drivers/scsi/pcmcia')
-rw-r--r--drivers/scsi/pcmcia/Kconfig82
-rw-r--r--drivers/scsi/pcmcia/Makefile13
-rw-r--r--drivers/scsi/pcmcia/aha152x_core.c3
-rw-r--r--drivers/scsi/pcmcia/aha152x_stub.c343
-rw-r--r--drivers/scsi/pcmcia/fdomain_core.c2
-rw-r--r--drivers/scsi/pcmcia/fdomain_stub.c323
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c2198
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.h472
-rw-r--r--drivers/scsi/pcmcia/nsp_debug.c215
-rw-r--r--drivers/scsi/pcmcia/nsp_io.h274
-rw-r--r--drivers/scsi/pcmcia/nsp_message.c78
-rw-r--r--drivers/scsi/pcmcia/qlogic_stub.c425
-rw-r--r--drivers/scsi/pcmcia/sym53c500_cs.c1022
13 files changed, 5450 insertions, 0 deletions
diff --git a/drivers/scsi/pcmcia/Kconfig b/drivers/scsi/pcmcia/Kconfig
new file mode 100644
index 000000000000..df52190f4d94
--- /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 000000000000..eca379059db6
--- /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 000000000000..dba3716511c5
--- /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 000000000000..e60b4c0a8427
--- /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 000000000000..a48913791868
--- /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 000000000000..3df7bc72e354
--- /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 000000000000..496c412c8854
--- /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 000000000000..c201b52e063a
--- /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 000000000000..62e5c60067fd
--- /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 000000000000..3b8746f85b6c
--- /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 000000000000..d7057737ff34
--- /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 000000000000..4766bcd63692
--- /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 000000000000..8457d0d7748a
--- /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);