patch-2.4.0-test2 linux/drivers/scsi/ncr53c8xx.c

Next file: linux/drivers/scsi/pci2000.c
Previous file: linux/drivers/scsi/megaraid.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
@@ -73,7 +73,7 @@
 */
 
 /*
-**	April 24 2000, version 3.2i
+**	May 11 2000, version 3.3b
 **
 **	Supported SCSI-II features:
 **	    Synchronous negotiation
@@ -93,6 +93,7 @@
 **		53C895		(Wide,   Fast 40,     on board rom BIOS)
 **		53C895A		(Wide,   Fast 40,     on board rom BIOS)
 **		53C896		(Wide,   Fast 40,     on board rom BIOS)
+**		53C897		(Wide,   Fast 40,     on board rom BIOS)
 **		53C1510D	(Wide,   Fast 40,     on board rom BIOS)
 **
 **	Other features:
@@ -104,7 +105,7 @@
 /*
 **	Name and version of the driver
 */
-#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx - version 3.2i"
+#define SCSI_NCR_DRIVER_NAME	"ncr53c8xx - version 3.3b"
 
 #define SCSI_NCR_DEBUG_FLAGS	(0)
 
@@ -187,6 +188,14 @@
 typedef	u_long		vm_offset_t;
 #include "ncr53c8xx.h"
 
+/*
+**	Donnot compile integrity checking code for Linux-2.3.0 
+**	and above since SCSI data structures are not ready yet.
+*/
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,0)
+#define	SCSI_NCR_INTEGRITY_CHECKING
+#endif
+
 #define NAME53C			"ncr53c"
 #define NAME53C8XX		"ncr53c8xx"
 #define DRIVER_SMP_LOCK		ncr53c8xx_lock
@@ -472,8 +481,10 @@
 **==========================================================
 */
 
+#define NS_NOCHANGE	(0)
 #define NS_SYNC		(1)
 #define NS_WIDE		(2)
+#define NS_PPR		(4)
 
 /*==========================================================
 **
@@ -666,6 +677,13 @@
 /*2*/	u_char	widedone;
 /*3*/	u_char	wval;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	u_char 	ic_min_sync;
+	u_char 	ic_max_width;
+	u_char 	ic_maximums_set;
+	u_char 	ic_done;
+#endif
+
 	/*----------------------------------------------------------------
 	**	User settable limits and options.
 	**	These limits are read from the NVRAM if present.
@@ -1204,6 +1222,17 @@
 	struct ccb	*ccb;		/* Global CCB			*/
 	struct usrcmd	user;		/* Command from user		*/
 	u_char		release_stage;	/* Synchronisation stage on release  */
+
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	/*----------------------------------------------------------------
+	**	Fields that are used for integrity check
+	**----------------------------------------------------------------
+	*/
+	unsigned char check_integrity; /* Enable midlayer integ.check on
+					* bus scan. */
+	unsigned char check_integ_par;  /* Set if par or Init. Det. error
+					 * used only during integ check */
+#endif
 };
 
 #define NCB_SCRIPT_PHYS(np,lbl)	 (np->p_script  + offsetof (struct script, lbl))
@@ -1382,6 +1411,10 @@
 static  void    ncr_int_sto     (ncb_p np);
 static	u_long	ncr_lookup	(char* id);
 static	void	ncr_negotiate	(struct ncb* np, struct tcb* tp);
+static	int	ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr);
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+static	int	ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr);
+#endif
 
 #ifdef SCSI_NCR_PROFILE_SUPPORT
 static	void	ncb_profile	(ncb_p np, ccb_p cp);
@@ -1396,6 +1429,7 @@
 static	void	ncr_setup_tags	(ncb_p np, u_char tn, u_char ln);
 static	void	ncr_setwide	(ncb_p np, ccb_p cp, u_char wide, u_char ack);
 static	int	ncr_show_msg	(u_char * msg);
+static  void    ncr_print_msg   (ccb_p cp, char *label, u_char *msg);
 static	int	ncr_snooptest	(ncb_p np);
 static	void	ncr_timeout	(ncb_p np);
 static  void    ncr_wakeup      (ncb_p np, u_long code);
