Commit | Line | Data |
---|---|---|
434f5077 KB |
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 | * | |
b702c21d | 8 | * %sccs.include.redist.c% |
434f5077 | 9 | * |
800bc4d3 | 10 | * @(#)vd.c 7.18 (Berkeley) %G% |
434f5077 | 11 | */ |
4006c5e1 | 12 | |
39b72d60 | 13 | /* |
bff99b3e | 14 | * Stand alone driver for the VDDC/SMDE controller |
4006c5e1 | 15 | */ |
c18e27ed | 16 | #include "machine/mtpr.h" |
39b72d60 | 17 | |
ec3296ac BJ |
18 | #include "sys/param.h" |
19 | #include "sys/time.h" | |
ec3296ac BJ |
20 | #include "sys/buf.h" |
21 | #include "sys/disklabel.h" | |
b28b3a13 | 22 | #include "stand/saio.h" |
bff99b3e | 23 | |
b28b3a13 KB |
24 | #include "../vba/vdreg.h" |
25 | #include "../vba/vbaparam.h" | |
39b72d60 | 26 | |
bff99b3e | 27 | #define COMPAT_42 1 |
39b72d60 | 28 | |
cb69d88e | 29 | #define NVD 4 /* controllers */ |
bff99b3e | 30 | #define NDRIVE 8 /* drives per controller */ |
39b72d60 | 31 | |
bff99b3e SL |
32 | #define VDADDR(ctlr) ((struct vddevice *)vdaddrs[ctlr]) |
33 | long vdaddrs[NVD] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300 }; | |
39b72d60 | 34 | |
bff99b3e SL |
35 | u_char vdinit[NVD]; /* controller initialized */ |
36 | u_char vdtype[NVD]; /* controller type */ | |
cb69d88e | 37 | u_char dkconfigured[NVD][NDRIVE]; /* unit configured */ |
7d68510b | 38 | u_char dkflags[NVD][NDRIVE]; /* unit flags */ |
39b72d60 | 39 | |
cb69d88e | 40 | static struct disklabel dklabel[NVD][NDRIVE]; /* pack label */ |
7d68510b MK |
41 | static struct mdcb mdcb; |
42 | static struct dcb dcb; | |
43 | static char lbuf[DEV_BSIZE]; | |
39b72d60 SL |
44 | |
45 | vdopen(io) | |
4006c5e1 | 46 | register struct iob *io; |
39b72d60 | 47 | { |
cb69d88e | 48 | register int ctlr = io->i_ctlr; |
bff99b3e | 49 | register struct dkinfo *dk; |
bdfd2fb7 | 50 | register struct disklabel *lp, *dlp; |
bff99b3e | 51 | int error; |
39b72d60 | 52 | |
cb69d88e KB |
53 | if ((u_int)io->i_adapt) |
54 | return (EADAPT); | |
55 | if ((u_int)ctlr >= NVD) | |
56 | return (ECTLR); | |
bff99b3e SL |
57 | if (!vdinit[ctlr] && (error = vdreset_ctlr(ctlr, io->i_unit))) |
58 | return (error); | |
cb69d88e KB |
59 | lp = &dklabel[io->i_ctlr][io->i_unit]; |
60 | if (!dkconfigured[io->i_ctlr][io->i_unit]) { | |
bff99b3e SL |
61 | struct iob tio; |
62 | ||
63 | /* | |
64 | * Read in the pack label. | |
65 | */ | |
7d68510b MK |
66 | lp->d_secsize = 1024; |
67 | lp->d_nsectors = 72; | |
bff99b3e SL |
68 | lp->d_ntracks = 24; |
69 | lp->d_ncylinders = 711; | |
7d68510b | 70 | lp->d_secpercyl = 72*24; |
bff99b3e SL |
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; | |
800bc4d3 | 78 | if (vdstrategy(&tio, F_READ) != DEV_BSIZE) |
cb69d88e | 79 | return (ERDLAB); |
bdfd2fb7 | 80 | dlp = (struct disklabel *)(lbuf + LABELOFFSET); |
cb69d88e | 81 | if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) |
bff99b3e | 82 | #ifdef COMPAT_42 |
cb69d88e KB |
83 | { |
84 | printf("dk%d: unlabeled\n", io->i_unit); | |
bff99b3e SL |
85 | if (error = vdmaptype(io)) |
86 | return (error); | |
cb69d88e | 87 | } |
bff99b3e | 88 | #else |
cb69d88e | 89 | return (EUNLAB); |
bff99b3e | 90 | #endif |
cb69d88e | 91 | else { |
bdfd2fb7 | 92 | *lp = *dlp; |
7d68510b MK |
93 | if (!vdreset_drive(io)) |
94 | return (ENXIO); | |
95 | } | |
cb69d88e | 96 | dkconfigured[io->i_ctlr][io->i_unit] = 1; |
39b72d60 | 97 | } |
cb69d88e KB |
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); | |
bff99b3e | 101 | io->i_boff = |
cb69d88e | 102 | (lp->d_partitions[io->i_part].p_offset * lp->d_secsize) / DEV_BSIZE; |
bff99b3e | 103 | return (0); |
39b72d60 SL |
104 | } |
105 | ||
bff99b3e SL |
106 | /* |
107 | * Reset and initialize the controller. | |
108 | */ | |
109 | vdreset_ctlr(ctlr, unit) | |
110 | register int ctlr, unit; | |
39b72d60 | 111 | { |
bff99b3e SL |
112 | register int i; |
113 | register struct vddevice *vdaddr = VDADDR(ctlr); | |
39b72d60 | 114 | |
bff99b3e SL |
115 | if (badaddr(vdaddr, 2)) { |
116 | printf("vd%d: %x: invalid csr\n", ctlr, vdaddr); | |
117 | return (ENXIO); | |
39b72d60 | 118 | } |
bff99b3e SL |
119 | /* probe further to find what kind of controller it is */ |
120 | vdaddr->vdreset = 0xffffffff; | |
39b72d60 | 121 | DELAY(1000000); |
bff99b3e SL |
122 | if (vdaddr->vdreset != 0xffffffff) { |
123 | vdtype[ctlr] = VDTYPE_VDDC; | |
39b72d60 | 124 | DELAY(1000000); |
4006c5e1 | 125 | } else { |
bff99b3e SL |
126 | vdtype[ctlr] = VDTYPE_SMDE; |
127 | vdaddr->vdrstclr = 0; | |
39b72d60 | 128 | DELAY(3000000); |
bff99b3e SL |
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; | |
f0f3c469 | 134 | vdaddr->vdccf = CCF_SEN | CCF_DIU | CCF_STS | |
bff99b3e SL |
135 | XMD_32BIT | BSZ_16WRD | |
136 | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; | |
39b72d60 | 137 | } |
bff99b3e SL |
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); | |
39b72d60 | 142 | } |
bff99b3e | 143 | vdinit[ctlr] = 1; |
cb69d88e KB |
144 | for (i = NDRIVE - 1; i >= 0; i--) |
145 | dkconfigured[ctlr][i] = 0; | |
bff99b3e | 146 | return (0); |
39b72d60 SL |
147 | } |
148 | ||
bff99b3e SL |
149 | /* |
150 | * Reset and configure a drive's parameters. | |
151 | */ | |
152 | vdreset_drive(io) | |
4006c5e1 | 153 | register struct iob *io; |
39b72d60 | 154 | { |
cb69d88e KB |
155 | register int ctlr = io->i_ctlr, slave = io->i_unit; |
156 | register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit]; | |
bff99b3e SL |
157 | register struct vddevice *vdaddr = VDADDR(ctlr); |
158 | int pass = 0, type = vdtype[ctlr], error; | |
7d68510b | 159 | int devflags = dkflags[ctlr][slave]; /* starts with 0 */ |
4006c5e1 | 160 | |
bff99b3e SL |
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; | |
7d68510b | 166 | dcb.devselect = slave | devflags; |
bff99b3e SL |
167 | dcb.trail.rstrail.ncyl = lp->d_ncylinders; |
168 | dcb.trail.rstrail.nsurfaces = lp->d_ntracks; | |
169 | if (type == VDTYPE_SMDE) { | |
bdfd2fb7 | 170 | dcb.trailcnt = sizeof (struct treset) / sizeof (long); |
bff99b3e | 171 | dcb.trail.rstrail.nsectors = lp->d_nsectors; |
7d68510b MK |
172 | dcb.trail.rstrail.slip_sec = lp->d_trackskew; |
173 | dcb.trail.rstrail.recovery = VDRF_NORMAL; | |
bff99b3e SL |
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; | |
39b72d60 | 188 | } |
7d68510b MK |
189 | if ((dcb.operrsta & VDERR_HARD) == 0) { /* success */ |
190 | dkflags[ctlr][slave] = devflags; | |
bff99b3e | 191 | return (1); |
7d68510b MK |
192 | } |
193 | if (devflags == 0) { | |
194 | devflags = VD_ESDI; | |
195 | goto again; | |
196 | } | |
bff99b3e SL |
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 | } | |
7d68510b | 205 | devflags = 0; |
bff99b3e SL |
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); | |
39b72d60 | 214 | } |
7f6fe1b4 | 215 | DELAY(62000000); |
bff99b3e | 216 | goto again; |
39b72d60 SL |
217 | } |
218 | ||
bff99b3e SL |
219 | vdcmd(ctlr, unit, cmd, time) |
220 | register int ctlr; | |
221 | int unit, cmd, time; | |
39b72d60 | 222 | { |
bff99b3e | 223 | register struct vddevice *vdaddr = VDADDR(ctlr); |
39b72d60 | 224 | |
bff99b3e SL |
225 | dcb.opcode = cmd; |
226 | dcb.intflg = DCBINT_NONE; | |
227 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
39b72d60 | 228 | dcb.operrsta = 0; |
7d68510b | 229 | dcb.devselect = unit | dkflags[ctlr][unit]; |
bff99b3e SL |
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); | |
39b72d60 SL |
237 | } |
238 | ||
bff99b3e | 239 | vdstrategy(io, cmd) |
4006c5e1 | 240 | register struct iob *io; |
bff99b3e | 241 | int cmd; |
39b72d60 | 242 | { |
bff99b3e | 243 | register struct disklabel *lp; |
7d68510b | 244 | int ctlr, cn, tn, sn, slave, retries = 0; |
bff99b3e SL |
245 | daddr_t bn; |
246 | struct vddevice *vdaddr; | |
4006c5e1 SL |
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); | |
d4285b3c | 251 | io->i_error = EIO; |
4006c5e1 | 252 | return (-1); |
39b72d60 | 253 | } |
cb69d88e | 254 | lp = &dklabel[io->i_ctlr][io->i_unit]; |
bff99b3e SL |
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; | |
39b72d60 | 260 | |
7d68510b | 261 | top: |
800bc4d3 | 262 | dcb.opcode = (cmd == F_READ ? VDOP_RD : VDOP_WD); |
bff99b3e SL |
263 | dcb.intflg = DCBINT_NONE; |
264 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
39b72d60 | 265 | dcb.operrsta = 0; |
cb69d88e KB |
266 | ctlr = io->i_ctlr; |
267 | slave = io->i_unit; | |
7d68510b | 268 | dcb.devselect = slave | dkflags[ctlr][slave]; |
bdfd2fb7 MK |
269 | dcb.trailcnt = sizeof (struct trrw) / sizeof (int); |
270 | dcb.trail.rwtrail.memadr = (u_long)io->i_ma; | |
bff99b3e SL |
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; | |
bff99b3e SL |
277 | vdaddr = VDADDR(ctlr); |
278 | VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]); | |
279 | if (!vdpoll(vdaddr, &dcb, 60, vdtype[ctlr])) | |
4006c5e1 | 280 | _stop(" during i/o operation.\n"); |
bff99b3e | 281 | if (dcb.operrsta & VDERR_HARD) { |
7d68510b MK |
282 | if (retries++ == 0 && vdreset_ctlr(ctlr, io->i_unit) == 0 && |
283 | vdreset_drive(io)) | |
284 | goto top; | |
800bc4d3 | 285 | vderror(io->i_unit, cmd == F_READ ? "read" : "write", &dcb); |
bff99b3e SL |
286 | io->i_error = EIO; |
287 | return (-1); | |
288 | } | |
289 | mtpr(PADC, 0); | |
290 | return (io->i_cc); | |
39b72d60 SL |
291 | } |
292 | ||
bff99b3e SL |
293 | vderror(unit, cmd, dcb) |
294 | int unit; | |
295 | char *cmd; | |
296 | struct dcb *dcb; | |
39b72d60 | 297 | { |
4006c5e1 | 298 | |
bff99b3e SL |
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); | |
39b72d60 | 303 | printf("\n"); |
39b72d60 SL |
304 | } |
305 | ||
1a9cb196 | 306 | /* |
bff99b3e SL |
307 | * Poll controller until operation |
308 | * completes or timeout expires. | |
1a9cb196 | 309 | */ |
bff99b3e SL |
310 | vdpoll(vdaddr, dcb, t, type) |
311 | register struct vddevice *vdaddr; | |
312 | register struct dcb *dcb; | |
1a9cb196 SL |
313 | register int t, type; |
314 | { | |
315 | ||
316 | t *= 1000; | |
d4285b3c | 317 | for (;;) { |
1a9cb196 | 318 | uncache(&dcb->operrsta); |
bff99b3e | 319 | if (dcb->operrsta & (DCBS_DONE|DCBS_ABORT)) |
d4285b3c | 320 | break; |
1a9cb196 SL |
321 | if (--t <= 0) { |
322 | printf("vd: controller timeout"); | |
bff99b3e | 323 | VDABORT(vdaddr, type); |
1a9cb196 SL |
324 | DELAY(30000); |
325 | uncache(&dcb->operrsta); | |
326 | return (0); | |
327 | } | |
d4285b3c | 328 | DELAY(1000); |
1a9cb196 | 329 | } |
bff99b3e | 330 | if (type == VDTYPE_SMDE) { |
d4285b3c | 331 | for (;;) { |
bff99b3e SL |
332 | uncache(&vdaddr->vdcsr); |
333 | if ((vdaddr->vdcsr & CS_GO) == 0) | |
d4285b3c SL |
334 | break; |
335 | DELAY(50); | |
1a9cb196 SL |
336 | } |
337 | DELAY(300); | |
7f6fe1b4 | 338 | uncache(&dcb->err_code); |
1a9cb196 SL |
339 | } |
340 | DELAY(200); | |
341 | uncache(&dcb->operrsta); | |
342 | return (1); | |
343 | } | |
bff99b3e SL |
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 */ | |
7d68510b | 350 | int secsize; /* sector size */ |
bff99b3e SL |
351 | #define NPART 2 |
352 | int poff[NPART]; /* [a+b] for bootstrapping */ | |
353 | } dkcompat[] = { | |
ef960e6f | 354 | { 64, 20, 842, 512, 0, 61440 }, /* 2361a eagle */ |
7d68510b MK |
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 */ | |
bff99b3e SL |
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 | { | |
cb69d88e | 373 | register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit]; |
bff99b3e | 374 | register struct dkcompat *dp; |
7d68510b | 375 | int i, ctlr, slave, type; |
bff99b3e SL |
376 | struct vddevice *vdaddr; |
377 | ||
cb69d88e KB |
378 | ctlr = io->i_ctlr; |
379 | slave = io->i_unit; | |
bff99b3e SL |
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; | |
7d68510b | 388 | lp->d_secsize = dp->secsize; |
bff99b3e SL |
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 */ | |
7d68510b | 394 | dcb.devselect = slave | dkflags[ctlr][slave]; |
bff99b3e | 395 | dcb.operrsta = 0; |
bdfd2fb7 MK |
396 | dcb.trailcnt = sizeof (struct trrw) / sizeof (long); |
397 | dcb.trail.rwtrail.memadr = (u_long)lbuf; | |
7d68510b | 398 | dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof (short); |
bff99b3e SL |
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 |