patch-2.4.20 linux-2.4.20/drivers/scsi/3w-xxxx.c

Next file: linux-2.4.20/drivers/scsi/3w-xxxx.h
Previous file: linux-2.4.20/drivers/sbus/dvma.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.19/drivers/scsi/3w-xxxx.c linux-2.4.20/drivers/scsi/3w-xxxx.c
@@ -144,6 +144,22 @@
                  Fix bug in raw command post with data ioctl method.
                  Fix bug where rollcall sometimes failed with cable errors.
    1.02.00.025 - Print unit # on all command timeouts.
+   1.02.00.026 - Fix possible infinite retry bug with power glitch induced
+                 drive timeouts.
+                 Cleanup some AEN severity levels.
+   1.02.00.027 - Add drive not supported AEN code for SATA controllers.
+                 Remove spurious unknown ioctl error message.
+   1.02.00.028 - Fix bug where multiple controllers with no units were the
+                 same card number.
+                 Fix bug where cards were being shut down more than once.
+   1.02.00.029 - Add new pci dma mapping for kernel 2.4 for highmem support.
+                 Replace pci_map_single() with pci_map_page() for highmem.
+                 Check for tw_setfeature() failure.
+   1.02.00.030 - Make driver 64-bit clean.
+   1.02.00.031 - Cleanup polling timeouts/routines in several places.
+                 Add support for mode sense opcode.
+                 Add support for cache mode page.
+                 Add support for synchronize cache opcode.
 */
 
 #include <linux/module.h>
@@ -190,6 +206,9 @@
 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);
+static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
+static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
+static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd);
 
 /* Notifier block to get a notify on system shutdown/halt/reboot */
 static struct notifier_block tw_notifier = {
@@ -197,7 +216,7 @@
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.025";
+char *tw_driver_version="1.02.00.031";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -208,7 +227,7 @@
 {
 	TW_Param *param;
 	unsigned short aen;
-	int error = 0;
+	int error = 0, table_max = 0;
 
 	dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
@@ -223,7 +242,8 @@
 	if (aen == 0x0ff) {
 		printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: INFO: AEN queue overflow.\n", tw_dev->host->host_no);
 	} else {
-		if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
+		table_max = sizeof(tw_aen_string)/sizeof(char *);
+		if ((aen & 0x0ff) < table_max) {
 			if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
 				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
 			} else {
@@ -272,11 +292,10 @@
 {
 	TW_Command *command_packet;
 	TW_Param *param;
-	int tries = 0;
 	int request_id = 0;
-	u32 command_que_value = 0, command_que_addr;
-	u32 status_reg_value = 0, status_reg_addr;
-	u32 param_value;
+	u32 command_que_addr;
+	unsigned long command_que_value;
+	unsigned long param_value;
 	TW_Response_Queue response_queue;
 	u32 response_que_addr;
 	unsigned short aen;
@@ -284,13 +303,11 @@
 	int finished = 0;
 	int first_reset = 0;
 	int queue = 0;
-	int imax, i;
-	int found = 0;
+	int found = 0, table_max = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue()\n");
 
 	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;
 
 	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT | TW_STATUS_MICROCONTROLLER_READY, 30)) {
@@ -342,113 +359,95 @@
 	command_packet->byte8.param.sgl[0].address = param_value;
 	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
 
-	imax = TW_POLL_MAX_RETRIES;
-
 	/* Now drain the controller's aen queue */
 	do {
 		/* Post command packet */
 		outl(command_que_value, command_que_addr);
 
 		/* Now poll for completion */
-		for (i=0;i<imax;i++) {
-			mdelay(5);
-			status_reg_value = inl(status_reg_addr);
-			if (tw_check_bits(status_reg_value)) {
-				dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected bits.\n");
-				tw_decode_bits(tw_dev, status_reg_value, 0);
+		if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 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_aen_drain_queue(): Unexpected request id.\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_aen_drain_queue(): Unexpected request id.\n");
+	
+			if (command_packet->status != 0) {
+				if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
+					/* Bad response */
+					tw_decode_sense(tw_dev, request_id, 0);
 					return 1;
+				} else {
+					/* We know this is a 3w-1x00, and doesn't support aen's */
+					return 0;
 				}
-	
-				if (command_packet->status != 0) {
-					if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) {
-						/* Bad response */
-						tw_decode_sense(tw_dev, request_id, 0);
+			}
+
+			/* Now check the aen */
+			aen = *(unsigned short *)(param->data);
+			aen_code = (aen & 0x0ff);
+			queue = 0;
+			switch (aen_code) {
+				case TW_AEN_QUEUE_EMPTY:
+					dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+					if (first_reset != 1) {
 						return 1;
 					} else {
-						/* We know this is a 3w-1x00, and doesn't support aen's */
-						return 0;
+						finished = 1;
 					}
-				}
-
-				/* Now check the aen */
-				aen = *(unsigned short *)(param->data);
-				aen_code = (aen & 0x0ff);
-				queue = 0;
-				switch (aen_code) {
-					case TW_AEN_QUEUE_EMPTY:
-						dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
-						if (first_reset != 1) {
-							continue;
-						} else {
-							finished = 1;
-						}
-						break;
-					case TW_AEN_SOFT_RESET:
-						if (first_reset == 0) {
-							first_reset = 1;
-						} else {
-							printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
-							tw_dev->aen_count++;
-							queue = 1;
-						}
-						break;
-					default:
-						if (aen == 0x0ff) {
-							printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
-						} else {
-							if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
-								if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
-									printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
-								} else {
-									printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
-								}
-							} else
-								printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
-						}
+					break;
+				case TW_AEN_SOFT_RESET:
+					if (first_reset == 0) {
+						first_reset = 1;
+					} else {
+						printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
 						tw_dev->aen_count++;
 						queue = 1;
-				}
-
-				/* Now put the aen on the aen_queue */
-				if (queue == 1) {
-					tw_dev->aen_queue[tw_dev->aen_tail] = aen;
-					if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
-						tw_dev->aen_tail = TW_Q_START;
+					}
+					break;
+				default:
+					if (aen == 0x0ff) {
+						printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n");
 					} else {
-						tw_dev->aen_tail = tw_dev->aen_tail + 1;
+						table_max = sizeof(tw_aen_string)/sizeof(char *);
+						if ((aen & 0x0ff) < table_max) {
+							if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+								printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
+							} else {
+								printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+							}
+						} else
+							printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
 					}
-					if (tw_dev->aen_head == tw_dev->aen_tail) {
-						if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
-							tw_dev->aen_head = TW_Q_START;
-						} else {
-							tw_dev->aen_head = tw_dev->aen_head + 1;
-						}
+					tw_dev->aen_count++;
+					queue = 1;
+			}
+
+			/* Now put the aen on the aen_queue */
+			if (queue == 1) {
+				tw_dev->aen_queue[tw_dev->aen_tail] = aen;
+				if (tw_dev->aen_tail == TW_Q_LENGTH - 1) {
+					tw_dev->aen_tail = TW_Q_START;
+				} else {
+					tw_dev->aen_tail = tw_dev->aen_tail + 1;
+				}
+				if (tw_dev->aen_head == tw_dev->aen_tail) {
+					if (tw_dev->aen_head == TW_Q_LENGTH - 1) {
+						tw_dev->aen_head = TW_Q_START;
+					} else {
+						tw_dev->aen_head = tw_dev->aen_head + 1;
 					}
 				}
-				found = 1;
-				break;
 			}
+			found = 1;
 		}
 		if (found == 0) {
 			printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n");
 			return 1;
 		}
-		tries++;
-	} while ((tries < TW_MAX_AEN_TRIES) && (finished == 0));
-
-	if (tries >=TW_MAX_AEN_TRIES) {
-		printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Aen queue error.\n");
-		return 1;
-	}
+	} while (finished == 0);
 
 	return 0;
 } /* End tw_aen_drain_queue() */
