Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 11 Feb 1997 23:57:44 +0100 (MET)
From:      Wolfgang Helbig <helbig@MX.BA-Stuttgart.De>
To:        hackers@freebsd.org
Subject:   CMD640b workaround - final(?) version
Message-ID:  <199702112257.XAA01704@helbig.informatik.ba-stuttgart.de>

next in thread | raw e-mail | index | archive | help
Hi,

this is my final patch to work around the CMD640b hardware bug.
This bug does not allow you to use the primary and secondary IDE-channel
simultaneously, since there is only one hardware buffer for both
channels. Other known bugs of the CMD640-series and the RZ1000-IDE-Chip
are not addressed by this patch. Those bugs (bad prefetch buffer handling
and problems resulting from interference with the floppy-controller) apparently
did not occure on my system. 

The workaround consists mainly in serializing access to the two ide-
channels. This job is done very similar to the serializing of master and
slave device of one channel. It is tested by Jason and Nadav. Jason
provided a version for FreeBSD 2.1.5 for the test. Thanx!!!

This final version detects the presence of the CMD640-chip and enables
accordingly the workaround code.
This detection takes place in the pci.c file and the result is communicated
to the wd.c  file by the global external variable cmd640_detected.
(I know this is dirty, but better ways that are still easier did not come
to my mind, sorry!)

For all this to take place, you still have to add the
	options "CMD640"
line to the kernel configuration file. Without this option, the old
code in wd.c and pci.c is active.
With this option, the detection is made, and -- if the chip is detected -- the
serialization of primary and secondary channel takes place.
If you use the 
	options "CMD640"
you also *have* to put
	controller pci0
into your configuration file! Maybe someone can put the right clauses
in /sys/conf/files to automaticly resoving this (ugly) dependency!

This code is tested with the GENERIC- and a customized kernel, but it
might need more testing, before it is committed to -current.
I think it is too late for FreeBSD 2.2

Have fun!
Wolfgang Helbig helbig@ba-stuttgart.de

Following are the patches of wd.c and pci.c 

Index: wd.c
===================================================================
RCS file: /usr/cvsroot/src/sys/i386/isa/wd.c,v
retrieving revision 1.122
diff -c -r1.122 wd.c
*** wd.c	1997/01/14 06:41:40	1.122
--- wd.c	1997/02/11 20:57:49
***************
*** 128,133 ****
--- 128,135 ----
  #define	RECAL		2	/* doing restore */
  #define	OPEN		3	/* done with open */
  
+ #define PRIMARY		0
+ 
  /*
   * Disk geometry.  A small part of struct disklabel.
   * XXX disklabel.5 contains an old clone of disklabel.h.
***************
*** 238,243 ****
--- 240,250 ----
  	{ wdopen,	wdclose,	wdstrategy,	wdioctl,	/*0*/
  	  wddump,	wdsize,		0,	"wd",	&wd_cdevsw,	-1 };
  
+ #ifdef CMD640
+ static int      atapictrlr;
+ extern int      cmd640_detected;    /* defined in pci.c */
+ #endif
+ 
  /*
   * Probe for controller.
   */
***************
*** 360,366 ****
--- 367,384 ----
  	if (dvp->id_unit >= NWDC)
  		return (0);
  
+ #ifdef CMD640
+ 	if (cmd640_detected) {
+ 		if (dvp->id_unit == PRIMARY) {
+ 			printf("wdc0: CMD640b workaround enabled\n");
+ 			TAILQ_INIT( &wdtab[PRIMARY].controller_queue);
+ 		}
+ 	} else
+ 		TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue);
+ 
+ #else
  	TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue);
+ #endif
  
  	for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
  		if (wdup->id_iobase != dvp->id_iobase)
***************
*** 467,480 ****
  			if (wddrives[lunit]->dk_ctrlr == dvp->id_unit &&
  			    wddrives[lunit]->dk_unit == unit)
  				goto next;
! 		atapi_attach (dvp->id_unit, unit, dvp->id_iobase);
  next:   }
  #endif
  	/*
  	 * Discard any interrupts generated by wdgetctlr().  wdflushirq()
  	 * doesn't work now because the ambient ipl is too high.
  	 */
  	wdtab[dvp->id_unit].b_active = 2;
  
  	return (1);
  }
--- 485,510 ----
  			if (wddrives[lunit]->dk_ctrlr == dvp->id_unit &&
  			    wddrives[lunit]->dk_unit == unit)
  				goto next;
