diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/dpt_i2o.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/dpt_i2o.c')
-rw-r--r-- | drivers/scsi/dpt_i2o.c | 3381 |
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); |