obio.c revision 1.38 1 /* $NetBSD: obio.c,v 1.38 1998/01/12 20:23:54 thorpej 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 void vmebus_translate __P((struct device *, struct confargs *, int));
89 int vmeintr __P((void *));
90
91 struct cfattach obio_ca = {
92 sizeof(struct bus_softc), busmatch, obioattach
93 };
94
95 struct cfattach vmel_ca = {
96 sizeof(struct bus_softc), busmatch, vmelattach
97 };
98
99 struct cfattach vmes_ca = {
100 sizeof(struct bus_softc), busmatch, vmesattach
101 };
102
103 struct cfattach vme_ca = {
104 sizeof(struct bus_softc), busmatch, vmeattach
105 };
106
107 struct intrhand **vmeints;
108
109
110 int
111 busmatch(parent, cf, aux)
112 struct device *parent;
113 struct cfdata *cf;
114 void *aux;
115 {
116 register struct confargs *ca = aux;
117 register struct romaux *ra = &ca->ca_ra;
118
119 if (CPU_ISSUN4M)
120 return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
121
122 if (!CPU_ISSUN4)
123 return (0);
124
125 return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
126 }
127
128 int
129 busprint(args, obio)
130 void *args;
131 const char *obio;
132 {
133 register struct confargs *ca = args;
134
135 if (ca->ca_ra.ra_name == NULL)
136 ca->ca_ra.ra_name = "<unknown>";
137
138 if (obio)
139 printf("[%s at %s]", ca->ca_ra.ra_name, obio);
140
141 printf(" addr %p", ca->ca_ra.ra_paddr);
142
143 if (CPU_ISSUN4 && ca->ca_ra.ra_intr[0].int_vec != -1)
144 printf(" vec 0x%x", ca->ca_ra.ra_intr[0].int_vec);
145
146 return (UNCONF);
147 }
148
149 int
150 vmeprint(args, name)
151 void *args;
152 const char *name;
153 {
154 register struct confargs *ca = args;
155
156 if (name)
157 printf("%s at %s", ca->ca_ra.ra_name, name);
158 return (UNCONF);
159 }
160
161 void
162 obioattach(parent, self, args)
163 struct device *parent, *self;
164 void *args;
165 {
166 #if defined(SUN4M)
167 register struct bus_softc *sc = (struct bus_softc *)self;
168 struct confargs oca, *ca = args;
169 register struct romaux *ra = &ca->ca_ra;
170 register int node0, node;
171 register char *name;
172 register const char *sp;
173 const char *const *ssp;
174 int rlen;
175 extern int autoconf_nzs;
176
177 static const char *const special4m[] = {
178 /* find these first */
179 "eeprom",
180 "counter",
181 #if 0 /* Not all sun4m's have an `auxio' */
182 "auxio",
183 #endif
184 "",
185 /* place device to ignore here */
186 "interrupt",
187 NULL
188 };
189 #endif
190
191 if (CPU_ISSUN4) {
192 if (self->dv_unit > 0) {
193 printf(" unsupported\n");
194 return;
195 }
196 printf("\n");
197
198 (void)config_search(obio_scan, self, args);
199 bus_untmp();
200 }
201
202 #if defined(SUN4M)
203 if (!CPU_ISSUN4M)
204 return;
205
206 /*
207 * There is only one obio bus (it is in fact one of the Sbus slots)
208 * How about VME?
209 */
210 if (self->dv_unit > 0) {
211 printf(" unsupported\n");
212 return;
213 }
214
215 printf("\n");
216
217 if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "obio") == 0)
218 oca.ca_ra.ra_bp = ra->ra_bp + 1;
219 else
220 oca.ca_ra.ra_bp = NULL;
221
222 node = ra->ra_node;
223 rlen = getproplen(node, "ranges");
224 if (rlen > 0) {
225 sc->bu.scu_sbus.sc_nrange = rlen / sizeof(struct rom_range);
226 sc->bu.scu_sbus.sc_range =
227 (struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
228 if (sc->bu.scu_sbus.sc_range == 0)
229 panic("obio: PROM ranges too large: %d", rlen);
230 (void)getprop(node, "ranges", sc->bu.scu_sbus.sc_range, rlen);
231 }
232
233 /*
234 * Loop through ROM children, fixing any relative addresses
235 * and then configuring each device.
236 * We first do the crucial ones, such as eeprom, etc.
237 */
238 node0 = firstchild(ra->ra_node);
239 for (ssp = special4m ; *(sp = *ssp) != 0; ssp++) {
240 if ((node = findnode(node0, sp)) == 0) {
241 printf("could not find %s amongst obio devices\n", sp);
242 panic(sp);
243 }
244 if (!romprop(&oca.ca_ra, sp, node))
245 continue;
246
247 sbus_translate(self, &oca);
248 oca.ca_bustype = BUS_OBIO;
249 (void) config_found(self, (void *)&oca, busprint);
250 }
251
252 for (node = node0; node; node = nextsibling(node)) {
253 name = getpropstring(node, "name");
254 for (ssp = special4m ; (sp = *ssp) != NULL; ssp++)
255 if (strcmp(name, sp) == 0)
256 break;
257
258 if (sp != NULL || !romprop(&oca.ca_ra, name, node))
259 continue;
260
261 if (strcmp(name, "zs") == 0)
262 /* XXX - see autoconf.c for this hack */
263 autoconf_nzs++;
264
265 /* Translate into parent address spaces */
266 sbus_translate(self, &oca);
267 oca.ca_bustype = BUS_OBIO;
268 (void) config_found(self, (void *)&oca, busprint);
269 }
270 #endif
271 }
272
273 void
274 vmesattach(parent, self, args)
275 struct device *parent, *self;
276 void *args;
277 {
278 if (self->dv_unit > 0 ||
279 (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
280 printf(" unsupported\n");
281 return;
282 }
283 printf("\n");
284
285 if (vmeints == NULL) {
286 vmeints = (struct intrhand **)malloc(256 *
287 sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
288 bzero(vmeints, 256 * sizeof(struct intrhand *));
289 }
290 (void)config_search(vmes_scan, self, args);
291 bus_untmp();
292 }
293
294 void
295 vmelattach(parent, self, args)
296 struct device *parent, *self;
297 void *args;
298 {
299 if (self->dv_unit > 0 ||
300 (CPU_ISSUN4M && strncmp(parent->dv_xname, "vme", 3) != 0)) {
301 printf(" unsupported\n");
302 return;
303 }
304 printf("\n");
305
306 if (vmeints == NULL) {
307 vmeints = (struct intrhand **)malloc(256 *
308 sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
309 bzero(vmeints, 256 * sizeof(struct intrhand *));
310 }
311 (void)config_search(vmel_scan, self, args);
312 bus_untmp();
313 }
314
315 void
316 vmeattach(parent, self, aux)
317 struct device *parent, *self;
318 void *aux;
319 {
320 struct vmebus_softc *sc = (struct vmebus_softc *)self;
321 struct confargs *ca = aux;
322 register struct romaux *ra = &ca->ca_ra;
323 int node, rlen;
324 struct confargs oca;
325
326 if (!CPU_ISSUN4M || self->dv_unit > 0) {
327 printf(" unsupported\n");
328 return;
329 }
330
331 node = ra->ra_node;
332
333 sc->sc_reg = (struct vmebusreg *)
334 mapdev(&ra->ra_reg[0], 0, 0, ra->ra_reg[0].rr_len);
335 sc->sc_vec = (struct vmebusvec *)
336 mapdev(&ra->ra_reg[1], 0, 0, ra->ra_reg[1].rr_len);
337
338 /*
339 * Get "range" property, though we don't do anything with it yet.
340 */
341 rlen = getproplen(node, "ranges");
342 if (rlen > 0) {
343 sc->sc_nrange = rlen / sizeof(struct rom_range);
344 sc->sc_range =
345 (struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT);
346 if (sc->sc_range == 0)
347 panic("vme: PROM ranges too large: %d", rlen);
348 (void)getprop(node, "ranges", sc->sc_range, rlen);
349 }
350
351 vmebus_sc = sc;
352 printf(": version 0x%x\n",
353 sc->sc_reg->vmebus_cr & VMEBUS_CR_IMPL);
354
355 if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "vme") == 0)
356 oca.ca_ra.ra_bp = ra->ra_bp + 1;
357 else
358 oca.ca_ra.ra_bp = NULL;
359
360 oca.ca_ra.ra_name = "vmes";
361 oca.ca_bustype = BUS_MAIN;
362 (void)config_found(self, (void *)&oca, vmeprint);
363
364 oca.ca_ra.ra_name = "vmel";
365 oca.ca_bustype = BUS_MAIN;
366 (void)config_found(self, (void *)&oca, vmeprint);
367 }
368
369 void
370 vmebus_translate(dev, ca, bustype)
371 struct device *dev;
372 struct confargs *ca;
373 int bustype;
374 {
375 struct vmebus_softc *sc = (struct vmebus_softc *)dev;
376 register int j;
377 int cspace;
378
379 if (sc->sc_nrange == 0)
380 panic("vmebus: no ranges");
381
382 /*
383 * Find VMEbus modifier based on address space.
384 * XXX - should not be encoded in `ra_paddr'
385 */
386 if (((u_long)ca->ca_ra.ra_paddr & 0xffff0000) == 0xffff0000)
387 cspace = VMEMOD_A16_D_S;
388 else if (((u_long)ca->ca_ra.ra_paddr & 0xff000000) == 0xff000000)
389 cspace = VMEMOD_A24_D_S;
390 else
391 cspace = VMEMOD_A32_D_S;
392
393 cspace |= (bustype == BUS_VME32) ? VMEMOD_D32 : 0;
394
395 /* Translate into parent address spaces */
396 for (j = 0; j < sc->sc_nrange; j++) {
397 if (sc->sc_range[j].cspace == cspace) {
398 #if notyet
399 (int)ca->ca_ra.ra_paddr +=
400 sc->sc_range[j].poffset;
401 #endif
402 (int)ca->ca_ra.ra_iospace =
403 sc->sc_range[j].pspace;
404 break;
405 }
406 }
407 }
408
409 int bt2pmt[] = {
410 PMAP_OBIO,
411 PMAP_OBIO,
412 PMAP_VME16,
413 PMAP_VME32,
414 PMAP_OBIO
415 };
416
417 int
418 busattach(parent, cf, args, bustype)
419 struct device *parent;
420 struct cfdata *cf;
421 void *args;
422 int bustype;
423 {
424 #if defined(SUN4) || defined(SUN4M)
425 register struct confargs *ca = args;
426 struct confargs oca;
427 caddr_t tmp;
428
429 if (bustype == BUS_OBIO && CPU_ISSUN4) {
430
431 /*
432 * avoid sun4m entries which don't have valid PA's.
433 * no point in even probing them.
434 */
435 if (cf->cf_loc[0] == -1) return 0;
436
437 /*
438 * On the 4/100 obio addresses must be mapped at
439 * 0x0YYYYYYY, but alias higher up (we avoid the
440 * alias condition because it causes pmap difficulties)
441 * XXX: We also assume that 4/[23]00 obio addresses
442 * must be 0xZYYYYYYY, where (Z != 0)
443 */
444 if (cpuinfo.cpu_type == CPUTYP_4_100 &&
445 (cf->cf_loc[0] & 0xf0000000))
446 return 0;
447 if (cpuinfo.cpu_type != CPUTYP_4_100 &&
448 !(cf->cf_loc[0] & 0xf0000000))
449 return 0;
450 }
451
452 oca.ca_ra.ra_paddr = (void *)cf->cf_loc[0];
453 oca.ca_ra.ra_len = 0;
454 oca.ca_ra.ra_nreg = 1;
455 if (CPU_ISSUN4M)
456 vmebus_translate(parent->dv_parent, &oca, bustype);
457 else
458 oca.ca_ra.ra_iospace = bt2pmt[bustype];
459
460 if (oca.ca_ra.ra_paddr)
461 tmp = (caddr_t)mapdev(oca.ca_ra.ra_reg, TMPMAP_VA, 0, NBPG);
462 else
463 tmp = NULL;
464 oca.ca_ra.ra_vaddr = tmp;
465 oca.ca_ra.ra_intr[0].int_pri = cf->cf_loc[1];
466 if (bustype == BUS_VME16 || bustype == BUS_VME32)
467 oca.ca_ra.ra_intr[0].int_vec = cf->cf_loc[2];
468 else
469 oca.ca_ra.ra_intr[0].int_vec = -1;
470 oca.ca_ra.ra_nintr = 1;
471 oca.ca_ra.ra_name = cf->cf_driver->cd_name;
472 if (ca->ca_ra.ra_bp != NULL &&
473 ((bustype == BUS_VME16 && strcmp(ca->ca_ra.ra_bp->name,"vmes") ==0) ||
474 (bustype == BUS_VME32 && strcmp(ca->ca_ra.ra_bp->name,"vmel") ==0) ||
475 (bustype == BUS_OBIO && strcmp(ca->ca_ra.ra_bp->name,"obio") == 0)))
476 oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1;
477 else
478 oca.ca_ra.ra_bp = NULL;
479 oca.ca_bustype = bustype;
480
481 if ((*cf->cf_attach->ca_match)(parent, cf, &oca) == 0)
482 return 0;
483
484 /*
485 * check if XXmatch routine replaced the temporary mapping with
486 * a real mapping. If not, then make sure we don't pass the
487 * tmp mapping to the attach routine.
488 */
489 if (oca.ca_ra.ra_vaddr == tmp)
490 oca.ca_ra.ra_vaddr = NULL; /* wipe out tmp address */
491 /*
492 * the match routine will set "ra_len" if it wants us to
493 * establish a mapping for it.
494 * (which won't be seen on future XXmatch calls,
495 * so not as useful as it seems.)
496 */
497 if (oca.ca_ra.ra_len)
498 oca.ca_ra.ra_vaddr =
499 bus_map(oca.ca_ra.ra_reg, oca.ca_ra.ra_len);
500
501 config_attach(parent, cf, &oca, busprint);
502 return 1;
503 #else
504 return 0;
505 #endif
506 }
507
508 int
509 obio_scan(parent, child, args)
510 struct device *parent;
511 struct cfdata *child;
512 void *args;
513 {
514 return busattach(parent, child, args, BUS_OBIO);
515 }
516
517 int
518 vmes_scan(parent, child, args)
519 struct device *parent;
520 struct cfdata *child;
521 void *args;
522 {
523 return busattach(parent, child, args, BUS_VME16);
524 }
525
526 int
527 vmel_scan(parent, child, args)
528 struct device *parent;
529 struct cfdata *child;
530 void *args;
531 {
532 return busattach(parent, child, args, BUS_VME32);
533 }
534
535 int pil_to_vme[] = {
536 -1, /* pil 0 */
537 -1, /* pil 1 */
538 1, /* pil 2 */
539 2, /* pil 3 */
540 -1, /* pil 4 */
541 3, /* pil 5 */
542 -1, /* pil 6 */
543 4, /* pil 7 */
544 -1, /* pil 8 */
545 5, /* pil 9 */
546 -1, /* pil 10 */
547 6, /* pil 11 */
548 -1, /* pil 12 */
549 7, /* pil 13 */
550 -1, /* pil 14 */
551 -1, /* pil 15 */
552 };
553
554 int
555 vmeintr(arg)
556 void *arg;
557 {
558 int pil = (int)arg, level, vec;
559 struct intrhand *ih;
560 int i = 0;
561
562 level = (pil_to_vme[pil] << 1) | 1;
563
564 if (CPU_ISSUN4) {
565 vec = ldcontrolb((caddr_t)(AC_VMEINTVEC | level));
566 } else if (CPU_ISSUN4M) {
567 vec = vmebus_sc->sc_vec->vmebusvec[level];
568 } else
569 panic("vme: spurious interrupt");
570
571 if (vec == -1) {
572 printf("vme: spurious interrupt\n");
573 return 0;
574 }
575
576 for (ih = vmeints[vec]; ih; ih = ih->ih_next)
577 if (ih->ih_fun)
578 i += (ih->ih_fun)(ih->ih_arg);
579 return (i);
580 }
581
582 void
583 vmeintr_establish(vec, level, ih)
584 int vec, level;
585 struct intrhand *ih;
586 {
587 struct intrhand *ihs;
588
589 if (vmeints == NULL)
590 panic("vmeintr_establish: interrupt vector not allocated");
591
592 if (vec == -1)
593 panic("vmeintr_establish: uninitialized vec");
594
595 if (vmeints[vec] == NULL)
596 vmeints[vec] = ih;
597 else {
598 for (ihs = vmeints[vec]; ihs->ih_next; ihs = ihs->ih_next)
599 ;
600 ihs->ih_next = ih;
601 }
602
603 /* ensure the interrupt subsystem will call us at this level */
604 for (ihs = intrhand[level]; ihs; ihs = ihs->ih_next)
605 if (ihs->ih_fun == vmeintr)
606 return;
607
608 ihs = (struct intrhand *)malloc(sizeof(struct intrhand),
609 M_TEMP, M_NOWAIT);
610 if (ihs == NULL)
611 panic("vme_addirq");
612 bzero(ihs, sizeof *ihs);
613 ihs->ih_fun = vmeintr;
614 ihs->ih_arg = (void *)level;
615 intr_establish(level, ihs);
616 }
617
618 #define getpte(va) lda(va, ASI_PTE)
619
620 /*
621 * If we can find a mapping that was established by the rom, use it.
622 * Else, create a new mapping.
623 */
624 void *
625 bus_map(pa, len)
626 struct rom_reg *pa;
627 int len;
628 {
629
630 if (CPU_ISSUN4 && len <= NBPG) {
631 u_long pf = (u_long)(pa->rr_paddr) >> PGSHIFT;
632 int pgtype = PMAP_T2PTE_4(pa->rr_iospace);
633 u_long va, pte;
634
635 for (va = OLDMON_STARTVADDR; va < OLDMON_ENDVADDR; va += NBPG) {
636 pte = getpte(va);
637 if ((pte & PG_V) != 0 && (pte & PG_TYPE) == pgtype &&
638 (pte & PG_PFNUM) == pf)
639 return ((void *)
640 (va | ((u_long)pa->rr_paddr & PGOFSET)) );
641 /* note: preserve page offset */
642 }
643 }
644
645 return mapiodev(pa, 0, len);
646 }
647
648 void
649 bus_untmp()
650 {
651 pmap_remove(pmap_kernel(), TMPMAP_VA, TMPMAP_VA+NBPG);
652 }
653