@@ -458,9 +457,10 @@
 {
 	TW_Command *command_packet;
 	TW_Param *param;
-	u32 command_que_value = 0, command_que_addr;
+	u32 command_que_addr;
+	unsigned long command_que_value;
 	u32 status_reg_value = 0, status_reg_addr;
-	u32 param_value = 0;
+	unsigned long param_value = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n");
 	command_que_addr = tw_dev->registers.command_que_addr;
@@ -525,39 +525,55 @@
 } /* End tw_aen_read_queue() */
 
 /* This function will allocate memory and check if it is 16 d-word aligned */
-int tw_allocate_memory(TW_Device_Extension *tw_dev, int request_id, int size, int which)
+int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
 {
-	u32 *virt_addr = kmalloc(size, GFP_ATOMIC);
+	int i, imax;
+	dma_addr_t dma_handle;
+	unsigned long *cpu_addr = NULL;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n");
 
-	if (!virt_addr) {
-		printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): kmalloc() failed.\n");
-		return 1;
-	}
 
-	if ((u32)virt_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
-		kfree(virt_addr);
-		printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
-		return 1;
-	}
+	if (which == 2)
+		imax = TW_MAX_BOUNCEBUF;
+	else
+		imax = TW_Q_LENGTH;
 
-	switch(which) {
-	case 0:
-		tw_dev->command_packet_virtual_address[request_id] = virt_addr;
-		tw_dev->command_packet_physical_address[request_id] = virt_to_bus(virt_addr);
-		break;
-	case 1:
-		tw_dev->alignment_virtual_address[request_id] = virt_addr;
-		tw_dev->alignment_physical_address[request_id] = virt_to_bus(virt_addr);
-		break;
-	case 2:
-		tw_dev->bounce_buffer[request_id] = virt_addr;
-		break;
-	default:
-		printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
-		return 1;
+	for (i=0;i<imax;i++) {
+		cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size, &dma_handle);
+		if (cpu_addr == NULL) {
+			printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n");
+			return 1;
+		}
+
+		if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) {
+			printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n");
+			pci_free_consistent(tw_dev->tw_pci_dev, size, cpu_addr, dma_handle);
+			return 1;
+		}
+
+		switch(which) {
+		case 0:
+			tw_dev->command_packet_virtual_address[i] = cpu_addr;
+			tw_dev->command_packet_physical_address[i] = dma_handle;
+			memset(tw_dev->command_packet_virtual_address[i], 0, size);
+			break;
+		case 1:
+			tw_dev->alignment_virtual_address[i] = cpu_addr;
+			tw_dev->alignment_physical_address[i] = dma_handle;
+			memset(tw_dev->alignment_virtual_address[i], 0, size);
+			break;
+		case 2:
+			tw_dev->bounce_buffer[i] = cpu_addr;
+			tw_dev->bounce_buffer_phys[i] = dma_handle;
+			memset(tw_dev->bounce_buffer[i], 0, size);
+			break;
+		default:
+			printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n");
+			return 1;
+		}
 	}
+
 	return 0;
 } /* End tw_allocate_memory() */
 
@@ -802,19 +818,29 @@
 	struct pci_dev *tw_pci_dev = NULL;
 	u32 status_reg_value;
 	unsigned char c = 1;
