obio.c revision 1.33
1/* $NetBSD: obio.c,v 1.33 1997/05/18 21:26:22 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 57struct 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}; 64struct vmebus_softc *vmebus_sc;/*XXX*/ 65 66struct 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 */ 76static int busmatch __P((struct device *, struct cfdata *, void *)); 77static void obioattach __P((struct device *, struct device *, void *)); 78static void vmesattach __P((struct device *, struct device *, void *)); 79static void vmelattach __P((struct device *, struct device *, void *)); 80static void vmeattach __P((struct device *, struct device *, void *)); 81 82int busprint __P((void *, const char *)); 83int vmeprint __P((void *, const char *)); 84static int busattach __P((struct device *, struct cfdata *, void *, int)); 85void * bus_map __P((struct rom_reg *, int, int)); 86int obio_scan __P((struct device *, struct cfdata *, void *)); 87int vmes_scan __P((struct device *, struct cfdata *, void *)); 88int vmel_scan __P((struct device *, struct cfdata *, void *)); 89int vmeintr __P((void *)); 90 91struct cfattach obio_ca = { 92 sizeof(struct bus_softc), busmatch, obioattach 93}; 94 95struct cfdriver obio_cd = { 96 NULL, "obio", DV_DULL 97}; 98 99struct cfattach vmel_ca = { 100 sizeof(struct bus_softc), busmatch, vmelattach 101}; 102 103struct cfdriver vmel_cd = { 104 NULL, "vmel", DV_DULL 105}; 106 107struct cfattach vmes_ca = { 108 sizeof(struct bus_softc), busmatch, vmesattach 109}; 110 111struct cfdriver vmes_cd = { 112 NULL, "vmes", DV_DULL 113}; 114 115struct cfattach vme_ca = { 116 sizeof(struct bus_softc), busmatch, vmeattach 117}; 118 119struct cfdriver vme_cd = { 120 NULL, "vme", DV_DULL 121}; 122 123struct intrhand **vmeints; 124 125 126int 127busmatch(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 144int 145busprint(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 165int 166vmeprint(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 177void 178obioattach(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 289void 290vmesattach(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 310void 311vmelattach(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 331void 332vmeattach(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 ra->ra_reg[0].rr_iospace); 352 sc->sc_vec = (struct vmebusvec *) 353 mapdev(&ra->ra_reg[1], 0, 0, ra->ra_reg[1].rr_len, 354 ra->ra_reg[1].rr_iospace); 355 356 /* 357 * Get "range" property, though we don't do anything with it yet. 358 */ 359 rlen = getproplen(node, "ranges"); 360 if (rlen > 0) { 361 sc->sc_nrange = rlen / sizeof(struct rom_range); 362 sc->sc_range = 363 (struct rom_range *)malloc(rlen, M_DEVBUF, M_NOWAIT); 364 if (sc->sc_range == 0) 365 panic("vme: PROM ranges too large: %d", rlen); 366 (void)getprop(node, "ranges", sc->sc_range, rlen); 367 } 368 369 vmebus_sc = sc; 370 printf(": version %x\n", 371 sc->sc_reg->vmebus_cr & VMEBUS_CR_IMPL); 372 373 if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "vme") == 0) 374 oca.ca_ra.ra_bp = ra->ra_bp + 1; 375 else 376 oca.ca_ra.ra_bp = NULL; 377 378 oca.ca_ra.ra_name = "vmes"; 379 oca.ca_bustype = BUS_MAIN; 380 (void)config_found(self, (void *)&oca, vmeprint); 381 382 oca.ca_ra.ra_name = "vmel"; 383 oca.ca_bustype = BUS_MAIN; 384 (void)config_found(self, (void *)&oca, vmeprint); 385} 386 387int 388busattach(parent, cf, args, bustype) 389 struct device *parent; 390 struct cfdata *cf; 391 void *args; 392 int bustype; 393{ 394#if defined(SUN4) || defined(SUN4M) 395 register struct confargs *ca = args; 396 struct confargs oca; 397 caddr_t tmp; 398 399 if (bustype == BUS_OBIO && CPU_ISSUN4) { 400 401 /* 402 * avoid sun4m entries which don't have valid PA's. 403 * no point in even probing them. 404 */ 405 if (cf->cf_loc[0] == -1) return 0; 406 407 /* 408 * On the 4/100 obio addresses must be mapped at 409 * 0x0YYYYYYY, but alias higher up (we avoid the 410 * alias condition because it causes pmap difficulties) 411 * XXX: We also assume that 4/[23]00 obio addresses 412 * must be 0xZYYYYYYY, where (Z != 0) 413 */ 414 if (cpuinfo.cpu_type == CPUTYP_4_100 && 415 (cf->cf_loc[0] & 0xf0000000)) 416 return 0; 417 if (cpuinfo.cpu_type != CPUTYP_4_100 && 418 !(cf->cf_loc[0] & 0xf0000000)) 419 return 0; 420 } 421 422 /* XXX - streamline bustype stuff */ 423 oca.ca_ra.ra_iospace = CPU_ISSUN4 424 ? -1 425 : ((bustype == BUS_VME32) ? VME_SUN4M_32 : VME_SUN4M_16); 426 oca.ca_ra.ra_paddr = (void *)cf->cf_loc[0]; 427 oca.ca_ra.ra_len = 0; 428 oca.ca_ra.ra_nreg = 1; 429 if (oca.ca_ra.ra_paddr) 430 tmp = (caddr_t)mapdev(oca.ca_ra.ra_reg, 431 TMPMAP_VA, 0, NBPG, bustype); 432 else 433 tmp = NULL; 434 oca.ca_ra.ra_vaddr = tmp; 435 oca.ca_ra.ra_intr[0].int_pri = cf->cf_loc[1]; 436 if (bustype == BUS_VME16 || bustype == BUS_VME32) 437 oca.ca_ra.ra_intr[0].int_vec = cf->cf_loc[2]; 438 else 439 oca.ca_ra.ra_intr[0].int_vec = -1; 440 oca.ca_ra.ra_nintr = 1; 441 oca.ca_ra.ra_name = cf->cf_driver->cd_name; 442 if (ca->ca_ra.ra_bp != NULL && 443 ((bustype == BUS_VME16 && strcmp(ca->ca_ra.ra_bp->name,"vmes") ==0) || 444 (bustype == BUS_VME32 && strcmp(ca->ca_ra.ra_bp->name,"vmel") ==0) || 445 (bustype == BUS_OBIO && strcmp(ca->ca_ra.ra_bp->name,"obio") == 0))) 446 oca.ca_ra.ra_bp = ca->ca_ra.ra_bp + 1; 447 else 448 oca.ca_ra.ra_bp = NULL; 449 oca.ca_bustype = bustype; 450 451 if ((*cf->cf_attach->ca_match)(parent, cf, &oca) == 0) 452 return 0; 453 454 /* 455 * check if XXmatch routine replaced the temporary mapping with 456 * a real mapping. If not, then make sure we don't pass the 457 * tmp mapping to the attach routine. 458 */ 459 if (oca.ca_ra.ra_vaddr == tmp) 460 oca.ca_ra.ra_vaddr = NULL; /* wipe out tmp address */ 461 /* 462 * the match routine will set "ra_len" if it wants us to 463 * establish a mapping for it. 464 * (which won't be seen on future XXmatch calls, 465 * so not as useful as it seems.) 466 */ 467 if (oca.ca_ra.ra_len) 468 oca.ca_ra.ra_vaddr = 469 bus_map(oca.ca_ra.ra_reg, oca.ca_ra.ra_len, oca.ca_bustype); 470 471 config_attach(parent, cf, &oca, busprint); 472 return 1; 473#else 474 return 0; 475#endif 476} 477 478int 479obio_scan(parent, child, args) 480 struct device *parent; 481 struct cfdata *child; 482 void *args; 483{ 484 return busattach(parent, child, args, BUS_OBIO); 485} 486 487int 488vmes_scan(parent, child, args) 489 struct device *parent; 490 struct cfdata *child; 491 void *args; 492{ 493 return busattach(parent, child, args, BUS_VME16); 494} 495 496int 497vmel_scan(parent, child, args) 498 struct device *parent; 499 struct cfdata *child; 500 void *args; 501{ 502 return busattach(parent, child, args, BUS_VME32); 503} 504 505int pil_to_vme[] = { 506 -1, /* pil 0 */ 507 -1, /* pil 1 */ 508 1, /* pil 2 */ 509 2, /* pil 3 */ 510 -1, /* pil 4 */ 511 3, /* pil 5 */ 512 -1, /* pil 6 */ 513 4, /* pil 7 */ 514 -1, /* pil 8 */ 515 5, /* pil 9 */ 516 -1, /* pil 10 */ 517 6, /* pil 11 */ 518 -1, /* pil 12 */ 519 7, /* pil 13 */ 520 -1, /* pil 14 */ 521 -1, /* pil 15 */ 522}; 523 524int 525vmeintr(arg) 526 void *arg; 527{ 528 int pil = (int)arg, level, vec; 529 struct intrhand *ih; 530 int i = 0; 531 532 level = (pil_to_vme[pil] << 1) | 1; 533 534 if (CPU_ISSUN4) { 535 vec = ldcontrolb((caddr_t)(AC_VMEINTVEC | level)); 536 } else if (CPU_ISSUN4M) { 537 vec = vmebus_sc->sc_vec->vmebusvec[level]; 538 } else 539 panic("vme: spurious interrupt"); 540 541 if (vec == -1) { 542 printf("vme: spurious interrupt\n"); 543 return 0; 544 } 545 546 for (ih = vmeints[vec]; ih; ih = ih->ih_next) 547 if (ih->ih_fun) 548 i += (ih->ih_fun)(ih->ih_arg); 549 return (i); 550} 551 552void 553vmeintr_establish(vec, level, ih) 554 int vec, level; 555 struct intrhand *ih; 556{ 557 struct intrhand *ihs; 558 559 if (!CPU_ISSUN4) { 560 panic("vmeintr_establish: not supported on cpu-type %d", 561 cputyp); 562 } 563 564 if (vec == -1) 565 panic("vmeintr_establish: uninitialized vec\n"); 566 567 if (vmeints[vec] == NULL) 568 vmeints[vec] = ih; 569 else { 570 for (ihs = vmeints[vec]; ihs->ih_next; ihs = ihs->ih_next) 571 ; 572 ihs->ih_next = ih; 573 } 574 575 /* ensure the interrupt subsystem will call us at this level */ 576 for (ihs = intrhand[level]; ihs; ihs = ihs->ih_next) 577 if (ihs->ih_fun == vmeintr) 578 return; 579 580 ihs = (struct intrhand *)malloc(sizeof(struct intrhand), 581 M_TEMP, M_NOWAIT); 582 if (ihs == NULL) 583 panic("vme_addirq"); 584 bzero(ihs, sizeof *ihs); 585 ihs->ih_fun = vmeintr; 586 ihs->ih_arg = (void *)level; 587 intr_establish(level, ihs); 588} 589 590#define getpte(va) lda(va, ASI_PTE) 591 592/* 593 * If we can find a mapping that was established by the rom, use it. 594 * Else, create a new mapping. 595 */ 596void * 597bus_map(pa, len, bustype) 598 struct rom_reg *pa; 599 int len; 600 int bustype; 601{ 602 603 if (CPU_ISSUN4 && len <= NBPG) { 604 u_long pf = (u_long)(pa->rr_paddr) >> PGSHIFT; 605 u_long va, pte; 606 int pgtype = -1; 607 608 switch (bt2pmt[bustype]) { 609 case PMAP_OBIO: 610 pgtype = PG_OBIO; 611 break; 612 case PMAP_VME32: 613 pgtype = PG_VME32; 614 break; 615 case PMAP_VME16: 616 pgtype = PG_VME16; 617 break; 618 } 619 620 for (va = OLDMON_STARTVADDR; va < OLDMON_ENDVADDR; va += NBPG) { 621 pte = getpte(va); 622 if ((pte & PG_V) != 0 && (pte & PG_TYPE) == pgtype && 623 (pte & PG_PFNUM) == pf) 624 return ((void *) 625 (va | ((u_long)pa->rr_paddr & PGOFSET)) ); 626 /* note: preserve page offset */ 627 } 628 } 629 630 if (CPU_ISSUN4M) 631 pa->rr_iospace = 632 (bustype == BUS_VME32) ? VME_SUN4M_32 : VME_SUN4M_16; 633 return mapiodev(pa, 0, len, bustype); 634} 635 636void 637bus_untmp() 638{ 639 pmap_remove(pmap_kernel(), TMPMAP_VA, TMPMAP_VA+NBPG); 640} 641