xref: /csrg-svn/sys/vax/uba/up.c (revision 272)
1*272Sbill int	trc = -1;
2*272Sbill int	trcw = 0;
3*272Sbill #define	D(i)	if (trc&(1<<i)) { if (trcw&(1<<i)) DELAY(1000); } else
4271Sbill int	csdel0 = 30;
5*272Sbill int	csdel1 = 0;
6*272Sbill int	csdel2 = 0;
7268Sbill int	asdel = 500;
8*272Sbill int	csdel3 = 100;
9*272Sbill int	softas;
10*272Sbill /*	10/14/12	3.8	06/19/80	*/
11264Sbill 
12264Sbill /*
13264Sbill  * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery.
14264Sbill  *
15266Sbill  * NB: This device is very sensitive: be aware that the code is the way
16266Sbill  *     it is for good reason and that there are delay loops here which may
17266Sbill  *     have to be lengthened if your processor is faster and which should
18266Sbill  *     probably be shortened if your processor is slower.
19266Sbill  *
20264Sbill  * This driver has been tested on a SC-11B Controller, configured
21264Sbill  * with the following internal switch settings:
22264Sbill  *	SW1-1	5/19 surfaces	(off, 19 surfaces on Ampex 9300)
23264Sbill  *	SW1-2	chksum enable	(off, checksum disabled)
24264Sbill  *	SW1-3	volume select	(off, 815 cylinders)
25264Sbill  *	SW1-4	sector select	(on, 32 sectors)
26264Sbill  *	SW1-5	unused		(off)
27264Sbill  *	SW1-6	port select	(on, single port)
28264Sbill  *	SW1-7	npr delay	(off, disable)
29264Sbill  *	SW1-8	ecc test mode	(off, disable)
30264Sbill  * and top mounted switches:
31264Sbill  *	SW2-1	extend opcodes	(off=open, disable)
32264Sbill  *	SW2-2	extend diag	(off=open, disable)
33264Sbill  *	SW2-3	4 wd dma burst	(off=open, disable)
34264Sbill  *	SW2-4	unused		(off=open)
35264Sbill  *
36264Sbill  * The controller transfers data much more rapidly with SW2-3 set,
37264Sbill  * but we have previously experienced problems with it set this way.
38264Sbill  * We intend to try this again in the near future.
39264Sbill  *
40264Sbill  *	wnj	June 14, 1980
41264Sbill  */
42264Sbill 
43264Sbill #include "../h/param.h"
44264Sbill #include "../h/systm.h"
45264Sbill #include "../h/buf.h"
46264Sbill #include "../h/conf.h"
47264Sbill #include "../h/dir.h"
48264Sbill #include "../h/user.h"
49264Sbill #include "../h/map.h"
50264Sbill #include "../h/mba.h"
51264Sbill #include "../h/mtpr.h"
52264Sbill #include "../h/pte.h"
53264Sbill #include "../h/uba.h"
54264Sbill #include "../h/vm.h"
55264Sbill 
56264Sbill /*
57264Sbill  * Define number of drives, and range of sampling information to be used.
58264Sbill  *
59264Sbill  * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats,
60264Sbill  * and DK_N+NUP gathers controller transferring stats.
61264Sbill  *
62264Sbill  * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
63264Sbill  * If DK_NMAX is yet smaller, some drives are not monitored.
64264Sbill  */
65264Sbill #define	DK_N	1
66264Sbill #define	DK_NMAX	2
67264Sbill 
68264Sbill #define	ushort	unsigned short
69264Sbill 
70264Sbill struct	device
71264Sbill {
72264Sbill 	ushort	upcs1;		/* control and status register 1 */
73264Sbill 	short	upwc;		/* word count register */
74264Sbill 	ushort	upba;		/* UNIBUS address register */
75264Sbill 	ushort	upda;		/* desired address register */
76264Sbill 	ushort	upcs2;		/* control and status register 2 */
77264Sbill 	ushort	upds;		/* drive Status */
78264Sbill 	ushort	uper1;		/* error register 1 */
79264Sbill 	ushort	upas;		/* attention summary */
80264Sbill 	ushort	upla;		/* look ahead */
81264Sbill 	ushort	updb;		/* data buffer */
82264Sbill 	ushort	upmr;		/* maintenance */
83264Sbill 	ushort	updt;		/* drive type */
84264Sbill 	ushort	upsn;		/* serial number */
85264Sbill 	ushort	upof;		/* offset register */
86264Sbill 	ushort	updc;		/* desired cylinder address register */
87264Sbill 	ushort	upcc;		/* current cylinder */
88264Sbill 	ushort	uper2;		/* error register 2 */
89264Sbill 	ushort	uper3;		/* error register 3 */
90264Sbill 	ushort	upec1;		/* burst error bit position */
91264Sbill 	ushort	upec2;		/* burst error bit pattern */
92264Sbill };
93264Sbill 
94264Sbill #define	UPADDR	((struct device *)(UBA0_DEV + 0176700))
95264Sbill 
96264Sbill #define	NUP	2		/* Number of drives this installation */
97264Sbill 
98264Sbill #define	NSECT	32
99264Sbill #define	NTRAC	19
100264Sbill 
101264Sbill /*
102264Sbill  * Constants controlling on-cylinder SEARCH usage.
103264Sbill  *
104264Sbill  * We assume that it takes SDIST sectors of time to set up a transfer.
105264Sbill  * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors
106264Sbill  * from the first sector to be transferred, then we just perform the
107264Sbill  * transfer.  SDIST represents interrupt latency, RDIST the amount
108264Sbill  * of rotation which is tolerable to avoid another interrupt.
109264Sbill  */
110266Sbill #define	SDIST	3		/* 2-3 sectors 1-1.5 msec */
111266Sbill #define	RDIST	6		/* 5-6 sectors 2.5-3 msec */
112264Sbill 
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 */
129264Sbill 	494912,	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  *
145264Sbill  * ARE ALL THESE 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 */
184264Sbill #define	RECAL	06		/* Recalibrate, needed after seek error */
185264Sbill #define	DCLR	010		/* Drive clear, after error */
186264Sbill #define	WCOM	060		/* Write */
187264Sbill #define	RCOM	070		/* Read */
188264Sbill 
189264Sbill /* Other bits of upcs1 */
190264Sbill #define	IE	0100		/* Controller wide interrupt enable */
191264Sbill #define	TRE	040000		/* Transfer error */
192266Sbill #define	RDY	020		/* Transfer terminated */
193264Sbill 
194264Sbill /* Drive status bits of upds */
195264Sbill #define	PIP	020000		/* Positioning in progress */
196264Sbill #define	ERR	040000		/* Error has occurred, DCLR necessary */
197264Sbill #define	VV	0100		/* Volume is valid, set by PRESET */
198264Sbill #define	DPR	0400		/* Drive has been preset */
199264Sbill #define	MOL	010000		/* Drive is online, heads loaded, etc */
200264Sbill #define	DRY	0200		/* Drive ready */
201264Sbill 
202264Sbill /* Bits of uper1 */
203264Sbill #define	DCK	0100000		/* Ecc error occurred */
204264Sbill #define	ECH	0100		/* Ecc error was unrecoverable */
205264Sbill #define	WLE	04000		/* Attempt to write read-only drive */
206264Sbill 
207264Sbill /* Bits of upof; the offset bits above are also in this register */
208264Sbill #define	FMT22	010000		/* 16 bits/word, must be always set */
209264Sbill 
210264Sbill #define	b_cylin b_resid
211264Sbill 
212264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
213264Sbill /*
214264Sbill  * The EMULEX controller balks if accessed quickly after
215264Sbill  * certain operations.  The exact timing has not yet been
216264Sbill  * determined, but delays are known to be needed when changing
217264Sbill  * the selected drive (by writing in upcs2), and thought to be
218264Sbill  * needed after operations like PRESET and DCLR.  The following
219264Sbill  * variables control the delay, DELAY(n) is approximately n usec.
220264Sbill  */
221264Sbill int	idelay = 500;		/* Delay after PRESET or DCLR */
222268Sbill int	sdelay = 150;		/* Delay after selecting drive in upcs2 */
223264Sbill 
224264Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
225264Sbill 
226264Sbill int	nwaitcs2;		/* How many sdelay loops ? */
227264Sbill int	neasycs2;		/* How many sdelay loops not needed ? */
228264Sbill 
229264Sbill #ifdef INTRLVE
230264Sbill daddr_t dkblock();
231264Sbill #endif
232264Sbill 
233264Sbill /*
234264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
235264Sbill  *
236264Sbill  * A unit start is issued if the drive is inactive, causing
237264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
238264Sbill  * already nearly on the money and the controller is not transferring
239264Sbill  * we kick it to start the transfer.
240264Sbill  */
241264Sbill upstrategy(bp)
242264Sbill register struct buf *bp;
243264Sbill {
244264Sbill 	register struct buf *dp;
245264Sbill 	register unit, xunit;
246264Sbill 	long sz, bn;
247264Sbill 
248264Sbill 	xunit = minor(bp->b_dev) & 077;
249264Sbill 	sz = bp->b_bcount;
250264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
251264Sbill 	unit = dkunit(bp);
252264Sbill 	if (unit >= NUP ||
253264Sbill 	    bp->b_blkno < 0 ||
254264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
255264Sbill 		bp->b_flags |= B_ERROR;
256264Sbill 		iodone(bp);
257264Sbill 		return;
258264Sbill 	}
259264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
260264Sbill 	dp = &uputab[unit];
261264Sbill 	(void) spl5();
262264Sbill 	disksort(dp, bp);
263*272Sbill #ifdef UTRACE
264*272Sbill D(1)	ttime();
265*272Sbill D(2)	trace("upstrat bn %d unit %d\n", bp->b_blkno, unit);
266*272Sbill #endif
267264Sbill 	if (dp->b_active == 0) {
268268Sbill 		(void) upustart(unit);
269264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
270268Sbill 			(void) upstart();
271264Sbill 	}
272264Sbill 	(void) spl0();
273264Sbill }
274264Sbill 
275264Sbill /*
276264Sbill  * Start activity on specified drive; called when drive is inactive
277264Sbill  * and new transfer request arrives and also when upas indicates that
278264Sbill  * a SEARCH command is complete.
279264Sbill  */
280264Sbill upustart(unit)
281264Sbill register unit;
282264Sbill {
283264Sbill 	register struct buf *bp, *dp;
284264Sbill 	register struct device *upaddr = UPADDR;
285264Sbill 	daddr_t bn;
286264Sbill 	int sn, cn, csn;
287268Sbill 	int didie = 0;
288264Sbill 
289266Sbill 	if (unit >= NUP)
290268Sbill 		goto out;
291*272Sbill 	if (uptab.b_active) {
292*272Sbill 		softas |= 1<<unit;
293*272Sbill 		return;
294*272Sbill 	}
295*272Sbill #ifdef UTRACE
296*272Sbill D(3)	ttime();
297*272Sbill D(4)	trace("upustart %d active %d", unit, uputab[unit].b_active);
298*272Sbill #endif
299266Sbill 	/*
300266Sbill 	 * Whether or not it was before, this unit is no longer busy.
301266Sbill 	 * Check to see if there is (still or now) a request in this
302266Sbill 	 * drives queue, and if there is, select this unit.
303266Sbill 	 */
304264Sbill 	if (unit+DK_N <= DK_NMAX)
305264Sbill 		dk_busy &= ~(1<<(unit+DK_N));
306264Sbill 	dp = &uputab[unit];
307266Sbill 	if ((bp = dp->b_actf) == NULL)
308268Sbill 		goto out;
309264Sbill 	if ((upaddr->upcs2 & 07) != unit) {
310264Sbill 		upaddr->upcs2 = unit;
311264Sbill 		DELAY(sdelay);
312264Sbill 		nwaitcs2++;
313264Sbill 	} else
314264Sbill 		neasycs2++;
315266Sbill 	/*
316266Sbill 	 * If we have changed packs or just initialized,
317266Sbill 	 * the the volume will not be valid; if so, clear
318266Sbill 	 * the drive, preset it and put in 16bit/word mode.
319266Sbill 	 */
320266Sbill 	if ((upaddr->upds & VV) == 0) {
321*272Sbill #ifdef UTRACE
322*272Sbill D(5)		trace(" not VV");
323*272Sbill #endif
324266Sbill 		upaddr->upcs1 = IE|DCLR|GO;
325266Sbill 		DELAY(idelay);
326264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
327264Sbill 		DELAY(idelay);
328264Sbill 		upaddr->upof = FMT22;
329268Sbill 		didie = 1;
330264Sbill 	}
331264Sbill 	/*
332266Sbill 	 * We are called from upstrategy when a new request arrives
333266Sbill 	 * if we are not already active (with dp->b_active == 0),
334266Sbill 	 * and we then set dp->b_active to 1 if we are to SEARCH
335266Sbill 	 * for the desired cylinder, or 2 if we are on-cylinder.
336266Sbill 	 * If we SEARCH then we will later be called from upintr()
337266Sbill 	 * when the search is complete, and will link this disk onto
338266Sbill 	 * the uptab.  We then set dp->b_active to 2 so that upintr()
339266Sbill 	 * will not call us again.
340266Sbill 	 *
341266Sbill 	 * NB: Other drives clear the bit in the attention status
342266Sbill 	 * (i.e. upas) register corresponding to the drive when they
343266Sbill 	 * place the drive on the ready (i.e. uptab) queue.  This does
344266Sbill 	 * not work with the Emulex, as the controller hangs the UBA
345266Sbill 	 * of the VAX shortly after the upas register is set, for
346266Sbill 	 * reasons unknown.  This only occurs in multi-spindle configurations,
347266Sbill 	 * but to avoid the problem we use the fact that dp->b_active is
348266Sbill 	 * 2 to replace the clearing of the upas bit.
349264Sbill 	 */
350266Sbill 	if (dp->b_active)
351264Sbill 		goto done;
352266Sbill 	dp->b_active = 1;
353264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
354266Sbill 		goto done;	/* Will redetect error in upstart() soon */
355264Sbill 
356266Sbill 	/*
357266Sbill 	 * Do enough of the disk address decoding to determine
358266Sbill 	 * which cylinder and sector the request is on.
359266Sbill 	 * Then compute the number of the sector SDIST sectors before
360266Sbill 	 * the one where the transfer is to start, this being the
361266Sbill 	 * point where we wish to attempt to begin the transfer,
362266Sbill 	 * allowing approximately SDIST/2 msec for interrupt latency
363266Sbill 	 * and preparation of the request.
364266Sbill 	 *
365266Sbill 	 * If we are on the correct cylinder and the desired sector
366266Sbill 	 * lies between SDIST and SDIST+RDIST sectors ahead of us, then
367266Sbill 	 * we don't bother to SEARCH but just begin the transfer asap.
368266Sbill 	 */
369264Sbill 	bn = dkblock(bp);
370264Sbill 	cn = bp->b_cylin;
371264Sbill 	sn = bn%(NSECT*NTRAC);
372264Sbill 	sn = (sn+NSECT-SDIST)%NSECT;
373264Sbill 
374266Sbill 	if (cn - upaddr->updc)
375266Sbill 		goto search;		/* Not on-cylinder */
376264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
377266Sbill 	if (csn < 0)
378264Sbill 		csn += NSECT;
379266Sbill 	if (csn > NSECT-RDIST)
380264Sbill 		goto done;
381264Sbill 
382264Sbill search:
383*272Sbill #ifdef UTRACE
384*272Sbill D(6)	trace(" search %d@%d to %d@%d", upaddr->updc, (upaddr->upla>>6),
385*272Sbill 	    cn, sn);
386*272Sbill #endif
387264Sbill 	upaddr->updc = cn;
388264Sbill 	upaddr->upda = sn;
389264Sbill 	upaddr->upcs1 = IE|SEARCH|GO;
390268Sbill 	didie = 1;
391266Sbill 	/*
392266Sbill 	 * Mark this unit busy.
393266Sbill 	 */
394264Sbill 	unit += DK_N;
395264Sbill 	if (unit <= DK_NMAX) {
396264Sbill 		dk_busy |= 1<<unit;
397264Sbill 		dk_numb[unit]++;
398264Sbill 	}
399270Sbill 	if (csdel0) DELAY(csdel0);
400268Sbill 	goto out;
401264Sbill 
402264Sbill done:
403266Sbill 	/*
404266Sbill 	 * This unit is ready to go.  Make active == 2 so
405266Sbill 	 * we won't get called again (by upintr() because upas&(1<<unit))
406266Sbill 	 * and link us onto the chain of ready disks.
407266Sbill 	 */
408*272Sbill #ifdef UTRACE
409*272Sbill D(7)	trace(" done");
410*272Sbill #endif
411266Sbill 	dp->b_active = 2;
412264Sbill 	dp->b_forw = NULL;
413266Sbill 	if (uptab.b_actf == NULL)
414264Sbill 		uptab.b_actf = dp;
415264Sbill 	else
416264Sbill 		uptab.b_actl->b_forw = dp;
417264Sbill 	uptab.b_actl = dp;
418268Sbill 
419268Sbill out:
420*272Sbill 	if (csdel1) DELAY(csdel1);
421*272Sbill #ifdef UTRACE
422*272Sbill D(8)	trace("\n");
423*272Sbill #endif
424268Sbill 	return (didie);
425264Sbill }
426264Sbill 
427264Sbill /*
428264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
429264Sbill  */
430264Sbill upstart()
431264Sbill {
432264Sbill 	register struct buf *bp, *dp;
433264Sbill 	register unit;
434264Sbill 	register struct device *upaddr;
435264Sbill 	daddr_t bn;
436266Sbill 	int dn, sn, tn, cn, cmd;
437264Sbill 
438264Sbill loop:
439*272Sbill 	if (csdel2) DELAY(csdel2);
440*272Sbill #ifdef UTRACE
441*272Sbill D(9)	ttime();
442*272Sbill D(10)	trace("upstart");
443*272Sbill #endif
444266Sbill 	/*
445266Sbill 	 * Pick a drive off the queue of ready drives, and
446266Sbill 	 * perform the first transfer on its queue.
447266Sbill 	 *
448266Sbill 	 * Looping here is completely for the sake of drives which
449266Sbill 	 * are not present and on-line, for which we completely clear the
450266Sbill 	 * request queue.
451266Sbill 	 */
452269Sbill 	if ((dp = uptab.b_actf) == NULL) {
453*272Sbill #ifdef UTRACE
454*272Sbill D(11)		trace("\n");
455*272Sbill #endif
456268Sbill 		return (0);
457269Sbill 	}
458264Sbill 	if ((bp = dp->b_actf) == NULL) {
459264Sbill 		uptab.b_actf = dp->b_forw;
460264Sbill 		goto loop;
461264Sbill 	}
462266Sbill 	/*
463266Sbill 	 * Mark the controller busy, and multi-part disk address.
464266Sbill 	 * Select the unit on which the i/o is to take place.
465266Sbill 	 */
466264Sbill 	uptab.b_active++;
467264Sbill 	unit = minor(bp->b_dev) & 077;
468264Sbill 	dn = dkunit(bp);
469264Sbill 	bn = dkblock(bp);
470264Sbill 	cn = up_sizes[unit&07].cyloff;
471264Sbill 	cn += bn/(NSECT*NTRAC);
472264Sbill 	sn = bn%(NSECT*NTRAC);
473264Sbill 	tn = sn/NSECT;
474266Sbill 	sn %= NSECT;
475264Sbill 	upaddr = UPADDR;
476*272Sbill #ifdef UTRACE
477*272Sbill D(12)	trace(" unit %d", dn);
478*272Sbill #endif
479264Sbill 	if ((upaddr->upcs2 & 07) != dn) {
480*272Sbill #ifdef UTRACE
481*272Sbill D(13)		trace(" select");
482*272Sbill #endif
483264Sbill 		upaddr->upcs2 = dn;
484264Sbill 		DELAY(sdelay);
485264Sbill 		nwaitcs2++;
486264Sbill 	} else
487264Sbill 		neasycs2++;
488266Sbill 	up_ubinfo = ubasetup(bp, 1);	/* In a funny place for delay... */
489266Sbill 	/*
490266Sbill 	 * If drive is not present and on-line, then
491266Sbill 	 * get rid of this with an error and loop to get
492266Sbill 	 * rid of the rest of its queued requests.
493266Sbill 	 * (Then on to any other ready drives.)
494266Sbill 	 */
495264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
496*272Sbill #ifdef UTRACE
497*272Sbill D(14)		trace(" !(DPR && MOL)");
498*272Sbill #endif
499264Sbill 		uptab.b_active = 0;
500264Sbill 		uptab.b_errcnt = 0;
501264Sbill 		dp->b_actf = bp->av_forw;
502266Sbill 		dp->b_active = 0;
503264Sbill 		bp->b_flags |= B_ERROR;
504264Sbill 		iodone(bp);
505266Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;	/* A funny place ... */
506264Sbill 		goto loop;
507264Sbill 	}
508266Sbill 	/*
509266Sbill 	 * If this is a retry, then with the 16'th retry we
510266Sbill 	 * begin to try offsetting the heads to recover the data.
511266Sbill 	 */
512266Sbill 	if (uptab.b_errcnt >= 16) {
513*272Sbill #ifdef UTRACE
514*272Sbill D(15)		trace(" offset");
515*272Sbill #endif
516264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
517266Sbill 		upaddr->upcs1 = IE|OFFSET|GO;
518264Sbill 		DELAY(idelay);
519266Sbill 		while (upaddr->upds & PIP)
520264Sbill 			DELAY(25);
521264Sbill 	}
522266Sbill 	/*
523266Sbill 	 * Now set up the transfer, retrieving the high
524266Sbill 	 * 2 bits of the UNIBUS address from the information
525266Sbill 	 * returned by ubasetup() for the cs1 register bits 8 and 9.
526266Sbill 	 */
527*272Sbill #ifdef UTRACE
528*272Sbill D(16)	trace(" %s %d.%d@%d cnt %d ba %x\n",
529*272Sbill 	    (bp->b_flags&B_READ) ? "read" : "write",
530*272Sbill 	    cn, tn, sn, bp->b_bcount, up_ubinfo & 0x3ffff);
531*272Sbill #endif
532264Sbill 	upaddr->updc = cn;
533264Sbill 	upaddr->upda = (tn << 8) + sn;
534264Sbill 	upaddr->upba = up_ubinfo;
535264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
536266Sbill 	cmd = (up_ubinfo >> 8) & 0x300;
537264Sbill 	if (bp->b_flags & B_READ)
538266Sbill 		cmd |= IE|RCOM|GO;
539264Sbill 	else
540266Sbill 		cmd |= IE|WCOM|GO;
541266Sbill 	upaddr->upcs1 = cmd;
542*272Sbill #ifdef notdef
543*272Sbill 	if (csdel3) DELAY(csdel3);
544*272Sbill #endif
545266Sbill 	/*
546266Sbill 	 * This is a controller busy situation.
547266Sbill 	 * Record in dk slot NUP+DK_N (after last drive)
548266Sbill 	 * unless there aren't that many slots reserved for
549266Sbill 	 * us in which case we record this as a drive busy
550266Sbill 	 * (if there is room for that).
551266Sbill 	 */
552264Sbill 	unit = dn+DK_N;
553264Sbill 	if (NUP+DK_N == DK_NMAX)
554264Sbill 		unit = NUP+DK_N;
555264Sbill 	if (unit <= DK_NMAX) {
556264Sbill 		dk_busy |= 1<<unit;
557264Sbill 		dk_numb[unit]++;
558264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
559264Sbill 	}
560268Sbill 	return (1);
561264Sbill }
562264Sbill 
563264Sbill /*
564264Sbill  * Handle a device interrupt.
565264Sbill  *
566264Sbill  * If the transferring drive needs attention, service it
567264Sbill  * retrying on error or beginning next transfer.
568264Sbill  * Service all other ready drives, calling ustart to transfer
569264Sbill  * their blocks to the ready queue in uptab, and then restart
570264Sbill  * the controller if there is anything to do.
571264Sbill  */
572264Sbill upintr()
573264Sbill {
574264Sbill 	register struct buf *bp, *dp;
575264Sbill 	register unit;
576264Sbill 	register struct device *upaddr = UPADDR;
577264Sbill 	int as = upaddr->upas & 0377;
578*272Sbill 	int osoftas;
579268Sbill 	int needie = 1;
580264Sbill 
581*272Sbill #ifdef UTRACE
582*272Sbill D(17)	ttime();
583*272Sbill D(18)	trace("upintr as %d act %d %d %d;", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active);
584*272Sbill #endif
585266Sbill 	if (uptab.b_active) {
586266Sbill 		/*
587266Sbill 		 * The drive is transferring, thus the hardware
588266Sbill 		 * (say the designers) will only interrupt when the transfer
589266Sbill 		 * completes; check for it anyways.
590266Sbill 		 */
591266Sbill 		if ((upaddr->upcs1 & RDY) == 0) {
592*272Sbill #ifdef UTRACE
593*272Sbill D(19)			trace(" !RDY");
594*272Sbill #endif
595*272Sbill 			printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
596*272Sbill 			    upaddr->upds, upaddr->upwc);
597267Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active);
598269Sbill 		}
599266Sbill 		/*
600266Sbill 		 * Mark controller or drive not busy, and check for an
601266Sbill 		 * error condition which may have resulted from the transfer.
602266Sbill 		 */
603264Sbill 		dp = uptab.b_actf;
604264Sbill 		bp = dp->b_actf;
605264Sbill 		unit = dkunit(bp);
606264Sbill 		if (DK_N+NUP == DK_NMAX)
607264Sbill 			dk_busy &= ~(1<<(DK_N+NUP));
608264Sbill 		else if (DK_N+unit <= DK_NMAX)
609264Sbill 			dk_busy &= ~(1<<(DK_N+unit));
610264Sbill 		if (upaddr->upcs1 & TRE) {
611*272Sbill #ifdef UTRACE
612*272Sbill D(20)			trace(" TRE");
613*272Sbill #endif
614266Sbill 			/*
615266Sbill 			 * An error occurred, indeed.  Select this unit
616266Sbill 			 * to get at the drive status (a SEARCH may have
617266Sbill 			 * intervened to change the selected unit), and
618266Sbill 			 * wait for the command which caused the interrupt
619266Sbill 			 * to complete (DRY).
620266Sbill 			 *
621266Sbill 			 * WHY IS THE WAIT NECESSARY?
622266Sbill 			 */
623264Sbill 			if ((upaddr->upcs2 & 07) != unit) {
624264Sbill 				upaddr->upcs2 = unit;
625264Sbill 				DELAY(sdelay);
626264Sbill 				nwaitcs2++;
627264Sbill 			} else
628264Sbill 				neasycs2++;
629266Sbill 			while ((upaddr->upds & DRY) == 0)
630264Sbill 				DELAY(25);
631266Sbill 			/*
632266Sbill 			 * After 28 retries (16 w/o servo offsets, and then
633266Sbill 			 * 12 with servo offsets), or if we encountered
634266Sbill 			 * an error because the drive is write-protected,
635266Sbill 			 * give up.  Print an error message on the last 2
636266Sbill 			 * retries before a hard failure.
637266Sbill 			 */
638266Sbill 			if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
639264Sbill 				bp->b_flags |= B_ERROR;
640264Sbill 			else
641266Sbill 				uptab.b_active = 0;	/* To force retry */
642266Sbill 			if (uptab.b_errcnt > 27)
643264Sbill 				deverror(bp, upaddr->upcs2, upaddr->uper1);
644266Sbill 			/*
645266Sbill 			 * If this was a correctible ECC error, let upecc
646266Sbill 			 * do the dirty work to correct it.  If upecc
647266Sbill 			 * starts another READ for the rest of the data
648266Sbill 			 * then it returns 1 (having set uptab.b_active).
649266Sbill 			 * Otherwise we are done and fall through to
650266Sbill 			 * finish up.
651266Sbill 			 */
652266Sbill 			if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
653266Sbill 				return;
654266Sbill 			/*
655266Sbill 			 * Clear the drive and, every 4 retries, recalibrate
656266Sbill 			 * to hopefully help clear up seek positioning problems.
657266Sbill 			 */
658264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
659264Sbill 			DELAY(idelay);
660268Sbill 			needie = 0;
661266Sbill 			if ((uptab.b_errcnt&07) == 4) {
662264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
663264Sbill 				DELAY(idelay);
664264Sbill 				while(upaddr->upds & PIP)
665264Sbill 					DELAY(25);
666264Sbill 			}
667264Sbill 		}
668266Sbill 		/*
669266Sbill 		 * If we are still noted as active, then no
670266Sbill 		 * (further) retries are necessary.
671266Sbill 		 *
672266Sbill 		 * Make sure the correct unit is selected,
673266Sbill 		 * return it to centerline if necessary, and mark
674266Sbill 		 * this i/o complete, starting the next transfer
675266Sbill 		 * on this drive with the upustart routine (if any).
676266Sbill 		 */
677266Sbill 		if (uptab.b_active) {
678*272Sbill #ifdef UTRACE
679*272Sbill D(21)			trace(" unit %d", unit);
680*272Sbill #endif
681266Sbill 			if ((upaddr->upcs2 & 07) != unit) {
682*272Sbill #ifdef UTRACE
683*272Sbill D(22)				trace(" select");
684*272Sbill #endif
685266Sbill 				upaddr->upcs2 = unit;
686266Sbill 				DELAY(sdelay);
687266Sbill 				nwaitcs2++;
688266Sbill 			} else
689266Sbill 				neasycs2++;
690266Sbill 			if (uptab.b_errcnt >= 16) {
691*272Sbill #ifdef UTRACE
692*272Sbill D(23)				trace(" rtc");
693*272Sbill #endif
694266Sbill 				upaddr->upcs1 = RTC|GO|IE;
695264Sbill 				DELAY(idelay);
696266Sbill 				while (upaddr->upds & PIP)
697264Sbill 					DELAY(25);
698268Sbill 				needie = 0;
699264Sbill 			}
700264Sbill 			uptab.b_active = 0;
701264Sbill 			uptab.b_errcnt = 0;
702264Sbill 			uptab.b_actf = dp->b_forw;
703264Sbill 			dp->b_active = 0;
704264Sbill 			dp->b_errcnt = 0;
705264Sbill 			dp->b_actf = bp->av_forw;
706266Sbill 			bp->b_resid = (-upaddr->upwc * sizeof(short));
707264Sbill 			iodone(bp);
708264Sbill 			if(dp->b_actf)
709268Sbill 				if (upustart(unit))
710268Sbill 					needie = 0;
711264Sbill 		}
712264Sbill 		as &= ~(1<<unit);
713*272Sbill 		softas &= ~(1<<unit);
714264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
715266Sbill 	}
716266Sbill #ifndef notdef
717266Sbill 	else {
718264Sbill 		if (upaddr->upcs1 & TRE) {
719*272Sbill #ifdef UTRACE
720*272Sbill D(24)			trace(" TRE");
721*272Sbill #endif
722264Sbill 			upaddr->upcs1 = TRE;
723264Sbill 			DELAY(idelay);
724264Sbill 		}
725264Sbill 	}
726266Sbill #endif
727266Sbill 	/*
728266Sbill 	 * If we have a unit with an outstanding SEARCH,
729266Sbill 	 * and the hardware indicates the unit requires attention,
730266Sbill 	 * the bring the drive to the ready queue.
731266Sbill 	 * Finally, if the controller is not transferring
732266Sbill 	 * start it if any drives are now ready to transfer.
733266Sbill 	 */
734*272Sbill #ifdef UTRACE
735*272Sbill D(25)	trace("\n");
736*272Sbill #endif
737*272Sbill 	as |= softas;
738*272Sbill 	osoftas = softas;
739*272Sbill 	softas = 0;
740266Sbill 	for (unit = 0; unit < NUP; unit++)
741*272Sbill 		if ((as|osoftas) & (1<<unit))
742*272Sbill /*
743267Sbill 			if (uputab[unit].b_active == 1) {
744*272Sbill */
745*272Sbill 			{
746*272Sbill 				if (as & (1<<unit)) {
747267Sbill 				upaddr->upas = 1<<unit;
748*272Sbill #ifdef UTRACE
749*272Sbill D(26)				trace("as clear %d\n", unit);
750*272Sbill #endif
751268Sbill 				if (asdel) DELAY(asdel);
752*272Sbill 				}
753268Sbill 				if (upustart(unit))
754268Sbill 					needie = 0;
755*272Sbill 			}
756*272Sbill /*
757267Sbill 			} else {
758266Sbill 				upaddr->upas = 1<<unit;
759*272Sbill #ifdef UTRACE
760*272Sbill D(27)				trace("spurious as clear %d\n", unit);
761*272Sbill #endif
762266Sbill 				DELAY(1000);
763266Sbill 			}
764*272Sbill */
765266Sbill 	if (uptab.b_actf && uptab.b_active == 0)
766268Sbill 		if (upstart())
767268Sbill 			needie = 0;
768266Sbill out:
769269Sbill 	if (needie) {
770*272Sbill #ifdef UTRACE
771*272Sbill D(28)		trace("upintr set IE\n");
772*272Sbill #endif
773266Sbill 		upaddr->upcs1 = IE;
774269Sbill 	}
775264Sbill }
776264Sbill 
777264Sbill upread(dev)
778264Sbill {
779264Sbill 
780264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
781264Sbill }
782264Sbill 
783264Sbill upwrite(dev)
784264Sbill {
785264Sbill 
786264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
787264Sbill }
788264Sbill 
789266Sbill /*
790266Sbill  * Correct an ECC error, and restart the i/o to complete
791266Sbill  * the transfer if necessary.  This is quite complicated because
792266Sbill  * the transfer may be going to an odd memory address base and/or
793266Sbill  * across a page boundary.
794266Sbill  */
795264Sbill upecc(up, bp)
796264Sbill register struct device *up;
797264Sbill register struct buf *bp;
798264Sbill {
799264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
800266Sbill 	register int i;
801264Sbill 	caddr_t addr;
802266Sbill 	int reg, bit, byte, npf, mask, o, cmd, ubaddr;
803264Sbill 	int bn, cn, tn, sn;
804264Sbill 
805264Sbill 	/*
806266Sbill 	 * Npf is the number of sectors transferred before the sector
807266Sbill 	 * containing the ECC error, and reg is the UBA register
808266Sbill 	 * mapping (the first part of) the transfer.
809266Sbill 	 * O is offset within a memory page of the first byte transferred.
810264Sbill 	 */
811266Sbill 	npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
812266Sbill 	reg = btop(up_ubinfo&0x3ffff) + npf;
813264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
814264Sbill 	printf("%D ", bp->b_blkno+npf);
815264Sbill 	prdev("ECC", bp->b_dev);
816264Sbill 	mask = up->upec2;
817264Sbill 	if (mask == 0) {
818266Sbill 		up->upof = FMT22;		/* == RTC ???? */
819264Sbill 		DELAY(idelay);
820264Sbill 		return (0);
821264Sbill 	}
822266Sbill 	/*
823266Sbill 	 * Flush the buffered data path, and compute the
824266Sbill 	 * byte and bit position of the error.  The variable i
825266Sbill 	 * is the byte offset in the transfer, the variable byte
826266Sbill 	 * is the offset from a page boundary in main memory.
827266Sbill 	 */
828266Sbill 	ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
829266Sbill 	i = up->upec1 - 1;		/* -1 makes 0 origin */
830266Sbill 	bit = i&07;
831266Sbill 	i = (i&~07)>>3;
832264Sbill 	byte = i + o;
833266Sbill 	/*
834266Sbill 	 * Correct while possible bits remain of mask.  Since mask
835266Sbill 	 * contains 11 bits, we continue while the bit offset is > -11.
836266Sbill 	 * Also watch out for end of this block and the end of the whole
837266Sbill 	 * transfer.
838266Sbill 	 */
839266Sbill 	while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
840266Sbill 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
841266Sbill 		    (byte & PGOFSET);
842266Sbill 		putmemc(addr, getmemc(addr)^(mask<<bit));
843266Sbill 		byte++;
844266Sbill 		i++;
845266Sbill 		bit -= 8;
846264Sbill 	}
847266Sbill 	uptab.b_active++;	/* Either complete or continuing... */
848264Sbill 	if (up->upwc == 0)
849264Sbill 		return (0);
850266Sbill 	/*
851266Sbill 	 * Have to continue the transfer... clear the drive,
852266Sbill 	 * and compute the position where the transfer is to continue.
853266Sbill 	 * We have completed npf+1 sectors of the transfer already;
854266Sbill 	 * restart at offset o of next sector (i.e. in UBA register reg+1).
855266Sbill 	 */
856266Sbill 	up->upcs1 = TRE|IE|DCLR|GO;
857264Sbill 	DELAY(idelay);
858264Sbill 	bn = dkblock(bp);
859264Sbill 	cn = bp->b_cylin;
860266Sbill 	sn = bn%(NSECT*NTRAC) + npf + 1;
861264Sbill 	tn = sn/NSECT;
862264Sbill 	sn %= NSECT;
863266Sbill 	cn += tn/NTRAC;
864266Sbill 	tn %= NTRAC;
865264Sbill 	up->updc = cn;
866266Sbill 	up->upda = (tn << 8) | sn;
867266Sbill 	ubaddr = (int)ptob(reg+1) + o;
868266Sbill 	up->upba = ubaddr;
869266Sbill 	cmd = (ubaddr >> 8) & 0x300;
870266Sbill 	cmd |= IE|GO|RCOM;
871266Sbill 	up->upcs1 = cmd;
872264Sbill 	return (1);
873264Sbill }
874