obio.c revision 1.34 1 /* $NetBSD: obio.c,v 1.34 1997/05/24 20:08:41 pk Exp $ */
2
3 /*
4 * Copyright (c) 1993, 1994 Theo de Raadt
5 * Copyright (c) 1995, 1997 Paul Kranenburg
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by Theo de Raadt.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <sys/malloc.h>
38
39 #ifdef DEBUG
40 #include <sys/proc.h>
41 #include <sys/syslog.h>
42 #endif
43
44 #include <vm/vm.h>
45
46 #include <machine/autoconf.h>
47 #include <machine/pmap.h>
48 #include <machine/oldmon.h>
49 #include <machine/cpu.h>
50 #include <machine/ctlreg.h>
51 #include <sparc/sparc/asm.h>
52 #include <sparc/sparc/vaddrs.h>
53 #include <sparc/sparc/cpuvar.h>
54 #include <sparc/dev/sbusvar.h>
55 #include <sparc/dev/vmereg.h>
56
57 struct vmebus_softc {
58 struct device sc_dev; /* base device */
59 struct vmebusreg *sc_reg; /* VME control registers */
60 struct vmebusvec *sc_vec; /* VME interrupt vector */
61 struct rom_range *sc_range; /* ROM range property */
62 int sc_nrange;
63 };
64 struct vmebus_softc *vmebus_sc;/*XXX*/
65
66 struct bus_softc {
67 union {
68 struct device scu_dev; /* base device */
69 struct sbus_softc scu_sbus; /* obio is another sbus slot */
70 struct vmebus_softc scu_vme;
71 } bu;
72 };
73
74
75 /* autoconfiguration driver */
76 static int busmatch __P((struct device *, struct cfdata *, void *));
77 static void obioattach __P((struct device *, struct device *, void *));
78 static void vmesattach __P((struct device *, struct device *, void *));
79 static void vmelattach __P((struct device *, struct device *, void *));
80 static void vmeattach __P((struct device *, struct device *, void *));
81
82 int busprint __P((void *, const char *));
83 int vmeprint __P((void *, const char *));
84 static int busattach __P((struct device *, struct cfdata *, void *, int));
85 int obio_scan __P((struct device *, struct cfdata *, void *));
86 int vmes_scan __P((struct device *, struct cfdata *, void *));
87 int vmel_scan __P((struct device *, struct cfdata *, void *));
88 int vmeintr __P((void *));
89
90 struct cfattach obio_ca = {
91 sizeof(struct bus_softc), busmatch, obioattach
92 };
93
94 struct cfdriver obio_cd = {
95 NULL, "obio", DV_DULL
96 };
97
98 struct cfattach vmel_ca = {
99 sizeof(struct bus_softc), busmatch, vmelattach
100 };
101
102 struct cfdriver vmel_cd = {
103 NULL, "vmel", DV_DULL
104 };
105
106 struct cfattach vmes_ca = {
107 sizeof(struct bus_softc), busmatch, vmesattach
108 };
109
110 struct cfdriver vmes_cd = {
111 NULL, "vmes", DV_DULL
112 };
113
114 struct cfattach vme_ca = {
115 sizeof(struct bus_softc), busmatch, vmeattach
116 };
117
118 struct cfdriver vme_cd = {
119 NULL, "vme", DV_DULL
120 };
121
122 struct intrhand **vmeints;
123
124
125 int
126 busmatch(parent, cf, aux)
127 struct device *parent;
128 struct cfdata *cf;
129 void *aux;
130 {
131 register struct confargs *ca = aux;
132 register struct romaux *ra = &ca->ca_ra;
133
134 if (CPU_ISSUN4M)
135 return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
136
137 if (!CPU_ISSUN4)
138 return (0);
139
140 return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
141 }
142
143 int
144 busprint(args, obio)
145 void *args;
146 const char *obio;
147 {
148 register struct confargs *ca = args;
149
150 if (ca->ca_ra.ra_name == NULL)
151 ca->ca_ra.ra_name = "<unknown>";
152
153 if (obio)
154 printf("[%s at %s]", ca->ca_ra.ra_name, obio);
155
156 printf(" addr %p", ca->ca_ra.ra_paddr);
157
158 if (CPU_ISSUN4 && ca->ca_ra.ra_intr[0].int_vec != -1)
159 printf(" vec 0x%x", ca->ca_ra.ra_intr[0].int_vec);
160
161 return (UNCONF);
162 }
163
164 int
165 vmeprint(args, name)
166 void *args;
167 const char *name;
168 {
169 register struct confargs *ca = args;
170
171 if (name)
172 printf("%s at %s", ca->ca_ra.ra_name, name);
173 return (UNCONF);
174 }
175
176 void
177 obioattach(parent, self, args)
178 struct device *parent, *self;
179 void *args;
180 {
181 #if defined(SUN4M)
182 register struct bus_softc *sc = (struct bus_softc *)self;
183 struct confargs oca, *ca = args;
184 register struct romaux *ra = &ca->ca_ra;
185 register int node0, node;
186 register char *name;
187 register const char *sp;
188 const char *const *ssp;
189 int rlen;
190 extern int autoconf_nzs;
191
192 static const char *const special4m[] = {
193 /* find these first */
194 "eeprom",
195 "counter",
196 #if 0 /* Not all sun4m's have an `auxio' */
197 "auxio",
198 #endif
199 "",
200 /* place device to ignore here */
201 "interrupt",
202 NULL
203 };
204 #endif
205
206 if (CPU_ISSUN4) {
207 if (self->dv_unit > 0) {
208 printf(" unsupported\n");
209 return;
210 }
211 printf("\n");
212
213 (void)config_search(obio_scan, self, args);
214 bus_untmp();
215 }
216
217 #if defined(SUN4M)
218 if (!CPU_ISSUN4M)
219 return;
220
221 /*
222 * There is only one obio bus (it is in fact one of the Sbus slots)
223 * How about VME?
224 */
225 if (self->dv_unit > 0) {
226 printf(" unsupported\n");
227 return;
228 }
229
230 printf("\n");
231
232 if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "obio") == 0)
233 oca.ca_ra.ra_bp = ra->ra_bp + 1;
234 else
235 oca.ca_ra.ra_bp = NULL;
236
237 node = ra->ra_node;
238 rlen = getproplen(node, "ranges");
239 if (rlen > 0) {
240 sc->bu.scu_sbus.sc_nrange = rlen / sizeof(struct rom_range);
241 sc->bu.scu_sbus.sc_range =
242 (struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
243 if (sc->bu.scu_sbus.sc_range == 0)
244 panic("obio: PROM ranges too large: %d", rlen);
245 (void)getprop(node, "ranges", sc->bu.scu_sbus.sc_range, rlen);
246 }
247
248 /*
249 * Loop through ROM children, fixing any relative addresses
250 * and then configuring each device.
251 * We first do the crucial ones, such as eeprom, etc.
252 */
253 node0 = firstchild(ra->ra_node);
254 for (ssp = special4m ; *(sp = *ssp) != 0; ssp++) {
255 if ((node = findnode(node0, sp)) == 0) {
256 printf("could not find %s amongst obio devices\n", sp);
257 panic(sp);
258 }
259 if (!romprop(&oca.ca_ra, sp, node))
260 continue;
261
262 sbus_translate(self, &oca);
263 oca.ca_bustype = BUS_OBIO;
264 (void) config_found(self, (void *)&oca, busprint);
265 }
266
267 for (node = node0; node; node = nextsibling(node)) {
268 name = getpropstring(node, "name");
269 for (ssp = special4m ; (sp = *ssp) != NULL; ssp++)
270 if (strcmp(name, sp) == 0)
271 break;
272
273 if (sp != NULL || !romprop(&oca.ca_ra, name, node))
274 continue;
275
276 if (strcmp(name, "zs") == 0)
277 /* XXX - see autoconf.c for this hack */
278 autoconf_nzs++;
279
280 /* Translate into parent address spaces */
281 sbus_translate(self, &oca);
282 oca.ca_bustype = BUS_OBIO;
283 (void) config_found(self, (void *)&oca, busprint);
284 }
285 #endif
286 }
287
288 void
289 vmesattach(parent, self, args)
290 struct device *parent, *self;
291 void *args;
292 {
293 if (self->dv_unit > 0 ||
294 (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
295 printf(" unsupported\n");
296 return;
297 }
298 printf("\n");
299
300 if (vmeints == NULL) {
301 vmeints = (struct intrhand **)malloc(256 *
302 sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
303 bzero(vmeints, 256 * sizeof(struct intrhand *));
304 }
305 (void)config_search(vmes_scan, self, args);
306 bus_untmp();
307 }
308
309 void
310 vmelattach(parent, self, args)
311 struct device *parent, *self;
312 void *args;
313 {
314 if (self->dv_unit > 0 ||
315 (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
316 printf(" unsupported\n");
317 return;
318 }
319 printf("\n");
320
321 if (vmeints == NULL) {
322 vmeints = (struct intrhand **)malloc(256 *
323 sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
324 bzero(vmeints, 256 * sizeof(struct intrhand *));
325 }
326 (void)config_search(vmel_scan, self, args);
327 bus_untmp();
328 }
329
330 void
331 vmeattach(parent, self, aux)
332 struct device *parent, *self;
333 void *aux;
334 {
335 struct vmebus_softc *sc = (struct vmebus_softc *)self;
336 struct confargs *ca = aux;
337 register struct romaux *ra = &ca->ca_ra;
338 int node, rlen;
339 struct confargs oca;
340
341 if (!CPU_ISSUN4M || self->dv_unit > 0) {
342 printf(" unsupported\n");
343 return;
344 }
345
346 node = ra->ra_node;
347
348 sc->sc_reg = (struct vmebusreg *)
349 mapdev(&ra->ra_reg[0], 0, 0, ra->ra_reg[0].rr_len);
350 sc->sc_vec = (struct vmebusvec *)
351 mapdev(&ra->ra_reg[1], 0, 0, ra->ra_reg[1].rr_len);
352
353 /*
354 * Get "range" property, though we don't do anything with it yet.
355 */
356 rlen = getproplen(node, "ranges");
357 if (rlen > 0) {
358 sc->sc_nrange = rlen / sizeof(struct rom_range);
359 sc->sc_range =
360 (struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
361 if (sc->sc_range == 0)
362 panic("vme: PROM ranges too large: %d", rlen);
363 (void)getprop(node, "ranges", sc->sc_range, rlen);
364 }
365
366 vmebus_sc = sc;
367 printf(": version %x\n",
368 sc->sc_reg->vmebus_cr & VMEBUS_CR_IMPL);
369
370 if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "vme") == 0)
371 oca.ca_ra.ra_bp = ra->ra_bp + 1;
372 else
373 oca.ca_ra.ra_bp = NULL;
374
375 oca.ca_ra.ra_name = "vmes";
376 oca.ca_bustype = BUS_MAIN;
377 (void)config_found(self, (void *)&oca, vmeprint);
378
379 oca.ca_ra.ra_name = "vmel";
380 oca.ca_bustype = BUS_MAIN;
381 (void)config_found(self, (void *)&oca, vmeprint);
382 }
383
384 int bt2pmt[] = {
385 PMAP_OBIO,
386 PMAP_OBIO,
387 PMAP_VME16,
388 PMAP_VME32,
389 PMAP_OBIO
390 };
391
392 int
393 busattach(parent, cf, args, bustype)
394 struct device *parent;
395 struct cfdata *cf;
396 void *args;
397 int bustype;
398 {
399 #if defined(SUN4) || defined(SUN4M)
400 register struct confargs *ca = args;
401 struct confargs oca;
402 caddr_t tmp;
403
404 if (bustype == BUS_OBIO && CPU_ISSUN4) {
405
406 /*
407 * avoid sun4m entries which don't have valid PA's.
408 * no point in even probing them.
409 */
410 if (cf->cf_loc[0] == -1) return 0;
411
412 /*
413 * On the 4/100 obio addresses must be mapped at
414 * 0x0YYYYYYY, but alias higher up (we avoid the
415 * alias condition because it causes pmap difficulties)
416 * XXX: We also assume that 4/[23]00 obio addresses
417 * must be 0xZYYYYYYY, where (Z != 0)
418 */
419 if (cpuinfo.cpu_type == CPUTYP_4_100 &&
420 (cf->cf_loc[0] & 0xf0000000))
421 return 0;
422 if (cpuinfo.cpu_type != CPUTYP_4_100 &&
423 !(cf->cf_loc[0] & 0xf0000000))
424 return 0;
425 }
426
427 oca.ca_ra.ra_iospace = CPU_ISSUN4
428 ? bt2pmt[bustype]
429 : ((bustype == BUS_VME32) ? VME_SUN4M_32 : VME_SUN4M_16);
430 oca.ca_ra.ra_paddr = (void *)cf->cf_loc[0];
431 oca.ca_ra.ra_len = 0;
432 oca.ca_ra.ra_nreg = 1;
433 if (oca.ca_ra.ra_paddr)
434 tmp = (caddr_t)mapdev(oca.ca_ra.ra_reg, TMPMAP_VA, 0, NBPG);
435 else
436 tmp = NULL;
437 oca.ca_ra.ra_vaddr = tmp;
438 oca.ca_ra.ra_intr[0].int_pri = cf->cf_loc[1];
439 if (bustype == BUS_VME16 || bustype == BUS_VME32)
440 oca.ca_ra.ra_intr[0].int_vec = cf->cf_loc[2];
441 else
442 oca.ca_ra.ra_intr[0].int_vec = -1;
443 oca.ca_ra.ra_nintr = 1;
444 oca.ca_ra.ra_name = cf->cf_driver->cd_name;
445 if (ca->ca_ra.ra_bp != NULL &&
446 ((bustype == BUS_VME16 && strcmp(ca->ca_ra.ra_bp->name,"vmes") ==0) ||
447 (bustype == BUS_VME32 && strcmp(ca->ca_ra.ra_bp->name,"vmel") ==0) ||
448 (bustype == BUS_OBIO && strcmp(ca->ca_ra.ra_bp->name,"obio") == 0)))
449 oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
450 else
451 oca.ca_ra.ra_bp = NULL;
452 oca.ca_bustype = bustype;
453
454 if ((*cf->cf_attach->ca_match)(parent, cf, &oca) == 0)
455 return 0;
456
457 /*
458 * check if XXmatch routine replaced the temporary mapping with
459 * a real mapping. If not, then make sure we don't pass the
460 * tmp mapping to the attach routine.
461 */
462 if (oca.ca_ra.ra_vaddr == tmp)
463 oca.ca_ra.ra_vaddr = NULL; /* wipe out tmp address */
464 /*
465 * the match routine will set "ra_len" if it wants us to
466 * establish a mapping for it.
467 * (which won't be seen on future XXmatch calls,
468 * so not as useful as it seems.)
469 */
470 if (oca.ca_ra.ra_len)
471 oca.ca_ra.ra_vaddr =
472 bus_map(oca.ca_ra.ra_reg, oca.ca_ra.ra_len);
473
474 config_attach(parent, cf, &oca, busprint);
475 return 1;
476 #else
477 return 0;
478 #endif
479 }
480
481 int
482 obio_scan(parent, child, args)
483 struct device *parent;
484 struct cfdata *child;
485 void *args;
486 {
487 return busattach(parent, child, args, BUS_OBIO);
488 }
489
490 int
491 vmes_scan(parent, child, args)
492 struct device *parent;
493 struct cfdata *child;
494 void *args;
495 {
496 return busattach(parent, child, args, BUS_VME16);
497 }
498
499 int
500 vmel_scan(parent, child, args)
501 struct device *parent;
502 struct cfdata *child;
503 void *args;
504 {
505 return busattach(parent, child, args, BUS_VME32);
506 }
507
508 int pil_to_vme[] = {
509 -1, /* pil 0 */
510 -1, /* pil 1 */
511 1, /* pil 2 */
512 2, /* pil 3 */
513 -1, /* pil 4 */
514 3, /* pil 5 */
515 -1, /* pil 6 */
516 4, /* pil 7 */
517 -1, /* pil 8 */
518 5, /* pil 9 */
519 -1, /* pil 10 */
520 6, /* pil 11 */
521 -1, /* pil 12 */
522 7, /* pil 13 */
523 -1, /* pil 14 */
524 -1, /* pil 15 */
525 };
526
527 int
528 vmeintr(arg)
529 void *arg;
530 {
531 int pil = (int)arg, level, vec;
532 struct intrhand *ih;
533 int i = 0;
534
535 level = (pil_to_vme[pil] << 1) | 1;
536
537 if (CPU_ISSUN4) {
538 vec = ldcontrolb((caddr_t)(AC_VMEINTVEC | level));
539 } else if (CPU_ISSUN4M) {
540 vec = vmebus_sc->sc_vec->vmebusvec[level];
541 } else
542 panic("vme: spurious interrupt");
543
544 if (vec == -1) {
545 printf("vme: spurious interrupt\n");
546 return 0;
547 }
548
549 for (ih = vmeints[vec]; ih; ih = ih->ih_next)
550 if (ih->ih_fun)
551 i += (ih->ih_fun)(ih->ih_arg);
552 return (i);
553 }
554
555 void
556 vmeintr_establish(vec, level, ih)
557 int vec, level;
558 struct intrhand *ih;
559 {
560 struct intrhand *ihs;
561
562 if (!CPU_ISSUN4) {
563 panic("vmeintr_establish: not supported on cpu-type %d",
564 cputyp);
565 }
566
567 if (vec == -1)
568 panic("vmeintr_establish: uninitialized vec\n");
569
570 if (vmeints[vec] == NULL)
571 vmeints[vec] = ih;
572 else {
573 for (ihs = vmeints[vec]; ihs->ih_next; ihs = ihs->ih_next)
574 ;
575 ihs->ih_next = ih;
576 }
577
578 /* ensure the interrupt subsystem will call us at this level */
579 for (ihs = intrhand[level]; ihs; ihs = ihs->ih_next)
580 if (ihs->ih_fun == vmeintr)
581 return;
582
583 ihs = (struct intrhand *)malloc(sizeof(struct intrhand),
584 M_TEMP, M_NOWAIT);
585 if (ihs == NULL)
586 panic("vme_addirq");
587 bzero(ihs, sizeof *ihs);
588 ihs->ih_fun = vmeintr;
589 ihs->ih_arg = (void *)level;
590 intr_establish(level, ihs);
591 }
592
593 #define getpte(va) lda(va, ASI_PTE)
594
595 /*
596 * If we can find a mapping that was established by the rom, use it.
597 * Else, create a new mapping.
598 */
599 void *
600 bus_map(pa, len)
601 struct rom_reg *pa;
602 int len;
603 {
604
605 if (CPU_ISSUN4 && len <= NBPG) {
606 u_long pf = (u_long)(pa->rr_paddr) >> PGSHIFT;
607 int pgtype = PMAP_T2PTE_4(pa->rr_iospace);
608 u_long va, pte;
609
610 for (va = OLDMON_STARTVADDR; va < OLDMON_ENDVADDR; va += NBPG) {
611 pte = getpte(va);
612 if ((pte & PG_V) != 0 && (pte & PG_TYPE) == pgtype &&
613 (pte & PG_PFNUM) == pf)
614 return ((void *)
615 (va | ((u_long)pa->rr_paddr & PGOFSET)) );
616 /* note: preserve page offset */
617 }
618 }
619
620 return mapiodev(pa, 0, len);
621 }
622
623 void
624 bus_untmp()
625 {
626 pmap_remove(pmap_kernel(), TMPMAP_VA, TMPMAP_VA+NBPG);
627 }
628