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