xref: /csrg-svn/sys/vax/uba/up.c (revision 313)
1*313Sbill /*	10/14/12	3.15	06/26/80	*/
2264Sbill 
3308Sbill #define	spl5	spl6		/* block clock, for delay loop's sake */
4264Sbill /*
5264Sbill  * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery.
6264Sbill  *
7266Sbill  * NB: This device is very sensitive: be aware that the code is the way
8266Sbill  *     it is for good reason and that there are delay loops here which may
9266Sbill  *     have to be lengthened if your processor is faster and which should
10266Sbill  *     probably be shortened if your processor is slower.
11266Sbill  *
12264Sbill  * This driver has been tested on a SC-11B Controller, configured
13264Sbill  * with the following internal switch settings:
14264Sbill  *	SW1-1	5/19 surfaces	(off, 19 surfaces on Ampex 9300)
15264Sbill  *	SW1-2	chksum enable	(off, checksum disabled)
16264Sbill  *	SW1-3	volume select	(off, 815 cylinders)
17264Sbill  *	SW1-4	sector select	(on, 32 sectors)
18264Sbill  *	SW1-5	unused		(off)
19264Sbill  *	SW1-6	port select	(on, single port)
20264Sbill  *	SW1-7	npr delay	(off, disable)
21264Sbill  *	SW1-8	ecc test mode	(off, disable)
22264Sbill  * and top mounted switches:
23264Sbill  *	SW2-1	extend opcodes	(off=open, disable)
24264Sbill  *	SW2-2	extend diag	(off=open, disable)
25264Sbill  *	SW2-3	4 wd dma burst	(off=open, disable)
26264Sbill  *	SW2-4	unused		(off=open)
27264Sbill  *
28264Sbill  * The controller transfers data much more rapidly with SW2-3 set,
29264Sbill  * but we have previously experienced problems with it set this way.
30264Sbill  * We intend to try this again in the near future.
31264Sbill  *
32308Sbill  * NB: OUR SYSTEM CURRENTLY GETS UBA ERRORS WHEN RUNNING THIS DRIVER
33308Sbill  *     AND THE BUS OCCASIONALLY HANGS, NECESSITATING THE DEVIE RESET
34308Sbill  *     CODE WHICH RE-INITS THE UNIBUS.  YECHHH.
35264Sbill  */
36264Sbill 
37264Sbill #include "../h/param.h"
38264Sbill #include "../h/systm.h"
39308Sbill #include "../h/dk.h"
40264Sbill #include "../h/buf.h"
41264Sbill #include "../h/conf.h"
42264Sbill #include "../h/dir.h"
43264Sbill #include "../h/user.h"
44264Sbill #include "../h/map.h"
45264Sbill #include "../h/mba.h"
46264Sbill #include "../h/mtpr.h"
47264Sbill #include "../h/pte.h"
48264Sbill #include "../h/uba.h"
49264Sbill #include "../h/vm.h"
50264Sbill 
51264Sbill /*
52264Sbill  * Define number of drives, and range of sampling information to be used.
53264Sbill  *
54264Sbill  * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats,
55264Sbill  * and DK_N+NUP gathers controller transferring stats.
56264Sbill  *
57264Sbill  * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
58264Sbill  * If DK_NMAX is yet smaller, some drives are not monitored.
59264Sbill  */
60308Sbill #define	DK_N	2
61308Sbill #define	DK_NMAX	3
62264Sbill 
63264Sbill #define	ushort	unsigned short
64264Sbill 
65264Sbill struct	device
66264Sbill {
67264Sbill 	ushort	upcs1;		/* control and status register 1 */
68264Sbill 	short	upwc;		/* word count register */
69264Sbill 	ushort	upba;		/* UNIBUS address register */
70264Sbill 	ushort	upda;		/* desired address register */
71264Sbill 	ushort	upcs2;		/* control and status register 2 */
72264Sbill 	ushort	upds;		/* drive Status */
73264Sbill 	ushort	uper1;		/* error register 1 */
74264Sbill 	ushort	upas;		/* attention summary */
75264Sbill 	ushort	upla;		/* look ahead */
76264Sbill 	ushort	updb;		/* data buffer */
77264Sbill 	ushort	upmr;		/* maintenance */
78264Sbill 	ushort	updt;		/* drive type */
79264Sbill 	ushort	upsn;		/* serial number */
80264Sbill 	ushort	upof;		/* offset register */
81264Sbill 	ushort	updc;		/* desired cylinder address register */
82264Sbill 	ushort	upcc;		/* current cylinder */
83264Sbill 	ushort	uper2;		/* error register 2 */
84264Sbill 	ushort	uper3;		/* error register 3 */
85264Sbill 	ushort	upec1;		/* burst error bit position */
86264Sbill 	ushort	upec2;		/* burst error bit pattern */
87264Sbill };
88264Sbill 
89275Sbill /*
90275Sbill  * Software extension to the upas register, so we can
91275Sbill  * postpone starting SEARCH commands until the controller
92275Sbill  * is not transferring.
93275Sbill  */
94275Sbill int	softas;
95275Sbill 
96275Sbill /*
97275Sbill  * If upseek then we don't issue SEARCH commands but rather just
98275Sbill  * settle for a SEEK to the correct cylinder.
99275Sbill  */
100275Sbill int	upseek;
101275Sbill 
102264Sbill #define	UPADDR	((struct device *)(UBA0_DEV + 0176700))
103264Sbill 
104264Sbill #define	NUP	2		/* Number of drives this installation */
105264Sbill 
106264Sbill #define	NSECT	32
107264Sbill #define	NTRAC	19
108264Sbill 
109264Sbill /*
110264Sbill  * Constants controlling on-cylinder SEARCH usage.
111264Sbill  *
112308Sbill  * 	upSDIST/2 msec		time needed to start transfer
113308Sbill  * 	upRDIST/2 msec		tolerable rotational latency when on-cylinder
114275Sbill  *
115308Sbill  * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
116275Sbill  * and in the driver then we take it as it is.  Otherwise we do a SEARCH
117308Sbill  * requesting an interrupt upSDIST sectors in advance.
118264Sbill  */
119308Sbill #define	_upSDIST	6		/* 3.0 msec */
120308Sbill #define	_upRDIST	6		/* 3.0 msec */
121264Sbill 
122308Sbill int	upSDIST = _upSDIST;
123308Sbill int	upRDIST = _upRDIST;
124275Sbill 
125264Sbill /*
126264Sbill  * To fill a 300M drive:
127264Sbill  *	A is designed to be used as a root.
128264Sbill  *	B is suitable for a swap area.
129264Sbill  *	H is the primary storage area.
130264Sbill  * On systems with RP06'es, we normally use only 291346 blocks of the H
131264Sbill  * area, and use DEF or G to cover the rest of the drive.  The C system
132264Sbill  * covers the whole drive and can be used for pack-pack copying.
133264Sbill  */
134264Sbill struct	size
135264Sbill {
136264Sbill 	daddr_t	nblocks;
137264Sbill 	int	cyloff;
138264Sbill } up_sizes[8] = {
139264Sbill 	15884,	0,		/* A=cyl 0 thru 26 */
140264Sbill 	33440,	27,		/* B=cyl 27 thru 81 */
141264Sbill 	494912,	0,		/* C=cyl 0 thru 814 */
142264Sbill 	15884,	562,		/* D=cyl 562 thru 588 */
143264Sbill 	55936,	589,		/* E=cyl 589 thru 680 */
144264Sbill 	81472,	681,		/* F=cyl 681 thru 814 */
145264Sbill 	153824,	562,		/* G=cyl 562 thru 814 */
146264Sbill 	445664,	82,		/* H=cyl 82 thru 814 */
147264Sbill /* Later, and more safely for H area...
148264Sbill 	291346,	82,		/* H=cyl 82 thru 561 */
149264Sbill };
150264Sbill 
151264Sbill /*
152264Sbill  * The following defines are used in offset positioning
153264Sbill  * when trying to recover disk errors, with the constants being
154264Sbill  * +/- microinches.  Note that header compare inhibit (HCI) is not
155264Sbill  * tried (this makes sense only during read, in any case.)
156264Sbill  *
157264Sbill  * ARE ALL THESE IMPLEMENTED ON 9300?
158264Sbill  */
159264Sbill #define	P400	020
160264Sbill #define	M400	0220
161264Sbill #define	P800	040
162264Sbill #define	M800	0240
163264Sbill #define	P1200	060
164264Sbill #define	M1200	0260
165264Sbill #define	HCI	020000
166264Sbill 
167264Sbill int	up_offset[16] =
168264Sbill {
169264Sbill 	P400, M400, P400, M400,
170264Sbill 	P800, M800, P800, M800,
171264Sbill 	P1200, M1200, P1200, M1200,
172264Sbill 	0, 0, 0, 0,
173264Sbill };
174264Sbill 
175264Sbill /*
176264Sbill  * Each drive has a table uputab[i].  On this table are sorted the
177264Sbill  * pending requests implementing an elevator algorithm (see dsort.c.)
178264Sbill  * In the upustart() routine, each drive is independently advanced
179264Sbill  * until it is on the desired cylinder for the next transfer and near
180264Sbill  * the desired sector.  The drive is then chained onto the uptab
181264Sbill  * table, and the transfer is initiated by the upstart() routine.
182264Sbill  * When the transfer is completed the driver reinvokes the upustart()
183264Sbill  * routine to set up the next transfer.
184264Sbill  */
185264Sbill struct	buf	uptab;
186264Sbill struct	buf	uputab[NUP];
187264Sbill 
188264Sbill struct	buf	rupbuf;			/* Buffer for raw i/o */
189264Sbill 
190264Sbill /* Drive commands, placed in upcs1 */
191264Sbill #define	GO	01		/* Go bit, set in all commands */
192264Sbill #define	PRESET	020		/* Preset drive at init or after errors */
193264Sbill #define	OFFSET	014		/* Offset heads to try to recover error */
194264Sbill #define	RTC	016		/* Return to center-line after OFFSET */
195264Sbill #define	SEARCH	030		/* Search for cylinder+sector */
196275Sbill #define	SEEK	04		/* Seek to cylinder */
197264Sbill #define	RECAL	06		/* Recalibrate, needed after seek error */
198264Sbill #define	DCLR	010		/* Drive clear, after error */
199264Sbill #define	WCOM	060		/* Write */
200264Sbill #define	RCOM	070		/* Read */
201264Sbill 
202264Sbill /* Other bits of upcs1 */
203264Sbill #define	IE	0100		/* Controller wide interrupt enable */
204264Sbill #define	TRE	040000		/* Transfer error */
205266Sbill #define	RDY	020		/* Transfer terminated */
206264Sbill 
207264Sbill /* Drive status bits of upds */
208264Sbill #define	PIP	020000		/* Positioning in progress */
209264Sbill #define	ERR	040000		/* Error has occurred, DCLR necessary */
210264Sbill #define	VV	0100		/* Volume is valid, set by PRESET */
211264Sbill #define	DPR	0400		/* Drive has been preset */
212264Sbill #define	MOL	010000		/* Drive is online, heads loaded, etc */
213264Sbill #define	DRY	0200		/* Drive ready */
214264Sbill 
215*313Sbill /* Bits of upcs2 */
216*313Sbill #define	CLR	040		/* Controller clear */
217264Sbill /* Bits of uper1 */
218264Sbill #define	DCK	0100000		/* Ecc error occurred */
219264Sbill #define	ECH	0100		/* Ecc error was unrecoverable */
220264Sbill #define	WLE	04000		/* Attempt to write read-only drive */
221264Sbill 
222264Sbill /* Bits of upof; the offset bits above are also in this register */
223264Sbill #define	FMT22	010000		/* 16 bits/word, must be always set */
224264Sbill 
225264Sbill #define	b_cylin b_resid
226264Sbill 
227264Sbill int	up_ubinfo;		/* Information about UBA usage saved here */
228264Sbill /*
229264Sbill  * The EMULEX controller balks if accessed quickly after
230264Sbill  * certain operations.  The exact timing has not yet been
231264Sbill  * determined, but delays are known to be needed when changing
232264Sbill  * the selected drive (by writing in upcs2), and thought to be
233264Sbill  * needed after operations like PRESET and DCLR.  The following
234264Sbill  * variables control the delay, DELAY(n) is approximately n usec.
235264Sbill  */
236264Sbill int	idelay = 500;		/* Delay after PRESET or DCLR */
237268Sbill int	sdelay = 150;		/* Delay after selecting drive in upcs2 */
238275Sbill int	rdelay = 100;		/* Delay after SEARCH */
239275Sbill int	asdel = 100;		/* Delay after clearing bit in upas */
240264Sbill 
241275Sbill int	csdel2 = 0;		/* ??? Delay in upstart ??? */
242275Sbill 
243264Sbill #define	DELAY(N)		{ register int d; d = N; while (--d > 0); }
244264Sbill 
245264Sbill int	nwaitcs2;		/* How many sdelay loops ? */
246264Sbill int	neasycs2;		/* How many sdelay loops not needed ? */
247264Sbill 
248*313Sbill int	up_wticks;		/* Ticks waiting for interrupt */
249*313Sbill int	upwstart;		/* Have started guardian */
250*313Sbill int	upwatch();
251*313Sbill 
252264Sbill #ifdef INTRLVE
253264Sbill daddr_t dkblock();
254264Sbill #endif
255264Sbill 
256264Sbill /*
257264Sbill  * Queue an i/o request for a drive, checking first that it is in range.
258264Sbill  *
259264Sbill  * A unit start is issued if the drive is inactive, causing
260264Sbill  * a SEARCH for the correct cylinder/sector.  If the drive is
261264Sbill  * already nearly on the money and the controller is not transferring
262264Sbill  * we kick it to start the transfer.
263264Sbill  */
264264Sbill upstrategy(bp)
265264Sbill register struct buf *bp;
266264Sbill {
267264Sbill 	register struct buf *dp;
268264Sbill 	register unit, xunit;
269264Sbill 	long sz, bn;
270264Sbill 
271*313Sbill 	if (upwstart == 0) {
272*313Sbill 		timeout((caddr_t)upwatch, 0, HZ);
273*313Sbill 		upwstart++;
274*313Sbill 	}
275264Sbill 	xunit = minor(bp->b_dev) & 077;
276264Sbill 	sz = bp->b_bcount;
277264Sbill 	sz = (sz+511) >> 9;		/* transfer size in 512 byte sectors */
278264Sbill 	unit = dkunit(bp);
279264Sbill 	if (unit >= NUP ||
280264Sbill 	    bp->b_blkno < 0 ||
281264Sbill 	    (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
282264Sbill 		bp->b_flags |= B_ERROR;
283264Sbill 		iodone(bp);
284264Sbill 		return;
285264Sbill 	}
286264Sbill 	bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
287264Sbill 	dp = &uputab[unit];
288264Sbill 	(void) spl5();
289264Sbill 	disksort(dp, bp);
290264Sbill 	if (dp->b_active == 0) {
291268Sbill 		(void) upustart(unit);
292264Sbill 		if (uptab.b_actf && uptab.b_active == 0)
293268Sbill 			(void) upstart();
294264Sbill 	}
295264Sbill 	(void) spl0();
296264Sbill }
297264Sbill 
298264Sbill /*
299264Sbill  * Start activity on specified drive; called when drive is inactive
300264Sbill  * and new transfer request arrives and also when upas indicates that
301264Sbill  * a SEARCH command is complete.
302264Sbill  */
303264Sbill upustart(unit)
304264Sbill register unit;
305264Sbill {
306264Sbill 	register struct buf *bp, *dp;
307264Sbill 	register struct device *upaddr = UPADDR;
308264Sbill 	daddr_t bn;
309264Sbill 	int sn, cn, csn;
310268Sbill 	int didie = 0;
311264Sbill 
312275Sbill 	/*
313275Sbill 	 * Other drivers tend to say something like
314275Sbill 	 *	upaddr->upcs1 = IE;
315275Sbill 	 *	upaddr->upas = 1<<unit;
316275Sbill 	 * here, but the SC-11B will cancel a command which
317275Sbill 	 * happens to be sitting in the cs1 if you clear the go
318275Sbill 	 * bit by storing there (so the first is not safe),
319275Sbill 	 * and it also does not like being bothered with operations
320275Sbill 	 * such as clearing upas when a transfer is active (as
321275Sbill 	 * it may well be.)
322275Sbill 	 *
323275Sbill 	 * Thus we keep careful track of when we re-enable IE
324275Sbill 	 * after an interrupt and do it only if we didn't issue
325275Sbill 	 * a command which re-enabled it as a matter of course.
326275Sbill 	 * We clear bits in upas in the interrupt routine, when
327275Sbill 	 * no transfers are active.
328275Sbill 	 */
329266Sbill 	if (unit >= NUP)
330268Sbill 		goto out;
331264Sbill 	if (unit+DK_N <= DK_NMAX)
332264Sbill 		dk_busy &= ~(1<<(unit+DK_N));
333264Sbill 	dp = &uputab[unit];
334266Sbill 	if ((bp = dp->b_actf) == NULL)
335268Sbill 		goto out;
336275Sbill 	/*
337275Sbill 	 * The SC-11B doesn't start SEARCH commands when transfers are
338275Sbill 	 * in progress.  In fact, it tends to get confused when given
339275Sbill 	 * SEARCH'es during transfers, generating interrupts with neither
340275Sbill 	 * RDY nor a bit in the upas register.  Thus we defer
341275Sbill 	 * until an interrupt when a transfer is pending.
342275Sbill 	 */
343275Sbill 	if (uptab.b_active) {
344275Sbill 		softas |= 1<<unit;
345275Sbill 		return (0);
346275Sbill 	}
347276Sbill 	if (dp->b_active)
348276Sbill 		goto done;
349276Sbill 	dp->b_active = 1;
350264Sbill 	if ((upaddr->upcs2 & 07) != unit) {
351264Sbill 		upaddr->upcs2 = unit;
352264Sbill 		DELAY(sdelay);
353264Sbill 		nwaitcs2++;
354264Sbill 	} else
355264Sbill 		neasycs2++;
356266Sbill 	/*
357266Sbill 	 * If we have changed packs or just initialized,
358275Sbill 	 * then the volume will not be valid; if so, clear
359266Sbill 	 * the drive, preset it and put in 16bit/word mode.
360266Sbill 	 */
361266Sbill 	if ((upaddr->upds & VV) == 0) {
362266Sbill 		upaddr->upcs1 = IE|DCLR|GO;
363266Sbill 		DELAY(idelay);
364264Sbill 		upaddr->upcs1 = IE|PRESET|GO;
365264Sbill 		DELAY(idelay);
366264Sbill 		upaddr->upof = FMT22;
367275Sbill 		printf("VV done ds %o, er? %o %o %o\n", upaddr->upds, upaddr->uper1, upaddr->uper2, upaddr->uper3);
368268Sbill 		didie = 1;
369264Sbill 	}
370264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
371275Sbill 		goto done;
372266Sbill 	/*
373266Sbill 	 * Do enough of the disk address decoding to determine
374266Sbill 	 * which cylinder and sector the request is on.
375266Sbill 	 * If we are on the correct cylinder and the desired sector
376308Sbill 	 * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
377266Sbill 	 * we don't bother to SEARCH but just begin the transfer asap.
378308Sbill 	 * Otherwise ask for a interrupt upSDIST sectors ahead.
379266Sbill 	 */
380264Sbill 	bn = dkblock(bp);
381264Sbill 	cn = bp->b_cylin;
382264Sbill 	sn = bn%(NSECT*NTRAC);
383308Sbill 	sn = (sn+NSECT-upSDIST)%NSECT;
384264Sbill 
385266Sbill 	if (cn - upaddr->updc)
386266Sbill 		goto search;		/* Not on-cylinder */
387275Sbill 	else if (upseek)
388275Sbill 		goto done;		/* Ok just to be on-cylinder */
389264Sbill 	csn = (upaddr->upla>>6) - sn - 1;
390266Sbill 	if (csn < 0)
391264Sbill 		csn += NSECT;
392308Sbill 	if (csn > NSECT-upRDIST)
393264Sbill 		goto done;
394264Sbill 
395264Sbill search:
396264Sbill 	upaddr->updc = cn;
397275Sbill 	if (upseek)
398275Sbill 		upaddr->upcs1 = IE|SEEK|GO;
399275Sbill 	else {
400275Sbill 		upaddr->upda = sn;
401275Sbill 		upaddr->upcs1 = IE|SEARCH|GO;
402275Sbill 	}
403268Sbill 	didie = 1;
404266Sbill 	/*
405266Sbill 	 * Mark this unit busy.
406266Sbill 	 */
407264Sbill 	unit += DK_N;
408264Sbill 	if (unit <= DK_NMAX) {
409264Sbill 		dk_busy |= 1<<unit;
410264Sbill 		dk_numb[unit]++;
411264Sbill 	}
412275Sbill 	DELAY(rdelay);
413268Sbill 	goto out;
414264Sbill 
415264Sbill done:
416266Sbill 	/*
417275Sbill 	 * This unit is ready to go so
418275Sbill 	 * link it onto the chain of ready disks.
419266Sbill 	 */
420264Sbill 	dp->b_forw = NULL;
421266Sbill 	if (uptab.b_actf == NULL)
422264Sbill 		uptab.b_actf = dp;
423264Sbill 	else
424264Sbill 		uptab.b_actl->b_forw = dp;
425264Sbill 	uptab.b_actl = dp;
426268Sbill 
427268Sbill out:
428268Sbill 	return (didie);
429264Sbill }
430264Sbill 
431264Sbill /*
432264Sbill  * Start a transfer; call from top level at spl5() or on interrupt.
433264Sbill  */
434264Sbill upstart()
435264Sbill {
436264Sbill 	register struct buf *bp, *dp;
437264Sbill 	register unit;
438264Sbill 	register struct device *upaddr;
439264Sbill 	daddr_t bn;
440266Sbill 	int dn, sn, tn, cn, cmd;
441264Sbill 
442264Sbill loop:
443272Sbill 	if (csdel2) DELAY(csdel2);
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 	 */
452273Sbill 	if ((dp = uptab.b_actf) == NULL)
453268Sbill 		return (0);
454264Sbill 	if ((bp = dp->b_actf) == NULL) {
455264Sbill 		uptab.b_actf = dp->b_forw;
456264Sbill 		goto loop;
457264Sbill 	}
458266Sbill 	/*
459266Sbill 	 * Mark the controller busy, and multi-part disk address.
460266Sbill 	 * Select the unit on which the i/o is to take place.
461266Sbill 	 */
462264Sbill 	uptab.b_active++;
463264Sbill 	unit = minor(bp->b_dev) & 077;
464264Sbill 	dn = dkunit(bp);
465264Sbill 	bn = dkblock(bp);
466264Sbill 	cn = up_sizes[unit&07].cyloff;
467264Sbill 	cn += bn/(NSECT*NTRAC);
468264Sbill 	sn = bn%(NSECT*NTRAC);
469264Sbill 	tn = sn/NSECT;
470266Sbill 	sn %= NSECT;
471264Sbill 	upaddr = UPADDR;
472264Sbill 	if ((upaddr->upcs2 & 07) != dn) {
473264Sbill 		upaddr->upcs2 = dn;
474275Sbill 		/* DELAY(sdelay);		Provided by ubasetup() */
475264Sbill 		nwaitcs2++;
476264Sbill 	} else
477264Sbill 		neasycs2++;
478275Sbill 	up_ubinfo = ubasetup(bp, 1);	/* Providing delay */
479266Sbill 	/*
480266Sbill 	 * If drive is not present and on-line, then
481266Sbill 	 * get rid of this with an error and loop to get
482266Sbill 	 * rid of the rest of its queued requests.
483266Sbill 	 * (Then on to any other ready drives.)
484266Sbill 	 */
485264Sbill 	if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
486275Sbill 		printf("!DPR || !MOL, unit %d, ds %o\n", dn, upaddr->upds);
487264Sbill 		uptab.b_active = 0;
488264Sbill 		uptab.b_errcnt = 0;
489264Sbill 		dp->b_actf = bp->av_forw;
490266Sbill 		dp->b_active = 0;
491264Sbill 		bp->b_flags |= B_ERROR;
492264Sbill 		iodone(bp);
493266Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;	/* A funny place ... */
494264Sbill 		goto loop;
495264Sbill 	}
496266Sbill 	/*
497266Sbill 	 * If this is a retry, then with the 16'th retry we
498266Sbill 	 * begin to try offsetting the heads to recover the data.
499266Sbill 	 */
500266Sbill 	if (uptab.b_errcnt >= 16) {
501264Sbill 		upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
502266Sbill 		upaddr->upcs1 = IE|OFFSET|GO;
503264Sbill 		DELAY(idelay);
504266Sbill 		while (upaddr->upds & PIP)
505264Sbill 			DELAY(25);
506264Sbill 	}
507266Sbill 	/*
508266Sbill 	 * Now set up the transfer, retrieving the high
509266Sbill 	 * 2 bits of the UNIBUS address from the information
510266Sbill 	 * returned by ubasetup() for the cs1 register bits 8 and 9.
511266Sbill 	 */
512264Sbill 	upaddr->updc = cn;
513264Sbill 	upaddr->upda = (tn << 8) + sn;
514264Sbill 	upaddr->upba = up_ubinfo;
515264Sbill 	upaddr->upwc = -bp->b_bcount / sizeof (short);
516266Sbill 	cmd = (up_ubinfo >> 8) & 0x300;
517264Sbill 	if (bp->b_flags & B_READ)
518266Sbill 		cmd |= IE|RCOM|GO;
519264Sbill 	else
520266Sbill 		cmd |= IE|WCOM|GO;
521266Sbill 	upaddr->upcs1 = cmd;
522266Sbill 	/*
523266Sbill 	 * This is a controller busy situation.
524266Sbill 	 * Record in dk slot NUP+DK_N (after last drive)
525266Sbill 	 * unless there aren't that many slots reserved for
526266Sbill 	 * us in which case we record this as a drive busy
527266Sbill 	 * (if there is room for that).
528266Sbill 	 */
529264Sbill 	unit = dn+DK_N;
530264Sbill 	if (NUP+DK_N == DK_NMAX)
531264Sbill 		unit = NUP+DK_N;
532264Sbill 	if (unit <= DK_NMAX) {
533264Sbill 		dk_busy |= 1<<unit;
534264Sbill 		dk_numb[unit]++;
535264Sbill 		dk_wds[unit] += bp->b_bcount>>6;
536264Sbill 	}
537268Sbill 	return (1);
538264Sbill }
539264Sbill 
540264Sbill /*
541264Sbill  * Handle a device interrupt.
542264Sbill  *
543264Sbill  * If the transferring drive needs attention, service it
544264Sbill  * retrying on error or beginning next transfer.
545264Sbill  * Service all other ready drives, calling ustart to transfer
546264Sbill  * their blocks to the ready queue in uptab, and then restart
547264Sbill  * the controller if there is anything to do.
548264Sbill  */
549264Sbill upintr()
550264Sbill {
551264Sbill 	register struct buf *bp, *dp;
552264Sbill 	register unit;
553264Sbill 	register struct device *upaddr = UPADDR;
554264Sbill 	int as = upaddr->upas & 0377;
555272Sbill 	int osoftas;
556268Sbill 	int needie = 1;
557264Sbill 
558276Sbill 	(void) spl6();
559*313Sbill 	up_wticks = 0;
560266Sbill 	if (uptab.b_active) {
561266Sbill 		/*
562266Sbill 		 * The drive is transferring, thus the hardware
563266Sbill 		 * (say the designers) will only interrupt when the transfer
564266Sbill 		 * completes; check for it anyways.
565266Sbill 		 */
566266Sbill 		if ((upaddr->upcs1 & RDY) == 0) {
567272Sbill 			printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
568272Sbill 			    upaddr->upds, upaddr->upwc);
569267Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active);
570269Sbill 		}
571266Sbill 		/*
572266Sbill 		 * Mark controller or drive not busy, and check for an
573266Sbill 		 * error condition which may have resulted from the transfer.
574266Sbill 		 */
575264Sbill 		dp = uptab.b_actf;
576264Sbill 		bp = dp->b_actf;
577264Sbill 		unit = dkunit(bp);
578264Sbill 		if (DK_N+NUP == DK_NMAX)
579264Sbill 			dk_busy &= ~(1<<(DK_N+NUP));
580264Sbill 		else if (DK_N+unit <= DK_NMAX)
581264Sbill 			dk_busy &= ~(1<<(DK_N+unit));
582275Sbill 		if ((upaddr->upcs2 & 07) != unit) {
583275Sbill 			upaddr->upcs2 = unit;
584275Sbill 			DELAY(sdelay);
585275Sbill 			nwaitcs2++;
586275Sbill 		} else
587275Sbill 			neasycs2++;
588275Sbill 		if (upaddr->upds & ERR) {
589266Sbill 			/*
590266Sbill 			 * An error occurred, indeed.  Select this unit
591266Sbill 			 * to get at the drive status (a SEARCH may have
592266Sbill 			 * intervened to change the selected unit), and
593266Sbill 			 * wait for the command which caused the interrupt
594266Sbill 			 * to complete (DRY).
595266Sbill 			 */
596266Sbill 			while ((upaddr->upds & DRY) == 0)
597264Sbill 				DELAY(25);
598266Sbill 			/*
599266Sbill 			 * After 28 retries (16 w/o servo offsets, and then
600266Sbill 			 * 12 with servo offsets), or if we encountered
601266Sbill 			 * an error because the drive is write-protected,
602266Sbill 			 * give up.  Print an error message on the last 2
603266Sbill 			 * retries before a hard failure.
604266Sbill 			 */
605266Sbill 			if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
606264Sbill 				bp->b_flags |= B_ERROR;
607264Sbill 			else
608266Sbill 				uptab.b_active = 0;	/* To force retry */
609266Sbill 			if (uptab.b_errcnt > 27)
610264Sbill 				deverror(bp, upaddr->upcs2, upaddr->uper1);
611266Sbill 			/*
612266Sbill 			 * If this was a correctible ECC error, let upecc
613266Sbill 			 * do the dirty work to correct it.  If upecc
614266Sbill 			 * starts another READ for the rest of the data
615266Sbill 			 * then it returns 1 (having set uptab.b_active).
616266Sbill 			 * Otherwise we are done and fall through to
617266Sbill 			 * finish up.
618266Sbill 			 */
619266Sbill 			if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
620266Sbill 				return;
621266Sbill 			/*
622266Sbill 			 * Clear the drive and, every 4 retries, recalibrate
623266Sbill 			 * to hopefully help clear up seek positioning problems.
624266Sbill 			 */
625264Sbill 			upaddr->upcs1 = TRE|IE|DCLR|GO;
626264Sbill 			DELAY(idelay);
627268Sbill 			needie = 0;
628266Sbill 			if ((uptab.b_errcnt&07) == 4) {
629264Sbill 				upaddr->upcs1 = RECAL|GO|IE;
630264Sbill 				DELAY(idelay);
631264Sbill 				while(upaddr->upds & PIP)
632264Sbill 					DELAY(25);
633264Sbill 			}
634264Sbill 		}
635266Sbill 		/*
636266Sbill 		 * If we are still noted as active, then no
637266Sbill 		 * (further) retries are necessary.
638266Sbill 		 *
639266Sbill 		 * Make sure the correct unit is selected,
640266Sbill 		 * return it to centerline if necessary, and mark
641266Sbill 		 * this i/o complete, starting the next transfer
642266Sbill 		 * on this drive with the upustart routine (if any).
643266Sbill 		 */
644266Sbill 		if (uptab.b_active) {
645266Sbill 			if (uptab.b_errcnt >= 16) {
646266Sbill 				upaddr->upcs1 = RTC|GO|IE;
647264Sbill 				DELAY(idelay);
648266Sbill 				while (upaddr->upds & PIP)
649264Sbill 					DELAY(25);
650268Sbill 				needie = 0;
651264Sbill 			}
652264Sbill 			uptab.b_active = 0;
653264Sbill 			uptab.b_errcnt = 0;
654264Sbill 			uptab.b_actf = dp->b_forw;
655264Sbill 			dp->b_active = 0;
656264Sbill 			dp->b_errcnt = 0;
657264Sbill 			dp->b_actf = bp->av_forw;
658266Sbill 			bp->b_resid = (-upaddr->upwc * sizeof(short));
659275Sbill 			if (bp->b_resid)
660275Sbill 				printf("resid %d ds %o er? %o %o %o\n", bp->b_resid, upaddr->upds,
661275Sbill 				    upaddr->uper1, upaddr->uper2, upaddr->uper3);
662264Sbill 			iodone(bp);
663264Sbill 			if(dp->b_actf)
664268Sbill 				if (upustart(unit))
665268Sbill 					needie = 0;
666264Sbill 		}
667264Sbill 		as &= ~(1<<unit);
668272Sbill 		softas &= ~(1<<unit);
669264Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
670273Sbill 	} else {
671264Sbill 		if (upaddr->upcs1 & TRE) {
672264Sbill 			upaddr->upcs1 = TRE;
673264Sbill 			DELAY(idelay);
674264Sbill 		}
675264Sbill 	}
676266Sbill 	/*
677266Sbill 	 * If we have a unit with an outstanding SEARCH,
678266Sbill 	 * and the hardware indicates the unit requires attention,
679266Sbill 	 * the bring the drive to the ready queue.
680266Sbill 	 * Finally, if the controller is not transferring
681266Sbill 	 * start it if any drives are now ready to transfer.
682266Sbill 	 */
683272Sbill 	as |= softas;
684272Sbill 	osoftas = softas;
685272Sbill 	softas = 0;
686266Sbill 	for (unit = 0; unit < NUP; unit++)
687273Sbill 		if ((as|osoftas) & (1<<unit)) {
688273Sbill 			if (as & (1<<unit)) {
689267Sbill 				upaddr->upas = 1<<unit;
690268Sbill 				if (asdel) DELAY(asdel);
691272Sbill 			}
692273Sbill 			if (upustart(unit))
693273Sbill 				needie = 0;
694273Sbill 		}
695266Sbill 	if (uptab.b_actf && uptab.b_active == 0)
696268Sbill 		if (upstart())
697268Sbill 			needie = 0;
698266Sbill out:
699275Sbill 	if (needie)
700266Sbill 		upaddr->upcs1 = IE;
701264Sbill }
702264Sbill 
703264Sbill upread(dev)
704264Sbill {
705264Sbill 
706264Sbill 	physio(upstrategy, &rupbuf, dev, B_READ, minphys);
707264Sbill }
708264Sbill 
709264Sbill upwrite(dev)
710264Sbill {
711264Sbill 
712264Sbill 	physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
713264Sbill }
714264Sbill 
715266Sbill /*
716266Sbill  * Correct an ECC error, and restart the i/o to complete
717266Sbill  * the transfer if necessary.  This is quite complicated because
718266Sbill  * the transfer may be going to an odd memory address base and/or
719266Sbill  * across a page boundary.
720266Sbill  */
721264Sbill upecc(up, bp)
722264Sbill register struct device *up;
723264Sbill register struct buf *bp;
724264Sbill {
725264Sbill 	struct uba_regs *ubp = (struct uba_regs *)UBA0;
726266Sbill 	register int i;
727264Sbill 	caddr_t addr;
728266Sbill 	int reg, bit, byte, npf, mask, o, cmd, ubaddr;
729264Sbill 	int bn, cn, tn, sn;
730264Sbill 
731264Sbill 	/*
732266Sbill 	 * Npf is the number of sectors transferred before the sector
733266Sbill 	 * containing the ECC error, and reg is the UBA register
734266Sbill 	 * mapping (the first part of) the transfer.
735266Sbill 	 * O is offset within a memory page of the first byte transferred.
736264Sbill 	 */
737266Sbill 	npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
738266Sbill 	reg = btop(up_ubinfo&0x3ffff) + npf;
739264Sbill 	o = (int)bp->b_un.b_addr & PGOFSET;
740264Sbill 	printf("%D ", bp->b_blkno+npf);
741264Sbill 	prdev("ECC", bp->b_dev);
742264Sbill 	mask = up->upec2;
743264Sbill 	if (mask == 0) {
744266Sbill 		up->upof = FMT22;		/* == RTC ???? */
745264Sbill 		DELAY(idelay);
746264Sbill 		return (0);
747264Sbill 	}
748266Sbill 	/*
749266Sbill 	 * Flush the buffered data path, and compute the
750266Sbill 	 * byte and bit position of the error.  The variable i
751266Sbill 	 * is the byte offset in the transfer, the variable byte
752266Sbill 	 * is the offset from a page boundary in main memory.
753266Sbill 	 */
754266Sbill 	ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
755266Sbill 	i = up->upec1 - 1;		/* -1 makes 0 origin */
756266Sbill 	bit = i&07;
757266Sbill 	i = (i&~07)>>3;
758264Sbill 	byte = i + o;
759266Sbill 	/*
760266Sbill 	 * Correct while possible bits remain of mask.  Since mask
761266Sbill 	 * contains 11 bits, we continue while the bit offset is > -11.
762266Sbill 	 * Also watch out for end of this block and the end of the whole
763266Sbill 	 * transfer.
764266Sbill 	 */
765266Sbill 	while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
766266Sbill 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
767266Sbill 		    (byte & PGOFSET);
768266Sbill 		putmemc(addr, getmemc(addr)^(mask<<bit));
769266Sbill 		byte++;
770266Sbill 		i++;
771266Sbill 		bit -= 8;
772264Sbill 	}
773266Sbill 	uptab.b_active++;	/* Either complete or continuing... */
774264Sbill 	if (up->upwc == 0)
775264Sbill 		return (0);
776266Sbill 	/*
777266Sbill 	 * Have to continue the transfer... clear the drive,
778266Sbill 	 * and compute the position where the transfer is to continue.
779266Sbill 	 * We have completed npf+1 sectors of the transfer already;
780266Sbill 	 * restart at offset o of next sector (i.e. in UBA register reg+1).
781266Sbill 	 */
782266Sbill 	up->upcs1 = TRE|IE|DCLR|GO;
783264Sbill 	DELAY(idelay);
784264Sbill 	bn = dkblock(bp);
785264Sbill 	cn = bp->b_cylin;
786266Sbill 	sn = bn%(NSECT*NTRAC) + npf + 1;
787264Sbill 	tn = sn/NSECT;
788264Sbill 	sn %= NSECT;
789266Sbill 	cn += tn/NTRAC;
790266Sbill 	tn %= NTRAC;
791264Sbill 	up->updc = cn;
792266Sbill 	up->upda = (tn << 8) | sn;
793266Sbill 	ubaddr = (int)ptob(reg+1) + o;
794266Sbill 	up->upba = ubaddr;
795266Sbill 	cmd = (ubaddr >> 8) & 0x300;
796266Sbill 	cmd |= IE|GO|RCOM;
797266Sbill 	up->upcs1 = cmd;
798264Sbill 	return (1);
799264Sbill }
800286Sbill 
801286Sbill /*
802286Sbill  * Reset driver after UBA init.
803286Sbill  * Cancel software state of all pending transfers
804286Sbill  * and restart all units and the controller.
805286Sbill  */
806286Sbill upreset()
807286Sbill {
808286Sbill 	int unit;
809286Sbill 
810286Sbill 	printf(" up");
811286Sbill 	uptab.b_active = 0;
812286Sbill 	uptab.b_actf = uptab.b_actl = 0;
813286Sbill 	if (DK_N+NUP == DK_NMAX)
814286Sbill 		dk_busy &= ~(1<<(DK_N+NUP));
815286Sbill 	if (up_ubinfo) {
816286Sbill 		printf("<%d>", (up_ubinfo>>28)&0xf);
817286Sbill 		ubafree(up_ubinfo), up_ubinfo = 0;
818286Sbill 	}
819*313Sbill 	UPADDR->upcs2 = CLR;		/* clear controller */
820*313Sbill 	DELAY(idelay);
821286Sbill 	for (unit = 0; unit < NUP; unit++) {
822286Sbill 		uputab[unit].b_active = 0;
823286Sbill 		(void) upustart(unit);
824286Sbill 	}
825286Sbill 	(void) upstart();
826286Sbill }
827*313Sbill 
828*313Sbill /*
829*313Sbill  * Wake up every second and if an interrupt is pending
830*313Sbill  * but nothing has happened increment a counter.
831*313Sbill  * If nothing happens for 20 seconds, reset the controller
832*313Sbill  * and begin anew.
833*313Sbill  */
834*313Sbill upwatch()
835*313Sbill {
836*313Sbill 	int i;
837*313Sbill 
838*313Sbill 	timeout((caddr_t)upwatch, 0, HZ);
839*313Sbill 	if (uptab.b_active == 0) {
840*313Sbill 		for (i = 0; i < NUP; i++)
841*313Sbill 			if (uputab[i].b_active)
842*313Sbill 				goto active;
843*313Sbill 		up_wticks = 0;		/* idling */
844*313Sbill 		return;
845*313Sbill 	}
846*313Sbill active:
847*313Sbill 	up_wticks++;
848*313Sbill 	if (up_wticks >= 20) {
849*313Sbill 		up_wticks = 0;
850*313Sbill 		printf("LOST INTERRUPT RESET");
851*313Sbill 		upreset();
852*313Sbill 		printf("\n");
853*313Sbill 	}
854*313Sbill }
855