1183882Snwhitehorn/*- 2183882Snwhitehorn * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3183882Snwhitehorn * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4183882Snwhitehorn * Copyright (c) 2000, BSDi 5183882Snwhitehorn * Copyright (c) 2003, Thomas Moestl <tmm@FreeBSD.org> 6183882Snwhitehorn * All rights reserved. 7183882Snwhitehorn * 8183882Snwhitehorn * Redistribution and use in source and binary forms, with or without 9183882Snwhitehorn * modification, are permitted provided that the following conditions 10183882Snwhitehorn * are met: 11183882Snwhitehorn * 1. Redistributions of source code must retain the above copyright 12183882Snwhitehorn * notice unmodified, this list of conditions, and the following 13183882Snwhitehorn * disclaimer. 14183882Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 15183882Snwhitehorn * notice, this list of conditions and the following disclaimer in the 16183882Snwhitehorn * documentation and/or other materials provided with the distribution. 17183882Snwhitehorn * 18183882Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19183882Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20183882Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21183882Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22183882Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23183882Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24183882Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25183882Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26183882Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27183882Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28183882Snwhitehorn */ 29183882Snwhitehorn 30183882Snwhitehorn#include <sys/cdefs.h> 31183882Snwhitehorn__FBSDID("$FreeBSD$"); 32183882Snwhitehorn 33183882Snwhitehorn#include <sys/param.h> 34183882Snwhitehorn#include <sys/bus.h> 35183882Snwhitehorn#include <sys/kernel.h> 36183882Snwhitehorn#include <sys/libkern.h> 37183882Snwhitehorn#include <sys/module.h> 38183882Snwhitehorn#include <sys/pciio.h> 39183882Snwhitehorn 40183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 41183882Snwhitehorn#include <dev/ofw/ofw_bus_subr.h> 42183882Snwhitehorn#include <dev/ofw/ofw_pci.h> 43183882Snwhitehorn#include <dev/ofw/openfirm.h> 44183882Snwhitehorn 45183882Snwhitehorn#include <machine/bus.h> 46208162Snwhitehorn#include <machine/intr_machdep.h> 47183882Snwhitehorn#include <machine/resource.h> 48183882Snwhitehorn 49183882Snwhitehorn#include <dev/pci/pcireg.h> 50183882Snwhitehorn#include <dev/pci/pcivar.h> 51183882Snwhitehorn#include <dev/pci/pci_private.h> 52183882Snwhitehorn 53259257Sandreast#include "ofw_pcibus.h" 54183882Snwhitehorn#include "pcib_if.h" 55183882Snwhitehorn#include "pci_if.h" 56183882Snwhitehorn 57186128Snwhitehorntypedef uint32_t ofw_pci_intr_t; 58183882Snwhitehorn 59183882Snwhitehorn/* Methods */ 60183882Snwhitehornstatic device_probe_t ofw_pcibus_probe; 61183882Snwhitehornstatic device_attach_t ofw_pcibus_attach; 62183882Snwhitehornstatic pci_assign_interrupt_t ofw_pcibus_assign_interrupt; 63183882Snwhitehornstatic ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo; 64186128Snwhitehornstatic int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, 65186128Snwhitehorn char *buf, size_t buflen); 66183882Snwhitehorn 67186128Snwhitehornstatic void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno); 68186128Snwhitehornstatic void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno); 69186128Snwhitehorn 70183882Snwhitehornstatic device_method_t ofw_pcibus_methods[] = { 71183882Snwhitehorn /* Device interface */ 72183882Snwhitehorn DEVMETHOD(device_probe, ofw_pcibus_probe), 73183882Snwhitehorn DEVMETHOD(device_attach, ofw_pcibus_attach), 74183882Snwhitehorn 75186128Snwhitehorn /* Bus interface */ 76186128Snwhitehorn DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), 77186128Snwhitehorn 78183882Snwhitehorn /* PCI interface */ 79183882Snwhitehorn DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt), 80183882Snwhitehorn 81183882Snwhitehorn /* ofw_bus interface */ 82183882Snwhitehorn DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo), 83183882Snwhitehorn DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 84183882Snwhitehorn DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 85183882Snwhitehorn DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 86183882Snwhitehorn DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 87183882Snwhitehorn DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 88183882Snwhitehorn 89259257Sandreast DEVMETHOD_END 90183882Snwhitehorn}; 91183882Snwhitehorn 92183882Snwhitehornstatic devclass_t pci_devclass; 93183882Snwhitehorn 94232403SjhbDEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods, 95232403Sjhb sizeof(struct pci_softc), pci_driver); 96183882SnwhitehornDRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); 97183882SnwhitehornMODULE_VERSION(ofw_pcibus, 1); 98183882SnwhitehornMODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); 99183882Snwhitehorn 100255615Snwhitehornstatic int ofw_devices_only = 0; 101255615SnwhitehornTUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); 102255615Snwhitehorn 103183882Snwhitehornstatic int 104183882Snwhitehornofw_pcibus_probe(device_t dev) 105183882Snwhitehorn{ 106186128Snwhitehorn 107233018Snwhitehorn if (ofw_bus_get_node(dev) == -1) 108183882Snwhitehorn return (ENXIO); 109183882Snwhitehorn device_set_desc(dev, "OFW PCI bus"); 110183882Snwhitehorn 111255615Snwhitehorn return (BUS_PROBE_DEFAULT); 112183882Snwhitehorn} 113183882Snwhitehorn 114183882Snwhitehornstatic int 115183882Snwhitehornofw_pcibus_attach(device_t dev) 116183882Snwhitehorn{ 117186128Snwhitehorn u_int busno, domain; 118232403Sjhb int error; 119186128Snwhitehorn 120232403Sjhb error = pci_attach_common(dev); 121232403Sjhb if (error) 122232403Sjhb return (error); 123186128Snwhitehorn domain = pcib_get_domain(dev); 124186128Snwhitehorn busno = pcib_get_bus(dev); 125186128Snwhitehorn 126186128Snwhitehorn /* 127186128Snwhitehorn * Attach those children represented in the device tree. 128186128Snwhitehorn */ 129186128Snwhitehorn 130186128Snwhitehorn ofw_pcibus_enum_devtree(dev, domain, busno); 131186128Snwhitehorn 132186128Snwhitehorn /* 133186128Snwhitehorn * We now attach any laggard devices. FDT, for instance, allows 134186128Snwhitehorn * the device tree to enumerate only some PCI devices. Apple's 135186128Snwhitehorn * OF device tree on some Grackle-based hardware can also miss 136186128Snwhitehorn * functions on multi-function cards. 137186128Snwhitehorn */ 138186128Snwhitehorn 139255615Snwhitehorn if (!ofw_devices_only) 140255615Snwhitehorn ofw_pcibus_enum_bus(dev, domain, busno); 141186128Snwhitehorn 142186128Snwhitehorn return (bus_generic_attach(dev)); 143186128Snwhitehorn} 144186128Snwhitehorn 145186128Snwhitehornstatic void 146186128Snwhitehornofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) 147186128Snwhitehorn{ 148183882Snwhitehorn device_t pcib; 149183882Snwhitehorn struct ofw_pci_register pcir; 150183882Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 151183882Snwhitehorn phandle_t node, child; 152186128Snwhitehorn u_int func, slot; 153186128Snwhitehorn int intline; 154183882Snwhitehorn 155183882Snwhitehorn pcib = device_get_parent(dev); 156183882Snwhitehorn node = ofw_bus_get_node(dev); 157183882Snwhitehorn 158183882Snwhitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 159183882Snwhitehorn if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) 160183882Snwhitehorn continue; 161183882Snwhitehorn slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); 162183882Snwhitehorn func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); 163183882Snwhitehorn 164183882Snwhitehorn /* Some OFW device trees contain dupes. */ 165183882Snwhitehorn if (pci_find_dbsf(domain, busno, slot, func) != NULL) 166183882Snwhitehorn continue; 167183882Snwhitehorn 168186128Snwhitehorn /* 169186128Snwhitehorn * The preset in the intline register is usually bogus. Reset 170186128Snwhitehorn * it such that the PCI code will reroute the interrupt if 171186128Snwhitehorn * needed. 172186128Snwhitehorn */ 173183882Snwhitehorn 174186128Snwhitehorn intline = PCI_INVALID_IRQ; 175186128Snwhitehorn if (OF_getproplen(child, "interrupts") > 0) 176186128Snwhitehorn intline = 0; 177186128Snwhitehorn PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE, 178186128Snwhitehorn intline, 1); 179186128Snwhitehorn 180186128Snwhitehorn /* 181186128Snwhitehorn * Now set up the PCI and OFW bus layer devinfo and add it 182186128Snwhitehorn * to the PCI bus. 183186128Snwhitehorn */ 184186128Snwhitehorn 185183882Snwhitehorn dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, 186183882Snwhitehorn domain, busno, slot, func, sizeof(*dinfo)); 187183882Snwhitehorn if (dinfo == NULL) 188183882Snwhitehorn continue; 189183882Snwhitehorn if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 190183882Snwhitehorn 0) { 191183882Snwhitehorn pci_freecfg((struct pci_devinfo *)dinfo); 192183882Snwhitehorn continue; 193183882Snwhitehorn } 194259257Sandreast dinfo->opd_dma_tag = NULL; 195183882Snwhitehorn pci_add_child(dev, (struct pci_devinfo *)dinfo); 196183882Snwhitehorn 197183882Snwhitehorn /* 198208162Snwhitehorn * Some devices don't have an intpin set, but do have 199208162Snwhitehorn * interrupts. These are fully specified, and set in the 200186128Snwhitehorn * interrupts property, so add that value to the device's 201186128Snwhitehorn * resource list. 202208162Snwhitehorn */ 203208162Snwhitehorn if (dinfo->opd_dinfo.cfg.intpin == 0) { 204208162Snwhitehorn ofw_pci_intr_t intr[2]; 205208162Snwhitehorn phandle_t iparent; 206208162Snwhitehorn int icells; 207183882Snwhitehorn 208186128Snwhitehorn if (OF_getprop(child, "interrupts", &intr, 209186128Snwhitehorn sizeof(intr)) > 0) { 210208162Snwhitehorn iparent = 0; 211208162Snwhitehorn icells = 1; 212208162Snwhitehorn OF_getprop(child, "interrupt-parent", &iparent, 213208162Snwhitehorn sizeof(iparent)); 214255596Snwhitehorn if (iparent != 0) { 215255596Snwhitehorn OF_getprop(OF_xref_phandle(iparent), 216255596Snwhitehorn "#interrupt-cells", &icells, 217255596Snwhitehorn sizeof(icells)); 218265969Sian intr[0] = ofw_bus_map_intr(dev, iparent, 219266128Sian icells, intr); 220255596Snwhitehorn } 221209298Snwhitehorn 222208162Snwhitehorn resource_list_add(&dinfo->opd_dinfo.resources, 223208162Snwhitehorn SYS_RES_IRQ, 0, intr[0], intr[0], 1); 224186128Snwhitehorn } 225208162Snwhitehorn } 226183882Snwhitehorn } 227183882Snwhitehorn} 228183882Snwhitehorn 229186128Snwhitehorn/* 230186128Snwhitehorn * The following is an almost exact clone of pci_add_children(), with the 231186128Snwhitehorn * addition that it (a) will not add children that have already been added, 232186128Snwhitehorn * and (b) will set up the OFW devinfo to point to invalid values. This is 233186128Snwhitehorn * to handle non-enumerated PCI children as exist in FDT and on the second 234186128Snwhitehorn * function of the Rage 128 in my Blue & White G3. 235186128Snwhitehorn */ 236183882Snwhitehorn 237186128Snwhitehornstatic void 238186128Snwhitehornofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno) 239183882Snwhitehorn{ 240186128Snwhitehorn device_t pcib; 241183882Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 242186128Snwhitehorn int maxslots; 243186128Snwhitehorn int s, f, pcifunchigh; 244186128Snwhitehorn uint8_t hdrtype; 245183882Snwhitehorn 246186128Snwhitehorn pcib = device_get_parent(dev); 247183882Snwhitehorn 248186128Snwhitehorn maxslots = PCIB_MAXSLOTS(pcib); 249186128Snwhitehorn for (s = 0; s <= maxslots; s++) { 250186128Snwhitehorn pcifunchigh = 0; 251186128Snwhitehorn f = 0; 252186128Snwhitehorn DELAY(1); 253186128Snwhitehorn hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); 254186128Snwhitehorn if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 255186128Snwhitehorn continue; 256186128Snwhitehorn if (hdrtype & PCIM_MFDEV) 257186128Snwhitehorn pcifunchigh = PCI_FUNCMAX; 258186128Snwhitehorn for (f = 0; f <= pcifunchigh; f++) { 259186128Snwhitehorn /* Filter devices we have already added */ 260186128Snwhitehorn if (pci_find_dbsf(domain, busno, s, f) != NULL) 261186128Snwhitehorn continue; 262183882Snwhitehorn 263186128Snwhitehorn dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( 264186128Snwhitehorn pcib, domain, busno, s, f, sizeof(*dinfo)); 265209299Snwhitehorn if (dinfo == NULL) 266209299Snwhitehorn continue; 267183882Snwhitehorn 268259257Sandreast dinfo->opd_dma_tag = NULL; 269209299Snwhitehorn dinfo->opd_obdinfo.obd_node = -1; 270209299Snwhitehorn 271209299Snwhitehorn dinfo->opd_obdinfo.obd_name = NULL; 272209299Snwhitehorn dinfo->opd_obdinfo.obd_compat = NULL; 273209299Snwhitehorn dinfo->opd_obdinfo.obd_type = NULL; 274209299Snwhitehorn dinfo->opd_obdinfo.obd_model = NULL; 275209299Snwhitehorn 276209299Snwhitehorn /* 277209299Snwhitehorn * For non OFW-devices, don't believe 0 278209299Snwhitehorn * for an interrupt. 279209299Snwhitehorn */ 280209299Snwhitehorn if (dinfo->opd_dinfo.cfg.intline == 0) { 281209299Snwhitehorn dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ; 282209299Snwhitehorn PCIB_WRITE_CONFIG(pcib, busno, s, f, 283209299Snwhitehorn PCIR_INTLINE, PCI_INVALID_IRQ, 1); 284186128Snwhitehorn } 285209299Snwhitehorn 286209299Snwhitehorn pci_add_child(dev, (struct pci_devinfo *)dinfo); 287183882Snwhitehorn } 288183882Snwhitehorn } 289183882Snwhitehorn} 290183882Snwhitehorn 291183882Snwhitehornstatic int 292186128Snwhitehornofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, 293186128Snwhitehorn size_t buflen) 294183882Snwhitehorn{ 295186128Snwhitehorn pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); 296183882Snwhitehorn 297186128Snwhitehorn if (ofw_bus_get_node(child) != -1) { 298186128Snwhitehorn strlcat(buf, " ", buflen); /* Separate info */ 299186128Snwhitehorn ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); 300186128Snwhitehorn } 301183882Snwhitehorn 302186128Snwhitehorn return (0); 303183882Snwhitehorn} 304186128Snwhitehorn 305183882Snwhitehornstatic int 306186128Snwhitehornofw_pcibus_assign_interrupt(device_t dev, device_t child) 307183882Snwhitehorn{ 308266128Sian ofw_pci_intr_t intr[2]; 309209298Snwhitehorn phandle_t node, iparent; 310266128Sian int isz, icells; 311183882Snwhitehorn 312186128Snwhitehorn node = ofw_bus_get_node(child); 313183882Snwhitehorn 314186128Snwhitehorn if (node == -1) { 315186128Snwhitehorn /* Non-firmware enumerated child, use standard routing */ 316186128Snwhitehorn 317266128Sian intr[0] = pci_get_intpin(child); 318186128Snwhitehorn return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, 319266128Sian intr[0])); 320183882Snwhitehorn } 321186128Snwhitehorn 322183882Snwhitehorn /* 323209298Snwhitehorn * Try to determine the node's interrupt parent so we know which 324209298Snwhitehorn * PIC to use. 325209298Snwhitehorn */ 326209298Snwhitehorn 327209298Snwhitehorn iparent = -1; 328209298Snwhitehorn if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) 329209298Snwhitehorn iparent = -1; 330266128Sian icells = 1; 331266128Sian if (iparent != -1) 332266128Sian OF_getprop(OF_xref_phandle(iparent), "#interrupt-cells", 333266128Sian &icells, sizeof(icells)); 334209298Snwhitehorn 335209298Snwhitehorn /* 336186128Snwhitehorn * Any AAPL,interrupts property gets priority and is 337186128Snwhitehorn * fully specified (i.e. does not need routing) 338183882Snwhitehorn */ 339183882Snwhitehorn 340266128Sian isz = OF_getprop(node, "AAPL,interrupts", intr, sizeof(intr)); 341266128Sian if (isz == sizeof(intr[0])*icells) 342266128Sian return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev, 343266128Sian iparent, icells, intr)); 344183882Snwhitehorn 345266128Sian isz = OF_getprop(node, "interrupts", intr, sizeof(intr)); 346266128Sian if (isz == sizeof(intr[0])*icells) { 347209298Snwhitehorn if (iparent != -1) 348266128Sian intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr); 349209298Snwhitehorn } else { 350209298Snwhitehorn /* No property: our best guess is the intpin. */ 351266128Sian intr[0] = pci_get_intpin(child); 352183882Snwhitehorn } 353183882Snwhitehorn 354183882Snwhitehorn /* 355186128Snwhitehorn * If we got intr from a property, it may or may not be an intpin. 356186128Snwhitehorn * For on-board devices, it frequently is not, and is completely out 357186128Snwhitehorn * of the valid intpin range. For PCI slots, it hopefully is, 358186128Snwhitehorn * otherwise we will have trouble interfacing with non-OFW buses 359186128Snwhitehorn * such as cardbus. 360186128Snwhitehorn * Since we cannot tell which it is without violating layering, we 361186128Snwhitehorn * will always use the route_interrupt method, and treat exceptions 362186128Snwhitehorn * on the level they become apparent. 363183882Snwhitehorn */ 364266128Sian return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0])); 365186128Snwhitehorn} 366183882Snwhitehorn 367186128Snwhitehornstatic const struct ofw_bus_devinfo * 368186128Snwhitehornofw_pcibus_get_devinfo(device_t bus, device_t dev) 369186128Snwhitehorn{ 370186128Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 371183882Snwhitehorn 372186128Snwhitehorn dinfo = device_get_ivars(dev); 373186128Snwhitehorn return (&dinfo->opd_obdinfo); 374183882Snwhitehorn} 375183882Snwhitehorn 376