xref: /csrg-svn/sys/tahoe/stand/vd.c (revision 32604)
1 /*	vd.c	7.8	87/11/12	*/
2 
3 /*
4  * Stand alone driver for the VDDC/SMDE controller
5  */
6 #include "../machine/mtpr.h"
7 
8 #include "param.h"
9 #include "inode.h"
10 #include "fs.h"
11 #include "buf.h"
12 #include "disklabel.h"
13 #include "saio.h"
14 
15 #include "../tahoevba/vdreg.h"
16 #include "../tahoevba/vbaparam.h"
17 
18 #define	COMPAT_42	1
19 
20 #define NVD		4
21 #define	NDRIVE		8		/* drives per controller */
22 #define VDSLAVE(x)	((x) % NDRIVE)
23 #define VDCTLR(x)	((x) / NDRIVE)
24 
25 #define	VDADDR(ctlr)	((struct vddevice *)vdaddrs[ctlr])
26 long	vdaddrs[NVD] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300 };
27 
28 u_char	vdinit[NVD];			/* controller initialized */
29 u_char	vdtype[NVD];			/* controller type */
30 u_char	dkconfigured[NVD*NDRIVE];	/* unit configured */
31 u_char	dkflags[NVD][NDRIVE];		/* unit flags */
32 
33 static	struct disklabel dklabel[NVD*NDRIVE];	/* pack label */
34 static	struct mdcb mdcb;
35 static	struct dcb dcb;
36 static	char lbuf[DEV_BSIZE];
37 
38 vdopen(io)
39 	register struct iob *io;
40 {
41 	register int ctlr = VDCTLR(io->i_unit);
42 	register struct dkinfo *dk;
43 	register struct disklabel *lp, *dlp;
44 	int error;
45 
46 	if (ctlr >= NVD) {
47 		printf("invalid controller number\n");
48 		return (ENXIO);
49 	}
50 	if (!vdinit[ctlr] && (error = vdreset_ctlr(ctlr, io->i_unit)))
51 		return (error);
52 	lp = &dklabel[io->i_unit];
53 	if (!dkconfigured[io->i_unit]) {
54 		struct iob tio;
55 
56 		/*
57 		 * Read in the pack label.
58 		 */
59 		lp->d_secsize = 1024;
60 		lp->d_nsectors = 72;
61 		lp->d_ntracks = 24;
62 		lp->d_ncylinders = 711;
63 		lp->d_secpercyl = 72*24;
64 		if (!vdreset_drive(io))
65 			return (ENXIO);
66 		tio = *io;
67 		tio.i_bn = LABELSECTOR;
68 		tio.i_ma = lbuf;
69 		tio.i_cc = DEV_BSIZE;
70 		tio.i_flgs |= F_RDDATA;
71 		if (vdstrategy(&tio, READ) != DEV_BSIZE) {
72 			printf("dk%d: can't read disk label\n", io->i_unit);
73 			return (EIO);
74 		}
75 		dlp = (struct disklabel *)(lbuf + LABELOFFSET);
76 		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
77 #ifdef COMPAT_42
78 			if (error = vdmaptype(io))
79 				return (error);
80 #else
81 			printf("dk%d: unlabeled\n", io->i_unit);
82 			return (ENXIO);
83 #endif
84 		} else {
85 			*lp = *dlp;
86 			if (!vdreset_drive(io))
87 				return (ENXIO);
88 		}
89 		dkconfigured[io->i_unit] = 1;
90 	}
91 	if (io->i_boff < 0 || io->i_boff >= lp->d_npartitions ||
92 	    lp->d_partitions[io->i_boff].p_size == 0) {
93 		printf("dk%d: bad minor\n", io->i_unit);
94 		return (EUNIT);
95 	}
96 	io->i_boff =
97 	    (lp->d_partitions[io->i_boff].p_offset * lp->d_secsize) / DEV_BSIZE;
98 	return (0);
99 }
100 
101 /*
102  * Reset and initialize the controller.
103  */
104 vdreset_ctlr(ctlr, unit)
105 	register int ctlr, unit;
106 {
107 	register int i;
108 	register struct vddevice *vdaddr = VDADDR(ctlr);
109 
110 	if (badaddr(vdaddr, 2)) {
111 		printf("vd%d: %x: invalid csr\n", ctlr, vdaddr);
112 		return (ENXIO);
113 	}
114 	/* probe further to find what kind of controller it is */
115 	vdaddr->vdreset = 0xffffffff;
116 	DELAY(1000000);
117 	if (vdaddr->vdreset != 0xffffffff) {
118 		vdtype[ctlr] = VDTYPE_VDDC;
119 		DELAY(1000000);
120 	} else {
121 		vdtype[ctlr] = VDTYPE_SMDE;
122 		vdaddr->vdrstclr = 0;
123 		DELAY(3000000);
124 		vdaddr->vdcsr =  0;
125 		vdaddr->vdtcf_mdcb = AM_ENPDA;
126 		vdaddr->vdtcf_dcb = AM_ENPDA;
127 		vdaddr->vdtcf_trail = AM_ENPDA;
128 		vdaddr->vdtcf_data = AM_ENPDA;
129 		vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS |
130 		    XMD_32BIT | BSZ_16WRD |
131 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
132 	}
133 	if (!vdcmd(ctlr, 0, VDOP_INIT, 10) ||
134 	    !vdcmd(ctlr, 0, VDOP_DIAG, 10)) {
135 		vderror(unit, dcb.opcode == VDOP_INIT ? "init" : "diag", &dcb);
136 		return (EIO);
137 	}
138 	vdinit[ctlr] = 1;
139 	for (i = unit = ctlr * NDRIVE; i < unit + NDRIVE; i++)
140 		dkconfigured[i] = 0;
141 	return (0);
142 }
143 
144 /*
145  * Reset and configure a drive's parameters.
146  */
147 vdreset_drive(io)
148 	register struct iob *io;
149 {
150 	register int ctlr = VDCTLR(io->i_unit), slave = VDSLAVE(io->i_unit);
151 	register struct disklabel *lp = &dklabel[io->i_unit];
152 	register struct vddevice *vdaddr = VDADDR(ctlr);
153 	int pass = 0, type = vdtype[ctlr], error;
154 	int devflags = dkflags[ctlr][slave];		/* starts with 0 */
155 
156 again:
157 	dcb.opcode = VDOP_CONFIG;		/* command */
158 	dcb.intflg = DCBINT_NONE;
159 	dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
160 	dcb.operrsta = 0;
161 	dcb.devselect = slave | devflags;
162 	dcb.trail.rstrail.ncyl = lp->d_ncylinders;
163 	dcb.trail.rstrail.nsurfaces = lp->d_ntracks;
164 	if (type == VDTYPE_SMDE) {
165 		dcb.trailcnt = sizeof (struct treset) / sizeof (long);
166 		dcb.trail.rstrail.nsectors = lp->d_nsectors;
167 		dcb.trail.rstrail.slip_sec = lp->d_trackskew;
168 		dcb.trail.rstrail.recovery = VDRF_NORMAL;
169 	} else
170 		dcb.trailcnt = 2;	/* XXX */
171 	mdcb.mdcb_head = &dcb;
172 	mdcb.mdcb_status = 0;
173 	VDGO(vdaddr, (u_long)&mdcb, type);
174 	if (!vdpoll(vdaddr, &dcb, 10, type)) {
175 		if (pass++ != 0) {
176 			printf(" during drive configuration.\n");
177 			return (0);
178 		}
179 		VDRESET(vdaddr, type);
180 		if (error = vdreset_ctlr(ctlr, io->i_unit))
181 			return (error);
182 		goto again;
183 	}
184 	if ((dcb.operrsta & VDERR_HARD) == 0) {		/* success */
185 		dkflags[ctlr][slave] = devflags;
186 		return (1);
187 	}
188 	if (devflags == 0) {
189 		devflags = VD_ESDI;
190 		goto again;
191 	}
192 	if (type == VDTYPE_SMDE && (vdaddr->vdstatus[slave] & STA_US) == 0) {
193 		printf("dk%d: nonexistent drive\n", io->i_unit);
194 		return (0);
195 	}
196 	if ((dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) {
197 		vderror(io->i_unit, "config", &dcb);
198 		return (0);
199 	}
200 	devflags = 0;
201 	if (pass++)			/* give up */
202 		return (0);
203 	/*
204 	 * Try to spin up drive with remote command.
205 	 */
206 	if (!vdcmd(ctlr, 0, VDOP_START, 62)) {
207 		vderror(io->i_unit, "start", &dcb);
208 		return (0);
209 	}
210 	DELAY(62000000);
211 	goto again;
212 }
213 
214 vdcmd(ctlr, unit, cmd, time)
215 	register int ctlr;
216 	int unit, cmd, time;
217 {
218 	register struct vddevice *vdaddr = VDADDR(ctlr);
219 
220 	dcb.opcode = cmd;
221 	dcb.intflg = DCBINT_NONE;
222 	dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
223 	dcb.operrsta  = 0;
224 	dcb.devselect = unit | dkflags[ctlr][unit];
225 	dcb.trailcnt = 0;
226 	mdcb.mdcb_head = &dcb;
227 	mdcb.mdcb_status = 0;
228 	VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]);
229 	if (!vdpoll(vdaddr, &dcb, time, vdtype[ctlr]))
230 		_stop(" during initialization operation.\n");
231 	return ((dcb.operrsta & VDERR_HARD) == 0);
232 }
233 
234 vdstrategy(io, cmd)
235 	register struct iob *io;
236 	int cmd;
237 {
238 	register struct disklabel *lp;
239 	int ctlr, cn, tn, sn, slave, retries = 0;
240 	daddr_t bn;
241 	struct vddevice *vdaddr;
242 
243 	if (io->i_cc == 0 || io->i_cc > 65535) {
244 		printf("dk%d: invalid transfer size %d\n", io->i_unit,
245 		    io->i_cc);
246 		io->i_error = EIO;
247 		return (-1);
248 	}
249 	lp = &dklabel[io->i_unit];
250 	bn = io->i_bn * (DEV_BSIZE / lp->d_secsize);
251 	cn = bn / lp->d_secpercyl;
252 	sn = bn % lp->d_secpercyl;
253 	tn = sn / lp->d_nsectors;
254 	sn = sn % lp->d_nsectors;
255 
256 top:
257 	dcb.opcode = (cmd == READ ? VDOP_RD : VDOP_WD);
258 	dcb.intflg = DCBINT_NONE;
259 	dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
260 	dcb.operrsta  = 0;
261 	ctlr = VDCTLR(io->i_unit);
262 	slave = VDSLAVE(io->i_unit);
263 	dcb.devselect = slave | dkflags[ctlr][slave];
264 	dcb.trailcnt = sizeof (struct trrw) / sizeof (int);
265 	dcb.trail.rwtrail.memadr = (u_long)io->i_ma;
266 	dcb.trail.rwtrail.wcount = (io->i_cc + 1) / sizeof (short);
267 	dcb.trail.rwtrail.disk.cylinder = cn;
268 	dcb.trail.rwtrail.disk.track = tn;
269 	dcb.trail.rwtrail.disk.sector = sn;
270 	mdcb.mdcb_head = &dcb;
271 	mdcb.mdcb_status = 0;
272 	vdaddr = VDADDR(ctlr);
273 	VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]);
274 	if (!vdpoll(vdaddr, &dcb, 60, vdtype[ctlr]))
275 		_stop(" during i/o operation.\n");
276 	if (dcb.operrsta & VDERR_HARD) {
277 		if (retries++ == 0 && vdreset_ctlr(ctlr, io->i_unit) == 0 &&
278 		    vdreset_drive(io))
279 			goto top;
280 		vderror(io->i_unit, cmd == READ ? "read" : "write", &dcb);
281 		io->i_error = EIO;
282 		return (-1);
283 	}
284 	mtpr(PADC, 0);
285 	return (io->i_cc);
286 }
287 
288 vderror(unit, cmd, dcb)
289 	int unit;
290 	char *cmd;
291 	struct dcb *dcb;
292 {
293 
294 	printf("dk%d: %s error; status %b", unit, cmd,
295 	    dcb->operrsta, VDERRBITS);
296 	if (dcb->err_code)
297 		printf(", code %x", dcb->err_code);
298 	printf("\n");
299 }
300 
301 /*
302  * Poll controller until operation
303  * completes or timeout expires.
304  */
305 vdpoll(vdaddr, dcb, t, type)
306 	register struct vddevice *vdaddr;
307 	register struct dcb *dcb;
308 	register int t, type;
309 {
310 
311 	t *= 1000;
312 	for (;;) {
313 		uncache(&dcb->operrsta);
314 		if (dcb->operrsta & (DCBS_DONE|DCBS_ABORT))
315 			break;
316 		if (--t <= 0) {
317 			printf("vd: controller timeout");
318 			VDABORT(vdaddr, type);
319 			DELAY(30000);
320 			uncache(&dcb->operrsta);
321 			return (0);
322 		}
323 		DELAY(1000);
324 	}
325 	if (type == VDTYPE_SMDE) {
326 		for (;;) {
327 			uncache(&vdaddr->vdcsr);
328 			if ((vdaddr->vdcsr & CS_GO) == 0)
329 				break;
330 			DELAY(50);
331 		}
332 		DELAY(300);
333 		uncache(&dcb->err_code);
334 	}
335 	DELAY(200);
336 	uncache(&dcb->operrsta);
337 	return (1);
338 }
339 
340 #ifdef COMPAT_42
341 struct	dkcompat {
342 	int	nsectors;		/* sectors per track */
343 	int	ntracks;		/* tracks per cylinder */
344 	int	ncylinders;		/* cylinders per drive */
345 	int	secsize;		/* sector size */
346 #define	NPART	2
347 	int	poff[NPART];		/* [a+b] for bootstrapping */
348 } dkcompat[] = {
349 	{ 48, 24,  711,  512, 0, 61056 },	/* xsd */
350 	{ 44, 20,  842,  512, 0, 52800 },	/* eagle */
351 	{ 64, 10,  823,  512, 0, 38400 },	/* fuji 360 */
352 	{ 32, 24,  711,  512, 0, 40704 },	/* xfd */
353 	{ 32, 19,  823,  512, 0, 40128 },	/* smd */
354 	{ 32, 10,  823,  512, 0, 19200 },	/* fsd */
355 	{ 18, 15, 1224, 1024, 0, 21600 },	/* mxd */
356 };
357 #define	NDKCOMPAT	(sizeof (dkcompat) / sizeof (dkcompat[0]))
358 
359 /*
360  * Identify and configure drive from above table
361  * by trying to read the last sector until a description
362  * is found for which we're successful.
363  */
364 vdmaptype(io)
365 	struct iob *io;
366 {
367 	register struct disklabel *lp = &dklabel[io->i_unit];
368 	register struct dkcompat *dp;
369 	int i, ctlr, slave, type;
370 	struct vddevice *vdaddr;
371 
372 	ctlr = VDCTLR(io->i_unit);
373 	slave = VDSLAVE(io->i_unit);
374 	vdaddr = VDADDR(ctlr);
375 	type = vdtype[ctlr];
376 	for (dp = dkcompat; dp < &dkcompat[NDKCOMPAT]; dp++) {
377 		if (type == VDTYPE_VDDC && dp->nsectors != 32)
378 			continue;
379 		lp->d_nsectors = dp->nsectors;
380 		lp->d_ntracks = dp->ntracks;
381 		lp->d_ncylinders = dp->ncylinders;
382 		lp->d_secsize = dp->secsize;
383 		if (!vdreset_drive(io))		/* set drive parameters */
384 			return (EIO);
385 		dcb.opcode = VDOP_RD;
386 		dcb.intflg = DCBINT_NONE;
387 		dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
388 		dcb.devselect = slave | dkflags[ctlr][slave];
389 		dcb.operrsta = 0;
390 		dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
391 		dcb.trail.rwtrail.memadr = (u_long)lbuf;
392 		dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof (short);
393 		dcb.trail.rwtrail.disk.cylinder = dp->ncylinders - 2;
394 		dcb.trail.rwtrail.disk.track = dp->ntracks - 1;
395 		dcb.trail.rwtrail.disk.sector = dp->nsectors - 1;
396 		mdcb.mdcb_head = &dcb;
397 		mdcb.mdcb_status = 0;
398 		VDGO(vdaddr, (u_long)&mdcb, type);
399 		if (!vdpoll(vdaddr, &dcb, 60, type))
400 			_stop(" during i/o operation.\n");
401 		if (dcb.operrsta & VDERR_HARD)
402 			continue;
403 		/* simulate necessary parts of disk label */
404 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
405 		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
406 		lp->d_npartitions = NPART;
407 		for (i = 0; i < NPART; i++) {
408 			lp->d_partitions[i].p_offset = dp->poff[i];
409 			lp->d_partitions[i].p_size =
410 			    lp->d_secperunit - dp->poff[i];
411 		}
412 		return (0);
413 	}
414 	printf("dk%d: unknown drive type\n", io->i_unit);
415 	return (ENXIO);
416 }
417 #endif
418