! 		if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) {
! #ifdef CMD640
! 			if (cmd640_detected) 
! 				atapictrlr = dvp->id_unit;
! #endif
! 		}
  next:   }
  #endif
  	/*
  	 * Discard any interrupts generated by wdgetctlr().  wdflushirq()
  	 * doesn't work now because the ambient ipl is too high.
  	 */
+ #ifdef CMD640
+ 	if (cmd640_detected) 
+ 		wdtab[PRIMARY].b_active = 2;
+ 	else
+ 		wdtab[dvp->id_unit].b_active = 2;
+ #else
  	wdtab[dvp->id_unit].b_active = 2;
+ #endif
  
  	return (1);
  }
***************
*** 490,495 ****
--- 520,528 ----
  	struct disk *du;
  	int	lunit = dkunit(bp->b_dev);
  	int	s;
+ #ifdef CMD640
+ 	int     ctrlr;
+ #endif
  
  	/* valid unit, controller, and request?  */
  	if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL
***************
*** 548,555 ****
--- 581,598 ----
  		wdsleep(du->dk_ctrlr, "wdlab");
  		du->dk_state = WANTOPEN;
  	}
+ #ifdef CMD640
+ 	if (cmd640_detected) 
+ 		ctrlr = PRIMARY;
+ 	else
+ 		ctrlr = du ->dk_ctrlr;
+ #endif
  
+ #ifdef CMD640
+ 	if (wdtab[ctrlr].b_active == 0)
+ #else
  	if (wdtab[du->dk_ctrlr].b_active == 0)
+ #endif
  		wdstart(du->dk_ctrlr);	/* start controller */
  
  	if (du->dk_dkunit >= 0) {
***************
*** 611,617 ****
--- 654,669 ----
  	TAILQ_REMOVE( &drive_queue[du->dk_lunit], bp, b_act);
  
  	/* link onto controller queue */
+ #ifdef CMD640
+ 	if (cmd640_detected) {
+ 		TAILQ_INSERT_TAIL( &wdtab[PRIMARY].controller_queue, bp, b_act);
+ 	} else {
+ 		TAILQ_INSERT_TAIL( &wdtab[ctrlr].controller_queue, bp, b_act);
+ 	}
+ 
+ #else
  	TAILQ_INSERT_TAIL( &wdtab[ctrlr].controller_queue, bp, b_act);
+ #endif
  
  	/* mark the drive unit as busy */
  	wdutab[du->dk_lunit].b_active = 1;
***************
*** 636,655 ****
  	int	lunit;
  	int	count;
  
  #ifdef ATAPI
  	if (wdtab[ctrlr].b_active == 2)
  		wdtab[ctrlr].b_active = 0;
  	if (wdtab[ctrlr].b_active)
  		return;
- #endif
  	/* is there a drive for the controller to do a transfer with? */
  	bp = wdtab[ctrlr].controller_queue.tqh_first;
  	if (bp == NULL) {
  #ifdef ATAPI
! 		if (atapi_start && atapi_start (ctrlr))
  			/* mark controller active in ATAPI mode */
  			wdtab[ctrlr].b_active = 3;
  #endif
  		return;
  	}
  
--- 688,737 ----
  	int	lunit;
  	int	count;
  
+ #ifdef CMD640
+ 	int     c;
+ 
+ 	if (cmd640_detected)
+ 		c = PRIMARY;
+ 	else
+ 		c = ctrlr;
+ #endif
+ 
  #ifdef ATAPI
+ #ifdef CMD640 
+ 	if (wdtab[c].b_active == 2)
+ 		wdtab[c].b_active = 0;
+ 	if (wdtab[c].b_active)
+ 		return;
+ #else
  	if (wdtab[ctrlr].b_active == 2)
  		wdtab[ctrlr].b_active = 0;
  	if (wdtab[ctrlr].b_active)
  		return;
  	/* is there a drive for the controller to do a transfer with? */
+ #endif
+ #endif
+ #ifdef CMD640
+ 	bp = wdtab[c].controller_queue.tqh_first;
+ #else
  	bp = wdtab[ctrlr].controller_queue.tqh_first;
+ #endif
  	if (bp == NULL) {
  #ifdef ATAPI
! #ifdef CMD640
! 		if (cmd640_detected) {
! 			if (atapi_start && atapi_start (atapictrlr))
! 				wdtab[c].b_active = 3;
! 		} else {
! 			if (atapi_start && atapi_start (ctrlr))
! 				wdtab[ctrlr].b_active = 3;
! 		}
! #else
  			/* mark controller active in ATAPI mode */
+ 		if (atapi_start && atapi_start (ctrlr))
  			wdtab[ctrlr].b_active = 3;
  #endif
+ #endif
  		return;
  	}
  
***************
*** 705,711 ****
--- 787,797 ----
  				     blknum - ds_offset) + ds_offset;
  	}
  
