Commit | Line | Data |
---|---|---|
c7c48d07 | 1 | /* vd.c 1.7 86/01/21 */ |
210a9f40 SL |
2 | |
3 | #include "fsd.h" | |
4 | #if NVD > 0 | |
5 | /* | |
9d915fad SL |
6 | * VDDC - Versabus SMD/ESMD driver. |
7 | */ | |
4db7c84e SL |
8 | #ifdef VDDCPERF |
9 | #define DOSCOPE | |
10 | #endif | |
11 | ||
9d915fad SL |
12 | #include "../tahoe/mtpr.h" |
13 | #include "../tahoe/pte.h" | |
14 | ||
15 | #include "param.h" | |
16 | #include "buf.h" | |
17 | #include "cmap.h" | |
18 | #include "conf.h" | |
19 | #include "dir.h" | |
20 | #include "dk.h" | |
21 | #include "map.h" | |
22 | #include "systm.h" | |
23 | #include "user.h" | |
24 | #include "vmmac.h" | |
25 | #include "proc.h" | |
26 | #include "uio.h" | |
27 | ||
28 | #include "../tahoevba/vbavar.h" | |
29 | #define VDGENDATA | |
c7c48d07 | 30 | #include "../tahoevba/vdreg.h" |
9d915fad | 31 | #undef VDGENDATA |
4db7c84e | 32 | #include "../tahoevba/scope.h" |
9d915fad | 33 | |
7c4f3479 | 34 | #define VDMAXIO (MAXBPTE*NBPG) |
9d915fad | 35 | #define DUMPSIZE 64 /* controller limit */ |
210a9f40 SL |
36 | |
37 | #define VDUNIT(x) (minor(x) >> 3) | |
9d915fad SL |
38 | #define FILSYS(x) (minor(x) & 0x07) |
39 | #define PHYS(x) (vtoph((struct proc *)0, (unsigned)(x))) | |
40 | #define TRUE 1 | |
41 | #define FALSE 0 | |
210a9f40 | 42 | |
9d915fad SL |
43 | #define CTLR_ERROR 1 |
44 | #define DRIVE_ERROR 2 | |
45 | #define HARD_DATA_ERROR 3 | |
46 | #define SOFT_DATA_ERROR 4 | |
47 | ||
48 | #define b_cylin b_resid | |
49 | #define b_daddr b_error | |
210a9f40 SL |
50 | |
51 | struct vba_ctlr *vdminfo[NVD]; | |
52 | struct vba_device *vddinfo[NFSD]; | |
9d915fad SL |
53 | int vdprobe(), vdslave(), vdattach(), vddgo(); |
54 | struct vba_driver vddriver = | |
55 | { vdprobe, vdslave, vdattach, vddgo, vddcaddr, "smd/fsd", | |
56 | vddinfo, "vd", vdminfo }; | |
210a9f40 SL |
57 | |
58 | /* | |
9d915fad SL |
59 | * Per-drive state. |
60 | */ | |
61 | typedef struct { | |
62 | struct buf raw_q_element; | |
63 | short sec_per_blk; | |
64 | short sec_per_cyl; | |
65 | char status; | |
66 | struct buf xfer_queue; | |
67 | int drive_type; | |
68 | fs_tab info; | |
69 | } unit_tab; | |
210a9f40 SL |
70 | |
71 | /* | |
9d915fad SL |
72 | * Per-controller state. |
73 | */ | |
74 | typedef struct { | |
75 | char ctlr_type; /* controller type */ | |
7c4f3479 SL |
76 | struct pte *map; /* i/o page map */ |
77 | caddr_t utl; /* mapped i/o space */ | |
9d915fad SL |
78 | u_int cur_slave:8; /* last active unit number */ |
79 | u_int int_expected:1; /* expect an interupt */ | |
80 | u_int ctlr_started:1; /* start command was issued */ | |
81 | u_int overlap_seeks:1;/* should overlap seeks */ | |
7c4f3479 | 82 | u_int initdone:1; /* controller initialization completed */ |
9d915fad SL |
83 | u_int off_cylinder:16;/* off cylinder bit map */ |
84 | u_int unit_type[16]; /* slave types */ | |
85 | u_int cur_cyl[16]; /* cylinder last selected */ | |
86 | long cur_trk[16]; /* track last selected */ | |
87 | fmt_mdcb ctlr_mdcb; /* controller mdcb */ | |
88 | fmt_dcb ctlr_dcb; /* r/w dcb */ | |
89 | fmt_dcb seek_dcb[4]; /* dcbs for overlapped seeks */ | |
90 | /* buffer for raw/swap i/o */ | |
7c4f3479 | 91 | char rawbuf[VDMAXIO]; |
9d915fad SL |
92 | } ctlr_tab; |
93 | ||
7c4f3479 | 94 | ctlr_tab vdctlr_info[NVD]; |
9d915fad | 95 | unit_tab vdunit_info[NFSD]; |
210a9f40 SL |
96 | |
97 | /* | |
9d915fad SL |
98 | * See if the controller is really there; if so, initialize it. |
99 | */ | |
336ca318 SL |
100 | vdprobe(reg, vm) |
101 | caddr_t reg; | |
102 | struct vba_ctlr *vm; | |
210a9f40 | 103 | { |
336ca318 | 104 | register br, cvec; /* must be r12, r11 */ |
7c4f3479 SL |
105 | register cdr *addr = (cdr *)reg; |
106 | register ctlr_tab *ci; | |
107 | int i; | |
336ca318 SL |
108 | |
109 | if (badaddr((caddr_t)reg, 2)) | |
9d915fad | 110 | return (0); |
7c4f3479 SL |
111 | ci = &vdctlr_info[vm->um_ctlr]; |
112 | addr->cdr_reset = 0xffffffff; | |
9d915fad | 113 | DELAY(1000000); |
7c4f3479 SL |
114 | if (addr->cdr_reset != (unsigned)0xffffffff) { |
115 | ci->ctlr_type = SMDCTLR; | |
116 | ci->overlap_seeks = 0; | |
9d915fad SL |
117 | DELAY(1000000); |
118 | } else { | |
7c4f3479 SL |
119 | ci->overlap_seeks = 1; |
120 | ci->ctlr_type = SMD_ECTLR; | |
121 | addr->cdr_reserved = 0x0; | |
9d915fad | 122 | DELAY(3000000); |
7c4f3479 SL |
123 | addr->cdr_csr = 0; |
124 | addr->mdcb_tcf = AM_ENPDA; | |
125 | addr->dcb_tcf = AM_ENPDA; | |
126 | addr->trail_tcf = AM_ENPDA; | |
127 | addr->data_tcf = AM_ENPDA; | |
128 | addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | | |
129 | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; | |
210a9f40 | 130 | } |
7c4f3479 SL |
131 | /* |
132 | * Allocate page tables. | |
133 | */ | |
134 | vbmapalloc(btoc(VDMAXIO)+1, &ci->map, &ci->utl); | |
135 | /* | |
136 | * Initialize all the drives to be of an unknown type. | |
137 | */ | |
138 | for (i = 0; i < 15; i++) | |
139 | ci->unit_type[i] = UNKNOWN; | |
336ca318 | 140 | br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */ |
7c4f3479 | 141 | return (sizeof (*addr)); |
9d915fad | 142 | } |
210a9f40 SL |
143 | |
144 | /* | |
145 | * See if a drive is really there | |
9d915fad SL |
146 | * Try to reset/configure the drive, then test its status. |
147 | */ | |
148 | vdslave(vi, addr) | |
149 | register struct vba_device *vi; | |
150 | register cdr *addr; | |
210a9f40 | 151 | { |
9d915fad SL |
152 | register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; |
153 | register unit_tab *ui = &vdunit_info[vi->ui_unit]; | |
154 | register fmt_mdcb *mdcb = &ci->ctlr_mdcb; | |
155 | register fmt_dcb *dcb = &ci->ctlr_dcb; | |
156 | register int type; | |
157 | ||
7c4f3479 SL |
158 | if (!ci->initdone) { |
159 | printf("vd%d: %s controller\n", vi->ui_ctlr, | |
36dcdb9b | 160 | ci->ctlr_type == SMDCTLR ? "smd" : "xsmd"); |
7c4f3479 SL |
161 | if (vdnotrailer(addr, vi->ui_ctlr, vi->ui_slave, INIT, 10) & |
162 | HRDERR) { | |
163 | printf("vd%d: init error\n", vi->ui_ctlr); | |
9d915fad SL |
164 | return (0); |
165 | } | |
7c4f3479 SL |
166 | if (vdnotrailer(addr, vi->ui_ctlr, vi->ui_slave, DIAG, 10) & |
167 | HRDERR) { | |
168 | printf("vd%d: diagnostic error\n", vi->ui_ctlr); | |
9d915fad SL |
169 | return (0); |
170 | } | |
7c4f3479 | 171 | ci->initdone = 1; |
210a9f40 | 172 | } |
210a9f40 | 173 | /* |
210a9f40 | 174 | * Seek on all drive types starting from the largest one. |
9d915fad | 175 | * a successful seek to the last sector/cylinder/track verifies |
210a9f40 SL |
176 | * the drive type connected to this port. |
177 | */ | |
9d915fad SL |
178 | for (type = 0; type < nvddrv; type++) { |
179 | /* XXX */ | |
180 | if (ci->ctlr_type == SMDCTLR && vdst[type].nsec != 32) | |
181 | continue; | |
182 | /* XXX */ | |
183 | if (!vdconfigure_drive(addr, vi->ui_ctlr, vi->ui_slave, type,0)) | |
184 | return (0); | |
185 | dcb->opcode = (short)RD; | |
210a9f40 | 186 | dcb->intflg = NOINT; |
9d915fad | 187 | dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ |
210a9f40 | 188 | dcb->operrsta = 0; |
9d915fad SL |
189 | dcb->devselect = (char)(vi->ui_slave); |
190 | dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); | |
191 | dcb->trail.rwtrail.memadr = (char *)PHYS(ci->rawbuf); | |
192 | dcb->trail.rwtrail.wcount = vdst[type].secsize/sizeof(short); | |
193 | dcb->trail.rwtrail.disk.cylinder = vdst[type].ncyl - 2; | |
194 | dcb->trail.rwtrail.disk.track = vdst[type].ntrak - 1; | |
195 | dcb->trail.rwtrail.disk.sector = vdst[type].nsec - 1; | |
196 | mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); | |
197 | mdcb->vddcstat = 0; | |
198 | VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); | |
7c4f3479 | 199 | if (!vdpoll(ci, addr, 60)) |
9d915fad SL |
200 | printf(" during probe\n"); |
201 | if ((dcb->operrsta&HRDERR) == 0) | |
210a9f40 SL |
202 | break; |
203 | } | |
9d915fad SL |
204 | if (type >= nvddrv) { |
205 | /* | |
206 | * If reached here, a drive which is not defined in the | |
210a9f40 SL |
207 | * 'vdst' tables is connected. Cannot set it's type. |
208 | */ | |
9d915fad SL |
209 | printf("vd%d: unknown drive type\n", vi->ui_unit); |
210 | return (0); | |
211 | } | |
212 | ui->drive_type = type; | |
213 | ui->info = vdst[type]; | |
214 | ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; | |
215 | vi->ui_type = type; | |
216 | vi->ui_dk = 1; | |
217 | vddriver.ud_dname = ui->info.type_name; | |
218 | return (1); | |
219 | } | |
220 | ||
221 | vdconfigure_drive(addr, ctlr, slave, type, pass) | |
222 | register cdr *addr; | |
223 | int ctlr, slave, type, pass; | |
224 | { | |
225 | register ctlr_tab *ci = &vdctlr_info[ctlr]; | |
226 | ||
227 | ci->ctlr_dcb.opcode = RSTCFG; /* command */ | |
228 | ci->ctlr_dcb.intflg = NOINT; | |
229 | ci->ctlr_dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */ | |
230 | ci->ctlr_dcb.operrsta = 0; | |
231 | ci->ctlr_dcb.devselect = (char)slave; | |
232 | ci->ctlr_dcb.trail.rstrail.ncyl = vdst[type].ncyl; | |
233 | ci->ctlr_dcb.trail.rstrail.nsurfaces = vdst[type].ntrak; | |
234 | if (ci->ctlr_type == SMD_ECTLR) { | |
235 | ci->ctlr_dcb.trailcnt = (char)4; | |
236 | ci->ctlr_dcb.trail.rstrail.nsectors = vdst[type].nsec; | |
237 | ci->ctlr_dcb.trail.rstrail.slip_sec = vdst[type].nslip; | |
238 | } else | |
239 | ci->ctlr_dcb.trailcnt = (char)2; | |
240 | ci->ctlr_mdcb.firstdcb = (fmt_dcb *)(PHYS(&ci->ctlr_dcb)); | |
241 | ci->ctlr_mdcb.vddcstat = 0; | |
242 | VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(&ci->ctlr_mdcb)), ci->ctlr_type); | |
7c4f3479 | 243 | if (!vdpoll(ci, addr, 5)) { |
9d915fad SL |
244 | printf(" during config\n"); |
245 | return (0); | |
246 | } | |
247 | if (ci->ctlr_dcb.operrsta & HRDERR) { | |
248 | if ((ci->ctlr_dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0) | |
249 | printf("vd%d: drive %d: config error\n", ctlr, slave); | |
250 | else if (pass == 0) { | |
251 | vdstart_drive(addr, ctlr, slave); | |
252 | return (vdconfigure_drive(addr, ctlr, slave, type, 1)); | |
253 | } else if (pass == 2) | |
254 | return (vdconfigure_drive(addr, ctlr, slave, type, 3)); | |
255 | return (0); | |
256 | } | |
257 | return (1); | |
258 | } | |
259 | ||
260 | vdstart_drive(addr, ctlr, slave) | |
261 | cdr *addr; | |
262 | register int ctlr, slave; | |
263 | { | |
264 | int error = 0; | |
265 | ||
266 | printf("vd%d: starting drive %d, wait...", ctlr, slave); | |
267 | if (vdctlr_info[ctlr].ctlr_started) { | |
268 | printf("DELAY(5500000)..."); | |
269 | DELAY(5500000); | |
270 | goto done; | |
271 | } | |
272 | vdctlr_info[ctlr].ctlr_started = 1; | |
273 | error = vdnotrailer(addr, ctlr, 0, VDSTART, (slave*6)+62) & HRDERR; | |
274 | if (!error) { | |
275 | printf("DELAY(%d)...", (slave * 5500000) + 62000000); | |
276 | DELAY((slave * 5500000) + 62000000); | |
277 | } | |
278 | done: | |
279 | printf("\n"); | |
280 | return (error == 0); | |
281 | } | |
282 | ||
283 | vdnotrailer(addr, ctlr, unit, function, time) | |
284 | register cdr *addr; | |
285 | int ctlr, unit, function, time; | |
286 | { | |
7c4f3479 SL |
287 | register ctlr_tab *ci = &vdctlr_info[ctlr]; |
288 | fmt_mdcb *mdcb = &ci->ctlr_mdcb; | |
289 | fmt_dcb *dcb = &ci->ctlr_dcb; | |
9d915fad SL |
290 | |
291 | dcb->opcode = function; /* command */ | |
292 | dcb->intflg = NOINT; | |
293 | dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ | |
294 | dcb->operrsta = 0; | |
295 | dcb->devselect = (char)unit; | |
296 | dcb->trailcnt = (char)0; | |
297 | mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); | |
298 | mdcb->vddcstat = 0; | |
7c4f3479 SL |
299 | VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); |
300 | if (!vdpoll(ci, addr, time)) { | |
9d915fad SL |
301 | printf(" during init\n"); |
302 | return (DCBCMP|ANYERR|HRDERR|OPABRT); | |
210a9f40 | 303 | } |
9d915fad | 304 | return (dcb->operrsta); |
210a9f40 SL |
305 | } |
306 | ||
9d915fad SL |
307 | vdattach(vi) |
308 | register struct vba_device *vi; | |
210a9f40 | 309 | { |
9d915fad SL |
310 | register unit_tab *ui = &vdunit_info[vi->ui_unit]; |
311 | register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; | |
312 | register struct buf *cq = &vi->ui_mi->um_tab; | |
313 | register struct buf *uq = cq->b_forw; | |
314 | register struct buf *start_queue = uq; | |
315 | register fs_tab *fs = &ui->info; | |
316 | ||
317 | ui->info = vdst[vi->ui_type]; | |
318 | ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; | |
319 | ui->sec_per_cyl = ui->info.nsec * ui->info.ntrak; | |
320 | ui->xfer_queue.b_dev = vi->ui_slave; | |
321 | ci->unit_type[vi->ui_slave] = vi->ui_type; | |
322 | /* load unit into controller's active unit list */ | |
323 | if (uq == NULL) { | |
324 | cq->b_forw = &ui->xfer_queue; | |
325 | ui->xfer_queue.b_forw = &ui->xfer_queue; | |
326 | ui->xfer_queue.b_back = &ui->xfer_queue; | |
327 | } else { | |
328 | while (uq->b_forw != start_queue) | |
329 | uq = uq->b_forw; | |
330 | ui->xfer_queue.b_forw = start_queue; | |
331 | ui->xfer_queue.b_back = uq; | |
332 | uq->b_forw = &ui->xfer_queue; | |
333 | start_queue->b_back = &ui->xfer_queue; | |
334 | } | |
335 | /* | |
336 | * (60 / rpm) / (number of sectors per track * (bytes per sector / 2)) | |
337 | */ | |
338 | dk_mspw[vi->ui_unit] = 120.0 / (fs->rpm * fs->nsec * fs->secsize); | |
210a9f40 SL |
339 | } |
340 | ||
9d915fad | 341 | /*ARGSUSED*/ |
210a9f40 | 342 | vddgo(um) |
9d915fad | 343 | struct vba_ctlr *um; |
210a9f40 | 344 | { |
9d915fad | 345 | |
210a9f40 SL |
346 | } |
347 | ||
348 | vdstrategy(bp) | |
9d915fad | 349 | register struct buf *bp; |
210a9f40 | 350 | { |
9d915fad SL |
351 | register int unit = VDUNIT(bp->b_dev); |
352 | register struct vba_device *vi = vddinfo[unit]; | |
353 | register par_tab *par; | |
354 | register unit_tab *ui; | |
355 | register fs_tab *fs; | |
356 | register int blks, bn, s; | |
357 | ||
358 | if (bp->b_bcount == 0 || vi == 0 || vi->ui_alive == 0) | |
359 | goto bad; | |
360 | ui = &vdunit_info[unit]; | |
361 | fs = &ui->info; | |
362 | par = &fs->partition[FILSYS(bp->b_dev)]; | |
363 | blks = (bp->b_bcount + DEV_BSIZE-1) >> DEV_BSHIFT; | |
364 | if (bp->b_blkno + blks >= par->par_len) { | |
365 | blks = par->par_len - bp->b_blkno; | |
366 | if (blks <= 0) | |
367 | goto bad; | |
368 | bp->b_bcount = blks * DEV_BSIZE; | |
210a9f40 | 369 | } |
9d915fad SL |
370 | bn = bp->b_blkno + par->par_start; |
371 | bn *= ui->sec_per_blk; | |
372 | bp->b_daddr = (bn / fs->nsec) % fs->ntrak; | |
373 | bp->b_cylin = bn / ui->sec_per_cyl; | |
374 | vbasetup(bp, ui->info.secsize); | |
375 | s = spl7(); | |
376 | if (ui->xfer_queue.av_forw == NULL) { | |
377 | register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; | |
378 | int slave = vi->ui_slave; | |
379 | ||
380 | if (bp->b_cylin != ci->cur_cyl[slave] || | |
381 | bp->b_daddr != ci->cur_trk[slave]) | |
382 | ci->off_cylinder |= 1 << slave; | |
383 | } | |
384 | bp->b_daddr |= (bn % fs->nsec) << 8; | |
385 | disksort(&ui->xfer_queue, bp); | |
386 | if (!vddinfo[unit]->ui_mi->um_tab.b_active++) { | |
387 | splx(s); | |
388 | vdstart(vddinfo[unit]->ui_mi); | |
389 | } else | |
390 | splx(s); | |
210a9f40 | 391 | return; |
9d915fad SL |
392 | bad: |
393 | bp->b_flags |= B_ERROR, bp->b_error = ENXIO; | |
394 | bp->b_resid = bp->b_bcount; | |
210a9f40 | 395 | iodone(bp); |
210a9f40 SL |
396 | } |
397 | ||
210a9f40 SL |
398 | /* |
399 | * Start up a transfer on a drive. | |
400 | */ | |
9d915fad SL |
401 | vdstart(ci) |
402 | register struct vba_ctlr *ci; | |
403 | { | |
404 | register struct buf *cq = &ci->um_tab; | |
405 | register struct buf *uq = cq->b_forw; | |
406 | ||
407 | /* search for next ready unit */ | |
408 | cq->b_forw = cq->b_forw->b_forw; | |
409 | uq = cq->b_forw; | |
410 | do { | |
411 | if (uq->av_forw != NULL) { | |
412 | cq->b_forw = uq; | |
413 | vdexecute(ci, uq); | |
414 | return; | |
415 | } | |
416 | uq = uq->b_forw; | |
417 | } while (uq != cq->b_forw); | |
418 | } | |
419 | ||
420 | /* | |
421 | * Initiate seeks for all drives off-cylinder. | |
422 | */ | |
423 | vdload_seeks(ci, uq) | |
424 | register ctlr_tab *ci; | |
425 | register struct buf *uq; | |
426 | { | |
427 | register int unit, slave, nseeks; | |
428 | register fmt_dcb *dcb; | |
429 | register struct buf *bp; | |
430 | register struct buf *start_queue = uq; | |
431 | ||
432 | nseeks = 0; | |
433 | do { | |
434 | bp = uq->av_forw; | |
435 | if (bp != NULL) { | |
436 | unit = VDUNIT(bp->b_dev); | |
437 | slave = vddinfo[unit]->ui_slave; | |
438 | if (ci->off_cylinder & (1 << slave)) { | |
439 | ci->off_cylinder &= ~(1 << slave); | |
440 | if (ci->cur_cyl[slave] != bp->b_cylin) { | |
441 | ci->cur_cyl[slave] = bp->b_cylin; | |
442 | dk_seek[unit]++; | |
443 | } | |
444 | ci->cur_trk[slave] = bp->b_daddr&0xff; | |
445 | dcb = &ci->seek_dcb[nseeks++]; | |
446 | dcb->opcode = SEEK; | |
447 | dcb->intflg = NOINT | INT_PBA; | |
448 | dcb->operrsta = 0; | |
449 | dcb->devselect = (char)slave; | |
450 | dcb->trailcnt = (char)1; | |
451 | dcb->trail.sktrail.skaddr.cylinder = | |
452 | bp->b_cylin; | |
453 | dcb->trail.sktrail.skaddr.track = | |
454 | bp->b_daddr & 0xff; | |
455 | dcb->trail.sktrail.skaddr.sector = 0; | |
456 | } | |
457 | } | |
458 | uq = uq->b_forw; | |
459 | } while (uq != start_queue && nseeks < 4); | |
460 | return (nseeks); | |
461 | } | |
462 | ||
463 | extern vd_int_timeout(); | |
464 | /* | |
465 | * Execute the next command on the unit queue uq. | |
466 | */ | |
467 | vdexecute(controller_info, uq) | |
468 | register struct vba_ctlr *controller_info; | |
469 | register struct buf *uq; | |
210a9f40 | 470 | { |
9d915fad SL |
471 | register struct buf *bp = uq->av_forw; |
472 | register int ctlr = controller_info->um_ctlr; | |
473 | register ctlr_tab *ci = &vdctlr_info[ctlr]; | |
474 | register int unit = VDUNIT(bp->b_dev); | |
475 | register int slave = vddinfo[unit]->ui_slave; | |
476 | register fmt_mdcb *mdcb = &ci->ctlr_mdcb; | |
477 | register fmt_dcb *dcb = &ci->ctlr_dcb; | |
478 | ||
210a9f40 | 479 | /* |
9d915fad SL |
480 | * If there are overlapped seeks to perform, shuffle |
481 | * them to the front of the queue and get them started | |
482 | * before any data transfers (to get some parallelism). | |
210a9f40 | 483 | */ |
9d915fad SL |
484 | if ((ci->off_cylinder & ~(1<<slave)) && ci->overlap_seeks) { |
485 | register int i, nseeks; | |
486 | ||
487 | /* setup seek requests in seek-q */ | |
488 | nseeks = vdload_seeks(ci, uq); | |
489 | /* place at the front of the master q */ | |
490 | mdcb->firstdcb = (fmt_dcb *)PHYS(&ci->seek_dcb[0]); | |
491 | /* shuffle any remaining seeks up in the seek-q */ | |
492 | for (i = 1; i < nseeks; i++) | |
493 | ci->seek_dcb[i-1].nxtdcb = | |
494 | (fmt_dcb *)PHYS(&ci->seek_dcb[i]); | |
495 | ci->seek_dcb[nseeks-1].nxtdcb = (fmt_dcb *)PHYS(dcb); | |
496 | } else { | |
497 | if (bp->b_cylin != ci->cur_cyl[slave]) { | |
498 | ci->cur_cyl[slave] = bp->b_cylin; | |
499 | dk_seek[unit]++; | |
500 | } | |
501 | ci->cur_trk[slave] = bp->b_daddr & 0xff; | |
502 | ci->off_cylinder = 0; | |
503 | mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); | |
210a9f40 | 504 | } |
210a9f40 | 505 | dcb->opcode = (bp->b_flags & B_READ) ? RD : WD; |
9d915fad SL |
506 | dcb->intflg = INTDONE; |
507 | dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ | |
210a9f40 | 508 | dcb->operrsta = 0; |
9d915fad SL |
509 | dcb->devselect = (char)slave; |
510 | dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); | |
511 | dcb->trail.rwtrail.memadr = (char *) | |
512 | vbastart(bp, ci->rawbuf, (long *)ci->map, ci->utl); | |
513 | dcb->trail.rwtrail.wcount = (short)((bp->b_bcount+1) / sizeof (short)); | |
514 | dcb->trail.rwtrail.disk.cylinder = bp->b_cylin; | |
515 | dcb->trail.rwtrail.disk.track = bp->b_daddr & 0xff; | |
516 | dcb->trail.rwtrail.disk.sector = bp->b_daddr >> 8; | |
517 | mdcb->vddcstat = 0; | |
518 | dk_wds[unit] += bp->b_bcount / 32; | |
519 | ci->int_expected = 1; | |
520 | timeout(vd_int_timeout, (caddr_t)ctlr, 20*60); | |
521 | dk_busy |= 1 << unit; | |
210a9f40 | 522 | scope_out(1); |
9d915fad SL |
523 | VDDC_ATTENTION((cdr *)(vdminfo[ctlr]->um_addr), |
524 | (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); | |
210a9f40 SL |
525 | } |
526 | ||
9d915fad SL |
527 | /* |
528 | * Watch for lost interrupts. | |
529 | */ | |
530 | vd_int_timeout(ctlr) | |
531 | register int ctlr; | |
532 | { | |
533 | register ctlr_tab *ci = &vdctlr_info[ctlr]; | |
534 | register fmt_dcb *dcb = &ci->ctlr_dcb; | |
535 | ||
536 | uncache(&dcb->operrsta); | |
537 | printf("vd%d: lost interupt, status %x", ctlr, dcb->operrsta); | |
538 | if (ci->ctlr_type == SMD_ECTLR) { | |
539 | uncache(&dcb->err_code); | |
540 | printf(", error code %x", dcb->err_code); | |
541 | } | |
542 | printf("\n"); | |
543 | if ((dcb->operrsta&DCBCMP) == 0) { | |
544 | VDDC_ABORT((cdr *)(vdminfo[ctlr]->um_addr), ci->ctlr_type); | |
545 | dcb->operrsta |= DCBUSC | DCBABT | ANYERR | HRDERR | CTLRERR; | |
546 | } | |
547 | vdintr(ctlr); | |
548 | } | |
210a9f40 SL |
549 | |
550 | /* | |
551 | * Handle a disk interrupt. | |
552 | */ | |
9d915fad SL |
553 | vdintr(ctlr) |
554 | register int ctlr; | |
210a9f40 | 555 | { |
9d915fad SL |
556 | register ctlr_tab *ci; |
557 | register struct buf *cq, *uq, *bp; | |
558 | register int slave, unit; | |
559 | register fmt_mdcb *mdcb; | |
560 | register fmt_dcb *dcb; | |
561 | int code, s; | |
562 | ||
563 | untimeout(vd_int_timeout, (caddr_t)ctlr); | |
210a9f40 | 564 | scope_out(2); |
9d915fad SL |
565 | ci = &vdctlr_info[ctlr]; |
566 | if (!ci->int_expected) { | |
567 | printf("vd%d: stray interrupt\n", ctlr); | |
210a9f40 SL |
568 | return; |
569 | } | |
9d915fad SL |
570 | /* |
571 | * Take first request off controller's queue. | |
572 | */ | |
573 | cq = &vdminfo[ctlr]->um_tab; | |
574 | uq = cq->b_forw; | |
575 | bp = uq->av_forw; | |
210a9f40 | 576 | unit = VDUNIT(bp->b_dev); |
9d915fad SL |
577 | dk_busy &= ~(1 << unit); |
578 | dk_xfer[unit]++; | |
579 | ci->int_expected = 0; | |
580 | /* find associated control blocks */ | |
581 | mdcb = &ci->ctlr_mdcb, uncache(&mdcb->intdcb); | |
582 | dcb = &ci->ctlr_dcb, uncache(&dcb->operrsta); | |
583 | if (ci->ctlr_type == SMD_ECTLR) | |
584 | uncache(&dcb->err_code); | |
585 | slave = uq->b_dev; | |
586 | switch (code = vddecode_error(dcb)) { | |
587 | ||
588 | case CTLR_ERROR: | |
589 | case DRIVE_ERROR: | |
590 | if (cq->b_errcnt >= 2) | |
591 | vdhard_error(ci, bp, dcb); | |
592 | if (code == CTLR_ERROR) | |
593 | vdreset_ctlr((cdr *)vdminfo[ctlr]->um_addr, ctlr); | |
594 | else | |
595 | reset_drive((cdr *)vdminfo[ctlr]->um_addr, ctlr, | |
596 | slave, 2); | |
597 | if (cq->b_errcnt++ < 2) { /* retry error */ | |
598 | cq->b_forw = uq->b_back; | |
599 | vdstart(vdminfo[ctlr]); | |
600 | return; | |
601 | } | |
602 | bp->b_resid = bp->b_bcount; | |
603 | break; | |
604 | ||
605 | case HARD_DATA_ERROR: | |
606 | vdhard_error(ci, bp, dcb); | |
607 | bp->b_resid = 0; | |
608 | break; | |
609 | ||
610 | case SOFT_DATA_ERROR: | |
611 | vdsoft_error(ci, bp, dcb); | |
612 | /* fall thru... */ | |
613 | ||
614 | default: /* operation completed */ | |
615 | bp->b_error = 0; | |
616 | bp->b_resid = 0; | |
617 | break; | |
210a9f40 | 618 | } |
9d915fad SL |
619 | vbadone(bp, ci->rawbuf, (long *)ci->map, ci->utl); |
620 | /* | |
621 | * Take next request on this unit q, or, if none, | |
622 | * the next request on the next active unit q. | |
623 | */ | |
624 | s = spl7(); | |
625 | uq->av_forw = bp->av_forw; | |
626 | if (uq->av_back != bp) { | |
627 | register struct buf *next; | |
628 | ||
629 | unit = VDUNIT(uq->av_forw->b_dev); | |
630 | slave = vddinfo[unit]->ui_slave; | |
631 | next = uq->av_forw; | |
632 | if (next->b_cylin != ci->cur_cyl[slave] || | |
633 | (next->b_daddr & 0xff) != ci->cur_trk[slave]) | |
634 | ci->off_cylinder |= 1 << slave; | |
635 | } else | |
636 | uq->av_back = NULL; | |
637 | splx(s); | |
638 | /* reset controller state */ | |
639 | cq->b_errcnt = 0; | |
640 | cq->b_active--; | |
210a9f40 | 641 | scope_out(3); |
9d915fad SL |
642 | if (bp->b_flags & B_ERROR) |
643 | bp->b_error = EIO; | |
210a9f40 | 644 | iodone(bp); |
9d915fad SL |
645 | vdstart(vdminfo[ctlr]); |
646 | } | |
647 | ||
648 | /* | |
649 | * Convert controller status to internal operation/error code. | |
650 | */ | |
651 | vddecode_error(dcb) | |
652 | register fmt_dcb *dcb; | |
653 | { | |
654 | ||
655 | if (dcb->operrsta & HRDERR) { | |
656 | if (dcb->operrsta & (HCRCERR | HCMPERR | UCDATERR | WPTERR | | |
657 | DSEEKERR | NOTCYLERR |DRVNRDY | INVDADR)) | |
658 | return (DRIVE_ERROR); | |
659 | if (dcb->operrsta & (CTLRERR | OPABRT | INVCMD | DNEMEM)) | |
660 | return (CTLR_ERROR); | |
661 | return (HARD_DATA_ERROR); | |
662 | } | |
663 | if (dcb->operrsta & SFTERR) | |
664 | return (SOFT_DATA_ERROR); | |
665 | return (0); | |
666 | } | |
667 | ||
668 | /* | |
669 | * Report a hard error. | |
670 | */ | |
671 | vdhard_error(ci, bp, dcb) | |
672 | ctlr_tab *ci; | |
673 | register struct buf *bp; | |
674 | register fmt_dcb *dcb; | |
675 | { | |
676 | unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; | |
677 | ||
678 | bp->b_flags |= B_ERROR; | |
679 | harderr(bp, ui->info.type_name); | |
680 | printf("status %x", dcb->operrsta); | |
681 | if (ci->ctlr_type == SMD_ECTLR) | |
682 | printf(" ecode %x", dcb->err_code); | |
683 | printf("\n"); | |
210a9f40 SL |
684 | } |
685 | ||
9d915fad SL |
686 | /* |
687 | * Report a soft error. | |
688 | */ | |
689 | vdsoft_error(ci, bp, dcb) | |
690 | ctlr_tab *ci; | |
691 | register struct buf *bp; | |
692 | register fmt_dcb *dcb; | |
693 | { | |
694 | unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; | |
695 | ||
696 | printf("%s%d%c: soft error sn%d status %x", ui->info.type_name, | |
336ca318 | 697 | minor(bp->b_dev) >> 3, 'a'+(minor(bp->b_dev)&07), bp->b_blkno, |
9d915fad SL |
698 | dcb->operrsta); |
699 | if (ci->ctlr_type == SMD_ECTLR) | |
700 | printf(" ecode %x", dcb->err_code); | |
701 | printf("\n"); | |
702 | } | |
703 | ||
704 | /*ARGSUSED*/ | |
705 | vdopen(dev, flag) | |
706 | dev_t dev; | |
707 | int flag; | |
708 | { | |
709 | register unit = VDUNIT(dev); | |
710 | register struct vba_device *vi = vddinfo[unit]; | |
711 | ||
712 | if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) | |
713 | return (ENXIO); | |
714 | if (vdunit_info[unit].info.partition[FILSYS(dev)].par_len == 0) | |
715 | return (ENXIO); | |
716 | return (0); | |
717 | } | |
210a9f40 SL |
718 | |
719 | vdread(dev, uio) | |
9d915fad SL |
720 | dev_t dev; |
721 | struct uio *uio; | |
210a9f40 SL |
722 | { |
723 | register int unit = VDUNIT(dev); | |
9d915fad | 724 | register unit_tab *ui = &vdunit_info[unit]; |
210a9f40 SL |
725 | |
726 | if (unit >= NFSD) | |
9d915fad SL |
727 | return (ENXIO); |
728 | return (physio(vdstrategy, &ui->raw_q_element, dev, B_READ, | |
729 | minphys, uio)); | |
210a9f40 SL |
730 | } |
731 | ||
732 | vdwrite(dev, uio) | |
9d915fad SL |
733 | dev_t dev; |
734 | struct uio *uio; | |
210a9f40 SL |
735 | { |
736 | register int unit = VDUNIT(dev); | |
9d915fad | 737 | register unit_tab *ui = &vdunit_info[unit]; |
210a9f40 SL |
738 | |
739 | if (unit >= NFSD) | |
9d915fad SL |
740 | return (ENXIO); |
741 | return (physio(vdstrategy, &ui->raw_q_element, dev, B_WRITE, | |
742 | minphys, uio)); | |
210a9f40 SL |
743 | } |
744 | ||
210a9f40 | 745 | /* |
9d915fad | 746 | * Crash dump. |
210a9f40 | 747 | */ |
9d915fad SL |
748 | vddump(dev) |
749 | dev_t dev; | |
210a9f40 | 750 | { |
9d915fad SL |
751 | register int unit = VDUNIT(dev); |
752 | register unit_tab *ui = &vdunit_info[unit]; | |
753 | register fs_tab *fs = &ui->info; | |
754 | register int ctlr = vddinfo[unit]->ui_ctlr; | |
755 | register struct vba_ctlr *vba_vdctlr_info = vdminfo[ctlr]; | |
756 | register int filsys = FILSYS(dev); | |
757 | register cdr *addr = (cdr *)(vba_vdctlr_info->um_addr); | |
758 | register int cur_blk, blkcount, blocks; | |
759 | caddr_t memaddr; | |
760 | ||
761 | vdreset_ctlr(addr, ctlr); | |
210a9f40 | 762 | blkcount = maxfree - 2; /* In 1k byte pages */ |
9d915fad SL |
763 | if (dumplo + blkcount > fs->partition[filsys].par_len) { |
764 | blkcount = fs->partition[filsys].par_len - dumplo; | |
765 | printf("vd%d: Dump truncated to %dMB\n", unit, blkcount/1024); | |
766 | } | |
767 | cur_blk = fs->partition[filsys].par_start + dumplo; | |
768 | memaddr = 0; | |
210a9f40 | 769 | while (blkcount > 0) { |
9d915fad SL |
770 | blocks = MIN(blkcount, DUMPSIZE); |
771 | if (!vdwrite_block(addr, ctlr, unit, memaddr, cur_blk, blocks)) | |
772 | return (EIO); | |
773 | blkcount -= blocks; | |
774 | memaddr += blocks * NBPG; | |
775 | cur_blk += blocks; | |
210a9f40 | 776 | } |
9d915fad | 777 | return (0); |
210a9f40 SL |
778 | } |
779 | ||
9d915fad SL |
780 | /* |
781 | * Write a block to disk during a crash dump. | |
782 | */ | |
783 | vdwrite_block(caddr, ctlr, unit, addr, block, blocks) | |
784 | register cdr *caddr; | |
785 | register int ctlr, unit; | |
786 | register caddr_t addr; | |
787 | register int block, blocks; | |
210a9f40 | 788 | { |
7c4f3479 SL |
789 | register ctlr_tab *ci = &vdctlr_info[ctlr]; |
790 | register fmt_mdcb *mdcb = &ci->ctlr_mdcb; | |
791 | register fmt_dcb *dcb = &ci->ctlr_dcb; | |
9d915fad SL |
792 | register unit_tab *ui = &vdunit_info[unit]; |
793 | register fs_tab *fs = &ui->info; | |
794 | ||
795 | block *= (int)ui->sec_per_blk; | |
796 | blocks *= (int)ui->sec_per_blk; | |
797 | mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); | |
798 | dcb->intflg = NOINT; | |
799 | dcb->opcode = WD; | |
800 | dcb->operrsta = 0; | |
801 | dcb->devselect = (char)(vddinfo[unit])->ui_slave; | |
802 | dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); | |
803 | dcb->trail.rwtrail.memadr = addr; | |
804 | dcb->trail.rwtrail.wcount = (short) | |
805 | ((blocks * fs->secsize)/ sizeof (short)); | |
806 | dcb->trail.rwtrail.disk.cylinder = (short)(block / ui->sec_per_cyl); | |
807 | dcb->trail.rwtrail.disk.track = (char)((block / fs->nsec) % fs->ntrak); | |
808 | dcb->trail.rwtrail.disk.sector = (char)(block % fs->nsec); | |
7c4f3479 SL |
809 | VDDC_ATTENTION(caddr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); |
810 | if (!vdpoll(ci, caddr, 5)) { | |
9d915fad SL |
811 | printf(" during dump\n"); |
812 | return (0); | |
813 | } | |
814 | if (dcb->operrsta & HRDERR) { | |
815 | printf("vd%d: hard error, status %x\n", unit, dcb->operrsta); | |
816 | return (0); | |
817 | } | |
818 | return (1); | |
210a9f40 SL |
819 | } |
820 | ||
821 | vdsize(dev) | |
9d915fad | 822 | dev_t dev; |
210a9f40 | 823 | { |
9d915fad | 824 | struct vba_device *vi = vddinfo[VDUNIT(dev)]; |
210a9f40 | 825 | |
9d915fad SL |
826 | if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) |
827 | return (-1); | |
828 | return (vdunit_info[VDUNIT(dev)].info.partition[FILSYS(dev)].par_len); | |
210a9f40 SL |
829 | } |
830 | ||
9d915fad SL |
831 | /* |
832 | * Perform a controller reset. | |
833 | */ | |
834 | vdreset_ctlr(addr, ctlr) | |
835 | register cdr *addr; | |
836 | register int ctlr; | |
210a9f40 | 837 | { |
9d915fad SL |
838 | register struct buf *cq = &vdminfo[ctlr]->um_tab; |
839 | register struct buf *uq = cq->b_forw; | |
840 | register ctlr_tab *ci = &vdctlr_info[ctlr]; | |
841 | ||
842 | VDDC_RESET(addr, ci->ctlr_type); | |
843 | ci->ctlr_started = 0; | |
844 | if (ci->ctlr_type == SMD_ECTLR) { | |
845 | addr->cdr_csr = 0; | |
846 | addr->mdcb_tcf = AM_ENPDA; | |
847 | addr->dcb_tcf = AM_ENPDA; | |
848 | addr->trail_tcf = AM_ENPDA; | |
849 | addr->data_tcf = AM_ENPDA; | |
850 | addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | | |
851 | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; | |
852 | } | |
853 | if (vdnotrailer(addr, ctlr, 0, INIT, 10) & HRDERR) { | |
854 | printf("failed to init\n"); | |
855 | return (0); | |
856 | } | |
857 | if (vdnotrailer(addr, ctlr, 0, DIAG, 10) & HRDERR) { | |
858 | printf("diagnostic error\n"); | |
859 | return (0); | |
860 | } | |
861 | /* reset all units attached to controller */ | |
862 | uq = cq->b_forw; | |
863 | do { | |
864 | reset_drive(addr, ctlr, uq->b_dev, 0); | |
865 | uq = uq->b_forw; | |
866 | } while (uq != cq->b_forw); | |
867 | return (1); | |
868 | } | |
210a9f40 | 869 | |
9d915fad SL |
870 | /* |
871 | * Perform a reset on a drive. | |
872 | */ | |
873 | reset_drive(addr, ctlr, slave, start) | |
874 | register cdr *addr; | |
875 | register int ctlr, slave, start; | |
876 | { | |
877 | register int type = vdctlr_info[ctlr].unit_type[slave]; | |
878 | ||
879 | if (type == UNKNOWN) | |
880 | return; | |
881 | if (!vdconfigure_drive(addr, ctlr, slave, type, start)) | |
882 | printf("vd%d: drive %d: couldn't reset\n", ctlr, slave); | |
883 | } | |
884 | ||
7c4f3479 SL |
885 | /* |
886 | * Poll controller until operation completes | |
887 | * or timeout expires. | |
888 | */ | |
889 | vdpoll(ci, addr, t) | |
890 | register ctlr_tab *ci; | |
891 | register cdr *addr; | |
892 | register int t; | |
893 | { | |
894 | register fmt_dcb *dcb = &ci->ctlr_dcb; | |
895 | ||
896 | t *= 1000; | |
897 | uncache(&dcb->operrsta); | |
898 | while ((dcb->operrsta&(DCBCMP|DCBABT)) == 0) { | |
899 | DELAY(1000); | |
900 | uncache(&dcb->operrsta); | |
901 | if (--t <= 0) { | |
902 | printf("vd%d: controller timeout", ci-vdctlr_info); | |
903 | VDDC_ABORT(addr, ci->ctlr_type); | |
904 | DELAY(30000); | |
905 | uncache(&dcb->operrsta); | |
906 | return (0); | |
907 | } | |
908 | } | |
909 | if (ci->ctlr_type == SMD_ECTLR) { | |
910 | uncache(&addr->cdr_csr); | |
911 | while (addr->cdr_csr&CS_GO) { | |
912 | DELAY(50); | |
913 | uncache(&addr->cdr_csr); | |
914 | } | |
915 | DELAY(300); | |
916 | } | |
917 | DELAY(200); | |
918 | uncache(&dcb->operrsta); | |
919 | return (1); | |
920 | } | |
921 | ||
9d915fad SL |
922 | #ifdef notdef |
923 | /* | |
924 | * Dump the mdcb and DCB for diagnostic purposes. | |
925 | */ | |
926 | vdprintdcb(lp) | |
927 | register long *lp; | |
928 | { | |
929 | register int i, dcb, tc; | |
930 | ||
931 | for (dcb = 0; lp; lp = (long *)(*lp), dcb++) { | |
932 | lp = (long *)((long)lp | 0xc0000000); | |
933 | printf("\nDump of dcb%d@%x:", dcb, lp); | |
934 | for (i = 0, tc = lp[3] & 0xff; i < tc+7; i++) | |
935 | printf(" %lx", lp[i]); | |
936 | printf("\n"); | |
210a9f40 | 937 | } |
9d915fad | 938 | DELAY(1750000); |
210a9f40 SL |
939 | } |
940 | #endif | |
9d915fad | 941 | #endif |