patch-2.4.19 linux-2.4.19/drivers/scsi/mesh.c

Next file: linux-2.4.19/drivers/scsi/mesh.h
Previous file: linux-2.4.19/drivers/scsi/megaraid.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/scsi/mesh.c linux-2.4.19/drivers/scsi/mesh.c
@@ -6,8 +6,16 @@
  *
  * Paul Mackerras, August 1996.
  * Copyright (C) 1996 Paul Mackerras.
+ *
+ * Apr. 14 2002  - BenH		Rework bus reset code for new error handler
+ *                              Add delay after initial bus reset
+ *                              Add module parameters
+ * To do:
+ * - handle aborts correctly
+ * - retry arbitration if lost (unless higher levels do this for us)
  */
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/types.h>
@@ -20,6 +28,7 @@
 #include <linux/interrupt.h>
 #include <linux/reboot.h>
 #include <linux/spinlock.h>
+#include <linux/pci.h>
 #include <asm/dbdma.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
@@ -30,6 +39,7 @@
 #include <asm/processor.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -39,35 +49,39 @@
 #include "hosts.h"
 #include "mesh.h"
 
-/*
- * To do:
- * - handle aborts correctly
- * - retry arbitration if lost (unless higher levels do this for us)
- */
-
-#define MESH_NEW_STYLE_EH
-
 #if 1
 #undef KERN_DEBUG
 #define KERN_DEBUG KERN_WARNING
 #endif
 
-#if CONFIG_SCSI_MESH_SYNC_RATE == 0
-int mesh_sync_period = 100;
-int mesh_sync_offset = 0;
-#else
-int mesh_sync_period = 1000 / CONFIG_SCSI_MESH_SYNC_RATE;	/* ns */
-int mesh_sync_offset = 15;
-#endif
-
-int mesh_sync_targets = 0xff;	/* targets to set synchronous (bitmap) */
-int mesh_resel_targets = 0xff;	/* targets that we let disconnect (bitmap) */
-int mesh_debug_targets = 0;	/* print debug for these targets */
-unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */
-
-#define ALLOW_SYNC(tgt)		((mesh_sync_targets >> (tgt)) & 1)
-#define ALLOW_RESEL(tgt)	((mesh_resel_targets >> (tgt)) & 1)
-#define ALLOW_DEBUG(tgt)	((mesh_debug_targets >> (tgt)) & 1)
+MODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
+MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(sync_rate, "i");
+MODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
+MODULE_PARM(sync_targets, "i");
+MODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
+MODULE_PARM(resel_targets, "i");
+MODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
+MODULE_PARM(debug_targets, "i");
+MODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
+MODULE_PARM(init_reset_delay, "i");
+MODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");
+
+static int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
+static int sync_targets = 0xff;
+static int resel_targets = 0xff;
+static int debug_targets = 0;	/* print debug for these targets */
+static int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;
+
+static int mesh_sync_period = 100;
+static int mesh_sync_offset = 0;
+static unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */
+
+#define ALLOW_SYNC(tgt)		((sync_targets >> (tgt)) & 1)
+#define ALLOW_RESEL(tgt)	((resel_targets >> (tgt)) & 1)
+#define ALLOW_DEBUG(tgt)	((debug_targets >> (tgt)) & 1)
 #define DEBUG_TARGET(cmd)	((cmd) && ALLOW_DEBUG((cmd)->target))
 
 #undef MESH_DBG
@@ -95,7 +109,8 @@
 	statusing,
 	busfreeing,
 	disconnecting,
