Commit | Line | Data |
---|---|---|
736772ef | 1 | /* mba.c 4.9 81/02/25 */ |
b81fd3e8 BJ |
2 | |
3 | /* | |
4 | * Massbus driver; arbitrates massbusses through device driver routines | |
5 | * and provides common functions. | |
6 | */ | |
7 | int mbadebug = 0; | |
8 | #define dprintf if (mbadebug) printf | |
b5ad10c3 BJ |
9 | |
10 | #include "../h/param.h" | |
b81fd3e8 BJ |
11 | #include "../h/systm.h" |
12 | #include "../h/dk.h" | |
b5ad10c3 BJ |
13 | #include "../h/buf.h" |
14 | #include "../h/conf.h" | |
b5ad10c3 BJ |
15 | #include "../h/dir.h" |
16 | #include "../h/user.h" | |
17 | #include "../h/proc.h" | |
b5ad10c3 | 18 | #include "../h/map.h" |
b81fd3e8 | 19 | #include "../h/pte.h" |
b5ad10c3 BJ |
20 | #include "../h/mba.h" |
21 | #include "../h/mtpr.h" | |
22 | #include "../h/vm.h" | |
23 | ||
736772ef | 24 | char mbasr_bits[] = MBASR_BITS; |
b5ad10c3 | 25 | /* |
b81fd3e8 BJ |
26 | * Start activity on a massbus device. |
27 | * We are given the device's mba_info structure and activate | |
28 | * the device via the unit start routine. The unit start | |
29 | * routine may indicate that it is finished (e.g. if the operation | |
30 | * was a ``sense'' on a tape drive), that the (multi-ported) unit | |
31 | * is busy (we will get an interrupt later), that it started the | |
32 | * unit (e.g. for a non-data transfer operation), or that it has | |
33 | * set up a data transfer operation and we should start the massbus adaptor. | |
b5ad10c3 | 34 | */ |
b81fd3e8 BJ |
35 | mbustart(mi) |
36 | register struct mba_info *mi; | |
37 | { | |
38 | register struct mba_drv *mdp; /* drive registers */ | |
39 | register struct buf *bp; /* i/o operation at head of queue */ | |
40 | register struct mba_hd *mhp; /* header for mba device is on */ | |
41 | ||
42 | dprintf("enter mbustart\n"); | |
43 | loop: | |
44 | /* | |
45 | * Get the first thing to do off device queue. | |
46 | */ | |
47 | bp = mi->mi_tab.b_actf; | |
48 | if (bp == NULL) | |
49 | return; | |
50 | mdp = mi->mi_drv; | |
51 | /* | |
52 | * Since we clear attentions on the drive when we are | |
53 | * finished processing it, the fact that an attention | |
54 | * status shows indicated confusion in the hardware or our logic. | |
55 | */ | |
56 | if (mdp->mbd_as & (1 << mi->mi_drive)) { | |
57 | printf("mbustart: ata on for %d\n", mi->mi_drive); | |
58 | mdp->mbd_as = 1 << mi->mi_drive; | |
59 | } | |
60 | /* | |
61 | * Let the drivers unit start routine have at it | |
62 | * and then process the request further, per its instructions. | |
63 | */ | |
64 | switch ((*mi->mi_driver->md_ustart)(mi)) { | |
65 | ||
66 | case MBU_NEXT: /* request is complete (e.g. ``sense'') */ | |
67 | dprintf("mbu_next\n"); | |
68 | mi->mi_tab.b_active = 0; | |
69 | mi->mi_tab.b_actf = bp->av_forw; | |
70 | iodone(bp); | |
71 | goto loop; | |
72 | ||
73 | case MBU_DODATA: /* all ready to do data transfer */ | |
74 | dprintf("mbu_dodata\n"); | |
75 | /* | |
76 | * Queue the device mba_info structure on the massbus | |
77 | * mba_hd structure for processing as soon as the | |
78 | * data path is available. | |
79 | */ | |
80 | mhp = mi->mi_hd; | |
81 | mi->mi_forw = NULL; | |
82 | if (mhp->mh_actf == NULL) | |
83 | mhp->mh_actf = mi; | |
84 | else | |
85 | mhp->mh_actl->mi_forw = mi; | |
86 | mhp->mh_actl = mi; | |
87 | /* | |
88 | * If data path is idle, start transfer now. | |
89 | * In any case the device is ``active'' waiting for the | |
90 | * data to transfer. | |
91 | */ | |
92 | if (mhp->mh_active == 0) | |
93 | mbstart(mhp); | |
94 | mi->mi_tab.b_active = 1; | |
95 | return; | |
96 | ||
97 | case MBU_STARTED: /* driver started a non-data transfer */ | |
98 | dprintf("mbu_started\n"); | |
99 | /* | |
100 | * Mark device busy during non-data transfer | |
101 | * and count this as a ``seek'' on the device. | |
102 | */ | |
103 | if (mi->mi_dk >= 0) | |
104 | dk_seek[mi->mi_dk]++; | |
105 | mi->mi_tab.b_active = 1; | |
106 | return; | |
107 | ||
108 | case MBU_BUSY: /* dual port drive busy */ | |
109 | dprintf("mbu_busy\n"); | |
110 | /* | |
111 | * We mark the device structure so that when an | |
112 | * interrupt occurs we will know to restart the unit. | |
113 | */ | |
114 | mi->mi_tab.b_flags |= B_BUSY; | |
115 | return; | |
116 | ||
117 | default: | |
118 | panic("mbustart"); | |
119 | } | |
e1e57888 | 120 | } |
b81fd3e8 BJ |
121 | |
122 | /* | |
123 | * Start an i/o operation on the massbus specified by the argument. | |
124 | * We peel the first operation off its queue and insure that the drive | |
125 | * is present and on-line. We then use the drivers start routine | |
126 | * (if any) to prepare the drive, setup the massbus map for the transfer | |
127 | * and start the transfer. | |
128 | */ | |
129 | mbstart(mhp) | |
130 | register struct mba_hd *mhp; | |
131 | { | |
132 | register struct mba_info *mi; | |
133 | struct buf *bp; | |
b81fd3e8 BJ |
134 | register struct mba_regs *mbp; |
135 | ||
136 | dprintf("mbstart\n"); | |
137 | loop: | |
138 | /* | |
139 | * Look for an operation at the front of the queue. | |
140 | */ | |
141 | if ((mi = mhp->mh_actf) == NULL) { | |
142 | dprintf("nothing to do\n"); | |
143 | return; | |
144 | } | |
145 | if ((bp = mi->mi_tab.b_actf) == NULL) { | |
146 | dprintf("nothing on actf\n"); | |
147 | mhp->mh_actf = mi->mi_forw; | |
148 | goto loop; | |
149 | } | |
150 | /* | |
151 | * If this device isn't present and on-line, then | |
152 | * we screwed up, and can't really do the operation. | |
153 | */ | |
154 | if ((mi->mi_drv->mbd_ds & (MBD_DPR|MBD_MOL)) != (MBD_DPR|MBD_MOL)) { | |
155 | dprintf("not on line ds %x\n", mi->mi_drv->mbd_ds); | |
156 | mi->mi_tab.b_actf = bp->av_forw; | |
157 | bp->b_flags |= B_ERROR; | |
158 | iodone(bp); | |
159 | goto loop; | |
160 | } | |
161 | /* | |
162 | * We can do the operation; mark the massbus active | |
163 | * and let the device start routine setup any necessary | |
164 | * device state for the transfer (e.g. desired cylinder, etc | |
165 | * on disks). | |
166 | */ | |
167 | mhp->mh_active = 1; | |
168 | if (mi->mi_driver->md_start) { | |
169 | dprintf("md_start\n"); | |
170 | (*mi->mi_driver->md_start)(mi); | |
171 | } | |
172 | ||
173 | /* | |
174 | * Setup the massbus control and map registers and start | |
175 | * the transfer. | |
176 | */ | |
177 | dprintf("start mba\n"); | |
178 | mbp = mi->mi_mba; | |
179 | mbp->mba_sr = -1; /* conservative */ | |
180 | mbp->mba_var = mbasetup(mi); | |
181 | mbp->mba_bcr = -bp->b_bcount; | |
182 | mi->mi_drv->mbd_cs1 = | |
183 | (bp->b_flags & B_READ) ? MBD_RCOM|MBD_GO : MBD_WCOM|MBD_GO; | |
184 | if (mi->mi_dk >= 0) { | |
185 | dk_busy |= 1 << mi->mi_dk; | |
186 | dk_xfer[mi->mi_dk]++; | |
187 | dk_wds[mi->mi_dk] += bp->b_bcount >> 6; | |
188 | } | |
189 | } | |
b5ad10c3 | 190 | |
b81fd3e8 BJ |
191 | /* |
192 | * Take an interrupt off of massbus mbanum, | |
193 | * and dispatch to drivers as appropriate. | |
194 | */ | |
195 | mbintr(mbanum) | |
196 | int mbanum; | |
197 | { | |
198 | register struct mba_hd *mhp = &mba_hd[mbanum]; | |
199 | register struct mba_regs *mbp = mhp->mh_mba; | |
200 | register struct mba_info *mi; | |
80e7c811 | 201 | register struct buf *bp; |
b81fd3e8 BJ |
202 | register int drive; |
203 | int mbastat, as; | |
204 | ||
205 | /* | |
206 | * Read out the massbus status register | |
207 | * and attention status register and clear | |
208 | * the bits in same by writing them back. | |
209 | */ | |
210 | mbastat = mbp->mba_sr; | |
211 | mbp->mba_sr = mbastat; | |
212 | /* note: the mbd_as register is shared between drives */ | |
213 | as = mbp->mba_drv[0].mbd_as; | |
214 | mbp->mba_drv[0].mbd_as = as; | |
215 | dprintf("mbintr mbastat %x as %x\n", mbastat, as); | |
216 | ||
217 | /* | |
218 | * Disable interrupts from the massbus adapter | |
219 | * for the duration of the operation of the massbus | |
220 | * driver, so that spurious interrupts won't be generated. | |
221 | */ | |
222 | mbp->mba_cr &= ~MBAIE; | |
223 | ||
224 | /* | |
225 | * If the mba was active, process the data transfer | |
226 | * complete interrupt; otherwise just process units which | |
227 | * are now finished. | |
228 | */ | |
229 | if (mhp->mh_active) { | |
230 | if ((mbastat & MBS_DTCMP) == 0) { | |
231 | printf("mbintr(%d),b_active,no DTCMP!\n", mbanum); | |
232 | goto doattn; | |
e1e57888 | 233 | } |
b81fd3e8 BJ |
234 | /* |
235 | * Clear attention status for drive whose data | |
236 | * transfer completed, and give the dtint driver | |
237 | * routine a chance to say what is next. | |
238 | */ | |
239 | mi = mhp->mh_actf; | |
240 | as &= ~(1 << mi->mi_drive); | |
241 | dk_busy &= ~(1 << mi->mi_dk); | |
242 | bp = mi->mi_tab.b_actf; | |
243 | switch((*mi->mi_driver->md_dtint)(mi, mbastat)) { | |
244 | ||
245 | case MBD_DONE: /* all done, for better or worse */ | |
246 | dprintf("mbd_done\n"); | |
247 | /* | |
248 | * Flush request from drive queue. | |
249 | */ | |
250 | mi->mi_tab.b_errcnt = 0; | |
251 | mi->mi_tab.b_actf = bp->av_forw; | |
252 | iodone(bp); | |
253 | /* fall into... */ | |
254 | case MBD_RETRY: /* attempt the operation again */ | |
255 | dprintf("mbd_retry\n"); | |
256 | /* | |
257 | * Dequeue data transfer from massbus queue; | |
258 | * if there is still a i/o request on the device | |
259 | * queue then start the next operation on the device. | |
260 | * (Common code for DONE and RETRY). | |
261 | */ | |
262 | mhp->mh_active = 0; | |
263 | mi->mi_tab.b_active = 0; | |
264 | mhp->mh_actf = mi->mi_forw; | |
265 | if (mi->mi_tab.b_actf) | |
266 | mbustart(mi); | |
267 | break; | |
268 | ||
269 | case MBD_RESTARTED: /* driver restarted op (ecc, e.g.) | |
270 | dprintf("mbd_restarted\n"); | |
271 | /* | |
272 | * Note that mp->b_active is still on. | |
273 | */ | |
274 | break; | |
275 | ||
276 | default: | |
277 | panic("mbaintr"); | |
278 | } | |
279 | } else { | |
280 | dprintf("!dtcmp\n"); | |
281 | if (mbastat & MBS_DTCMP) | |
282 | printf("mbaintr,DTCMP,!b_active\n"); | |
283 | } | |
284 | doattn: | |
285 | /* | |
286 | * Service drives which require attention | |
287 | * after non-data-transfer operations. | |
288 | */ | |
289 | for (drive = 0; as && drive < 8; drive++) | |
290 | if (as & (1 << drive)) { | |
291 | dprintf("service as %d\n", drive); | |
292 | as &= ~(1 << drive); | |
293 | /* | |
294 | * Consistency check the implied attention, | |
295 | * to make sure the drive should have interrupted. | |
296 | */ | |
297 | mi = mhp->mh_mbip[drive]; | |
298 | if (mi == NULL) | |
299 | goto random; /* no such drive */ | |
300 | if (mi->mi_tab.b_active == 0 && | |
301 | (mi->mi_tab.b_flags&B_BUSY) == 0) | |
302 | goto random; /* not active */ | |
303 | if ((bp = mi->mi_tab.b_actf) == NULL) { | |
304 | /* nothing doing */ | |
305 | random: | |
306 | printf("random mbaintr %d %d\n",mbanum,drive); | |
307 | continue; | |
308 | } | |
309 | /* | |
310 | * If this interrupt wasn't a notification that | |
311 | * a dual ported drive is available, and if the | |
312 | * driver has a handler for non-data transfer | |
313 | * interrupts, give it a chance to tell us that | |
314 | * the operation needs to be redone | |
315 | */ | |
316 | if ((mi->mi_tab.b_flags&B_BUSY) == 0 && | |
317 | mi->mi_driver->md_ndint) { | |
318 | mi->mi_tab.b_active = 0; | |
319 | switch((*mi->mi_driver->md_ndint)(mi)) { | |
320 | ||
321 | case MBN_DONE: | |
322 | dprintf("mbn_done\n"); | |
323 | /* | |
324 | * Non-data transfer interrupt | |
325 | * completed i/o request's processing. | |
326 | */ | |
327 | mi->mi_tab.b_errcnt = 0; | |
328 | mi->mi_tab.b_actf = bp->av_forw; | |
329 | iodone(bp); | |
330 | /* fall into... */ | |
331 | case MBN_RETRY: | |
332 | dprintf("mbn_retry\n"); | |
333 | if (mi->mi_tab.b_actf) | |
334 | mbustart(mi); | |
335 | break; | |
336 | ||
337 | default: | |
338 | panic("mbintr ndint"); | |
339 | } | |
340 | } else | |
341 | mbustart(mi); | |
342 | } | |
343 | /* | |
344 | * If there is an operation available and | |
345 | * the massbus isn't active, get it going. | |
346 | */ | |
347 | if (mhp->mh_actf && !mhp->mh_active) | |
348 | mbstart(mhp); | |
349 | mbp->mba_cr |= MBAIE; | |
350 | } | |
351 | ||
352 | /* | |
353 | * Setup the mapping registers for a transfer. | |
354 | */ | |
355 | mbasetup(mi) | |
356 | register struct mba_info *mi; | |
b5ad10c3 | 357 | { |
b81fd3e8 BJ |
358 | register struct mba_regs *mbap = mi->mi_mba; |
359 | struct buf *bp = mi->mi_tab.b_actf; | |
b5ad10c3 BJ |
360 | register int i; |
361 | int npf; | |
362 | unsigned v; | |
363 | register struct pte *pte, *io; | |
364 | int o; | |
365 | int vaddr; | |
b5ad10c3 | 366 | struct proc *rp; |
b5ad10c3 | 367 | |
f9b6e695 BJ |
368 | io = mbap->mba_map; |
369 | v = btop(bp->b_un.b_addr); | |
370 | o = (int)bp->b_un.b_addr & PGOFSET; | |
371 | npf = btoc(bp->b_bcount + o); | |
372 | rp = bp->b_flags&B_DIRTY ? &proc[2] : bp->b_proc; | |
373 | vaddr = o; | |
374 | if (bp->b_flags & B_UAREA) { | |
375 | for (i = 0; i < UPAGES; i++) { | |
376 | if (rp->p_addr[i].pg_pfnum == 0) | |
377 | panic("mba: zero upage"); | |
378 | *(int *)io++ = rp->p_addr[i].pg_pfnum | PG_V; | |
379 | } | |
380 | } else if ((bp->b_flags & B_PHYS) == 0) { | |
381 | pte = &Sysmap[btop(((int)bp->b_un.b_addr)&0x7fffffff)]; | |
382 | while (--npf >= 0) | |
383 | *(int *)io++ = pte++->pg_pfnum | PG_V; | |
384 | } else { | |
385 | if (bp->b_flags & B_PAGET) | |
386 | pte = &Usrptmap[btokmx((struct pte *)bp->b_un.b_addr)]; | |
387 | else | |
388 | pte = vtopte(rp, v); | |
389 | while (--npf >= 0) { | |
390 | if (pte->pg_pfnum == 0) | |
391 | panic("mba, zero entry"); | |
392 | *(int *)io++ = pte++->pg_pfnum | PG_V; | |
b5ad10c3 BJ |
393 | } |
394 | } | |
f9b6e695 | 395 | *(int *)io++ = 0; |
b81fd3e8 | 396 | return (vaddr); |
b5ad10c3 | 397 | } |