xref: /csrg-svn/sys/vax/stand/up.c (revision 11085)
1 /*	up.c	4.6	83/02/16	*/
2 
3 /*
4  * UNIBUS peripheral standalone driver
5  * with ECC correction and bad block forwarding.
6  * Also supports header operation and write
7  * check for data and/or header.
8  */
9 
10 #include "../h/param.h"
11 #include "../h/inode.h"
12 #include "../h/fs.h"
13 #include "../h/dkbad.h"
14 #include "../h/vmmac.h"
15 
16 #include "../vax/pte.h"
17 #include "../vaxuba/upreg.h"
18 #include "../vaxuba/ubareg.h"
19 
20 #include "saio.h"
21 #include "savax.h"
22 
23 #define MAXBADDESC	126	/* max number of bad sectors recorded */
24 #define SECTSIZ		512	/* sector size in bytes */
25 #define HDRSIZ		4	/* number of bytes in sector header */
26 #define MAXECC		5	/* max number of bad bits accepted in
27 				 * a soft ecc error when F_ECCLM is set */
28 #define	NUPTYPES	3
29 
30 u_short	ubastd[] = { 0776700 };
31 
32 char	up_gottype[MAXNUBA*8] = { 0 };
33 char	up_type[MAXNUBA*8] = { 0 };
34 short	up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
35 short	fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 };
36 /* this is called upam instead of am because hp.c has a similar array */
37 short	upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 };
38 
39 struct st upst[NUPTYPES] = {
40 	32,	19,	32*19,	823,	up_off,		/* 9300/equiv */
41 	32,	10,	32*10,	823,	fj_off,		/* Fuji 160 */
42 	32,	16,	32*16,	1024,	upam_off,	/* Capricorn */
43 };
44 
45 u_char	up_offset[16] = {
46 	UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
47 	UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
48 	UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
49 	0, 0, 0, 0
50 };
51 
52 struct  dkbad upbad[MAXNUBA*8];		/* bad sector table */
53 int 	sectsiz;			/* real sector size */
54 
55 upopen(io)
56 	register struct iob *io;
57 {
58 	register unit = io->i_unit;
59 	register struct updevice *upaddr;
60 	register struct st *st = &upst[up_type[unit]];
61 
62 	if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1)
63 		_stop("up bad unit");
64 	upaddr = (struct updevice *)ubamem(unit, ubastd[0]);
65 	while ((upaddr->upcs1 & UP_DVA) == 0)
66 		;
67 	if (up_gottype[unit] == 0) {
68 		register int i;
69 		struct iob tio;
70 
71 		upaddr->uphr = UPHR_MAXTRAK;
72 		for (st = upst; st < &upst[NUPTYPES]; st++)
73 			if (upaddr->uphr == st->ntrak - 1) {
74 				up_type[unit] = st - upst;
75 				break;
76 			}
77 		if (st == &upst[NUPTYPES]) {
78 			printf("up%d: uphr=%x\n", unit, upaddr->uphr);
79 			_stop("unknown drive type");
80 		}
81 		upaddr->upcs2 = UPCS2_CLR;
82 #ifdef DEBUG
83 		printf("Unittype=%d\n",up_type[unit]);
84 #endif
85 
86 		/*
87 		 * Read in the bad sector table:
88 		 *	copy the contents of the io structure
89 		 *	to tio for use during the bb pointer
90 		 *	read operation.
91 		 */
92 		tio = *io;
93 		tio.i_bn = st->nspc * st->ncyl - st->nsect;
94 		tio.i_ma = (char *)&upbad[tio.i_unit];
95 		tio.i_cc = sizeof (struct dkbad);
96 		tio.i_flgs |= F_RDDATA;
97 		for (i = 0; i < 5; i++) {
98 			if (upstrategy(&tio, READ) == sizeof (struct dkbad))
99 				break;
100 			tio.i_bn += 2;
101 		}
102 		if (i == 5) {
103 			printf("Unable to read bad sector table\n");
104 			for (i = 0; i < MAXBADDESC; i++) {
105 				upbad[unit].bt_bad[i].bt_cyl = -1;
106 				upbad[unit].bt_bad[i].bt_trksec = -1;
107 			}
108 		}
109 		up_gottype[unit] = 1;
110 	}
111 	io->i_boff = st->off[io->i_boff] * st->nspc;
112 	io->i_flgs &= ~F_TYPEMASK;
113 }
114 
115 upstrategy(io, func)
116 	register struct iob *io;
117 {
118 	int cn, tn, sn;
119 	register unit = io->i_unit;
120 	daddr_t bn;
121 	int recal, info, waitdry;
122 	register struct updevice *upaddr =
123 	    (struct updevice *)ubamem(unit, ubastd[0]);
124 	register struct st *st = &upst[up_type[unit]];
125 
126 	sectsiz = SECTSIZ;
127 	if (io->i_flgs & (F_HDR|F_HCHECK))
128 		sectsiz += HDRSIZ;
129 	upaddr->upcs2 = unit;
130 	if ((upaddr->upds & UPDS_VV) == 0) {
131 		upaddr->upcs1 = UP_DCLR|UP_GO;
132 		upaddr->upcs1 = UP_PRESET|UP_GO;
133 		upaddr->upof = UPOF_FMT22;
134 	}
135 	if ((upaddr->upds & UPDS_DREADY) == 0)
136 		_stop("up not ready");
137 	info = ubasetup(io, 1);
138 	upaddr->upwc = -io->i_cc / sizeof (short);
139 	upaddr->upba = info;
140 	recal = 0;
141 	io->i_errcnt = 0;
142 
143 restart:
144 	bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz;
145 	while((upaddr->upds & UPDS_DRY) == 0)
146 		;
147 	if (upstart(io, bn) != 0) {
148 		ubafree(io, info);
149 		return (-1);
150 	}
151 	do {
152 		DELAY(25);
153 	} while ((upaddr->upcs1 & UP_RDY) == 0);
154 	/*
155 	 * If transfer has completed, free UNIBUS
156 	 * resources and return transfer size.
157 	 */
158 	if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) {
159 		ubafree(io, info);
160 		return (io->i_cc);
161 	}
162 #ifdef LOGALLERRS
163 	printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
164 		upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
165 	    	upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
166 		UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
167 #endif
168 	waitdry = 0;
169 	while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz)
170 		DELAY(5);
171 	if (upaddr->uper1&UPER1_WLE) {
172 		/*
173 		 * Give up on write locked devices immediately.
174 		 */
175 		printf("up%d: write locked\n", unit);
176 		return (-1);
177 	}
178 	if (++io->i_errcnt > 27) {
179 		/*
180 		 * After 28 retries (16 without offset, and
181 		 * 12 with offset positioning) give up.
182 		 */
183 		io->i_error = EHER;
184 		if (upaddr->upcs2 & UPCS2_WCE)
185 			io->i_error = EWCK;
186 hard:
187 		bn = io->i_bn +
188 			(io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
189 		cn = bn/st->nspc;
190 		sn = bn%st->nspc;
191 		tn = sn/st->nsect;
192 		sn = sn%st->nsect;
193 		printf(
194 		  "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
195 		   cn, tn, sn,
196 		   upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
197 		   UPER1_BITS, upaddr->uper2, UPER2_BITS);
198 		upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
199 		io->i_errblk = bn;
200 		return (io->i_cc + upaddr->upwc * sizeof(short));
201 	}
202 	if (upaddr->uper2 & UPER2_BSE) {
203 		short wc = upaddr->upwc;
204 		if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) {
205 			if (wc != upaddr->upwc)
206 				printf("wc %x upwc %x\n", wc, upaddr->upwc);
207 			goto success;
208 		}
209 		io->i_error = EBSE;
210 		goto hard;
211 	}
212 	/*
213 	 * Retriable error.
214 	 * If a soft ecc, correct it
215 	 * Otherwise fall through and retry the transfer
216 	 */
217 	if (upaddr->uper1 & UPER1_DCK) {
218 		/*
219 		 * If a write check command is active, all
220 		 * ecc errors give UPER1_ECH.
221 		 */
222 		if ((upaddr->uper1 & UPER1_ECH) == 0 ||
223 		    (upaddr->upcs2 & UPCS2_WCE)) {
224 			if (upecc(io, ECC) == 0)
225 				goto success;
226 			io->i_error = EECC;
227 			goto hard;
228 		}
229 	}
230 	/*
231 	 * Clear drive error and, every eight attempts,
232 	 * (starting with the fourth)
233 	 * recalibrate to clear the slate.
234 	 */
235 	upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
236 	if ((io->i_errcnt&07) == 4 ) {
237 		upaddr->upcs1 = UP_RECAL|UP_GO;
238 		recal = 1;
239 		goto restart;
240 	}
241 	/*
242 	 * Advance recalibration finite state machine
243 	 * if recalibrate in progress, through
244 	 *	RECAL
245 	 *	SEEK
246 	 *	OFFSET (optional)
247 	 *	RETRY
248 	 */
249 	switch (recal) {
250 
251 	case 1:
252 		upaddr->updc = cn;
253 		upaddr->upcs1 = UP_SEEK|UP_GO;
254 		recal = 2;
255 		goto restart;
256 
257 	case 2:
258 		if (io->i_errcnt < 16 || (func & READ) == 0)
259 			goto donerecal;
260 		upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
261 		upaddr->upcs1 = UP_OFFSET|UP_GO;
262 		recal = 3;
263 		goto restart;
264 
265 	donerecal:
266 	case 3:
267 		recal = 0;
268 		break;
269 	}
270 	/*
271 	 * If we were offset positioning,
272 	 * return to centerline.
273 	 */
274 	if (io->i_errcnt >= 16) {
275 		upaddr->upof = UPOF_FMT22;
276 		upaddr->upcs1 = UP_RTC|UP_GO;
277 		while ((upaddr->upds&UPDS_DRY) == 0)
278 			DELAY(25);
279 	}
280 	goto restart;
281 
282 success:
283 	if (upaddr->upwc != 0)
284 		goto restart;
285 	/*
286 	 * Release unibus
287 	 */
288 	ubafree(io, info);
289 	return (io->i_cc);
290 }
291 
292 /*
293  * Correct an ECC error, and restart the i/o to complete
294  * the transfer if necessary.  This is quite complicated because
295  * the transfer may be going to an odd memory address base and/or
296  * across a page boundary.
297  */
298 upecc(io, flag)
299 	register struct iob *io;
300 	int flag;
301 {
302 	register struct updevice *up =
303 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
304 	register struct st *st;
305 	register int i;
306 	caddr_t addr;
307 	int bn, twc, npf, mask, cn, tn, sn;
308 	daddr_t bbn;
309 
310 	/*
311 	 * Npf is the number of sectors transferred before the sector
312 	 * containing the ECC error, bn is the current block number
313 	 */
314 	twc = up->upwc;
315 	npf = ((twc * sizeof(short)) + io->i_cc)/sectsiz;
316 #ifdef UPECCDEBUG
317 	printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc);
318 #endif
319 	bn = io->i_bn + npf ;
320 	st = &upst[up_type[io->i_unit]];
321 	cn = bn/st->nspc;
322 	sn = bn%st->nspc;
323 	tn = sn/st->nsect;
324 	sn = sn%st->nsect;
325 	/*
326 	 * action taken depends on the flag
327 	 */
328 	if (flag == ECC) {
329 		int bit, byte, ecccnt;
330 
331 		ecccnt = 0;
332 		mask = up->upec2;
333 		printf("up%d: soft ecc sn%d\n", io->i_unit, bn);
334 		/*
335 		 * Compute the
336 		 * byte and bit position of the error.  The variable i
337 		 * is the byte offset in the transfer.
338 		 */
339 		i = up->upec1 - 1;		/* -1 makes 0 origin */
340 		bit = i&07;
341 		i = (i&~07)>>3;
342 		byte = i;
343 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
344 		/*
345 		 * Correct while possible bits remain of mask.  Since mask
346 		 * contains 11 bits, we continue while the bit offset is > -11.
347 		 * Also watch out for end of this block and the end of the whole
348 		 * transfer.
349 		 */
350 		while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) {
351 			/*
352 			 * addr = vax base addr + (number of sectors transferred
353 			 *	  before the error sector times the sector size)
354 			 *	  + byte number
355 			 */
356 			addr = io->i_ma + (npf * sectsiz) + byte;
357 #ifdef UPECCDEBUG
358 			printf("addr %x old: %x ",addr, (*addr&0xff));
359 #endif
360 			if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0)
361 				*addr ^= (mask << bit);
362 #ifdef UPECCDEBUG
363 			printf("new: %x\n", (*addr&0xff));
364 #endif
365 			byte++;
366 			i++;
367 			bit -= 8;
368 			if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC)
369 				return (1);
370 		}
371 		return (0);
372 	}
373 	if (flag == BSE) {
374 		/*
375 		 * if not in bad sector table, return 1 (= hard error)
376 		 */
377 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
378 		if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0)
379 			return (1);
380 		bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
381 		twc = up->upwc + sectsiz;
382 		up->upwc = - (sectsiz / sizeof (short));
383 #ifdef UPECCDEBUG
384 		printf("revector to block %d\n", bbn);
385 #endif
386 		/*
387 	 	* Clear the drive & read the replacement sector.
388 	 	* If this is in the middle of a transfer, then set up the
389 	 	* controller registers in a normal fashion.
390 	 	* The ub-address need not be changed.
391 	 	*/
392 		while (up->upcs1 & UP_RDY == 0)
393 			;
394 		if (upstart(io, bbn) != 0)
395 			return (1);		/* error */
396 		io->i_errcnt = 0;		/* success */
397 		do {
398 			DELAY(25);
399 		} while ( up->upcs1 & UP_RDY == 0) ;
400 		if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
401 			up->upwc = twc -sectsiz;
402 			return (1);
403 		}
404 	}
405 	if (twc)
406 		up->upwc = twc;
407 	return (0);
408 }
409 
410 upstart(io, bn)
411 	register struct iob *io;
412 	daddr_t bn;
413 {
414 	register struct updevice *upaddr =
415 		(struct updevice *)ubamem(io->i_unit, ubastd[0]);
416 	register struct st *st = &upst[up_type[io->i_unit]];
417 	int sn, tn;
418 
419 	sn = bn%st->nspc;
420 	tn = sn/st->nsect;
421 	sn %= st->nsect;
422 	upaddr->updc = bn/st->nspc;
423 	upaddr->upda = (tn << 8) + sn;
424 	switch (io->i_flgs & F_TYPEMASK) {
425 
426 	case F_RDDATA:
427 		upaddr->upcs1 = UP_RCOM|UP_GO;
428 		break;
429 
430 	case F_WRDATA:
431 		upaddr->upcs1 = UP_WCOM|UP_GO;
432 		break;
433 
434 	case F_HDR|F_RDDATA:
435 		upaddr->upcs1 = UP_RHDR|UP_GO;
436 		break;
437 
438 	case F_HDR|F_WRDATA:
439 		upaddr->upcs1 = UP_WHDR|UP_GO;
440 		break;
441 
442 	case F_CHECK|F_WRDATA:
443 	case F_CHECK|F_RDDATA:
444 		upaddr->upcs1 = UP_WCDATA|UP_GO;
445 		break;
446 
447 	case F_HCHECK|F_WRDATA:
448 	case F_HCHECK|F_RDDATA:
449 		upaddr->upcs1 = UP_WCHDR|UP_GO;
450 		break;
451 
452 	default:
453 		io->i_error = ECMD;
454 		io->i_flgs &= ~F_TYPEMASK;
455 		return (1);
456 	}
457 	return (0);
458 }
459 
460 /*ARGSUSED*/
461 upioctl(io, cmd, arg)
462 	struct iob *io;
463 	int cmd;
464 	caddr_t arg;
465 {
466 	struct st *st = &upst[up_type[io->i_unit]], *tmp;
467 
468 	switch(cmd) {
469 
470 	case SAIODEVDATA:
471 		tmp = (struct st *)arg;
472 		*tmp = *st;
473 		return (0);
474 	}
475 	return (ECMD);
476 }
477