ofw_pcibus.c revision 255615
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: head/sys/powerpc/ofw/ofw_pcibus.c 255615 2013-09-16 15:10:11Z nwhitehorn $"); 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 53183882Snwhitehorn#include "pcib_if.h" 54183882Snwhitehorn#include "pci_if.h" 55183882Snwhitehorn 56186128Snwhitehorntypedef uint32_t ofw_pci_intr_t; 57183882Snwhitehorn 58183882Snwhitehorn/* Methods */ 59183882Snwhitehornstatic device_probe_t ofw_pcibus_probe; 60183882Snwhitehornstatic device_attach_t ofw_pcibus_attach; 61183882Snwhitehornstatic pci_assign_interrupt_t ofw_pcibus_assign_interrupt; 62183882Snwhitehornstatic ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo; 63186128Snwhitehornstatic int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, 64186128Snwhitehorn char *buf, size_t buflen); 65183882Snwhitehorn 66186128Snwhitehornstatic void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno); 67186128Snwhitehornstatic void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno); 68186128Snwhitehorn 69183882Snwhitehornstatic device_method_t ofw_pcibus_methods[] = { 70183882Snwhitehorn /* Device interface */ 71183882Snwhitehorn DEVMETHOD(device_probe, ofw_pcibus_probe), 72183882Snwhitehorn DEVMETHOD(device_attach, ofw_pcibus_attach), 73183882Snwhitehorn 74186128Snwhitehorn /* Bus interface */ 75186128Snwhitehorn DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method), 76186128Snwhitehorn 77183882Snwhitehorn /* PCI interface */ 78183882Snwhitehorn DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt), 79183882Snwhitehorn 80183882Snwhitehorn /* ofw_bus interface */ 81183882Snwhitehorn DEVMETHOD(ofw_bus_get_devinfo, ofw_pcibus_get_devinfo), 82183882Snwhitehorn DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 83183882Snwhitehorn DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 84183882Snwhitehorn DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 85183882Snwhitehorn DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 86183882Snwhitehorn DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 87183882Snwhitehorn 88183882Snwhitehorn { 0, 0 } 89183882Snwhitehorn}; 90183882Snwhitehorn 91183882Snwhitehornstruct ofw_pcibus_devinfo { 92183882Snwhitehorn struct pci_devinfo opd_dinfo; 93183882Snwhitehorn struct ofw_bus_devinfo opd_obdinfo; 94183882Snwhitehorn}; 95183882Snwhitehorn 96183882Snwhitehornstatic devclass_t pci_devclass; 97183882Snwhitehorn 98232403SjhbDEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods, 99232403Sjhb sizeof(struct pci_softc), pci_driver); 100183882SnwhitehornDRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0); 101183882SnwhitehornMODULE_VERSION(ofw_pcibus, 1); 102183882SnwhitehornMODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1); 103183882Snwhitehorn 104255615Snwhitehornstatic int ofw_devices_only = 0; 105255615SnwhitehornTUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only); 106255615Snwhitehorn 107183882Snwhitehornstatic int 108183882Snwhitehornofw_pcibus_probe(device_t dev) 109183882Snwhitehorn{ 110186128Snwhitehorn 111233018Snwhitehorn if (ofw_bus_get_node(dev) == -1) 112183882Snwhitehorn return (ENXIO); 113183882Snwhitehorn device_set_desc(dev, "OFW PCI bus"); 114183882Snwhitehorn 115255615Snwhitehorn return (BUS_PROBE_DEFAULT); 116183882Snwhitehorn} 117183882Snwhitehorn 118183882Snwhitehornstatic int 119183882Snwhitehornofw_pcibus_attach(device_t dev) 120183882Snwhitehorn{ 121186128Snwhitehorn u_int busno, domain; 122232403Sjhb int error; 123186128Snwhitehorn 124232403Sjhb error = pci_attach_common(dev); 125232403Sjhb if (error) 126232403Sjhb return (error); 127186128Snwhitehorn domain = pcib_get_domain(dev); 128186128Snwhitehorn busno = pcib_get_bus(dev); 129186128Snwhitehorn 130186128Snwhitehorn /* 131186128Snwhitehorn * Attach those children represented in the device tree. 132186128Snwhitehorn */ 133186128Snwhitehorn 134186128Snwhitehorn ofw_pcibus_enum_devtree(dev, domain, busno); 135186128Snwhitehorn 136186128Snwhitehorn /* 137186128Snwhitehorn * We now attach any laggard devices. FDT, for instance, allows 138186128Snwhitehorn * the device tree to enumerate only some PCI devices. Apple's 139186128Snwhitehorn * OF device tree on some Grackle-based hardware can also miss 140186128Snwhitehorn * functions on multi-function cards. 141186128Snwhitehorn */ 142186128Snwhitehorn 143255615Snwhitehorn if (!ofw_devices_only) 144255615Snwhitehorn ofw_pcibus_enum_bus(dev, domain, busno); 145186128Snwhitehorn 146186128Snwhitehorn return (bus_generic_attach(dev)); 147186128Snwhitehorn} 148186128Snwhitehorn 149186128Snwhitehornstatic void 150186128Snwhitehornofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno) 151186128Snwhitehorn{ 152183882Snwhitehorn device_t pcib; 153183882Snwhitehorn struct ofw_pci_register pcir; 154183882Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 155183882Snwhitehorn phandle_t node, child; 156186128Snwhitehorn u_int func, slot; 157186128Snwhitehorn int intline; 158183882Snwhitehorn 159183882Snwhitehorn pcib = device_get_parent(dev); 160183882Snwhitehorn node = ofw_bus_get_node(dev); 161183882Snwhitehorn 162183882Snwhitehorn for (child = OF_child(node); child != 0; child = OF_peer(child)) { 163183882Snwhitehorn if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) 164183882Snwhitehorn continue; 165183882Snwhitehorn slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); 166183882Snwhitehorn func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); 167183882Snwhitehorn 168183882Snwhitehorn /* Some OFW device trees contain dupes. */ 169183882Snwhitehorn if (pci_find_dbsf(domain, busno, slot, func) != NULL) 170183882Snwhitehorn continue; 171183882Snwhitehorn 172186128Snwhitehorn /* 173186128Snwhitehorn * The preset in the intline register is usually bogus. Reset 174186128Snwhitehorn * it such that the PCI code will reroute the interrupt if 175186128Snwhitehorn * needed. 176186128Snwhitehorn */ 177183882Snwhitehorn 178186128Snwhitehorn intline = PCI_INVALID_IRQ; 179186128Snwhitehorn if (OF_getproplen(child, "interrupts") > 0) 180186128Snwhitehorn intline = 0; 181186128Snwhitehorn PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE, 182186128Snwhitehorn intline, 1); 183186128Snwhitehorn 184186128Snwhitehorn /* 185186128Snwhitehorn * Now set up the PCI and OFW bus layer devinfo and add it 186186128Snwhitehorn * to the PCI bus. 187186128Snwhitehorn */ 188186128Snwhitehorn 189183882Snwhitehorn dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib, 190183882Snwhitehorn domain, busno, slot, func, sizeof(*dinfo)); 191183882Snwhitehorn if (dinfo == NULL) 192183882Snwhitehorn continue; 193183882Snwhitehorn if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 194183882Snwhitehorn 0) { 195183882Snwhitehorn pci_freecfg((struct pci_devinfo *)dinfo); 196183882Snwhitehorn continue; 197183882Snwhitehorn } 198183882Snwhitehorn pci_add_child(dev, (struct pci_devinfo *)dinfo); 199183882Snwhitehorn 200183882Snwhitehorn /* 201208162Snwhitehorn * Some devices don't have an intpin set, but do have 202208162Snwhitehorn * interrupts. These are fully specified, and set in the 203186128Snwhitehorn * interrupts property, so add that value to the device's 204186128Snwhitehorn * resource list. 205208162Snwhitehorn */ 206208162Snwhitehorn if (dinfo->opd_dinfo.cfg.intpin == 0) { 207208162Snwhitehorn ofw_pci_intr_t intr[2]; 208208162Snwhitehorn phandle_t iparent; 209208162Snwhitehorn int icells; 210183882Snwhitehorn 211186128Snwhitehorn if (OF_getprop(child, "interrupts", &intr, 212186128Snwhitehorn sizeof(intr)) > 0) { 213208162Snwhitehorn iparent = 0; 214208162Snwhitehorn icells = 1; 215208162Snwhitehorn OF_getprop(child, "interrupt-parent", &iparent, 216208162Snwhitehorn sizeof(iparent)); 217255596Snwhitehorn if (iparent != 0) { 218255596Snwhitehorn OF_getprop(OF_xref_phandle(iparent), 219255596Snwhitehorn "#interrupt-cells", &icells, 220255596Snwhitehorn sizeof(icells)); 221218184Smarcel intr[0] = MAP_IRQ(iparent, intr[0]); 222255596Snwhitehorn } 223209298Snwhitehorn 224208162Snwhitehorn if (iparent != 0 && icells > 1) { 225208162Snwhitehorn powerpc_config_intr(intr[0], 226208162Snwhitehorn (intr[1] & 1) ? INTR_TRIGGER_LEVEL : 227208162Snwhitehorn INTR_TRIGGER_EDGE, 228209299Snwhitehorn INTR_POLARITY_LOW); 229208162Snwhitehorn } 230209298Snwhitehorn 231208162Snwhitehorn resource_list_add(&dinfo->opd_dinfo.resources, 232208162Snwhitehorn SYS_RES_IRQ, 0, intr[0], intr[0], 1); 233186128Snwhitehorn } 234208162Snwhitehorn } 235183882Snwhitehorn } 236183882Snwhitehorn} 237183882Snwhitehorn 238186128Snwhitehorn/* 239186128Snwhitehorn * The following is an almost exact clone of pci_add_children(), with the 240186128Snwhitehorn * addition that it (a) will not add children that have already been added, 241186128Snwhitehorn * and (b) will set up the OFW devinfo to point to invalid values. This is 242186128Snwhitehorn * to handle non-enumerated PCI children as exist in FDT and on the second 243186128Snwhitehorn * function of the Rage 128 in my Blue & White G3. 244186128Snwhitehorn */ 245183882Snwhitehorn 246186128Snwhitehornstatic void 247186128Snwhitehornofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno) 248183882Snwhitehorn{ 249186128Snwhitehorn device_t pcib; 250183882Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 251186128Snwhitehorn int maxslots; 252186128Snwhitehorn int s, f, pcifunchigh; 253186128Snwhitehorn uint8_t hdrtype; 254183882Snwhitehorn 255186128Snwhitehorn pcib = device_get_parent(dev); 256183882Snwhitehorn 257186128Snwhitehorn maxslots = PCIB_MAXSLOTS(pcib); 258186128Snwhitehorn for (s = 0; s <= maxslots; s++) { 259186128Snwhitehorn pcifunchigh = 0; 260186128Snwhitehorn f = 0; 261186128Snwhitehorn DELAY(1); 262186128Snwhitehorn hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1); 263186128Snwhitehorn if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 264186128Snwhitehorn continue; 265186128Snwhitehorn if (hdrtype & PCIM_MFDEV) 266186128Snwhitehorn pcifunchigh = PCI_FUNCMAX; 267186128Snwhitehorn for (f = 0; f <= pcifunchigh; f++) { 268186128Snwhitehorn /* Filter devices we have already added */ 269186128Snwhitehorn if (pci_find_dbsf(domain, busno, s, f) != NULL) 270186128Snwhitehorn continue; 271183882Snwhitehorn 272186128Snwhitehorn dinfo = (struct ofw_pcibus_devinfo *)pci_read_device( 273186128Snwhitehorn pcib, domain, busno, s, f, sizeof(*dinfo)); 274209299Snwhitehorn if (dinfo == NULL) 275209299Snwhitehorn continue; 276183882Snwhitehorn 277209299Snwhitehorn dinfo->opd_obdinfo.obd_node = -1; 278209299Snwhitehorn 279209299Snwhitehorn dinfo->opd_obdinfo.obd_name = NULL; 280209299Snwhitehorn dinfo->opd_obdinfo.obd_compat = NULL; 281209299Snwhitehorn dinfo->opd_obdinfo.obd_type = NULL; 282209299Snwhitehorn dinfo->opd_obdinfo.obd_model = NULL; 283209299Snwhitehorn 284209299Snwhitehorn /* 285209299Snwhitehorn * For non OFW-devices, don't believe 0 286209299Snwhitehorn * for an interrupt. 287209299Snwhitehorn */ 288209299Snwhitehorn if (dinfo->opd_dinfo.cfg.intline == 0) { 289209299Snwhitehorn dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ; 290209299Snwhitehorn PCIB_WRITE_CONFIG(pcib, busno, s, f, 291209299Snwhitehorn PCIR_INTLINE, PCI_INVALID_IRQ, 1); 292186128Snwhitehorn } 293209299Snwhitehorn 294209299Snwhitehorn pci_add_child(dev, (struct pci_devinfo *)dinfo); 295183882Snwhitehorn } 296183882Snwhitehorn } 297183882Snwhitehorn} 298183882Snwhitehorn 299183882Snwhitehornstatic int 300186128Snwhitehornofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf, 301186128Snwhitehorn size_t buflen) 302183882Snwhitehorn{ 303186128Snwhitehorn pci_child_pnpinfo_str_method(cbdev, child, buf, buflen); 304183882Snwhitehorn 305186128Snwhitehorn if (ofw_bus_get_node(child) != -1) { 306186128Snwhitehorn strlcat(buf, " ", buflen); /* Separate info */ 307186128Snwhitehorn ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen); 308186128Snwhitehorn } 309183882Snwhitehorn 310186128Snwhitehorn return (0); 311183882Snwhitehorn} 312186128Snwhitehorn 313183882Snwhitehornstatic int 314186128Snwhitehornofw_pcibus_assign_interrupt(device_t dev, device_t child) 315183882Snwhitehorn{ 316186128Snwhitehorn ofw_pci_intr_t intr; 317209298Snwhitehorn phandle_t node, iparent; 318186128Snwhitehorn int isz; 319183882Snwhitehorn 320186128Snwhitehorn node = ofw_bus_get_node(child); 321183882Snwhitehorn 322186128Snwhitehorn if (node == -1) { 323186128Snwhitehorn /* Non-firmware enumerated child, use standard routing */ 324186128Snwhitehorn 325183882Snwhitehorn /* 326186128Snwhitehorn * XXX: Right now we don't have anything sensible to do here, 327209298Snwhitehorn * since the ofw_imap stuff relies on nodes having a reg 328209298Snwhitehorn * property. There exist ways around this, so the ePAPR 329186128Snwhitehorn * spec will need to be studied. 330183882Snwhitehorn */ 331183882Snwhitehorn 332209299Snwhitehorn return (PCI_INVALID_IRQ); 333183882Snwhitehorn 334186128Snwhitehorn#ifdef NOTYET 335186128Snwhitehorn intr = pci_get_intpin(child); 336186128Snwhitehorn return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, 337186128Snwhitehorn intr)); 338186128Snwhitehorn#endif 339183882Snwhitehorn } 340186128Snwhitehorn 341183882Snwhitehorn /* 342209298Snwhitehorn * Try to determine the node's interrupt parent so we know which 343209298Snwhitehorn * PIC to use. 344209298Snwhitehorn */ 345209298Snwhitehorn 346209298Snwhitehorn iparent = -1; 347209298Snwhitehorn if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) 348209298Snwhitehorn iparent = -1; 349209298Snwhitehorn 350209298Snwhitehorn /* 351186128Snwhitehorn * Any AAPL,interrupts property gets priority and is 352186128Snwhitehorn * fully specified (i.e. does not need routing) 353183882Snwhitehorn */ 354183882Snwhitehorn 355186128Snwhitehorn isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); 356209298Snwhitehorn if (isz == sizeof(intr)) 357218184Smarcel return ((iparent == -1) ? intr : MAP_IRQ(iparent, intr)); 358183882Snwhitehorn 359186128Snwhitehorn isz = OF_getprop(node, "interrupts", &intr, sizeof(intr)); 360209298Snwhitehorn if (isz == sizeof(intr)) { 361209298Snwhitehorn if (iparent != -1) 362218184Smarcel intr = MAP_IRQ(iparent, intr); 363209298Snwhitehorn } else { 364209298Snwhitehorn /* No property: our best guess is the intpin. */ 365186128Snwhitehorn intr = pci_get_intpin(child); 366183882Snwhitehorn } 367183882Snwhitehorn 368183882Snwhitehorn /* 369186128Snwhitehorn * If we got intr from a property, it may or may not be an intpin. 370186128Snwhitehorn * For on-board devices, it frequently is not, and is completely out 371186128Snwhitehorn * of the valid intpin range. For PCI slots, it hopefully is, 372186128Snwhitehorn * otherwise we will have trouble interfacing with non-OFW buses 373186128Snwhitehorn * such as cardbus. 374186128Snwhitehorn * Since we cannot tell which it is without violating layering, we 375186128Snwhitehorn * will always use the route_interrupt method, and treat exceptions 376186128Snwhitehorn * on the level they become apparent. 377183882Snwhitehorn */ 378186128Snwhitehorn return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr)); 379186128Snwhitehorn} 380183882Snwhitehorn 381186128Snwhitehornstatic const struct ofw_bus_devinfo * 382186128Snwhitehornofw_pcibus_get_devinfo(device_t bus, device_t dev) 383186128Snwhitehorn{ 384186128Snwhitehorn struct ofw_pcibus_devinfo *dinfo; 385183882Snwhitehorn 386186128Snwhitehorn dinfo = device_get_ivars(dev); 387186128Snwhitehorn return (&dinfo->opd_obdinfo); 388183882Snwhitehorn} 389183882Snwhitehorn 390