+ #ifdef CMD640
+ 	wdtab[c].b_active = 1;	/* mark controller active */
+ #else
  	wdtab[ctrlr].b_active = 1;	/* mark controller active */
+ #endif
  
  	/* if starting a multisector transfer, or doing single transfers */
  	if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
***************
*** 717,723 ****
--- 803,813 ----
  		head = (blknum % secpercyl) / secpertrk;
  		sector = blknum % secpertrk;
  
+ #ifdef CDM640
+ 		if (wdtab[c].b_errcnt && (bp->b_flags & B_READ) == 0)
+ #else
  		if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
+ #endif
  			du->dk_bc += DEV_BSIZE;
  		count = howmany( du->dk_bc, DEV_BSIZE);
  
***************
*** 863,872 ****
--- 953,973 ----
  {
  	register struct	disk *du;
  	register struct buf *bp;
+ #ifdef CMD640
+ 	int c ;
  
+ 	if (cmd640_detected)
+ 		c = PRIMARY;
+ 	else
+ 		c = unit;
+ 	if (wdtab[c].b_active == 2)
+ 		return;		/* intr in wdflushirq() */
+ 	if (!wdtab[c].b_active) {
+ #else
  	if (wdtab[unit].b_active == 2)
  		return;		/* intr in wdflushirq() */
  	if (!wdtab[unit].b_active) {
+ #endif
  #ifdef WDDEBUG
  		/*
  		 * These happen mostly because the power-mgt part of the
***************
*** 878,895 ****
--- 979,1020 ----
  		return;
  	}
  #ifdef ATAPI
+ #ifdef CMD640
+ 	if (wdtab[c].b_active == 3) {
+ #else
  	if (wdtab[unit].b_active == 3) {
+ #endif
  		/* process an ATAPI interrupt */
+ #ifdef CMD640
+ 		if (cmd640_detected) {
+ 			if (atapi_intr && atapi_intr (atapictrlr))
+ 				/* ATAPI op continues */
+ 				return;
+ 		} else {
+ 			if (atapi_intr && atapi_intr (unit))
+ 				/* ATAPI op continues */
+ 				return;
+ 		}
+ #else
  		if (atapi_intr && atapi_intr (unit))
  			/* ATAPI op continues */
  			return;
+ #endif
  		/* controller is free, start new op */
+ #ifdef CMD640
+ 		wdtab[c].b_active = 0;
+ #else
  		wdtab[unit].b_active = 0;
+ #endif
  		wdstart (unit);
  		return;
  	}
  #endif
+ #ifdef CMD640
+ 	bp = wdtab[c].controller_queue.tqh_first;
+ #else
  	bp = wdtab[unit].controller_queue.tqh_first;
+ #endif
  	du = wddrives[dkunit(bp->b_dev)];
  	du->dk_timeout = 0;
  
***************
*** 900,906 ****
--- 1025,1035 ----
  
  	/* is it not a transfer, but a control operation? */
  	if (du->dk_state < OPEN) {
+ #ifdef CMD640
+ 		wdtab[c].b_active = 0;
+ #else
  		wdtab[unit].b_active = 0;
+ #endif
  		switch (wdcontrol(bp)) {
  		case 0:
  			return;
***************
*** 942,949 ****
--- 1071,1083 ----
  			bp->b_error = EIO;
  			bp->b_flags |= B_ERROR;
  		} else if (du->dk_status & WDCS_ERR) {
+ #ifdef CMD640
+ 			if (++wdtab[c].b_errcnt < RETRIES) {
+ 				wdtab[c].b_active = 0;
+ #else
  			if (++wdtab[unit].b_errcnt < RETRIES) {
  				wdtab[unit].b_active = 0;
+ #endif
  			} else {
  				wderror(bp, du, "hard error");
  				bp->b_error = EIO;
***************
*** 957,963 ****
--- 1091,1101 ----
  	 * If this was a successful read operation, fetch the data.
  	 */
  	if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
+ #ifdef CMD640
+ 	    && wdtab[c].b_active) {
+ #else
  	    && wdtab[unit].b_active) {
+ #endif
  		int	chk, dummy, multisize;
  		multisize = chk = du->dk_currentiosize * DEV_BSIZE;
  		if( du->dk_bc < chk) {
***************
*** 999,1016 ****
--- 1137,1168 ----
  	}
  
  outt:
+ #ifdef CMD640
+ 	if (wdtab[c].b_active) {
+ #else
  	if (wdtab[unit].b_active) {
+ #endif
  		if ((bp->b_flags & B_ERROR) == 0) {
  			du->dk_skip += du->dk_currentiosize;/* add to successful sectors */
+ #ifdef CMD640
+ 			if (wdtab[c].b_errcnt)
+ 				wderror(bp, du, "soft error");
+ 			wdtab[c].b_errcnt = 0;
+ #else
  			if (wdtab[unit].b_errcnt)
  				wderror(bp, du, "soft error");
  			wdtab[unit].b_errcnt = 0;
+ #endif
  
  			/* see if more to transfer */
  			if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
  				if( (du->dk_flags & DKFL_SINGLE) ||
  					((bp->b_flags & B_READ) == 0)) {
+ #ifdef CMD640
+ 					wdtab[c].b_active = 0;
+ #else
  					wdtab[unit].b_active = 0;
+ #endif 
  					wdstart(unit);
  				} else {
  					du->dk_timeout = 1 + 3;
***************
*** 1021,1027 ****
--- 1173,1183 ----
  				du->dk_skip = 0;
  				du->dk_flags &= ~DKFL_ERROR;
  				du->dk_flags |= DKFL_SINGLE;
+ #ifdef CMD640
+ 				wdtab[c].b_active = 0;
+ #else
  				wdtab[unit].b_active = 0;
+ #endif 
  				wdstart(unit);
  				return;	/* redo xfer sector by sector */
  			}
***************
*** 1030,1037 ****
--- 1186,1198 ----
  done: ;
  		/* done with this transfer, with or without error */
  		du->dk_flags &= ~DKFL_SINGLE;
+ #ifdef CMD640
+ 		TAILQ_REMOVE(&wdtab[c].controller_queue, bp, b_act);
+ 		wdtab[c].b_errcnt = 0;
+ #else
  		TAILQ_REMOVE(&wdtab[unit].controller_queue, bp, b_act);
  		wdtab[unit].b_errcnt = 0;
+ #endif
  		bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
  		wdutab[du->dk_lunit].b_active = 0;
  		wdutab[du->dk_lunit].b_errcnt = 0;
***************
*** 1044,1057 ****
--- 1205,1226 ----
  	}
  
  	/* controller idle */
+ #ifdef CMD640
+ 	wdtab[c].b_active = 0;
+ #else
  	wdtab[unit].b_active = 0;
+ #endif  
  
  	/* anything more on drive queue? */
  	wdustart(du);
  	/* anything more for controller to do? */
  #ifndef ATAPI
  	/* This is not valid in ATAPI mode. */
+ #ifdef CMD640
+ 	if (wdtab[c].controller_queue.tqh_first)
+ #else
  	if (wdtab[unit].controller_queue.tqh_first)
+ #endif  
  #endif
  		wdstart(unit);
  }
***************
*** 1065,1070 ****
--- 1234,1242 ----
  	register unsigned int lunit;
  	register struct disk *du;
  	int	error;
+ #ifdef CMD640
+ 	int     c;
+ #endif
  
  	lunit = dkunit(dev);
  	if (lunit >= NWD || dktype(dev) != 0)
***************
*** 1074,1081 ****
--- 1246,1263 ----
  		return (ENXIO);
  
  	/* Finish flushing IRQs left over from wdattach(). */
+ #ifdef CMD640
+ 	if (cmd640_detected)
+ 		c = PRIMARY;
+ 	else
+ 		c = du->dk_ctrlr;
+ 
+ 	if (wdtab[c].b_active == 2)
+ 		wdtab[c].b_active = 0;
+ #else
  	if (wdtab[du->dk_ctrlr].b_active == 2)
  		wdtab[du->dk_ctrlr].b_active = 0;
+ #endif
  
  	du->dk_flags &= ~DKFL_BADSCAN;
  
***************
*** 1238,1251 ****
--- 1420,1446 ----
  {
  	register struct disk *du;
  	int	ctrlr;
+ #ifdef CMD640
+ 	int	c;
+ #endif
  
  	du = wddrives[dkunit(bp->b_dev)];
  	ctrlr = du->dk_ctrlr;
+ #ifdef CMD640
+ 	if (cmd640_detected)
+ 		c = PRIMARY;
+ 	else
+ 		c = ctrlr;
+ #endif
  
  	switch (du->dk_state) {
  	case WANTOPEN:
  tryagainrecal:
+ #ifdef CMD640
+ 		wdtab[c].b_active = 1;
+ #else
  		wdtab[ctrlr].b_active = 1;
+ #endif
  		if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
  			wderror(bp, du, "wdcontrol: wdcommand failed");
  			goto maybe_retry;
***************
*** 1259,1271 ****
--- 1454,1474 ----
  			if (du->dk_status & WDCS_ERR)
  				wdunwedge(du);
  			du->dk_state = WANTOPEN;
+ #ifdef CMD640
+ 			if (++wdtab[c].b_errcnt < RETRIES)
+ #else
  			if (++wdtab[ctrlr].b_errcnt < RETRIES)
+ #endif
  				goto tryagainrecal;
  			bp->b_error = ENXIO;	/* XXX needs translation */
  			bp->b_flags |= B_ERROR;
  			return (2);
  		}
+ #ifdef CMD640
+ 		wdtab[c].b_errcnt = 0;
+ #else
  		wdtab[ctrlr].b_errcnt = 0;
+ #endif
  		du->dk_state = OPEN;
  		/*
  		 * The rest of the initialization can be done by normal
***************
*** 1373,1379 ****
--- 1576,1589 ----
  		error = 1;
  	}
  	if (error) {
+ #ifdef CMD640 
+ 		if (cmd640_detected)
+ 			wdtab[PRIMARY].b_errcnt += RETRIES;
+ 		else
+ 			wdtab[du->dk_ctrlr].b_errcnt += RETRIES;
+ #else
  		wdtab[du->dk_ctrlr].b_errcnt += RETRIES;
+ #endif
  		return (1);
  	}
  	if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
***************
*** 1750,1759 ****
  	if (wddoingadump)
  		return (EFAULT);
  
- #if 0
- 	/* Mark controller active for if we panic during the dump. */
- 	wdtab[du->dk_ctrlr].b_active = 1;
- #endif
  	wddoingadump = 1;
  
  	/* Recalibrate the drive. */
--- 1960,1965 ----
***************
*** 1900,1909 ****
--- 2106,2125 ----
  static void
  wdflushirq(struct disk *du, int old_ipl)
  {
+ #ifdef CMD640
+ 	int c = du->dk_ctrlr;
+ 	if (cmd640_detected) 
+ 		c = PRIMARY;
+ 	wdtab[c].b_active = 2;
+ 	splx(old_ipl);
+ 	(void)splbio();
+ 	wdtab[c].b_active = 0;
+ #else
  	wdtab[du->dk_ctrlr].b_active = 2;
  	splx(old_ipl);
  	(void)splbio();
  	wdtab[du->dk_ctrlr].b_active = 0;
+ #endif  
  }
  
  /*
***************
*** 1943,1950 ****
--- 2159,2175 ----
  wdsleep(int ctrlr, char *wmesg)
  {
  	int s = splbio();
+ #ifdef CMD640
+ 	int c = ctrlr;
+ 
+ 	if (cmd640_detected)
+ 		c = PRIMARY;
+ 	while (wdtab[c].b_active)
+ 		tsleep((caddr_t)&wdtab[PRIMARY].b_active, PZERO - 1, wmesg, 1);
+ #else
  	while (wdtab[ctrlr].b_active)
  		tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
+ #endif
  	splx(s);
  }
  
-----------------------------------------------------------------------------


Index: pci.c
===================================================================
RCS file: /usr/cvsroot/src/sys/pci/pci.c,v
retrieving revision 1.65
diff -c -r1.65 pci.c
*** pci.c	1997/02/05 07:23:56	1.65
--- pci.c	1997/02/11 20:27:40
***************
*** 148,153 ****
--- 148,156 ----
  				 * for the Orion host to PCI bridge
  				 * UGLY hack ... :( Will be changed :)
  				 */
+ #ifdef CMD640
+ int cmd640_detected	   = 0;
+ #endif
  /*--------------------------------------------------------
  **
  **	Local variables.
***************
*** 728,733 ****
--- 731,740 ----
  
  		if ((!type) || (type==0xfffffffful)) continue;
  
+ #ifdef CMD640
+ 		if (type == 0x06401095)
+ 			cmd640_detected = 1;
+ #endif
  		/*
  		**	lookup device in ioconfiguration:
  		*/



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199702112257.XAA01704>