uninorth.c revision 208149
1169689Skan/*- 2169689Skan * Copyright (C) 2002 Benno Rice. 3169689Skan * All rights reserved. 4169689Skan * 5169689Skan * Redistribution and use in source and binary forms, with or without 6169689Skan * modification, are permitted provided that the following conditions 7169689Skan * are met: 8169689Skan * 1. Redistributions of source code must retain the above copyright 9169689Skan * notice, this list of conditions and the following disclaimer. 10169689Skan * 2. Redistributions in binary form must reproduce the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer in the 12169689Skan * documentation and/or other materials provided with the distribution. 13169689Skan * 14169689Skan * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 15169689Skan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16169689Skan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17169689Skan * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18169689Skan * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19169689Skan * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20169689Skan * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21169689Skan * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 22169689Skan * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23169689Skan * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24169689Skan * 25169689Skan * $FreeBSD: head/sys/powerpc/powermac/uninorth.c 208149 2010-05-16 15:18:25Z nwhitehorn $ 26169689Skan */ 27169689Skan 28169689Skan#include <sys/param.h> 29169689Skan#include <sys/systm.h> 30169689Skan#include <sys/module.h> 31169689Skan#include <sys/bus.h> 32169689Skan#include <sys/conf.h> 33169689Skan#include <sys/kernel.h> 34169689Skan 35169689Skan#include <dev/ofw/openfirm.h> 36169689Skan#include <dev/ofw/ofw_pci.h> 37169689Skan#include <dev/ofw/ofw_bus.h> 38169689Skan#include <dev/ofw/ofw_bus_subr.h> 39169689Skan 40169689Skan#include <dev/pci/pcivar.h> 41169689Skan#include <dev/pci/pcireg.h> 42169689Skan 43169689Skan#include <machine/bus.h> 44169689Skan#include <machine/intr_machdep.h> 45169689Skan#include <machine/md_var.h> 46169689Skan#include <machine/pio.h> 47169689Skan#include <machine/resource.h> 48169689Skan 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 60static MALLOC_DEFINE(M_UNIN, "unin", "unin device information"); 61 62/* 63 * Device interface. 64 */ 65 66static int unin_chip_probe(device_t); 67static int unin_chip_attach(device_t); 68 69/* 70 * Bus interface. 71 */ 72static int unin_chip_print_child(device_t dev, device_t child); 73static void unin_chip_probe_nomatch(device_t, device_t); 74static struct resource *unin_chip_alloc_resource(device_t, device_t, int, int *, 75 u_long, u_long, u_long, u_int); 76static int unin_chip_activate_resource(device_t, device_t, int, int, 77 struct resource *); 78static int unin_chip_deactivate_resource(device_t, device_t, int, int, 79 struct resource *); 80static int unin_chip_release_resource(device_t, device_t, int, int, 81 struct resource *); 82static struct resource_list *unin_chip_get_resource_list (device_t, device_t); 83 84/* 85 * OFW Bus interface 86 */ 87 88static ofw_bus_get_devinfo_t unin_chip_get_devinfo; 89 90/* 91 * Local routines 92 */ 93 94static void unin_enable_gmac(device_t dev); 95static void unin_enable_mpic(device_t dev); 96 97/* 98 * Driver methods. 99 */ 100static 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 /* ofw_bus interface */ 119 DEVMETHOD(ofw_bus_get_devinfo, unin_chip_get_devinfo), 120 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 121 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 122 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 123 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 124 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 125 126 { 0, 0 } 127}; 128 129static driver_t unin_chip_driver = { 130 "unin", 131 unin_chip_methods, 132 sizeof(struct unin_chip_softc) 133}; 134 135static devclass_t unin_chip_devclass; 136 137DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0); 138 139/* 140 * Add an interrupt to the dev's resource list if present 141 */ 142static void 143unin_chip_add_intr(phandle_t devnode, struct unin_chip_devinfo *dinfo) 144{ 145 int *intr; 146 int i, nintr; 147 phandle_t iparent; 148 int icells; 149 150 if (dinfo->udi_ninterrupts >= 6) { 151 printf("unin: device has more than 6 interrupts\n"); 152 return; 153 } 154 155 nintr = OF_getprop_alloc(devnode, "interrupts", sizeof(*intr), 156 (void **)&intr); 157 if (nintr == -1) { 158 nintr = OF_getprop_alloc(devnode, "AAPL,interrupts", 159 sizeof(*intr), (void **)&intr); 160 if (nintr == -1) 161 return; 162 } 163 164 if (intr[0] == -1) 165 return; 166 167 if (OF_getprop(devnode, "interrupt-parent", &iparent, sizeof(iparent)) 168 <= 0) 169 panic("Interrupt but no interrupt parent!\n"); 170 171 if (OF_searchprop(iparent, "#interrupt-cells", &icells, sizeof(icells)) 172 <= 0) 173 icells = 1; 174 175 for (i = 0; i < nintr; i+=icells) { 176 resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, 177 dinfo->udi_ninterrupts, intr[i], intr[i], 1); 178 179 dinfo->udi_interrupts[dinfo->udi_ninterrupts] = intr[i]; 180 dinfo->udi_ninterrupts++; 181 } 182} 183 184static void 185unin_chip_add_reg(phandle_t devnode, struct unin_chip_devinfo *dinfo) 186{ 187 struct unin_chip_reg *reg; 188 int i, nreg; 189 190 nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®); 191 if (nreg == -1) 192 return; 193 194 for (i = 0; i < nreg; i++) { 195 resource_list_add(&dinfo->udi_resources, SYS_RES_MEMORY, i, 196 reg[i].mr_base, 197 reg[i].mr_base + reg[i].mr_size, 198 reg[i].mr_size); 199 } 200} 201 202static void 203unin_enable_gmac(device_t dev) 204{ 205 volatile u_int *clkreg; 206 struct unin_chip_softc *sc; 207 u_int32_t tmpl; 208 209 sc = device_get_softc(dev); 210 clkreg = (void *)(sc->sc_addr + UNIN_CLOCKCNTL); 211 tmpl = inl(clkreg); 212 tmpl |= UNIN_CLOCKCNTL_GMAC; 213 outl(clkreg, tmpl); 214} 215 216static void 217unin_enable_mpic(device_t dev) 218{ 219 volatile u_int *toggle; 220 struct unin_chip_softc *sc; 221 u_int32_t tmpl; 222 223 sc = device_get_softc(dev); 224 toggle = (void *)(sc->sc_addr + UNIN_TOGGLE_REG); 225 tmpl = inl(toggle); 226 tmpl |= UNIN_MPIC_RESET | UNIN_MPIC_OUTPUT_ENABLE; 227 outl(toggle, tmpl); 228} 229 230static int 231unin_chip_probe(device_t dev) 232{ 233 const char *name; 234 235 name = ofw_bus_get_name(dev); 236 237 if (name == NULL) 238 return (ENXIO); 239 240 if (strcmp(name, "uni-n") != 0 && strcmp(name, "u3") != 0 241 && strcmp(name, "u4") != 0) 242 return (ENXIO); 243 244 device_set_desc(dev, "Apple UniNorth System Controller"); 245 return (0); 246} 247 248static int 249unin_chip_attach(device_t dev) 250{ 251 struct unin_chip_softc *sc; 252 struct unin_chip_devinfo *dinfo; 253 phandle_t root; 254 phandle_t child; 255 device_t cdev; 256 char compat[32]; 257 u_int reg[3]; 258 int error, i = 0; 259 260 sc = device_get_softc(dev); 261 root = ofw_bus_get_node(dev); 262 263 if (OF_getprop(root, "reg", reg, sizeof(reg)) < 8) 264 return (ENXIO); 265 266 if (strcmp(ofw_bus_get_name(dev), "u3") == 0 267 || strcmp(ofw_bus_get_name(dev), "u4") == 0) 268 i = 1; /* #address-cells lies */ 269 270 sc->sc_physaddr = reg[i]; 271 sc->sc_size = reg[i+1]; 272 273 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 274 sc->sc_mem_rman.rm_descr = "UniNorth Device Memory"; 275 276 error = rman_init(&sc->sc_mem_rman); 277 278 if (error) { 279 device_printf(dev, "rman_init() failed. error = %d\n", error); 280 return (error); 281 } 282 283 error = rman_manage_region(&sc->sc_mem_rman, sc->sc_physaddr, 284 sc->sc_physaddr + sc->sc_size - 1); 285 if (error) { 286 device_printf(dev, 287 "rman_manage_region() failed. error = %d\n", 288 error); 289 return (error); 290 } 291 292 /* 293 * Iterate through the sub-devices 294 */ 295 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 296 dinfo = malloc(sizeof(*dinfo), M_UNIN, M_WAITOK | M_ZERO); 297 if (ofw_bus_gen_setup_devinfo(&dinfo->udi_obdinfo, child) 298 != 0) 299 { 300 free(dinfo, M_UNIN); 301 continue; 302 } 303 304 resource_list_init(&dinfo->udi_resources); 305 dinfo->udi_ninterrupts = 0; 306 unin_chip_add_intr(child, dinfo); 307 308 unin_chip_add_reg(child, dinfo); 309 310 cdev = device_add_child(dev, NULL, -1); 311 if (cdev == NULL) { 312 device_printf(dev, "<%s>: device_add_child failed\n", 313 dinfo->udi_obdinfo.obd_name); 314 resource_list_free(&dinfo->udi_resources); 315 ofw_bus_gen_destroy_devinfo(&dinfo->udi_obdinfo); 316 free(dinfo, M_UNIN); 317 continue; 318 } 319 320 device_set_ivars(cdev, dinfo); 321 } 322 323 /* 324 * Only map the first page, since that is where the registers 325 * of interest lie. 326 */ 327 sc->sc_addr = (vm_offset_t)pmap_mapdev(sc->sc_physaddr, PAGE_SIZE); 328 329 sc->sc_version = *(u_int *)sc->sc_addr; 330 device_printf(dev, "Version %d\n", sc->sc_version); 331 332 /* 333 * Enable the GMAC Ethernet cell and the integrated OpenPIC 334 * if Open Firmware says they are used. 335 */ 336 for (child = OF_child(root); child; child = OF_peer(child)) { 337 memset(compat, 0, sizeof(compat)); 338 OF_getprop(child, "compatible", compat, sizeof(compat)); 339 if (strcmp(compat, "gmac") == 0) 340 unin_enable_gmac(dev); 341 if (strcmp(compat, "chrp,open-pic") == 0) 342 unin_enable_mpic(dev); 343 } 344 345 /* 346 * GMAC lives under the PCI bus, so just check if enet is gmac. 347 */ 348 child = OF_finddevice("enet"); 349 memset(compat, 0, sizeof(compat)); 350 OF_getprop(child, "compatible", compat, sizeof(compat)); 351 if (strcmp(compat, "gmac") == 0) 352 unin_enable_gmac(dev); 353 354 return (bus_generic_attach(dev)); 355} 356 357static int 358unin_chip_print_child(device_t dev, device_t child) 359{ 360 struct unin_chip_devinfo *dinfo; 361 struct resource_list *rl; 362 int retval = 0; 363 364 dinfo = device_get_ivars(child); 365 rl = &dinfo->udi_resources; 366 367 retval += bus_print_child_header(dev, child); 368 369 retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 370 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 371 372 retval += bus_print_child_footer(dev, child); 373 374 return (retval); 375} 376 377static void 378unin_chip_probe_nomatch(device_t dev, device_t child) 379{ 380 struct unin_chip_devinfo *dinfo; 381 struct resource_list *rl; 382 const char *type; 383 384 if (bootverbose) { 385 dinfo = device_get_ivars(child); 386 rl = &dinfo->udi_resources; 387 388 if ((type = ofw_bus_get_type(child)) == NULL) 389 type = "(unknown)"; 390 device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); 391 resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 392 resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 393 printf(" (no driver attached)\n"); 394 } 395} 396 397 398static struct resource * 399unin_chip_alloc_resource(device_t bus, device_t child, int type, int *rid, 400 u_long start, u_long end, u_long count, u_int flags) 401{ 402 struct unin_chip_softc *sc; 403 int needactivate; 404 struct resource *rv; 405 struct rman *rm; 406 u_long adjstart, adjend, adjcount; 407 struct unin_chip_devinfo *dinfo; 408 struct resource_list_entry *rle; 409 410 sc = device_get_softc(bus); 411 dinfo = device_get_ivars(child); 412 413 needactivate = flags & RF_ACTIVE; 414 flags &= ~RF_ACTIVE; 415 416 switch (type) { 417 case SYS_RES_MEMORY: 418 case SYS_RES_IOPORT: 419 rle = resource_list_find(&dinfo->udi_resources, SYS_RES_MEMORY, 420 *rid); 421 if (rle == NULL) { 422 device_printf(bus, "no rle for %s memory %d\n", 423 device_get_nameunit(child), *rid); 424 return (NULL); 425 } 426 427 rle->end = rle->end - 1; /* Hack? */ 428 429 if (start < rle->start) 430 adjstart = rle->start; 431 else if (start > rle->end) 432 adjstart = rle->end; 433 else 434 adjstart = start; 435 436 if (end < rle->start) 437 adjend = rle->start; 438 else if (end > rle->end) 439 adjend = rle->end; 440 else 441 adjend = end; 442 443 adjcount = adjend - adjstart; 444 445 rm = &sc->sc_mem_rman; 446 break; 447 448 case SYS_RES_IRQ: 449 /* Check for passthrough from subattachments. */ 450 if (device_get_parent(child) != bus) 451 return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 452 type, rid, start, end, count, 453 flags); 454 455 rle = resource_list_find(&dinfo->udi_resources, SYS_RES_IRQ, 456 *rid); 457 if (rle == NULL) { 458 if (dinfo->udi_ninterrupts >= 6) { 459 device_printf(bus, 460 "%s has more than 6 interrupts\n", 461 device_get_nameunit(child)); 462 return (NULL); 463 } 464 resource_list_add(&dinfo->udi_resources, SYS_RES_IRQ, 465 dinfo->udi_ninterrupts, start, start, 466 1); 467 468 dinfo->udi_interrupts[dinfo->udi_ninterrupts] = start; 469 dinfo->udi_ninterrupts++; 470 } 471 472 return (resource_list_alloc(&dinfo->udi_resources, bus, child, 473 type, rid, start, end, count, 474 flags)); 475 default: 476 device_printf(bus, "unknown resource request from %s\n", 477 device_get_nameunit(child)); 478 return (NULL); 479 } 480 481 rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags, 482 child); 483 if (rv == NULL) { 484 device_printf(bus, 485 "failed to reserve resource %#lx - %#lx (%#lx)" 486 " for %s\n", adjstart, adjend, adjcount, 487 device_get_nameunit(child)); 488 return (NULL); 489 } 490 491 rman_set_rid(rv, *rid); 492 493 if (needactivate) { 494 if (bus_activate_resource(child, type, *rid, rv) != 0) { 495 device_printf(bus, 496 "failed to activate resource for %s\n", 497 device_get_nameunit(child)); 498 rman_release_resource(rv); 499 return (NULL); 500 } 501 } 502 503 return (rv); 504} 505 506static int 507unin_chip_release_resource(device_t bus, device_t child, int type, int rid, 508 struct resource *res) 509{ 510 if (rman_get_flags(res) & RF_ACTIVE) { 511 int error = bus_deactivate_resource(child, type, rid, res); 512 if (error) 513 return error; 514 } 515 516 return (rman_release_resource(res)); 517} 518 519static int 520unin_chip_activate_resource(device_t bus, device_t child, int type, int rid, 521 struct resource *res) 522{ 523 struct unin_chip_softc *sc; 524 void *p; 525 526 sc = device_get_softc(bus); 527 528 if (type == SYS_RES_IRQ) 529 return (bus_activate_resource(bus, type, rid, res)); 530 531 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 532 vm_offset_t start; 533 534 start = (vm_offset_t) rman_get_start(res); 535 536 if (bootverbose) 537 printf("unin mapdev: start %zx, len %ld\n", start, 538 rman_get_size(res)); 539 540 p = pmap_mapdev(start, (vm_size_t) rman_get_size(res)); 541 if (p == NULL) 542 return (ENOMEM); 543 rman_set_virtual(res, p); 544 rman_set_bustag(res, &bs_be_tag); 545 rman_set_bushandle(res, (u_long)p); 546 } 547 548 return (rman_activate_resource(res)); 549} 550 551 552static int 553unin_chip_deactivate_resource(device_t bus, device_t child, int type, int rid, 554 struct resource *res) 555{ 556 /* 557 * If this is a memory resource, unmap it. 558 */ 559 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 560 u_int32_t psize; 561 562 psize = rman_get_size(res); 563 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 564 } 565 566 return (rman_deactivate_resource(res)); 567} 568 569 570static struct resource_list * 571unin_chip_get_resource_list (device_t dev, device_t child) 572{ 573 struct unin_chip_devinfo *dinfo; 574 575 dinfo = device_get_ivars(child); 576 return (&dinfo->udi_resources); 577} 578 579static const struct ofw_bus_devinfo * 580unin_chip_get_devinfo(device_t dev, device_t child) 581{ 582 struct unin_chip_devinfo *dinfo; 583 584 dinfo = device_get_ivars(child); 585 return (&dinfo->udi_obdinfo); 586} 587 588