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