-	reselecting
+	reselecting,
+	sleeping
 };
 
 enum msg_phase {
@@ -119,7 +134,6 @@
 	int	data_goes_out;		/* guess as to data direction */
 	Scsi_Cmnd *current_req;
 	u32	saved_ptr;
-	int	want_abort;
 #ifdef MESH_DBG
 	int	log_ix;
 	int	n_log;
@@ -156,11 +170,7 @@
 	struct mesh_target tgts[8];
 	void	*dma_cmd_space;
 	struct device_node *ofnode;
-#ifndef MESH_NEW_STYLE_EH
-	Scsi_Cmnd *completed_q;
-	Scsi_Cmnd *completed_qtail;
-	struct tq_struct tqueue;
-#endif
+	struct pci_dev* pdev;
 #ifdef MESH_DBG
 	int	log_ix;
 	int	n_log;
@@ -192,9 +202,6 @@
 static void mesh_dump_regs(struct mesh_state *);
 static void mesh_start(struct mesh_state *);
 static void mesh_start_cmd(struct mesh_state *, Scsi_Cmnd *);
-#ifndef MESH_NEW_STYLE_EH
-static void finish_cmds(void *);
-#endif
 static void add_sdtr_msg(struct mesh_state *);
 static void set_sdtr(struct mesh_state *, int, int);
 static void start_phase(struct mesh_state *);
@@ -240,6 +247,12 @@
 	struct Scsi_Host *mesh_host;
 	void *dma_cmd_space;
 
+	/* The SCSI layer is dumb ! It calls us with the IO request
+	 * lock held and interrupts disabled here... This doesn't happen
+	 * in 2.5
+	 */
+	spin_unlock_irq(&io_request_lock);
+	
 	if (_machine == _MACH_Pmac) {
 	    use_active_neg = (find_devices("mac-io") ? 0 : SEQ_ACTIVE_NEG);
 	} else {
@@ -247,6 +260,16 @@
 	    use_active_neg = SEQ_ACTIVE_NEG;
 	}
 
+	/* Calculate sync rate from module parameters */
+	if (sync_rate > 10)
+		sync_rate = 10;
+	if (sync_rate > 0) {
+		printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
+		mesh_sync_period = 1000 / sync_rate;	/* ns */
+		mesh_sync_offset = 15;
+	} else
+		printk(KERN_INFO "mesh: configured for asynchronous\n");
+
 	nmeshes = 0;
 	prev_statep = &all_meshes;
 	/*
@@ -258,11 +281,23 @@
 	if (mesh == 0)
 		mesh = find_compatible_devices("scsi", "chrp,mesh0");
 	for (; mesh != 0; mesh = mesh->next) {
+		u8 pci_bus, pci_devfn;
+		struct pci_dev* pdev = NULL;
+		
 		if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
 			printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
-			       " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);
+			       " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
+			continue;
+		}
+		if (mesh->parent != NULL
+		    && pci_device_from_OF_node(mesh->parent, &pci_bus,
+					       &pci_devfn) == 0)
+			pdev = pci_find_slot(pci_bus, pci_devfn);
+		if (pdev == NULL) {
+			printk(KERN_ERR "mesh: Can't locate PCI entry\n");
 			continue;
 		}
+
 		mesh_host = scsi_register(tp, sizeof(struct mesh_state));
 		if (mesh_host == 0) {
 			printk(KERN_ERR "mesh: couldn't register host");
@@ -279,6 +314,7 @@
 		memset(ms, 0, sizeof(*ms));
 		ms->host = mesh_host;
 		ms->ofnode = mesh;
+		ms->pdev = pdev;
 		ms->mesh = (volatile struct mesh_regs *)
 			ioremap(mesh->addrs[0].address, 0x1000);
 		ms->dma = (volatile struct dbdma_regs *)
@@ -303,10 +339,6 @@
 			ms->tgts[tgt].sync_params = ASYNC_PARAMS;
 			ms->tgts[tgt].current_req = 0;
 		}
-#ifndef MESH_NEW_STYLE_EH
-		ms->tqueue.routine = finish_cmds;
-		ms->tqueue.data = ms;
-#endif
 		*prev_statep = ms;
 		prev_statep = &ms->next;
 
@@ -341,6 +373,9 @@
 		register_reboot_notifier(&mesh_notifier);
 	}
 
+	/* Return to the SCSI layer in the same state we got called */
+	spin_lock_irq(&io_request_lock);
+
 	return nmeshes;
 }
 