-	int i;
+	int i, j = -1;
 	u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID, TW_DEVICE_ID2 };
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
 
 	for (i=0;i<TW_NUMDEVICES;i++) {
 		while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {
+			j++;
 			if (pci_enable_device(tw_pci_dev))
 				continue;
+
+			/* We only need 32-bit addressing for 5,6,7xxx cards */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
+			if (pci_set_dma_mask(tw_pci_dev, 0xffffffff)) {
+				printk(KERN_WARNING "3w-xxxx: No suitable DMA available.\n");
+				continue;
+			}
+#endif
+
 			/* Prepare temporary device extension */
 			tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
 			if (tw_dev == NULL) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", j);
 				continue;
 			}
 			memset(tw_dev, 0, sizeof(TW_Device_Extension));
@@ -824,7 +850,7 @@
 
 			error = tw_initialize_device_extension(tw_dev);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", j);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -844,7 +870,7 @@
 			
 			/* Poll status register for 60 secs for 'Controller Ready' flag */
 			if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", j);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -859,14 +885,14 @@
 
 				error = tw_aen_drain_queue(tw_dev);
 				if (error) {
-					printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", j);
 					tries++;
 					continue;
 				}
 
 				/* Check for controller errors */
 				if (tw_check_errors(tw_dev)) {
-					printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", j);
 					tries++;
 					continue;
 				}
@@ -876,7 +902,7 @@
 			}
 
 			if (tries >= TW_MAX_RESET_TRIES) {
-				printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", j);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -887,7 +913,7 @@
 				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", 
 				       (tw_dev->tw_pci_dev->resource[0].start), 
 				       (tw_dev->tw_pci_dev->resource[0].start) + 
-				       TW_IO_ADDRESS_RANGE, numcards);
+				       TW_IO_ADDRESS_RANGE, j);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -897,7 +923,7 @@
 			request_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
 			error = tw_initialize_units(tw_dev);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: No valid units for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: No valid units for card %d.\n", j);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
@@ -906,13 +932,16 @@
 
 			error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", j);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
 			}
 
+			/* Set card status as online */
+			tw_dev->online = 1;
+
 			/* Calculate max cmds per lun, and setup queues */
 			if (tw_dev->num_units > 0) {
 				if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
@@ -931,7 +960,7 @@
 		/* Register the card with the kernel SCSI layer */
 			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
 			if (host == NULL) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", j);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
@@ -973,7 +1002,7 @@
 				tw_device_extension_count = numcards;
 				tw_dev2->host = host;
 			} else { 
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", j);
 				scsi_unregister(host);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
@@ -982,12 +1011,21 @@
 			}
 
 			/* Tell the firmware we support shutdown notification*/
-			tw_setfeature(tw_dev2, 2, 1, &c);
+			error = tw_setfeature(tw_dev2, 2, 1, &c);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_setfeature(): Error setting features for card %d.\n", j);
+				scsi_unregister(host);
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				numcards--;
+				continue;
+			}
 
 			/* Now setup the interrupt handler */
 			error = tw_setup_irq(tw_dev2);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", j);
 				scsi_unregister(host);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 
@@ -1022,16 +1060,16 @@
 	dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n");
 	/* Free command packet and generic buffer memory */
 	for (i=0;i<TW_Q_LENGTH;i++) {
-		if (tw_dev->command_packet_virtual_address[i]) 
-			kfree(tw_dev->command_packet_virtual_address[i]);
+		if (tw_dev->command_packet_virtual_address[i])
+			pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->command_packet_virtual_address[i], tw_dev->command_packet_physical_address[i]);
 
 		if (tw_dev->alignment_virtual_address[i])
-			kfree(tw_dev->alignment_virtual_address[i]);
+			pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector), tw_dev->alignment_virtual_address[i], tw_dev->alignment_physical_address[i]);
 
 	}
 	for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
 		if (tw_dev->bounce_buffer[i])
-			kfree(tw_dev->bounce_buffer[i]);
+			pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, tw_dev->bounce_buffer[i], tw_dev->bounce_buffer_phys[i]);
 	}
 } /* End tw_free_device_extension() */
 
@@ -1041,8 +1079,11 @@
 	int i;
 
 	for (i=0;i<tw_device_extension_count;i++) {
-		printk(KERN_NOTICE "3w-xxxx: Shutting down card %d.\n", i);
-		tw_shutdown_device(tw_device_extension_list[i]);
+		if (tw_device_extension_list[i]->online == 1) {
+			printk(KERN_NOTICE "3w-xxxx: Shutting down card %d.\n", i);
+			tw_shutdown_device(tw_device_extension_list[i]);
+			tw_device_extension_list[i]->online = 0;
+		}
 	}
 	unregister_reboot_notifier(&tw_notifier);
 
@@ -1052,18 +1093,15 @@
 /* This function will send an initconnection command to controller */
 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;
+	unsigned long command_que_value;
+	u32 command_que_addr;
 	u32 response_que_addr;
 	TW_Command  *command_packet;
 	TW_Response_Queue response_queue;
 	int request_id = 0;
-	int i = 0;
-	int imax = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n");
 	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;
 
 	/* Initialize InitConnection command packet */
@@ -1095,29 +1133,18 @@
 	outl(command_que_value, command_que_addr);
     
 	/* Poll for completion */
-	imax = TW_POLL_MAX_RETRIES;
-	for (i=0;i<imax;i++) {
-		mdelay(5);
-		status_reg_value = inl(status_reg_addr);
-		if (tw_check_bits(status_reg_value)) {
-			dprintk(KERN_WARNING "3w-xxxx: tw_initconnection(): Unexpected bits.\n");
-			tw_decode_bits(tw_dev, status_reg_value, 0);
+	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 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_initconnection(): Unexpected request id.\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_initconnection(): Unexpected request id.\n");
-				return 1;
-			}
-			if (command_packet->status != 0) {
-				/* bad response */
-				tw_decode_sense(tw_dev, request_id, 0);
-				return 1;
-			}
-			break;	/* Response was okay, so we exit */
+		if (command_packet->status != 0) {
+			/* bad response */
+			tw_decode_sense(tw_dev, request_id, 0);
+			return 1;
 		}
 	}
 	return 0;
