diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-23 14:05:01 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-23 14:05:01 +0000 |
commit | f3627cbe9236a062012c836f3b6ee311b43f63f2 (patch) | |
tree | ae854838b9a73b35bd0f3b8f42e5fb7f9cb1d5a9 /drivers/scsi/3w-xxxx.c | |
parent | fea12a7b3f20bc135ab533491411e9ff753c01c8 (diff) |
Merge with Linux 2.4.0-test5-pre4.
Diffstat (limited to 'drivers/scsi/3w-xxxx.c')
-rw-r--r-- | drivers/scsi/3w-xxxx.c | 193 |
1 files changed, 179 insertions, 14 deletions
diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index b3b8d17fb..ebe4e33ba 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/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,19 @@ 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. */ #include <linux/module.h> @@ -65,6 +80,7 @@ MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver"); #include <linux/string.h> #include <linux/delay.h> #include <linux/smp.h> +#include <linux/reboot.h> #include <linux/spinlock.h> #include <asm/errno.h> @@ -83,9 +99,15 @@ MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver"); 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 +}; /* Globals */ -char *tw_driver_version="1.1.000"; +char *tw_driver_version="1.02.00.001"; TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; int tw_device_extension_count = 0; @@ -224,7 +246,7 @@ int tw_aen_drain_queue(TW_Device_Extension *tw_dev) 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 */ @@ -562,6 +584,7 @@ int tw_findcards(Scsi_Host_Template *tw_host) TW_Device_Extension *tw_dev2; struct pci_dev *tw_pci_dev = NULL; 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))) { @@ -662,7 +685,7 @@ int tw_findcards(Scsi_Host_Template *tw_host) 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->resource[0].start), TW_IO_ADDRESS_RANGE); @@ -722,10 +745,15 @@ int tw_findcards(Scsi_Host_Template *tw_host) /* Free the temporary device extension */ if (tw_dev) kfree(tw_dev); + + /* Tell the firmware we support shutdown notification*/ + tw_setfeature(tw_dev, 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() */ @@ -747,8 +775,22 @@ void tw_free_device_extension(TW_Device_Extension *tw_dev) } } /* 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; @@ -780,7 +822,7 @@ int tw_initconnection(TW_Device_Extension *tw_dev) 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]; @@ -811,7 +853,7 @@ int tw_initconnection(TW_Device_Extension *tw_dev) } 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 */ @@ -959,7 +1001,7 @@ int tw_initialize_units(TW_Device_Extension *tw_dev) } 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; @@ -981,9 +1023,11 @@ int tw_initialize_units(TW_Device_Extension *tw_dev) 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; @@ -1089,7 +1133,7 @@ static void tw_interrupt(int irq, void *dev_instance, struct pt_regs *regs) 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 +1230,7 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id) /* 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 +1300,12 @@ int tw_ioctl(TW_Device_Extension *tw_dev, int request_id) 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 +1530,7 @@ int tw_reset_sequence(TW_Device_Extension *tw_dev) 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; @@ -1598,7 +1648,6 @@ int tw_scsi_eh_abort(Scsi_Cmnd *SCpnt) int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt) { TW_Device_Extension *tw_dev=NULL; - int flags = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_eh_reset()\n"); @@ -1613,17 +1662,17 @@ int tw_scsi_eh_reset(Scsi_Cmnd *SCpnt) return (FAILED); } - spin_lock_irqsave(&tw_dev->tw_lock, flags); + spin_lock(&tw_dev->tw_lock); tw_dev->num_resets++; /* Now reset the card and some of the device extension data */ if (tw_reset_device_extension(tw_dev)) { printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset failed for card %d.\n", tw_dev->host->host_no); - spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + spin_unlock(&tw_dev->tw_lock); return (FAILED); } printk(KERN_WARNING "3w-xxxx: tw_scsi_eh_reset(): Reset succeeded for card %d.\n", tw_dev->host->host_no); - spin_unlock_irqrestore(&tw_dev->tw_lock, flags); + spin_unlock(&tw_dev->tw_lock); return (SUCCESS); } /* End tw_scsi_eh_reset() */ @@ -1803,6 +1852,11 @@ int tw_scsi_release(struct Scsi_Host *tw_host) /* 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 +1955,10 @@ int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) 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); + } } } @@ -2124,6 +2180,92 @@ int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id) 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 +2282,29 @@ int tw_setup_irq(TW_Device_Extension *tw_dev) 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) { |