@@ -395,15 +430,33 @@
 		
 	case PBOOK_SLEEP_NOW:
 		for (ms = all_meshes; ms != 0; ms = ms->next) {
+			unsigned long flags;
+
+			scsi_block_requests(ms->host);
+			spin_lock_irqsave(&io_request_lock, flags);
+			while(ms->phase != idle) {
+				spin_unlock_irqrestore(&io_request_lock, flags);
+				current->state = TASK_UNINTERRUPTIBLE;
+				schedule_timeout(1);
+				spin_lock_irqsave(&io_request_lock, flags);
+			}
+			ms->phase = sleeping;
+			spin_unlock_irqrestore(&io_request_lock, flags);
 			disable_irq(ms->meshintr);
 			set_mesh_power(ms, 0);
 		}
 		break;
 	case PBOOK_WAKE:
 		for (ms = all_meshes; ms != 0; ms = ms->next) {
+			unsigned long flags;
+			
 			set_mesh_power(ms, 1);
 			mesh_init(ms);
+			spin_lock_irqsave(&io_request_lock, flags);
+			mesh_start(ms);
+			spin_unlock_irqrestore(&io_request_lock, flags);
 			enable_irq(ms->meshintr);
+			scsi_unblock_requests(ms->host);
 		}
 		break;
 	}
@@ -411,10 +464,13 @@
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
+/*
+ * Called by midlayer with host locked to queue a new
+ * request
+ */
 int
 mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
-	unsigned long flags;
 	struct mesh_state *ms;
 
 	cmd->scsi_done = done;
@@ -422,8 +478,6 @@
 
 	ms = (struct mesh_state *) cmd->host->hostdata;
 
-	save_flags(flags);
-	cli();
 	if (ms->request_q == NULL)
 		ms->request_q = cmd;
 	else
@@ -433,19 +487,25 @@
 	if (ms->phase == idle)
 		mesh_start(ms);
 
-	restore_flags(flags);
 	return 0;
 }
 
+/* Todo: here we can at least try to remove the command from the
+ * queue if it isn't connected yet, and for pending command, assert
+ * ATN until the bus gets freed.
+ */
 int
 mesh_abort(Scsi_Cmnd *cmd)
 {
 	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
+	unsigned long flags;
 
 	printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+	spin_lock_irqsave(&io_request_lock, flags);
 	mesh_dump_regs(ms);
 	dumplog(ms, cmd->target);
 	dumpslog(ms);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	return SCSI_ABORT_SNOOZE;
 }
 
@@ -482,49 +542,41 @@
 	}
 }
 
+/*
+ * Called by the midlayer with the lock held to reset the
+ * SCSI host and bus.
+ * The midlayer will wait for devices to come back, we don't need
+ * to do that ourselves
+ */
 int