@@ -1126,27 +1153,25 @@
 /* This function will initialize the fields of a device extension */
 int tw_initialize_device_extension(TW_Device_Extension *tw_dev)
 {
-	int i;
+	int i, error = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_device_extension()\n");
 
-	for (i=0;i<TW_Q_LENGTH;i++) {
-		/* Initialize command packet buffers */
-		tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 0);
-		if (tw_dev->command_packet_virtual_address[i] == NULL) {
-			printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad command packet virtual address.\n");
-			return 1;
-		}
-		memset(tw_dev->command_packet_virtual_address[i], 0, sizeof(TW_Sector));
-    
-		/* Initialize generic buffer */
-		tw_allocate_memory(tw_dev, i, sizeof(TW_Sector), 1);
-		if (tw_dev->alignment_virtual_address[i] == NULL) {
-			printk(KERN_WARNING "3w-xxxx: tw_initialize_device_extension(): Bad alignment virtual address.\n");
-			return 1;
-		}
-		memset(tw_dev->alignment_virtual_address[i], 0, sizeof(TW_Sector));
+	/* Initialize command packet buffers */
+	error = tw_allocate_memory(tw_dev, sizeof(TW_Command), 0);
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: Command packet memory allocation failed.\n");
+		return 1;
+	}
 
+	/* Initialize generic buffer */
+	error = tw_allocate_memory(tw_dev, sizeof(TW_Sector), 1);
+	if (error) {
+		printk(KERN_WARNING "3w-xxxx: Generic memory allocation failed.\n");
+		return 1;
+	}
+	
+	for (i=0;i<TW_Q_LENGTH;i++) {
 		tw_dev->free_queue[i] = i;
 		tw_dev->state[i] = TW_S_INITIAL;
 	}
@@ -1161,22 +1186,21 @@
 /* This function will get unit info from the controller */
 int tw_initialize_units(TW_Device_Extension *tw_dev) 
 {
-	int found = 0;
+	int found = 0, error = 0;
 	unsigned char request_id = 0;
 	TW_Command *command_packet;
 	TW_Param *param;
 	int i, j, imax, num_units = 0, num_raid_five = 0;
-	u32 status_reg_addr, status_reg_value;
-	u32 command_que_addr, command_que_value;
+	unsigned long command_que_value;
+	u32 command_que_addr;
 	u32 response_que_addr;
 	TW_Response_Queue response_queue;
-	u32 param_value;
+	unsigned long param_value;
 	unsigned char *is_unit_present;
 	unsigned char *raid_level;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_initialize_units()\n");
 
-	status_reg_addr = tw_dev->registers.status_reg_addr;
 	command_que_addr = tw_dev->registers.command_que_addr;
 	response_que_addr = tw_dev->registers.response_que_addr;
   
@@ -1225,31 +1249,20 @@
 	outl(command_que_value, command_que_addr);
 
 	/* Poll for completion */
-	imax = TW_POLL_MAX_RETRIES;
-	for(i=0; i<imax; i++) {
-		mdelay(5);
-		status_reg_value = inl(status_reg_addr);
-		if (tw_check_bits(status_reg_value)) {
-			dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
-			tw_decode_bits(tw_dev, status_reg_value, 0);
+	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 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_initialize_units(): Unexpected request id.\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_initialize_units(): Unexpected request id.\n");
-				return 1;
-			}
-			if (command_packet->status != 0) {
-				/* bad response */
-				tw_decode_sense(tw_dev, request_id, 0);
-				return 1;
-			}
-			found = 1;
-			break;
+		if (command_packet->status != 0) {
+			/* bad response */
+			tw_decode_sense(tw_dev, request_id, 0);
+			return 1;
 		}
+		found = 1;
 	}
 	if (found == 0) {
 		/* response never received */
@@ -1328,31 +1341,20 @@
 		outl(command_que_value, command_que_addr);
 
 		/* Poll for completion */
-		imax = TW_POLL_MAX_RETRIES;
-		for(i=0; i<imax; i++) {
-			mdelay(5);
-			status_reg_value = inl(status_reg_addr);
-			if (tw_check_bits(status_reg_value)) {
-				dprintk(KERN_WARNING "3w-xxxx: tw_initialize_units(): Unexpected bits.\n");
-				tw_decode_bits(tw_dev, status_reg_value, 0);
+		if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 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_initialize_units(): Unexpected request id.\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_initialize_units(): Unexpected request id.\n");
-					return 1;
-				}
-				if (command_packet->status != 0) {
-					/* bad response */
-					tw_decode_sense(tw_dev, request_id, 0);
-					return 1;
-				}
-				found = 1;
-				break;
+			if (command_packet->status != 0) {
+				/* bad response */
+				tw_decode_sense(tw_dev, request_id, 0);
+				return 1;
 			}
+			found = 1;
 		}
 		if (found == 0) {
 			/* response never received */
@@ -1372,13 +1374,10 @@
 
 	/* Now allocate raid5 bounce buffers */
 	if ((num_raid_five != 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
-		for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
-			tw_allocate_memory(tw_dev, i, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, 2);
-			if (tw_dev->bounce_buffer[i] == NULL) {
-				printk(KERN_WARNING "3w-xxxx: Bounce buffer allocation failed.\n");
-				return 1;
-			}
-			memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS);
+		error = tw_allocate_memory(tw_dev, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, 2);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: Bounce buffer allocation failed.\n");
+			return 1;
 		}
 	}
   
@@ -1533,6 +1532,13 @@
 						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught READ_CAPACITY\n");
 						error = tw_scsiop_read_capacity_complete(tw_dev, request_id);
 						break;
+					case MODE_SENSE:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught MODE_SENSE\n");
+						error = tw_scsiop_mode_sense_complete(tw_dev, request_id);
+						break;
+					case SYNCHRONIZE_CACHE:
+						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught SYNCHRONIZE_CACHE\n");
+						break;
 					case TW_IOCTL:
 						dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): caught TW_IOCTL\n");
 						error = tw_ioctl_complete(tw_dev, request_id);
@@ -1549,7 +1555,8 @@
 
 					/* If error, command failed */
 					if (error == 1) {
-						tw_dev->srb[request_id]->result = (DID_RESET << 16);
+						/* Ask for a host reset */
+						tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
 					}
 					
 					/* Now complete the io */
@@ -1558,6 +1565,7 @@
 						tw_state_request_finish(tw_dev, request_id);
 						tw_dev->posted_request_count--;
 						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+						tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
 					}
 				}
 
