Subject: port-amiga/1300: voodoo style stopgap fix for sbic.c problems
To: None <gnats-bugs@gnats.netbsd.org>
From: Ignatios Souvatzis <is@beverly.rhein.de>
List: netbsd-bugs
Date: 08/01/1995 00:03:45
>Number:         1300
>Category:       port-amiga
>Synopsis:       voodoo style fix for sbic.c driver
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Jul 31 18:35:01 1995
>Last-Modified:
>Originator:     Ignatios Souvatzis
>Organization:
		private site
>Release:        950710
>Environment:
System: NetBSD beverly 1.0A NetBSD 1.0A (BEVERLY) #146: Sat Jul 29 18:15:40 MET DST 1995 is@beverly:/usr/src/sys/arch/amiga/compile/BEVERLY amiga


>Description:
	Two problems:

	a) the sbic.c has no way to reset the SCSI bus without losing the
	autoconfig information. If sbic_inhibit_sync is set, the scsi bus will
	lock, if it was operating sync before booting NetBSD.

	b) the sbic driver locks the scsi bus when trying to tell my new harddisk
	to do sync (or explicitly, to do async) transfers.

>How-To-Repeat:
	a) run AmigaOS with sync data transfers, then boot NetBSD with
	sbic_inhibit_sync = 1. The scsi bus will lock at the first data transfer.

	b) try to negotiate sync with a Quantum Fireball 1080S.

>Fix:
	My patch does the following:

	about a)
	sbic_inhibit_sync = 2:
		I send a SDTR msg with parameters for ASYNC (offset = 0).

	sbic_inhibit_sync = 3:
		I send a BUS_RESET msg to that device, wait for disconnect, and
		start again. For slow start devices, this breaks, unfortunately.

	about b)
	When waiting for command completion in sbicicmd, I check the DBR bit of the
	sbic and read a byte (and throw it away/save it for diagnostic printing) if 
	it is set. The rationale is that the driver shouldn't tell the sbic that 
	the transfer size is more than the data I give it, anyway, so that this will
	always be a transfer in phase.

	Of course, this is very voodooish. I publish it only as a stopgap fix (works for
	me), till eeh@btr.com has his new sbic.c driver ready.

	unrelated fix: there is a typo in one of the diagnostic printfs, which I fix.

===================================================================
RCS file: RCS/sbic.c,v
retrieving revision 1.1
diff -c -r1.1 sbic.c
*** sbic.c	1995/07/19 19:32:22	1.1
--- sbic.c	1995/07/31 21:45:25
***************
*** 529,535 ****
  		return;
  
  	printf("%s: ", dev->sc_dev.dv_xname);
! 	printf("csr == 0x%02i\n", csr);	/* XXX */
  }
  
  /*
--- 529,535 ----
  		return;
  
  	printf("%s: ", dev->sc_dev.dv_xname);
! 	printf("csr == 0x%02x\n", csr);	/* XXX */
  }
  
  /*
***************
*** 551,556 ****
--- 551,557 ----
  	if (dev->sc_flags & SBICF_SELECTED)
  		return(1);
  
+ restart_selection:
  	/*
  	 * issue select
  	 */
***************
*** 601,607 ****
  #endif
  			dev->sc_sync[id].offset = 0;
  			dev->sc_sync[id].period = sbic_min_period;
! 			dev->sc_sync[id].state = SYNC_DONE;
  		}
  	
  
--- 602,637 ----
  #endif
  			dev->sc_sync[id].offset = 0;
  			dev->sc_sync[id].period = sbic_min_period;
! 			if (sbic_inhibit_sync == 1)
! 				dev->sc_sync[id].state = SYNC_DONE;
! 			
! 			/* 
! 			 * XXX A3000/2091 didn't reset the SCSI bus (can't 
! 			 * do it in hardware), so the target might be 
! 			 * synchronous at this point.
! 			 * We have two strategies to handle this:
! 			 * sbic_inhibit_sync == 2: send an "async" sync msg
! 			 * sbic_inhibit_sync == 3: send a reset msg
! 			 */
! 			if (sbic_inhibit_sync > 2) {
! #ifdef DEBUG
! 				if (sync_debug)
! 					printf("sending reset msg");
! #endif
! 				SEND_BYTE (regs, MSG_BUS_DEVICE_RESET);
! 
! 				/* wait for disconnect */
! 				SBIC_WAIT (regs, SBIC_ASR_INT, 0);
! 				GET_SBIC_csr (regs, csr);
! 				QPRINTF(("[%02x]", csr));
! 				while (csr != SBIC_CSR_DISC && 
! 				    csr != SBIC_CSR_DISC_1) {
! 					DELAY(1);
! 					GET_SBIC_csr(regs, csr);
! 				}
! 				goto restart_selection;
! 				/* NOTREACHED */
! 			}
  		}
  	
  
***************
*** 628,634 ****
  			dev->sc_msg[3] = MSG_SYNC_REQ;
  			dev->sc_msg[4] = sbictoscsiperiod(dev, regs,
  			    sbic_min_period);
! 			dev->sc_msg[5] = sbic_max_offset;
  
  			if (sbicxfstart(regs, 6, MESG_OUT_PHASE, sbic_cmd_wait))
  				sbicxfout(regs, 6, dev->sc_msg, MESG_OUT_PHASE);
--- 658,665 ----
  			dev->sc_msg[3] = MSG_SYNC_REQ;
  			dev->sc_msg[4] = sbictoscsiperiod(dev, regs,
  			    sbic_min_period);
