1 /*- 2 * Copyright (C) 2002 Benno Rice. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/module.h> 31 #include <sys/bus.h> 32 #include <sys/conf.h> 33 #include <sys/kernel.h> 34 35 #include <dev/ofw/openfirm.h> 36 #include <dev/ofw/ofw_pci.h> 37 #include <dev/ofw/ofw_bus.h> 38 #include <dev/ofw/ofw_bus_subr.h> 39 40 #include <dev/pci/pcivar.h> 41 #include <dev/pci/pcireg.h> 42 43 #include <machine/bus.h> 44 #include <machine/intr_machdep.h> 45 #include <machine/md_var.h> 46 #include <machine/pio.h> 47 #include <machine/resource.h> 48 49 #include <sys/rman.h> 50 51 #include <powerpc/powermac/uninorthvar.h> 52 53 #include <vm/vm.h> 54 #include <vm/pmap.h> 55 56 /* 57 * Driver for the Uninorth chip itself. 58 */ 59 60 static MALLOC_DEFINE(M_UNIN, "unin", "unin device information"); 61 62 /* 63 * Device interface. 64 */ 65 66 static int unin_chip_probe(device_t); 67 static int unin_chip_attach(device_t); 68 69 /* 70 * Bus interface. 71 */ 72 static int unin_chip_print_child(device_t dev, device_t child); 73 static void unin_chip_probe_nomatch(device_t, device_t); 74 static struct resource *unin_chip_alloc_resource(device_t, device_t, int, int *, 75 u_long, u_long, u_long, u_int); 76 static int unin_chip_activate_resource(device_t, device_t, int, int, 77 struct resource *); 78 static int unin_chip_deactivate_resource(device_t, device_t, int, int, 79 struct resource *); 80 static int unin_chip_release_resource(device_t, device_t, int, int, 81 struct resource *); 82 static struct resource_list *unin_chip_get_resource_list (device_t, device_t); 83 84 /* 85 * OFW Bus interface 86 */ 87 88 static ofw_bus_get_devinfo_t unin_chip_get_devinfo; 89 90 /* 91 * Local routines 92 */ 93 94 static void unin_enable_gmac(device_t dev); 95 static void unin_enable_mpic(device_t dev); 96 97 /* 98 * Driver methods. 99 */ 100 static device_method_t unin_chip_methods[] = { 101 102 /* Device interface */ 103 DEVMETHOD(device_probe, unin_chip_probe), 104 DEVMETHOD(device_attach, unin_chip_attach), 105 106 /* Bus interface */ 107 DEVMETHOD(bus_print_child, unin_chip_print_child), 108 DEVMETHOD(bus_probe_nomatch, unin_chip_probe_nomatch), 109 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 110 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 111 112 DEVMETHOD(bus_alloc_resource, unin_chip_alloc_resource), 113 DEVMETHOD(bus_release_resource, unin_chip_release_resource), 114 DEVMETHOD(bus_activate_resource, unin_chip_activate_resource), 115 DEVMETHOD(bus_deactivate_resource, unin_chip_deactivate_resource), 116 DEVMETHOD(bus_get_resource_list, unin_chip_get_resource_list), 117 118 DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), 119 120 /* ofw_bus interface */ 121 DEVMETHOD(ofw_bus_get_devinfo, unin_chip_get_devinfo), 122 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 123 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 124 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 125 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 126 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 127 128 { 0, 0 } 129 }; 130 131 static driver_t unin_chip_driver = { 132 "unin", 133 unin_chip_methods, 134 sizeof(struct unin_chip_softc) 135 }; 136 137 static devclass_t unin_chip_devclass; 138 139 /* 140 * Assume there is only one unin chip in a PowerMac, so that pmu.c functions can 141 * suspend the chip after the whole rest of the device tree is suspended, not 142 * earlier. 143 */ 144 static device_t unin_chip; 145 146 DRIVER_MODULE(unin, ofwbus, unin_chip_driver, unin_chip_devclass, 0, 0); 147 148 /* 149 * Add an interrupt to the dev's resource list if present 150 */ 151 static void 152 unin_chip_add_intr(phandle_t devnode, struct unin_chip_devinfo *dinfo) 153 { 154 phandle_t iparent; 155 int *intr; 156 int i, nintr; 157 int icells; 158 159 if (dinfo->udi_ninterrupts >= 6) { 160 printf("unin: device has more than 6 interrupts\n"); 161 return; 162 } 163 164 nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), 165 (void **)&intr); 166 if (nintr == -1) { 167 nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", 168 sizeof(*intr), (void **)&intr); 169 if (nintr == -1) 170 return; 171 } 172 173 if (intr[0] == -1) 174 return; 175 176 if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) 177 <= 0) 178 panic("Interrupt but no interrupt parent!\n"); 179 180 if (OF_searchprop(iparent, "#interrupt-cells", &icells, sizeof(icells)) 181 <= 0) 182 icells = 1; 183 184 for (i = 0; i < nintr; i+=icells) { 185 u_int irq = MAP_IRQ(iparent, intr[i]); 186 187 resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, 188 dinfo->udi_ninterrupts, irq, irq, 1); 189 190 if (icells > 1) { 191 powerpc_config_intr(irq, 192 (intr[i+1] & 1) ? INTR_TRIGGER_LEVEL : 193 INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); 194 } 195 196 dinfo->udi_interrupts[dinfo->udi_ninterrupts] = irq; 197 dinfo->udi_ninterrupts++; 198 } 199 } 200 201 static void 202 unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo) 203 { 204 struct unin_chip_reg *reg; 205 int i, nreg; 206 207 nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); 208 if (nreg == -1) 209 return; 210 211 for (i = 0; i < nreg; i++) { 212 resource_list_add(&dinfo->udi_resources, SYS_RES_MEMORY, i, 213 reg[i].mr_base, 214 reg[i].mr_base + reg[i].mr_size, 215 reg[i].mr_size); 216 } 217 } 218 219 static void 220 unin_update_reg(device_t dev, uint32_t regoff, uint32_t set, uint32_t clr) 221 { 222 volatile u_int *reg; 223 struct unin_chip_softc *sc; 224 u_int32_t tmpl; 225 226 sc = device_get_softc(dev); 227 reg = (void *)(sc->sc_addr + regoff); 228 tmpl = inl(reg); 229 tmpl &= ~clr; 230 tmpl |= set; 231 outl(reg, tmpl); 232 } 233 234 static void 235 unin_enable_gmac(device_t dev) 236 { 237 unin_update_reg(dev, UNIN_CLOCKCNTL, UNIN_CLOCKCNTL_GMAC, 0); 238 } 239 240 static void 241 unin_enable_mpic(device_t dev) 242 { 243 unin_update_reg(dev, UNIN_TOGGLE_REG, UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE, 0); 244 } 245 246 static int 247 unin_chip_probe(device_t dev) 248 { 249 const char *name; 250 251 name = ofw_bus_get_name(dev); 252 253 if (name == NULL) 254 return (ENXIO); 255 256 if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0 257 && strcmp(name, "u4") != 0) 258 return (ENXIO); 259 260 device_set_desc(dev, "Apple UniNorth System Controller"); 261 return (0); 262 } 263 264 static int 265 unin_chip_attach(device_t dev) 266 { 267 struct unin_chip_softc *sc; 268 struct unin_chip_devinfo *dinfo; 269 phandle_t root; 270 phandle_t child; 271 phandle_t iparent; 272 device_t cdev; 273 cell_t acells, scells; 274 char compat[32]; 275 char name[32]; 276 u_int irq, reg[3]; 277 int error, i = 0; 278 279 sc = device_get_softc(dev); 280 root = ofw_bus_get_node(dev); 281 282 if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) 283 return (ENXIO); 284 285 acells = scells = 1; 286 OF_getprop(OF_parent(root), "#address-cells", &acells, sizeof(acells)); 287 OF_getprop(OF_parent(root), "#size-cells", &scells, sizeof(scells)); 288 289 i = 0; 290 sc->sc_physaddr = reg[i++]; 291 if (acells == 2) { 292 sc->sc_physaddr <<= 32; 293 sc->sc_physaddr |= reg[i++]; 294 } 295 sc->sc_size = reg[i++]; 296 if (scells == 2) { 297 sc->sc_size <<= 32; 298 sc->sc_size |= reg[i++]; 299 } 300 301 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 302 sc->sc_mem_rman.rm_descr = "UniNorth Device Memory"; 303 304 error = rman_init(&sc->sc_mem_rman); 305 306 if (error) { 307 device_printf(dev, "rman_init() failed. error = %d\n", error); 308 return (error); 309 } 310 311 error = rman_manage_region(&sc->sc_mem_rman, sc->sc_physaddr, 312 sc->sc_physaddr + sc->sc_size - 1); 313 if (error) { 314 device_printf(dev, 315 "rman_manage_region() failed. error = %d\n", 316 error); 317 return (error); 318 } 319 320 if (unin_chip == NULL) 321 unin_chip = dev; 322 323 /* 324 * Iterate through the sub-devices 325 */ 326 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 327 dinfo = malloc(sizeof(*dinfo), M_UNIN, M_WAITOK | M_ZERO); 328 if (ofw_bus_gen_setup_devinfo(&dinfo->udi_obdinfo, child) 329 != 0) 330 { 331 free(dinfo, M_UNIN); 332 continue; 333 } 334 335 resource_list_init(&dinfo->udi_resources); 336 dinfo->udi_ninterrupts = 0; 337 unin_chip_add_intr(child, dinfo); 338 339 /* 340 * Some Apple machines do have a bug in OF, they miss 341 * the interrupt entries on the U3 I2C node. That means they 342 * do not have an entry with number of interrupts nor the 343 * entry of the interrupt parent handle. 344 * We define an interrupt and hardwire it to the /u3/mpic 345 * handle. 346 */ 347 348 if (OF_getprop(child, "name", name, sizeof(name)) <= 0) 349 device_printf(dev, "device has no name!\n"); 350 if (dinfo->udi_ninterrupts == 0 && 351 (strcmp(name, "i2c-bus") == 0 || 352 strcmp(name, "i2c") == 0)) { 353 if (OF_getprop(child, "interrupt-parent", &iparent, 354 sizeof(iparent)) <= 0) { 355 iparent = OF_finddevice("/u3/mpic"); 356 device_printf(dev, "Set /u3/mpic as iparent!\n"); 357 } 358 /* Add an interrupt number 0 to the parent. */ 359 irq = MAP_IRQ(iparent, 0); 360 resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, 361 dinfo->udi_ninterrupts, irq, irq, 1); 362 dinfo->udi_interrupts[dinfo->udi_ninterrupts] = irq; 363 dinfo->udi_ninterrupts++; 364 } 365 366 unin_chip_add_reg(child, dinfo); 367 368 cdev = device_add_child(dev, NULL, -1); 369 if (cdev == NULL) { 370 device_printf(dev, "<%s>: device_add_child failed\n", 371 dinfo->udi_obdinfo.obd_name); 372 resource_list_free(&dinfo->udi_resources); 373 ofw_bus_gen_destroy_devinfo(&dinfo->udi_obdinfo); 374 free(dinfo, M_UNIN); 375 continue; 376 } 377 378 device_set_ivars(cdev, dinfo); 379 } 380 381 /* 382 * Only map the first page, since that is where the registers 383 * of interest lie. 384 */ 385 sc->sc_addr = (vm_offset_t)pmap_mapdev(sc->sc_physaddr, PAGE_SIZE); 386 387 sc->sc_version = *(u_int *)sc->sc_addr; 388 device_printf(dev, "Version %d\n", sc->sc_version); 389 390 /* 391 * Enable the GMAC Ethernet cell and the integrated OpenPIC 392 * if Open Firmware says they are used. 393 */ 394 for (child = OF_child(root); child; child = OF_peer(child)) { 395 memset(compat, 0, sizeof(compat)); 396 OF_getprop(child, "compatible", compat, sizeof(compat)); 397 if (strcmp(compat, "gmac") == 0) 398 unin_enable_gmac(dev); 399 if (strcmp(compat, "chrp,open-pic") == 0) 400 unin_enable_mpic(dev); 401 } 402 403 /* 404 * GMAC lives under the PCI bus, so just check if enet is gmac. 405 */ 406 child = OF_finddevice("enet"); 407 memset(compat, 0, sizeof(compat)); 408 OF_getprop(child, "compatible", compat, sizeof(compat)); 409 if (strcmp(compat, "gmac") == 0) 410 unin_enable_gmac(dev); 411 412 return (bus_generic_attach(dev)); 413 } 414 415 static int 416 unin_chip_print_child(device_t dev, device_t child) 417 { 418 struct unin_chip_devinfo *dinfo; 419 struct resource_list *rl; 420 int retval = 0; 421 422 dinfo = device_get_ivars(child); 423 rl = &dinfo->udi_resources; 424 425 retval += bus_print_child_header(dev, child); 426 427 retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 428 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 429 430 retval += bus_print_child_footer(dev, child); 431 432 return (retval); 433 } 434 435 static void 436 unin_chip_probe_nomatch(device_t dev, device_t child) 437 { 438 struct unin_chip_devinfo *dinfo; 439 struct resource_list *rl; 440 const char *type; 441 442 if (bootverbose) { 443 dinfo = device_get_ivars(child); 444 rl = &dinfo->udi_resources; 445 446 if ((type = ofw_bus_get_type(child)) == NULL) 447 type = "(unknown)"; 448 device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); 449 resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 450 resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 451 printf(" (no driver attached)\n"); 452 } 453 } 454 455 456 static struct resource * 457 unin_chip_alloc_resource(device_t bus, device_t child, int type, int *rid, 458 u_long start, u_long end, u_long count, u_int flags) 459 { 460 struct unin_chip_softc *sc; 461 int needactivate; 462 struct resource *rv; 463 struct rman *rm; 464 u_long adjstart, adjend, adjcount; 465 struct unin_chip_devinfo *dinfo; 466 struct resource_list_entry *rle; 467 468 sc = device_get_softc(bus); 469 dinfo = device_get_ivars(child); 470 471 needactivate = flags & RF_ACTIVE; 472 flags &= ~RF_ACTIVE; 473 474 switch (type) { 475 case SYS_RES_MEMORY: 476 case SYS_RES_IOPORT: 477 rle = resource_list_find(&dinfo->udi_resources, SYS_RES_MEMORY, 478 *rid); 479 if (rle == NULL) { 480 device_printf(bus, "no rle for %s memory %d\n", 481 device_get_nameunit(child), *rid); 482 return (NULL); 483 } 484 485 rle->end = rle->end - 1; /* Hack? */ 486 487 if (start < rle->start) 488 adjstart = rle->start; 489 else if (start > rle->end) 490 adjstart = rle->end; 491 else 492 adjstart = start; 493 494 if (end < rle->start) 495 adjend = rle->start; 496 else if (end > rle->end) 497 adjend = rle->end; 498 else 499 adjend = end; 500 501 adjcount = adjend - adjstart; 502 503 rm = &sc->sc_mem_rman; 504 break; 505 506 case SYS_RES_IRQ: 507 /* Check for passthrough from subattachments. */ 508 if (device_get_parent(child) != bus) 509 return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 510 type, rid, start, end, count, 511 flags); 512 513 rle = resource_list_find(&dinfo->udi_resources, SYS_RES_IRQ, 514 *rid); 515 if (rle == NULL) { 516 if (dinfo->udi_ninterrupts >= 6) { 517 device_printf(bus, 518 "%s has more than 6 interrupts\n", 519 device_get_nameunit(child)); 520 return (NULL); 521 } 522 resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, 523 dinfo->udi_ninterrupts, start, start, 524 1); 525 526 dinfo->udi_interrupts[dinfo->udi_ninterrupts] = start; 527 dinfo->udi_ninterrupts++; 528 } 529 530 return (resource_list_alloc(&dinfo->udi_resources, bus, child, 531 type, rid, start, end, count, 532 flags)); 533 default: 534 device_printf(bus, "unknown resource request from %s\n", 535 device_get_nameunit(child)); 536 return (NULL); 537 } 538 539 rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, 540 child); 541 if (rv == NULL) { 542 device_printf(bus, 543 "failed to reserve resource %#lx - %#lx (%#lx)" 544 " for %s\n", adjstart, adjend, adjcount, 545 device_get_nameunit(child)); 546 return (NULL); 547 } 548 549 rman_set_rid(rv, *rid); 550 551 if (needactivate) { 552 if (bus_activate_resource(child, type, *rid, rv) != 0) { 553 device_printf(bus, 554 "failed to activate resource for %s\n", 555 device_get_nameunit(child)); 556 rman_release_resource(rv); 557 return (NULL); 558 } 559 } 560 561 return (rv); 562 } 563 564 static int 565 unin_chip_release_resource(device_t bus, device_t child, int type, int rid, 566 struct resource *res) 567 { 568 if (rman_get_flags(res) & RF_ACTIVE) { 569 int error = bus_deactivate_resource(child, type, rid, res); 570 if (error) 571 return error; 572 } 573 574 return (rman_release_resource(res)); 575 } 576 577 static int 578 unin_chip_activate_resource(device_t bus, device_t child, int type, int rid, 579 struct resource *res) 580 { 581 void *p; 582 583 if (type == SYS_RES_IRQ) 584 return (bus_activate_resource(bus, type, rid, res)); 585 586 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 587 vm_offset_t start; 588 589 start = (vm_offset_t) rman_get_start(res); 590 591 if (bootverbose) 592 printf("unin mapdev: start %zx, len %ld\n", start, 593 rman_get_size(res)); 594 595 p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); 596 if (p == NULL) 597 return (ENOMEM); 598 rman_set_virtual(res, p); 599 rman_set_bustag(res, &bs_be_tag); 600 rman_set_bushandle(res, (u_long)p); 601 } 602 603 return (rman_activate_resource(res)); 604 } 605 606 607 static int 608 unin_chip_deactivate_resource(device_t bus, device_t child, int type, int rid, 609 struct resource *res) 610 { 611 /* 612 * If this is a memory resource, unmap it. 613 */ 614 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 615 u_int32_t psize; 616 617 psize = rman_get_size(res); 618 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 619 } 620 621 return (rman_deactivate_resource(res)); 622 } 623 624 625 static struct resource_list * 626 unin_chip_get_resource_list (device_t dev, device_t child) 627 { 628 struct unin_chip_devinfo *dinfo; 629 630 dinfo = device_get_ivars(child); 631 return (&dinfo->udi_resources); 632 } 633 634 static const struct ofw_bus_devinfo * 635 unin_chip_get_devinfo(device_t dev, device_t child) 636 { 637 struct unin_chip_devinfo *dinfo; 638 639 dinfo = device_get_ivars(child); 640 return (&dinfo->udi_obdinfo); 641 } 642 643 int 644 unin_chip_wake(device_t dev) 645 { 646 647 if (dev == NULL) 648 dev = unin_chip; 649 unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_NORMAL, UNIN_PWR_MASK); 650 DELAY(10); 651 unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_RUNNING, 0); 652 DELAY(100); 653 654 return (0); 655 } 656 657 int 658 unin_chip_sleep(device_t dev, int idle) 659 { 660 if (dev == NULL) 661 dev = unin_chip; 662 663 unin_update_reg(dev, UNIN_HWINIT_STATE, UNIN_SLEEPING, 0); 664 DELAY(10); 665 if (idle) 666 unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_IDLE2, UNIN_PWR_MASK); 667 else 668 unin_update_reg(dev, UNIN_PWR_MGMT, UNIN_PWR_SLEEP, UNIN_PWR_MASK); 669 DELAY(10); 670 671 return (0); 672 } 673