@@ -1588,12 +1596,13 @@
 	int bufflen, error = 0;
 	TW_Param *param;
 	TW_Command *command_packet, *command_save;
-	u32 param_value;
+	unsigned long param_value;
 	TW_Ioctl *ioctl = NULL;
 	TW_Passthru *passthru = NULL;
 	int tw_aen_code, i, use_sg;
-	char *data_ptr;
+	unsigned long *data_ptr;
 	int total_bytes = 0, posted = 0;
+	dma_addr_t dma_handle;
 	struct timeval before, timeout;
 
 	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
@@ -1698,10 +1707,10 @@
 			passthru = (TW_Passthru *)tw_dev->command_packet_virtual_address[request_id];
 			passthru->sg_list[0].length = passthru->sector_count*512;
 			if (passthru->sg_list[0].length > TW_MAX_PASSTHRU_BYTES) {
-				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%ld) too big.\n", passthru->sg_list[0].length);
+				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): Passthru size (%d) too big.\n", passthru->sg_list[0].length);
 				return 1;
 			}
-			passthru->sg_list[0].address = virt_to_bus(tw_dev->alignment_virtual_address[request_id]);
+			passthru->sg_list[0].address = tw_dev->alignment_physical_address[request_id];
 			tw_post_command_packet(tw_dev, request_id);
 			return 0;
 		case TW_CMD_PACKET:
@@ -1733,17 +1742,18 @@
 					use_sg = command_packet->size - 3;
 					for (i=0;i<use_sg;i++)
 						total_bytes+=command_packet->byte8.param.sgl[i].length;
