summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/megaraid.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/megaraid.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/megaraid.c')
-rw-r--r--drivers/scsi/megaraid.c5122
1 files changed, 5122 insertions, 0 deletions
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
new file mode 100644
index 000000000000..8d707b29027d
--- /dev/null
+++ b/drivers/scsi/megaraid.c
@@ -0,0 +1,5122 @@
+/*
+ *
+ * Linux MegaRAID device driver
+ *
+ * Copyright © 2002 LSI Logic Corporation.
+ *
+ * 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.
+ *
+ * Copyright (c) 2002 Red Hat, Inc. All rights reserved.
+ * - fixes
+ * - speed-ups (list handling fixes, issued_list, optimizations.)
+ * - lots of cleanups.
+ *
+ * Copyright (c) 2003 Christoph Hellwig <hch@lst.de>
+ * - new-style, hotplug-aware pci probing and scsi registration
+ *
+ * Version : v2.00.3 (Feb 19, 2003) - Atul Mukker <Atul.Mukker@lsil.com>
+ *
+ * Description: Linux device driver for LSI Logic MegaRAID controller
+ *
+ * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493
+ * 518, 520, 531, 532
+ *
+ * This driver is supported by LSI Logic, with assistance from Red Hat, Dell,
+ * and others. Please send updates to the mailing list
+ * linux-scsi@vger.kernel.org .
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <scsi/scsicam.h>
+
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+
+#include "megaraid.h"
+
+#define MEGARAID_MODULE_VERSION "2.00.3"
+
+MODULE_AUTHOR ("LSI Logic Corporation");
+MODULE_DESCRIPTION ("LSI Logic MegaRAID driver");
+MODULE_LICENSE ("GPL");
+MODULE_VERSION(MEGARAID_MODULE_VERSION);
+
+static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN;
+module_param(max_cmd_per_lun, uint, 0);
+MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)");
+
+static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO;
+module_param(max_sectors_per_io, ushort, 0);
+MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)");
+
+
+static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT;
+module_param(max_mbox_busy_wait, ushort, 0);
+MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)");
+
+#define RDINDOOR(adapter) readl((adapter)->base + 0x20)
+#define RDOUTDOOR(adapter) readl((adapter)->base + 0x2C)
+#define WRINDOOR(adapter,value) writel(value, (adapter)->base + 0x20)
+#define WROUTDOOR(adapter,value) writel(value, (adapter)->base + 0x2C)
+
+/*
+ * Global variables
+ */
+
+static int hba_count;
+static adapter_t *hba_soft_state[MAX_CONTROLLERS];
+static struct proc_dir_entry *mega_proc_dir_entry;
+
+/* For controller re-ordering */
+static struct mega_hbas mega_hbas[MAX_CONTROLLERS];
+
+/*
+ * The File Operations structure for the serial/ioctl interface of the driver
+ */
+static struct file_operations megadev_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = megadev_ioctl,
+ .open = megadev_open,
+};
+
+/*
+ * Array to structures for storing the information about the controllers. This
+ * information is sent to the user level applications, when they do an ioctl
+ * for this information.
+ */
+static struct mcontroller mcontroller[MAX_CONTROLLERS];
+
+/* The current driver version */
+static u32 driver_ver = 0x02000000;
+
+/* major number used by the device for character interface */
+static int major;
+
+#define IS_RAID_CH(hba, ch) (((hba)->mega_ch_class >> (ch)) & 0x01)
+
+
+/*
+ * Debug variable to print some diagnostic messages
+ */
+static int trace_level;
+
+/**
+ * mega_setup_mailbox()
+ * @adapter - pointer to our soft state
+ *
+ * Allocates a 8 byte aligned memory for the handshake mailbox.
+ */
+static int
+mega_setup_mailbox(adapter_t *adapter)
+{
+ unsigned long align;
+
+ adapter->una_mbox64 = pci_alloc_consistent(adapter->dev,
+ sizeof(mbox64_t), &adapter->una_mbox64_dma);
+
+ if( !adapter->una_mbox64 ) return -1;
+
+ adapter->mbox = &adapter->una_mbox64->mbox;
+
+ adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) &
+ (~0UL ^ 0xFUL));
+
+ adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8);
+
+ align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox);
+
+ adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align;
+
+ /*
+ * Register the mailbox if the controller is an io-mapped controller
+ */
+ if( adapter->flag & BOARD_IOMAP ) {
+
+ outb_p(adapter->mbox_dma & 0xFF,
+ adapter->host->io_port + MBOX_PORT0);
+
+ outb_p((adapter->mbox_dma >> 8) & 0xFF,
+ adapter->host->io_port + MBOX_PORT1);
+
+ outb_p((adapter->mbox_dma >> 16) & 0xFF,
+ adapter->host->io_port + MBOX_PORT2);
+
+ outb_p((adapter->mbox_dma >> 24) & 0xFF,
+ adapter->host->io_port + MBOX_PORT3);
+
+ outb_p(ENABLE_MBOX_BYTE,
+ adapter->host->io_port + ENABLE_MBOX_REGION);
+
+ irq_ack(adapter);
+
+ irq_enable(adapter);
+ }
+
+ return 0;
+}
+
+
+/*
+ * mega_query_adapter()
+ * @adapter - pointer to our soft state
+ *
+ * Issue the adapter inquiry commands to the controller and find out
+ * information and parameter about the devices attached
+ */
+static int
+mega_query_adapter(adapter_t *adapter)
+{
+ dma_addr_t prod_info_dma_handle;
+ mega_inquiry3 *inquiry3;
+ u8 raw_mbox[sizeof(struct mbox_out)];
+ mbox_t *mbox;
+ int retval;
+
+ /* Initialize adapter inquiry mailbox */
+
+ mbox = (mbox_t *)raw_mbox;
+
+ memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE);
+ memset(&mbox->m_out, 0, sizeof(raw_mbox));
+
+ /*
+ * Try to issue Inquiry3 command
+ * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and
+ * update enquiry3 structure
+ */
+ mbox->m_out.xferaddr = (u32)adapter->buf_dma_handle;
+
+ inquiry3 = (mega_inquiry3 *)adapter->mega_buffer;
+
+ raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */
+ raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */
+
+ /* Issue a blocking command to the card */
+ if ((retval = issue_scb_block(adapter, raw_mbox))) {
+ /* the adapter does not support 40ld */
+
+ mraid_ext_inquiry *ext_inq;
+ mraid_inquiry *inq;
+ dma_addr_t dma_handle;
+
+ ext_inq = pci_alloc_consistent(adapter->dev,
+ sizeof(mraid_ext_inquiry), &dma_handle);
+
+ if( ext_inq == NULL ) return -1;
+
+ inq = &ext_inq->raid_inq;
+
+ mbox->m_out.xferaddr = (u32)dma_handle;
+
+ /*issue old 0x04 command to adapter */
+ mbox->m_out.cmd = MEGA_MBOXCMD_ADPEXTINQ;
+
+ issue_scb_block(adapter, raw_mbox);
+
+ /*
+ * update Enquiry3 and ProductInfo structures with
+ * mraid_inquiry structure
+ */
+ mega_8_to_40ld(inq, inquiry3,
+ (mega_product_info *)&adapter->product_info);
+
+ pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry),
+ ext_inq, dma_handle);
+
+ } else { /*adapter supports 40ld */
+ adapter->flag |= BOARD_40LD;
+
+ /*
+ * get product_info, which is static information and will be
+ * unchanged
+ */
+ prod_info_dma_handle = pci_map_single(adapter->dev, (void *)
+ &adapter->product_info,
+ sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+
+ mbox->m_out.xferaddr = prod_info_dma_handle;
+
+ raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
+ raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */
+
+ if ((retval = issue_scb_block(adapter, raw_mbox)))
+ printk(KERN_WARNING
+ "megaraid: Product_info cmd failed with error: %d\n",
+ retval);
+
+ pci_unmap_single(adapter->dev, prod_info_dma_handle,
+ sizeof(mega_product_info), PCI_DMA_FROMDEVICE);
+ }
+
+
+ /*
+ * kernel scans the channels from 0 to <= max_channel
+ */
+ adapter->host->max_channel =
+ adapter->product_info.nchannels + NVIRT_CHAN -1;
+
+ adapter->host->max_id = 16; /* max targets per channel */
+
+ adapter->host->max_lun = 7; /* Upto 7 luns for non disk devices */
+
+ adapter->host->cmd_per_lun = max_cmd_per_lun;
+
+ adapter->numldrv = inquiry3->num_ldrv;
+
+ adapter->max_cmds = adapter->product_info.max_commands;
+
+ if(adapter->max_cmds > MAX_COMMANDS)
+ adapter->max_cmds = MAX_COMMANDS;
+
+ adapter->host->can_queue = adapter->max_cmds - 1;
+
+ /*
+ * Get the maximum number of scatter-gather elements supported by this
+ * firmware
+ */
+ mega_get_max_sgl(adapter);
+
+ adapter->host->sg_tablesize = adapter->sglen;
+
+
+ /* use HP firmware and bios version encoding */
+ if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
+ sprintf (adapter->fw_version, "%c%d%d.%d%d",
+ adapter->product_info.fw_version[2],
+ adapter->product_info.fw_version[1] >> 8,
+ adapter->product_info.fw_version[1] & 0x0f,
+ adapter->product_info.fw_version[0] >> 8,
+ adapter->product_info.fw_version[0] & 0x0f);
+ sprintf (adapter->bios_version, "%c%d%d.%d%d",
+ adapter->product_info.bios_version[2],
+ adapter->product_info.bios_version[1] >> 8,
+ adapter->product_info.bios_version[1] & 0x0f,
+ adapter->product_info.bios_version[0] >> 8,
+ adapter->product_info.bios_version[0] & 0x0f);
+ } else {
+ memcpy(adapter->fw_version,
+ (char *)adapter->product_info.fw_version, 4);
+ adapter->fw_version[4] = 0;
+
+ memcpy(adapter->bios_version,
+ (char *)adapter->product_info.bios_version, 4);
+
+ adapter->bios_version[4] = 0;
+ }
+
+ printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n",
+ adapter->fw_version, adapter->bios_version, adapter->numldrv);
+
+ /*
+ * Do we support extended (>10 bytes) cdbs
+ */
+ adapter->support_ext_cdb = mega_support_ext_cdb(adapter);
+ if (adapter->support_ext_cdb)
+ printk(KERN_NOTICE "megaraid: supports extended CDBs.\n");
+
+
+ return 0;
+}
+
+/**
+ * mega_runpendq()
+ * @adapter - pointer to our soft state
+ *
+ * Runs through the list of pending requests.
+ */
+static inline void
+mega_runpendq(adapter_t *adapter)
+{
+ if(!list_empty(&adapter->pending_list))
+ __mega_runpendq(adapter);
+}
+
+/*
+ * megaraid_queue()
+ * @scmd - Issue this scsi command
+ * @done - the callback hook into the scsi mid-layer
+ *
+ * The command queuing entry point for the mid-layer.
+ */
+static int
+megaraid_queue(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *))
+{
+ adapter_t *adapter;
+ scb_t *scb;
+ int busy=0;
+
+ adapter = (adapter_t *)scmd->device->host->hostdata;
+
+ scmd->scsi_done = done;
+
+
+ /*
+ * Allocate and build a SCB request
+ * busy flag will be set if mega_build_cmd() command could not
+ * allocate scb. We will return non-zero status in that case.
+ * NOTE: scb can be null even though certain commands completed
+ * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would
+ * return 0 in that case.
+ */
+
+ scb = mega_build_cmd(adapter, scmd, &busy);
+
+ if(scb) {
+ scb->state |= SCB_PENDQ;
+ list_add_tail(&scb->list, &adapter->pending_list);
+
+ /*
+ * Check if the HBA is in quiescent state, e.g., during a
+ * delete logical drive opertion. If it is, don't run
+ * the pending_list.
+ */
+ if(atomic_read(&adapter->quiescent) == 0) {
+ mega_runpendq(adapter);
+ }
+ return 0;
+ }
+
+ return busy;
+}
+
+/**
+ * mega_allocate_scb()
+ * @adapter - pointer to our soft state
+ * @cmd - scsi command from the mid-layer
+ *
+ * Allocate a SCB structure. This is the central structure for controller
+ * commands.
+ */
+static inline scb_t *
+mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd)
+{
+ struct list_head *head = &adapter->free_list;
+ scb_t *scb;
+
+ /* Unlink command from Free List */
+ if( !list_empty(head) ) {
+
+ scb = list_entry(head->next, scb_t, list);
+
+ list_del_init(head->next);
+
+ scb->state = SCB_ACTIVE;
+ scb->cmd = cmd;
+ scb->dma_type = MEGA_DMA_TYPE_NONE;
+
+ return scb;
+ }
+
+ return NULL;
+}
+
+/**
+ * mega_get_ldrv_num()
+ * @adapter - pointer to our soft state
+ * @cmd - scsi mid layer command
+ * @channel - channel on the controller
+ *
+ * Calculate the logical drive number based on the information in scsi command
+ * and the channel number.
+ */
+static inline int
+mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel)
+{
+ int tgt;
+ int ldrv_num;
+
+ tgt = cmd->device->id;
+
+ if ( tgt > adapter->this_id )
+ tgt--; /* we do not get inquires for initiator id */
+
+ ldrv_num = (channel * 15) + tgt;
+
+
+ /*
+ * If we have a logical drive with boot enabled, project it first
+ */
+ if( adapter->boot_ldrv_enabled ) {
+ if( ldrv_num == 0 ) {
+ ldrv_num = adapter->boot_ldrv;
+ }
+ else {
+ if( ldrv_num <= adapter->boot_ldrv ) {
+ ldrv_num--;
+ }
+ }
+ }
+
+ /*
+ * If "delete logical drive" feature is enabled on this controller.
+ * Do only if at least one delete logical drive operation was done.
+ *
+ * Also, after logical drive deletion, instead of logical drive number,
+ * the value returned should be 0x80+logical drive id.
+ *
+ * These is valid only for IO commands.
+ */
+
+ if (adapter->support_random_del && adapter->read_ldidmap )
+ switch (cmd->cmnd[0]) {
+ case READ_6: /* fall through */
+ case WRITE_6: /* fall through */
+ case READ_10: /* fall through */
+ case WRITE_10:
+ ldrv_num += 0x80;
+ }
+
+ return ldrv_num;
+}
+
+/**
+ * mega_build_cmd()
+ * @adapter - pointer to our soft state
+ * @cmd - Prepare using this scsi command
+ * @busy - busy flag if no resources
+ *
+ * Prepares a command and scatter gather list for the controller. This routine
+ * also finds out if the commands is intended for a logical drive or a
+ * physical device and prepares the controller command accordingly.
+ *
+ * We also re-order the logical drives and physical devices based on their
+ * boot settings.
+ */
+static scb_t *
+mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy)
+{
+ mega_ext_passthru *epthru;
+ mega_passthru *pthru;
+ scb_t *scb;
+ mbox_t *mbox;
+ long seg;
+ char islogical;
+ int max_ldrv_num;
+ int channel = 0;
+ int target = 0;
+ int ldrv_num = 0; /* logical drive number */
+
+
+ /*
+ * filter the internal and ioctl commands
+ */
+ if((cmd->cmnd[0] == MEGA_INTERNAL_CMD)) {
+ return cmd->buffer;
+ }
+
+
+ /*
+ * We know what channels our logical drives are on - mega_find_card()
+ */
+ islogical = adapter->logdrv_chan[cmd->device->channel];
+
+ /*
+ * The theory: If physical drive is chosen for boot, all the physical
+ * devices are exported before the logical drives, otherwise physical
+ * devices are pushed after logical drives, in which case - Kernel sees
+ * the physical devices on virtual channel which is obviously converted
+ * to actual channel on the HBA.
+ */
+ if( adapter->boot_pdrv_enabled ) {
+ if( islogical ) {
+ /* logical channel */
+ channel = cmd->device->channel -
+ adapter->product_info.nchannels;
+ }
+ else {
+ /* this is physical channel */
+ channel = cmd->device->channel;
+ target = cmd->device->id;
+
+ /*
+ * boot from a physical disk, that disk needs to be
+ * exposed first IF both the channels are SCSI, then
+ * booting from the second channel is not allowed.
+ */
+ if( target == 0 ) {
+ target = adapter->boot_pdrv_tgt;
+ }
+ else if( target == adapter->boot_pdrv_tgt ) {
+ target = 0;
+ }
+ }
+ }
+ else {
+ if( islogical ) {
+ /* this is the logical channel */
+ channel = cmd->device->channel;
+ }
+ else {
+ /* physical channel */
+ channel = cmd->device->channel - NVIRT_CHAN;
+ target = cmd->device->id;
+ }
+ }
+
+
+ if(islogical) {
+
+ /* have just LUN 0 for each target on virtual channels */
+ if (cmd->device->lun) {
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ ldrv_num = mega_get_ldrv_num(adapter, cmd, channel);
+
+
+ max_ldrv_num = (adapter->flag & BOARD_40LD) ?
+ MAX_LOGICAL_DRIVES_40LD : MAX_LOGICAL_DRIVES_8LD;
+
+ /*
+ * max_ldrv_num increases by 0x80 if some logical drive was
+ * deleted.
+ */
+ if(adapter->read_ldidmap)
+ max_ldrv_num += 0x80;
+
+ if(ldrv_num > max_ldrv_num ) {
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ }
+ else {
+ if( cmd->device->lun > 7) {
+ /*
+ * Do not support lun >7 for physically accessed
+ * devices
+ */
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+ }
+
+ /*
+ *
+ * Logical drive commands
+ *
+ */
+ if(islogical) {
+ switch (cmd->cmnd[0]) {
+ case TEST_UNIT_READY:
+ memset(cmd->request_buffer, 0, cmd->request_bufflen);
+
+#if MEGA_HAVE_CLUSTERING
+ /*
+ * Do we support clustering and is the support enabled
+ * If no, return success always
+ */
+ if( !adapter->has_cluster ) {
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->raw_mbox[0] = MEGA_CLUSTER_CMD;
+ scb->raw_mbox[2] = MEGA_RESERVATION_STATUS;
+ scb->raw_mbox[3] = ldrv_num;
+
+ scb->dma_direction = PCI_DMA_NONE;
+
+ return scb;
+#else
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+#endif
+
+ case MODE_SENSE:
+ memset(cmd->request_buffer, 0, cmd->cmnd[4]);
+ cmd->result = (DID_OK << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+
+ case READ_CAPACITY:
+ case INQUIRY:
+
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d ",
+ adapter->host->host_no,
+ cmd->device->channel);
+ printk("for logical drives.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+
+ /* Allocate a SCB and initialize passthru */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+ pthru = scb->pthru;
+
+ mbox = (mbox_t *)scb->raw_mbox;
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+ memset(pthru, 0, sizeof(mega_passthru));
+
+ pthru->timeout = 0;
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 1;
+ pthru->logdrv = ldrv_num;
+ pthru->cdblen = cmd->cmd_len;
+ memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU64;
+ }
+ else {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU;
+ }
+
+ scb->dma_direction = PCI_DMA_FROMDEVICE;
+
+ pthru->numsgelements = mega_build_sglist(adapter, scb,
+ &pthru->dataxferaddr, &pthru->dataxferlen);
+
+ mbox->m_out.xferaddr = scb->pthru_dma_addr;
+
+ return scb;
+
+ case READ_6:
+ case WRITE_6:
+ case READ_10:
+ case WRITE_10:
+ case READ_12:
+ case WRITE_12:
+
+ /* Allocate a SCB and initialize mailbox */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+ mbox = (mbox_t *)scb->raw_mbox;
+
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+ mbox->m_out.logdrv = ldrv_num;
+
+ /*
+ * A little hack: 2nd bit is zero for all scsi read
+ * commands and is set for all scsi write commands
+ */
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = (*cmd->cmnd & 0x02) ?
+ MEGA_MBOXCMD_LWRITE64:
+ MEGA_MBOXCMD_LREAD64 ;
+ }
+ else {
+ mbox->m_out.cmd = (*cmd->cmnd & 0x02) ?
+ MEGA_MBOXCMD_LWRITE:
+ MEGA_MBOXCMD_LREAD ;
+ }
+
+ /*
+ * 6-byte READ(0x08) or WRITE(0x0A) cdb
+ */
+ if( cmd->cmd_len == 6 ) {
+ mbox->m_out.numsectors = (u32) cmd->cmnd[4];
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[1] << 16) |
+ ((u32)cmd->cmnd[2] << 8) |
+ (u32)cmd->cmnd[3];
+
+ mbox->m_out.lba &= 0x1FFFFF;
+
+#if MEGA_HAVE_STATS
+ /*
+ * Take modulo 0x80, since the logical drive
+ * number increases by 0x80 when a logical
+ * drive was deleted
+ */
+ if (*cmd->cmnd == READ_6) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * 10-byte READ(0x28) or WRITE(0x2A) cdb
+ */
+ if( cmd->cmd_len == 10 ) {
+ mbox->m_out.numsectors =
+ (u32)cmd->cmnd[8] |
+ ((u32)cmd->cmnd[7] << 8);
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[2] << 24) |
+ ((u32)cmd->cmnd[3] << 16) |
+ ((u32)cmd->cmnd[4] << 8) |
+ (u32)cmd->cmnd[5];
+
+#if MEGA_HAVE_STATS
+ if (*cmd->cmnd == READ_10) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * 12-byte READ(0xA8) or WRITE(0xAA) cdb
+ */
+ if( cmd->cmd_len == 12 ) {
+ mbox->m_out.lba =
+ ((u32)cmd->cmnd[2] << 24) |
+ ((u32)cmd->cmnd[3] << 16) |
+ ((u32)cmd->cmnd[4] << 8) |
+ (u32)cmd->cmnd[5];
+
+ mbox->m_out.numsectors =
+ ((u32)cmd->cmnd[6] << 24) |
+ ((u32)cmd->cmnd[7] << 16) |
+ ((u32)cmd->cmnd[8] << 8) |
+ (u32)cmd->cmnd[9];
+
+#if MEGA_HAVE_STATS
+ if (*cmd->cmnd == READ_12) {
+ adapter->nreads[ldrv_num%0x80]++;
+ adapter->nreadblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ } else {
+ adapter->nwrites[ldrv_num%0x80]++;
+ adapter->nwriteblocks[ldrv_num%0x80] +=
+ mbox->m_out.numsectors;
+ }
+#endif
+ }
+
+ /*
+ * If it is a read command
+ */
+ if( (*cmd->cmnd & 0x0F) == 0x08 ) {
+ scb->dma_direction = PCI_DMA_FROMDEVICE;
+ }
+ else {
+ scb->dma_direction = PCI_DMA_TODEVICE;
+ }
+
+ /* Calculate Scatter-Gather info */
+ mbox->m_out.numsgelements = mega_build_sglist(adapter, scb,
+ (u32 *)&mbox->m_out.xferaddr, (u32 *)&seg);
+
+ return scb;
+
+#if MEGA_HAVE_CLUSTERING
+ case RESERVE: /* Fall through */
+ case RELEASE:
+
+ /*
+ * Do we support clustering and is the support enabled
+ */
+ if( ! adapter->has_cluster ) {
+
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+
+ /* Allocate a SCB and initialize mailbox */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ scb->raw_mbox[0] = MEGA_CLUSTER_CMD;
+ scb->raw_mbox[2] = ( *cmd->cmnd == RESERVE ) ?
+ MEGA_RESERVE_LD : MEGA_RELEASE_LD;
+
+ scb->raw_mbox[3] = ldrv_num;
+
+ scb->dma_direction = PCI_DMA_NONE;
+
+ return scb;
+#endif
+
+ default:
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->scsi_done(cmd);
+ return NULL;
+ }
+ }
+
+ /*
+ * Passthru drive commands
+ */
+ else {
+ /* Allocate a SCB and initialize passthru */
+ if(!(scb = mega_allocate_scb(adapter, cmd))) {
+ *busy = 1;
+ return NULL;
+ }
+
+ mbox = (mbox_t *)scb->raw_mbox;
+ memset(mbox, 0, sizeof(scb->raw_mbox));
+
+ if( adapter->support_ext_cdb ) {
+
+ epthru = mega_prepare_extpassthru(adapter, scb, cmd,
+ channel, target);
+
+ mbox->m_out.cmd = MEGA_MBOXCMD_EXTPTHRU;
+
+ mbox->m_out.xferaddr = scb->epthru_dma_addr;
+
+ }
+ else {
+
+ pthru = mega_prepare_passthru(adapter, scb, cmd,
+ channel, target);
+
+ /* Initialize mailbox */
+ if( adapter->has_64bit_addr ) {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU64;
+ }
+ else {
+ mbox->m_out.cmd = MEGA_MBOXCMD_PASSTHRU;
+ }
+
+ mbox->m_out.xferaddr = scb->pthru_dma_addr;
+
+ }
+ return scb;
+ }
+ return NULL;
+}
+
+
+/**
+ * mega_prepare_passthru()
+ * @adapter - pointer to our soft state
+ * @scb - our scsi control block
+ * @cmd - scsi command from the mid-layer
+ * @channel - actual channel on the controller
+ * @target - actual id on the controller.
+ *
+ * prepare a command for the scsi physical devices.
+ */
+static mega_passthru *
+mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
+ int channel, int target)
+{
+ mega_passthru *pthru;
+
+ pthru = scb->pthru;
+ memset(pthru, 0, sizeof (mega_passthru));
+
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ pthru->timeout = 2;
+
+ pthru->ars = 1;
+ pthru->reqsenselen = 14;
+ pthru->islogical = 0;
+
+ pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel;
+
+ pthru->target = (adapter->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+
+ pthru->cdblen = cmd->cmd_len;
+ pthru->logdrv = cmd->device->lun;
+
+ memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
+ switch (cmd->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d [P%d] ",
+ adapter->host->host_no,
+ cmd->device->channel, channel);
+ printk("for physical devices.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+ /* Fall through */
+ default:
+ pthru->numsgelements = mega_build_sglist(adapter, scb,
+ &pthru->dataxferaddr, &pthru->dataxferlen);
+ break;
+ }
+ return pthru;
+}
+
+
+/**
+ * mega_prepare_extpassthru()
+ * @adapter - pointer to our soft state
+ * @scb - our scsi control block
+ * @cmd - scsi command from the mid-layer
+ * @channel - actual channel on the controller
+ * @target - actual id on the controller.
+ *
+ * prepare a command for the scsi physical devices. This rountine prepares
+ * commands for devices which can take extended CDBs (>10 bytes)
+ */
+static mega_ext_passthru *
+mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd,
+ int channel, int target)
+{
+ mega_ext_passthru *epthru;
+
+ epthru = scb->epthru;
+ memset(epthru, 0, sizeof(mega_ext_passthru));
+
+ /* 0=6sec/1=60sec/2=10min/3=3hrs */
+ epthru->timeout = 2;
+
+ epthru->ars = 1;
+ epthru->reqsenselen = 14;
+ epthru->islogical = 0;
+
+ epthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel;
+ epthru->target = (adapter->flag & BOARD_40LD) ?
+ (channel << 4) | target : target;
+
+ epthru->cdblen = cmd->cmd_len;
+ epthru->logdrv = cmd->device->lun;
+
+ memcpy(epthru->cdb, cmd->cmnd, cmd->cmd_len);
+
+ /* Not sure about the direction */
+ scb->dma_direction = PCI_DMA_BIDIRECTIONAL;
+
+ switch(cmd->cmnd[0]) {
+ case INQUIRY:
+ case READ_CAPACITY:
+ if(!(adapter->flag & (1L << cmd->device->channel))) {
+
+ printk(KERN_NOTICE
+ "scsi%d: scanning scsi channel %d [P%d] ",
+ adapter->host->host_no,
+ cmd->device->channel, channel);
+ printk("for physical devices.\n");
+
+ adapter->flag |= (1L << cmd->device->channel);
+ }
+ /* Fall through */
+ default:
+ epthru->numsgelements = mega_build_sglist(adapter, scb,
+ &epthru->dataxferaddr, &epthru->dataxferlen);
+ break;
+ }
+
+ return epthru;
+}
+
+static void
+__mega_runpendq(adapter_t *adapter)
+{
+ scb_t *scb;
+ struct list_head *pos, *next;
+
+ /* Issue any pending commands to the card */
+ list_for_each_safe(pos, next, &adapter->pending_list) {
+
+ scb = list_entry(pos, scb_t, list);
+
+ if( !(scb->state & SCB_ISSUED) ) {
+
+ if( issue_scb(adapter, scb) != 0 )
+ return;
+ }
+ }
+
+ return;
+}
+
+
+/**
+ * issue_scb()
+ * @adapter - pointer to our soft state
+ * @scb - scsi control block
+ *
+ * Post a command to the card if the mailbox is available, otherwise return
+ * busy. We also take the scb from the pending list if the mailbox is
+ * available.
+ */
+static int
+issue_scb(adapter_t *adapter, scb_t *scb)
+{
+ volatile mbox64_t *mbox64 = adapter->mbox64;
+ volatile mbox_t *mbox = adapter->mbox;
+ unsigned int i = 0;
+
+ if(unlikely(mbox->m_in.busy)) {
+ do {
+ udelay(1);
+ i++;
+ } while( mbox->m_in.busy && (i < max_mbox_busy_wait) );
+
+ if(mbox->m_in.busy) return -1;
+ }
+
+ /* Copy mailbox data into host structure */
+ memcpy((char *)&mbox->m_out, (char *)scb->raw_mbox,
+ sizeof(struct mbox_out));
+
+ mbox->m_out.cmdid = scb->idx; /* Set cmdid */
+ mbox->m_in.busy = 1; /* Set busy */
+
+
+ /*
+ * Increment the pending queue counter
+ */
+ atomic_inc(&adapter->pend_cmds);
+
+ switch (mbox->m_out.cmd) {
+ case MEGA_MBOXCMD_LREAD64:
+ case MEGA_MBOXCMD_LWRITE64:
+ case MEGA_MBOXCMD_PASSTHRU64:
+ case MEGA_MBOXCMD_EXTPTHRU:
+ mbox64->xfer_segment_lo = mbox->m_out.xferaddr;
+ mbox64->xfer_segment_hi = 0;
+ mbox->m_out.xferaddr = 0xFFFFFFFF;
+ break;
+ default:
+ mbox64->xfer_segment_lo = 0;
+ mbox64->xfer_segment_hi = 0;
+ }
+
+ /*
+ * post the command
+ */
+ scb->state |= SCB_ISSUED;
+
+ if( likely(adapter->flag & BOARD_MEMMAP) ) {
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0;
+ WRINDOOR(adapter, adapter->mbox_dma | 0x1);
+ }
+ else {
+ irq_enable(adapter);
+ issue_command(adapter);
+ }
+
+ return 0;
+}
+
+/*
+ * Wait until the controller's mailbox is available
+ */
+static inline int
+mega_busywait_mbox (adapter_t *adapter)
+{
+ if (adapter->mbox->m_in.busy)
+ return __mega_busywait_mbox(adapter);
+ return 0;
+}
+
+/**
+ * issue_scb_block()
+ * @adapter - pointer to our soft state
+ * @raw_mbox - the mailbox
+ *
+ * Issue a scb in synchronous and non-interrupt mode
+ */
+static int
+issue_scb_block(adapter_t *adapter, u_char *raw_mbox)
+{
+ volatile mbox64_t *mbox64 = adapter->mbox64;
+ volatile mbox_t *mbox = adapter->mbox;
+ u8 byte;
+
+ /* Wait until mailbox is free */
+ if(mega_busywait_mbox (adapter))
+ goto bug_blocked_mailbox;
+
+ /* Copy mailbox data into host structure */
+ memcpy((char *) mbox, raw_mbox, sizeof(struct mbox_out));
+ mbox->m_out.cmdid = 0xFE;
+ mbox->m_in.busy = 1;
+
+ switch (raw_mbox[0]) {
+ case MEGA_MBOXCMD_LREAD64:
+ case MEGA_MBOXCMD_LWRITE64:
+ case MEGA_MBOXCMD_PASSTHRU64:
+ case MEGA_MBOXCMD_EXTPTHRU:
+ mbox64->xfer_segment_lo = mbox->m_out.xferaddr;
+ mbox64->xfer_segment_hi = 0;
+ mbox->m_out.xferaddr = 0xFFFFFFFF;
+ break;
+ default:
+ mbox64->xfer_segment_lo = 0;
+ mbox64->xfer_segment_hi = 0;
+ }
+
+ if( likely(adapter->flag & BOARD_MEMMAP) ) {
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0;
+ mbox->m_in.numstatus = 0xFF;
+ mbox->m_in.status = 0xFF;
+ WRINDOOR(adapter, adapter->mbox_dma | 0x1);
+
+ while((volatile u8)mbox->m_in.numstatus == 0xFF)
+ cpu_relax();
+
+ mbox->m_in.numstatus = 0xFF;
+
+ while( (volatile u8)mbox->m_in.poll != 0x77 )
+ cpu_relax();
+
+ mbox->m_in.poll = 0;
+ mbox->m_in.ack = 0x77;
+
+ WRINDOOR(adapter, adapter->mbox_dma | 0x2);
+