! 			dev->sc_msg[5] = sbic_inhibit_sync ? 0 : 
! 			    sbic_max_offset;
  
  			if (sbicxfstart(regs, 6, MESG_OUT_PHASE, sbic_cmd_wait))
  				sbicxfout(regs, 6, dev->sc_msg, MESG_OUT_PHASE);
***************
*** 800,807 ****
  	u_char xferphase;
  {
  	sbic_regmap_p regs;
! 	u_char phase, csr, asr;
  	int wait;
  
  	regs = dev->sc_sbicp;
  
--- 831,844 ----
  	u_char xferphase;
  {
  	sbic_regmap_p regs;
! 	u_char phase, csr, asr, oldphase;
  	int wait;
+ 	int frg_data;
+ #ifdef DEBUG
+ 	int i;
+ 	u_char frg_buf[32];
+ 
+ #endif
  
  	regs = dev->sc_sbicp;
  
***************
*** 822,830 ****
  	 */
  	dev->sc_stat[0] = 0xff;
  	dev->sc_msg[0] = 0xff;
! 	phase = CMD_PHASE;
  
  new_phase:
  	wait = sbic_cmd_wait;
  
  	GET_SBIC_csr (regs, csr);
--- 859,872 ----
  	 */
  	dev->sc_stat[0] = 0xff;
  	dev->sc_msg[0] = 0xff;
! 	/* 
! 	 * XXX who made that assumption? sbicselectbus had MSG_OUT as last
! 	 * phase, most of the time
! 	 */
! 	 phase = CMD_PHASE;
  
  new_phase:
+ 	oldphase = phase;
  	wait = sbic_cmd_wait;
  
  	GET_SBIC_csr (regs, csr);
***************
*** 833,839 ****
  	/*
  	 * requesting some new phase
  	 */
! 	if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08))
  		phase = csr & PHASE;
  	else if ((csr == SBIC_CSR_DISC) || (csr == SBIC_CSR_DISC_1)
  	    || (csr == SBIC_CSR_S_XFERRED)) {
--- 875,881 ----
  	/*
  	 * requesting some new phase
  	 */
! 	if ((csr != 0xff) && (csr & 0xf0) && (csr & 0x08)) 
  		phase = csr & PHASE;
  	else if ((csr == SBIC_CSR_DISC) || (csr == SBIC_CSR_DISC_1)
  	    || (csr == SBIC_CSR_S_XFERRED)) {
***************
*** 849,854 ****
--- 891,898 ----
  		goto abort;
  	}
  
+ 	frg_data = 0;
+ 
  	switch (phase) {
  	case CMD_PHASE:
  		if (sbicxfstart (regs, clen, phase, wait))
***************
*** 888,893 ****
--- 932,958 ----
  
  			dev->sc_sync[target].period = sbicfromscsiperiod(dev,
  			    regs, dev->sc_msg[3]);
+ 			if (dev->sc_sync[target].period < sbic_min_period) {
+ 				/* 
+ 				 * really should try to renegotiate; for now
+ 				 * we just reject it.
+ 				 */
+ #ifdef DEBUG
+ 				if (sbic_debug || sync_debug)
+ 					printf ("Rejecting message 0x%02x\n",
+ 					    dev->sc_msg[0]);
+ #endif
+ 				/* prepare to reject the message, NACK */
+ 				SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ 				WAIT_CIP(regs);
+ 				SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ 				WAIT_CIP(regs);
+ 				phase = MESG_OUT_PHASE;
+ 
+ 				break;
+ 				/* NOTREACHED */
+ 			}
+ 
  			dev->sc_sync[target].offset = dev->sc_msg[4];
  			dev->sc_sync[target].state = SYNC_DONE;
  			SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[target].offset,
***************
*** 896,905 ****
--- 961,972 ----
  			SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
  			WAIT_CIP(regs);
  			phase = CMD_PHASE;  /* or whatever */
+ #ifdef DEBUG
  			printf("%s: target %d now synchronous,"
  			    " period=%dns, offset=%d.\n",
  			    dev->sc_dev.dv_xname, target, dev->sc_msg[3] * 4,
  			    dev->sc_msg[4]);
+ #endif
  		} else if (dev->sc_msg[0] == MSG_REJECT
  		    && dev->sc_sync[target].state == SYNC_SENT) {
  #ifdef DEBUG
***************
*** 1027,1036 ****
--- 1094,1121 ----
  		goto abort;
  
  	/* tapes may take a loooong time.. */
+ 
  	while (asr & SBIC_ASR_BSY) {
+ 		if (asr & SBIC_ASR_DBR) {
+ #ifdef DEBUG
+ 			if (frg_data < sizeof(frg_buf))
+ 				GET_SBIC_data(regs, frg_buf[frg_data]);
+ 			else
+ #endif
+ 			GET_SBIC_data(regs, asr);
+ 		}
  		DELAY(1);
  		GET_SBIC_asr(regs, asr);
  	}
+ #ifdef DEBUG
+ 	if (frg_data) {
+ 		printf("%s: somebody forgot to get(?) %d bytes:", 
+ 		    dev->sc_dev.dv_xname, frg_data);
+ 		for (i=0; i < frg_data; ++i)
+ 			printf(" %02x", frg_buf[i]);
+ 		printf("\nold phase = %d, new phase= %d\n", oldphase, phase);
+ 	}
+ #endif
  
  	/* 
  	 * wait for last command to complete
***************
*** 1045,1050 ****
--- 1130,1136 ----
  	sbicabort(dev, regs, "icmd");
  out:
  	QPRINTF(("=STS:%02x=", dev->sc_stat[0]));
+ 
  	return(dev->sc_stat[0]);
  }
  
>Audit-Trail:
>Unformatted:
Ignatios Souvatzis