xref: /csrg-svn/sys/vax/uba/up.c (revision 1412)
1*1412Sbill /*	up.c	3.25	10/13/80	*/
2264Sbill 
3264Sbill /*
4885Sbill  * UNIBUS disk driver with overlapped seeks and ECC recovery.
5264Sbill  *
6885Sbill  * This driver works marginally on an Emulex SC-11B controller with rev
7885Sbill  * level J microcode, defining:
8885Sbill  *	int	olducode = 1;
9885Sbill  * to force CPU stalling delays.
10266Sbill  *
11885Sbill  * It has worked with no delays and no problems on a prototype
12885Sbill  * SC-21 controller.  Emulex intends to upgrade all SC-11s on VAXes to SC-21s.
13885Sbill  * You should get a SC-21 to replace any SC-11 on a VAX.
14885Sbill  *
15885Sbill  * SC-11B Controller switch settings:
16264Sbill  *	SW1-1	5/19 surfaces	(off, 19 surfaces on Ampex 9300)
17264Sbill  *	SW1-2	chksum enable	(off, checksum disabled)
18264Sbill  *	SW1-3	volume select	(off, 815 cylinders)
19264Sbill  *	SW1-4	sector select	(on, 32 sectors)
20264Sbill  *	SW1-5	unused		(off)
21264Sbill  *	SW1-6	port select	(on, single port)
22264Sbill  *	SW1-7	npr delay	(off, disable)
23264Sbill  *	SW1-8	ecc test mode	(off, disable)
24264Sbill  * and top mounted switches:
25264Sbill  *	SW2-1	extend opcodes	(off=open, disable)
26264Sbill  *	SW2-2	extend diag	(off=open, disable)
27341Sbill  *	SW2-3	4 wd dma burst	(on=closed, enable)
28264Sbill  *	SW2-4	unused		(off=open)
29264Sbill  */
30264Sbill 
31264Sbill #include "../h/param.h"
32264Sbill #include "../h/systm.h"
33308Sbill #include "../h/dk.h"
34264Sbill #include "../h/buf.h"
35264Sbill #include "../h/conf.h"
36264Sbill #include "../h/dir.h"
37264Sbill #include "../h/user.h"
38264Sbill #include "../h/map.h"
39420Sbill #include "../h/pte.h"
40264Sbill #include "../h/mba.h"
41264Sbill #include "../h/mtpr.h"
42264Sbill #include "../h/uba.h"
43264Sbill #include "../h/vm.h"
44264Sbill 
45264Sbill /*
46*1412Sbill  * Drive stats are gathered in dk_*[DK_N].. dk_*[DK_NMAX]
47264Sbill  */
48308Sbill #define	DK_N	2
49308Sbill #define	DK_NMAX	3
50264Sbill 
51264Sbill #define	ushort	unsigned short
52264Sbill 
53264Sbill struct	device
54264Sbill {
55264Sbill 	ushort	upcs1;		/* control and status register 1 */
56264Sbill 	short	upwc;		/* word count register */
57264Sbill 	ushort	upba;		/* UNIBUS address register */
58264Sbill 	ushort	upda;		/* desired address register */
59264Sbill 	ushort	upcs2;		/* control and status register 2 */
60264Sbill 	ushort	upds;		/* drive Status */
61264Sbill 	ushort	uper1;		/* error register 1 */
62264Sbill 	ushort	upas;		/* attention summary */
63264Sbill 	ushort	upla;		/* look ahead */
64264Sbill 	ushort	updb;		/* data buffer */
65264Sbill 	ushort	upmr;		/* maintenance */
66264Sbill 	ushort	updt;		/* drive type */
67264Sbill 	ushort	upsn;		/* serial number */
68264Sbill 	ushort	upof;		/* offset register */
69264Sbill 	ushort	updc;		/* desired cylinder address register */
70264Sbill 	ushort	upcc;		/* current cylinder */
71264Sbill 	ushort	uper2;		/* error register 2 */
72264Sbill 	ushort	uper3;		/* error register 3 */
73264Sbill 	ushort	upec1;		/* burst error bit position */
74264Sbill 	ushort	upec2;		/* burst error bit pattern */
75264Sbill };
76264Sbill 
77275Sbill /*
78275Sbill  * Software extension to the upas register, so we can
79275Sbill  * postpone starting SEARCH commands until the controller
80275Sbill  * is not transferring.
81275Sbill  */
82341Sbill int	upsoftas;
83275Sbill 
84275Sbill /*
85275Sbill  * If upseek then we don't issue SEARCH commands but rather just
86275Sbill  * settle for a SEEK to the correct cylinder.
87275Sbill  */
88275Sbill int	upseek;
89275Sbill 
90264Sbill #define	UPADDR	((struct device *)(UBA0_DEV + 0176700))
91264Sbill 
92264Sbill #define	NUP	2		/* Number of drives this installation */
93264Sbill 
94264Sbill #define	NSECT	32
95264Sbill #define	NTRAC	19
96264Sbill 
97264Sbill /*
98264Sbill  * Constants controlling on-cylinder SEARCH usage.
99264Sbill  *
100308Sbill  * 	upSDIST/2 msec		time needed to start transfer
101308Sbill  * 	upRDIST/2 msec		tolerable rotational latency when on-cylinder
102275Sbill  *
103308Sbill  * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
104275Sbill  * and in the driver then we take it as it is.  Otherwise we do a SEARCH
105308Sbill  * requesting an interrupt upSDIST sectors in advance.
106264Sbill  */
107308Sbill #define	_upSDIST	6		/* 3.0 msec */
108308Sbill #define	_upRDIST	6		/* 3.0 msec */
109264Sbill 
110308Sbill int	upSDIST = _upSDIST;
111308Sbill int	upRDIST = _upRDIST;
112275Sbill 
113264Sbill /*
114264Sbill  * To fill a 300M drive:
115264Sbill  *	A is designed to be used as a root.
116264Sbill  *	B is suitable for a swap area.
117264Sbill  *	H is the primary storage area.
118264Sbill  * On systems with RP06'es, we normally use only 291346 blocks of the H
119264Sbill  * area, and use DEF or G to cover the rest of the drive.  The C system
120264Sbill  * covers the whole drive and can be used for pack-pack copying.
121264Sbill  */
122264Sbill struct	size
123264Sbill {
124264Sbill 	daddr_t	nblocks;
125264Sbill 	int	cyloff;
126264Sbill } up_sizes[8] = {
127264Sbill 	15884,	0,		/* A=cyl 0 thru 26 */
128264Sbill 	33440,	27,		/* B=cyl 27 thru 81 */
129341Sbill 	495520,	0,		/* C=cyl 0 thru 814 */
130264Sbill 	15884,	562,		/* D=cyl 562 thru 588 */
131264Sbill 	55936,	589,		/* E=cyl 589 thru 680 */
132264Sbill 	81472,	681,		/* F=cyl 681 thru 814 */
133264Sbill 	153824,	562,		/* G=cyl 562 thru 814 */
134264Sbill 	445664,	82,		/* H=cyl 82 thru 814 */
135264Sbill /* Later, and more safely for H area...
136264Sbill 	291346,	82,		/* H=cyl 82 thru 561 */
137264Sbill };
138264Sbill 
139264Sbill /*
140264Sbill  * The following defines are used in offset positioning
141264Sbill  * when trying to recover disk errors, with the constants being
142264Sbill  * +/- microinches.  Note that header compare inhibit (HCI) is not
143264Sbill  * tried (this makes sense only during read, in any case.)
144264Sbill  *
145341Sbill  * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?!
146264Sbill  */
147264Sbill #define	P400	020
148264Sbill #define	M400	0220
149264Sbill #define	P800	040
150264Sbill #define	M800	0240
151264Sbill #define	P1200	060
152264Sbill #define	M1200	0260
153264Sbill #define	HCI	020000
154264Sbill 
155264Sbill int	up_offset[16] =
156264Sbill {
157264Sbill 	P400, M400, P400, M400,
158264Sbill 	P800, M800, P800, M800,
159264Sbill 	P1200, M1200, P1200, M1200,
160264Sbill 	0, 0, 0, 0,
161264Sbill };
162264Sbill 
163264Sbill /*
164264Sbill  * Each drive has a table uputab[i].  On this table are sorted the
165264Sbill  * pending requests implementing an elevator algorithm (see dsort.c.)
166264Sbill  * In the upustart() routine, each drive is independently advanced
167264Sbill  * until it is on the desired cylinder for the next transfer and near
168264Sbill  * the desired sector.  The drive is then chained onto the uptab
169264Sbill  * table, and the transfer is initiated by the upstart() routine.
170264Sbill  * When the transfer is completed the driver reinvokes the upustart()
171264Sbill  * routine to set up the next transfer.
172264Sbill  */
173264Sbill struct	buf	uptab;
174264Sbill struct	buf	uputab[NUP];
175264Sbill 
176264Sbill struct	buf	rupbuf;			/* Buffer for raw i/o */
177264Sbill 
178264Sbill /* Drive commands, placed in upcs1 */
179264Sbill #define	GO	01		/* Go bit, set in all commands */
180264Sbill #define	PRESET	020		/* Preset drive at init or after errors */
181264Sbill #define	OFFSET	014		/* Offset heads to try to recover error */
182264Sbill #define	RTC	016		/* Return to center-line after OFFSET */
183264Sbill #define	SEARCH	030		/* Search for cylinder+sector */
184275Sbill #define	SEEK	04		/* Seek to cylinder */
185264Sbill #define	RECAL	06		/* Recalibrate, needed after seek error */
186264Sbill #define	DCLR	010		/* Drive clear, after error */
187264Sbill #define	WCOM	060		/* Write */
188264Sbill #define	RCOM	070		/* Read */
189264Sbill 
190264Sbill /* Other bits of upcs1 */
191264Sbill #define	IE	0100		/* Controller wide interrupt enable */
192264Sbill #define	TRE	040000		/* Transfer error */
193345Sbill #define	RDY	0200		/* Transfer terminated */
194264Sbill 
195264Sbill /* Drive status bits of upds */
196264Sbill #define	PIP	020000		/* Positioning in progress */
197264Sbill #define	ERR	040000		/* Error has occurred, DCLR necessary */
198264Sbill #define	VV	0100		/* Volume is valid, set by PRESET */
199264Sbill #define	DPR	0400		/* Drive has been preset */
200264Sbill #define	MOL	010000		/* Drive is online, heads loaded, etc */
201264Sbill #define	DRY	0200		/* Drive ready */
202264Sbill 
203313Sbill /* Bits of upcs2 */
204313Sbill #define	CLR	040		/* Controller clear */
205264Sbill /* Bits of uper1 */
206264Sbill #define	DCK	0100000		/* Ecc error occurred */
207264Sbill #define	ECH	0100		/* Ecc error was unrecoverable */
208264Sbill #define	WLE	04000		/* Attempt to write read-only drive */
209264Sbill 
210264Sbill /* Bits of upof; the offset bits above are also in this register */
211264Sbill #define	FMT22	010000		/* 16 bits/word, must be always set */
212264Sbill 
213264Sbill #define	b_cylin b_resid
214264Sbill 
215264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
216264Sbill /*
217264Sbill  * The EMULEX controller balks if accessed quickly after
218341Sbill  * certain operations.  With rev J delays seem to be needed only
219341Sbill  * when selecting a new unit, and in drive initialization type
220341Sbill  * like PRESET and DCLR.  The following variables control the delay
221341Sbill  * DELAY(n) is approximately n usec.
222264Sbill  */
223367Sbill int	olducode = 1;
224264Sbill int	idelay = 500;		/* Delay after PRESET or DCLR */
225367Sbill int	osdelay = 150;		/* Old delay after selecting drive in upcs2 */
226367Sbill int	ordelay = 100;		/* Old delay after SEARCH */
227367Sbill int	oasdel = 100;		/* Old delay after clearing bit in upas */
228367Sbill int	nsdelay = 25;
229264Sbill 
230264Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
231264Sbill 
232264Sbill int	nwaitcs2;		/* How many sdelay loops ? */
233264Sbill int	neasycs2;		/* How many sdelay loops not needed ? */
234264Sbill 
235313Sbill int	up_wticks;		/* Ticks waiting for interrupt */
236313Sbill int	upwstart;		/* Have started guardian */
237313Sbill int	upwatch();
238313Sbill 
239264Sbill #ifdef INTRLVE
240264Sbill daddr_t dkblock();
241264Sbill #endif
242264Sbill 
243264Sbill /*
244264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
245264Sbill  *
246264Sbill  * A unit start is issued if the drive is inactive, causing
247264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
248264Sbill  * already nearly on the money and the controller is not transferring
249264Sbill  * we kick it to start the transfer.
250264Sbill  */
251264Sbill upstrategy(bp)
252264Sbill register struct buf *bp;
253264Sbill {
254264Sbill 	register struct buf *dp;
255264Sbill 	register unit, xunit;
256264Sbill 	long sz, bn;
257264Sbill 
258313Sbill 	if (upwstart == 0) {
259313Sbill 		timeout((caddr_t)upwatch, 0, HZ);
260313Sbill 		upwstart++;
261313Sbill 	}
262264Sbill 	xunit = minor(bp->b_dev) & 077;
263264Sbill 	sz = bp->b_bcount;
264264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
265264Sbill 	unit = dkunit(bp);
266264Sbill 	if (unit >= NUP ||
267264Sbill 	    bp->b_blkno < 0 ||
268264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
269264Sbill 		bp->b_flags |= B_ERROR;
270264Sbill 		iodone(bp);
271264Sbill 		return;
272264Sbill 	}
273*1412Sbill 	if (DK_N+unit <= DK_NMAX)
274*1412Sbill 		dk_mspw[DK_N+unit] = .0000020345;
275264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
276264Sbill 	dp = &uputab[unit];
277264Sbill 	(void) spl5();
278264Sbill 	disksort(dp, bp);
279264Sbill 	if (dp->b_active == 0) {
280268Sbill 		(void) upustart(unit);
281264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
282268Sbill 			(void) upstart();
283264Sbill 	}
284264Sbill 	(void) spl0();
285264Sbill }
286264Sbill 
287264Sbill /*
288264Sbill  * Start activity on specified drive; called when drive is inactive
289264Sbill  * and new transfer request arrives and also when upas indicates that
290264Sbill  * a SEARCH command is complete.
291264Sbill  */
292264Sbill upustart(unit)
293264Sbill register unit;
294264Sbill {
295264Sbill 	register struct buf *bp, *dp;
296264Sbill 	register struct device *upaddr = UPADDR;
297264Sbill 	daddr_t bn;
298264Sbill 	int sn, cn, csn;
299268Sbill 	int didie = 0;
300264Sbill 
301275Sbill 	/*
302275Sbill 	 * Other drivers tend to say something like
303275Sbill 	 *	upaddr->upcs1 = IE;
304275Sbill 	 *	upaddr->upas = 1<<unit;
305275Sbill 	 * here, but the SC-11B will cancel a command which
306275Sbill 	 * happens to be sitting in the cs1 if you clear the go
307275Sbill 	 * bit by storing there (so the first is not safe),
308275Sbill 	 * and it also does not like being bothered with operations
309275Sbill 	 * such as clearing upas when a transfer is active (as
310275Sbill 	 * it may well be.)
311275Sbill 	 *
312275Sbill 	 * Thus we keep careful track of when we re-enable IE
313275Sbill 	 * after an interrupt and do it only if we didn't issue
314275Sbill 	 * a command which re-enabled it as a matter of course.
315275Sbill 	 * We clear bits in upas in the interrupt routine, when
316275Sbill 	 * no transfers are active.
317275Sbill 	 */
318266Sbill 	if (unit >= NUP)
319268Sbill 		goto out;
320264Sbill 	if (unit+DK_N <= DK_NMAX)
321264Sbill 		dk_busy &= ~(1<<(unit+DK_N));
322264Sbill 	dp = &uputab[unit];
323266Sbill 	if ((bp = dp->b_actf) == NULL)
324268Sbill 		goto out;
325275Sbill 	/*
326275Sbill 	 * The SC-11B doesn't start SEARCH commands when transfers are
327275Sbill 	 * in progress.  In fact, it tends to get confused when given
328275Sbill 	 * SEARCH'es during transfers, generating interrupts with neither
329275Sbill 	 * RDY nor a bit in the upas register.  Thus we defer
330275Sbill 	 * until an interrupt when a transfer is pending.
331275Sbill 	 */
332275Sbill 	if (uptab.b_active) {
333341Sbill 		upsoftas |= 1<<unit;
334275Sbill 		return (0);
335275Sbill 	}
336276Sbill 	if (dp->b_active)
337276Sbill 		goto done;
338276Sbill 	dp->b_active = 1;
339264Sbill 	if ((upaddr->upcs2 & 07) != unit) {
340264Sbill 		upaddr->upcs2 = unit;
341367Sbill 		DELAY(olducode ? osdelay : nsdelay);
342264Sbill 		nwaitcs2++;
343264Sbill 	} else
344264Sbill 		neasycs2++;
345266Sbill 	/*
346266Sbill 	 * If we have changed packs or just initialized,
347275Sbill 	 * then the volume will not be valid; if so, clear
348266Sbill 	 * the drive, preset it and put in 16bit/word mode.
349266Sbill 	 */
350266Sbill 	if ((upaddr->upds & VV) == 0) {
351266Sbill 		upaddr->upcs1 = IE|DCLR|GO;
352266Sbill 		DELAY(idelay);
353264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
354264Sbill 		DELAY(idelay);
355264Sbill 		upaddr->upof = FMT22;
356268Sbill 		didie = 1;
357264Sbill 	}
358264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
359275Sbill 		goto done;
360266Sbill 	/*
361266Sbill 	 * Do enough of the disk address decoding to determine
362266Sbill 	 * which cylinder and sector the request is on.
363266Sbill 	 * If we are on the correct cylinder and the desired sector
364308Sbill 	 * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
365266Sbill 	 * we don't bother to SEARCH but just begin the transfer asap.
366308Sbill 	 * Otherwise ask for a interrupt upSDIST sectors ahead.
367266Sbill 	 */
368264Sbill 	bn = dkblock(bp);
369264Sbill 	cn = bp->b_cylin;
370264Sbill 	sn = bn%(NSECT*NTRAC);
371308Sbill 	sn = (sn+NSECT-upSDIST)%NSECT;
372264Sbill 
373266Sbill 	if (cn - upaddr->updc)
374266Sbill 		goto search;		/* Not on-cylinder */
375275Sbill 	else if (upseek)
376275Sbill 		goto done;		/* Ok just to be on-cylinder */
377264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
378266Sbill 	if (csn < 0)
379264Sbill 		csn += NSECT;
380308Sbill 	if (csn > NSECT-upRDIST)
381264Sbill 		goto done;
382264Sbill 
383264Sbill search:
384264Sbill 	upaddr->updc = cn;
385275Sbill 	if (upseek)
386275Sbill 		upaddr->upcs1 = IE|SEEK|GO;
387275Sbill 	else {
388275Sbill 		upaddr->upda = sn;
389275Sbill 		upaddr->upcs1 = IE|SEARCH|GO;
390275Sbill 	}
391268Sbill 	didie = 1;
392266Sbill 	/*
393266Sbill 	 * Mark this unit busy.
394266Sbill 	 */
395264Sbill 	unit += DK_N;
396*1412Sbill 	if (unit <= DK_NMAX) {
397264Sbill 		dk_busy |= 1<<unit;
398*1412Sbill 		dk_seek[unit]++;
399264Sbill 	}
400367Sbill 	if (olducode)
401367Sbill 		DELAY(ordelay);
402268Sbill 	goto out;
403264Sbill 
404264Sbill done:
405266Sbill 	/*
406275Sbill 	 * This unit is ready to go so
407275Sbill 	 * link it onto the chain of ready disks.
408266Sbill 	 */
409264Sbill 	dp->b_forw = NULL;
410266Sbill 	if (uptab.b_actf == NULL)
411264Sbill 		uptab.b_actf = dp;
412264Sbill 	else
413264Sbill 		uptab.b_actl->b_forw = dp;
414264Sbill 	uptab.b_actl = dp;
415268Sbill 
416268Sbill out:
417268Sbill 	return (didie);
418264Sbill }
419264Sbill 
420264Sbill /*
421264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
422264Sbill  */
423264Sbill upstart()
424264Sbill {
425264Sbill 	register struct buf *bp, *dp;
426264Sbill 	register unit;
427264Sbill 	register struct device *upaddr;
428264Sbill 	daddr_t bn;
429266Sbill 	int dn, sn, tn, cn, cmd;
430264Sbill 
431264Sbill loop:
432266Sbill 	/*
433266Sbill 	 * Pick a drive off the queue of ready drives, and
434266Sbill 	 * perform the first transfer on its queue.
435266Sbill 	 *
436266Sbill 	 * Looping here is completely for the sake of drives which
437266Sbill 	 * are not present and on-line, for which we completely clear the
438266Sbill 	 * request queue.
439266Sbill 	 */
440273Sbill 	if ((dp = uptab.b_actf) == NULL)
441268Sbill 		return (0);
442264Sbill 	if ((bp = dp->b_actf) == NULL) {
443264Sbill 		uptab.b_actf = dp->b_forw;
444264Sbill 		goto loop;
445264Sbill 	}
446266Sbill 	/*
447266Sbill 	 * Mark the controller busy, and multi-part disk address.
448266Sbill 	 * Select the unit on which the i/o is to take place.
449266Sbill 	 */
450264Sbill 	uptab.b_active++;
451264Sbill 	unit = minor(bp->b_dev) & 077;
452264Sbill 	dn = dkunit(bp);
453264Sbill 	bn = dkblock(bp);
454264Sbill 	cn = up_sizes[unit&07].cyloff;
455264Sbill 	cn += bn/(NSECT*NTRAC);
456264Sbill 	sn = bn%(NSECT*NTRAC);
457264Sbill 	tn = sn/NSECT;
458266Sbill 	sn %= NSECT;
459264Sbill 	upaddr = UPADDR;
460264Sbill 	if ((upaddr->upcs2 & 07) != dn) {
461264Sbill 		upaddr->upcs2 = dn;
462275Sbill 		/* DELAY(sdelay);		Provided by ubasetup() */
463264Sbill 		nwaitcs2++;
464264Sbill 	} else
465264Sbill 		neasycs2++;
466275Sbill 	up_ubinfo = ubasetup(bp, 1);	/* Providing delay */
467266Sbill 	/*
468266Sbill 	 * If drive is not present and on-line, then
469266Sbill 	 * get rid of this with an error and loop to get
470266Sbill 	 * rid of the rest of its queued requests.
471266Sbill 	 * (Then on to any other ready drives.)
472266Sbill 	 */
473264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
474893Sbill 		printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds);
475893Sbill 		if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
476893Sbill 			printf("-- hard\n");
477893Sbill 			uptab.b_active = 0;
478893Sbill 			uptab.b_errcnt = 0;
479893Sbill 			dp->b_actf = bp->av_forw;
480893Sbill 			dp->b_active = 0;
481893Sbill 			bp->b_flags |= B_ERROR;
482893Sbill 			iodone(bp);
483893Sbill 			/* A funny place to do this ... */
484893Sbill 			ubafree(up_ubinfo), up_ubinfo = 0;
485893Sbill 			goto loop;
486893Sbill 		}
487893Sbill 		printf("-- came back\n");
488264Sbill 	}
489266Sbill 	/*
490266Sbill 	 * If this is a retry, then with the 16'th retry we
491266Sbill 	 * begin to try offsetting the heads to recover the data.
492266Sbill 	 */
493924Sbill 	if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) {
494264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
495266Sbill 		upaddr->upcs1 = IE|OFFSET|GO;
496264Sbill 		DELAY(idelay);
497266Sbill 		while (upaddr->upds & PIP)
498264Sbill 			DELAY(25);
499264Sbill 	}
500266Sbill 	/*
501266Sbill 	 * Now set up the transfer, retrieving the high
502266Sbill 	 * 2 bits of the UNIBUS address from the information
503266Sbill 	 * returned by ubasetup() for the cs1 register bits 8 and 9.
504266Sbill 	 */
505264Sbill 	upaddr->updc = cn;
506264Sbill 	upaddr->upda = (tn << 8) + sn;
507264Sbill 	upaddr->upba = up_ubinfo;
508264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
509266Sbill 	cmd = (up_ubinfo >> 8) & 0x300;
510264Sbill 	if (bp->b_flags & B_READ)
511266Sbill 		cmd |= IE|RCOM|GO;
512264Sbill 	else
513266Sbill 		cmd |= IE|WCOM|GO;
514266Sbill 	upaddr->upcs1 = cmd;
515266Sbill 	/*
516266Sbill 	 * This is a controller busy situation.
517266Sbill 	 * Record in dk slot NUP+DK_N (after last drive)
518266Sbill 	 * unless there aren't that many slots reserved for
519266Sbill 	 * us in which case we record this as a drive busy
520266Sbill 	 * (if there is room for that).
521266Sbill 	 */
522264Sbill 	unit = dn+DK_N;
523264Sbill 	if (unit <= DK_NMAX) {
524264Sbill 		dk_busy |= 1<<unit;
525*1412Sbill 		dk_xfer[unit]++;
526264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
527264Sbill 	}
528268Sbill 	return (1);
529264Sbill }
530264Sbill 
531264Sbill /*
532264Sbill  * Handle a device interrupt.
533264Sbill  *
534264Sbill  * If the transferring drive needs attention, service it
535264Sbill  * retrying on error or beginning next transfer.
536264Sbill  * Service all other ready drives, calling ustart to transfer
537264Sbill  * their blocks to the ready queue in uptab, and then restart
538264Sbill  * the controller if there is anything to do.
539264Sbill  */
540264Sbill upintr()
541264Sbill {
542264Sbill 	register struct buf *bp, *dp;
543264Sbill 	register unit;
544264Sbill 	register struct device *upaddr = UPADDR;
545264Sbill 	int as = upaddr->upas & 0377;
546341Sbill 	int oupsoftas;
547268Sbill 	int needie = 1;
548264Sbill 
549276Sbill 	(void) spl6();
550313Sbill 	up_wticks = 0;
551266Sbill 	if (uptab.b_active) {
552266Sbill 		/*
553266Sbill 		 * The drive is transferring, thus the hardware
554266Sbill 		 * (say the designers) will only interrupt when the transfer
555266Sbill 		 * completes; check for it anyways.
556266Sbill 		 */
557266Sbill 		if ((upaddr->upcs1 & RDY) == 0) {
558272Sbill 			printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
559272Sbill 			    upaddr->upds, upaddr->upwc);
560341Sbill 			printf("as=%d act %d %d %d\n", as, uptab.b_active,
561341Sbill 			    uputab[0].b_active, uputab[1].b_active);
562269Sbill 		}
563266Sbill 		/*
564*1412Sbill 		 * Mark drive not busy, and check for an
565266Sbill 		 * error condition which may have resulted from the transfer.
566266Sbill 		 */
567264Sbill 		dp = uptab.b_actf;
568264Sbill 		bp = dp->b_actf;
569264Sbill 		unit = dkunit(bp);
570*1412Sbill 		if (DK_N+unit <= DK_NMAX)
571264Sbill 			dk_busy &= ~(1<<(DK_N+unit));
572275Sbill 		if ((upaddr->upcs2 & 07) != unit) {
573275Sbill 			upaddr->upcs2 = unit;
574367Sbill 			DELAY(olducode ? osdelay : nsdelay);
575275Sbill 			nwaitcs2++;
576275Sbill 		} else
577275Sbill 			neasycs2++;
578885Sbill 		if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) {
579266Sbill 			/*
580266Sbill 			 * An error occurred, indeed.  Select this unit
581266Sbill 			 * to get at the drive status (a SEARCH may have
582266Sbill 			 * intervened to change the selected unit), and
583266Sbill 			 * wait for the command which caused the interrupt
584266Sbill 			 * to complete (DRY).
585266Sbill 			 */
586266Sbill 			while ((upaddr->upds & DRY) == 0)
587264Sbill 				DELAY(25);
588266Sbill 			/*
589266Sbill 			 * After 28 retries (16 w/o servo offsets, and then
590266Sbill 			 * 12 with servo offsets), or if we encountered
591266Sbill 			 * an error because the drive is write-protected,
592266Sbill 			 * give up.  Print an error message on the last 2
593266Sbill 			 * retries before a hard failure.
594266Sbill 			 */
595266Sbill 			if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
596264Sbill 				bp->b_flags |= B_ERROR;
597264Sbill 			else
598266Sbill 				uptab.b_active = 0;	/* To force retry */
599266Sbill 			if (uptab.b_errcnt > 27)
600264Sbill 				deverror(bp, upaddr->upcs2, upaddr->uper1);
601266Sbill 			/*
602266Sbill 			 * If this was a correctible ECC error, let upecc
603266Sbill 			 * do the dirty work to correct it.  If upecc
604266Sbill 			 * starts another READ for the rest of the data
605266Sbill 			 * then it returns 1 (having set uptab.b_active).
606266Sbill 			 * Otherwise we are done and fall through to
607266Sbill 			 * finish up.
608266Sbill 			 */
609266Sbill 			if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
610266Sbill 				return;
611266Sbill 			/*
612266Sbill 			 * Clear the drive and, every 4 retries, recalibrate
613266Sbill 			 * to hopefully help clear up seek positioning problems.
614266Sbill 			 */
615264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
616264Sbill 			DELAY(idelay);
617268Sbill 			needie = 0;
618266Sbill 			if ((uptab.b_errcnt&07) == 4) {
619264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
620264Sbill 				DELAY(idelay);
621264Sbill 				while(upaddr->upds & PIP)
622264Sbill 					DELAY(25);
623264Sbill 			}
624264Sbill 		}
625266Sbill 		/*
626266Sbill 		 * If we are still noted as active, then no
627266Sbill 		 * (further) retries are necessary.
628266Sbill 		 *
629266Sbill 		 * Make sure the correct unit is selected,
630266Sbill 		 * return it to centerline if necessary, and mark
631266Sbill 		 * this i/o complete, starting the next transfer
632266Sbill 		 * on this drive with the upustart routine (if any).
633266Sbill 		 */
634266Sbill 		if (uptab.b_active) {
635266Sbill 			if (uptab.b_errcnt >= 16) {
636266Sbill 				upaddr->upcs1 = RTC|GO|IE;
637264Sbill 				DELAY(idelay);
638266Sbill 				while (upaddr->upds & PIP)
639264Sbill 					DELAY(25);
640268Sbill 				needie = 0;
641264Sbill 			}
642264Sbill 			uptab.b_active = 0;
643264Sbill 			uptab.b_errcnt = 0;
644264Sbill 			uptab.b_actf = dp->b_forw;
645264Sbill 			dp->b_active = 0;
646264Sbill 			dp->b_errcnt = 0;
647264Sbill 			dp->b_actf = bp->av_forw;
648266Sbill 			bp->b_resid = (-upaddr->upwc * sizeof(short));
649275Sbill 			if (bp->b_resid)
650341Sbill 				printf("resid %d ds %o er? %o %o %o\n",
651341Sbill 				    bp->b_resid, upaddr->upds,
652275Sbill 				    upaddr->uper1, upaddr->uper2, upaddr->uper3);
653264Sbill 			iodone(bp);
654264Sbill 			if(dp->b_actf)
655268Sbill 				if (upustart(unit))
656268Sbill 					needie = 0;
657264Sbill 		}
658264Sbill 		as &= ~(1<<unit);
659341Sbill 		upsoftas &= ~(1<<unit);
660264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
661273Sbill 	} else {
662264Sbill 		if (upaddr->upcs1 & TRE) {
663264Sbill 			upaddr->upcs1 = TRE;
664264Sbill 			DELAY(idelay);
665264Sbill 		}
666264Sbill 	}
667266Sbill 	/*
668266Sbill 	 * If we have a unit with an outstanding SEARCH,
669266Sbill 	 * and the hardware indicates the unit requires attention,
670266Sbill 	 * the bring the drive to the ready queue.
671266Sbill 	 * Finally, if the controller is not transferring
672266Sbill 	 * start it if any drives are now ready to transfer.
673266Sbill 	 */
674341Sbill 	as |= upsoftas;
675341Sbill 	oupsoftas = upsoftas;
676341Sbill 	upsoftas = 0;
677266Sbill 	for (unit = 0; unit < NUP; unit++)
678341Sbill 		if ((as|oupsoftas) & (1<<unit)) {
679273Sbill 			if (as & (1<<unit)) {
680267Sbill 				upaddr->upas = 1<<unit;
681367Sbill 				if (olducode)
682367Sbill 					DELAY(oasdel);
683272Sbill 			}
684273Sbill 			if (upustart(unit))
685273Sbill 				needie = 0;
686273Sbill 		}
687266Sbill 	if (uptab.b_actf && uptab.b_active == 0)
688268Sbill 		if (upstart())
689268Sbill 			needie = 0;
690266Sbill out:
691275Sbill 	if (needie)
692266Sbill 		upaddr->upcs1 = IE;
693264Sbill }
694264Sbill 
695264Sbill upread(dev)
696264Sbill {
697264Sbill 
698264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
699264Sbill }
700264Sbill 
701264Sbill upwrite(dev)
702264Sbill {
703264Sbill 
704264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
705264Sbill }
706264Sbill 
707266Sbill /*
708266Sbill  * Correct an ECC error, and restart the i/o to complete
709266Sbill  * the transfer if necessary.  This is quite complicated because
710266Sbill  * the transfer may be going to an odd memory address base and/or
711266Sbill  * across a page boundary.
712266Sbill  */
713264Sbill upecc(up, bp)
714264Sbill register struct device *up;
715264Sbill register struct buf *bp;
716264Sbill {
717264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
718266Sbill 	register int i;
719264Sbill 	caddr_t addr;
720266Sbill 	int reg, bit, byte, npf, mask, o, cmd, ubaddr;
721264Sbill 	int bn, cn, tn, sn;
722264Sbill 
723264Sbill 	/*
724266Sbill 	 * Npf is the number of sectors transferred before the sector
725266Sbill 	 * containing the ECC error, and reg is the UBA register
726266Sbill 	 * mapping (the first part of) the transfer.
727266Sbill 	 * O is offset within a memory page of the first byte transferred.
728264Sbill 	 */
729266Sbill 	npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
730266Sbill 	reg = btop(up_ubinfo&0x3ffff) + npf;
731264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
732264Sbill 	printf("%D ", bp->b_blkno+npf);
733264Sbill 	prdev("ECC", bp->b_dev);
734264Sbill 	mask = up->upec2;
735264Sbill 	if (mask == 0) {
736266Sbill 		up->upof = FMT22;		/* == RTC ???? */
737264Sbill 		DELAY(idelay);
738264Sbill 		return (0);
739264Sbill 	}
740266Sbill 	/*
741266Sbill 	 * Flush the buffered data path, and compute the
742266Sbill 	 * byte and bit position of the error.  The variable i
743266Sbill 	 * is the byte offset in the transfer, the variable byte
744266Sbill 	 * is the offset from a page boundary in main memory.
745266Sbill 	 */
746266Sbill 	ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
747266Sbill 	i = up->upec1 - 1;		/* -1 makes 0 origin */
748266Sbill 	bit = i&07;
749266Sbill 	i = (i&~07)>>3;
750264Sbill 	byte = i + o;
751266Sbill 	/*
752266Sbill 	 * Correct while possible bits remain of mask.  Since mask
753266Sbill 	 * contains 11 bits, we continue while the bit offset is > -11.
754266Sbill 	 * Also watch out for end of this block and the end of the whole
755266Sbill 	 * transfer.
756266Sbill 	 */
757266Sbill 	while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
758266Sbill 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
759266Sbill 		    (byte & PGOFSET);
760266Sbill 		putmemc(addr, getmemc(addr)^(mask<<bit));
761266Sbill 		byte++;
762266Sbill 		i++;
763266Sbill 		bit -= 8;
764264Sbill 	}
765266Sbill 	uptab.b_active++;	/* Either complete or continuing... */
766264Sbill 	if (up->upwc == 0)
767264Sbill 		return (0);
768266Sbill 	/*
769266Sbill 	 * Have to continue the transfer... clear the drive,
770266Sbill 	 * and compute the position where the transfer is to continue.
771266Sbill 	 * We have completed npf+1 sectors of the transfer already;
772266Sbill 	 * restart at offset o of next sector (i.e. in UBA register reg+1).
773266Sbill 	 */
774266Sbill 	up->upcs1 = TRE|IE|DCLR|GO;
775264Sbill 	DELAY(idelay);
776264Sbill 	bn = dkblock(bp);
777264Sbill 	cn = bp->b_cylin;
778266Sbill 	sn = bn%(NSECT*NTRAC) + npf + 1;
779264Sbill 	tn = sn/NSECT;
780264Sbill 	sn %= NSECT;
781266Sbill 	cn += tn/NTRAC;
782266Sbill 	tn %= NTRAC;
783264Sbill 	up->updc = cn;
784266Sbill 	up->upda = (tn << 8) | sn;
785266Sbill 	ubaddr = (int)ptob(reg+1) + o;
786266Sbill 	up->upba = ubaddr;
787266Sbill 	cmd = (ubaddr >> 8) & 0x300;
788266Sbill 	cmd |= IE|GO|RCOM;
789266Sbill 	up->upcs1 = cmd;
790264Sbill 	return (1);
791264Sbill }
792286Sbill 
793286Sbill /*
794286Sbill  * Reset driver after UBA init.
795286Sbill  * Cancel software state of all pending transfers
796286Sbill  * and restart all units and the controller.
797286Sbill  */
798286Sbill upreset()
799286Sbill {
800286Sbill 	int unit;
801286Sbill 
802286Sbill 	printf(" up");
803286Sbill 	uptab.b_active = 0;
804286Sbill 	uptab.b_actf = uptab.b_actl = 0;
805286Sbill 	if (up_ubinfo) {
806286Sbill 		printf("<%d>", (up_ubinfo>>28)&0xf);
807286Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
808286Sbill 	}
809313Sbill 	UPADDR->upcs2 = CLR;		/* clear controller */
810313Sbill 	DELAY(idelay);
811286Sbill 	for (unit = 0; unit < NUP; unit++) {
812286Sbill 		uputab[unit].b_active = 0;
813286Sbill 		(void) upustart(unit);
814286Sbill 	}
815286Sbill 	(void) upstart();
816286Sbill }
817313Sbill 
818313Sbill /*
819313Sbill  * Wake up every second and if an interrupt is pending
820313Sbill  * but nothing has happened increment a counter.
821313Sbill  * If nothing happens for 20 seconds, reset the controller
822313Sbill  * and begin anew.
823313Sbill  */
824313Sbill upwatch()
825313Sbill {
826313Sbill 	int i;
827313Sbill 
828313Sbill 	timeout((caddr_t)upwatch, 0, HZ);
829313Sbill 	if (uptab.b_active == 0) {
830313Sbill 		for (i = 0; i < NUP; i++)
831313Sbill 			if (uputab[i].b_active)
832313Sbill 				goto active;
833313Sbill 		up_wticks = 0;		/* idling */
834313Sbill 		return;
835313Sbill 	}
836313Sbill active:
837313Sbill 	up_wticks++;
838313Sbill 	if (up_wticks >= 20) {
839313Sbill 		up_wticks = 0;
840313Sbill 		printf("LOST INTERRUPT RESET");
841313Sbill 		upreset();
842313Sbill 		printf("\n");
843313Sbill 	}
844313Sbill }
845