summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/nsp32.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/nsp32.c
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/nsp32.c')
-rw-r--r--drivers/scsi/nsp32.c3585
1 files changed, 3585 insertions, 0 deletions
diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
new file mode 100644
index 000000000000..acfead1e9f17
--- /dev/null
+++ b/drivers/scsi/nsp32.c
@@ -0,0 +1,3585 @@
+/*
+ * NinjaSCSI-32Bi Cardbus, NinjaSCSI-32UDE PCI/CardBus SCSI driver
+ * Copyright (C) 2001, 2002, 2003
+ * YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+ * GOTO Masanori <gotom@debian.or.jp>, <gotom@debian.org>
+ *
+ * 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.
+ *
+ *
+ * Revision History:
+ * 1.0: Initial Release.
+ * 1.1: Add /proc SDTR status.
+ * Remove obsolete error handler nsp32_reset.
+ * Some clean up.
+ * 1.2: PowerPC (big endian) support.
+ */
+
+#include <linux/version.h>
+#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/timer.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+# include <linux/blk.h>
+#endif
+
+#include "nsp32.h"
+
+
+/***********************************************************************
+ * Module parameters
+ */
+static int trans_mode = 0; /* default: BIOS */
+module_param (trans_mode, int, 0);
+MODULE_PARM_DESC(trans_mode, "transfer mode (0: BIOS(default) 1: Async 2: Ultra20M");
+#define ASYNC_MODE 1
+#define ULTRA20M_MODE 2
+
+static int auto_param = 0; /* default: ON */
+module_param (auto_param, bool, 0);
+MODULE_PARM_DESC(auto_param, "AutoParameter mode (0: ON(default) 1: OFF)");
+
+static int disc_priv = 1; /* default: OFF */
+module_param (disc_priv, bool, 0);
+MODULE_PARM_DESC(disc_priv, "disconnection privilege mode (0: ON 1: OFF(default))");
+
+MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>, GOTO Masanori <gotom@debian.or.jp>");
+MODULE_DESCRIPTION("Workbit NinjaSCSI-32Bi/UDE CardBus/PCI SCSI host bus adapter module");
+MODULE_LICENSE("GPL");
+
+static const char *nsp32_release_version = "1.2";
+
+
+/****************************************************************************
+ * Supported hardware
+ */
+static struct pci_device_id nsp32_pci_table[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_IODATA,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_IODATA,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_KME,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_KME,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_WBT,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_WORKBIT,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_WORKBIT_STANDARD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_WORKBIT,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_LOGITEC,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_LOGITEC,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_MELCO,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_WORKBIT,
+ .device = PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO_II,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = MODEL_PCI_MELCO,
+ },
+ {0,0,},
+};
+MODULE_DEVICE_TABLE(pci, nsp32_pci_table);
+
+static nsp32_hw_data nsp32_data_base; /* probe <-> detect glue */
+
+
+/*
+ * Period/AckWidth speed conversion table
+ *
+ * Note: This period/ackwidth speed table must be in descending order.
+ */
+static nsp32_sync_table nsp32_sync_table_40M[] = {
+ /* {PNo, AW, SP, EP, SREQ smpl} Speed(MB/s) Period AckWidth */
+ {0x1, 0, 0x0c, 0x0c, SMPL_40M}, /* 20.0 : 50ns, 25ns */
+ {0x2, 0, 0x0d, 0x18, SMPL_40M}, /* 13.3 : 75ns, 25ns */
+ {0x3, 1, 0x19, 0x19, SMPL_40M}, /* 10.0 : 100ns, 50ns */
+ {0x4, 1, 0x1a, 0x1f, SMPL_20M}, /* 8.0 : 125ns, 50ns */
+ {0x5, 2, 0x20, 0x25, SMPL_20M}, /* 6.7 : 150ns, 75ns */
+ {0x6, 2, 0x26, 0x31, SMPL_20M}, /* 5.7 : 175ns, 75ns */
+ {0x7, 3, 0x32, 0x32, SMPL_20M}, /* 5.0 : 200ns, 100ns */
+ {0x8, 3, 0x33, 0x38, SMPL_10M}, /* 4.4 : 225ns, 100ns */
+ {0x9, 3, 0x39, 0x3e, SMPL_10M}, /* 4.0 : 250ns, 100ns */
+};
+
+static nsp32_sync_table nsp32_sync_table_20M[] = {
+ {0x1, 0, 0x19, 0x19, SMPL_40M}, /* 10.0 : 100ns, 50ns */
+ {0x2, 0, 0x1a, 0x25, SMPL_20M}, /* 6.7 : 150ns, 50ns */
+ {0x3, 1, 0x26, 0x32, SMPL_20M}, /* 5.0 : 200ns, 100ns */
+ {0x4, 1, 0x33, 0x3e, SMPL_10M}, /* 4.0 : 250ns, 100ns */
+ {0x5, 2, 0x3f, 0x4b, SMPL_10M}, /* 3.3 : 300ns, 150ns */
+ {0x6, 2, 0x4c, 0x57, SMPL_10M}, /* 2.8 : 350ns, 150ns */
+ {0x7, 3, 0x58, 0x64, SMPL_10M}, /* 2.5 : 400ns, 200ns */
+ {0x8, 3, 0x65, 0x70, SMPL_10M}, /* 2.2 : 450ns, 200ns */
+ {0x9, 3, 0x71, 0x7d, SMPL_10M}, /* 2.0 : 500ns, 200ns */
+};
+
+static nsp32_sync_table nsp32_sync_table_pci[] = {
+ {0x1, 0, 0x0c, 0x0f, SMPL_40M}, /* 16.6 : 60ns, 30ns */
+ {0x2, 0, 0x10, 0x16, SMPL_40M}, /* 11.1 : 90ns, 30ns */
+ {0x3, 1, 0x17, 0x1e, SMPL_20M}, /* 8.3 : 120ns, 60ns */
+ {0x4, 1, 0x1f, 0x25, SMPL_20M}, /* 6.7 : 150ns, 60ns */
+ {0x5, 2, 0x26, 0x2d, SMPL_20M}, /* 5.6 : 180ns, 90ns */
+ {0x6, 2, 0x2e, 0x34, SMPL_10M}, /* 4.8 : 210ns, 90ns */
+ {0x7, 3, 0x35, 0x3c, SMPL_10M}, /* 4.2 : 240ns, 120ns */
+ {0x8, 3, 0x3d, 0x43, SMPL_10M}, /* 3.7 : 270ns, 120ns */
+ {0x9, 3, 0x44, 0x4b, SMPL_10M}, /* 3.3 : 300ns, 120ns */
+};
+
+/*
+ * function declaration
+ */
+/* module entry point */
+static int __devinit nsp32_probe (struct pci_dev *, const struct pci_device_id *);
+static void __devexit nsp32_remove(struct pci_dev *);
+static int __init init_nsp32 (void);
+static void __exit exit_nsp32 (void);
+
+/* struct Scsi_Host_Template */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+static int nsp32_proc_info (struct Scsi_Host *, char *, char **, off_t, int, int);
+#else
+static int nsp32_proc_info (char *, char **, off_t, int, int, int);
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,73))
+static int nsp32_detect (struct pci_dev *pdev);
+#else
+static int nsp32_detect (Scsi_Host_Template *);
+#endif
+static int nsp32_queuecommand(struct scsi_cmnd *,
+ void (*done)(struct scsi_cmnd *));
+static const char *nsp32_info (struct Scsi_Host *);
+static int nsp32_release (struct Scsi_Host *);
+
+/* SCSI error handler */
+static int nsp32_eh_abort (struct scsi_cmnd *);
+static int nsp32_eh_bus_reset (struct scsi_cmnd *);
+static int nsp32_eh_host_reset(struct scsi_cmnd *);
+
+/* generate SCSI message */
+static void nsp32_build_identify(struct scsi_cmnd *);
+static void nsp32_build_nop (struct scsi_cmnd *);
+static void nsp32_build_reject (struct scsi_cmnd *);
+static void nsp32_build_sdtr (struct scsi_cmnd *, unsigned char, unsigned char);
+
+/* SCSI message handler */
+static int nsp32_busfree_occur(struct scsi_cmnd *, unsigned short);
+static void nsp32_msgout_occur (struct scsi_cmnd *);
+static void nsp32_msgin_occur (struct scsi_cmnd *, unsigned long, unsigned short);
+
+static int nsp32_setup_sg_table (struct scsi_cmnd *);
+static int nsp32_selection_autopara(struct scsi_cmnd *);
+static int nsp32_selection_autoscsi(struct scsi_cmnd *);
+static void nsp32_scsi_done (struct scsi_cmnd *);
+static int nsp32_arbitration (struct scsi_cmnd *, unsigned int);
+static int nsp32_reselection (struct scsi_cmnd *, unsigned char);
+static void nsp32_adjust_busfree (struct scsi_cmnd *, unsigned int);
+static void nsp32_restart_autoscsi (struct scsi_cmnd *, unsigned short);
+
+/* SCSI SDTR */
+static void nsp32_analyze_sdtr (struct scsi_cmnd *);
+static int nsp32_search_period_entry(nsp32_hw_data *, nsp32_target *, unsigned char);
+static void nsp32_set_async (nsp32_hw_data *, nsp32_target *);
+static void nsp32_set_max_sync (nsp32_hw_data *, nsp32_target *, unsigned char *, unsigned char *);
+static void nsp32_set_sync_entry (nsp32_hw_data *, nsp32_target *, int, unsigned char);
+
+/* SCSI bus status handler */
+static void nsp32_wait_req (nsp32_hw_data *, int);
+static void nsp32_wait_sack (nsp32_hw_data *, int);
+static void nsp32_sack_assert (nsp32_hw_data *);
+static void nsp32_sack_negate (nsp32_hw_data *);
+static void nsp32_do_bus_reset(nsp32_hw_data *);
+
+/* hardware interrupt handler */
+static irqreturn_t do_nsp32_isr(int, void *, struct pt_regs *);
+
+/* initialize hardware */
+static int nsp32hw_init(nsp32_hw_data *);
+
+/* EEPROM handler */
+static int nsp32_getprom_param (nsp32_hw_data *);
+static int nsp32_getprom_at24 (nsp32_hw_data *);
+static int nsp32_getprom_c16 (nsp32_hw_data *);
+static void nsp32_prom_start (nsp32_hw_data *);
+static void nsp32_prom_stop (nsp32_hw_data *);
+static int nsp32_prom_read (nsp32_hw_data *, int);
+static int nsp32_prom_read_bit (nsp32_hw_data *);
+static void nsp32_prom_write_bit(nsp32_hw_data *, int);
+static void nsp32_prom_set (nsp32_hw_data *, int, int);
+static int nsp32_prom_get (nsp32_hw_data *, int);
+
+/* debug/warning/info message */
+static void nsp32_message (const char *, int, char *, char *, ...);
+#ifdef NSP32_DEBUG
+static void nsp32_dmessage(const char *, int, int, char *, ...);
+#endif
+
+/*
+ * max_sectors is currently limited up to 128.
+ */
+static struct scsi_host_template nsp32_template = {
+ .proc_name = "nsp32",
+ .name = "Workbit NinjaSCSI-32Bi/UDE",
+ .proc_info = nsp32_proc_info,
+ .info = nsp32_info,
+ .queuecommand = nsp32_queuecommand,
+ .can_queue = 1,
+ .sg_tablesize = NSP32_SG_SIZE,
+ .max_sectors = 128,
+ .cmd_per_lun = 1,
+ .this_id = NSP32_HOST_SCSIID,
+ .use_clustering = DISABLE_CLUSTERING,
+ .eh_abort_handler = nsp32_eh_abort,
+/* .eh_device_reset_handler = NULL, */
+ .eh_bus_reset_handler = nsp32_eh_bus_reset,
+ .eh_host_reset_handler = nsp32_eh_host_reset,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,74))
+ .detect = nsp32_detect,
+ .release = nsp32_release,
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
+ .use_new_eh_code = 1,
+#else
+/* .highmem_io = 1, */
+#endif
+};
+
+#include "nsp32_io.h"
+
+/***********************************************************************
+ * debug, error print
+ */
+#ifndef NSP32_DEBUG
+# define NSP32_DEBUG_MASK 0x000000
+# define nsp32_msg(type, args...) nsp32_message ("", 0, (type), args)
+# define nsp32_dbg(mask, args...) /* */
+#else
+# define NSP32_DEBUG_MASK 0xffffff
+# define nsp32_msg(type, args...) \
+ nsp32_message (__FUNCTION__, __LINE__, (type), args)
+# define nsp32_dbg(mask, args...) \
+ nsp32_dmessage(__FUNCTION__, __LINE__, (mask), args)
+#endif
+
+#define NSP32_DEBUG_QUEUECOMMAND BIT(0)
+#define NSP32_DEBUG_REGISTER BIT(1)
+#define NSP32_DEBUG_AUTOSCSI BIT(2)
+#define NSP32_DEBUG_INTR BIT(3)
+#define NSP32_DEBUG_SGLIST BIT(4)
+#define NSP32_DEBUG_BUSFREE BIT(5)
+#define NSP32_DEBUG_CDB_CONTENTS BIT(6)
+#define NSP32_DEBUG_RESELECTION BIT(7)
+#define NSP32_DEBUG_MSGINOCCUR BIT(8)
+#define NSP32_DEBUG_EEPROM BIT(9)
+#define NSP32_DEBUG_MSGOUTOCCUR BIT(10)
+#define NSP32_DEBUG_BUSRESET BIT(11)
+#define NSP32_DEBUG_RESTART BIT(12)
+#define NSP32_DEBUG_SYNC BIT(13)
+#define NSP32_DEBUG_WAIT BIT(14)
+#define NSP32_DEBUG_TARGETFLAG BIT(15)
+#define NSP32_DEBUG_PROC BIT(16)
+#define NSP32_DEBUG_INIT BIT(17)
+#define NSP32_SPECIAL_PRINT_REGISTER BIT(20)
+
+#define NSP32_DEBUG_BUF_LEN 100
+
+static void nsp32_message(const char *func, int line, char *type, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP32_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+#ifndef NSP32_DEBUG
+ printk("%snsp32: %s\n", type, buf);
+#else
+ printk("%snsp32: %s (%d): %s\n", type, func, line, buf);
+#endif
+}
+
+#ifdef NSP32_DEBUG
+static void nsp32_dmessage(const char *func, int line, int mask, char *fmt, ...)
+{
+ va_list args;
+ char buf[NSP32_DEBUG_BUF_LEN];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+
+ if (mask & NSP32_DEBUG_MASK) {
+ printk("nsp32-debug: 0x%x %s (%d): %s\n", mask, func, line, buf);
+ }
+}
+#endif
+
+#ifdef NSP32_DEBUG
+# include "nsp32_debug.c"
+#else
+# define show_command(arg) /* */
+# define show_busphase(arg) /* */
+# define show_autophase(arg) /* */
+#endif
+
+/*
+ * IDENTIFY Message
+ */
+static void nsp32_build_identify(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+ int mode = FALSE;
+
+ /* XXX: Auto DiscPriv detection is progressing... */
+ if (disc_priv == 0) {
+ /* mode = TRUE; */
+ }
+
+ data->msgoutbuf[pos] = IDENTIFY(mode, SCpnt->device->lun); pos++;
+
+ data->msgout_len = pos;
+}
+
+/*
+ * SDTR Message Routine
+ */
+static void nsp32_build_sdtr(struct scsi_cmnd *SCpnt,
+ unsigned char period,
+ unsigned char offset)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ data->msgoutbuf[pos] = EXTENDED_MESSAGE; pos++;
+ data->msgoutbuf[pos] = EXTENDED_SDTR_LEN; pos++;
+ data->msgoutbuf[pos] = EXTENDED_SDTR; pos++;
+ data->msgoutbuf[pos] = period; pos++;
+ data->msgoutbuf[pos] = offset; pos++;
+
+ data->msgout_len = pos;
+}
+
+/*
+ * No Operation Message
+ */
+static void nsp32_build_nop(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ if (pos != 0) {
+ nsp32_msg(KERN_WARNING,
+ "Some messages are already contained!");
+ return;
+ }
+
+ data->msgoutbuf[pos] = NOP; pos++;
+ data->msgout_len = pos;
+}
+
+/*
+ * Reject Message
+ */
+static void nsp32_build_reject(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ int pos = data->msgout_len;
+
+ data->msgoutbuf[pos] = MESSAGE_REJECT; pos++;
+ data->msgout_len = pos;
+}
+
+/*
+ * timer
+ */
+#if 0
+static void nsp32_start_timer(struct scsi_cmnd *SCpnt, int time)
+{
+ unsigned int base = SCpnt->host->io_port;
+
+ nsp32_dbg(NSP32_DEBUG_INTR, "timer=%d", time);
+
+ if (time & (~TIMER_CNT_MASK)) {
+ nsp32_dbg(NSP32_DEBUG_INTR, "timer set overflow");
+ }
+
+ nsp32_write2(base, TIMER_SET, time & TIMER_CNT_MASK);
+}
+#endif
+
+
+/*
+ * set SCSI command and other parameter to asic, and start selection phase
+ */
+static int nsp32_selection_autopara(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned char target = SCpnt->device->id;
+ nsp32_autoparam *param = data->autoparam;
+ unsigned char phase;
+ int i, ret;
+ unsigned int msgout;
+ u16_le s;
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
+
+ /*
+ * check bus free
+ */
+ phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if (phase != BUSMON_BUS_FREE) {
+ nsp32_msg(KERN_WARNING, "bus busy");
+ show_busphase(phase & BUSMON_PHASE_MASK);
+ SCpnt->result = DID_BUS_BUSY << 16;
+ return FALSE;
+ }
+
+ /*
+ * message out
+ *
+ * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.
+ * over 3 messages needs another routine.
+ */
+ if (data->msgout_len == 0) {
+ nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
+ SCpnt->result = DID_ERROR << 16;
+ return FALSE;
+ } else if (data->msgout_len > 0 && data->msgout_len <= 3) {
+ msgout = 0;
+ for (i = 0; i < data->msgout_len; i++) {
+ /*
+ * the sending order of the message is:
+ * MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+ * MCNT 2: MSG#1 -> MSG#2
+ * MCNT 1: MSG#2
+ */
+ msgout >>= 8;
+ msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
+ }
+ msgout |= MV_VALID; /* MV valid */
+ msgout |= (unsigned int)data->msgout_len; /* len */
+ } else {
+ /* data->msgout_len > 3 */
+ msgout = 0;
+ }
+
+ // nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n", nsp32_read2(base, SEL_TIME_OUT));
+ // nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+ /*
+ * setup asic parameter
+ */
+ memset(param, 0, sizeof(nsp32_autoparam));
+
+ /* cdb */
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ param->cdb[4 * i] = SCpnt->cmnd[i];
+ }
+
+ /* outgoing messages */
+ param->msgout = cpu_to_le32(msgout);
+
+ /* syncreg, ackwidth, target id, SREQ sampling rate */
+ param->syncreg = data->cur_target->syncreg;
+ param->ackwidth = data->cur_target->ackwidth;
+ param->target_id = BIT(host_id) | BIT(target);
+ param->sample_reg = data->cur_target->sample_reg;
+
+ // nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "sample rate=0x%x\n", data->cur_target->sample_reg);
+
+ /* command control */
+ param->command_control = cpu_to_le16(CLEAR_CDB_FIFO_POINTER |
+ AUTOSCSI_START |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 |
+ AUTO_ATN );
+
+
+ /* transfer control */
+ s = 0;
+ switch (data->trans_method) {
+ case NSP32_TRANSFER_BUSMASTER:
+ s |= BM_START;
+ break;
+ case NSP32_TRANSFER_MMIO:
+ s |= CB_MMIO_MODE;
+ break;
+ case NSP32_TRANSFER_PIO:
+ s |= CB_IO_MODE;
+ break;
+ default:
+ nsp32_msg(KERN_ERR, "unknown trans_method");
+ break;
+ }
+ /*
+ * OR-ed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.
+ * For bus master transfer, it's taken off.
+ */
+ s |= (TRANSFER_GO | ALL_COUNTER_CLR);
+ param->transfer_control = cpu_to_le16(s);
+
+ /* sg table addr */
+ param->sgt_pointer = cpu_to_le32(data->cur_lunt->sglun_paddr);
+
+ /*
+ * transfer parameter to ASIC
+ */
+ nsp32_write4(base, SGT_ADR, data->auto_paddr);
+ nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |
+ AUTO_PARAMETER );
+
+ /*
+ * Check arbitration
+ */
+ ret = nsp32_arbitration(SCpnt, base);
+
+ return ret;
+}
+
+
+/*
+ * Selection with AUTO SCSI (without AUTO PARAMETER)
+ */
+static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned char target = SCpnt->device->id;
+ unsigned char phase;
+ int status;
+ unsigned short command = 0;
+ unsigned int msgout = 0;
+ unsigned short execph;
+ int i;
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");
+
+ /*
+ * IRQ disable
+ */
+ nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+
+ /*
+ * check bus line
+ */
+ phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+ if(((phase & BUSMON_BSY) == 1) || (phase & BUSMON_SEL) == 1) {
+ nsp32_msg(KERN_WARNING, "bus busy");
+ SCpnt->result = DID_BUS_BUSY << 16;
+ status = 1;
+ goto out;
+ }
+
+ /*
+ * clear execph
+ */
+ execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+
+ /*
+ * clear FIFO counter to set CDBs
+ */
+ nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);
+
+ /*
+ * set CDB0 - CDB15
+ */
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);
+ }
+ nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[0]);
+
+ /*
+ * set SCSIOUT LATCH(initiator)/TARGET(target) (OR-ed) ID
+ */
+ nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID, BIT(host_id) | BIT(target));
+
+ /*
+ * set SCSI MSGOUT REG
+ *
+ * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.
+ * over 3 messages needs another routine.
+ */
+ if (data->msgout_len == 0) {
+ nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
+ SCpnt->result = DID_ERROR << 16;
+ status = 1;
+ goto out;
+ } else if (data->msgout_len > 0 && data->msgout_len <= 3) {
+ msgout = 0;
+ for (i = 0; i < data->msgout_len; i++) {
+ /*
+ * the sending order of the message is:
+ * MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+ * MCNT 2: MSG#1 -> MSG#2
+ * MCNT 1: MSG#2
+ */
+ msgout >>= 8;
+ msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);
+ }
+ msgout |= MV_VALID; /* MV valid */
+ msgout |= (unsigned int)data->msgout_len; /* len */
+ nsp32_write4(base, SCSI_MSG_OUT, msgout);
+ } else {
+ /* data->msgout_len > 3 */
+ nsp32_write4(base, SCSI_MSG_OUT, 0);
+ }
+
+ /*
+ * set selection timeout(= 250ms)
+ */
+ nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+ /*
+ * set SREQ hazard killer sampling rate
+ *
+ * TODO: sample_rate (BASE+0F) is 0 when internal clock = 40MHz.
+ * check other internal clock!
+ */
+ nsp32_write1(base, SREQ_SMPL_RATE, data->cur_target->sample_reg);
+
+ /*
+ * clear Arbit
+ */
+ nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+ /*
+ * set SYNCREG
+ * Don't set BM_START_ADR before setting this register.
+ */
+ nsp32_write1(base, SYNC_REG, data->cur_target->syncreg);
+
+ /*
+ * set ACKWIDTH
+ */
+ nsp32_write1(base, ACK_WIDTH, data->cur_target->ackwidth);
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+ "syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",
+ nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),
+ nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgout_len=%d, msgout=0x%x",
+ data->msgout_len, msgout);
+
+ /*
+ * set SGT ADDR (physical address)
+ */
+ nsp32_write4(base, SGT_ADR, data->cur_lunt->sglun_paddr);
+
+ /*
+ * set TRANSFER CONTROL REG
+ */
+ command = 0;
+ command |= (TRANSFER_GO | ALL_COUNTER_CLR);
+ if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+ if (SCpnt->request_bufflen > 0) {
+ command |= BM_START;
+ }
+ } else if (data->trans_method & NSP32_TRANSFER_MMIO) {
+ command |= CB_MMIO_MODE;
+ } else if (data->trans_method & NSP32_TRANSFER_PIO) {
+ command |= CB_IO_MODE;
+ }
+ nsp32_write2(base, TRANSFER_CONTROL, command);
+
+ /*
+ * start AUTO SCSI, kick off arbitration
+ */
+ command = (CLEAR_CDB_FIFO_POINTER |
+ AUTOSCSI_START |
+ AUTO_MSGIN_00_OR_04 |
+ AUTO_MSGIN_02 |
+ AUTO_ATN );
+ nsp32_write2(base, COMMAND_CONTROL, command);
+
+ /*
+ * Check arbitration
+ */
+ status = nsp32_arbitration(SCpnt, base);
+
+ out:
+ /*
+ * IRQ enable
+ */
+ nsp32_write2(base, IRQ_CONTROL, 0);
+
+ return status;
+}
+
+
+/*
+ * Arbitration Status Check
+ *
+ * Note: Arbitration counter is waited during ARBIT_GO is not lifting.
+ * Using udelay(1) consumes CPU time and system time, but
+ * arbitration delay time is defined minimal 2.4us in SCSI
+ * specification, thus udelay works as coarse grained wait timer.
+ */
+static int nsp32_arbitration(struct scsi_cmnd *SCpnt, unsigned int base)
+{
+ unsigned char arbit;
+ int status = TRUE;
+ int time = 0;
+
+ do {
+ arbit = nsp32_read1(base, ARBIT_STATUS);
+ time++;
+ } while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
+ (time <= ARBIT_TIMEOUT_TIME));
+
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+ "arbit: 0x%x, delay time: %d", arbit, time);
+
+ if (arbit & ARBIT_WIN) {
+ /* Arbitration succeeded */
+ SCpnt->result = DID_OK << 16;
+ nsp32_index_write1(base, EXT_PORT, LED_ON); /* PCI LED on */
+ } else if (arbit & ARBIT_FAIL) {
+ /* Arbitration failed */
+ SCpnt->result = DID_BUS_BUSY << 16;
+ status = FALSE;
+ } else {
+ /*
+ * unknown error or ARBIT_GO timeout,
+ * something lock up! guess no connection.
+ */
+ nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit timeout");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ status = FALSE;
+ }
+
+ /*
+ * clear Arbit
+ */
+ nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+ return status;
+}
+
+
+/*
+ * reselection
+ *
+ * Note: This reselection routine is called from msgin_occur,
+ * reselection target id&lun must be already set.
+ * SCSI-2 says IDENTIFY implies RESTORE_POINTER operation.
+ */
+static int nsp32_reselection(struct scsi_cmnd *SCpnt, unsigned char newlun)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ unsigned int host_id = SCpnt->device->host->this_id;
+ unsigned int base = SCpnt->device->host->io_port;
+ unsigned char tmpid, newid;
+
+ nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");
+
+ /*
+ * calculate reselected SCSI ID
+ */
+ tmpid = nsp32_read1(base, RESELECT_ID);
+ tmpid &= (~BIT(host_id));
+ newid = 0;
+ while (tmpid) {
+ if (tmpid & 1) {
+ break;
+ }
+ tmpid >>= 1;
+ newid++;
+ }
+
+ /*
+ * If reselected New ID:LUN is not existed
+ * or current nexus is not existed, unexpected
+ * reselection is occurred. Send reject message.
+ */
+ if (newid >= ARRAY_SIZE(data->lunt) || newlun >= ARRAY_SIZE(data->lunt[0])) {
+ nsp32_msg(KERN_WARNING, "unknown id/lun");
+ return FALSE;
+ } else if(data->lunt[newid][newlun].SCpnt == NULL) {
+ nsp32_msg(KERN_WARNING, "no SCSI command is processing");
+ return FALSE;
+ }
+
+ data->cur_id = newid;
+ data->cur_lun = newlun;
+ data->cur_target = &(data->target[newid]);
+ data->cur_lunt = &(data->lunt[newid][newlun]);
+
+ /* reset SACK/SavedACK counter (or ALL clear?) */
+ nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+ return TRUE;
+}
+
+
+/*
+ * nsp32_setup_sg_table - build scatter gather list for transfer data
+ * with bus master.
+ *
+ * Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time.
+ */
+static int nsp32_setup_sg_table(struct scsi_cmnd *SCpnt)
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ struct scatterlist *sgl;
+ nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;
+ int num, i;
+ u32_le l;
+
+ if (SCpnt->request_bufflen == 0) {
+ return TRUE;
+ }
+
+ if (sgt == NULL) {
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");
+ return FALSE;
+ }
+
+ if (SCpnt->use_sg) {
+ sgl = (struct scatterlist *)SCpnt->request_buffer;
+ num = pci_map_sg(data->Pci, sgl, SCpnt->use_sg,
+ SCpnt->sc_data_direction);
+ for (i = 0; i < num; i++) {
+ /*
+ * Build nsp32_sglist, substitute sg dma addresses.
+ */
+ sgt[i].addr = cpu_to_le32(sg_dma_address(sgl));
+ sgt[i].len = cpu_to_le32(sg_dma_len(sgl));
+ sgl++;
+
+ if (le32_to_cpu(sgt[i].len) > 0x10000) {
+ nsp32_msg(KERN_ERR,
+ "can't transfer over 64KB at a time, size=0x%lx", le32_to_cpu(sgt[i].len));
+ return FALSE;
+ }
+ nsp32_dbg(NSP32_DEBUG_SGLIST,
+ "num 0x%x : addr 0x%lx len 0x%lx",
+ i,
+ le32_to_cpu(sgt[i].addr),
+ le32_to_cpu(sgt[i].len ));
+ }
+
+ /* set end mark */
+ l = le32_to_cpu(sgt[num-1].len);
+ sgt[num-1].len = cpu_to_le32(l | SGTEND);
+
+ } else {
+ SCpnt->SCp.have_data_in = pci_map_single(data->Pci,
+ SCpnt->request_buffer, SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+
+ sgt[0].addr = cpu_to_le32(SCpnt->SCp.have_data_in);
+ sgt[0].len = cpu_to_le32(SCpnt->request_bufflen | SGTEND); /* set end mark */
+
+ if (SCpnt->request_bufflen > 0x10000) {
+ nsp32_msg(KERN_ERR,
+ "can't transfer over 64KB at a time, size=0x%lx", SCpnt->request_bufflen);
+ return FALSE;
+ }
+ nsp32_dbg(NSP32_DEBUG_SGLIST, "single : addr 0x%lx len=0x%lx",
+ le32_to_cpu(sgt[0].addr),
+ le32_to_cpu(sgt[0].len ));
+ }
+
+ return TRUE;
+}
+
+static int nsp32_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;
+ nsp32_target *target;
+ nsp32_lunt *cur_lunt;
+ int ret;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "
+ "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",
+ SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len,
+ SCpnt->use_sg, SCpnt->request_buffer, SCpnt->request_bufflen);
+
+ if (data->CurrentSC != NULL) {
+ nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");
+ data->CurrentSC = NULL;
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ /* check target ID is not same as this initiator ID */
+ if (SCpnt->device->id == SCpnt->device->host->this_id) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "terget==host???");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ /* check target LUN is allowable value */
+ if (SCpnt->device->lun >= MAX_LUN) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "no more lun");
+ SCpnt->result = DID_BAD_TARGET << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ show_command(SCpnt);
+
+ SCpnt->scsi_done = done;
+ data->CurrentSC = SCpnt;
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->resid = SCpnt->request_bufflen;
+
+ SCpnt->SCp.ptr = (char *) SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+
+ /* initialize data */
+ data->msgout_len = 0;
+ data->msgin_len = 0;
+ cur_lunt = &(data->lunt[SCpnt->device->id][SCpnt->device->lun]);
+ cur_lunt->SCpnt = SCpnt;
+ cur_lunt->save_datp = 0;
+ cur_lunt->msgin03 = FALSE;
+ data->cur_lunt = cur_lunt;
+ data->cur_id = SCpnt->device->id;
+ data->cur_lun = SCpnt->device->lun;
+
+ ret = nsp32_setup_sg_table(SCpnt);
+ if (ret == FALSE) {
+ nsp32_msg(KERN_ERR, "SGT fail");
+ SCpnt->result = DID_ERROR << 16;
+ nsp32_scsi_done(SCpnt);
+ return 0;
+ }
+
+ /* Build IDENTIFY */
+ nsp32_build_identify(SCpnt);
+
+ /*
+ * If target is the first time to transfer after the reset
+ * (target don't have SDTR_DONE and SDTR_INITIATOR), sync
+ * message SDTR is needed to do synchronous transfer.
+ */
+ target = &data->target[SCpnt->device->id];
+ data->cur_target = target;
+
+ if (!(target->sync_flag & (SDTR_DONE | SDTR_INITIATOR | SDTR_TARGET))) {
+ unsigned char period, offset;
+
+ if (trans_mode != ASYNC_MODE) {
+ nsp32_set_max_sync(data, target, &period, &offset);
+ nsp32_build_sdtr(SCpnt, period, offset);
+ target->sync_flag |= SDTR_INITIATOR;
+ } else {
+ nsp32_set_async(data, target);
+ target->sync_flag |= SDTR_DONE;
+ }
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "SDTR: entry: %d start_period: 0x%x offset: 0x%x\n",
+ target->limit_entry, period, offset);
+ } else if (target->sync_flag & SDTR_INITIATOR) {
+ /*
+ * It was negotiating SDTR with target, sending from the
+ * initiator, but there are no chance to remove this flag.
+ * Set async because we don't get proper negotiation.
+ */
+ nsp32_set_async(data, target);
+ target->sync_flag &= ~SDTR_INITIATOR;
+ target->sync_flag |= SDTR_DONE;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "SDTR_INITIATOR: fall back to async");
+ } else if (target->sync_flag & SDTR_TARGET) {
+ /*
+ * It was negotiating SDTR with target, sending from target,
+ * but there are no chance to remove this flag. Set async
+ * because we don't get proper negotiation.
+ */
+ nsp32_set_async(data, target);
+ target->sync_flag &= ~SDTR_TARGET;
+ target->sync_flag |= SDTR_DONE;
+
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+ "Unknown SDTR from target is reached, fall back to async.");
+ }
+
+ nsp32_dbg(NSP32_DEBUG_TARGETFLAG,
+ "target: %d sync_flag: 0x%x syncreg: 0x%x ackwidth: 0x%x",
+ SCpnt->device->id, target->sync_flag, target->syncreg,
+ target->ackwidth);
+
+ /* Selection */
+ if (auto_param == 0) {
+ ret = nsp32_selection_autopara(SCpnt);
+ } else {
+ ret = nsp32_selection_autoscsi(SCpnt);
+ }
+
+ if (ret != TRUE) {
+ nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "selection fail");
+ nsp32_scsi_done(SCpnt);
+ }
+
+ return 0;
+}
+
+/* initialize asic */
+static int nsp32hw_init(nsp32_hw_data *data)
+{
+ unsigned int base = data->BaseAddress;
+ unsigned short irq_stat;
+ unsigned long lc_reg;
+ unsigned char power;
+
+ lc_reg = nsp32_index_read4(base, CFG_LATE_CACHE);
+ if ((lc_reg & 0xff00) == 0) {
+ lc_reg |= (0x20 << 8);
+ nsp32_index_write2(base, CFG_LATE_CACHE, lc_reg & 0xffff);
+ }