-mesh_reset(Scsi_Cmnd *cmd, unsigned how)
+mesh_host_reset(Scsi_Cmnd *cmd)
 {
 	struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
 	volatile struct mesh_regs *mr = ms->mesh;
 	volatile struct dbdma_regs *md = ms->dma;
-	unsigned long flags;
-	int ret;
 
-	printk(KERN_DEBUG "mesh_reset %x\n", how);
-	ret = SCSI_RESET_BUS_RESET;
-	save_flags(flags);
-	cli();
+	printk(KERN_DEBUG "mesh_host_reset\n");
+
+	/* Reset the controller & dbdma channel */
 	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
 	out_8(&mr->exception, 0xff);	/* clear all exception bits */
 	out_8(&mr->error, 0xff);	/* clear all error bits */
-	if (how & SCSI_RESET_SUGGEST_HOST_RESET) {
-		out_8(&mr->sequence, SEQ_RESETMESH);
-		ret |= SCSI_RESET_HOST_RESET;
-		udelay(1);
-		out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
-		out_8(&mr->source_id, ms->host->this_id);
-		out_8(&mr->sel_timeout, 25);	/* 250ms */
-		out_8(&mr->sync_params, ASYNC_PARAMS);
-	}
+	out_8(&mr->sequence, SEQ_RESETMESH);
+	udelay(1);
+	out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+	out_8(&mr->source_id, ms->host->this_id);
+	out_8(&mr->sel_timeout, 25);	/* 250ms */
+	out_8(&mr->sync_params, ASYNC_PARAMS);
+
+	/* Reset the bus */
 	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
 	udelay(30);			/* leave it on for >= 25us */
 	out_8(&mr->bus_status1, 0);	/* negate RST */
-#ifdef DO_ASYNC_RESET
-	if (how & SCSI_RESET_ASYNCHRONOUS) {
-		restore_flags(flags);
-		ret |= SCSI_RESET_PENDING;
-	} else
-#endif
-	{
-		handle_reset(ms);
-		restore_flags(flags);
-#ifndef MESH_NEW_STYLE_EH
-		finish_cmds(ms);
-#endif
-		ret |= SCSI_RESET_SUCCESS;
-	}
-	return ret;
+
+	/* Complete pending commands */
+	handle_reset(ms);
+	
+	return SUCCESS;
 }
 
 /*
@@ -553,13 +605,10 @@
 	return NOTIFY_DONE;
 }
 
-int
-mesh_command(Scsi_Cmnd *cmd)
-{
-	printk(KERN_WARNING "whoops... mesh_command called\n");
-	return -1;
-}
-
+/* Called with  meshinterrupt disabled, initialize the chipset
+ * and eventually do the initial bus reset. The lock must not be
+ * held since we can schedule.
+ */
 static void
 mesh_init(struct mesh_state *ms)
 {
@@ -568,6 +617,7 @@
 
 	udelay(100);
 
+	/* Reset controller */
 	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
 	out_8(&mr->exception, 0xff);	/* clear all exception bits */
 	out_8(&mr->error, 0xff);	/* clear all error bits */
@@ -578,15 +628,28 @@
 	out_8(&mr->sel_timeout, 25);	/* 250ms */
 	out_8(&mr->sync_params, ASYNC_PARAMS);
 
-	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
-	udelay(30);			/* leave it on for >= 25us */
-	out_8(&mr->bus_status1, 0);	/* negate RST */
-
+	if (init_reset_delay) {
+		printk(KERN_INFO "mesh: performing initial bus reset...\n");
+		
+		/* Reset bus */
+		out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
+		udelay(30);			/* leave it on for >= 25us */
+		out_8(&mr->bus_status1, 0);	/* negate RST */
+
+		/* Wait for bus to come back */
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout((init_reset_delay * HZ) / 1000);
+	}
+	
+	/* Reconfigure controller */
+	out_8(&mr->interrupt, 0xff);	/* clear all interrupt bits */
 	out_8(&mr->sequence, SEQ_FLUSHFIFO);
 	udelay(1);
 	out_8(&mr->sync_params, ASYNC_PARAMS);
 	out_8(&mr->sequence, SEQ_ENBRESEL);
-	out_8(&mr->interrupt, 0xff);	/* clear all interrupt bits */
+
+	ms->phase = idle;
+	ms->msgphase = msg_none;
 }
 
 /*
@@ -646,6 +709,8 @@
 		       cmd->use_sg, cmd->request_buffer, cmd->request_bufflen);
 	}
 #endif
+	if (ms->dma_started)
+		panic("mesh: double DMA start !\n");
 
 	ms->phase = arbitrating;
 	ms->msgphase = msg_none;
@@ -770,28 +835,6 @@
 	}
 }
 
-#ifndef MESH_NEW_STYLE_EH
-static void
-finish_cmds(void *data)
-{
-	struct mesh_state *ms = data;
-	Scsi_Cmnd *cmd;
-	unsigned long flags;
-
-	for (;;) {
-		spin_lock_irqsave(&io_request_lock, flags);
-		cmd = ms->completed_q;
-		if (cmd == NULL) {
-			spin_unlock_irqrestore(&io_request_lock, flags);
-			break;
-		}
-		ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble;
-		(*cmd->scsi_done)(cmd);
-		spin_unlock_irqrestore(&io_request_lock, flags);
-	}
-}
-#endif /* MESH_NEW_STYLE_EH */
-
 static inline void
 add_sdtr_msg(struct mesh_state *ms)
 {
@@ -1316,10 +1359,13 @@
 		dumpslog(ms);
 	}
 
