xref: /csrg-svn/sys/vax/uba/up.c (revision 2379)
1*2379Swnj /*	up.c	4.12	02/08/81	*/
2264Sbill 
31937Swnj #include "up.h"
41809Sbill #if NUP > 0
5264Sbill /*
6885Sbill  * UNIBUS disk driver with overlapped seeks and ECC recovery.
7264Sbill  */
81756Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
9264Sbill 
10264Sbill #include "../h/param.h"
11264Sbill #include "../h/systm.h"
12308Sbill #include "../h/dk.h"
13264Sbill #include "../h/buf.h"
14264Sbill #include "../h/conf.h"
15264Sbill #include "../h/dir.h"
16264Sbill #include "../h/user.h"
17264Sbill #include "../h/map.h"
18420Sbill #include "../h/pte.h"
19264Sbill #include "../h/mba.h"
20264Sbill #include "../h/mtpr.h"
21264Sbill #include "../h/uba.h"
22264Sbill #include "../h/vm.h"
23*2379Swnj #include "../h/cmap.h"
24264Sbill 
25*2379Swnj #include "../h/upreg.h"
26264Sbill 
27275Sbill /*
28275Sbill  * Software extension to the upas register, so we can
29275Sbill  * postpone starting SEARCH commands until the controller
30275Sbill  * is not transferring.
31275Sbill  */
32341Sbill int	upsoftas;
33275Sbill 
34275Sbill /*
35275Sbill  * If upseek then we don't issue SEARCH commands but rather just
36275Sbill  * settle for a SEEK to the correct cylinder.
37275Sbill  */
38275Sbill int	upseek;
39275Sbill 
40264Sbill #define	NSECT	32
41264Sbill #define	NTRAC	19
42264Sbill 
43264Sbill /*
44264Sbill  * Constants controlling on-cylinder SEARCH usage.
45264Sbill  *
46308Sbill  * 	upSDIST/2 msec		time needed to start transfer
47308Sbill  * 	upRDIST/2 msec		tolerable rotational latency when on-cylinder
48275Sbill  *
49308Sbill  * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
50275Sbill  * and in the driver then we take it as it is.  Otherwise we do a SEARCH
51308Sbill  * requesting an interrupt upSDIST sectors in advance.
52264Sbill  */
531592Sbill #define	_upSDIST	2		/* 1.0 msec */
541592Sbill #define	_upRDIST	4		/* 2.0 msec */
55264Sbill 
56308Sbill int	upSDIST = _upSDIST;
57308Sbill int	upRDIST = _upRDIST;
58275Sbill 
59264Sbill /*
60264Sbill  * To fill a 300M drive:
61264Sbill  *	A is designed to be used as a root.
62264Sbill  *	B is suitable for a swap area.
63264Sbill  *	H is the primary storage area.
64264Sbill  * On systems with RP06'es, we normally use only 291346 blocks of the H
65264Sbill  * area, and use DEF or G to cover the rest of the drive.  The C system
66264Sbill  * covers the whole drive and can be used for pack-pack copying.
671756Sbill  *
681756Sbill  * Note: sizes here are for AMPEX drives with 815 cylinders.
691756Sbill  * CDC drives can make the F,G, and H areas larger as they have 823 cylinders.
70264Sbill  */
71264Sbill struct	size
72264Sbill {
73264Sbill 	daddr_t	nblocks;
74264Sbill 	int	cyloff;
75264Sbill } up_sizes[8] = {
76264Sbill 	15884,	0,		/* A=cyl 0 thru 26 */
77264Sbill 	33440,	27,		/* B=cyl 27 thru 81 */
78341Sbill 	495520,	0,		/* C=cyl 0 thru 814 */
79264Sbill 	15884,	562,		/* D=cyl 562 thru 588 */
80264Sbill 	55936,	589,		/* E=cyl 589 thru 680 */
81264Sbill 	81472,	681,		/* F=cyl 681 thru 814 */
82264Sbill 	153824,	562,		/* G=cyl 562 thru 814 */
83264Sbill 	291346,	82,		/* H=cyl 82 thru 561 */
84264Sbill };
85264Sbill 
86264Sbill /*
87264Sbill  * The following defines are used in offset positioning
88264Sbill  * when trying to recover disk errors, with the constants being
89264Sbill  * +/- microinches.  Note that header compare inhibit (HCI) is not
90264Sbill  * tried (this makes sense only during read, in any case.)
91264Sbill  *
921756Sbill  * NB: Not all drives/controllers emulate all of these.
93264Sbill  */
94264Sbill #define	P400	020
95264Sbill #define	M400	0220
96264Sbill #define	P800	040
97264Sbill #define	M800	0240
98264Sbill #define	P1200	060
99264Sbill #define	M1200	0260
100264Sbill #define	HCI	020000
101264Sbill 
102264Sbill int	up_offset[16] =
103264Sbill {
104264Sbill 	P400, M400, P400, M400,
105264Sbill 	P800, M800, P800, M800,
106264Sbill 	P1200, M1200, P1200, M1200,
107264Sbill 	0, 0, 0, 0,
108264Sbill };
109264Sbill 
110264Sbill /*
111264Sbill  * Each drive has a table uputab[i].  On this table are sorted the
112264Sbill  * pending requests implementing an elevator algorithm (see dsort.c.)
113264Sbill  * In the upustart() routine, each drive is independently advanced
114264Sbill  * until it is on the desired cylinder for the next transfer and near
115264Sbill  * the desired sector.  The drive is then chained onto the uptab
116264Sbill  * table, and the transfer is initiated by the upstart() routine.
117264Sbill  * When the transfer is completed the driver reinvokes the upustart()
118264Sbill  * routine to set up the next transfer.
119264Sbill  */
120264Sbill struct	buf	uptab;
121264Sbill struct	buf	uputab[NUP];
122264Sbill 
123264Sbill struct	buf	rupbuf;			/* Buffer for raw i/o */
124264Sbill 
125264Sbill #define	b_cylin b_resid
126264Sbill 
127264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
128264Sbill 
129313Sbill int	up_wticks;		/* Ticks waiting for interrupt */
130313Sbill int	upwstart;		/* Have started guardian */
131313Sbill int	upwatch();
132313Sbill 
133264Sbill #ifdef INTRLVE
134264Sbill daddr_t dkblock();
135264Sbill #endif
136264Sbill 
137264Sbill /*
138264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
139264Sbill  *
140264Sbill  * A unit start is issued if the drive is inactive, causing
141264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
142264Sbill  * already nearly on the money and the controller is not transferring
143264Sbill  * we kick it to start the transfer.
144264Sbill  */
145264Sbill upstrategy(bp)
146264Sbill register struct buf *bp;
147264Sbill {
148264Sbill 	register struct buf *dp;
149264Sbill 	register unit, xunit;
150264Sbill 	long sz, bn;
151264Sbill 
152313Sbill 	if (upwstart == 0) {
1531783Sbill 		timeout(upwatch, (caddr_t)0, HZ);
154313Sbill 		upwstart++;
155313Sbill 	}
156264Sbill 	xunit = minor(bp->b_dev) & 077;
157264Sbill 	sz = bp->b_bcount;
158264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
159264Sbill 	unit = dkunit(bp);
160264Sbill 	if (unit >= NUP ||
161264Sbill 	    bp->b_blkno < 0 ||
162264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
163264Sbill 		bp->b_flags |= B_ERROR;
164264Sbill 		iodone(bp);
165264Sbill 		return;
166264Sbill 	}
1671945Swnj 	if (UPDK_N+unit <= UPDK_NMAX)
1681945Swnj 		dk_mspw[UPDK_N+unit] = .0000020345;
169264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
170264Sbill 	dp = &uputab[unit];
171264Sbill 	(void) spl5();
172264Sbill 	disksort(dp, bp);
173264Sbill 	if (dp->b_active == 0) {
174268Sbill 		(void) upustart(unit);
175264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
176268Sbill 			(void) upstart();
177264Sbill 	}
178264Sbill 	(void) spl0();
179264Sbill }
180264Sbill 
181264Sbill /*
182264Sbill  * Start activity on specified drive; called when drive is inactive
183264Sbill  * and new transfer request arrives and also when upas indicates that
184264Sbill  * a SEARCH command is complete.
185264Sbill  */
186264Sbill upustart(unit)
187264Sbill register unit;
188264Sbill {
189264Sbill 	register struct buf *bp, *dp;
190264Sbill 	register struct device *upaddr = UPADDR;
191264Sbill 	daddr_t bn;
192264Sbill 	int sn, cn, csn;
193268Sbill 	int didie = 0;
194264Sbill 
195275Sbill 	/*
196275Sbill 	 * Other drivers tend to say something like
197275Sbill 	 *	upaddr->upcs1 = IE;
198275Sbill 	 *	upaddr->upas = 1<<unit;
1991756Sbill 	 * here, but some controllers will cancel a command
200275Sbill 	 * happens to be sitting in the cs1 if you clear the go
2011756Sbill 	 * bit by storing there (so the first is not safe).
202275Sbill 	 *
203275Sbill 	 * Thus we keep careful track of when we re-enable IE
204275Sbill 	 * after an interrupt and do it only if we didn't issue
205275Sbill 	 * a command which re-enabled it as a matter of course.
206275Sbill 	 * We clear bits in upas in the interrupt routine, when
207275Sbill 	 * no transfers are active.
208275Sbill 	 */
209266Sbill 	if (unit >= NUP)
210268Sbill 		goto out;
2111945Swnj 	if (unit+UPDK_N <= UPDK_NMAX)
2121945Swnj 		dk_busy &= ~(1<<(unit+UPDK_N));
213264Sbill 	dp = &uputab[unit];
214266Sbill 	if ((bp = dp->b_actf) == NULL)
215268Sbill 		goto out;
216275Sbill 	/*
2171756Sbill 	 * Most controllers don't start SEARCH commands when transfers are
2181756Sbill 	 * in progress.  In fact, some tend to get confused when given
219275Sbill 	 * SEARCH'es during transfers, generating interrupts with neither
220275Sbill 	 * RDY nor a bit in the upas register.  Thus we defer
221275Sbill 	 * until an interrupt when a transfer is pending.
222275Sbill 	 */
223275Sbill 	if (uptab.b_active) {
224341Sbill 		upsoftas |= 1<<unit;
225275Sbill 		return (0);
226275Sbill 	}
227276Sbill 	if (dp->b_active)
228276Sbill 		goto done;
229276Sbill 	dp->b_active = 1;
2301756Sbill 	if ((upaddr->upcs2 & 07) != unit)
231264Sbill 		upaddr->upcs2 = unit;
232266Sbill 	/*
233266Sbill 	 * If we have changed packs or just initialized,
234275Sbill 	 * then the volume will not be valid; if so, clear
235266Sbill 	 * the drive, preset it and put in 16bit/word mode.
236266Sbill 	 */
237266Sbill 	if ((upaddr->upds & VV) == 0) {
238266Sbill 		upaddr->upcs1 = IE|DCLR|GO;
239264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
240264Sbill 		upaddr->upof = FMT22;
241268Sbill 		didie = 1;
242264Sbill 	}
243264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
244275Sbill 		goto done;
2452298Skre 
2462298Skre #if NUP > 1
247266Sbill 	/*
248266Sbill 	 * Do enough of the disk address decoding to determine
249266Sbill 	 * which cylinder and sector the request is on.
250266Sbill 	 * If we are on the correct cylinder and the desired sector
251308Sbill 	 * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
252266Sbill 	 * we don't bother to SEARCH but just begin the transfer asap.
253308Sbill 	 * Otherwise ask for a interrupt upSDIST sectors ahead.
254266Sbill 	 */
255264Sbill 	bn = dkblock(bp);
256264Sbill 	cn = bp->b_cylin;
257264Sbill 	sn = bn%(NSECT*NTRAC);
258308Sbill 	sn = (sn+NSECT-upSDIST)%NSECT;
259264Sbill 
260266Sbill 	if (cn - upaddr->updc)
261266Sbill 		goto search;		/* Not on-cylinder */
262275Sbill 	else if (upseek)
263275Sbill 		goto done;		/* Ok just to be on-cylinder */
264264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
265266Sbill 	if (csn < 0)
266264Sbill 		csn += NSECT;
267308Sbill 	if (csn > NSECT-upRDIST)
268264Sbill 		goto done;
269264Sbill 
270264Sbill search:
271264Sbill 	upaddr->updc = cn;
272275Sbill 	if (upseek)
273275Sbill 		upaddr->upcs1 = IE|SEEK|GO;
274275Sbill 	else {
275275Sbill 		upaddr->upda = sn;
276275Sbill 		upaddr->upcs1 = IE|SEARCH|GO;
277275Sbill 	}
278268Sbill 	didie = 1;
279266Sbill 	/*
280266Sbill 	 * Mark this unit busy.
281266Sbill 	 */
2821945Swnj 	unit += UPDK_N;
2831945Swnj 	if (unit <= UPDK_NMAX) {
284264Sbill 		dk_busy |= 1<<unit;
2851412Sbill 		dk_seek[unit]++;
286264Sbill 	}
287268Sbill 	goto out;
2882298Skre #endif
289264Sbill 
290264Sbill done:
291266Sbill 	/*
292275Sbill 	 * This unit is ready to go so
293275Sbill 	 * link it onto the chain of ready disks.
294266Sbill 	 */
295264Sbill 	dp->b_forw = NULL;
296266Sbill 	if (uptab.b_actf == NULL)
297264Sbill 		uptab.b_actf = dp;
298264Sbill 	else
299264Sbill 		uptab.b_actl->b_forw = dp;
300264Sbill 	uptab.b_actl = dp;
301268Sbill 
302268Sbill out:
303268Sbill 	return (didie);
304264Sbill }
305264Sbill 
306264Sbill /*
307264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
308264Sbill  */
309264Sbill upstart()
310264Sbill {
311264Sbill 	register struct buf *bp, *dp;
312264Sbill 	register unit;
313264Sbill 	register struct device *upaddr;
314264Sbill 	daddr_t bn;
315266Sbill 	int dn, sn, tn, cn, cmd;
316264Sbill 
317264Sbill loop:
318266Sbill 	/*
319266Sbill 	 * Pick a drive off the queue of ready drives, and
320266Sbill 	 * perform the first transfer on its queue.
321266Sbill 	 *
322266Sbill 	 * Looping here is completely for the sake of drives which
323266Sbill 	 * are not present and on-line, for which we completely clear the
324266Sbill 	 * request queue.
325266Sbill 	 */
326273Sbill 	if ((dp = uptab.b_actf) == NULL)
327268Sbill 		return (0);
328264Sbill 	if ((bp = dp->b_actf) == NULL) {
329264Sbill 		uptab.b_actf = dp->b_forw;
330264Sbill 		goto loop;
331264Sbill 	}
332266Sbill 	/*
333266Sbill 	 * Mark the controller busy, and multi-part disk address.
334266Sbill 	 * Select the unit on which the i/o is to take place.
335266Sbill 	 */
336264Sbill 	uptab.b_active++;
337264Sbill 	unit = minor(bp->b_dev) & 077;
338264Sbill 	dn = dkunit(bp);
339264Sbill 	bn = dkblock(bp);
340264Sbill 	cn = up_sizes[unit&07].cyloff;
341264Sbill 	cn += bn/(NSECT*NTRAC);
342264Sbill 	sn = bn%(NSECT*NTRAC);
343264Sbill 	tn = sn/NSECT;
344266Sbill 	sn %= NSECT;
345264Sbill 	upaddr = UPADDR;
3461756Sbill 	if ((upaddr->upcs2 & 07) != dn)
347264Sbill 		upaddr->upcs2 = dn;
3481756Sbill 	up_ubinfo = ubasetup(bp, 1);
349266Sbill 	/*
350266Sbill 	 * If drive is not present and on-line, then
351266Sbill 	 * get rid of this with an error and loop to get
352266Sbill 	 * rid of the rest of its queued requests.
353266Sbill 	 * (Then on to any other ready drives.)
354266Sbill 	 */
355264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
356893Sbill 		printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds);
357893Sbill 		if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
358893Sbill 			printf("-- hard\n");
359893Sbill 			uptab.b_active = 0;
360893Sbill 			uptab.b_errcnt = 0;
361893Sbill 			dp->b_actf = bp->av_forw;
362893Sbill 			dp->b_active = 0;
363893Sbill 			bp->b_flags |= B_ERROR;
364893Sbill 			iodone(bp);
365893Sbill 			/* A funny place to do this ... */
3662053Swnj 			ubarelse(&up_ubinfo);
367893Sbill 			goto loop;
368893Sbill 		}
369893Sbill 		printf("-- came back\n");
370264Sbill 	}
371266Sbill 	/*
372266Sbill 	 * If this is a retry, then with the 16'th retry we
373266Sbill 	 * begin to try offsetting the heads to recover the data.
374266Sbill 	 */
3752298Skre 	if (uptab.b_errcnt >= 16 && (bp->b_flags&B_READ) != 0) {
376264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
377266Sbill 		upaddr->upcs1 = IE|OFFSET|GO;
378266Sbill 		while (upaddr->upds & PIP)
379264Sbill 			DELAY(25);
380264Sbill 	}
381266Sbill 	/*
382266Sbill 	 * Now set up the transfer, retrieving the high
383266Sbill 	 * 2 bits of the UNIBUS address from the information
384266Sbill 	 * returned by ubasetup() for the cs1 register bits 8 and 9.
385266Sbill 	 */
386264Sbill 	upaddr->updc = cn;
387264Sbill 	upaddr->upda = (tn << 8) + sn;
388264Sbill 	upaddr->upba = up_ubinfo;
389264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
390266Sbill 	cmd = (up_ubinfo >> 8) & 0x300;
391264Sbill 	if (bp->b_flags & B_READ)
392266Sbill 		cmd |= IE|RCOM|GO;
393264Sbill 	else
394266Sbill 		cmd |= IE|WCOM|GO;
395266Sbill 	upaddr->upcs1 = cmd;
396266Sbill 	/*
397266Sbill 	 * This is a controller busy situation.
3981945Swnj 	 * Record in dk slot NUP+UPDK_N (after last drive)
399266Sbill 	 * unless there aren't that many slots reserved for
400266Sbill 	 * us in which case we record this as a drive busy
401266Sbill 	 * (if there is room for that).
402266Sbill 	 */
4031945Swnj 	unit = dn+UPDK_N;
4041945Swnj 	if (unit <= UPDK_NMAX) {
405264Sbill 		dk_busy |= 1<<unit;
4061412Sbill 		dk_xfer[unit]++;
407264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
408264Sbill 	}
409268Sbill 	return (1);
410264Sbill }
411264Sbill 
412264Sbill /*
413264Sbill  * Handle a device interrupt.
414264Sbill  *
415264Sbill  * If the transferring drive needs attention, service it
416264Sbill  * retrying on error or beginning next transfer.
417264Sbill  * Service all other ready drives, calling ustart to transfer
418264Sbill  * their blocks to the ready queue in uptab, and then restart
419264Sbill  * the controller if there is anything to do.
420264Sbill  */
421264Sbill upintr()
422264Sbill {
423264Sbill 	register struct buf *bp, *dp;
424264Sbill 	register unit;
425264Sbill 	register struct device *upaddr = UPADDR;
426264Sbill 	int as = upaddr->upas & 0377;
427341Sbill 	int oupsoftas;
428268Sbill 	int needie = 1;
429264Sbill 
430276Sbill 	(void) spl6();
431313Sbill 	up_wticks = 0;
432266Sbill 	if (uptab.b_active) {
433266Sbill 		/*
434266Sbill 		 * The drive is transferring, thus the hardware
435266Sbill 		 * (say the designers) will only interrupt when the transfer
436266Sbill 		 * completes; check for it anyways.
437266Sbill 		 */
438266Sbill 		if ((upaddr->upcs1 & RDY) == 0) {
439272Sbill 			printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
440272Sbill 			    upaddr->upds, upaddr->upwc);
441341Sbill 			printf("as=%d act %d %d %d\n", as, uptab.b_active,
442341Sbill 			    uputab[0].b_active, uputab[1].b_active);
443269Sbill 		}
444266Sbill 		/*
4451412Sbill 		 * Mark drive not busy, and check for an
446266Sbill 		 * error condition which may have resulted from the transfer.
447266Sbill 		 */
448264Sbill 		dp = uptab.b_actf;
449264Sbill 		bp = dp->b_actf;
450264Sbill 		unit = dkunit(bp);
4511945Swnj 		if (UPDK_N+unit <= UPDK_NMAX)
4521945Swnj 			dk_busy &= ~(1<<(UPDK_N+unit));
4531756Sbill 		if ((upaddr->upcs2 & 07) != unit)
454275Sbill 			upaddr->upcs2 = unit;
455885Sbill 		if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) {
4561829Sbill 			int cs2;
457266Sbill 			/*
458266Sbill 			 * An error occurred, indeed.  Select this unit
459266Sbill 			 * to get at the drive status (a SEARCH may have
460266Sbill 			 * intervened to change the selected unit), and
461266Sbill 			 * wait for the command which caused the interrupt
462266Sbill 			 * to complete (DRY).
463266Sbill 			 */
464266Sbill 			while ((upaddr->upds & DRY) == 0)
465264Sbill 				DELAY(25);
466266Sbill 			/*
467266Sbill 			 * After 28 retries (16 w/o servo offsets, and then
468266Sbill 			 * 12 with servo offsets), or if we encountered
469266Sbill 			 * an error because the drive is write-protected,
470266Sbill 			 * give up.  Print an error message on the last 2
471266Sbill 			 * retries before a hard failure.
472266Sbill 			 */
473266Sbill 			if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
474264Sbill 				bp->b_flags |= B_ERROR;
475264Sbill 			else
476266Sbill 				uptab.b_active = 0;	/* To force retry */
477266Sbill 			if (uptab.b_errcnt > 27)
4781829Sbill 				cs2 = (int)upaddr->upcs2;
4791783Sbill 				deverror(bp, (int)upaddr->upcs2,
4801783Sbill 				    (int)upaddr->uper1);
481266Sbill 			/*
482266Sbill 			 * If this was a correctible ECC error, let upecc
483266Sbill 			 * do the dirty work to correct it.  If upecc
484266Sbill 			 * starts another READ for the rest of the data
485266Sbill 			 * then it returns 1 (having set uptab.b_active).
486266Sbill 			 * Otherwise we are done and fall through to
487266Sbill 			 * finish up.
488266Sbill 			 */
489266Sbill 			if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
490266Sbill 				return;
491266Sbill 			/*
492266Sbill 			 * Clear the drive and, every 4 retries, recalibrate
493266Sbill 			 * to hopefully help clear up seek positioning problems.
494266Sbill 			 */
495264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
496268Sbill 			needie = 0;
497266Sbill 			if ((uptab.b_errcnt&07) == 4) {
498264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
499264Sbill 				while(upaddr->upds & PIP)
500264Sbill 					DELAY(25);
501264Sbill 			}
5021829Sbill 			if (uptab.b_errcnt == 28 && cs2&(NEM|MXF)) {
5031829Sbill 				printf("FLAKEY UP ");
5041829Sbill 				ubareset();
5051829Sbill 				return;
5061829Sbill 			}
507264Sbill 		}
508266Sbill 		/*
509266Sbill 		 * If we are still noted as active, then no
510266Sbill 		 * (further) retries are necessary.
511266Sbill 		 *
512266Sbill 		 * Make sure the correct unit is selected,
513266Sbill 		 * return it to centerline if necessary, and mark
514266Sbill 		 * this i/o complete, starting the next transfer
515266Sbill 		 * on this drive with the upustart routine (if any).
516266Sbill 		 */
517266Sbill 		if (uptab.b_active) {
518266Sbill 			if (uptab.b_errcnt >= 16) {
519266Sbill 				upaddr->upcs1 = RTC|GO|IE;
520266Sbill 				while (upaddr->upds & PIP)
521264Sbill 					DELAY(25);
522268Sbill 				needie = 0;
523264Sbill 			}
524264Sbill 			uptab.b_active = 0;
525264Sbill 			uptab.b_errcnt = 0;
526264Sbill 			uptab.b_actf = dp->b_forw;
527264Sbill 			dp->b_active = 0;
528264Sbill 			dp->b_errcnt = 0;
529264Sbill 			dp->b_actf = bp->av_forw;
530266Sbill 			bp->b_resid = (-upaddr->upwc * sizeof(short));
531275Sbill 			if (bp->b_resid)
532341Sbill 				printf("resid %d ds %o er? %o %o %o\n",
533341Sbill 				    bp->b_resid, upaddr->upds,
534275Sbill 				    upaddr->uper1, upaddr->uper2, upaddr->uper3);
535264Sbill 			iodone(bp);
536264Sbill 			if(dp->b_actf)
537268Sbill 				if (upustart(unit))
538268Sbill 					needie = 0;
539264Sbill 		}
540264Sbill 		as &= ~(1<<unit);
541341Sbill 		upsoftas &= ~(1<<unit);
5422053Swnj 		ubarelse(&up_ubinfo);
543273Sbill 	} else {
5441756Sbill 		if (upaddr->upcs1 & TRE)
545264Sbill 			upaddr->upcs1 = TRE;
546264Sbill 	}
547266Sbill 	/*
548266Sbill 	 * If we have a unit with an outstanding SEARCH,
549266Sbill 	 * and the hardware indicates the unit requires attention,
550266Sbill 	 * the bring the drive to the ready queue.
551266Sbill 	 * Finally, if the controller is not transferring
552266Sbill 	 * start it if any drives are now ready to transfer.
553266Sbill 	 */
554341Sbill 	as |= upsoftas;
555341Sbill 	oupsoftas = upsoftas;
556341Sbill 	upsoftas = 0;
557266Sbill 	for (unit = 0; unit < NUP; unit++)
558341Sbill 		if ((as|oupsoftas) & (1<<unit)) {
5591756Sbill 			if (as & (1<<unit))
560267Sbill 				upaddr->upas = 1<<unit;
561273Sbill 			if (upustart(unit))
562273Sbill 				needie = 0;
563273Sbill 		}
564266Sbill 	if (uptab.b_actf && uptab.b_active == 0)
565268Sbill 		if (upstart())
566268Sbill 			needie = 0;
567275Sbill 	if (needie)
568266Sbill 		upaddr->upcs1 = IE;
569264Sbill }
570264Sbill 
571264Sbill upread(dev)
572264Sbill {
573264Sbill 
574264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
575264Sbill }
576264Sbill 
577264Sbill upwrite(dev)
578264Sbill {
579264Sbill 
580264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
581264Sbill }
582264Sbill 
583266Sbill /*
584266Sbill  * Correct an ECC error, and restart the i/o to complete
585266Sbill  * the transfer if necessary.  This is quite complicated because
586266Sbill  * the transfer may be going to an odd memory address base and/or
587266Sbill  * across a page boundary.
588266Sbill  */
589264Sbill upecc(up, bp)
590264Sbill register struct device *up;
591264Sbill register struct buf *bp;
592264Sbill {
593264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
594266Sbill 	register int i;
595264Sbill 	caddr_t addr;
596266Sbill 	int reg, bit, byte, npf, mask, o, cmd, ubaddr;
597264Sbill 	int bn, cn, tn, sn;
598264Sbill 
599264Sbill 	/*
600266Sbill 	 * Npf is the number of sectors transferred before the sector
601266Sbill 	 * containing the ECC error, and reg is the UBA register
602266Sbill 	 * mapping (the first part of) the transfer.
603266Sbill 	 * O is offset within a memory page of the first byte transferred.
604264Sbill 	 */
605266Sbill 	npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
606266Sbill 	reg = btop(up_ubinfo&0x3ffff) + npf;
607264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
608264Sbill 	printf("%D ", bp->b_blkno+npf);
609264Sbill 	prdev("ECC", bp->b_dev);
610264Sbill 	mask = up->upec2;
611264Sbill 	if (mask == 0) {
612266Sbill 		up->upof = FMT22;		/* == RTC ???? */
613264Sbill 		return (0);
614264Sbill 	}
615266Sbill 	/*
616266Sbill 	 * Flush the buffered data path, and compute the
617266Sbill 	 * byte and bit position of the error.  The variable i
618266Sbill 	 * is the byte offset in the transfer, the variable byte
619266Sbill 	 * is the offset from a page boundary in main memory.
620266Sbill 	 */
621266Sbill 	ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
622266Sbill 	i = up->upec1 - 1;		/* -1 makes 0 origin */
623266Sbill 	bit = i&07;
624266Sbill 	i = (i&~07)>>3;
625264Sbill 	byte = i + o;
626266Sbill 	/*
627266Sbill 	 * Correct while possible bits remain of mask.  Since mask
628266Sbill 	 * contains 11 bits, we continue while the bit offset is > -11.
629266Sbill 	 * Also watch out for end of this block and the end of the whole
630266Sbill 	 * transfer.
631266Sbill 	 */
632266Sbill 	while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
633266Sbill 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
634266Sbill 		    (byte & PGOFSET);
635266Sbill 		putmemc(addr, getmemc(addr)^(mask<<bit));
636266Sbill 		byte++;
637266Sbill 		i++;
638266Sbill 		bit -= 8;
639264Sbill 	}
640266Sbill 	uptab.b_active++;	/* Either complete or continuing... */
641264Sbill 	if (up->upwc == 0)
642264Sbill 		return (0);
643266Sbill 	/*
644266Sbill 	 * Have to continue the transfer... clear the drive,
645266Sbill 	 * and compute the position where the transfer is to continue.
646266Sbill 	 * We have completed npf+1 sectors of the transfer already;
647266Sbill 	 * restart at offset o of next sector (i.e. in UBA register reg+1).
648266Sbill 	 */
649266Sbill 	up->upcs1 = TRE|IE|DCLR|GO;
650264Sbill 	bn = dkblock(bp);
651264Sbill 	cn = bp->b_cylin;
652266Sbill 	sn = bn%(NSECT*NTRAC) + npf + 1;
653264Sbill 	tn = sn/NSECT;
654264Sbill 	sn %= NSECT;
655266Sbill 	cn += tn/NTRAC;
656266Sbill 	tn %= NTRAC;
657264Sbill 	up->updc = cn;
658266Sbill 	up->upda = (tn << 8) | sn;
659266Sbill 	ubaddr = (int)ptob(reg+1) + o;
660266Sbill 	up->upba = ubaddr;
661266Sbill 	cmd = (ubaddr >> 8) & 0x300;
662266Sbill 	cmd |= IE|GO|RCOM;
663266Sbill 	up->upcs1 = cmd;
664264Sbill 	return (1);
665264Sbill }
666286Sbill 
667286Sbill /*
668286Sbill  * Reset driver after UBA init.
669286Sbill  * Cancel software state of all pending transfers
670286Sbill  * and restart all units and the controller.
671286Sbill  */
672286Sbill upreset()
673286Sbill {
674286Sbill 	int unit;
675286Sbill 
676286Sbill 	printf(" up");
6771829Sbill 	DELAY(15000000);		/* give it time to self-test */
678286Sbill 	uptab.b_active = 0;
679286Sbill 	uptab.b_actf = uptab.b_actl = 0;
680286Sbill 	if (up_ubinfo) {
681286Sbill 		printf("<%d>", (up_ubinfo>>28)&0xf);
6822053Swnj 		ubarelse(&up_ubinfo);
683286Sbill 	}
684313Sbill 	UPADDR->upcs2 = CLR;		/* clear controller */
685286Sbill 	for (unit = 0; unit < NUP; unit++) {
686286Sbill 		uputab[unit].b_active = 0;
687286Sbill 		(void) upustart(unit);
688286Sbill 	}
689286Sbill 	(void) upstart();
690286Sbill }
691313Sbill 
692313Sbill /*
693313Sbill  * Wake up every second and if an interrupt is pending
694313Sbill  * but nothing has happened increment a counter.
695313Sbill  * If nothing happens for 20 seconds, reset the controller
696313Sbill  * and begin anew.
697313Sbill  */
698313Sbill upwatch()
699313Sbill {
700313Sbill 	int i;
701313Sbill 
7021783Sbill 	timeout(upwatch, (caddr_t)0, HZ);
703313Sbill 	if (uptab.b_active == 0) {
704313Sbill 		for (i = 0; i < NUP; i++)
705313Sbill 			if (uputab[i].b_active)
706313Sbill 				goto active;
707313Sbill 		up_wticks = 0;		/* idling */
708313Sbill 		return;
709313Sbill 	}
710313Sbill active:
711313Sbill 	up_wticks++;
712313Sbill 	if (up_wticks >= 20) {
713313Sbill 		up_wticks = 0;
714313Sbill 		printf("LOST INTERRUPT RESET");
715313Sbill 		upreset();
716313Sbill 		printf("\n");
717313Sbill 	}
718313Sbill }
719*2379Swnj 
720*2379Swnj #define	DBSIZE	20
721*2379Swnj 
722*2379Swnj updump(dev)
723*2379Swnj 	dev_t dev;
724*2379Swnj {
725*2379Swnj 	struct device *upaddr;
726*2379Swnj 	char *start;
727*2379Swnj 	int num, blk, unit, nsect, ntrak, nspc;
728*2379Swnj 	struct size *sizes;
729*2379Swnj #if VAX==780
730*2379Swnj 	register struct uba_regs *up = (struct uba_regs *)PHYSUBA0;
731*2379Swnj 	register short *rp;
732*2379Swnj 	int bdp;
733*2379Swnj 
734*2379Swnj 	up->uba_cr = ADINIT;
735*2379Swnj 	up->uba_cr = IFS|BRIE|USEFIE|SUEFIE;
736*2379Swnj 	while ((up->uba_cnfgr & UBIC) == 0)
737*2379Swnj 		;
7381809Sbill #endif
739*2379Swnj 	DELAY(1000000);
740*2379Swnj 	while ((UPADDR->upcs1&DVA) == 0)
741*2379Swnj 		;
742*2379Swnj 	num = maxfree;
743*2379Swnj 	start = 0;
744*2379Swnj 	unit = minor(dev) >> 3;
745*2379Swnj 	if (unit >= NUP) {
746*2379Swnj 		printf("bad unit\n");
747*2379Swnj 		return (-1);
748*2379Swnj 	}
749*2379Swnj 	upaddr = UPPHYS;
750*2379Swnj 	upaddr->upcs2 = unit;
751*2379Swnj 	if ((upaddr->upds & VV) == 0) {
752*2379Swnj 		upaddr->upcs1 = DCLR|GO;
753*2379Swnj 		upaddr->upcs1 = PRESET|GO;
754*2379Swnj 		upaddr->upof = FMT22;
755*2379Swnj 	}
756*2379Swnj 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
757*2379Swnj 		printf("up !DPR || !MOL\n");
758*2379Swnj 		return (-1);
759*2379Swnj 	}
760*2379Swnj 	nsect = NSECT; ntrak = NTRAC; sizes = up_sizes;
761*2379Swnj 	if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) {
762*2379Swnj 		printf("dumplo+num, sizes %d %d\n", dumplo+num, sizes[minor(dev)&07].nblocks);
763*2379Swnj 		return (-1);
764*2379Swnj 	}
765*2379Swnj 	nspc = nsect * ntrak;
766*2379Swnj 	while (num > 0) {
767*2379Swnj 		register struct pte *io;
768*2379Swnj 		register int i;
769*2379Swnj 		int cn, sn, tn;
770*2379Swnj 		daddr_t bn;
771*2379Swnj 
772*2379Swnj 		blk = num > DBSIZE ? DBSIZE : num;
773*2379Swnj 		bdp = 1;		/* trick pcc */
774*2379Swnj 		((struct uba_regs *)PHYSUBA0)->uba_dpr[bdp] |= BNE;
775*2379Swnj 		io = ((struct uba_regs *)PHYSUBA0)->uba_map;
776*2379Swnj 		for (i = 0; i < blk; i++)
777*2379Swnj 			*(int *)io++ = (btop(start)+i) | (1<<21) | MRV;
778*2379Swnj 		*(int *)io = 0;
779*2379Swnj 		bn = dumplo + btop(start);
780*2379Swnj 		cn = bn/nspc + sizes[minor(dev)&07].cyloff;
781*2379Swnj 		sn = bn%nspc;
782*2379Swnj 		tn = sn/nsect;
783*2379Swnj 		sn = sn%nsect;
784*2379Swnj 		upaddr->updc = cn;
785*2379Swnj 		rp = (short *) &upaddr->upda;
786*2379Swnj 		*rp = (tn << 8) + sn;
787*2379Swnj 		*--rp = 0;
788*2379Swnj 		*--rp = -blk*NBPG / sizeof (short);
789*2379Swnj 		*--rp = GO|WCOM;
790*2379Swnj 		do {
791*2379Swnj 			DELAY(25);
792*2379Swnj 		} while ((upaddr->upcs1 & RDY) == 0);
793*2379Swnj 		if (upaddr->upcs1&ERR) {
794*2379Swnj 			printf("up dump dsk err: (%d,%d,%d) cs1=%x, er1=%x\n",
795*2379Swnj 			    cn, tn, sn, upaddr->upcs1, upaddr->uper1);
796*2379Swnj 			return (-1);
797*2379Swnj 		}
798*2379Swnj 		start += blk*NBPG;
799*2379Swnj 		num -= blk;
800*2379Swnj 	}
801*2379Swnj 	bdp = 1;		/* crud to fool c compiler */
802*2379Swnj 	((struct uba_regs *)PHYSUBA0)->uba_dpr[bdp] |= BNE;
803*2379Swnj 	return (0);
804*2379Swnj }
8051902Swnj #endif
806