patch-2.2.17 linux/drivers/scsi/3w-xxxx.c
Next file: linux/drivers/scsi/3w-xxxx.h
Previous file: linux/drivers/sbus/char/rtc.c
Back to the patch index
Back to the overall index
- Lines: 351
- Date:
Mon Sep 4 18:39:20 2000
- Orig file:
v2.2.16/drivers/scsi/3w-xxxx.c
- Orig date:
Mon Sep 4 18:37:37 2000
diff -u --recursive --new-file v2.2.16/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
@@ -2,6 +2,8 @@
3w-xxxx.c -- 3ware Storage Controller device driver for Linux.
Written By: Adam Radford <linux@3ware.com>
+ Modifications By: Joel Jacobson <linux@3ware.com>
+
Copyright (C) 1999-2000 3ware Inc.
Kernel compatablity By: Andre Hedrick <andre@suse.com>
@@ -47,6 +49,21 @@
For more information, goto:
http://www.3ware.com
+
+ History
+ -------
+ 0.1.000 - Initial release.
+ 0.4.000 - Added support for Asynchronous Event Notification through
+ ioctls for 3DM.
+ 1.0.000 - Added DPO & FUA bit support for WRITE_10 & WRITE_6 cdb
+ to disable drive write-cache before writes.
+ 1.1.000 - Fixed performance bug with DPO & FUA not existing for WRITE_6.
+ 1.2.000 - Added support for clean shutdown notification/feature table.
+ 1.02.00.001 - Added support for full command packet posts through ioctls
+ for 3DM.
+ Bug fix so hot spare drives don't show up.
+ 1.02.00.002 - Fix bug with tw_setfeature() call that caused oops on some
+ systems.
*/
#include <linux/module.h>
@@ -65,6 +82,7 @@
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/smp.h>
+#include <linux/reboot.h>
#include <asm/spinlock.h>
#include <asm/errno.h>
@@ -83,6 +101,12 @@
static int tw_copy_info(TW_Info *info, char *fmt, ...);
static void tw_copy_mem_info(TW_Info *info, char *data, int len);
static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int tw_halt(struct notifier_block *nb, ulong event, void *buf);
+
+/* Notifier block to get a notify on system shutdown/halt/reboot */
+static struct notifier_block tw_notifier = {
+ tw_halt, NULL, 0
+};
struct proc_dir_entry tw_scsi_proc_entry = {
PROC_SCSI_3W_XXXX,
@@ -91,7 +115,7 @@
};
/* Globals */
-char *tw_driver_version="1.1.000";
+char *tw_driver_version="1.02.00.002";
TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
int tw_device_extension_count = 0;
@@ -230,7 +254,7 @@
if (command_packet->status != 0) {
if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
/* Bad response */
- printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
} else {
/* We know this is a 3w-1x00, and doesn't support aen's */
@@ -568,6 +592,7 @@
TW_Device_Extension *tw_dev2;
struct pci_dev *tw_pci_dev = pci_devices;
u32 status_reg_value;
+ unsigned char c = 1;
dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) {
@@ -666,7 +691,7 @@
continue;
}
- error = tw_initconnection(tw_dev);
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
release_region((tw_dev->tw_pci_dev->base_address[0]), TW_IO_ADDRESS_RANGE);
@@ -726,10 +751,14 @@
/* Free the temporary device extension */
if (tw_dev)
kfree(tw_dev);
+ /* Tell the firmware we support shutdown notification*/
+ tw_setfeature(tw_dev2, 2, 1, &c);
}
if (numcards == 0)
printk(KERN_WARNING "3w-xxxx: tw_findcards(): No cards found.\n");
+ else
+ register_reboot_notifier(&tw_notifier);
return numcards;
} /* End tw_findcards() */
@@ -751,8 +780,22 @@
}
} /* End tw_free_device_extension() */
+/* Clean shutdown routine */
+static int tw_halt(struct notifier_block *nb, ulong event, void *buf)
+{
+ int i;
+
+ for (i=0;i<tw_device_extension_count;i++) {
+ printk(KERN_NOTICE "3w-xxxx: Notifying card #%d\n", i);
+ tw_shutdown_device(tw_device_extension_list[i]);
+ }
+ unregister_reboot_notifier(&tw_notifier);
+
+ return NOTIFY_OK;
+} /* End tw_halt() */
+
/* This function will send an initconnection command to controller */
-int tw_initconnection(TW_Device_Extension *tw_dev)
+int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits)
{
u32 command_que_addr, command_que_value;
u32 status_reg_addr, status_reg_value;
@@ -784,7 +827,7 @@
command_packet->byte3.host_id = 0x0;
command_packet->status = 0x0;
command_packet->flags = 0x0;
- command_packet->byte6.message_credits = TW_INIT_MESSAGE_CREDITS;
+ command_packet->byte6.message_credits = message_credits;
command_packet->byte8.init_connection.response_queue_pointer = 0x0;
command_que_value = tw_dev->command_packet_physical_address[request_id];
@@ -815,7 +858,7 @@
}
if (command_packet->status != 0) {
/* bad response */
- printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
}
break; /* Response was okay, so we exit */
@@ -962,7 +1005,7 @@
}
if (command_packet->status != 0) {
/* bad response */
- printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
return 1;
}
found = 1;
@@ -984,9 +1027,11 @@
if (is_unit_present[i] == 0) {
tw_dev->is_unit_present[i] = FALSE;
} else {
+ if (is_unit_present[i] & TW_UNIT_ONLINE) {
dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units(): Unit %d found.\n", i);
tw_dev->is_unit_present[i] = TRUE;
num_units++;
+ }
}
}
tw_dev->num_units = num_units;
@@ -1090,7 +1135,7 @@
request_id = response_que.u.response_id;
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
if (command_packet->status != 0) {
- printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, flags = 0x%x.\n", command_packet->flags);
+ printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
}
if (tw_dev->state[request_id] != TW_S_POSTED) {
printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode);
@@ -1186,7 +1231,7 @@
/* Initialize command packet */
command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
if (command_packet == NULL) {
- printk(KERN_WARNING "3w-xxxx: twioctl(): Bad command packet virtual address.\n");
+ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad command packet virtual address.\n");
tw_dev->state[request_id] = TW_S_COMPLETED;
tw_state_request_finish(tw_dev, request_id);
tw_dev->srb[request_id]->result = (DID_OK << 16);
@@ -1256,6 +1301,12 @@
tw_dev->srb[request_id]->result = (DID_OK << 16);
tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
return 0;
+ case TW_CMD_PACKET:
+ memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+ command_packet->request_id = request_id;
+ tw_post_command_packet(tw_dev, request_id);
+
+ return 0;
default:
printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1480,7 +1531,7 @@
return 1;
}
- error = tw_initconnection(tw_dev);
+ error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
if (error) {
printk(KERN_WARNING "3w-xxxx: tw_reset_sequence(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no);
return 1;
@@ -1804,6 +1855,11 @@
/* Tell kernel scsi-layer we are gone */
scsi_unregister(tw_host);
+ /* Fake like we just shut down, so notify the card that
+ * we "shut down cleanly".
+ */
+ tw_halt(0, 0, 0); // parameters aren't actually used
+
return 0;
} /* End tw_scsi_release() */
@@ -1901,8 +1957,10 @@
if (is_unit_present[i] == 0) {
tw_dev->is_unit_present[i] = FALSE;
} else {
+ if (is_unit_present[i] & TW_UNIT_ONLINE) {
tw_dev->is_unit_present[i] = TRUE;
dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete: Unit %d found.\n", i);
+ }
}
}
@@ -2125,6 +2183,92 @@
return 0;
} /* End tw_scsiop_test_unit_ready() */
+/* Set a value in the features table */
+int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size,
+ unsigned char *val)
+{
+ TW_Param *param;
+ TW_Command *command_packet;
+ TW_Response_Queue response_queue;
+ int request_id = 0;
+ u32 command_que_value, command_que_addr;
+ u32 status_reg_addr, status_reg_value;
+ u32 response_que_addr;
+ u32 param_value;
+ int imax, i;
+
+ /* Initialize SetParam command packet */
+ if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet virtual address.\n");
+ return 1;
+ }
+ command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+ memset(command_packet, 0, sizeof(TW_Sector));
+ param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+
+ command_packet->byte0.opcode = TW_OP_SET_PARAM;
+ command_packet->byte0.sgl_offset = 2;
+ param->table_id = 0x404; /* Features table */
+ param->parameter_id = parm;
+ param->parameter_size_bytes = param_size;
+ memcpy(param->data, val, param_size);
+
+ param_value = tw_dev->alignment_physical_address[request_id];
+ if (param_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Bad alignment physical address.\n");
+ tw_dev->state[request_id] = TW_S_COMPLETED;
+ tw_state_request_finish(tw_dev, request_id);
+ tw_dev->srb[request_id]->result = (DID_OK << 16);
+ tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+ }
+ command_packet->byte8.param.sgl[0].address = param_value;
+ command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+
+ command_packet->size = 4;
+ command_packet->request_id = request_id;
+ command_packet->byte6.parameter_count = 1;
+
+ command_que_value = tw_dev->command_packet_physical_address[request_id];
+ if (command_que_value == 0) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad command packet physical address.\n");
+ return 1;
+ }
+ command_que_addr = tw_dev->registers.command_que_addr;
+ status_reg_addr = tw_dev->registers.status_reg_addr;
+ response_que_addr = tw_dev->registers.response_que_addr;
+
+ /* Send command packet to the board */
+ outl(command_que_value, command_que_addr);
+
+ /* Poll for completion */
+ imax = TW_POLL_MAX_RETRIES;
+ for (i=0;i<imax;i++) {
+ mdelay(10);
+ status_reg_value = inl(status_reg_addr);
+ if (tw_check_bits(status_reg_value)) {
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected bits.\n");
+ return 1;
+ }
+ if ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
+ response_queue.value = inl(response_que_addr);
+ request_id = (unsigned char)response_queue.u.response_id;
+ if (request_id != 0) {
+ /* unexpected request id */
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected request id.\n");
+ return 1;
+ }
+ if (command_packet->status != 0) {
+ /* bad response */
+ printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
+ return 1;
+ }
+ break; /* Response was okay, so we exit */
+ }
+ }
+
+ return 0;
+} /* End tw_setfeature() */
+
/* This function will setup the interrupt handler */
int tw_setup_irq(TW_Device_Extension *tw_dev)
{
@@ -2140,6 +2284,29 @@
}
return 0;
} /* End tw_setup_irq() */
+
+/* This function will tell the controller we're shutting down by sending
+ initconnection with a 1 */
+int tw_shutdown_device(TW_Device_Extension *tw_dev)
+{
+ int error;
+
+ /* Disable interrupts */
+ tw_disable_interrupts(tw_dev);
+
+ /* poke the board */
+ error = tw_initconnection(tw_dev, 1);
+ if (error) {
+ printk(KERN_WARNING "3w-xxxx: tw_shutdown_device(): Couldn't initconnection for card %d.\n", tw_dev->host->host_no);
+ } else {
+ printk(KERN_NOTICE "3w-xxxx shutdown succeeded\n");
+ }
+
+ /* Re-enable interrupts */
+ tw_enable_interrupts(tw_dev);
+
+ return 0;
+} /* End tw_shutdown_device() */
/* This function will soft reset the controller */
void tw_soft_reset(TW_Device_Extension *tw_dev)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)