|
|
2.26.02.006 - Fix 9550SX pchip reset timeout. | 2.26.02.006 - Fix 9550SX pchip reset timeout. |
Add big endian support. | Add big endian support. |
2.26.02.007 - Disable local interrupts during kmap/unmap_atomic(). | 2.26.02.007 - Disable local interrupts during kmap/unmap_atomic(). |
|
2.26.02.008 - Free irq handler in __twa_shutdown(). |
|
Serialize reset code. |
|
Add support for 9650SE controllers. |
*/ | */ |
| |
#include <linux/module.h> | #include <linux/module.h> |
|
|
#include "3w-9xxx.h" | #include "3w-9xxx.h" |
| |
/* Globals */ | /* Globals */ |
#define TW_DRIVER_VERSION "2.26.02.007" |
#define TW_DRIVER_VERSION "2.26.02.008" |
static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; | static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; |
static unsigned int twa_device_extension_count; | static unsigned int twa_device_extension_count; |
static int twa_major = -1; | static int twa_major = -1; |
|
Lines 566-574
static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
|
Link Here
|
|---|
|
goto out; | goto out; |
} | } |
| |
tw_dev->working_srl = fw_on_ctlr_srl; |
tw_dev->tw_compat_info.working_srl = fw_on_ctlr_srl; |
tw_dev->working_branch = fw_on_ctlr_branch; |
tw_dev->tw_compat_info.working_branch = fw_on_ctlr_branch; |
tw_dev->working_build = fw_on_ctlr_build; |
tw_dev->tw_compat_info.working_build = fw_on_ctlr_build; |
| |
/* Try base mode compatibility */ | /* Try base mode compatibility */ |
if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) { | if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) { |
|
Lines 590-599
static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
|
Link Here
|
|---|
|
} | } |
goto out; | goto out; |
} | } |
tw_dev->working_srl = TW_BASE_FW_SRL; |
tw_dev->tw_compat_info.working_srl = TW_BASE_FW_SRL; |
tw_dev->working_branch = TW_BASE_FW_BRANCH; |
tw_dev->tw_compat_info.working_branch = TW_BASE_FW_BRANCH; |
tw_dev->working_build = TW_BASE_FW_BUILD; |
tw_dev->tw_compat_info.working_build = TW_BASE_FW_BUILD; |
} |
} |
|
|
|
/* Load rest of compatibility struct */ |
|
strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); |
|
tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL; |
|
tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH; |
|
tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD; |
|
tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL; |
|
tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH; |
|
tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD; |
|
tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl; |
|
tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch; |
|
tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build; |
|
|
retval = 0; | retval = 0; |
out: | out: |
return retval; | return retval; |
|
Lines 631-637
static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
|
Link Here
|
|---|
|
goto out2; | goto out2; |
| |
/* Check data buffer size */ | /* Check data buffer size */ |
if (driver_command.buffer_length > TW_MAX_SECTORS * 512) { |
if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) { |
retval = TW_IOCTL_ERROR_OS_EINVAL; | retval = TW_IOCTL_ERROR_OS_EINVAL; |
goto out2; | goto out2; |
} | } |
|
Lines 680-692
static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
|
Link Here
|
|---|
|
/* Now wait for command to complete */ | /* Now wait for command to complete */ |
timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); | timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); |
| |
/* See if we reset while waiting for the ioctl to complete */ |
|
if (test_bit(TW_IN_RESET, &tw_dev->flags)) { |
|
clear_bit(TW_IN_RESET, &tw_dev->flags); |
|
retval = TW_IOCTL_ERROR_OS_ERESTARTSYS; |
|
goto out3; |
|
} |
|
|
|
/* We timed out, and didn't get an interrupt */ | /* We timed out, and didn't get an interrupt */ |
if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { | if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { |
/* Now we need to reset the board */ | /* Now we need to reset the board */ |
|
Lines 694-704
static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
|
Link Here
|
|---|
|
tw_dev->host->host_no, TW_DRIVER, 0xc, | tw_dev->host->host_no, TW_DRIVER, 0xc, |
cmd); | cmd); |
retval = TW_IOCTL_ERROR_OS_EIO; | retval = TW_IOCTL_ERROR_OS_EIO; |
spin_lock_irqsave(tw_dev->host->host_lock, flags); |
|
tw_dev->state[request_id] = TW_S_COMPLETED; |
|
twa_free_request_id(tw_dev, request_id); |
|
tw_dev->posted_request_count--; |
|
spin_unlock_irqrestore(tw_dev->host->host_lock, flags); |
|
twa_reset_device_extension(tw_dev, 1); | twa_reset_device_extension(tw_dev, 1); |
goto out3; | goto out3; |
} | } |
|
Lines 717-732
static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
|
Link Here
|
|---|
|
tw_ioctl->driver_command.status = 0; | tw_ioctl->driver_command.status = 0; |
/* Copy compatiblity struct into ioctl data buffer */ | /* Copy compatiblity struct into ioctl data buffer */ |
tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; | tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer; |
strncpy(tw_compat_info->driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION)); |
memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info)); |
tw_compat_info->working_srl = tw_dev->working_srl; |
|
tw_compat_info->working_branch = tw_dev->working_branch; |
|
tw_compat_info->working_build = tw_dev->working_build; |
|
tw_compat_info->driver_srl_high = TW_CURRENT_DRIVER_SRL; |
|
tw_compat_info->driver_branch_high = TW_CURRENT_DRIVER_BRANCH; |
|
tw_compat_info->driver_build_high = TW_CURRENT_DRIVER_BUILD; |
|
tw_compat_info->driver_srl_low = TW_BASE_FW_SRL; |
|
tw_compat_info->driver_branch_low = TW_BASE_FW_BRANCH; |
|
tw_compat_info->driver_build_low = TW_BASE_FW_BUILD; |
|
break; | break; |
case TW_IOCTL_GET_LAST_EVENT: | case TW_IOCTL_GET_LAST_EVENT: |
if (tw_dev->event_queue_wrapped) { | if (tw_dev->event_queue_wrapped) { |
|
Lines 895-901
static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
|
Link Here
|
|---|
|
} | } |
| |
if (status_reg_value & TW_STATUS_QUEUE_ERROR) { | if (status_reg_value & TW_STATUS_QUEUE_ERROR) { |
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); |
if ((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) || (!test_bit(TW_IN_RESET, &tw_dev->flags))) |
|
TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); |
writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); | writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); |
} | } |
| |
|
Lines 939-948
static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev)
|
Link Here
|
|---|
|
unsigned long before; | unsigned long before; |
int retval = 1; | int retval = 1; |
| |
if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) { |
if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) || |
|
(tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE)) { |
before = jiffies; | before = jiffies; |
while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) { | while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) { |
response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev)); | response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev)); |
|
msleep(1); |
if (time_after(jiffies, before + HZ * 30)) | if (time_after(jiffies, before + HZ * 30)) |
goto out; | goto out; |
} | } |
|
Lines 1214-1219
static irqreturn_t twa_interrupt(int irq, void *dev_instance)
|
Link Here
|
|---|
|
| |
handled = 1; | handled = 1; |
| |
|
/* If we are resetting, bail */ |
|
if (test_bit(TW_IN_RESET, &tw_dev->flags)) |
|
goto twa_interrupt_bail; |
|
|
/* Check controller for errors */ | /* Check controller for errors */ |
if (twa_check_bits(status_reg_value)) { | if (twa_check_bits(status_reg_value)) { |
if (twa_decode_bits(tw_dev, status_reg_value)) { | if (twa_decode_bits(tw_dev, status_reg_value)) { |
|
Lines 1355-1362
static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, d
|
Link Here
|
|---|
|
| |
if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { | if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { |
newcommand = &full_command_packet->command.newcommand; | newcommand = &full_command_packet->command.newcommand; |
newcommand->request_id__lunl = |
newcommand->request_id__lunl = |
TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id); |
cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id)); |
newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); | newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); |
newcommand->sg_list[0].length = cpu_to_le32(length); | newcommand->sg_list[0].length = cpu_to_le32(length); |
newcommand->sgl_entries__lunh = | newcommand->sgl_entries__lunh = |
|
Lines 1531-1536
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id,
|
Link Here
|
|---|
|
int retval = 1; | int retval = 1; |
| |
command_que_value = tw_dev->command_packet_phys[request_id]; | command_que_value = tw_dev->command_packet_phys[request_id]; |
|
|
|
/* For 9650SE write low 4 bytes first */ |
|
if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { |
|
command_que_value += TW_COMMAND_OFFSET; |
|
writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev)); |
|
} |
|
|
status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); | status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); |
| |
if (twa_check_bits(status_reg_value)) | if (twa_check_bits(status_reg_value)) |
|
Lines 1557-1569
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id,
|
Link Here
|
|---|
|
TW_UNMASK_COMMAND_INTERRUPT(tw_dev); | TW_UNMASK_COMMAND_INTERRUPT(tw_dev); |
goto out; | goto out; |
} else { | } else { |
/* We successfully posted the command packet */ |
if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { |
if (sizeof(dma_addr_t) > 4) { |
/* Now write upper 4 bytes */ |
command_que_value += TW_COMMAND_OFFSET; |
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4); |
writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); |
|
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4); |
|
} else { | } else { |
writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); |
if (sizeof(dma_addr_t) > 4) { |
|
command_que_value += TW_COMMAND_OFFSET; |
|
writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); |
|
writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4); |
|
} else { |
|
writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); |
|
} |
} | } |
tw_dev->state[request_id] = TW_S_POSTED; | tw_dev->state[request_id] = TW_S_POSTED; |
tw_dev->posted_request_count++; | tw_dev->posted_request_count++; |
|
Lines 1620-1633
static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res
|
Link Here
|
|---|
|
goto out; | goto out; |
| |
TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); | TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev); |
|
clear_bit(TW_IN_RESET, &tw_dev->flags); |
|
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; |
| |
/* Wake up any ioctl that was pending before the reset */ |
|
if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) { |
|
clear_bit(TW_IN_RESET, &tw_dev->flags); |
|
} else { |
|
tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE; |
|
wake_up(&tw_dev->ioctl_wqueue); |
|
} |
|
retval = 0; | retval = 0; |
out: | out: |
return retval; | return retval; |
|
Lines 1736-1741
static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
|
Link Here
|
|---|
|
"WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n", | "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n", |
TW_DRIVER, 0x2c, SCpnt->cmnd[0]); | TW_DRIVER, 0x2c, SCpnt->cmnd[0]); |
| |
|
/* Make sure we are not issuing an ioctl or resetting from ioctl */ |
|
mutex_lock(&tw_dev->ioctl_lock); |
|
|
/* Now reset the card and some of the device extension data */ | /* Now reset the card and some of the device extension data */ |
if (twa_reset_device_extension(tw_dev, 0)) { | if (twa_reset_device_extension(tw_dev, 0)) { |
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); | TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); |
|
Lines 1744-1749
static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
|
Link Here
|
|---|
|
| |
retval = SUCCESS; | retval = SUCCESS; |
out: | out: |
|
mutex_unlock(&tw_dev->ioctl_lock); |
return retval; | return retval; |
} /* End twa_scsi_eh_reset() */ | } /* End twa_scsi_eh_reset() */ |
| |
|
Lines 1753-1760
static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd
|
Link Here
|
|---|
|
int request_id, retval; | int request_id, retval; |
TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; | TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata; |
| |
|
/* If we are resetting due to timed out ioctl, report as busy */ |
|
if (test_bit(TW_IN_RESET, &tw_dev->flags)) { |
|
retval = SCSI_MLQUEUE_HOST_BUSY; |
|
goto out; |
|
} |
|
|
/* Check if this FW supports luns */ | /* Check if this FW supports luns */ |
if ((SCpnt->device->lun != 0) && (tw_dev->working_srl < TW_FW_SRL_LUNS_SUPPORTED)) { |
if ((SCpnt->device->lun != 0) && (tw_dev->tw_compat_info.working_srl < TW_FW_SRL_LUNS_SUPPORTED)) { |
SCpnt->result = (DID_BAD_TARGET << 16); | SCpnt->result = (DID_BAD_TARGET << 16); |
done(SCpnt); | done(SCpnt); |
retval = 0; | retval = 0; |
|
Lines 1960-1965
static void __twa_shutdown(TW_Device_Extension *tw_dev)
|
Link Here
|
|---|
|
/* Disable interrupts */ | /* Disable interrupts */ |
TW_DISABLE_INTERRUPTS(tw_dev); | TW_DISABLE_INTERRUPTS(tw_dev); |
| |
|
/* Free up the IRQ */ |
|
free_irq(tw_dev->tw_pci_dev->irq, tw_dev); |
|
|
printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no); | printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no); |
| |
/* Tell the card we are shutting down */ | /* Tell the card we are shutting down */ |
|
Lines 2091-2111
static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
|
Link Here
|
|---|
|
| |
/* Initialize the card */ | /* Initialize the card */ |
if (twa_reset_sequence(tw_dev, 0)) | if (twa_reset_sequence(tw_dev, 0)) |
goto out_release_mem_region; |
goto out_iounmap; |
| |
/* Set host specific parameters */ | /* Set host specific parameters */ |
host->max_id = TW_MAX_UNITS; |
if (pdev->device == PCI_DEVICE_ID_3WARE_9650SE) |
|
host->max_id = TW_MAX_UNITS_9650SE; |
|
else |
|
host->max_id = TW_MAX_UNITS; |
|
|
host->max_cmd_len = TW_MAX_CDB_LEN; | host->max_cmd_len = TW_MAX_CDB_LEN; |
| |
/* Channels aren't supported by adapter */ | /* Channels aren't supported by adapter */ |
host->max_lun = TW_MAX_LUNS(tw_dev->working_srl); |
host->max_lun = TW_MAX_LUNS(tw_dev->tw_compat_info.working_srl); |
host->max_channel = 0; | host->max_channel = 0; |
| |
/* Register the card with the kernel SCSI layer */ | /* Register the card with the kernel SCSI layer */ |
retval = scsi_add_host(host, &pdev->dev); | retval = scsi_add_host(host, &pdev->dev); |
if (retval) { | if (retval) { |
TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed"); | TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed"); |
goto out_release_mem_region; |
goto out_iounmap; |
} | } |
| |
pci_set_drvdata(pdev, host); | pci_set_drvdata(pdev, host); |
|
Lines 2145-2150
static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id
|
Link Here
|
|---|
|
| |
out_remove_host: | out_remove_host: |
scsi_remove_host(host); | scsi_remove_host(host); |
|
out_iounmap: |
|
iounmap(tw_dev->base_addr); |
out_release_mem_region: | out_release_mem_region: |
pci_release_regions(pdev); | pci_release_regions(pdev); |
out_free_device_extension: | out_free_device_extension: |
|
Lines 2170-2181
static void twa_remove(struct pci_dev *pdev)
|
Link Here
|
|---|
|
twa_major = -1; | twa_major = -1; |
} | } |
| |
/* Free up the IRQ */ |
|
free_irq(tw_dev->tw_pci_dev->irq, tw_dev); |
|
|
|
/* Shutdown the card */ | /* Shutdown the card */ |
__twa_shutdown(tw_dev); | __twa_shutdown(tw_dev); |
| |
|
/* Free IO remapping */ |
|
iounmap(tw_dev->base_addr); |
|
|
/* Free up the mem region */ | /* Free up the mem region */ |
pci_release_regions(pdev); | pci_release_regions(pdev); |
| |
|
Lines 2193-2198
static struct pci_device_id twa_pci_tbl[] __devinitdata = {
|
Link Here
|
|---|
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX, | { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX, |
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
|
{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9650SE, |
|
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{ } | { } |
}; | }; |
MODULE_DEVICE_TABLE(pci, twa_pci_tbl); | MODULE_DEVICE_TABLE(pci, twa_pci_tbl); |