xref: /csrg-svn/sys/vax/uba/up.c (revision 1937)
1*1937Swnj /*	up.c	4.7	12/19/80	*/
2264Sbill 
3*1937Swnj #include "up.h"
41809Sbill #if NUP > 0
51933Swnj #if SC11 > 0
61933Swnj #include "../dev/up.c.SC11"
71902Swnj #else
8264Sbill /*
9885Sbill  * UNIBUS disk driver with overlapped seeks and ECC recovery.
10264Sbill  */
111756Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
12264Sbill 
13264Sbill #include "../h/param.h"
14264Sbill #include "../h/systm.h"
15308Sbill #include "../h/dk.h"
16264Sbill #include "../h/buf.h"
17264Sbill #include "../h/conf.h"
18264Sbill #include "../h/dir.h"
19264Sbill #include "../h/user.h"
20264Sbill #include "../h/map.h"
21420Sbill #include "../h/pte.h"
22264Sbill #include "../h/mba.h"
23264Sbill #include "../h/mtpr.h"
24264Sbill #include "../h/uba.h"
25264Sbill #include "../h/vm.h"
26264Sbill 
27264Sbill #define	ushort	unsigned short
28264Sbill 
29264Sbill struct	device
30264Sbill {
31264Sbill 	ushort	upcs1;		/* control and status register 1 */
32264Sbill 	short	upwc;		/* word count register */
33264Sbill 	ushort	upba;		/* UNIBUS address register */
34264Sbill 	ushort	upda;		/* desired address register */
35264Sbill 	ushort	upcs2;		/* control and status register 2 */
36264Sbill 	ushort	upds;		/* drive Status */
37264Sbill 	ushort	uper1;		/* error register 1 */
38264Sbill 	ushort	upas;		/* attention summary */
39264Sbill 	ushort	upla;		/* look ahead */
40264Sbill 	ushort	updb;		/* data buffer */
41264Sbill 	ushort	upmr;		/* maintenance */
42264Sbill 	ushort	updt;		/* drive type */
43264Sbill 	ushort	upsn;		/* serial number */
44264Sbill 	ushort	upof;		/* offset register */
45264Sbill 	ushort	updc;		/* desired cylinder address register */
46264Sbill 	ushort	upcc;		/* current cylinder */
47264Sbill 	ushort	uper2;		/* error register 2 */
48264Sbill 	ushort	uper3;		/* error register 3 */
49264Sbill 	ushort	upec1;		/* burst error bit position */
50264Sbill 	ushort	upec2;		/* burst error bit pattern */
51264Sbill };
52264Sbill 
53275Sbill /*
54275Sbill  * Software extension to the upas register, so we can
55275Sbill  * postpone starting SEARCH commands until the controller
56275Sbill  * is not transferring.
57275Sbill  */
58341Sbill int	upsoftas;
59275Sbill 
60275Sbill /*
61275Sbill  * If upseek then we don't issue SEARCH commands but rather just
62275Sbill  * settle for a SEEK to the correct cylinder.
63275Sbill  */
64275Sbill int	upseek;
65275Sbill 
66264Sbill #define	NSECT	32
67264Sbill #define	NTRAC	19
68264Sbill 
69264Sbill /*
70264Sbill  * Constants controlling on-cylinder SEARCH usage.
71264Sbill  *
72308Sbill  * 	upSDIST/2 msec		time needed to start transfer
73308Sbill  * 	upRDIST/2 msec		tolerable rotational latency when on-cylinder
74275Sbill  *
75308Sbill  * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
76275Sbill  * and in the driver then we take it as it is.  Otherwise we do a SEARCH
77308Sbill  * requesting an interrupt upSDIST sectors in advance.
78264Sbill  */
791592Sbill #define	_upSDIST	2		/* 1.0 msec */
801592Sbill #define	_upRDIST	4		/* 2.0 msec */
81264Sbill 
82308Sbill int	upSDIST = _upSDIST;
83308Sbill int	upRDIST = _upRDIST;
84275Sbill 
85264Sbill /*
86264Sbill  * To fill a 300M drive:
87264Sbill  *	A is designed to be used as a root.
88264Sbill  *	B is suitable for a swap area.
89264Sbill  *	H is the primary storage area.
90264Sbill  * On systems with RP06'es, we normally use only 291346 blocks of the H
91264Sbill  * area, and use DEF or G to cover the rest of the drive.  The C system
92264Sbill  * covers the whole drive and can be used for pack-pack copying.
931756Sbill  *
941756Sbill  * Note: sizes here are for AMPEX drives with 815 cylinders.
951756Sbill  * CDC drives can make the F,G, and H areas larger as they have 823 cylinders.
96264Sbill  */
97264Sbill struct	size
98264Sbill {
99264Sbill 	daddr_t	nblocks;
100264Sbill 	int	cyloff;
101264Sbill } up_sizes[8] = {
102264Sbill 	15884,	0,		/* A=cyl 0 thru 26 */
103264Sbill 	33440,	27,		/* B=cyl 27 thru 81 */
104341Sbill 	495520,	0,		/* C=cyl 0 thru 814 */
105264Sbill 	15884,	562,		/* D=cyl 562 thru 588 */
106264Sbill 	55936,	589,		/* E=cyl 589 thru 680 */
107264Sbill 	81472,	681,		/* F=cyl 681 thru 814 */
108264Sbill 	153824,	562,		/* G=cyl 562 thru 814 */
109264Sbill 	291346,	82,		/* H=cyl 82 thru 561 */
110264Sbill };
111264Sbill 
112264Sbill /*
113264Sbill  * The following defines are used in offset positioning
114264Sbill  * when trying to recover disk errors, with the constants being
115264Sbill  * +/- microinches.  Note that header compare inhibit (HCI) is not
116264Sbill  * tried (this makes sense only during read, in any case.)
117264Sbill  *
1181756Sbill  * NB: Not all drives/controllers emulate all of these.
119264Sbill  */
120264Sbill #define	P400	020
121264Sbill #define	M400	0220
122264Sbill #define	P800	040
123264Sbill #define	M800	0240
124264Sbill #define	P1200	060
125264Sbill #define	M1200	0260
126264Sbill #define	HCI	020000
127264Sbill 
128264Sbill int	up_offset[16] =
129264Sbill {
130264Sbill 	P400, M400, P400, M400,
131264Sbill 	P800, M800, P800, M800,
132264Sbill 	P1200, M1200, P1200, M1200,
133264Sbill 	0, 0, 0, 0,
134264Sbill };
135264Sbill 
136264Sbill /*
137264Sbill  * Each drive has a table uputab[i].  On this table are sorted the
138264Sbill  * pending requests implementing an elevator algorithm (see dsort.c.)
139264Sbill  * In the upustart() routine, each drive is independently advanced
140264Sbill  * until it is on the desired cylinder for the next transfer and near
141264Sbill  * the desired sector.  The drive is then chained onto the uptab
142264Sbill  * table, and the transfer is initiated by the upstart() routine.
143264Sbill  * When the transfer is completed the driver reinvokes the upustart()
144264Sbill  * routine to set up the next transfer.
145264Sbill  */
146264Sbill struct	buf	uptab;
147264Sbill struct	buf	uputab[NUP];
148264Sbill 
149264Sbill struct	buf	rupbuf;			/* Buffer for raw i/o */
150264Sbill 
151264Sbill /* Drive commands, placed in upcs1 */
152264Sbill #define	GO	01		/* Go bit, set in all commands */
153264Sbill #define	PRESET	020		/* Preset drive at init or after errors */
154264Sbill #define	OFFSET	014		/* Offset heads to try to recover error */
155264Sbill #define	RTC	016		/* Return to center-line after OFFSET */
156264Sbill #define	SEARCH	030		/* Search for cylinder+sector */
157275Sbill #define	SEEK	04		/* Seek to cylinder */
158264Sbill #define	RECAL	06		/* Recalibrate, needed after seek error */
159264Sbill #define	DCLR	010		/* Drive clear, after error */
160264Sbill #define	WCOM	060		/* Write */
161264Sbill #define	RCOM	070		/* Read */
162264Sbill 
163264Sbill /* Other bits of upcs1 */
164264Sbill #define	IE	0100		/* Controller wide interrupt enable */
165264Sbill #define	TRE	040000		/* Transfer error */
166345Sbill #define	RDY	0200		/* Transfer terminated */
167264Sbill 
168264Sbill /* Drive status bits of upds */
169264Sbill #define	PIP	020000		/* Positioning in progress */
170264Sbill #define	ERR	040000		/* Error has occurred, DCLR necessary */
171264Sbill #define	VV	0100		/* Volume is valid, set by PRESET */
172264Sbill #define	DPR	0400		/* Drive has been preset */
173264Sbill #define	MOL	010000		/* Drive is online, heads loaded, etc */
174264Sbill #define	DRY	0200		/* Drive ready */
175264Sbill 
176313Sbill /* Bits of upcs2 */
177313Sbill #define	CLR	040		/* Controller clear */
1781829Sbill #define	MXF	01000
1791829Sbill #define	NEM	04000
1801829Sbill 
181264Sbill /* Bits of uper1 */
182264Sbill #define	DCK	0100000		/* Ecc error occurred */
183264Sbill #define	ECH	0100		/* Ecc error was unrecoverable */
184264Sbill #define	WLE	04000		/* Attempt to write read-only drive */
185264Sbill 
186264Sbill /* Bits of upof; the offset bits above are also in this register */
187264Sbill #define	FMT22	010000		/* 16 bits/word, must be always set */
188264Sbill 
189264Sbill #define	b_cylin b_resid
190264Sbill 
191264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
192264Sbill 
193313Sbill int	up_wticks;		/* Ticks waiting for interrupt */
194313Sbill int	upwstart;		/* Have started guardian */
195313Sbill int	upwatch();
196313Sbill 
197264Sbill #ifdef INTRLVE
198264Sbill daddr_t dkblock();
199264Sbill #endif
200264Sbill 
201264Sbill /*
202264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
203264Sbill  *
204264Sbill  * A unit start is issued if the drive is inactive, causing
205264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
206264Sbill  * already nearly on the money and the controller is not transferring
207264Sbill  * we kick it to start the transfer.
208264Sbill  */
209264Sbill upstrategy(bp)
210264Sbill register struct buf *bp;
211264Sbill {
212264Sbill 	register struct buf *dp;
213264Sbill 	register unit, xunit;
214264Sbill 	long sz, bn;
215264Sbill 
216313Sbill 	if (upwstart == 0) {
2171783Sbill 		timeout(upwatch, (caddr_t)0, HZ);
218313Sbill 		upwstart++;
219313Sbill 	}
220264Sbill 	xunit = minor(bp->b_dev) & 077;
221264Sbill 	sz = bp->b_bcount;
222264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
223264Sbill 	unit = dkunit(bp);
224264Sbill 	if (unit >= NUP ||
225264Sbill 	    bp->b_blkno < 0 ||
226264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
227264Sbill 		bp->b_flags |= B_ERROR;
228264Sbill 		iodone(bp);
229264Sbill 		return;
230264Sbill 	}
2311412Sbill 	if (DK_N+unit <= DK_NMAX)
2321412Sbill 		dk_mspw[DK_N+unit] = .0000020345;
233264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
234264Sbill 	dp = &uputab[unit];
235264Sbill 	(void) spl5();
236264Sbill 	disksort(dp, bp);
237264Sbill 	if (dp->b_active == 0) {
238268Sbill 		(void) upustart(unit);
239264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
240268Sbill 			(void) upstart();
241264Sbill 	}
242264Sbill 	(void) spl0();
243264Sbill }
244264Sbill 
245264Sbill /*
246264Sbill  * Start activity on specified drive; called when drive is inactive
247264Sbill  * and new transfer request arrives and also when upas indicates that
248264Sbill  * a SEARCH command is complete.
249264Sbill  */
250264Sbill upustart(unit)
251264Sbill register unit;
252264Sbill {
253264Sbill 	register struct buf *bp, *dp;
254264Sbill 	register struct device *upaddr = UPADDR;
255264Sbill 	daddr_t bn;
256264Sbill 	int sn, cn, csn;
257268Sbill 	int didie = 0;
258264Sbill 
259275Sbill 	/*
260275Sbill 	 * Other drivers tend to say something like
261275Sbill 	 *	upaddr->upcs1 = IE;
262275Sbill 	 *	upaddr->upas = 1<<unit;
2631756Sbill 	 * here, but some controllers will cancel a command
264275Sbill 	 * happens to be sitting in the cs1 if you clear the go
2651756Sbill 	 * bit by storing there (so the first is not safe).
266275Sbill 	 *
267275Sbill 	 * Thus we keep careful track of when we re-enable IE
268275Sbill 	 * after an interrupt and do it only if we didn't issue
269275Sbill 	 * a command which re-enabled it as a matter of course.
270275Sbill 	 * We clear bits in upas in the interrupt routine, when
271275Sbill 	 * no transfers are active.
272275Sbill 	 */
273266Sbill 	if (unit >= NUP)
274268Sbill 		goto out;
275264Sbill 	if (unit+DK_N <= DK_NMAX)
276264Sbill 		dk_busy &= ~(1<<(unit+DK_N));
277264Sbill 	dp = &uputab[unit];
278266Sbill 	if ((bp = dp->b_actf) == NULL)
279268Sbill 		goto out;
280275Sbill 	/*
2811756Sbill 	 * Most controllers don't start SEARCH commands when transfers are
2821756Sbill 	 * in progress.  In fact, some tend to get confused when given
283275Sbill 	 * SEARCH'es during transfers, generating interrupts with neither
284275Sbill 	 * RDY nor a bit in the upas register.  Thus we defer
285275Sbill 	 * until an interrupt when a transfer is pending.
286275Sbill 	 */
287275Sbill 	if (uptab.b_active) {
288341Sbill 		upsoftas |= 1<<unit;
289275Sbill 		return (0);
290275Sbill 	}
291276Sbill 	if (dp->b_active)
292276Sbill 		goto done;
293276Sbill 	dp->b_active = 1;
2941756Sbill 	if ((upaddr->upcs2 & 07) != unit)
295264Sbill 		upaddr->upcs2 = unit;
296266Sbill 	/*
297266Sbill 	 * If we have changed packs or just initialized,
298275Sbill 	 * then the volume will not be valid; if so, clear
299266Sbill 	 * the drive, preset it and put in 16bit/word mode.
300266Sbill 	 */
301266Sbill 	if ((upaddr->upds & VV) == 0) {
302266Sbill 		upaddr->upcs1 = IE|DCLR|GO;
303264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
304264Sbill 		upaddr->upof = FMT22;
305268Sbill 		didie = 1;
306264Sbill 	}
307264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
308275Sbill 		goto done;
309266Sbill 	/*
310266Sbill 	 * Do enough of the disk address decoding to determine
311266Sbill 	 * which cylinder and sector the request is on.
312266Sbill 	 * If we are on the correct cylinder and the desired sector
313308Sbill 	 * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
314266Sbill 	 * we don't bother to SEARCH but just begin the transfer asap.
315308Sbill 	 * Otherwise ask for a interrupt upSDIST sectors ahead.
316266Sbill 	 */
317264Sbill 	bn = dkblock(bp);
318264Sbill 	cn = bp->b_cylin;
319264Sbill 	sn = bn%(NSECT*NTRAC);
320308Sbill 	sn = (sn+NSECT-upSDIST)%NSECT;
321264Sbill 
322266Sbill 	if (cn - upaddr->updc)
323266Sbill 		goto search;		/* Not on-cylinder */
324275Sbill 	else if (upseek)
325275Sbill 		goto done;		/* Ok just to be on-cylinder */
326264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
327266Sbill 	if (csn < 0)
328264Sbill 		csn += NSECT;
329308Sbill 	if (csn > NSECT-upRDIST)
330264Sbill 		goto done;
331264Sbill 
332264Sbill search:
333264Sbill 	upaddr->updc = cn;
334275Sbill 	if (upseek)
335275Sbill 		upaddr->upcs1 = IE|SEEK|GO;
336275Sbill 	else {
337275Sbill 		upaddr->upda = sn;
338275Sbill 		upaddr->upcs1 = IE|SEARCH|GO;
339275Sbill 	}
340268Sbill 	didie = 1;
341266Sbill 	/*
342266Sbill 	 * Mark this unit busy.
343266Sbill 	 */
344264Sbill 	unit += DK_N;
3451412Sbill 	if (unit <= DK_NMAX) {
346264Sbill 		dk_busy |= 1<<unit;
3471412Sbill 		dk_seek[unit]++;
348264Sbill 	}
349268Sbill 	goto out;
350264Sbill 
351264Sbill done:
352266Sbill 	/*
353275Sbill 	 * This unit is ready to go so
354275Sbill 	 * link it onto the chain of ready disks.
355266Sbill 	 */
356264Sbill 	dp->b_forw = NULL;
357266Sbill 	if (uptab.b_actf == NULL)
358264Sbill 		uptab.b_actf = dp;
359264Sbill 	else
360264Sbill 		uptab.b_actl->b_forw = dp;
361264Sbill 	uptab.b_actl = dp;
362268Sbill 
363268Sbill out:
364268Sbill 	return (didie);
365264Sbill }
366264Sbill 
367264Sbill /*
368264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
369264Sbill  */
370264Sbill upstart()
371264Sbill {
372264Sbill 	register struct buf *bp, *dp;
373264Sbill 	register unit;
374264Sbill 	register struct device *upaddr;
375264Sbill 	daddr_t bn;
376266Sbill 	int dn, sn, tn, cn, cmd;
377264Sbill 
378264Sbill loop:
379266Sbill 	/*
380266Sbill 	 * Pick a drive off the queue of ready drives, and
381266Sbill 	 * perform the first transfer on its queue.
382266Sbill 	 *
383266Sbill 	 * Looping here is completely for the sake of drives which
384266Sbill 	 * are not present and on-line, for which we completely clear the
385266Sbill 	 * request queue.
386266Sbill 	 */
387273Sbill 	if ((dp = uptab.b_actf) == NULL)
388268Sbill 		return (0);
389264Sbill 	if ((bp = dp->b_actf) == NULL) {
390264Sbill 		uptab.b_actf = dp->b_forw;
391264Sbill 		goto loop;
392264Sbill 	}
393266Sbill 	/*
394266Sbill 	 * Mark the controller busy, and multi-part disk address.
395266Sbill 	 * Select the unit on which the i/o is to take place.
396266Sbill 	 */
397264Sbill 	uptab.b_active++;
398264Sbill 	unit = minor(bp->b_dev) & 077;
399264Sbill 	dn = dkunit(bp);
400264Sbill 	bn = dkblock(bp);
401264Sbill 	cn = up_sizes[unit&07].cyloff;
402264Sbill 	cn += bn/(NSECT*NTRAC);
403264Sbill 	sn = bn%(NSECT*NTRAC);
404264Sbill 	tn = sn/NSECT;
405266Sbill 	sn %= NSECT;
406264Sbill 	upaddr = UPADDR;
4071756Sbill 	if ((upaddr->upcs2 & 07) != dn)
408264Sbill 		upaddr->upcs2 = dn;
4091756Sbill 	up_ubinfo = ubasetup(bp, 1);
410266Sbill 	/*
411266Sbill 	 * If drive is not present and on-line, then
412266Sbill 	 * get rid of this with an error and loop to get
413266Sbill 	 * rid of the rest of its queued requests.
414266Sbill 	 * (Then on to any other ready drives.)
415266Sbill 	 */
416264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
417893Sbill 		printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds);
418893Sbill 		if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
419893Sbill 			printf("-- hard\n");
420893Sbill 			uptab.b_active = 0;
421893Sbill 			uptab.b_errcnt = 0;
422893Sbill 			dp->b_actf = bp->av_forw;
423893Sbill 			dp->b_active = 0;
424893Sbill 			bp->b_flags |= B_ERROR;
425893Sbill 			iodone(bp);
426893Sbill 			/* A funny place to do this ... */
427893Sbill 			ubafree(up_ubinfo), up_ubinfo = 0;
428893Sbill 			goto loop;
429893Sbill 		}
430893Sbill 		printf("-- came back\n");
431264Sbill 	}
432266Sbill 	/*
433266Sbill 	 * If this is a retry, then with the 16'th retry we
434266Sbill 	 * begin to try offsetting the heads to recover the data.
435266Sbill 	 */
436924Sbill 	if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) {
437264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
438266Sbill 		upaddr->upcs1 = IE|OFFSET|GO;
439266Sbill 		while (upaddr->upds & PIP)
440264Sbill 			DELAY(25);
441264Sbill 	}
442266Sbill 	/*
443266Sbill 	 * Now set up the transfer, retrieving the high
444266Sbill 	 * 2 bits of the UNIBUS address from the information
445266Sbill 	 * returned by ubasetup() for the cs1 register bits 8 and 9.
446266Sbill 	 */
447264Sbill 	upaddr->updc = cn;
448264Sbill 	upaddr->upda = (tn << 8) + sn;
449264Sbill 	upaddr->upba = up_ubinfo;
450264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
451266Sbill 	cmd = (up_ubinfo >> 8) & 0x300;
452264Sbill 	if (bp->b_flags & B_READ)
453266Sbill 		cmd |= IE|RCOM|GO;
454264Sbill 	else
455266Sbill 		cmd |= IE|WCOM|GO;
456266Sbill 	upaddr->upcs1 = cmd;
457266Sbill 	/*
458266Sbill 	 * This is a controller busy situation.
459266Sbill 	 * Record in dk slot NUP+DK_N (after last drive)
460266Sbill 	 * unless there aren't that many slots reserved for
461266Sbill 	 * us in which case we record this as a drive busy
462266Sbill 	 * (if there is room for that).
463266Sbill 	 */
464264Sbill 	unit = dn+DK_N;
465264Sbill 	if (unit <= DK_NMAX) {
466264Sbill 		dk_busy |= 1<<unit;
4671412Sbill 		dk_xfer[unit]++;
468264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
469264Sbill 	}
470268Sbill 	return (1);
471264Sbill }
472264Sbill 
473264Sbill /*
474264Sbill  * Handle a device interrupt.
475264Sbill  *
476264Sbill  * If the transferring drive needs attention, service it
477264Sbill  * retrying on error or beginning next transfer.
478264Sbill  * Service all other ready drives, calling ustart to transfer
479264Sbill  * their blocks to the ready queue in uptab, and then restart
480264Sbill  * the controller if there is anything to do.
481264Sbill  */
482264Sbill upintr()
483264Sbill {
484264Sbill 	register struct buf *bp, *dp;
485264Sbill 	register unit;
486264Sbill 	register struct device *upaddr = UPADDR;
487264Sbill 	int as = upaddr->upas & 0377;
488341Sbill 	int oupsoftas;
489268Sbill 	int needie = 1;
490264Sbill 
491276Sbill 	(void) spl6();
492313Sbill 	up_wticks = 0;
493266Sbill 	if (uptab.b_active) {
494266Sbill 		/*
495266Sbill 		 * The drive is transferring, thus the hardware
496266Sbill 		 * (say the designers) will only interrupt when the transfer
497266Sbill 		 * completes; check for it anyways.
498266Sbill 		 */
499266Sbill 		if ((upaddr->upcs1 & RDY) == 0) {
500272Sbill 			printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
501272Sbill 			    upaddr->upds, upaddr->upwc);
502341Sbill 			printf("as=%d act %d %d %d\n", as, uptab.b_active,
503341Sbill 			    uputab[0].b_active, uputab[1].b_active);
504269Sbill 		}
505266Sbill 		/*
5061412Sbill 		 * Mark drive not busy, and check for an
507266Sbill 		 * error condition which may have resulted from the transfer.
508266Sbill 		 */
509264Sbill 		dp = uptab.b_actf;
510264Sbill 		bp = dp->b_actf;
511264Sbill 		unit = dkunit(bp);
5121412Sbill 		if (DK_N+unit <= DK_NMAX)
513264Sbill 			dk_busy &= ~(1<<(DK_N+unit));
5141756Sbill 		if ((upaddr->upcs2 & 07) != unit)
515275Sbill 			upaddr->upcs2 = unit;
516885Sbill 		if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) {
5171829Sbill 			int cs2;
518266Sbill 			/*
519266Sbill 			 * An error occurred, indeed.  Select this unit
520266Sbill 			 * to get at the drive status (a SEARCH may have
521266Sbill 			 * intervened to change the selected unit), and
522266Sbill 			 * wait for the command which caused the interrupt
523266Sbill 			 * to complete (DRY).
524266Sbill 			 */
525266Sbill 			while ((upaddr->upds & DRY) == 0)
526264Sbill 				DELAY(25);
527266Sbill 			/*
528266Sbill 			 * After 28 retries (16 w/o servo offsets, and then
529266Sbill 			 * 12 with servo offsets), or if we encountered
530266Sbill 			 * an error because the drive is write-protected,
531266Sbill 			 * give up.  Print an error message on the last 2
532266Sbill 			 * retries before a hard failure.
533266Sbill 			 */
534266Sbill 			if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
535264Sbill 				bp->b_flags |= B_ERROR;
536264Sbill 			else
537266Sbill 				uptab.b_active = 0;	/* To force retry */
538266Sbill 			if (uptab.b_errcnt > 27)
5391829Sbill 				cs2 = (int)upaddr->upcs2;
5401783Sbill 				deverror(bp, (int)upaddr->upcs2,
5411783Sbill 				    (int)upaddr->uper1);
542266Sbill 			/*
543266Sbill 			 * If this was a correctible ECC error, let upecc
544266Sbill 			 * do the dirty work to correct it.  If upecc
545266Sbill 			 * starts another READ for the rest of the data
546266Sbill 			 * then it returns 1 (having set uptab.b_active).
547266Sbill 			 * Otherwise we are done and fall through to
548266Sbill 			 * finish up.
549266Sbill 			 */
550266Sbill 			if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
551266Sbill 				return;
552266Sbill 			/*
553266Sbill 			 * Clear the drive and, every 4 retries, recalibrate
554266Sbill 			 * to hopefully help clear up seek positioning problems.
555266Sbill 			 */
556264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
557268Sbill 			needie = 0;
558266Sbill 			if ((uptab.b_errcnt&07) == 4) {
559264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
560264Sbill 				while(upaddr->upds & PIP)
561264Sbill 					DELAY(25);
562264Sbill 			}
5631829Sbill 			if (uptab.b_errcnt == 28 && cs2&(NEM|MXF)) {
5641829Sbill 				printf("FLAKEY UP ");
5651829Sbill 				ubareset();
5661829Sbill 				return;
5671829Sbill 			}
568264Sbill 		}
569266Sbill 		/*
570266Sbill 		 * If we are still noted as active, then no
571266Sbill 		 * (further) retries are necessary.
572266Sbill 		 *
573266Sbill 		 * Make sure the correct unit is selected,
574266Sbill 		 * return it to centerline if necessary, and mark
575266Sbill 		 * this i/o complete, starting the next transfer
576266Sbill 		 * on this drive with the upustart routine (if any).
577266Sbill 		 */
578266Sbill 		if (uptab.b_active) {
579266Sbill 			if (uptab.b_errcnt >= 16) {
580266Sbill 				upaddr->upcs1 = RTC|GO|IE;
581266Sbill 				while (upaddr->upds & PIP)
582264Sbill 					DELAY(25);
583268Sbill 				needie = 0;
584264Sbill 			}
585264Sbill 			uptab.b_active = 0;
586264Sbill 			uptab.b_errcnt = 0;
587264Sbill 			uptab.b_actf = dp->b_forw;
588264Sbill 			dp->b_active = 0;
589264Sbill 			dp->b_errcnt = 0;
590264Sbill 			dp->b_actf = bp->av_forw;
591266Sbill 			bp->b_resid = (-upaddr->upwc * sizeof(short));
592275Sbill 			if (bp->b_resid)
593341Sbill 				printf("resid %d ds %o er? %o %o %o\n",
594341Sbill 				    bp->b_resid, upaddr->upds,
595275Sbill 				    upaddr->uper1, upaddr->uper2, upaddr->uper3);
596264Sbill 			iodone(bp);
597264Sbill 			if(dp->b_actf)
598268Sbill 				if (upustart(unit))
599268Sbill 					needie = 0;
600264Sbill 		}
601264Sbill 		as &= ~(1<<unit);
602341Sbill 		upsoftas &= ~(1<<unit);
603264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
604273Sbill 	} else {
6051756Sbill 		if (upaddr->upcs1 & TRE)
606264Sbill 			upaddr->upcs1 = TRE;
607264Sbill 	}
608266Sbill 	/*
609266Sbill 	 * If we have a unit with an outstanding SEARCH,
610266Sbill 	 * and the hardware indicates the unit requires attention,
611266Sbill 	 * the bring the drive to the ready queue.
612266Sbill 	 * Finally, if the controller is not transferring
613266Sbill 	 * start it if any drives are now ready to transfer.
614266Sbill 	 */
615341Sbill 	as |= upsoftas;
616341Sbill 	oupsoftas = upsoftas;
617341Sbill 	upsoftas = 0;
618266Sbill 	for (unit = 0; unit < NUP; unit++)
619341Sbill 		if ((as|oupsoftas) & (1<<unit)) {
6201756Sbill 			if (as & (1<<unit))
621267Sbill 				upaddr->upas = 1<<unit;
622273Sbill 			if (upustart(unit))
623273Sbill 				needie = 0;
624273Sbill 		}
625266Sbill 	if (uptab.b_actf && uptab.b_active == 0)
626268Sbill 		if (upstart())
627268Sbill 			needie = 0;
628275Sbill 	if (needie)
629266Sbill 		upaddr->upcs1 = IE;
630264Sbill }
631264Sbill 
632264Sbill upread(dev)
633264Sbill {
634264Sbill 
635264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
636264Sbill }
637264Sbill 
638264Sbill upwrite(dev)
639264Sbill {
640264Sbill 
641264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
642264Sbill }
643264Sbill 
644266Sbill /*
645266Sbill  * Correct an ECC error, and restart the i/o to complete
646266Sbill  * the transfer if necessary.  This is quite complicated because
647266Sbill  * the transfer may be going to an odd memory address base and/or
648266Sbill  * across a page boundary.
649266Sbill  */
650264Sbill upecc(up, bp)
651264Sbill register struct device *up;
652264Sbill register struct buf *bp;
653264Sbill {
654264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
655266Sbill 	register int i;
656264Sbill 	caddr_t addr;
657266Sbill 	int reg, bit, byte, npf, mask, o, cmd, ubaddr;
658264Sbill 	int bn, cn, tn, sn;
659264Sbill 
660264Sbill 	/*
661266Sbill 	 * Npf is the number of sectors transferred before the sector
662266Sbill 	 * containing the ECC error, and reg is the UBA register
663266Sbill 	 * mapping (the first part of) the transfer.
664266Sbill 	 * O is offset within a memory page of the first byte transferred.
665264Sbill 	 */
666266Sbill 	npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
667266Sbill 	reg = btop(up_ubinfo&0x3ffff) + npf;
668264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
669264Sbill 	printf("%D ", bp->b_blkno+npf);
670264Sbill 	prdev("ECC", bp->b_dev);
671264Sbill 	mask = up->upec2;
672264Sbill 	if (mask == 0) {
673266Sbill 		up->upof = FMT22;		/* == RTC ???? */
674264Sbill 		return (0);
675264Sbill 	}
676266Sbill 	/*
677266Sbill 	 * Flush the buffered data path, and compute the
678266Sbill 	 * byte and bit position of the error.  The variable i
679266Sbill 	 * is the byte offset in the transfer, the variable byte
680266Sbill 	 * is the offset from a page boundary in main memory.
681266Sbill 	 */
682266Sbill 	ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
683266Sbill 	i = up->upec1 - 1;		/* -1 makes 0 origin */
684266Sbill 	bit = i&07;
685266Sbill 	i = (i&~07)>>3;
686264Sbill 	byte = i + o;
687266Sbill 	/*
688266Sbill 	 * Correct while possible bits remain of mask.  Since mask
689266Sbill 	 * contains 11 bits, we continue while the bit offset is > -11.
690266Sbill 	 * Also watch out for end of this block and the end of the whole
691266Sbill 	 * transfer.
692266Sbill 	 */
693266Sbill 	while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
694266Sbill 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
695266Sbill 		    (byte & PGOFSET);
696266Sbill 		putmemc(addr, getmemc(addr)^(mask<<bit));
697266Sbill 		byte++;
698266Sbill 		i++;
699266Sbill 		bit -= 8;
700264Sbill 	}
701266Sbill 	uptab.b_active++;	/* Either complete or continuing... */
702264Sbill 	if (up->upwc == 0)
703264Sbill 		return (0);
704266Sbill 	/*
705266Sbill 	 * Have to continue the transfer... clear the drive,
706266Sbill 	 * and compute the position where the transfer is to continue.
707266Sbill 	 * We have completed npf+1 sectors of the transfer already;
708266Sbill 	 * restart at offset o of next sector (i.e. in UBA register reg+1).
709266Sbill 	 */
710266Sbill 	up->upcs1 = TRE|IE|DCLR|GO;
711264Sbill 	bn = dkblock(bp);
712264Sbill 	cn = bp->b_cylin;
713266Sbill 	sn = bn%(NSECT*NTRAC) + npf + 1;
714264Sbill 	tn = sn/NSECT;
715264Sbill 	sn %= NSECT;
716266Sbill 	cn += tn/NTRAC;
717266Sbill 	tn %= NTRAC;
718264Sbill 	up->updc = cn;
719266Sbill 	up->upda = (tn << 8) | sn;
720266Sbill 	ubaddr = (int)ptob(reg+1) + o;
721266Sbill 	up->upba = ubaddr;
722266Sbill 	cmd = (ubaddr >> 8) & 0x300;
723266Sbill 	cmd |= IE|GO|RCOM;
724266Sbill 	up->upcs1 = cmd;
725264Sbill 	return (1);
726264Sbill }
727286Sbill 
728286Sbill /*
729286Sbill  * Reset driver after UBA init.
730286Sbill  * Cancel software state of all pending transfers
731286Sbill  * and restart all units and the controller.
732286Sbill  */
733286Sbill upreset()
734286Sbill {
735286Sbill 	int unit;
736286Sbill 
737286Sbill 	printf(" up");
7381829Sbill 	DELAY(15000000);		/* give it time to self-test */
739286Sbill 	uptab.b_active = 0;
740286Sbill 	uptab.b_actf = uptab.b_actl = 0;
741286Sbill 	if (up_ubinfo) {
742286Sbill 		printf("<%d>", (up_ubinfo>>28)&0xf);
743286Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
744286Sbill 	}
745313Sbill 	UPADDR->upcs2 = CLR;		/* clear controller */
746286Sbill 	for (unit = 0; unit < NUP; unit++) {
747286Sbill 		uputab[unit].b_active = 0;
748286Sbill 		(void) upustart(unit);
749286Sbill 	}
750286Sbill 	(void) upstart();
751286Sbill }
752313Sbill 
753313Sbill /*
754313Sbill  * Wake up every second and if an interrupt is pending
755313Sbill  * but nothing has happened increment a counter.
756313Sbill  * If nothing happens for 20 seconds, reset the controller
757313Sbill  * and begin anew.
758313Sbill  */
759313Sbill upwatch()
760313Sbill {
761313Sbill 	int i;
762313Sbill 
7631783Sbill 	timeout(upwatch, (caddr_t)0, HZ);
764313Sbill 	if (uptab.b_active == 0) {
765313Sbill 		for (i = 0; i < NUP; i++)
766313Sbill 			if (uputab[i].b_active)
767313Sbill 				goto active;
768313Sbill 		up_wticks = 0;		/* idling */
769313Sbill 		return;
770313Sbill 	}
771313Sbill active:
772313Sbill 	up_wticks++;
773313Sbill 	if (up_wticks >= 20) {
774313Sbill 		up_wticks = 0;
775313Sbill 		printf("LOST INTERRUPT RESET");
776313Sbill 		upreset();
777313Sbill 		printf("\n");
778313Sbill 	}
779313Sbill }
7801809Sbill #endif
7811902Swnj #endif
782