mba.c revision 1.3 1 /* $NetBSD: mba.c,v 1.3 1996/02/11 13:19:36 ragge Exp $ */
2 /*
3 * Copyright (c) 1994, 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed at Ludd, University of
17 * Lule}, Sweden and its contributors.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * Simple massbus drive routine.
35 * TODO:
36 * Autoconfig new devices 'on the fly'.
37 * More intelligent way to handle different interrupts.
38 */
39
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/queue.h>
43 #include <sys/buf.h>
44 #include <sys/proc.h>
45
46 #include <vm/vm.h>
47 #include <vm/vm_kern.h>
48
49 #include <machine/trap.h>
50 #include <machine/scb.h>
51 #include <machine/nexus.h>
52 #include <machine/pte.h>
53 #include <machine/pcb.h>
54
55 #include <vax/mba/mbareg.h>
56 #include <vax/mba/mbavar.h>
57
58 struct mbaunit mbaunit[] = {
59 MBADT_RP04, "rp04", MB_RP,
60 MBADT_RP05, "rp05", MB_RP,
61 MBADT_RP06, "rp06", MB_RP,
62 MBADT_RP07, "rp07", MB_RP,
63 MBADT_RM02, "rm02", MB_RP,
64 MBADT_RM03, "rm03", MB_RP,
65 MBADT_RM05, "rm05", MB_RP,
66 MBADT_RM80, "rm80", MB_RP,
67 0, 0, 0
68 };
69
70 int mbamatch __P((struct device *, void *, void *));
71 void mbaattach __P((struct device *, struct device *, void *));
72 void mbaintr __P((int));
73 int mbaprint __P((void *, char *));
74 void mbaqueue __P((struct mba_device *));
75 void mbastart __P((struct mba_softc *));
76 void mbamapregs __P((struct mba_softc *));
77
78 struct cfdriver mbacd = {
79 NULL, "mba", mbamatch, mbaattach, DV_DULL, sizeof(struct mba_softc)
80 };
81
82 /*
83 * Look if this is a massbuss adapter.
84 */
85 int
86 mbamatch(parent, match, aux)
87 struct device *parent;
88 void *match, *aux;
89 {
90 struct sbi_attach_args *sa = (struct sbi_attach_args *)aux;
91 struct cfdata *cf = match;
92
93 if ((cf->cf_loc[0] != sa->nexnum) && (cf->cf_loc[0] > -1 ))
94 return 0;
95
96 if (sa->type == NEX_MBA)
97 return 1;
98
99 return 0;
100 }
101
102 /*
103 * Attach the found massbuss adapter. Setup its interrupt vectors,
104 * reset it and go searching for drives on it.
105 */
106 void
107 mbaattach(parent, self, aux)
108 struct device *parent, *self;
109 void *aux;
110 {
111 struct mba_softc *sc = (void *)self;
112 struct sbi_attach_args *sa = (struct sbi_attach_args *)aux;
113 volatile struct mba_regs *mbar = (struct mba_regs *)sa->nexaddr;
114 struct mba_attach_args ma;
115 extern struct ivec_dsp idsptch;
116 int i, j;
117
118 printf("\n");
119 /*
120 * Set up interrupt vectors for this MBA.
121 */
122 bcopy(&idsptch, &sc->sc_dsp, sizeof(struct ivec_dsp));
123 scb->scb_nexvec[0][sa->nexnum] = scb->scb_nexvec[1][sa->nexnum] =
124 scb->scb_nexvec[2][sa->nexnum] = scb->scb_nexvec[3][sa->nexnum] =
125 &sc->sc_dsp;
126 sc->sc_dsp.pushlarg = sc->sc_dev.dv_unit;
127 sc->sc_dsp.hoppaddr = mbaintr;
128
129 sc->sc_first = 0;
130 sc->sc_last = (void *)&sc->sc_first;
131 sc->sc_mbareg = (struct mba_regs *)mbar;
132 mbar->mba_cr = MBACR_INIT; /* Reset adapter */
133 mbar->mba_cr = MBACR_IE; /* Enable interrupts */
134
135 for (i = 0; i < MAXMBADEV; i++) {
136 sc->sc_state = SC_AUTOCONF;
137 if ((mbar->mba_md[i].md_ds & MBADS_DPR) == 0)
138 continue;
139 /* We have a drive, ok. */
140 ma.unit = i;
141 ma.type = mbar->mba_md[i].md_dt & 0777;
142 j = 0;
143 while (mbaunit[j++].nr)
144 if (mbaunit[j].nr == ma.type)
145 break;
146 ma.devtyp = mbaunit[j].devtyp;
147 ma.name = mbaunit[j].name;
148 config_found(&sc->sc_dev, (void *)&ma, mbaprint);
149 }
150 }
151
152 /*
153 * We got an interrupt. Check type of interrupt and call the specific
154 * device interrupt handling routine.
155 */
156 void
157 mbaintr(mba)
158 int mba;
159 {
160 struct mba_softc *sc = mbacd.cd_devs[mba];
161 volatile struct mba_regs *mr = sc->sc_mbareg;
162 struct mba_device *md;
163 struct buf *bp;
164 int itype, attn, anr, serv = 0;
165
166 itype = mr->mba_sr;
167 mr->mba_sr = itype; /* Write back to clear bits */
168
169 attn = mr->mba_md[0].md_as & 0xff;
170 mr->mba_md[0].md_as = attn;
171
172 if (sc->sc_state == SC_AUTOCONF)
173 return; /* During autoconfig */
174
175 md = sc->sc_first;
176 bp = md->md_q.b_actf;
177 /*
178 * A data-transfer interrupt. Current operation is finished,
179 * call that device's finish routine to see what to do next.
180 */
181 if (sc->sc_state == SC_ACTIVE) {
182
183 sc->sc_state = SC_IDLE;
184 switch ((*md->md_finish)(md, itype, &attn)) {
185
186 case XFER_FINISH:
187 /*
188 * Transfer is finished. Take buffer of drive
189 * queue, and take drive of adapter queue.
190 * If more to transfer, start the adapter again
191 * by calling mbastart().
192 */
193 md->md_q.b_actf = bp->b_actf;
194 sc->sc_first = md->md_back;
195 md->md_back = 0;
196 if (sc->sc_first == 0)
197 sc->sc_last = (void *)&sc->sc_first;
198
199 if (md->md_q.b_actf) {
200 sc->sc_last->md_back = md;
201 sc->sc_last = md;
202 }
203
204 bp->b_resid = 0;
205 biodone(bp);
206 if (sc->sc_first)
207 mbastart(sc);
208 break;
209
210 case XFER_RESTART:
211 /*
212 * Something went wrong with the transfer. Try again.
213 */
214 mbastart(sc);
215 break;
216 }
217 }
218
219 while (attn) {
220 anr = ffs(attn) - 1;
221 attn &= ~(1 << anr);
222 if (sc->sc_md[anr]->md_attn == 0)
223 panic("Should check for new MBA device %d", anr);
224 (*sc->sc_md[anr]->md_attn)(sc->sc_md[anr]);
225 }
226 }
227
228 int
229 mbaprint(aux, mbaname)
230 void *aux;
231 char *mbaname;
232 {
233 struct mba_attach_args *ma = aux;
234
235 if (mbaname) {
236 if (ma->name)
237 printf("%s", ma->name);
238 else
239 printf("device type %o", ma->type);
240 printf(" at %s", mbaname);
241 }
242 printf(" drive %d", ma->unit);
243 return (ma->name ? UNCONF : UNSUPP);
244 }
245
246 /*
247 * A device calls mbaqueue() when it wants to get on the adapter queue.
248 * Called at splbio(). If the adapter is inactive, start it.
249 */
250 void
251 mbaqueue(md)
252 struct mba_device *md;
253 {
254 struct mba_softc *sc = md->md_mba;
255 int i = (int)sc->sc_first;
256
257 sc->sc_last->md_back = md;
258 sc->sc_last = md;
259
260 if (i == 0)
261 mbastart(sc);
262 }
263
264 /*
265 * Start activity on (idling) adapter. Calls mbamapregs() to setup
266 * for dma transfer, then the unit-specific start routine.
267 */
268 void
269 mbastart(sc)
270 struct mba_softc *sc;
271 {
272 struct mba_device *md = sc->sc_first;
273 volatile struct mba_regs *mr = sc->sc_mbareg;
274 struct buf *bp = md->md_q.b_actf;
275
276 mbamapregs(sc);
277
278 sc->sc_state = SC_ACTIVE;
279 mr->mba_var = ((u_int)bp->b_un.b_addr & PGOFSET);
280 mr->mba_bc = (~bp->b_bcount) + 1;
281 (*md->md_start)(md); /* machine-dependent start */
282 }
283
284 /*
285 * Setup map registers for a dma transfer.
286 * This routine could be synced with the other adapter map routines!
287 */
288 void
289 mbamapregs(sc)
290 struct mba_softc *sc;
291 {
292 struct mba_device *md = sc->sc_first;
293 volatile struct mba_regs *mr = sc->sc_mbareg;
294 struct buf *bp = md->md_q.b_actf;
295 struct pcb *pcb;
296 pt_entry_t *pte;
297 volatile pt_entry_t *io;
298 int pfnum, npf, o, i;
299 caddr_t addr;
300
301 o = (int)bp->b_un.b_addr & PGOFSET;
302 npf = btoc(bp->b_bcount + o) + 1;
303 addr = bp->b_un.b_addr;
304
305 /*
306 * Get a pointer to the pte pointing out the first virtual address.
307 * Use different ways in kernel and user space.
308 */
309 if ((bp->b_flags & B_PHYS) == 0) {
310 pte = kvtopte(addr);
311 } else {
312 pcb = bp->b_proc->p_vmspace->vm_pmap.pm_pcb;
313 pte = uvtopte(addr, pcb);
314 }
315
316 /*
317 * When we are doing DMA to user space, be sure that all pages
318 * we want to transfer to is mapped. WHY DO WE NEED THIS???
319 * SHOULDN'T THEY ALWAYS BE MAPPED WHEN DOING THIS???
320 */
321 for (i = 0; i < (npf - 1); i++) {
322 if ((pte + i)->pg_pfn == 0) {
323 int rv;
324 rv = vm_fault(&bp->b_proc->p_vmspace->vm_map,
325 (unsigned)addr + i * NBPG,
326 VM_PROT_READ|VM_PROT_WRITE, FALSE);
327 if (rv)
328 panic("MBA DMA to nonexistent page, %d", rv);
329 }
330 }
331
332 io = &mr->mba_map[0];
333 while (--npf > 0) {
334 pfnum = pte->pg_pfn;
335 if (pfnum == 0)
336 panic("mba zero entry");
337 pte++;
338 *(int *)io++ = pfnum | PG_V;
339 }
340 *(int *)io = 0;
341 }
342
343
344