-					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle);
+
 					if (!tw_dev->ioctl_data[request_id]) {
-						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): pci_alloc_consistent() failed for request_id %d.\n", tw_dev->host->host_no, request_id);
 						return 1;
 					}
 
 					/* Copy param sglist into the kernel */
 					data_ptr = tw_dev->ioctl_data[request_id];
 					for (i=0;i<use_sg;i++) {
-						if ((u32 *)command_packet->byte8.param.sgl[i].address != NULL) {
-							error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
+						if (command_packet->byte8.param.sgl[i].address != 0) {
+							error = copy_from_user(data_ptr, (void *)(unsigned long)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
 							if (error) {
 								dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
 								goto tw_ioctl_bail;
@@ -1756,24 +1766,25 @@
 						data_ptr+=command_packet->byte8.param.sgl[i].length;
 					}
 					command_packet->size = 4;
-					command_packet->byte8.param.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.param.sgl[0].address = dma_handle;
 					command_packet->byte8.param.sgl[0].length = total_bytes;
 				}
 				if (command_packet->byte0.sgl_offset == 3) {
 					use_sg = command_packet->size - 4;
 					for (i=0;i<use_sg;i++)
 						total_bytes+=command_packet->byte8.io.sgl[i].length;
-					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					tw_dev->ioctl_data[request_id] = pci_alloc_consistent(tw_dev->tw_pci_dev, total_bytes, &dma_handle);
+
 					if (!tw_dev->ioctl_data[request_id]) {
-						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): pci_alloc_consistent() failed for request_id %d.\n", tw_dev->host->host_no, request_id);
 						return 1;
 					}
 					if (command_packet->byte0.opcode == TW_OP_WRITE) {
 						/* Copy io sglist into the kernel */
 						data_ptr = tw_dev->ioctl_data[request_id];
 						for (i=0;i<use_sg;i++) {
-							if ((u32 *)command_packet->byte8.io.sgl[i].address != NULL) {
-								error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
+							if (command_packet->byte8.io.sgl[i].address != 0) {
+								error = copy_from_user(data_ptr, (void *)(unsigned long)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
 								if (error) {
 									dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
 									goto tw_ioctl_bail;
@@ -1787,7 +1798,7 @@
 						}
 					}
 					command_packet->size = 5;
-					command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.io.sgl[0].address = dma_handle;
 					command_packet->byte8.io.sgl[0].length = total_bytes;
 				}
 
@@ -1827,10 +1838,10 @@
 				/* Now copy up the param or io sglist to userspace */
 				if (command_packet->byte0.sgl_offset == 2) {
 					use_sg = command_save->size - 3;
-					data_ptr = phys_to_virt(command_packet->byte8.param.sgl[0].address);
+					data_ptr = tw_dev->ioctl_data[request_id];
 					for (i=0;i<use_sg;i++) {
-						if ((u32 *)command_save->byte8.param.sgl[i].address != NULL) {
-							error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
+						if (command_save->byte8.param.sgl[i].address != 0) {
+							error = copy_to_user((void *)(unsigned long)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
 							if (error) {
 								dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
 								goto tw_ioctl_bail;
@@ -1847,10 +1858,10 @@
 				if (command_packet->byte0.sgl_offset == 3) {
 					use_sg = command_save->size - 4;
 					if (command_packet->byte0.opcode == TW_OP_READ) {
-						data_ptr = phys_to_virt(command_packet->byte8.io.sgl[0].address);
+						data_ptr = tw_dev->ioctl_data[request_id];
 						for(i=0;i<use_sg;i++) {
-							if ((u32 *)command_save->byte8.io.sgl[i].address != NULL) {
-								error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
+							if (command_save->byte8.io.sgl[i].address != 0) {
+								error = copy_to_user((void *)(unsigned long)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
 								if (error) {
 									dprintk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
 									goto tw_ioctl_bail;
@@ -1870,7 +1881,7 @@
 
 				/* Free up sglist memory */
 				if (tw_dev->ioctl_data[request_id])
-					kfree(tw_dev->ioctl_data[request_id]);
+					pci_free_consistent(tw_dev->tw_pci_dev, total_bytes, tw_dev->ioctl_data[request_id], dma_handle);
 				else
 					printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Error freeing ioctl data.\n", tw_dev->host->host_no);
 				
@@ -1886,7 +1897,7 @@
 				return 1;
 			}
 		default:
-			printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
+			dprintk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
 			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);
@@ -1969,6 +1980,55 @@
 	return 0;
 } /* End tw_ioctl_complete() */
 
+static int tw_map_scsi_sg_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+	int use_sg;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data()\n");
+	
+	if (cmd->use_sg == 0)
+		return 0;
+
+	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, dma_dir);
+
+	if (use_sg == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_map_scsi_sg_data(): pci_map_sg() failed.\n");
+		return 0;
+	}
+
+	cmd->SCp.phase = 2;
+	cmd->SCp.have_data_in = use_sg;
+
+	return use_sg;
+} /* End tw_map_scsi_sg_data() */
+
+static u32 tw_map_scsi_single_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+	dma_addr_t mapping;
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+
+	dprintk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data()\n");
+	
+	if (cmd->request_bufflen == 0)
+		return 0;
+#ifdef BLK_BOUNCE_HIGH
+	mapping = pci_map_page(pdev, virt_to_page(cmd->request_buffer), ((unsigned long)cmd->request_buffer & ~PAGE_MASK), cmd->request_bufflen, dma_dir);
+#else
+	mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, dma_dir);
+#endif
+
+	if (mapping == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_map_scsi_single_data(): pci_map_page() failed.\n");
+		return 0;
+	}
+
+	cmd->SCp.phase = 1;
+	cmd->SCp.have_data_in = mapping;
+
+	return mapping;
+} /* End tw_map_scsi_single_data() */
+
 /* This function will mask the command interrupt */
 void tw_mask_command_interrupt(TW_Device_Extension *tw_dev)
 {
@@ -2012,11 +2072,45 @@
 	return 0;
 } /* End tw_poll_status() */
 
+/* This function will poll the status register for disappearance of a flag */
+int tw_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
+{
+        u32 status_reg_addr, status_reg_value;
+        struct timeval before, timeout;
+
+        status_reg_addr = tw_dev->registers.status_reg_addr;
+        do_gettimeofday(&before);
+        status_reg_value = inl(status_reg_addr);
+
+	if (tw_check_bits(status_reg_value)) {
+		dprintk(KERN_WARNING "3w-xxxx: tw_poll_status_gone(): Unexpected bits.\n");
+		tw_decode_bits(tw_dev, status_reg_value, 0);
+	}
+
+        while ((status_reg_value & flag) != 0) {
+                status_reg_value = inl(status_reg_addr);
+
+		if (tw_check_bits(status_reg_value)) {
+			dprintk(KERN_WARNING "3w-xxxx: tw_poll_status_gone(): Unexpected bits.\n");
+			tw_decode_bits(tw_dev, status_reg_value, 0);
+		}
+
+                do_gettimeofday(&timeout);
+                if (before.tv_sec + seconds < timeout.tv_sec) {
+			dprintk(KERN_WARNING "3w-xxxx: tw_poll_status_gone(): Flag 0x%x never disappeared.\n", flag);
+                        return 1;
+                }
+                mdelay(5);
+        }
+        return 0;
+} /* End tw_poll_status_gone() */
+
 /* This function will attempt to post a command packet to the board */
 int tw_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
 {
 	u32 status_reg_addr, status_reg_value;
-	u32 command_que_addr, command_que_value;
+	unsigned long command_que_value;
+	u32 command_que_addr;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_post_command_packet()\n");
 	command_que_addr = tw_dev->registers.command_que_addr;
@@ -2082,6 +2176,7 @@
 			if (srb != NULL) {
 				srb->result = (DID_RESET << 16);
 				tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+				tw_unmap_scsi_data(tw_dev->tw_pci_dev, tw_dev->srb[i]);
 			}
 		}
 	}
@@ -2225,14 +2320,14 @@
 	for (i=0;i<TW_Q_LENGTH;i++) {
 		if (tw_dev->srb[i] == SCpnt) {
 			if (tw_dev->state[i] == TW_S_STARTED) {
-				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
+				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (%p) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, SCpnt);
 				tw_dev->state[i] = TW_S_COMPLETED;
 				tw_state_request_finish(tw_dev, i);
 				spin_unlock(&tw_dev->tw_lock);
 				return (SUCCESS);
 			}
 			if (tw_dev->state[i] == TW_S_PENDING) {
-				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
+				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (%p) timed out.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, SCpnt);
 				if (tw_dev->pending_head == TW_Q_LENGTH-1) {
 					tw_dev->pending_head = TW_Q_START;
 				} else {
@@ -2246,7 +2341,7 @@
 			}
 			if (tw_dev->state[i] == TW_S_POSTED) {
 				/* If the command has already been posted, we have to reset the card */
-				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, (u32)SCpnt);
+				printk(KERN_WARNING "3w-xxxx: scsi%d: Unit #%d: Command (%p) timed out, resetting card.\n", tw_dev->host->host_no, tw_dev->srb[i]==0 ? 0 : tw_dev->srb[i]->target, SCpnt);
 				/* We have to let AEN requests through before the reset */
 				spin_unlock(&tw_dev->tw_lock);
 				spin_unlock_irq(&io_request_lock);
@@ -2339,9 +2434,9 @@
 					printk(KERN_INFO "3w-xxxx: Request_id: %d\n", j);
 					printk(KERN_INFO "Opcode: 0x%x\n", command->byte0.opcode);
 					printk(KERN_INFO "Block_count: 0x%x\n", command->byte6.block_count);
-					printk(KERN_INFO "LBA: 0x%x\n", (u32)command->byte8.io.lba);
-					printk(KERN_INFO "Physical command packet addr: 0x%x\n", tw_dev->command_packet_physical_address[j]);
-					printk(KERN_INFO "Scsi_Cmnd: 0x%x\n", (u32)tw_dev->srb[j]);
+					printk(KERN_INFO "LBA: 0x%x\n", command->byte8.io.lba);
+					printk(KERN_INFO "Physical command packet addr: 0x%lx\n", tw_dev->command_packet_physical_address[j]);
+					printk(KERN_INFO "Scsi_Cmnd: %p\n", tw_dev->srb[j]);
 				}
 			}
 			printk(KERN_INFO "3w-xxxx: Free_head: %3d\n", tw_dev->free_head);
@@ -2434,6 +2529,14 @@
 		        dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
 		        error = tw_scsiop_request_sense(tw_dev, request_id);
 		        break;
+		case MODE_SENSE:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught MODE_SENSE.\n");
+			error = tw_scsiop_mode_sense(tw_dev, request_id);
+			break;
+		case SYNCHRONIZE_CACHE:
+			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught SYNCHRONIZE_CACHE.\n");
+			error = tw_scsiop_synchronize_cache(tw_dev, request_id);
+			break;
 		case TW_IOCTL:
 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TW_SCSI_IOCTL.\n");
 			error = tw_ioctl(tw_dev, request_id);
@@ -2489,8 +2592,9 @@
 {
 	TW_Param *param;
 	TW_Command *command_packet;
-	u32 command_que_value, command_que_addr;
-	u32 param_value;
+	unsigned long command_que_value;
+	u32 command_que_addr;
+	unsigned long param_value;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry()\n");
 
@@ -2588,13 +2692,115 @@
 	return 0;
 } /* End tw_scsiop_inquiry_complete() */
 
+/* This function handles scsi mode_sense commands */
+int tw_scsiop_mode_sense(TW_Device_Extension *tw_dev, int request_id)
+{
+	TW_Param *param;
+	TW_Command *command_packet;
+	unsigned long command_que_value;
+	unsigned long param_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense()\n");
+
+	/* Only page control = 0, page code = 0x8 (cache page) supported */
+	if (tw_dev->srb[request_id]->cmnd[2] != 0x8) {
+		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]);
+		return 0;
+	}
+
+	/* Now read firmware cache setting for this unit */
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet virtual address.\n");
+		return 1;
+	}
+
+	/* Setup the command packet */
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_GET_PARAM;
+	command_packet->byte0.sgl_offset = 2;
+	command_packet->size = 4;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = 0;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	command_packet->byte6.parameter_count = 1;
+
+	/* Setup the param */
+	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment virtual address.\n");
+		return 1;
+	}
+
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	memset(param, 0, sizeof(TW_Sector));
+	param->table_id = TW_UNIT_INFORMATION_TABLE_BASE + tw_dev->srb[request_id]->target;
+	param->parameter_id = 7; /* unit flags */
+	param->parameter_size_bytes = 1;
+	param_value = tw_dev->alignment_physical_address[request_id];
+	if (param_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad alignment physical address.\n");
+		return 1;
+	}
+
+	command_packet->byte8.param.sgl[0].address = param_value;
+	command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector);
+	command_que_value = tw_dev->command_packet_physical_address[request_id];
+	if (command_que_value == 0) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now try to post the command packet */
+	tw_post_command_packet(tw_dev, request_id);
+
+	return 0;
+} /* End tw_scsiop_mode_sense() */
+
+/* This function is called by the isr to complete a mode sense command */
+int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+	TW_Param *param;
+	unsigned char *flags;
+	unsigned char *request_buffer;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n");
+
+	param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
+	if (param == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_mode_sense_complete(): Bad alignment virtual address.\n");
+		return 1;
+	}
+	flags = (char *)&(param->data[0]);
+	request_buffer = tw_dev->srb[request_id]->buffer;
+	memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+
+	request_buffer[0] = 0xf;        /* mode data length */
+	request_buffer[1] = 0;          /* default medium type */
+	request_buffer[2] = 0x10;       /* dpo/fua support on */
+	request_buffer[3] = 0;          /* no block descriptors */
+	request_buffer[4] = 0x8;        /* caching page */
+	request_buffer[5] = 0xa;        /* page length */
+	if (*flags & 0x1)
+		request_buffer[6] = 0x4;        /* WCE on */
+	else
+		request_buffer[6] = 0x0;        /* WCE off */
+
+	return 0;
+} /* End tw_scsiop_mode_sense_complete() */
+
 /* This function handles scsi read_capacity commands */
 int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id) 
 {
 	TW_Param *param;
 	TW_Command *command_packet;
-	u32 command_que_addr, command_que_value;
-	u32 param_value;
+	unsigned long command_que_value;
+	u32 command_que_addr;
+	unsigned long param_value;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity()\n");
 
@@ -2698,9 +2904,10 @@
 int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) 
 {
 	TW_Command *command_packet;
-	u32 command_que_addr, command_que_value = 0;
-	u32 lba = 0x0, num_sectors = 0x0;
-	int i, count = 0;
+	unsigned long command_que_value;
+	u32 command_que_addr = 0x0;
+	u32 lba = 0x0, num_sectors = 0x0, buffaddr = 0x0;
+	int i, use_sg, count = 0;
 	Scsi_Cmnd *srb;
 	struct scatterlist *sglist;
 
@@ -2761,16 +2968,24 @@
 		/* Do this if there are no sg list entries */
 		if (tw_dev->srb[request_id]->use_sg == 0) {    
 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_write(): SG = 0\n");
-			command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->srb[request_id]->request_buffer);
+			buffaddr = tw_map_scsi_single_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+			if (buffaddr == 0)
+				return 1;
+
+			command_packet->byte8.io.sgl[0].address = buffaddr;
 			command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
 			command_packet->size+=2;
 		}
 
 		/* Do this if we have multiple sg list entries */
 		if (tw_dev->srb[request_id]->use_sg > 0) {
-			for (i=0;i<tw_dev->srb[request_id]->use_sg; i++) {
-				command_packet->byte8.io.sgl[i].address = virt_to_bus(sglist[i].address);
-				command_packet->byte8.io.sgl[i].length = sglist[i].length;
+			use_sg = tw_map_scsi_sg_data(tw_dev->tw_pci_dev, tw_dev->srb[request_id]);
+			if (use_sg == 0)
+				return 1;
+
+			for (i=0;i<use_sg; i++) {
+				command_packet->byte8.io.sgl[i].address = sg_dma_address(&sglist[i]);
+				command_packet->byte8.io.sgl[i].length = sg_dma_len(&sglist[i]);
 				command_packet->size+=2;
 			}
 		}
@@ -2779,7 +2994,7 @@
                 if (tw_dev->srb[request_id]->use_sg == 0) {
 			dprintk(KERN_WARNING "doing raid 5 write use_sg = 0, bounce_buffer[%d] = 0x%p\n", request_id, tw_dev->bounce_buffer[request_id]);
 			memcpy(tw_dev->bounce_buffer[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
-			command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
+			command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
 			command_packet->byte8.io.sgl[0].length = tw_dev->srb[request_id]->request_bufflen;
 			command_packet->size+=2;
                 }
@@ -2791,7 +3006,7 @@
                                 memcpy((char *)(tw_dev->bounce_buffer[request_id])+count, sglist[i].address, sglist[i].length);
 				count+=sglist[i].length;
                         }
-                        command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->bounce_buffer[request_id]);
+                        command_packet->byte8.io.sgl[0].address = tw_dev->bounce_buffer_phys[request_id];
                         command_packet->byte8.io.sgl[0].length = count;
                         command_packet->size = 5; /* single sgl */
                 }
@@ -2832,6 +3047,44 @@
 	return 0;
 } /* End tw_scsiop_request_sense() */
 
+/* This function will handle synchronize cache scsi command */
+int tw_scsiop_synchronize_cache(TW_Device_Extension *tw_dev, int request_id)
+{
+	TW_Command *command_packet;
+	unsigned long command_que_value;
+
+	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_synchronize_cache()\n");
+
+	/* Send firmware flush command for this unit */
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: tw_scsiop_synchronize_cache(): Bad command packet virtual address.\n");
+		return 1;
+	}
+
+	/* Setup the command packet */
+	memset(command_packet, 0, sizeof(TW_Sector));
+	command_packet->byte0.opcode = TW_OP_FLUSH_CACHE;
+	command_packet->byte0.sgl_offset = 0;
+	command_packet->size = 2;
+	command_packet->request_id = request_id;
+	command_packet->byte3.unit = tw_dev->srb[request_id]->target;
+	command_packet->byte3.host_id = 0;
+	command_packet->status = 0;
+	command_packet->flags = 0;
+	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_scsiop_synchronize_cache(): Bad command packet physical address.\n");
+		return 1;
+	}
+
+	/* Now try to post the command packet */
+	tw_post_command_packet(tw_dev, request_id);
+
+	return 0;
+} /* End tw_scsiop_synchronize_cache() */
+
 /* This function will handle test unit ready scsi command */
 int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
 {
@@ -2854,11 +3107,10 @@
 	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;
+	unsigned long command_que_value;
+	u32 command_que_addr;
 	u32 response_que_addr;
-	u32 param_value;
-	int imax, i;
+	unsigned long param_value;
 
   	/* Initialize SetParam command packet */
 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
@@ -2897,40 +3149,28 @@
 	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(5);
-		status_reg_value = inl(status_reg_addr);
-		if (tw_check_bits(status_reg_value)) {
-			dprintk(KERN_WARNING "3w-xxxx: tw_setfeature(): Unexpected bits.\n");
-			tw_decode_bits(tw_dev, status_reg_value, 1);
+	if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 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 ((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 */
-				tw_decode_sense(tw_dev, request_id, 0);
-				return 1;
-			}
-			break; /* Response was okay, so we exit */
+		if (command_packet->status != 0) {
+			/* bad response */
+			tw_decode_sense(tw_dev, request_id, 0);
+			return 1;
 		}
 	}
 
-  return 0;
+	return 0;
 } /* End tw_setfeature() */
 
 /* This function will setup the interrupt handler */
@@ -3034,6 +3274,26 @@
 	return 0;
 } /* End tw_state_request_start() */
 
+static void tw_unmap_scsi_data(struct pci_dev *pdev, Scsi_Cmnd *cmd)
+{
+	int dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);
+	
+	dprintk(KERN_WARNING "3w-xxxx: tw_unamp_scsi_data()\n");
+	
+	switch(cmd->SCp.phase) {
+		case 1:
+#ifdef BLK_BOUNCE_HIGH
+			pci_unmap_page(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir);
+#else
+			pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, dma_dir);
+#endif
+			break;
+		case 2:
+			pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, dma_dir);
+			break;
+	}
+} /* End tw_unmap_scsi_data() */
+
 /* This function will unmask the command interrupt on the controller */
 void tw_unmask_command_interrupt(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)