@@ -3792,6 +3826,17 @@
 	instance->can_queue	= (MAX_START-4);
 	instance->select_queue_depths = ncr53c8xx_select_queue_depths;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	np->check_integrity	  = 0;
+	instance->check_integrity = 0;
+
+#ifdef SCSI_NCR_ENABLE_INTEGRITY_CHECK
+	if ( !(driver_setup.bus_check & 0x04) ) {
+		np->check_integrity	  = 1;
+		instance->check_integrity = 1;
+	}
+#endif
+#endif
 	/*
 	**	Patch script to physical addresses
 	*/
@@ -4020,6 +4065,270 @@
 	}
 }
 
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message for integrity check,
+**	if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+static int ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+	u_char no_increase;
+
+	if (tp->inq_done) {
+
+		if (!tp->ic_maximums_set) {
+			tp->ic_maximums_set = 1;
+
+			/* check target and host adapter capabilities */
+			if ( (tp->inq_byte7 & INQ7_WIDE16) && 
+					np->maxwide && tp->usrwide ) 
+				tp->ic_max_width = 1;
+			else
+				tp->ic_max_width = 0;
+
+			if ((tp->inq_byte7 & INQ7_SYNC) && tp->maxoffs) {
+				tp->ic_min_sync   = (tp->minsync < np->minsync) ?
+							np->minsync : tp->minsync;
+			}
+			else 
+				tp->ic_min_sync   = 255;
+			
+			tp->period   = 1;
+			tp->widedone = 1;
+		}
+
+		if (DEBUG_FLAGS & DEBUG_IC) {
+			printk("%s: cmd->ic_nego %d, 1st byte 0x%2X\n",
+				ncr_name(np), cmd->ic_nego, cmd->cmnd[0]);
+		}
+
+		/* First command from integrity check routine will request
+		 * a PPR message.  Disable.
+		 */
+		if ((cmd->ic_nego & NS_PPR) == NS_PPR)
+			cmd->ic_nego &= ~NS_PPR;
+		/* Previous command recorded a parity or an initiator
+		 * detected error condition. Force bus to narrow for this
+		 * target. Clear flag. Negotation on request sense.
+		 * Note: kernel forces 2 bus resets :o( but clears itself out.
+		 * Minor bug? in scsi_obsolete.c (ugly)
+		 */
+		if (np->check_integ_par) {
+			printk("%s: Parity Error. Target set to narrow.\n",
+				ncr_name(np));
+			tp->ic_max_width = 0;
+			tp->widedone = tp->period = 0;
+		}
+		
+		/* In case of a bus reset, ncr_negotiate will reset 
+                 * the flags tp->widedone and tp->period to 0, forcing
+		 * a new negotiation. 
+		 */
+		no_increase = 0;
+		if (tp->widedone == 0) {
+			cmd->ic_nego = NS_WIDE;
+			tp->widedone = 1;
+			no_increase = 1;
+		}
+		else if (tp->period == 0) {
+			cmd->ic_nego = NS_SYNC;
+			tp->period = 1;
+			no_increase = 1;
+		}
+			
+		switch (cmd->ic_nego) {
+		case NS_WIDE:
+			/*
+			**	negotiate wide transfers ?
+			**	Do NOT negotiate if device only supports
+			**	narrow.	
+			*/
+			if (tp->ic_max_width | np->check_integ_par) {
+				nego = NS_WIDE;
+
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 2;
+				msgptr[msglen++] = M_X_WIDE_REQ;
+				msgptr[msglen++] = cmd->ic_nego_width & tp->ic_max_width;
+			}
+			else
+				cmd->ic_nego_width &= tp->ic_max_width;
+              
+			break;
+
+		case NS_SYNC:
+			/*
+			**	negotiate synchronous transfers?
+			**	Target must support sync transfers.
+			**
+			**	If period becomes longer than max, reset to async
+			*/
+
+			if (tp->inq_byte7 & INQ7_SYNC) {
+
+				nego = NS_SYNC;
+
+				msgptr[msglen++] = M_EXTENDED;
+				msgptr[msglen++] = 3;
+				msgptr[msglen++] = M_X_SYNC_REQ;
+
+				switch (cmd->ic_nego_sync) {
+				case 2: /* increase the period */
+					if (!no_increase) {
+					  if (tp->ic_min_sync <= 0x0A)
+					      tp->ic_min_sync = 0x0C;
+					  else if (tp->ic_min_sync <= 0x0C)
+					      tp->ic_min_sync = 0x19;
+					  else if (tp->ic_min_sync <= 0x19)
+					      tp->ic_min_sync *= 2;
+					  else {
+						tp->ic_min_sync = 255;
+						cmd->ic_nego_sync = 0;
+						tp->maxoffs = 0;
+					   }
+					}
+					msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0;
+					msgptr[msglen++] = tp->maxoffs;
+					break;
+
+				case 1: /* nego. to maximum */
+					msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0;
+					msgptr[msglen++] = tp->maxoffs;
+					break;
+
+				case 0:	/* nego to async */
+				default:
+					msgptr[msglen++] = 0;
+					msgptr[msglen++] = 0;
+					break;
+				};
+			}
+			else
+				cmd->ic_nego_sync = 0;
+			break;
+
+		case NS_NOCHANGE:
+		default:
+			break;
+		};
+	};
+
+	cp->nego_status = nego;
+	np->check_integ_par = 0;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+			  "wide/narrow msgout": "sync/async msgout", msgptr);
+		};
+	};
+
+	return msglen;
+}
+#endif /* SCSI_NCR_INTEGRITY_CHECKING */
+
+/*==========================================================
+**
+**
+**	Prepare the next negotiation message if needed.
+**
+**	Fill in the part of message buffer that contains the 
+**	negotiation and the nego_status field of the CCB.
+**	Returns the size of the message in bytes.
+**
+**
+**==========================================================
+*/
+
+
+static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr)
+{
+	tcb_p tp = &np->target[cp->target];
+	int msglen = 0;
+	int nego = 0;
+
+	if (tp->inq_done) {
+
+		/*
+		**	negotiate wide transfers ?
+		*/
+
+		if (!tp->widedone) {
+			if (tp->inq_byte7 & INQ7_WIDE16) {
+				nego = NS_WIDE;
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+				if (tp->ic_done)
+		       			 tp->usrwide &= tp->ic_max_width;
+#endif
+			} else
+				tp->widedone=1;
+
+		};
+
+		/*
+		**	negotiate synchronous transfers?
+		*/
+
+		if (!nego && !tp->period) {
+			if (tp->inq_byte7 & INQ7_SYNC) {
+				nego = NS_SYNC;
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+				if ((tp->ic_done) &&
+				 	      (tp->minsync < tp->ic_min_sync))
+		       			 tp->minsync = tp->ic_min_sync;
+#endif
+			} else {
+				tp->period  =0xffff;
+				PRINT_TARGET(np, cp->target);
+				printk ("target did not report SYNC.\n");
+			};
+		};
+	};
+
+	switch (nego) {
+	case NS_SYNC:
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 3;
+		msgptr[msglen++] = M_X_SYNC_REQ;
+		msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
+		msgptr[msglen++] = tp->maxoffs;
+		break;
+	case NS_WIDE:
+		msgptr[msglen++] = M_EXTENDED;
+		msgptr[msglen++] = 2;
+		msgptr[msglen++] = M_X_WIDE_REQ;
+		msgptr[msglen++] = tp->usrwide;
+		break;
+	};
+
+	cp->nego_status = nego;
+
+	if (nego) {
+		tp->nego_cp = cp;
+		if (DEBUG_FLAGS & DEBUG_NEGO) {
+			ncr_print_msg(cp, nego == NS_WIDE ?
+					  "wide msgout":"sync_msgout", msgptr);
+		};
+	};
+
+	return msglen;
+}
+
+
 
 /*==========================================================
 **
@@ -4038,7 +4347,7 @@
 	ccb_p cp;
 
 	int	segments;
-	u_char	nego, idmsg, *msgptr;
+	u_char	idmsg, *msgptr;
 	u_int  msglen;
 	int	direction;
 	u_int32	lastp, goalp;
@@ -4120,51 +4429,6 @@
 	cp->phys.header.stamp.start = jiffies;
 #endif
 
-	/*---------------------------------------------------
-	**
-	**	negotiation required?
-	**
-	**---------------------------------------------------
-	*/
-
-	nego = 0;
-
-	if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) {
-
-		/*
-		**	negotiate wide transfers ?
-		*/
-
-		if (!tp->widedone) {
-			if (tp->inq_byte7 & INQ7_WIDE16) {
-				nego = NS_WIDE;
-			} else
-				tp->widedone=1;
-		};
-
-		/*
-		**	negotiate synchronous transfers?
-		*/
-
-		if (!nego && !tp->period) {
-			if (tp->inq_byte7 & INQ7_SYNC) {
-				nego = NS_SYNC;
-			} else {
-				tp->period  =0xffff;
-				PRINT_TARGET(np, cmd->target);
-				printk ("device did not report SYNC.\n");
-			};
-		};
-
-		/*
-		**	remember nego is pending for the target.
-		**	Avoid to start a nego for all queued commands 
-		**	when tagged command queuing is enabled.
-		*/
-
-		if (nego)
-			tp->nego_cp = cp;
-	};
 
 	/*----------------------------------------------------
 	**
@@ -4225,34 +4489,6 @@
 		msgptr[msglen++] = (cp->tag << 1) + 1;
 	}
 
-	switch (nego) {
-	case NS_SYNC:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 3;
-		msgptr[msglen++] = M_X_SYNC_REQ;
-		msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0;
-		msgptr[msglen++] = tp->maxoffs;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("sync msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-5]);
-			printk (".\n");
-		};
-		break;
-	case NS_WIDE:
-		msgptr[msglen++] = M_EXTENDED;
-		msgptr[msglen++] = 2;
-		msgptr[msglen++] = M_X_WIDE_REQ;
-		msgptr[msglen++] = tp->usrwide;
-		if (DEBUG_FLAGS & DEBUG_NEGO) {
-			PRINT_ADDR(cp->cmd);
-			printk ("wide msgout: ");
-			ncr_show_msg (&cp->scsi_smsg [msglen-4]);
-			printk (".\n");
-		};
-		break;
-	};
-
 	/*----------------------------------------------------
 	**
 	**	Build the data descriptors
@@ -4273,6 +4509,84 @@
 		segments = 0;
 	}
 
+	/*---------------------------------------------------
+	**
+	**	negotiation required?
+	**
+	**	(nego_status is filled by ncr_prepare_nego())
+	**
+	**---------------------------------------------------
+	*/
+
+	cp->nego_status = 0;
+
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	if ((np->check_integrity && tp->ic_done) || !np->check_integrity) {
+		 if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+			msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
+		 }
+	}
+	else if (np->check_integrity && (cmd->ic_in_progress)) { 
+		msglen += ncr_ic_nego (np, cp, cmd, msgptr + msglen);
+        }
+	else if (np->check_integrity && cmd->ic_complete) {
+                /*
+                 * Midlayer signal to the driver that all of the scsi commands
+                 * for the integrity check have completed. Save the negotiated
+                 * parameters (extracted from sval and wval). 
+                 */ 
+
+		{
+			u_char idiv;
+			idiv = (tp->wval>>4) & 0x07;
+			if ((tp->sval&0x1f) && idiv )
+				tp->period = (((tp->sval>>5)+4)  
+						*div_10M[idiv-1])/np->clock_khz;
+			else
+				tp->period = 0xffff;
+		}
+		/*
+		 * tp->period contains 10 times the transfer period, 
+		 * which itself is 4 * the requested negotiation rate.
+		 */
+		if	(tp->period <= 250)	tp->ic_min_sync = 10;
+		else if	(tp->period <= 303)	tp->ic_min_sync = 11;
+		else if	(tp->period <= 500)	tp->ic_min_sync = 12;
+		else				
+				tp->ic_min_sync = (tp->period + 40 - 1) / 40;
+
+
+		/*
+                 * Negotiation for this target it complete.
+                 */
+		tp->ic_max_width =  (tp->wval & EWS) ? 1: 0;
+		tp->ic_done = 1;
+		tp->widedone = 1;
+
+		printk("%s: Integrity Check Complete: \n", ncr_name(np)); 
+
+		printk("%s: %s %s SCSI", ncr_name(np), 
+				(tp->sval&0x1f)?"SYNC":"ASYNC",
+				tp->ic_max_width?"WIDE":"NARROW");
+
+		if (tp->sval&0x1f) {
+                        u_long mbs = 10000 * (tp->ic_max_width + 1);
+
+                        printk(" %d.%d  MB/s", 
+                                (int) (mbs / tp->period), (int) (mbs % tp->period));
+
+			printk(" (%d ns, %d offset)\n", 
+				  tp->period/10, tp->sval&0x1f);
+		}
+		else
+                        printk(" %d MB/s. \n ", (tp->ic_max_width+1)*5);
+        }
+#else
+	if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) {
+		msglen += ncr_prepare_nego (np, cp, msgptr + msglen);
+	}
+#endif /* SCSI_NCR_INTEGRITY_CHECKING */
+
 	/*----------------------------------------------------
 	**
 	**	Determine xfer direction.
@@ -4376,12 +4690,11 @@
 	**	status
 	*/
 	cp->actualquirks		= tp->quirks;
