xref: /csrg-svn/sys/vax/stand/up.c (revision 11143)
1*11143Ssam /*	up.c	4.8	83/02/18	*/
29974Ssam 
310023Ssam /*
410023Ssam  * UNIBUS peripheral standalone driver
510023Ssam  * with ECC correction and bad block forwarding.
610023Ssam  * Also supports header operation and write
710023Ssam  * check for data and/or header.
810023Ssam  */
910352Shelge 
109974Ssam #include "../h/param.h"
119974Ssam #include "../h/inode.h"
129974Ssam #include "../h/fs.h"
139974Ssam #include "../h/dkbad.h"
149974Ssam #include "../h/vmmac.h"
159974Ssam 
169974Ssam #include "../vax/pte.h"
179974Ssam #include "../vaxuba/upreg.h"
189974Ssam #include "../vaxuba/ubareg.h"
199974Ssam 
2010023Ssam #include "saio.h"
219974Ssam #include "savax.h"
229974Ssam 
2310352Shelge #define MAXBADDESC	126	/* max number of bad sectors recorded */
2410352Shelge #define SECTSIZ		512	/* sector size in bytes */
2510352Shelge #define HDRSIZ		4	/* number of bytes in sector header */
2611118Ssam #define MAXECC		5	/* max # bad bits allowed on ecc w/ F_ECCLM */
2710352Shelge 
2810023Ssam u_short	ubastd[] = { 0776700 };
299974Ssam 
309974Ssam char	up_gottype[MAXNUBA*8] = { 0 };
319974Ssam char	up_type[MAXNUBA*8] = { 0 };
32*11143Ssam extern	struct st upst[];
3310023Ssam 
349974Ssam u_char	up_offset[16] = {
359974Ssam 	UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
369974Ssam 	UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
379974Ssam 	UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
389974Ssam 	0, 0, 0, 0
399974Ssam };
409974Ssam 
4110023Ssam struct  dkbad upbad[MAXNUBA*8];		/* bad sector table */
4210352Shelge int 	sectsiz;			/* real sector size */
4310023Ssam 
449974Ssam upopen(io)
459974Ssam 	register struct iob *io;
469974Ssam {
4710352Shelge 	register unit = io->i_unit;
4810023Ssam 	register struct updevice *upaddr;
4911118Ssam 	register struct st *st;
509974Ssam 
51*11143Ssam 	if (io->i_boff < 0 || io->i_boff > 7)
5210023Ssam 		_stop("up bad unit");
5310352Shelge 	upaddr = (struct updevice *)ubamem(unit, ubastd[0]);
5411085Ssam 	while ((upaddr->upcs1 & UP_DVA) == 0)
559974Ssam 		;
5610352Shelge 	if (up_gottype[unit] == 0) {
5710023Ssam 		register int i;
5810023Ssam 		struct iob tio;
5910023Ssam 
6011118Ssam 		up_type[unit] = upmaptype(unit, upaddr);
6111118Ssam 		if (up_type[unit] < 0)
6210023Ssam 			_stop("unknown drive type");
6311118Ssam 		st = &upst[up_type[unit]];
64*11143Ssam 		if (st->off[io->i_boff] == -1)
65*11143Ssam 			_stop("up bad unit");
6610023Ssam 		/*
6710023Ssam 		 * Read in the bad sector table:
6810023Ssam 		 *	copy the contents of the io structure
6910023Ssam 		 *	to tio for use during the bb pointer
7010023Ssam 		 *	read operation.
7110023Ssam 		 */
7210023Ssam 		tio = *io;
7310023Ssam 		tio.i_bn = st->nspc * st->ncyl - st->nsect;
749974Ssam 		tio.i_ma = (char *)&upbad[tio.i_unit];
7510638Shelge 		tio.i_cc = sizeof (struct dkbad);
7610023Ssam 		tio.i_flgs |= F_RDDATA;
7710023Ssam 		for (i = 0; i < 5; i++) {
7810638Shelge 			if (upstrategy(&tio, READ) == sizeof (struct dkbad))
7910023Ssam 				break;
809974Ssam 			tio.i_bn += 2;
819974Ssam 		}
829974Ssam 		if (i == 5) {
8310023Ssam 			printf("Unable to read bad sector table\n");
8410352Shelge 			for (i = 0; i < MAXBADDESC; i++) {
8510352Shelge 				upbad[unit].bt_bad[i].bt_cyl = -1;
8610352Shelge 				upbad[unit].bt_bad[i].bt_trksec = -1;
879974Ssam 			}
889974Ssam 		}
8910352Shelge 		up_gottype[unit] = 1;
909974Ssam 	}
919974Ssam 	io->i_boff = st->off[io->i_boff] * st->nspc;
9210023Ssam 	io->i_flgs &= ~F_TYPEMASK;
939974Ssam }
949974Ssam 
959974Ssam upstrategy(io, func)
969974Ssam 	register struct iob *io;
979974Ssam {
9810352Shelge 	int cn, tn, sn;
9910352Shelge 	register unit = io->i_unit;
1009974Ssam 	daddr_t bn;
1019974Ssam 	int recal, info, waitdry;
1029974Ssam 	register struct updevice *upaddr =
10310352Shelge 	    (struct updevice *)ubamem(unit, ubastd[0]);
10410352Shelge 	register struct st *st = &upst[up_type[unit]];
1059974Ssam 
10610352Shelge 	sectsiz = SECTSIZ;
10711085Ssam 	if (io->i_flgs & (F_HDR|F_HCHECK))
10810352Shelge 		sectsiz += HDRSIZ;
1099974Ssam 	upaddr->upcs2 = unit;
1109974Ssam 	if ((upaddr->upds & UPDS_VV) == 0) {
1119974Ssam 		upaddr->upcs1 = UP_DCLR|UP_GO;
1129974Ssam 		upaddr->upcs1 = UP_PRESET|UP_GO;
1139974Ssam 		upaddr->upof = UPOF_FMT22;
1149974Ssam 	}
11511085Ssam 	if ((upaddr->upds & UPDS_DREADY) == 0)
1169974Ssam 		_stop("up not ready");
1179974Ssam 	info = ubasetup(io, 1);
1189974Ssam 	upaddr->upwc = -io->i_cc / sizeof (short);
1199974Ssam 	upaddr->upba = info;
12011085Ssam 	recal = 0;
12111085Ssam 	io->i_errcnt = 0;
12211085Ssam 
12310638Shelge restart:
12411085Ssam 	bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz;
12510023Ssam 	while((upaddr->upds & UPDS_DRY) == 0)
12610023Ssam 		;
12710352Shelge 	if (upstart(io, bn) != 0) {
12810352Shelge 		ubafree(io, info);
1299974Ssam 		return (-1);
13010352Shelge 	}
1319974Ssam 	do {
1329974Ssam 		DELAY(25);
1339974Ssam 	} while ((upaddr->upcs1 & UP_RDY) == 0);
13411085Ssam 	/*
13511085Ssam 	 * If transfer has completed, free UNIBUS
13611085Ssam 	 * resources and return transfer size.
13711085Ssam 	 */
13811085Ssam 	if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) {
13910352Shelge 		ubafree(io, info);
14011085Ssam 		return (io->i_cc);
14110352Shelge 	}
1429974Ssam #ifdef LOGALLERRS
1439974Ssam 	printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
14411085Ssam 		upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
14511085Ssam 	    	upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
14611085Ssam 		UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
1479974Ssam #endif
1489974Ssam 	waitdry = 0;
14910352Shelge 	while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz)
15010023Ssam 		DELAY(5);
15111085Ssam 	if (upaddr->uper1&UPER1_WLE) {
15211085Ssam 		/*
15311085Ssam 		 * Give up on write locked devices immediately.
15411085Ssam 		 */
15511085Ssam 		printf("up%d: write locked\n", unit);
15611085Ssam 		return (-1);
15711085Ssam 	}
1589974Ssam 	if (++io->i_errcnt > 27) {
1599974Ssam 		/*
1609974Ssam 		 * After 28 retries (16 without offset, and
1619974Ssam 		 * 12 with offset positioning) give up.
1629974Ssam 		 */
16310023Ssam 		io->i_error = EHER;
16410023Ssam 		if (upaddr->upcs2 & UPCS2_WCE)
16510023Ssam 			io->i_error = EWCK;
16610352Shelge hard:
16711085Ssam 		bn = io->i_bn +
16811085Ssam 			(io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
1699974Ssam 		cn = bn/st->nspc;
1709974Ssam 		sn = bn%st->nspc;
1719974Ssam 		tn = sn/st->nsect;
1729974Ssam 		sn = sn%st->nsect;
17311085Ssam 		printf(
17411085Ssam 		  "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
17511085Ssam 		   cn, tn, sn,
17611085Ssam 		   upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
17711085Ssam 		   UPER1_BITS, upaddr->uper2, UPER2_BITS);
1789974Ssam 		upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
17910352Shelge 		io->i_errblk = bn;
18011085Ssam 		return (io->i_cc + upaddr->upwc * sizeof(short));
18111085Ssam 	}
18211085Ssam 	if (upaddr->uper2 & UPER2_BSE) {
183*11143Ssam 		if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0)
1849974Ssam 			goto success;
18511085Ssam 		io->i_error = EBSE;
18611085Ssam 		goto hard;
1879974Ssam 	}
18811085Ssam 	/*
18911085Ssam 	 * Retriable error.
19011085Ssam 	 * If a soft ecc, correct it
19111085Ssam 	 * Otherwise fall through and retry the transfer
19211085Ssam 	 */
19311085Ssam 	if (upaddr->uper1 & UPER1_DCK) {
1949974Ssam 		/*
19511085Ssam 		 * If a write check command is active, all
19611085Ssam 		 * ecc errors give UPER1_ECH.
1979974Ssam 		 */
19811085Ssam 		if ((upaddr->uper1 & UPER1_ECH) == 0 ||
19911085Ssam 		    (upaddr->upcs2 & UPCS2_WCE)) {
20011085Ssam 			if (upecc(io, ECC) == 0)
20111085Ssam 				goto success;
20211085Ssam 			io->i_error = EECC;
20311085Ssam 			goto hard;
20411085Ssam 		}
20511085Ssam 	}
2069974Ssam 	/*
2079974Ssam 	 * Clear drive error and, every eight attempts,
2089974Ssam 	 * (starting with the fourth)
2099974Ssam 	 * recalibrate to clear the slate.
2109974Ssam 	 */
2119974Ssam 	upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
21210638Shelge 	if ((io->i_errcnt&07) == 4 ) {
2139974Ssam 		upaddr->upcs1 = UP_RECAL|UP_GO;
21410638Shelge 		recal = 1;
21510638Shelge 		goto restart;
2169974Ssam 	}
2179974Ssam 	/*
2189974Ssam 	 * Advance recalibration finite state machine
2199974Ssam 	 * if recalibrate in progress, through
2209974Ssam 	 *	RECAL
2219974Ssam 	 *	SEEK
2229974Ssam 	 *	OFFSET (optional)
2239974Ssam 	 *	RETRY
2249974Ssam 	 */
2259974Ssam 	switch (recal) {
2269974Ssam 
2279974Ssam 	case 1:
2289974Ssam 		upaddr->updc = cn;
2299974Ssam 		upaddr->upcs1 = UP_SEEK|UP_GO;
23011085Ssam 		recal = 2;
23110638Shelge 		goto restart;
23210023Ssam 
2339974Ssam 	case 2:
2349974Ssam 		if (io->i_errcnt < 16 || (func & READ) == 0)
2359974Ssam 			goto donerecal;
2369974Ssam 		upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
2379974Ssam 		upaddr->upcs1 = UP_OFFSET|UP_GO;
23811085Ssam 		recal = 3;
23910638Shelge 		goto restart;
24010023Ssam 
2419974Ssam 	donerecal:
2429974Ssam 	case 3:
2439974Ssam 		recal = 0;
2449974Ssam 		break;
2459974Ssam 	}
2469974Ssam 	/*
24710638Shelge 	 * If we were offset positioning,
24810638Shelge 	 * return to centerline.
2499974Ssam 	 */
25010638Shelge 	if (io->i_errcnt >= 16) {
25110638Shelge 		upaddr->upof = UPOF_FMT22;
25210638Shelge 		upaddr->upcs1 = UP_RTC|UP_GO;
25310638Shelge 		while ((upaddr->upds&UPDS_DRY) == 0)
25410638Shelge 			DELAY(25);
2559974Ssam 	}
25610638Shelge 	goto restart;
25711085Ssam 
2589974Ssam success:
25910023Ssam 	if (upaddr->upwc != 0)
26010638Shelge 		goto restart;
2619974Ssam 	/*
2629974Ssam 	 * Release unibus
2639974Ssam 	 */
2649974Ssam 	ubafree(io, info);
2659974Ssam 	return (io->i_cc);
2669974Ssam }
2679974Ssam 
2689974Ssam /*
269*11143Ssam  * Correct an ECC error, and restart the
270*11143Ssam  * i/o to complete the transfer (if necessary).
271*11143Ssam  * This is quite complicated because the transfer
272*11143Ssam  * may be going to an odd memory address base and/or
2739974Ssam  * across a page boundary.
2749974Ssam  */
27510023Ssam upecc(io, flag)
2769974Ssam 	register struct iob *io;
2779974Ssam 	int flag;
2789974Ssam {
2799974Ssam 	register struct updevice *up =
2809974Ssam 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
28110352Shelge 	register struct st *st;
2829974Ssam 	register int i;
2839974Ssam 	caddr_t addr;
28410410Shelge 	int bn, twc, npf, mask, cn, tn, sn;
28510352Shelge 	daddr_t bbn;
2869974Ssam 
2879974Ssam 	/*
288*11143Ssam 	 * Npf is the number of sectors transferred
289*11143Ssam 	 * before the sector containing the ECC error;
290*11143Ssam 	 * bn is the current block number.
2919974Ssam 	 */
29210352Shelge 	twc = up->upwc;
293*11143Ssam 	npf = ((twc * sizeof(short)) + io->i_cc) / sectsiz;
2949974Ssam #ifdef UPECCDEBUG
295*11143Ssam 	printf("npf %d mask 0x%x pos %d wc 0x%x\n",
296*11143Ssam 		npf, up->ec2, up->upec1, -twc);
2979974Ssam #endif
29810352Shelge 	bn = io->i_bn + npf ;
2999974Ssam 	st = &upst[up_type[io->i_unit]];
30010410Shelge 	cn = bn/st->nspc;
30110410Shelge 	sn = bn%st->nspc;
30210410Shelge 	tn = sn/st->nsect;
30310410Shelge 	sn = sn%st->nsect;
304*11143Ssam 
3059974Ssam 	/*
306*11143Ssam 	 * ECC correction.
3079974Ssam 	 */
3089974Ssam 	if (flag == ECC) {
30910352Shelge 		int bit, byte, ecccnt;
31011085Ssam 
31110352Shelge 		ecccnt = 0;
3129974Ssam 		mask = up->upec2;
31310638Shelge 		printf("up%d: soft ecc sn%d\n", io->i_unit, bn);
3149974Ssam 		/*
315*11143Ssam 		 * Compute the byte and bit position
316*11143Ssam 		 * of the error.  i is the byte offset
317*11143Ssam 		 * in the transfer at which the correction
318*11143Ssam 		 * is first applied.
3199974Ssam 		 */
3209974Ssam 		i = up->upec1 - 1;		/* -1 makes 0 origin */
3219974Ssam 		bit = i&07;
3229974Ssam 		i = (i&~07)>>3;
3239974Ssam 		byte = i;
3249974Ssam 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
3259974Ssam 		/*
326*11143Ssam 		 * Correct while possible bits remain of mask.
327*11143Ssam 		 * Since mask contains 11 bits, we continue while
328*11143Ssam 		 * the bit offset is > -11.  Also watch out for
329*11143Ssam 		 * end of this block and the end of the transfer.
3309974Ssam 		 */
33110352Shelge 		while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) {
3329974Ssam 			/*
333*11143Ssam 			 * addr =
334*11143Ssam 			 *  vax base addr +
335*11143Ssam 			 *  (# sectors transferred before the error) *
336*11143Ssam 			 *    (sector size) +
337*11143Ssam 			 *  byte number
3389974Ssam 			 */
33911085Ssam 			addr = io->i_ma + (npf * sectsiz) + byte;
3409974Ssam #ifdef UPECCDEBUG
34110352Shelge 			printf("addr %x old: %x ",addr, (*addr&0xff));
3429974Ssam #endif
34310352Shelge 			if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0)
34410352Shelge 				*addr ^= (mask << bit);
3459974Ssam #ifdef UPECCDEBUG
34610352Shelge 			printf("new: %x\n", (*addr&0xff));
3479974Ssam #endif
348*11143Ssam 			byte++, i++;
3499974Ssam 			bit -= 8;
35011085Ssam 			if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC)
35111085Ssam 				return (1);
3529974Ssam 		}
35311085Ssam 		return (0);
35411085Ssam 	}
355*11143Ssam 
356*11143Ssam 	/*
357*11143Ssam 	 * Bad sector forwarding.
358*11143Ssam 	 */
35911085Ssam 	if (flag == BSE) {
3609974Ssam 		/*
361*11143Ssam 		 * If not in bad sector table,
362*11143Ssam 		 * indicate a hard error to caller.
3639974Ssam 		 */
36410352Shelge 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
36510410Shelge 		if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0)
36611085Ssam 			return (1);
3679974Ssam 		bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
36810352Shelge 		twc = up->upwc + sectsiz;
36911085Ssam 		up->upwc = - (sectsiz / sizeof (short));
3709974Ssam #ifdef UPECCDEBUG
3719974Ssam 		printf("revector to block %d\n", bbn);
3729974Ssam #endif
3739974Ssam 		/*
374*11143Ssam 	 	 * Clear the drive & read the replacement
375*11143Ssam 		 * sector.  If this is in the middle of a
376*11143Ssam 		 * transfer, then set up the controller
377*11143Ssam 		 * registers in a normal fashion.
378*11143Ssam 	 	 * The UNIBUS address need not be changed.
379*11143Ssam 	 	 */
38010023Ssam 		while (up->upcs1 & UP_RDY == 0)
3819974Ssam 			;
38210023Ssam 		if (upstart(io, bbn) != 0)
38310352Shelge 			return (1);		/* error */
38410352Shelge 		io->i_errcnt = 0;		/* success */
3859974Ssam 		do {
3869974Ssam 			DELAY(25);
3879974Ssam 		} while ( up->upcs1 & UP_RDY == 0) ;
3889974Ssam 		if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
38910352Shelge 			up->upwc = twc -sectsiz;
39010352Shelge 			return (1);
3919974Ssam 		}
3929974Ssam 	}
39310638Shelge 	if (twc)
3949974Ssam 		up->upwc = twc;
39510352Shelge 	return (0);
3969974Ssam }
3979974Ssam 
3989974Ssam upstart(io, bn)
39910023Ssam 	register struct iob *io;
40010023Ssam 	daddr_t bn;
4019974Ssam {
4029974Ssam 	register struct updevice *upaddr =
40310023Ssam 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
40410352Shelge 	register struct st *st = &upst[up_type[io->i_unit]];
4059974Ssam 	int sn, tn;
4069974Ssam 
4079974Ssam 	sn = bn%st->nspc;
4089974Ssam 	tn = sn/st->nsect;
4099974Ssam 	sn %= st->nsect;
4109974Ssam 	upaddr->updc = bn/st->nspc;
4119974Ssam 	upaddr->upda = (tn << 8) + sn;
41210352Shelge 	switch (io->i_flgs & F_TYPEMASK) {
41310023Ssam 
41410023Ssam 	case F_RDDATA:
41510023Ssam 		upaddr->upcs1 = UP_RCOM|UP_GO;
4169974Ssam 		break;
41710023Ssam 
41810023Ssam 	case F_WRDATA:
41910023Ssam 		upaddr->upcs1 = UP_WCOM|UP_GO;
4209974Ssam 		break;
42110023Ssam 
42210023Ssam 	case F_HDR|F_RDDATA:
42310023Ssam 		upaddr->upcs1 = UP_RHDR|UP_GO;
42410023Ssam 		break;
42510023Ssam 
42610023Ssam 	case F_HDR|F_WRDATA:
42710023Ssam 		upaddr->upcs1 = UP_WHDR|UP_GO;
42810023Ssam 		break;
42910023Ssam 
43010023Ssam 	case F_CHECK|F_WRDATA:
43110023Ssam 	case F_CHECK|F_RDDATA:
4329974Ssam 		upaddr->upcs1 = UP_WCDATA|UP_GO;
4339974Ssam 		break;
43410023Ssam 
43510023Ssam 	case F_HCHECK|F_WRDATA:
43610023Ssam 	case F_HCHECK|F_RDDATA:
4379974Ssam 		upaddr->upcs1 = UP_WCHDR|UP_GO;
4389974Ssam 		break;
43910023Ssam 
4409974Ssam 	default:
44110023Ssam 		io->i_error = ECMD;
44210023Ssam 		io->i_flgs &= ~F_TYPEMASK;
44310023Ssam 		return (1);
4449974Ssam 	}
44510023Ssam 	return (0);
4469974Ssam }
4479974Ssam 
44810023Ssam /*ARGSUSED*/
44910023Ssam upioctl(io, cmd, arg)
45010023Ssam 	struct iob *io;
45110023Ssam 	int cmd;
45210023Ssam 	caddr_t arg;
45310023Ssam {
45410352Shelge 	struct st *st = &upst[up_type[io->i_unit]], *tmp;
45510352Shelge 
45610352Shelge 	switch(cmd) {
45710352Shelge 
45810352Shelge 	case SAIODEVDATA:
45910352Shelge 		tmp = (struct st *)arg;
46010352Shelge 		*tmp = *st;
46111085Ssam 		return (0);
46210352Shelge 	}
46311085Ssam 	return (ECMD);
46410023Ssam }
465