+	if (ms->dma_started) {
+		printk(KERN_ERR "mesh: reselected with DMA started !\n");
+		halt_dma(ms);
+	}
 	ms->current_req = NULL;
 	ms->phase = dataing;
 	ms->msgphase = msg_in;
-	ms->dma_started = 0;
 	ms->n_msgout = 0;
 	ms->last_n_msgout = 0;
 	prev = ms->conn_tgt;
@@ -1738,18 +1784,7 @@
 static void
 mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
 {
-#ifdef MESH_NEW_STYLE_EH
 	(*cmd->scsi_done)(cmd);
-#else
-	if (ms->completed_q == NULL)
-		ms->completed_q = cmd;
-	else
-		ms->completed_qtail->host_scribble = (void *) cmd;
-	ms->completed_qtail = cmd;
-	cmd->host_scribble = NULL;
-	queue_task(&ms->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
-#endif /* MESH_NEW_STYLE_EH */
 }
 
 /*
@@ -1769,24 +1804,29 @@
 	if (cmd) {
 		cmd->SCp.this_residual = cmd->request_bufflen;
 		if (cmd->use_sg > 0) {
+			int nseg;
 			total = 0;
 			scl = (struct scatterlist *) cmd->buffer;
 			off = ms->data_ptr;
-			for (i = 0; i < cmd->use_sg; ++i, ++scl) {
+			nseg = pci_map_sg(ms->pdev, scl, cmd->use_sg,
+				 scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+			for (i = 0; i <nseg; ++i, ++scl) {
+				u32 dma_addr = sg_dma_address(scl);
+				u32 dma_len = sg_dma_len(scl);
+				
 				total += scl->length;
-				if (off >= scl->length) {
-					off -= scl->length;
+				if (off >= dma_len) {
+					off -= dma_len;
 					continue;
 				}
-				if (scl->length > 0xffff)
+				if (dma_len > 0xffff)
 					panic("mesh: scatterlist element >= 64k");
-				st_le16(&dcmds->req_count, scl->length - off);
+				st_le16(&dcmds->req_count, dma_len - off);
 				st_le16(&dcmds->command, dma_cmd);
-				st_le32(&dcmds->phy_addr,
-					virt_to_phys(scl->address) + off);
+				st_le32(&dcmds->phy_addr, dma_addr + off);
 				dcmds->xfer_status = 0;
 				++dcmds;
-				dtot += scl->length - off;
+				dtot += dma_len - off;
 				off = 0;
 			}
 		} else if (ms->data_ptr < cmd->request_bufflen) {
@@ -1794,6 +1834,7 @@
 			if (dtot > 0xffff)
 				panic("mesh: transfer size >= 64k");
 			st_le16(&dcmds->req_count, dtot);
+			/* XXX Use pci DMA API here ... */
 			st_le32(&dcmds->phy_addr,
 				virt_to_phys(cmd->request_buffer) + ms->data_ptr);
 			dcmds->xfer_status = 0;
@@ -1860,6 +1901,12 @@
 		       ms->conn_tgt, ms->data_ptr, cmd->request_bufflen,
 		       ms->tgts[ms->conn_tgt].data_goes_out);
 	}
+	if (cmd->use_sg != 0) {
+		struct scatterlist *sg;
+		sg = (struct scatterlist *)cmd->request_buffer;
+		pci_unmap_sg(ms->pdev, sg, cmd->use_sg,
+			     scsi_to_pci_dma_dir(cmd->sc_data_direction));
+	}
 	ms->dma_started = 0;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)