grackle.c revision 209298
138465Smsmith/*- 238465Smsmith * Copyright 2003 by Peter Grehan. All rights reserved. 338465Smsmith * 438465Smsmith * Redistribution and use in source and binary forms, with or without 538465Smsmith * modification, are permitted provided that the following conditions 638465Smsmith * are met: 738465Smsmith * 1. Redistributions of source code must retain the above copyright 838465Smsmith * notice, this list of conditions and the following disclaimer. 938465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1038465Smsmith * notice, this list of conditions and the following disclaimer in the 1138465Smsmith * documentation and/or other materials provided with the distribution. 1238465Smsmith * 3. The name of the author may not be used to endorse or promote products 1338465Smsmith * derived from this software without specific prior written permission. 1438465Smsmith * 1538465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1638465Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1738465Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1838465Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1938465Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2038465Smsmith * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2138465Smsmith * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2238465Smsmith * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2338465Smsmith * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2438465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2538465Smsmith * SUCH DAMAGE. 2640393Speter * 2738465Smsmith * $FreeBSD: head/sys/powerpc/powermac/grackle.c 209298 2010-06-18 14:06:27Z nwhitehorn $ 2838465Smsmith */ 2939902Smsmith 3039902Smsmith#include <sys/param.h> 3138465Smsmith#include <sys/systm.h> 3240146Speter#include <sys/module.h> 3339902Smsmith#include <sys/bus.h> 3438465Smsmith#include <sys/conf.h> 3539902Smsmith#include <sys/kernel.h> 3639902Smsmith 3738465Smsmith#include <dev/ofw/openfirm.h> 3839902Smsmith#include <dev/ofw/ofw_pci.h> 3938465Smsmith#include <dev/ofw/ofw_bus.h> 4038465Smsmith#include <dev/ofw/ofw_bus_subr.h> 4138465Smsmith 4238465Smsmith#include <dev/pci/pcivar.h> 4338465Smsmith#include <dev/pci/pcireg.h> 4438465Smsmith 4538465Smsmith#include <machine/bus.h> 4638465Smsmith#include <machine/intr_machdep.h> 4738465Smsmith#include <machine/md_var.h> 4838465Smsmith#include <machine/pio.h> 4938465Smsmith#include <machine/resource.h> 5038465Smsmith 5138465Smsmith#include <sys/rman.h> 5238465Smsmith 5338465Smsmith#include <powerpc/powermac/gracklevar.h> 5438465Smsmith 5538465Smsmith#include <vm/vm.h> 5638465Smsmith#include <vm/pmap.h> 5738465Smsmith 5838465Smsmith#include "pcib_if.h" 5938465Smsmith 6038465Smsmithint badaddr(void *, size_t); /* XXX */ 6138465Smsmith 6238465Smsmith/* 6338465Smsmith * Device interface. 6438465Smsmith */ 6538465Smsmithstatic int grackle_probe(device_t); 6639178Smsmithstatic int grackle_attach(device_t); 6738465Smsmith 6838465Smsmith/* 6938465Smsmith * Bus interface. 7038465Smsmith */ 7138465Smsmithstatic int grackle_read_ivar(device_t, device_t, int, 7238465Smsmith uintptr_t *); 7338465Smsmithstatic struct resource * grackle_alloc_resource(device_t bus, 7438465Smsmith device_t child, int type, int *rid, u_long start, 7538465Smsmith u_long end, u_long count, u_int flags); 7638465Smsmithstatic int grackle_release_resource(device_t bus, device_t child, 7738465Smsmith int type, int rid, struct resource *res); 7838465Smsmithstatic int grackle_activate_resource(device_t bus, device_t child, 7938465Smsmith int type, int rid, struct resource *res); 8038465Smsmithstatic int grackle_deactivate_resource(device_t bus, 8138465Smsmith device_t child, int type, int rid, 8238465Smsmith struct resource *res); 8338465Smsmith 8438465Smsmith 8538465Smsmith/* 8638465Smsmith * pcib interface. 8738465Smsmith */ 8838465Smsmithstatic int grackle_maxslots(device_t); 8938465Smsmithstatic u_int32_t grackle_read_config(device_t, u_int, u_int, u_int, 9038465Smsmith u_int, int); 9138465Smsmithstatic void grackle_write_config(device_t, u_int, u_int, u_int, 9238465Smsmith u_int, u_int32_t, int); 9338465Smsmithstatic int grackle_route_interrupt(device_t, device_t, int); 9438465Smsmith 9538465Smsmith/* 9638465Smsmith * ofw_bus interface 9738465Smsmith */ 9838465Smsmithstatic phandle_t grackle_get_node(device_t bus, device_t dev); 9938465Smsmith 10038465Smsmith/* 10138465Smsmith * Local routines. 10238465Smsmith */ 10338465Smsmithstatic int grackle_enable_config(struct grackle_softc *, u_int, 10439178Smsmith u_int, u_int, u_int); 10538465Smsmithstatic void grackle_disable_config(struct grackle_softc *); 10638465Smsmith 10739178Smsmith/* 10838465Smsmith * Driver methods. 10938465Smsmith */ 11038465Smsmithstatic device_method_t grackle_methods[] = { 11138465Smsmith /* Device interface */ 11238465Smsmith DEVMETHOD(device_probe, grackle_probe), 11338465Smsmith DEVMETHOD(device_attach, grackle_attach), 11438465Smsmith 11538465Smsmith /* Bus interface */ 11638465Smsmith DEVMETHOD(bus_print_child, bus_generic_print_child), 11738465Smsmith DEVMETHOD(bus_read_ivar, grackle_read_ivar), 11838465Smsmith DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 11938465Smsmith DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 12038465Smsmith DEVMETHOD(bus_alloc_resource, grackle_alloc_resource), 12138465Smsmith DEVMETHOD(bus_release_resource, grackle_release_resource), 12238465Smsmith DEVMETHOD(bus_activate_resource, grackle_activate_resource), 12338465Smsmith DEVMETHOD(bus_deactivate_resource, grackle_deactivate_resource), 12438465Smsmith 12538465Smsmith /* pcib interface */ 12638465Smsmith DEVMETHOD(pcib_maxslots, grackle_maxslots), 12738465Smsmith DEVMETHOD(pcib_read_config, grackle_read_config), 12839441Smsmith DEVMETHOD(pcib_write_config, grackle_write_config), 12938465Smsmith DEVMETHOD(pcib_route_interrupt, grackle_route_interrupt), 13039441Smsmith 13138465Smsmith /* ofw_bus interface */ 13238465Smsmith DEVMETHOD(ofw_bus_get_node, grackle_get_node), 13339441Smsmith 13438465Smsmith { 0, 0 } 13538465Smsmith}; 13639441Smsmith 13738465Smsmithstatic driver_t grackle_driver = { 13838465Smsmith "pcib", 13939441Smsmith grackle_methods, 14038465Smsmith sizeof(struct grackle_softc) 14139730Speter}; 14238465Smsmith 14338465Smsmithstatic devclass_t grackle_devclass; 14438465Smsmith 14538465SmsmithDRIVER_MODULE(grackle, nexus, grackle_driver, grackle_devclass, 0, 0); 14638465Smsmith 14738465Smsmithstatic int 14838465Smsmithgrackle_probe(device_t dev) 14939178Smsmith{ 15038465Smsmith const char *type, *compatible; 15138465Smsmith 15238465Smsmith type = ofw_bus_get_type(dev); 15338465Smsmith compatible = ofw_bus_get_compat(dev); 15438465Smsmith 15538465Smsmith if (type == NULL || compatible == NULL) 15638465Smsmith return (ENXIO); 15738465Smsmith 15838465Smsmith if (strcmp(type, "pci") != 0 || strcmp(compatible, "grackle") != 0) 15940107Smsmith return (ENXIO); 16040107Smsmith 16140107Smsmith device_set_desc(dev, "MPC106 (Grackle) Host-PCI bridge"); 16240107Smsmith return (0); 16338465Smsmith} 16438465Smsmith 16540107Smsmithstatic int 16640107Smsmithgrackle_attach(device_t dev) 16740107Smsmith{ 16840107Smsmith struct grackle_softc *sc; 16940336Speter phandle_t node; 17040107Smsmith u_int32_t busrange[2]; 17140107Smsmith struct grackle_range *rp, *io, *mem[2]; 17238465Smsmith int nmem, i, error; 17338465Smsmith 17438465Smsmith node = ofw_bus_get_node(dev); 17538465Smsmith sc = device_get_softc(dev); 17640107Smsmith 17740107Smsmith if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) 17839441Smsmith return (ENXIO); 17940336Speter 18038465Smsmith sc->sc_dev = dev; 18138465Smsmith sc->sc_node = node; 18238465Smsmith sc->sc_bus = busrange[0]; 18338465Smsmith 18438465Smsmith /* 18540107Smsmith * The Grackle PCI config addr/data registers are actually in 18640107Smsmith * PCI space, but since they are needed to actually probe the 18740107Smsmith * PCI bus, use the fact that they are also available directly 18840107Smsmith * on the processor bus and map them 18940336Speter */ 19038465Smsmith sc->sc_addr = (vm_offset_t)pmap_mapdev(GRACKLE_ADDR, PAGE_SIZE); 19138465Smsmith sc->sc_data = (vm_offset_t)pmap_mapdev(GRACKLE_DATA, PAGE_SIZE); 19239441Smsmith 19340107Smsmith bzero(sc->sc_range, sizeof(sc->sc_range)); 19440107Smsmith sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range, 19539178Smsmith sizeof(sc->sc_range)); 19639178Smsmith 19738465Smsmith if (sc->sc_nrange == -1) { 19838465Smsmith device_printf(dev, "could not get ranges\n"); 19938465Smsmith return (ENXIO); 20038465Smsmith } 20138465Smsmith 20238465Smsmith sc->sc_range[6].pci_hi = 0; 20338465Smsmith io = NULL; 20438465Smsmith nmem = 0; 20540146Speter 20639178Smsmith for (rp = sc->sc_range; rp->pci_hi != 0; rp++) { 20738465Smsmith switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { 20838465Smsmith case OFW_PCI_PHYS_HI_SPACE_CONFIG: 20938465Smsmith break; 21038465Smsmith case OFW_PCI_PHYS_HI_SPACE_IO: 21138764Smsmith io = rp; 21238764Smsmith break; 21338465Smsmith case OFW_PCI_PHYS_HI_SPACE_MEM32: 21439178Smsmith mem[nmem] = rp; 21538465Smsmith nmem++; 21638465Smsmith break; 21739902Smsmith case OFW_PCI_PHYS_HI_SPACE_MEM64: 21839902Smsmith break; 21939902Smsmith } 22039902Smsmith } 22139902Smsmith 22239902Smsmith if (io == NULL) { 22339902Smsmith device_printf(dev, "can't find io range\n"); 22439902Smsmith return (ENXIO); 22539902Smsmith } 22639902Smsmith sc->sc_io_rman.rm_type = RMAN_ARRAY; 22739902Smsmith sc->sc_io_rman.rm_descr = "Grackle PCI I/O Ports"; 22839902Smsmith sc->sc_iostart = io->pci_iospace; 22939902Smsmith if (rman_init(&sc->sc_io_rman) != 0 || 23039902Smsmith rman_manage_region(&sc->sc_io_rman, io->pci_lo, 23139902Smsmith io->pci_lo + io->size_lo) != 0) { 23239902Smsmith panic("grackle_attach: failed to set up I/O rman"); 23339902Smsmith } 23439902Smsmith 23539902Smsmith if (nmem == 0) { 23639902Smsmith device_printf(dev, "can't find mem ranges\n"); 23739989Smsmith return (ENXIO); 23839902Smsmith } 23939902Smsmith sc->sc_mem_rman.rm_type = RMAN_ARRAY; 24039902Smsmith sc->sc_mem_rman.rm_descr = "Grackle PCI Memory"; 24139902Smsmith error = rman_init(&sc->sc_mem_rman); 24239902Smsmith if (error) { 24339902Smsmith device_printf(dev, "rman_init() failed. error = %d\n", error); 24439902Smsmith return (error); 24539902Smsmith } 24639902Smsmith for (i = 0; i < nmem; i++) { 24739902Smsmith error = rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo, 24839902Smsmith mem[i]->pci_lo + mem[i]->size_lo); 24939902Smsmith if (error) { 25039902Smsmith device_printf(dev, 25139902Smsmith "rman_manage_region() failed. error = %d\n", error); 25239902Smsmith return (error); 25339902Smsmith } 25439902Smsmith } 25539902Smsmith 25639902Smsmith ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); 25739902Smsmith 25839902Smsmith device_add_child(dev, "pci", device_get_unit(dev)); 25939902Smsmith return (bus_generic_attach(dev)); 26039902Smsmith} 26139902Smsmith 26239902Smsmithstatic int 26339902Smsmithgrackle_maxslots(device_t dev) 26439902Smsmith{ 26539902Smsmith 26639902Smsmith return (PCI_SLOTMAX); 26739902Smsmith} 26839902Smsmith 26939902Smsmithstatic u_int32_t 27039902Smsmithgrackle_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, 27139902Smsmith int width) 27239902Smsmith{ 27339902Smsmith struct grackle_softc *sc; 27439902Smsmith vm_offset_t caoff; 27539902Smsmith u_int32_t retval = 0xffffffff; 27639902Smsmith 27739902Smsmith sc = device_get_softc(dev); 27839902Smsmith caoff = sc->sc_data + (reg & 0x03); 27939902Smsmith 28040393Speter if (grackle_enable_config(sc, bus, slot, func, reg) != 0) { 28140393Speter 28240393Speter /* 28340393Speter * Config probes to non-existent devices on the 28440393Speter * secondary bus generates machine checks. Be sure 28539902Smsmith * to catch these. 28639902Smsmith */ 28739902Smsmith if (bus > 0) { 28839902Smsmith if (badaddr((void *)sc->sc_data, 4)) { 28939902Smsmith return (retval); 29039902Smsmith } 29139902Smsmith } 29239902Smsmith 29339902Smsmith switch (width) { 29439902Smsmith case 1: 29539902Smsmith retval = (in8rb(caoff)); 29639902Smsmith break; 29739902Smsmith case 2: 29839902Smsmith retval = (in16rb(caoff)); 29939902Smsmith break; 30039902Smsmith case 4: 30139902Smsmith retval = (in32rb(caoff)); 30239902Smsmith break; 30339902Smsmith } 30439902Smsmith } 30539902Smsmith grackle_disable_config(sc); 30639902Smsmith 30739902Smsmith return (retval); 30839902Smsmith} 30939902Smsmith 31039902Smsmithstatic void 31139989Smsmithgrackle_write_config(device_t dev, u_int bus, u_int slot, u_int func, 31239989Smsmith u_int reg, u_int32_t val, int width) 31339989Smsmith{ 31439902Smsmith struct grackle_softc *sc; 31539989Smsmith vm_offset_t caoff; 31639989Smsmith 31739902Smsmith sc = device_get_softc(dev); 31839902Smsmith caoff = sc->sc_data + (reg & 0x03); 31939902Smsmith 32039902Smsmith if (grackle_enable_config(sc, bus, slot, func, reg)) { 321 switch (width) { 322 case 1: 323 out8rb(caoff, val); 324 (void)in8rb(caoff); 325 break; 326 case 2: 327 out16rb(caoff, val); 328 (void)in16rb(caoff); 329 break; 330 case 4: 331 out32rb(caoff, val); 332 (void)in32rb(caoff); 333 break; 334 } 335 } 336 grackle_disable_config(sc); 337} 338 339static int 340grackle_route_interrupt(device_t bus, device_t dev, int pin) 341{ 342 struct grackle_softc *sc; 343 struct ofw_pci_register reg; 344 uint32_t pintr, mintr; 345 phandle_t iparent; 346 uint8_t maskbuf[sizeof(reg) + sizeof(pintr)]; 347 348 sc = device_get_softc(bus); 349 pintr = pin; 350 if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, ®, 351 sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), 352 &iparent, maskbuf)) 353 return (INTR_VEC(iparent, mintr)); 354 355 /* Maybe it's a real interrupt, not an intpin */ 356 if (pin > 4) 357 return (pin); 358 359 device_printf(bus, "could not route pin %d for device %d.%d\n", 360 pin, pci_get_slot(dev), pci_get_function(dev)); 361 return (PCI_INVALID_IRQ); 362} 363 364static int 365grackle_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 366{ 367 struct grackle_softc *sc; 368 369 sc = device_get_softc(dev); 370 371 switch (which) { 372 case PCIB_IVAR_DOMAIN: 373 *result = 0; 374 return (0); 375 case PCIB_IVAR_BUS: 376 *result = sc->sc_bus; 377 return (0); 378 } 379 380 return (ENOENT); 381} 382 383static struct resource * 384grackle_alloc_resource(device_t bus, device_t child, int type, int *rid, 385 u_long start, u_long end, u_long count, u_int flags) 386{ 387 struct grackle_softc *sc; 388 struct resource *rv; 389 struct rman *rm; 390 int needactivate; 391 392 needactivate = flags & RF_ACTIVE; 393 flags &= ~RF_ACTIVE; 394 395 sc = device_get_softc(bus); 396 397 switch (type) { 398 case SYS_RES_MEMORY: 399 rm = &sc->sc_mem_rman; 400 break; 401 402 case SYS_RES_IOPORT: 403 rm = &sc->sc_io_rman; 404 break; 405 406 case SYS_RES_IRQ: 407 return (bus_alloc_resource(bus, type, rid, start, end, count, 408 flags)); 409 410 default: 411 device_printf(bus, "unknown resource request from %s\n", 412 device_get_nameunit(child)); 413 return (NULL); 414 } 415 416 rv = rman_reserve_resource(rm, start, end, count, flags, child); 417 if (rv == NULL) { 418 device_printf(bus, "failed to reserve resource for %s\n", 419 device_get_nameunit(child)); 420 return (NULL); 421 } 422 423 rman_set_rid(rv, *rid); 424 425 if (needactivate) { 426 if (bus_activate_resource(child, type, *rid, rv) != 0) { 427 device_printf(bus, 428 "failed to activate resource for %s\n", 429 device_get_nameunit(child)); 430 rman_release_resource(rv); 431 return (NULL); 432 } 433 } 434 435 return (rv); 436} 437 438static int 439grackle_release_resource(device_t bus, device_t child, int type, int rid, 440 struct resource *res) 441{ 442 if (rman_get_flags(res) & RF_ACTIVE) { 443 int error = bus_deactivate_resource(child, type, rid, res); 444 if (error) 445 return error; 446 } 447 448 return (rman_release_resource(res)); 449} 450 451static int 452grackle_activate_resource(device_t bus, device_t child, int type, int rid, 453 struct resource *res) 454{ 455 struct grackle_softc *sc; 456 void *p; 457 458 sc = device_get_softc(bus); 459 460 if (type == SYS_RES_IRQ) { 461 return (bus_activate_resource(bus, type, rid, res)); 462 } 463 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 464 vm_offset_t start; 465 466 start = (vm_offset_t)rman_get_start(res); 467 /* 468 * For i/o-ports, convert the start address to the 469 * MPC106 PCI i/o window 470 */ 471 if (type == SYS_RES_IOPORT) 472 start += sc->sc_iostart; 473 474 if (bootverbose) 475 printf("grackle mapdev: start %x, len %ld\n", start, 476 rman_get_size(res)); 477 478 p = pmap_mapdev(start, (vm_size_t)rman_get_size(res)); 479 if (p == NULL) 480 return (ENOMEM); 481 482 rman_set_virtual(res, p); 483 rman_set_bustag(res, &bs_le_tag); 484 rman_set_bushandle(res, (u_long)p); 485 } 486 487 return (rman_activate_resource(res)); 488} 489 490static int 491grackle_deactivate_resource(device_t bus, device_t child, int type, int rid, 492 struct resource *res) 493{ 494 /* 495 * If this is a memory resource, unmap it. 496 */ 497 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 498 u_int32_t psize; 499 500 psize = rman_get_size(res); 501 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 502 } 503 504 return (rman_deactivate_resource(res)); 505} 506 507 508static int 509grackle_enable_config(struct grackle_softc *sc, u_int bus, u_int slot, 510 u_int func, u_int reg) 511{ 512 u_int32_t cfgval; 513 514 /* 515 * Unlike UniNorth, the format of the config word is the same 516 * for local (0) and remote busses. 517 */ 518 cfgval = (bus << 16) | (slot << 11) | (func << 8) | (reg & 0xFC) 519 | GRACKLE_CFG_ENABLE; 520 521 out32rb(sc->sc_addr, cfgval); 522 (void) in32rb(sc->sc_addr); 523 524 return (1); 525} 526 527static void 528grackle_disable_config(struct grackle_softc *sc) 529{ 530 /* 531 * Clear the GRACKLE_CFG_ENABLE bit to prevent stray 532 * accesses from causing config cycles 533 */ 534 out32rb(sc->sc_addr, 0); 535} 536 537static phandle_t 538grackle_get_node(device_t bus, device_t dev) 539{ 540 struct grackle_softc *sc; 541 542 sc = device_get_softc(bus); 543 /* We only have one child, the PCI bus, which needs our own node. */ 544 545 return sc->sc_node; 546} 547 548/* 549 * Driver to swallow Grackle host bridges from the PCI bus side. 550 */ 551static int 552grackle_hb_probe(device_t dev) 553{ 554 555 if (pci_get_devid(dev) == 0x00021057) { 556 device_set_desc(dev, "Grackle Host to PCI bridge"); 557 device_quiet(dev); 558 return (0); 559 } 560 561 return (ENXIO); 562} 563 564static int 565grackle_hb_attach(device_t dev) 566{ 567 568 return (0); 569} 570 571static device_method_t grackle_hb_methods[] = { 572 /* Device interface */ 573 DEVMETHOD(device_probe, grackle_hb_probe), 574 DEVMETHOD(device_attach, grackle_hb_attach), 575 576 { 0, 0 } 577}; 578 579static driver_t grackle_hb_driver = { 580 "grackle_hb", 581 grackle_hb_methods, 582 1, 583}; 584static devclass_t grackle_hb_devclass; 585 586DRIVER_MODULE(grackle_hb, pci, grackle_hb_driver, grackle_hb_devclass, 0, 0); 587