aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/dpt_i2o.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/dpt_i2o.c')
-rw-r--r--drivers/scsi/dpt_i2o.c3381
1 files changed, 3381 insertions, 0 deletions
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
new file mode 100644
index 00000000000..53c9b93013f
--- /dev/null
+++ b/drivers/scsi/dpt_i2o.c
@@ -0,0 +1,3381 @@
+/***************************************************************************
+ dpti.c - description
+ -------------------
+ begin : Thu Sep 7 2000
+ copyright : (C) 2000 by Adaptec
+
+ July 30, 2001 First version being submitted
+ for inclusion in the kernel. V2.4
+
+ See Documentation/scsi/dpti.txt for history, notes, license info
+ and credits
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/***************************************************************************
+ * Sat Dec 20 2003 Go Taniguchi <go@turbolinux.co.jp>
+ - Support 2.6 kernel and DMA-mapping
+ - ioctl fix for raid tools
+ - use schedule_timeout in long long loop
+ **************************************************************************/
+
+/*#define DEBUG 1 */
+/*#define UARTDELAY 1 */
+
+/* On the real kernel ADDR32 should always be zero for 2.4. GFP_HIGH allocates
+ high pages. Keep the macro around because of the broken unmerged ia64 tree */
+
+#define ADDR32 (0)
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn");
+MODULE_DESCRIPTION("Adaptec I2O RAID Driver");
+
+////////////////////////////////////////////////////////////////
+
+#include <linux/ioctl.h> /* For SCSI-Passthrough */
+#include <asm/uaccess.h>
+
+#include <linux/stat.h>
+#include <linux/slab.h> /* for kmalloc() */
+#include <linux/config.h> /* for CONFIG_PCI */
+#include <linux/pci.h> /* for PCI support */
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h> /* for udelay */
+#include <linux/interrupt.h>
+#include <linux/kernel.h> /* for printk */
+#include <linux/sched.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+
+#include <asm/processor.h> /* for boot_cpu_data */
+#include <asm/pgtable.h>
+#include <asm/io.h> /* for virt_to_bus, etc. */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "dpt/dptsig.h"
+#include "dpti.h"
+
+/*============================================================================
+ * Create a binary signature - this is read by dptsig
+ * Needed for our management apps
+ *============================================================================
+ */
+static dpt_sig_S DPTI_sig = {
+ {'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION,
+#ifdef __i386__
+ PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM,
+#elif defined(__ia64__)
+ PROC_INTEL, PROC_IA64,
+#elif defined(__sparc__)
+ PROC_ULTRASPARC, PROC_ULTRASPARC,
+#elif defined(__alpha__)
+ PROC_ALPHA, PROC_ALPHA,
+#else
+ (-1),(-1),
+#endif
+ FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL,
+ ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION,
+ DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver"
+};
+
+
+
+
+/*============================================================================
+ * Globals
+ *============================================================================
+ */
+
+static DECLARE_MUTEX(adpt_configuration_lock);
+
+static struct i2o_sys_tbl *sys_tbl = NULL;
+static int sys_tbl_ind = 0;
+static int sys_tbl_len = 0;
+
+static adpt_hba* hbas[DPTI_MAX_HBA];
+static adpt_hba* hba_chain = NULL;
+static int hba_count = 0;
+
+static struct file_operations adpt_fops = {
+ .ioctl = adpt_ioctl,
+ .open = adpt_open,
+ .release = adpt_close
+};
+
+#ifdef REBOOT_NOTIFIER
+static struct notifier_block adpt_reboot_notifier =
+{
+ adpt_reboot_event,
+ NULL,
+ 0
+};
+#endif
+
+/* Structures and definitions for synchronous message posting.
+ * See adpt_i2o_post_wait() for description
+ * */
+struct adpt_i2o_post_wait_data
+{
+ int status;
+ u32 id;
+ adpt_wait_queue_head_t *wq;
+ struct adpt_i2o_post_wait_data *next;
+};
+
+static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL;
+static u32 adpt_post_wait_id = 0;
+static DEFINE_SPINLOCK(adpt_post_wait_lock);
+
+
+/*============================================================================
+ * Functions
+ *============================================================================
+ */
+
+static u8 adpt_read_blink_led(adpt_hba* host)
+{
+ if(host->FwDebugBLEDflag_P != 0) {
+ if( readb(host->FwDebugBLEDflag_P) == 0xbc ){
+ return readb(host->FwDebugBLEDvalue_P);
+ }
+ }
+ return 0;
+}
+
+/*============================================================================
+ * Scsi host template interface functions
+ *============================================================================
+ */
+
+static struct pci_device_id dptids[] = {
+ { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci,dptids);
+
+static int adpt_detect(struct scsi_host_template* sht)
+{
+ struct pci_dev *pDev = NULL;
+ adpt_hba* pHba;
+
+ adpt_init();
+
+ PINFO("Detecting Adaptec I2O RAID controllers...\n");
+
+ /* search for all Adatpec I2O RAID cards */
+ while ((pDev = pci_find_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) {
+ if(pDev->device == PCI_DPT_DEVICE_ID ||
+ pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){
+ if(adpt_install_hba(sht, pDev) ){
+ PERROR("Could not Init an I2O RAID device\n");
+ PERROR("Will not try to detect others.\n");
+ return hba_count-1;
+ }
+ }
+ }
+
+ /* In INIT state, Activate IOPs */
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ // Activate does get status , init outbound, and get hrt
+ if (adpt_i2o_activate_hba(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ }
+ }
+
+
+ /* Active IOPs in HOLD state */
+
+rebuild_sys_tab:
+ if (hba_chain == NULL)
+ return 0;
+
+ /*
+ * If build_sys_table fails, we kill everything and bail
+ * as we can't init the IOPs w/o a system table
+ */
+ if (adpt_i2o_build_sys_table() < 0) {
+ adpt_i2o_sys_shutdown();
+ return 0;
+ }
+
+ PDEBUG("HBA's in HOLD state\n");
+
+ /* If IOP don't get online, we need to rebuild the System table */
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (adpt_i2o_online_hba(pHba) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ goto rebuild_sys_tab;
+ }
+ }
+
+ /* Active IOPs now in OPERATIONAL state */
+ PDEBUG("HBA's in OPERATIONAL state\n");
+
+ printk("dpti: If you have a lot of devices this could take a few minutes.\n");
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name);
+ if (adpt_i2o_lct_get(pHba) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+
+ if (adpt_i2o_parse_lct(pHba) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+ adpt_inquiry(pHba);
+ }
+
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if( adpt_scsi_register(pHba,sht) < 0){
+ adpt_i2o_delete_hba(pHba);
+ continue;
+ }
+ pHba->initialized = TRUE;
+ pHba->state &= ~DPTI_STATE_RESET;
+ }
+
+ // Register our control device node
+ // nodes will need to be created in /dev to access this
+ // the nodes can not be created from within the driver
+ if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) {
+ adpt_i2o_sys_shutdown();
+ return 0;
+ }
+ return hba_count;
+}
+
+
+/*
+ * scsi_unregister will be called AFTER we return.
+ */
+static int adpt_release(struct Scsi_Host *host)
+{
+ adpt_hba* pHba = (adpt_hba*) host->hostdata[0];
+// adpt_i2o_quiesce_hba(pHba);
+ adpt_i2o_delete_hba(pHba);
+ scsi_unregister(host);
+ return 0;
+}
+
+
+static void adpt_inquiry(adpt_hba* pHba)
+{
+ u32 msg[14];
+ u32 *mptr;
+ u32 *lenptr;
+ int direction;
+ int scsidir;
+ u32 len;
+ u32 reqlen;
+ u8* buf;
+ u8 scb[16];
+ s32 rcode;
+
+ memset(msg, 0, sizeof(msg));
+ buf = (u8*)kmalloc(80,GFP_KERNEL|ADDR32);
+ if(!buf){
+ printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
+ return;
+ }
+ memset((void*)buf, 0, 36);
+
+ len = 36;
+ direction = 0x00000000;
+ scsidir =0x40000000; // DATA IN (iop<--dev)
+
+ reqlen = 14; // SINGLE SGE
+ /* Stick the headers on */
+ msg[0] = reqlen<<16 | SGL_OFFSET_12;
+ msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID);
+ msg[2] = 0;
+ msg[3] = 0;
+ // Adaptec/DPT Private stuff
+ msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16;
+ msg[5] = ADAPTER_TID | 1<<16 /* Interpret*/;
+ /* Direction, disconnect ok | sense data | simple queue , CDBLen */
+ // I2O_SCB_FLAG_ENABLE_DISCONNECT |
+ // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG |
+ // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
+ msg[6] = scsidir|0x20a00000| 6 /* cmd len*/;
+
+ mptr=msg+7;
+
+ memset(scb, 0, sizeof(scb));
+ // Write SCSI command into the message - always 16 byte block
+ scb[0] = INQUIRY;
+ scb[1] = 0;
+ scb[2] = 0;
+ scb[3] = 0;
+ scb[4] = 36;
+ scb[5] = 0;
+ // Don't care about the rest of scb
+
+ memcpy(mptr, scb, sizeof(scb));
+ mptr+=4;
+ lenptr=mptr++; /* Remember me - fill in when we know */
+
+ /* Now fill in the SGList and command */
+ *lenptr = len;
+ *mptr++ = 0xD0000000|direction|len;
+ *mptr++ = virt_to_bus(buf);
+
+ // Send it on it's way
+ rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120);
+ if (rcode != 0) {
+ sprintf(pHba->detail, "Adaptec I2O RAID");
+ printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode);
+ if (rcode != -ETIME && rcode != -EINTR)
+ kfree(buf);
+ } else {
+ memset(pHba->detail, 0, sizeof(pHba->detail));
+ memcpy(&(pHba->detail), "Vendor: Adaptec ", 16);
+ memcpy(&(pHba->detail[16]), " Model: ", 8);
+ memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16);
+ memcpy(&(pHba->detail[40]), " FW: ", 4);
+ memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4);
+ pHba->detail[48] = '\0'; /* precautionary */
+ kfree(buf);
+ }
+ adpt_i2o_status_get(pHba);
+ return ;
+}
+
+
+static int adpt_slave_configure(struct scsi_device * device)
+{
+ struct Scsi_Host *host = device->host;
+ adpt_hba* pHba;
+
+ pHba = (adpt_hba *) host->hostdata[0];
+
+ if (host->can_queue && device->tagged_supported) {
+ scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
+ host->can_queue - 1);
+ } else {
+ scsi_adjust_queue_depth(device, 0, 1);
+ }
+ return 0;
+}
+
+static int adpt_queue(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
+{
+ adpt_hba* pHba = NULL;
+ struct adpt_device* pDev = NULL; /* dpt per device information */
+ ulong timeout = jiffies + (TMOUT_SCSI*HZ);
+
+ cmd->scsi_done = done;
+ /*
+ * SCSI REQUEST_SENSE commands will be executed automatically by the
+ * Host Adapter for any errors, so they should not be executed
+ * explicitly unless the Sense Data is zero indicating that no error
+ * occurred.
+ */
+
+ if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) {
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
+
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ if (!pHba) {
+ return FAILED;
+ }
+
+ rmb();
+ /*
+ * TODO: I need to block here if I am processing ioctl cmds
+ * but if the outstanding cmds all finish before the ioctl,
+ * the scsi-core will not know to start sending cmds to me again.
+ * I need to a way to restart the scsi-cores queues or should I block
+ * calling scsi_done on the outstanding cmds instead
+ * for now we don't set the IOCTL state
+ */
+ if(((pHba->state) & DPTI_STATE_IOCTL) || ((pHba->state) & DPTI_STATE_RESET)) {
+ pHba->host->last_reset = jiffies;
+ pHba->host->resetting = 1;
+ return 1;
+ }
+
+ if(cmd->eh_state != SCSI_STATE_QUEUED){
+ // If we are not doing error recovery
+ mod_timer(&cmd->eh_timeout, timeout);
+ }
+
+ // TODO if the cmd->device if offline then I may need to issue a bus rescan
+ // followed by a get_lct to see if the device is there anymore
+ if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) {
+ /*
+ * First command request for this device. Set up a pointer
+ * to the device structure. This should be a TEST_UNIT_READY
+ * command from scan_scsis_single.
+ */
+ if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun)) == NULL) {
+ // TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response
+ // with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue.
+ cmd->result = (DID_NO_CONNECT << 16);
+ cmd->scsi_done(cmd);
+ return 0;
+ }
+ cmd->device->hostdata = pDev;
+ }
+ pDev->pScsi_dev = cmd->device;
+
+ /*
+ * If we are being called from when the device is being reset,
+ * delay processing of the command until later.
+ */
+ if (pDev->state & DPTI_DEV_RESET ) {
+ return FAILED;
+ }
+ return adpt_scsi_to_i2o(pHba, cmd, pDev);
+}
+
+static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int geom[])
+{
+ int heads=-1;
+ int sectors=-1;
+ int cylinders=-1;
+
+ // *** First lets set the default geometry ****
+
+ // If the capacity is less than ox2000
+ if (capacity < 0x2000 ) { // floppy
+ heads = 18;
+ sectors = 2;
+ }
+ // else if between 0x2000 and 0x20000
+ else if (capacity < 0x20000) {
+ heads = 64;
+ sectors = 32;
+ }
+ // else if between 0x20000 and 0x40000
+ else if (capacity < 0x40000) {
+ heads = 65;
+ sectors = 63;
+ }
+ // else if between 0x4000 and 0x80000
+ else if (capacity < 0x80000) {
+ heads = 128;
+ sectors = 63;
+ }
+ // else if greater than 0x80000
+ else {
+ heads = 255;
+ sectors = 63;
+ }
+ cylinders = sector_div(capacity, heads * sectors);
+
+ // Special case if CDROM
+ if(sdev->type == 5) { // CDROM
+ heads = 252;
+ sectors = 63;
+ cylinders = 1111;
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ PDEBUG("adpt_bios_param: exit\n");
+ return 0;
+}
+
+
+static const char *adpt_info(struct Scsi_Host *host)
+{
+ adpt_hba* pHba;
+
+ pHba = (adpt_hba *) host->hostdata[0];
+ return (char *) (pHba->detail);
+}
+
+static int adpt_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
+ int length, int inout)
+{
+ struct adpt_device* d;
+ int id;
+ int chan;
+ int len = 0;
+ int begin = 0;
+ int pos = 0;
+ adpt_hba* pHba;
+ int unit;
+
+ *start = buffer;
+ if (inout == TRUE) {
+ /*
+ * The user has done a write and wants us to take the
+ * data in the buffer and do something with it.
+ * proc_scsiwrite calls us with inout = 1
+ *
+ * Read data from buffer (writing to us) - NOT SUPPORTED
+ */
+ return -EINVAL;
+ }
+
+ /*
+ * inout = 0 means the user has done a read and wants information
+ * returned, so we write information about the cards into the buffer
+ * proc_scsiread() calls us with inout = 0
+ */
+
+ // Find HBA (host bus adapter) we are looking for
+ down(&adpt_configuration_lock);
+ for (pHba = hba_chain; pHba; pHba = pHba->next) {
+ if (pHba->host == host) {
+ break; /* found adapter */
+ }
+ }
+ up(&adpt_configuration_lock);
+ if (pHba == NULL) {
+ return 0;
+ }
+ host = pHba->host;
+
+ len = sprintf(buffer , "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION);
+ len += sprintf(buffer+len, "%s\n", pHba->detail);
+ len += sprintf(buffer+len, "SCSI Host=scsi%d Control Node=/dev/%s irq=%d\n",
+ pHba->host->host_no, pHba->name, host->irq);
+ len += sprintf(buffer+len, "\tpost fifo size = %d\n\treply fifo size = %d\n\tsg table size = %d\n\n",
+ host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize);
+
+ pos = begin + len;
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ /*
+ * If we haven't even written to where we last left
+ * off (the last time we were called), reset the
+ * beginning pointer.
+ */
+ len = 0;
+ begin = pos;
+ }
+ len += sprintf(buffer+len, "Devices:\n");
+ for(chan = 0; chan < MAX_CHANNEL; chan++) {
+ for(id = 0; id < MAX_ID; id++) {
+ d = pHba->channel[chan].device[id];
+ while(d){
+ len += sprintf(buffer+len,"\t%-24.24s", d->pScsi_dev->vendor);
+ len += sprintf(buffer+len," Rev: %-8.8s\n", d->pScsi_dev->rev);
+ pos = begin + len;
+
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ unit = d->pI2o_dev->lct_data.tid;
+ len += sprintf(buffer+len, "\tTID=%d, (Channel=%d, Target=%d, Lun=%d) (%s)\n\n",
+ unit, (int)d->scsi_channel, (int)d->scsi_id, (int)d->scsi_lun,
+ scsi_device_online(d->pScsi_dev)? "online":"offline");
+ pos = begin + len;
+
+ /* CHECKPOINT */
+ if(pos > offset + length) {
+ goto stop_output;
+ }
+ if(pos <= offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ d = d->next_lun;
+ }
+ }
+ }
+
+ /*
+ * begin is where we last checked our position with regards to offset
+ * begin is always less than offset. len is relative to begin. It
+ * is the number of bytes written past begin
+ *
+ */
+stop_output:
+ /* stop the output and calculate the correct length */
+ *(buffer + len) = '\0';
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length) {
+ len = length;
+ } else if(len < 0){
+ len = 0;
+ **start = '\0';
+ }
+ return len;
+}
+
+
+/*===========================================================================
+ * Error Handling routines
+ *===========================================================================
+ */
+
+static int adpt_abort(struct scsi_cmnd * cmd)
+{
+ adpt_hba* pHba = NULL; /* host bus adapter structure */
+ struct adpt_device* dptdevice; /* dpt per device information */
+ u32 msg[5];
+ int rcode;
+
+ if(cmd->serial_number == 0){
+ return FAILED;
+ }
+ pHba = (adpt_hba*) cmd->device->host->hostdata[0];
+ printk(KERN_INFO"%s: Trying to Abort cmd=%ld\n",pHba->name, cmd->serial_number);
+ if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
+ printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
+ return FAILED;
+ }
+
+ memset(msg, 0, sizeof(msg));
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
+ msg[2] = 0;
+ msg[3]= 0;
+ msg[4] = (u32)cmd;
+ if( (rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER)) != 0){
+ if(rcode == -EOPNOTSUPP ){
+ printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Abort cmd=%ld failed.\n",pHba->name, cmd->serial_number);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Abort cmd=%ld complete.\n",pHba->name, cmd->serial_number);
+ return SUCCESS;
+}
+
+
+#define I2O_DEVICE_RESET 0x27
+// This is the same for BLK and SCSI devices
+// NOTE this is wrong in the i2o.h definitions
+// This is not currently supported by our adapter but we issue it anyway
+static int adpt_device_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ u32 msg[4];
+ u32 rcode;
+ int old_state;
+ struct adpt_device* d = (void*) cmd->device->hostdata;
+
+ pHba = (void*) cmd->device->host->hostdata[0];
+ printk(KERN_INFO"%s: Trying to reset device\n",pHba->name);
+ if (!d) {
+ printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name);
+ return FAILED;
+ }
+ memset(msg, 0, sizeof(msg));
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid);
+ msg[2] = 0;
+ msg[3] = 0;
+
+ old_state = d->state;
+ d->state |= DPTI_DEV_RESET;
+ if( (rcode = adpt_i2o_post_wait(pHba, (void*)msg,sizeof(msg), FOREVER)) ){
+ d->state = old_state;
+ if(rcode == -EOPNOTSUPP ){
+ printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
+ return FAILED;
+ }
+ printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
+ return FAILED;
+ } else {
+ d->state = old_state;
+ printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
+ return SUCCESS;
+ }
+}
+
+
+#define I2O_HBA_BUS_RESET 0x87
+// This version of bus reset is called by the eh_error handler
+static int adpt_bus_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ u32 msg[4];
+
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ memset(msg, 0, sizeof(msg));
+ printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid );
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
+ msg[2] = 0;
+ msg[3] = 0;
+ if(adpt_i2o_post_wait(pHba, (void*)msg,sizeof(msg), FOREVER) ){
+ printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
+ return FAILED;
+ } else {
+ printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name);
+ return SUCCESS;
+ }
+}
+
+// This version of reset is called by the eh_error_handler
+static int adpt_reset(struct scsi_cmnd* cmd)
+{
+ adpt_hba* pHba;
+ int rcode;
+ pHba = (adpt_hba*)cmd->device->host->hostdata[0];
+ printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n",pHba->name,cmd->device->channel,pHba->channel[cmd->device->channel].tid );
+ rcode = adpt_hba_reset(pHba);
+ if(rcode == 0){
+ printk(KERN_WARNING"%s: HBA reset complete\n",pHba->name);
+ return SUCCESS;
+ } else {
+ printk(KERN_WARNING"%s: HBA reset failed (%x)\n",pHba->name, rcode);
+ return FAILED;
+ }
+}
+
+// This version of reset is called by the ioctls and indirectly from eh_error_handler via adpt_reset
+static int adpt_hba_reset(adpt_hba* pHba)
+{
+ int rcode;
+
+ pHba->state |= DPTI_STATE_RESET;
+
+ // Activate does get status , init outbound, and get hrt
+ if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) {
+ printk(KERN_ERR "%s: Could not activate\n", pHba->name);
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+
+ if ((rcode=adpt_i2o_build_sys_table()) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ PDEBUG("%s: in HOLD state\n",pHba->name);
+
+ if ((rcode=adpt_i2o_online_hba(pHba)) < 0) {
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ PDEBUG("%s: in OPERATIONAL state\n",pHba->name);
+
+ if ((rcode=adpt_i2o_lct_get(pHba)) < 0){
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+
+ if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){
+ adpt_i2o_delete_hba(pHba);
+ return rcode;
+ }
+ pHba->state &= ~DPTI_STATE_RESET;
+
+ adpt_fail_posted_scbs(pHba);
+ return 0; /* return success */
+}
+
+/*===========================================================================
+ *
+ *===========================================================================
+ */
+
+
+static void adpt_i2o_sys_shutdown(void)
+{
+ adpt_hba *pHba, *pNext;
+ struct adpt_i2o_post_wait_data *p1, *p2;
+
+ printk(KERN_INFO"Shutting down Adaptec I2O controllers.\n");
+ printk(KERN_INFO" This could take a few minutes if there are many devices attached\n");
+ /* Delete all IOPs from the controller chain */
+ /* They should have already been released by the
+ * scsi-core
+ */
+ for (pHba = hba_chain; pHba; pHba = pNext) {
+ pNext = pHba->next;
+ adpt_i2o_delete_hba(pHba);
+ }
+
+ /* Remove any timedout entries from the wait queue. */
+ p2 = NULL;
+// spin_lock_irqsave(&adpt_post_wait_lock, flags);
+ /* Nothing should be outstanding at this point so just
+ * free them
+ */
+ for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p2->next) {
+ kfree(p1);
+ }
+// spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
+ adpt_post_wait_queue = NULL;
+
+ printk(KERN_INFO "Adaptec I2O controllers down.\n");
+}
+
+/*
+ * reboot/shutdown notification.
+ *
+ * - Quiesce each IOP in the system
+ *
+ */
+
+#ifdef REBOOT_NOTIFIER
+static int adpt_reboot_event(struct notifier_block *n, ulong code, void *p)
+{
+
+ if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+
+ adpt_i2o_sys_shutdown();
+
+ return NOTIFY_DONE;
+}
+#endif
+
+
+static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev)
+{
+
+ adpt_hba* pHba = NULL;
+ adpt_hba* p = NULL;
+ ulong base_addr0_phys = 0;
+ ulong base_addr1_phys = 0;
+ u32 hba_map0_area_size = 0;
+ u32 hba_map1_area_size = 0;
+ void __iomem *base_addr_virt = NULL;
+ void __iomem *msg_addr_virt = NULL;
+
+ int raptorFlag = FALSE;
+ int i;
+
+ if(pci_enable_device(pDev)) {
+ return -EINVAL;
+ }
+ pci_set_master(pDev);
+ if (pci_set_dma_mask(pDev, 0xffffffffffffffffULL) &&
+ pci_set_dma_mask(pDev, 0xffffffffULL))
+ return -EINVAL;
+
+ base_addr0_phys = pci_resource_start(pDev,0);
+ hba_map0_area_size = pci_resource_len(pDev,0);
+
+ // Check if standard PCI card or single BAR Raptor
+ if(pDev->device == PCI_DPT_DEVICE_ID){
+ if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){
+ // Raptor card with this device id needs 4M
+ hba_map0_area_size = 0x400000;
+ } else { // Not Raptor - it is a PCI card
+ if(hba_map0_area_size > 0x100000 ){
+ hba_map0_area_size = 0x100000;
+ }
+ }
+ } else {// Raptor split BAR config
+ // Use BAR1 in this configuration
+ base_addr1_phys = pci_resource_start(pDev,1);
+ hba_map1_area_size = pci_resource_len(pDev,1);
+ raptorFlag = TRUE;
+ }
+
+
+ base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size);
+ if (!base_addr_virt) {
+ PERROR("dpti: adpt_config_hba: io remap failed\n");
+ return -EINVAL;
+ }
+
+ if(raptorFlag == TRUE) {
+ msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size );
+ if (!msg_addr_virt) {
+ PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n");
+ iounmap(base_addr_virt);
+ return -EINVAL;
+ }
+ } else {
+ msg_addr_virt = base_addr_virt;
+ }
+
+ // Allocate and zero the data structure
+ pHba = kmalloc(sizeof(adpt_hba), GFP_KERNEL);
+ if( pHba == NULL) {
+ if(msg_addr_virt != base_addr_virt){
+ iounmap(msg_addr_virt);
+ }
+ iounmap(base_addr_virt);
+ return -ENOMEM;
+ }
+ memset(pHba, 0, sizeof(adpt_hba));
+
+ down(&adpt_configuration_lock);
+ for(i=0;i<DPTI_MAX_HBA;i++) {
+ if(hbas[i]==NULL) {
+ hbas[i]=pHba;
+ break;
+ }
+ }
+
+ if(hba_chain != NULL){
+ for(p = hba_chain; p->next; p = p->next);
+ p->next = pHba;
+ } else {
+ hba_chain = pHba;
+ }
+ pHba->next = NULL;
+ pHba->unit = hba_count;
+ sprintf(pHba->name, "dpti%d", i);
+ hba_count++;
+
+ up(&adpt_configuration_lock);
+
+ pHba->pDev = pDev;
+ pHba->base_addr_phys = base_addr0_phys;
+
+ // Set up the Virtual Base Address of the I2O Device
+ pHba->base_addr_virt = base_addr_virt;
+ pHba->msg_addr_virt = msg_addr_virt;
+ pHba->irq_mask = base_addr_virt+0x30;
+ pHba->post_port = base_addr_virt+0x40;
+ pHba->reply_port = base_addr_virt+0x44;
+
+ pHba->hrt = NULL;
+ pHba->lct = NULL;
+ pHba->lct_size = 0;
+ pHba->status_block = NULL;
+ pHba->post_count = 0;
+ pHba->state = DPTI_STATE_RESET;
+ pHba->pDev = pDev;
+ pHba->devices = NULL;
+
+ // Initializing the spinlocks
+ spin_lock_init(&pHba->state_lock);
+ spin_lock_init(&adpt_post_wait_lock);
+
+ if(raptorFlag == 0){
+ printk(KERN_INFO"Adaptec I2O RAID controller %d at %p size=%x irq=%d\n",
+ hba_count-1, base_addr_virt, hba_map0_area_size, pDev->irq);
+ } else {
+ printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d\n",hba_count-1, pDev->irq);
+ printk(KERN_INFO" BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size);
+ printk(KERN_INFO" BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size);
+ }
+
+ if (request_irq (pDev->irq, adpt_isr, SA_SHIRQ, pHba->name, pHba)) {
+ printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq);
+ adpt_i2o_delete_hba(pHba);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static void adpt_i2o_delete_hba(adpt_hba* pHba)
+{
+ adpt_hba* p1;
+ adpt_hba* p2;
+ struct i2o_device* d;
+ struct i2o_device* next;
+ int i;
+ int j;
+ struct adpt_device* pDev;
+ struct adpt_device* pNext;
+
+
+ down(&adpt_configuration_lock);
+ // scsi_unregister calls our adpt_release which
+ // does a quiese
+ if(pHba->host){
+ free_irq(pHba->host->irq, pHba);
+ }
+ for(i=0;i<DPTI_MAX_HBA;i++) {
+ if(hbas[i]==pHba) {
+ hbas[i] = NULL;
+ }
+ }
+ p2 = NULL;
+ for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){
+ if(p1 == pHba) {
+ if(p2) {
+ p2->next = p1->next;
+ } else {
+ hba_chain = p1->next;
+ }
+ break;
+ }
+ }
+
+ hba_count--;
+ up(&adpt_configuration_lock);
+
+ iounmap(pHba->base_addr_virt);
+ if(pHba->msg_addr_virt != pHba->base_addr_virt){
+ iounmap(pHba->msg_addr_virt);
+ }
+ if(pHba->hrt) {
+ kfree(pHba->hrt);
+ }
+ if(pHba->lct){
+ kfree(pHba->lct);
+ }
+ if(pHba->status_block) {
+ kfree(pHba->status_block);
+ }
+ if(pHba->reply_pool){
+ kfree(pHba->reply_pool);
+ }
+
+ for(d = pHba->devices; d ; d = next){
+ next = d->next;
+ kfree(d);
+ }
+ for(i = 0 ; i < pHba->top_scsi_channel ; i++){
+ for(j = 0; j < MAX_ID; j++){
+ if(pHba->channel[i].device[j] != NULL){
+ for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){
+ pNext = pDev->next_lun;
+ kfree(pDev);
+ }
+ }
+ }
+ }
+ kfree(pHba);
+
+ if(hba_count <= 0){
+ unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER);
+ }
+}
+
+
+static int adpt_init(void)
+{
+ int i;
+
+ printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n");
+ for (i = 0; i < DPTI_MAX_HBA; i++) {
+ hbas[i] = NULL;
+ }
+#ifdef REBOOT_NOTIFIER
+ register_reboot_notifier(&adpt_reboot_notifier);
+#endif
+
+ return 0;
+}
+
+
+static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun)
+{
+ struct adpt_device* d;
+
+ if(chan < 0 || chan >= MAX_CHANNEL)
+ return NULL;
+
+ if( pHba->channel[chan].device == NULL){
+ printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n");
+ return NULL;
+ }
+
+ d = pHba->channel[chan].device[id];
+ if(!d || d->tid == 0) {
+ return NULL;
+ }
+
+ /* If it is the only lun at that address then this should match*/
+ if(d->scsi_lun == lun){
+ return d;
+ }
+
+ /* else we need to look through all the luns */
+ for(d=d->next_lun ; d ; d = d->next_lun){
+ if(d->scsi_lun == lun){
+ return d;
+ }
+ }
+ return NULL;
+}
+
+
+static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout)
+{
+ // I used my own version of the WAIT_QUEUE_HEAD
+ // to handle some version differences
+ // When embedded in the kernel this could go back to the vanilla one
+ ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post);
+ int status = 0;
+ ulong flags = 0;
+ struct adpt_i2o_post_wait_data *p1, *p2;
+ struct adpt_i2o_post_wait_data *wait_data =
+ kmalloc(sizeof(struct adpt_i2o_post_wait_data),GFP_KERNEL);
+ adpt_wait_queue_t wait;
+
+ if(!wait_data){
+ return -ENOMEM;
+ }
+ /*
+ * The spin locking is needed to keep anyone from playing
+ * with the queue pointers and id while we do the same
+ */
+ spin_lock_irqsave(&adpt_post_wait_lock, flags);
+ // TODO we need a MORE unique way of getting ids
+ // to support async LCT get
+ wait_data->next = adpt_post_wait_queue;
+ adpt_post_wait_queue = wait_data;
+ adpt_post_wait_id++;
+ adpt_post_wait_id &= 0x7fff;
+ wait_data->id = adpt_post_wait_id;
+ spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
+
+ wait_data->wq = &adpt_wq_i2o_post;
+ wait_data->status = -ETIMEDOUT;
+
+ // this code is taken from kernel/sched.c:interruptible_sleep_on_timeout
+ wait.task = current;
+ init_waitqueue_entry(&wait, current);
+ spin_lock_irqsave(&adpt_wq_i2o_post.lock, flags);
+ __add_wait_queue(&adpt_wq_i2o_post, &wait);
+ spin_unlock(&adpt_wq_i2o_post.lock);
+
+ msg[2] |= 0x80000000 | ((u32)wait_data->id);
+ timeout *= HZ;
+ if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){
+ set_current_state(TASK_INTERRUPTIBLE);
+ if(pHba->host)
+ spin_unlock_irq(pHba->host->host_lock);
+ if (!timeout)
+ schedule();
+ else{
+ timeout = schedule_timeout(timeout);
+ if (timeout == 0) {
+ // I/O issued, but cannot get result in
+ // specified time. Freeing resorces is
+ // dangerous.
+ status = -ETIME;
+ }
+ }
+ if(pHba->host)
+ spin_lock_irq(pHba->host->host_lock);
+ }
+ spin_lock_irq(&adpt_wq_i2o_post.lock);
+ __remove_wait_queue(&adpt_wq_i2o_post, &wait);
+ spin_unlock_irqrestore(&adpt_wq_i2o_post.lock, flags);
+
+ if(status == -ETIMEDOUT){
+ printk(KERN_INFO"dpti%d: POST WAIT T