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