-	cp->host_status			= nego ? HS_NEGOTIATE : HS_BUSY;
+	cp->host_status			= cp->nego_status ? HS_NEGOTIATE : HS_BUSY;
 	cp->scsi_status			= S_ILLEGAL;
 	cp->parity_status		= 0;
 
 	cp->xerr_status			= XE_OK;
-	cp->nego_status			= nego;
 #if 0
 	cp->sync_status			= tp->sval;
 	cp->wide_status			= tp->wval;
@@ -5041,7 +5354,13 @@
 			for (i=0; i<14; i++) printk (" %x", *p++);
 			printk (".\n");
 		}
-
+	} else if ((cp->host_status == HS_COMPLETE)
+		&& (cp->scsi_status == S_CONFLICT)) {
+		/*
+		**   Reservation Conflict condition code
+		*/
+		cmd->result = ScsiResult(DID_OK, S_CONFLICT);
+	
 	} else if ((cp->host_status == HS_COMPLETE)
 		&& (cp->scsi_status == S_BUSY ||
 		    cp->scsi_status == S_QUEUE_FULL)) {
@@ -6398,6 +6717,14 @@
 	else
 		msg = M_ID_ERROR;
 
+#ifdef SCSI_NCR_INTEGRITY_CHECKING
+	/*
+	**      Save error message. For integrity check use only.
+	*/
+	if (np->check_integrity)
+		np->check_integ_par = msg;
+#endif
+
 	/*
 	 *	If the NCR stopped on a MOVE ^ DATA_IN, we jump to a 
 	 *	script that will ignore all data in bytes until phase 
@@ -6908,6 +7235,16 @@
 	return (1);
 }
 
+static void ncr_print_msg ( ccb_p cp, char *label, u_char *msg)
+{
+	if (cp)
+		PRINT_ADDR(cp->cmd);
+	if (label)
+		printk("%s: ", label);
+	
+	(void) ncr_show_msg (msg);
+	printk(".\n");
+}
 
 void ncr_int_sir (ncb_p np)
 {
@@ -8275,6 +8612,7 @@
 static unsigned __init ncrgetfreq (ncb_p np, int gen)
 {
 	unsigned ms = 0;
+	char count = 0;
 
 	/*
 	 * Measure GEN timer delay in order 
@@ -8299,8 +8637,10 @@
 	OUTB (nc_scntl3, 4);	/* set pre-scaler to divide by 3 */
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
 	OUTB (nc_stime1, gen);	/* set to nominal delay of 1<<gen * 125us */
-	while (!(INW(nc_sist) & GEN) && ms++ < 100000)
-		UDELAY (1000);	/* count ms */
+	while (!(INW(nc_sist) & GEN) && ms++ < 100000) {
+		for (count = 0; count < 10; count ++)
+			UDELAY (100);	/* count ms */
+	}
 	OUTB (nc_stime1, 0);	/* disable general purpose timer */
  	/*
  	 * set prescaler to divide by whatever 0 means

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