Mercurial > illumos > illumos-gate
changeset 11245:28613b254aad
6831378 identify device type during early boot
line wrap: on
line diff
--- a/usr/src/pkgdefs/SUNWcakr.u/prototype_com Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/pkgdefs/SUNWcakr.u/prototype_com Fri Dec 04 12:16:40 2009 +0800 @@ -488,6 +488,7 @@ l none platform/sun4u/kernel/misc/sparcv9/md5=../../../kernel/crypto/sparcv9/md5 f none platform/sun4u/kernel/misc/sparcv9/obpsym 755 root sys f none platform/sun4u/kernel/misc/sparcv9/opl_cfg 755 root sys +f none platform/sun4u/kernel/misc/sparcv9/pcie 755 root sys f none platform/sun4u/kernel/misc/sparcv9/platmod 755 root sys f none platform/sun4u/kernel/misc/sparcv9/sbd 755 root sys l none platform/sun4u/kernel/misc/sparcv9/sha1=../../../kernel/crypto/sparcv9/sha1
--- a/usr/src/pkgdefs/SUNWcakr.v/prototype_com Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/pkgdefs/SUNWcakr.v/prototype_com Fri Dec 04 12:16:40 2009 +0800 @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -85,6 +83,7 @@ f none platform/sun4v/kernel/misc/sparcv9/bootdev 755 root sys f none platform/sun4v/kernel/misc/sparcv9/forthdebug 755 root sys f none platform/sun4v/kernel/misc/sparcv9/obpsym 755 root sys +f none platform/sun4v/kernel/misc/sparcv9/pcie 755 root sys f none platform/sun4v/kernel/misc/sparcv9/platmod 755 root sys f none platform/sun4v/kernel/misc/sparcv9/vis 755 root sys d none platform/sun4v/kernel/sparcv9 755 root sys
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc Fri Dec 04 12:16:40 2009 +0800 @@ -208,7 +208,6 @@ l none kernel/misc/sparcv9/md5=../../../kernel/crypto/sparcv9/md5 f none kernel/misc/sparcv9/mii 755 root sys f none kernel/misc/sparcv9/neti 755 root sys -f none kernel/misc/sparcv9/pcie 755 root sys f none kernel/misc/sparcv9/pcihp 755 root sys f none kernel/misc/sparcv9/pcmcia 755 root sys f none kernel/misc/sparcv9/rpcsec 755 root sys
--- a/usr/src/tools/scripts/bfu.sh Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/tools/scripts/bfu.sh Fri Dec 04 12:16:40 2009 +0800 @@ -7242,6 +7242,16 @@ rm -f $usr/sbin/hotplug # + # Remove old pcie misc module. Also remove new pcie modules for + # backward bfu. + # + if [ $target_isa = sparc ]; then + rm -f $root/kernel/misc/sparcv9/pcie + rm -f $root/platform/sun4u/kernel/misc/sparcv9/pcie + rm -f $root/platform/sun4v/kernel/misc/sparcv9/pcie + fi + + # # Remove the IPsec encryption and authentication modules. # IPsec now uses the Kernel Crypto Framework for crypto. #
--- a/usr/src/uts/common/io/pciex/hotplug/pcishpc.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/common/io/pciex/hotplug/pcishpc.c Fri Dec 04 12:16:40 2009 +0800 @@ -950,11 +950,13 @@ /* * Deallocate the slot state structures for this controller. */ + PCIE_SET_HP_CTRL(dip, NULL); + bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE; + (void) pcishpc_destroy_slots(ctrl_p); cv_destroy(&ctrl_p->hc_cmd_comp_cv); mutex_destroy(&ctrl_p->hc_mutex); kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t)); - bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE; PCIE_DBG("pcishpc_destroy_controller() success\n"); return (DDI_SUCCESS);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/pciex/pci_cfgacc.c Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,120 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/pci_cfgacc.h> + +#define PCI_CFGACC_FILLREQ(r, d, b, o, s, w, v) \ + {(r).rcdip = (d); (r).bdf = (b); (r).offset = (o); \ + (r).size = (s); (r).write = w; (r).ioacc = B_FALSE; \ + VAL64(&(r)) = (v); } + +/* + * Common interfaces for accessing pci config space + */ + +/* + * This pointer should be initialized before using, here doesn't check it. + * For x86: + * initialized at the end of pci_check(); + * For Sparc: + * initialized in the px_attach(). + */ +void (*pci_cfgacc_acc_p)(pci_cfgacc_req_t *req); + +uint8_t +pci_cfgacc_get8(dev_info_t *rcdip, uint16_t bdf, uint16_t off) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 1, B_FALSE, 0); + (*pci_cfgacc_acc_p)(&req); + return (VAL8(&req)); +} + +void +pci_cfgacc_put8(dev_info_t *rcdip, uint16_t bdf, uint16_t off, uint8_t data) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 1, B_TRUE, data); + (*pci_cfgacc_acc_p)(&req); +} + +uint16_t +pci_cfgacc_get16(dev_info_t *rcdip, uint16_t bdf, uint16_t off) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 2, B_FALSE, 0); + (*pci_cfgacc_acc_p)(&req); + return (VAL16(&req)); +} + +void +pci_cfgacc_put16(dev_info_t *rcdip, uint16_t bdf, uint16_t off, uint16_t data) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 2, B_TRUE, data); + (*pci_cfgacc_acc_p)(&req); +} + +uint32_t +pci_cfgacc_get32(dev_info_t *rcdip, uint16_t bdf, uint16_t off) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 4, B_FALSE, 0); + (*pci_cfgacc_acc_p)(&req); + return (VAL32(&req)); +} + +void +pci_cfgacc_put32(dev_info_t *rcdip, uint16_t bdf, uint16_t off, uint32_t data) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 4, B_TRUE, data); + (*pci_cfgacc_acc_p)(&req); +} + +uint64_t +pci_cfgacc_get64(dev_info_t *rcdip, uint16_t bdf, uint16_t off) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 8, B_FALSE, 0); + (*pci_cfgacc_acc_p)(&req); + return (VAL64(&req)); +} + +void +pci_cfgacc_put64(dev_info_t *rcdip, uint16_t bdf, uint16_t off, uint64_t data) +{ + pci_cfgacc_req_t req; + + PCI_CFGACC_FILLREQ(req, rcdip, bdf, off, 8, B_TRUE, data); + (*pci_cfgacc_acc_p)(&req); +}
--- a/usr/src/uts/common/io/pciex/pcie.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/common/io/pciex/pcie.c Fri Dec 04 12:16:40 2009 +0800 @@ -41,6 +41,7 @@ #include <sys/pcie_impl.h> #include <sys/hotplug/pci/pcie_hp.h> #include <sys/hotplug/pci/pcicfg.h> +#include <sys/pci_cfgacc.h> /* Local functions prototypes */ static void pcie_init_pfd(dev_info_t *); @@ -144,6 +145,8 @@ caddr_t *addrp, ddi_acc_handle_t *handlep); static void pcie_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph); +dev_info_t *pcie_get_rc_dip(dev_info_t *dip); + /* * modload support */ @@ -389,6 +392,35 @@ return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp)); } +int +pcie_init_cfghdl(dev_info_t *cdip) +{ + pcie_bus_t *bus_p; + ddi_acc_handle_t eh = NULL; + + bus_p = PCIE_DIP2BUS(cdip); + if (bus_p == NULL) + return (DDI_FAILURE); + + /* Create an config access special to error handling */ + if (pci_config_setup(cdip, &eh) != DDI_SUCCESS) { + cmn_err(CE_WARN, "Cannot setup config access" + " for BDF 0x%x\n", bus_p->bus_bdf); + return (DDI_FAILURE); + } + + bus_p->bus_cfg_hdl = eh; + return (DDI_SUCCESS); +} + +void +pcie_fini_cfghdl(dev_info_t *cdip) +{ + pcie_bus_t *bus_p = PCIE_DIP2BUS(cdip); + + pci_config_teardown(&bus_p->bus_cfg_hdl); +} + /* * PCI-Express child device initialization. * This function enables generic pci-express interrupts and error @@ -413,6 +445,9 @@ return (DDI_FAILURE); } + if (pcie_init_cfghdl(cdip) != DDI_SUCCESS) + return (DDI_FAILURE); + /* Clear the device's status register */ reg16 = PCIE_GET(16, bus_p, PCI_CONF_STAT); PCIE_PUT(16, bus_p, PCI_CONF_STAT, reg16); @@ -509,8 +544,10 @@ bus_p->bus_ari = B_TRUE; } - if (pcie_initchild_mps(cdip) == DDI_FAILURE) + if (pcie_initchild_mps(cdip) == DDI_FAILURE) { + pcie_fini_cfghdl(cdip); return (DDI_FAILURE); + } return (DDI_SUCCESS); } @@ -702,6 +739,37 @@ kmem_free(PCIE_ROOT_FAULT(pfd_p), sizeof (pf_root_fault_t)); } +/* + * init pcie_bus_t for root complex + * + * Only a few of the fields in bus_t is valid for root complex. + * The fields that are bracketed are initialized in this routine: + * + * dev_info_t * <bus_dip> + * dev_info_t * bus_rp_dip + * ddi_acc_handle_t bus_cfg_hdl + * uint_t <bus_fm_flags> + * pcie_req_id_t bus_bdf + * pcie_req_id_t bus_rp_bdf + * uint32_t bus_dev_ven_id + * uint8_t bus_rev_id + * uint8_t <bus_hdr_type> + * uint16_t <bus_dev_type> + * uint8_t bus_bdg_secbus + * uint16_t bus_pcie_off + * uint16_t <bus_aer_off> + * uint16_t bus_pcix_off + * uint16_t bus_ecc_ver + * pci_bus_range_t bus_bus_range + * ppb_ranges_t * bus_addr_ranges + * int bus_addr_entries + * pci_regspec_t * bus_assigned_addr + * int bus_assigned_entries + * pf_data_t * bus_pfd + * int bus_mps + * uint64_t bus_cfgacc_base + * void * bus_plat_private + */ void pcie_rc_init_bus(dev_info_t *dip) { @@ -724,140 +792,228 @@ void pcie_rc_fini_bus(dev_info_t *dip) { - pcie_bus_t *bus_p = (pcie_bus_t *)ndi_get_bus_private(dip, B_FALSE); - + pcie_bus_t *bus_p = PCIE_DIP2DOWNBUS(dip); ndi_set_bus_private(dip, B_FALSE, NULL, NULL); kmem_free(bus_p, sizeof (pcie_bus_t)); } /* - * Initialize PCIe Bus Private Data + * partially init pcie_bus_t for device (dip,bdf) for accessing pci + * config space + * + * This routine is invoked during boot, either after creating a devinfo node + * (x86 case) or during px driver attach (sparc case); it is also invoked + * in hotplug context after a devinfo node is created. + * + * The fields that are bracketed are initialized if flag PCIE_BUS_INITIAL + * is set: * - * PCIe Bus Private Data contains commonly used PCI/PCIe information and offsets - * to key registers. + * dev_info_t * <bus_dip> + * dev_info_t * <bus_rp_dip> + * ddi_acc_handle_t bus_cfg_hdl + * uint_t bus_fm_flags + * pcie_req_id_t <bus_bdf> + * pcie_req_id_t <bus_rp_bdf> + * uint32_t <bus_dev_ven_id> + * uint8_t <bus_rev_id> + * uint8_t <bus_hdr_type> + * uint16_t <bus_dev_type> + * uint8_t <bus_bdg_secbus + * uint16_t <bus_pcie_off> + * uint16_t <bus_aer_off> + * uint16_t <bus_pcix_off> + * uint16_t <bus_ecc_ver> + * pci_bus_range_t bus_bus_range + * ppb_ranges_t * bus_addr_ranges + * int bus_addr_entries + * pci_regspec_t * bus_assigned_addr + * int bus_assigned_entries + * pf_data_t * bus_pfd + * int bus_mps + * uint64_t bus_cfgacc_base + * void * bus_plat_private + * + * The fields that are bracketed are initialized if flag PCIE_BUS_FINAL + * is set: + * + * dev_info_t * bus_dip + * dev_info_t * bus_rp_dip + * ddi_acc_handle_t bus_cfg_hdl + * uint_t bus_fm_flags + * pcie_req_id_t bus_bdf + * pcie_req_id_t bus_rp_bdf + * uint32_t bus_dev_ven_id + * uint8_t bus_rev_id + * uint8_t bus_hdr_type + * uint16_t bus_dev_type + * uint8_t <bus_bdg_secbus> + * uint16_t bus_pcie_off + * uint16_t bus_aer_off + * uint16_t bus_pcix_off + * uint16_t bus_ecc_ver + * pci_bus_range_t <bus_bus_range> + * ppb_ranges_t * <bus_addr_ranges> + * int <bus_addr_entries> + * pci_regspec_t * <bus_assigned_addr> + * int <bus_assigned_entries> + * pf_data_t * <bus_pfd> + * int bus_mps + * uint64_t bus_cfgacc_base + * void * <bus_plat_private> */ + pcie_bus_t * -pcie_init_bus(dev_info_t *cdip) +pcie_init_bus(dev_info_t *dip, pcie_req_id_t bdf, uint8_t flags) { - pcie_bus_t *bus_p = 0; - ddi_acc_handle_t eh = NULL; - int range_size; - dev_info_t *pdip; - const char *errstr = NULL; + uint16_t status, base, baseptr, num_cap; + uint32_t capid; + int range_size; + pcie_bus_t *bus_p; + dev_info_t *rcdip; + dev_info_t *pdip; + const char *errstr = NULL; - ASSERT(PCIE_DIP2UPBUS(cdip) == NULL); + if (!(flags & PCIE_BUS_INITIAL)) + goto initial_done; - /* allocate memory for pcie bus data */ bus_p = kmem_zalloc(sizeof (pcie_bus_t), KM_SLEEP); - /* Set back pointer to dip */ - bus_p->bus_dip = cdip; + bus_p->bus_dip = dip; + bus_p->bus_bdf = bdf; - /* Create an config access special to error handling */ - if (pci_config_setup(cdip, &eh) != DDI_SUCCESS) { - errstr = "Cannot setup config access"; - goto fail; - } - - bus_p->bus_cfg_hdl = eh; - bus_p->bus_fm_flags = 0; - bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED; + rcdip = pcie_get_rc_dip(dip); + ASSERT(rcdip != NULL); - /* get device's bus/dev/function number */ - if (pcie_get_bdf_from_dip(cdip, &bus_p->bus_bdf) != DDI_SUCCESS) { - errstr = "Cannot get device BDF"; - goto fail; - } - - /* Save the Vendor Id Device Id */ - bus_p->bus_dev_ven_id = PCIE_GET(32, bus_p, PCI_CONF_VENID); - bus_p->bus_rev_id = PCIE_GET(8, bus_p, PCI_CONF_REVID); - + /* Save the Vendor ID, Device ID and revision ID */ + bus_p->bus_dev_ven_id = pci_cfgacc_get32(rcdip, bdf, PCI_CONF_VENID); + bus_p->bus_rev_id = pci_cfgacc_get8(rcdip, bdf, PCI_CONF_REVID); /* Save the Header Type */ - bus_p->bus_hdr_type = PCIE_GET(8, bus_p, PCI_CONF_HEADER); + bus_p->bus_hdr_type = pci_cfgacc_get8(rcdip, bdf, PCI_CONF_HEADER); bus_p->bus_hdr_type &= PCI_HEADER_TYPE_M; - /* Figure out the device type and all the relavant capability offsets */ - if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCI_E, &bus_p->bus_pcie_off)) - != DDI_FAILURE) { - bus_p->bus_dev_type = PCI_CAP_GET16(eh, NULL, - bus_p->bus_pcie_off, PCIE_PCIECAP) & - PCIE_PCIECAP_DEV_TYPE_MASK; + /* + * Figure out the device type and all the relavant capability offsets + */ + /* set default value */ + bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO; - if (PCI_CAP_LOCATE(eh, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_AER), - &bus_p->bus_aer_off) != DDI_SUCCESS) - bus_p->bus_aer_off = NULL; + status = pci_cfgacc_get16(rcdip, bdf, PCI_CONF_STAT); + if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP)) + goto caps_done; /* capability not supported */ + + /* Relevant conventional capabilities first */ + + /* Conventional caps: PCI_CAP_ID_PCI_E, PCI_CAP_ID_PCIX */ + num_cap = 2; - /* Check and save PCIe hotplug capability information */ - if ((PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p)) && - (PCI_CAP_GET16(eh, NULL, bus_p->bus_pcie_off, PCIE_PCIECAP) - & PCIE_PCIECAP_SLOT_IMPL) && - (PCI_CAP_GET32(eh, NULL, bus_p->bus_pcie_off, PCIE_SLOTCAP) - & PCIE_SLOTCAP_HP_CAPABLE)) - bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE; - } else { - bus_p->bus_pcie_off = NULL; - bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO; - } - - if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCIX, &bus_p->bus_pcix_off)) - != DDI_FAILURE) { - if (PCIE_IS_BDG(bus_p)) - bus_p->bus_ecc_ver = PCIX_CAP_GET(16, bus_p, - PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK; - else - bus_p->bus_ecc_ver = PCIX_CAP_GET(16, bus_p, - PCI_PCIX_COMMAND) & PCI_PCIX_VER_MASK; - } else { - bus_p->bus_pcix_off = NULL; - bus_p->bus_ecc_ver = NULL; + switch (bus_p->bus_hdr_type) { + case PCI_HEADER_ZERO: + baseptr = PCI_CONF_CAP_PTR; + break; + case PCI_HEADER_PPB: + baseptr = PCI_BCNF_CAP_PTR; + break; + case PCI_HEADER_CARDBUS: + baseptr = PCI_CBUS_CAP_PTR; + break; + default: + cmn_err(CE_WARN, "%s: unexpected pci header type:%x", + __func__, bus_p->bus_hdr_type); + goto caps_done; } - /* Save the Range information if device is a switch/bridge */ - if (PCIE_IS_BDG(bus_p)) { - /* Check and save PCI hotplug (SHPC) capability information */ - if ((PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCI_HOTPLUG, - &bus_p->bus_pci_hp_off)) == DDI_SUCCESS) - bus_p->bus_hp_sup_modes |= PCIE_PCI_HP_MODE; + base = baseptr; + for (base = pci_cfgacc_get8(rcdip, bdf, base); base && num_cap; + base = pci_cfgacc_get8(rcdip, bdf, base + PCI_CAP_NEXT_PTR)) { + capid = pci_cfgacc_get8(rcdip, bdf, base); + switch (capid) { + case PCI_CAP_ID_PCI_E: + bus_p->bus_pcie_off = base; + bus_p->bus_dev_type = pci_cfgacc_get16(rcdip, bdf, + base + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK; + + /* Check and save PCIe hotplug capability information */ + if ((PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p)) && + (pci_cfgacc_get16(rcdip, bdf, base + PCIE_PCIECAP) + & PCIE_PCIECAP_SLOT_IMPL) && + (pci_cfgacc_get32(rcdip, bdf, base + PCIE_SLOTCAP) + & PCIE_SLOTCAP_HP_CAPABLE)) + bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE; - /* get "bus_range" property */ - range_size = sizeof (pci_bus_range_t); - if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, - "bus-range", (caddr_t)&bus_p->bus_bus_range, &range_size) - != DDI_PROP_SUCCESS) { - errstr = "Cannot find \"bus-range\" property"; - goto fail; + num_cap--; + break; + case PCI_CAP_ID_PCIX: + bus_p->bus_pcix_off = base; + if (PCIE_IS_BDG(bus_p)) + bus_p->bus_ecc_ver = + pci_cfgacc_get16(rcdip, bdf, base + + PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK; + else + bus_p->bus_ecc_ver = + pci_cfgacc_get16(rcdip, bdf, base + + PCI_PCIX_COMMAND) & PCI_PCIX_VER_MASK; + num_cap--; + break; + default: + break; } - - /* get secondary bus number */ - bus_p->bus_bdg_secbus = PCIE_GET(8, bus_p, PCI_BCNF_SECBUS); - - /* Get "ranges" property */ - if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, - "ranges", (caddr_t)&bus_p->bus_addr_ranges, - &bus_p->bus_addr_entries) != DDI_PROP_SUCCESS) - bus_p->bus_addr_entries = 0; - bus_p->bus_addr_entries /= sizeof (ppb_ranges_t); } - /* save "assigned-addresses" property array, ignore failues */ - if (ddi_getlongprop(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, - "assigned-addresses", (caddr_t)&bus_p->bus_assigned_addr, - &bus_p->bus_assigned_entries) == DDI_PROP_SUCCESS) - bus_p->bus_assigned_entries /= sizeof (pci_regspec_t); - else - bus_p->bus_assigned_entries = 0; + /* Check and save PCI hotplug (SHPC) capability information */ + if (PCIE_IS_BDG(bus_p)) { + base = baseptr; + for (base = pci_cfgacc_get8(rcdip, bdf, base); + base; base = pci_cfgacc_get8(rcdip, bdf, + base + PCI_CAP_NEXT_PTR)) { + capid = pci_cfgacc_get8(rcdip, bdf, base); + if (capid == PCI_CAP_ID_PCI_HOTPLUG) { + bus_p->bus_pci_hp_off = base; + bus_p->bus_hp_sup_modes |= PCIE_PCI_HP_MODE; + break; + } + } + } + + /* Then, relevant extended capabilities */ + if (!PCIE_IS_PCIE(bus_p)) + goto caps_done; + + /* Extended caps: PCIE_EXT_CAP_ID_AER */ + for (base = PCIE_EXT_CAP; base; base = (capid >> + PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) { + capid = pci_cfgacc_get32(rcdip, bdf, base); + if (capid == PCI_CAP_EINVAL32) + break; + if (((capid >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) + == PCIE_EXT_CAP_ID_AER) { + bus_p->bus_aer_off = base; + break; + } + } + +caps_done: /* save RP dip and RP bdf */ if (PCIE_IS_RP(bus_p)) { - bus_p->bus_rp_dip = cdip; + bus_p->bus_rp_dip = dip; bus_p->bus_rp_bdf = bus_p->bus_bdf; } else { - for (pdip = ddi_get_parent(cdip); pdip; + for (pdip = ddi_get_parent(dip); pdip; pdip = ddi_get_parent(pdip)) { pcie_bus_t *parent_bus_p = PCIE_DIP2BUS(pdip); /* + * If RP dip and RP bdf in parent's bus_t have + * been initialized, simply use these instead of + * continuing up to the RC. + */ + if (parent_bus_p->bus_rp_dip != NULL) { + bus_p->bus_rp_dip = parent_bus_p->bus_rp_dip; + bus_p->bus_rp_bdf = parent_bus_p->bus_rp_bdf; + break; + } + + /* * When debugging be aware that some NVIDIA x86 * architectures have 2 nodes for each RP, One at Bus * 0x0 and one at Bus 0x80. The requester is from Bus @@ -871,33 +1027,110 @@ } } - ndi_set_bus_private(cdip, B_TRUE, DEVI_PORT_TYPE_PCI, (void *)bus_p); + bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED; + bus_p->bus_fm_flags = 0; + bus_p->bus_mps = 0; - if (PCIE_IS_HOTPLUG_CAPABLE(cdip)) - (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip, + ndi_set_bus_private(dip, B_TRUE, DEVI_PORT_TYPE_PCI, (void *)bus_p); + + if (PCIE_IS_HOTPLUG_CAPABLE(dip)) + (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, "hotplug-capable"); - pcie_init_pfd(cdip); +initial_done: + if (!(flags & PCIE_BUS_FINAL)) + goto final_done; + + /* already initialized? */ + bus_p = PCIE_DIP2BUS(dip); + + /* Save the Range information if device is a switch/bridge */ + if (PCIE_IS_BDG(bus_p)) { + /* get "bus_range" property */ + range_size = sizeof (pci_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&bus_p->bus_bus_range, &range_size) + != DDI_PROP_SUCCESS) { + errstr = "Cannot find \"bus-range\" property"; + cmn_err(CE_WARN, + "PCIE init err info failed BDF 0x%x:%s\n", + bus_p->bus_bdf, errstr); + } + + /* get secondary bus number */ + rcdip = pcie_get_rc_dip(dip); + ASSERT(rcdip != NULL); - bus_p->bus_mps = 0; + bus_p->bus_bdg_secbus = pci_cfgacc_get8(rcdip, + bus_p->bus_bdf, PCI_BCNF_SECBUS); + + /* Get "ranges" property */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "ranges", (caddr_t)&bus_p->bus_addr_ranges, + &bus_p->bus_addr_entries) != DDI_PROP_SUCCESS) + bus_p->bus_addr_entries = 0; + bus_p->bus_addr_entries /= sizeof (ppb_ranges_t); + } - pcie_init_plat(cdip); + /* save "assigned-addresses" property array, ignore failues */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "assigned-addresses", (caddr_t)&bus_p->bus_assigned_addr, + &bus_p->bus_assigned_entries) == DDI_PROP_SUCCESS) + bus_p->bus_assigned_entries /= sizeof (pci_regspec_t); + else + bus_p->bus_assigned_entries = 0; + + pcie_init_pfd(dip); + + pcie_init_plat(dip); + +final_done: PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n", - ddi_driver_name(cdip), (void *)cdip, bus_p->bus_bdf, + ddi_driver_name(dip), (void *)dip, bus_p->bus_bdf, bus_p->bus_bdg_secbus); #ifdef DEBUG pcie_print_bus(bus_p); #endif return (bus_p); -fail: - cmn_err(CE_WARN, "PCIE init err info failed BDF 0x%x:%s\n", - bus_p->bus_bdf, errstr); - if (eh) - pci_config_teardown(&eh); - kmem_free(bus_p, sizeof (pcie_bus_t)); - return (NULL); +} + +/* + * Invoked before destroying devinfo node, mostly during hotplug + * operation to free pcie_bus_t data structure + */ +/* ARGSUSED */ +void +pcie_fini_bus(dev_info_t *dip, uint8_t flags) +{ + pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); + ASSERT(bus_p); + + if (flags & PCIE_BUS_INITIAL) { + pcie_fini_plat(dip); + pcie_fini_pfd(dip); + + kmem_free(bus_p->bus_assigned_addr, + (sizeof (pci_regspec_t) * bus_p->bus_assigned_entries)); + kmem_free(bus_p->bus_addr_ranges, + (sizeof (ppb_ranges_t) * bus_p->bus_addr_entries)); + /* zero out the fields that have been destroyed */ + bus_p->bus_assigned_addr = NULL; + bus_p->bus_addr_ranges = NULL; + bus_p->bus_assigned_entries = 0; + bus_p->bus_addr_entries = 0; + } + + if (flags & PCIE_BUS_FINAL) { + if (PCIE_IS_HOTPLUG_CAPABLE(dip)) { + (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, + "hotplug-capable"); + } + + ndi_set_bus_private(dip, B_TRUE, NULL, NULL); + kmem_free(bus_p, sizeof (pcie_bus_t)); + } } int @@ -920,31 +1153,109 @@ pcie_uninitchild(dev_info_t *cdip) { pcie_disable_errors(cdip); - pcie_fini_bus(cdip); + pcie_fini_cfghdl(cdip); +} + +/* + * find the root complex dip + */ +dev_info_t * +pcie_get_rc_dip(dev_info_t *dip) +{ + dev_info_t *rcdip; + pcie_bus_t *rc_bus_p; + + for (rcdip = ddi_get_parent(dip); rcdip; + rcdip = ddi_get_parent(rcdip)) { + rc_bus_p = PCIE_DIP2BUS(rcdip); + if (rc_bus_p && PCIE_IS_RC(rc_bus_p)) + break; + } + + return (rcdip); +} + +static boolean_t +pcie_is_pci_device(dev_info_t *dip) +{ + dev_info_t *pdip; + char *device_type; + + pdip = ddi_get_parent(dip); + ASSERT(pdip); + + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS, + "device_type", &device_type) != DDI_PROP_SUCCESS) + return (B_FALSE); + + if (strcmp(device_type, "pciex") != 0 && + strcmp(device_type, "pci") != 0) { + ddi_prop_free(device_type); + return (B_FALSE); + } + + ddi_prop_free(device_type); + return (B_TRUE); +} + +typedef struct { + boolean_t init; + uint8_t flags; +} pcie_bus_arg_t; + +/*ARGSUSED*/ +static int +pcie_fab_do_init_fini(dev_info_t *dip, void *arg) +{ + pcie_req_id_t bdf; + pcie_bus_arg_t *bus_arg = (pcie_bus_arg_t *)arg; + + if (!pcie_is_pci_device(dip)) + goto out; + + if (bus_arg->init) { + if (pcie_get_bdf_from_dip(dip, &bdf) != DDI_SUCCESS) + goto out; + + (void) pcie_init_bus(dip, bdf, bus_arg->flags); + } else { + (void) pcie_fini_bus(dip, bus_arg->flags); + } + + return (DDI_WALK_CONTINUE); + +out: + return (DDI_WALK_PRUNECHILD); } void -pcie_fini_bus(dev_info_t *cdip) +pcie_fab_init_bus(dev_info_t *rcdip, uint8_t flags) { - pcie_bus_t *bus_p; + int circular_count; + dev_info_t *dip = ddi_get_child(rcdip); + pcie_bus_arg_t arg; - pcie_fini_plat(cdip); - pcie_fini_pfd(cdip); + arg.init = B_TRUE; + arg.flags = flags; - bus_p = PCIE_DIP2UPBUS(cdip); - ASSERT(bus_p); + ndi_devi_enter(rcdip, &circular_count); + ddi_walk_devs(dip, pcie_fab_do_init_fini, &arg); + ndi_devi_exit(rcdip, circular_count); +} - if (PCIE_IS_HOTPLUG_CAPABLE(cdip)) - (void) ndi_prop_remove(DDI_DEV_T_NONE, cdip, "hotplug-capable"); +void +pcie_fab_fini_bus(dev_info_t *rcdip, uint8_t flags) +{ + int circular_count; + dev_info_t *dip = ddi_get_child(rcdip); + pcie_bus_arg_t arg; - pci_config_teardown(&bus_p->bus_cfg_hdl); - ndi_set_bus_private(cdip, B_TRUE, NULL, NULL); - kmem_free(bus_p->bus_assigned_addr, - (sizeof (pci_regspec_t) * bus_p->bus_assigned_entries)); - kmem_free(bus_p->bus_addr_ranges, - (sizeof (ppb_ranges_t) * bus_p->bus_addr_entries)); + arg.init = B_FALSE; + arg.flags = flags; - kmem_free(bus_p, sizeof (pcie_bus_t)); + ndi_devi_enter(rcdip, &circular_count); + ddi_walk_devs(dip, pcie_fab_do_init_fini, &arg); + ndi_devi_exit(rcdip, circular_count); } void @@ -1980,4 +2291,5 @@ *empty_mem_range = B_TRUE; } } + #endif /* defined(__i386) || defined(__amd64) */
--- a/usr/src/uts/common/io/pciex/pcieb.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/common/io/pciex/pcieb.c Fri Dec 04 12:16:40 2009 +0800 @@ -780,7 +780,7 @@ "INITCHILD: config regs setup for %s@%s\n", ddi_node_name(child), ddi_get_name_addr(child)); - if (!pcie_init_bus(child) || pcie_initchild(child) != DDI_SUCCESS) { + if (pcie_initchild(child) != DDI_SUCCESS) { result = DDI_FAILURE; goto cleanup; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/sys/pci_cfgacc.h Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,87 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PCI_CFGACC_H +#define _PCI_CFGACC_H + +#include <sys/dditypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ASM + +#define PCI_GETBDF(b, d, f) \ + ((d != 0) ? \ + ((((uint16_t)b & 0xff) << 8) + (((uint8_t)d & 0x1f) << 3) + \ + ((uint8_t)f & 0x7)) : \ + ((((uint16_t)b & 0xff) << 8) + ((uint8_t)f & 0xff))) + +typedef union pci_cfg_data { + uint8_t b; + uint16_t w; + uint32_t dw; + uint64_t qw; +} pci_cfg_data_t; + +typedef enum pci_config_size { + PCI_CFG_SIZE_BYTE = 1, + PCI_CFG_SIZE_WORD = 2, + PCI_CFG_SIZE_DWORD = 4, + PCI_CFG_SIZE_QWORD = 8 +} pci_config_size_t; + +typedef struct pci_cfgacc_req { + dev_info_t *rcdip; + uint16_t bdf; + uint16_t offset; + uint8_t size; + boolean_t write; + pci_cfg_data_t value; + boolean_t ioacc; +} pci_cfgacc_req_t; +#define VAL8(req) ((req)->value.b) +#define VAL16(req) ((req)->value.w) +#define VAL32(req) ((req)->value.dw) +#define VAL64(req) ((req)->value.qw) + +extern uint8_t pci_cfgacc_get8(dev_info_t *, uint16_t, uint16_t); +extern uint16_t pci_cfgacc_get16(dev_info_t *, uint16_t, uint16_t); +extern uint32_t pci_cfgacc_get32(dev_info_t *, uint16_t, uint16_t); +extern uint64_t pci_cfgacc_get64(dev_info_t *, uint16_t, uint16_t); +extern void pci_cfgacc_put8(dev_info_t *, uint16_t, uint16_t, uint8_t); +extern void pci_cfgacc_put16(dev_info_t *, uint16_t, uint16_t, uint16_t); +extern void pci_cfgacc_put32(dev_info_t *, uint16_t, uint16_t, uint32_t); +extern void pci_cfgacc_put64(dev_info_t *, uint16_t, uint16_t, uint64_t); +extern void pci_cfgacc_acc(pci_cfgacc_req_t *); + +#endif /* _ASM */ + +#ifdef __cplusplus +} +#endif + +#endif /* _PCI_CFGACC_H */
--- a/usr/src/uts/common/sys/pcie_impl.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/common/sys/pcie_impl.h Fri Dec 04 12:16:40 2009 +0800 @@ -59,6 +59,9 @@ #define PCIE_BUS2DIP(bus_p) bus_p->bus_dip #define PCIE_BUS2PFD(bus_p) PCIE_DIP2PFD(PCIE_BUS2DIP(bus_p)) +/* + * These macros depend on initialization of type related data in bus_p. + */ #define PCIE_IS_PCIE(bus_p) (bus_p->bus_pcie_off) #define PCIE_IS_PCIX(bus_p) (bus_p->bus_pcix_off) #define PCIE_IS_PCI(bus_p) (!PCIE_IS_PCIE(bus_p)) @@ -256,6 +259,16 @@ typedef struct pf_data pf_data_t; +/* + * For hot plugged device, these data are init'ed during during probe + * For non-hotplugged device, these data are init'ed in pci_autoconfig (on x86), + * or in px_attach()(on sparc). + * + * For root complex the fields are initialized in pcie_rc_init_bus(); + * for others part of the fields are initialized in pcie_init_bus(), + * and part of fields initialized in pcie_post_init_bus(). See comments + * on top of respective functions for details. + */ typedef struct pcie_bus { /* Needed for PCI/PCIe fabric error handling */ dev_info_t *bus_dip; @@ -294,6 +307,8 @@ pcie_hp_mode_t bus_hp_curr_mode; /* HP mode used */ void *bus_hp_ctrl; /* HP bus ctrl data */ int bus_ari; /* ARI device */ + + uint64_t bus_cfgacc_base; /* config space base address */ } pcie_bus_t; struct pf_data { @@ -399,6 +414,13 @@ pcie_disable_errors(dip); \ } +/* + * pcie_init_buspcie_fini_bus specific flags + */ +#define PCIE_BUS_INITIAL 0x0001 +#define PCIE_BUS_FINAL 0x0002 +#define PCIE_BUS_ALL (PCIE_BUS_INITIAL | PCIE_BUS_FINAL) + #ifdef DEBUG #define PCIE_DBG pcie_dbg /* Common Debugging shortcuts */ @@ -441,6 +463,8 @@ extern void pcie_init_root_port_mps(dev_info_t *dip); extern int pcie_initchild(dev_info_t *dip); extern void pcie_uninitchild(dev_info_t *dip); +extern int pcie_init_cfghdl(dev_info_t *dip); +extern void pcie_fini_cfghdl(dev_info_t *dip); extern void pcie_clear_errors(dev_info_t *dip); extern int pcie_postattach_child(dev_info_t *dip); extern void pcie_enable_errors(dev_info_t *dip); @@ -448,8 +472,11 @@ extern int pcie_enable_ce(dev_info_t *dip); extern boolean_t pcie_bridge_is_link_disabled(dev_info_t *); -extern pcie_bus_t *pcie_init_bus(dev_info_t *cdip); -extern void pcie_fini_bus(dev_info_t *cdip); +extern pcie_bus_t *pcie_init_bus(dev_info_t *dip, pcie_req_id_t bdf, + uint8_t flags); +extern void pcie_fini_bus(dev_info_t *dip, uint8_t flags); +extern void pcie_fab_init_bus(dev_info_t *dip, uint8_t flags); +extern void pcie_fab_fini_bus(dev_info_t *dip, uint8_t flags); extern void pcie_rc_init_bus(dev_info_t *dip); extern void pcie_rc_fini_bus(dev_info_t *dip); extern void pcie_rc_init_pfd(dev_info_t *dip, pf_data_t *pfd);
--- a/usr/src/uts/i86pc/Makefile.files Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/Makefile.files Fri Dec 04 12:16:40 2009 +0800 @@ -94,6 +94,8 @@ mpcore.o \ notes.o \ pci_bios.o \ + pci_cfgacc.o \ + pci_cfgacc_x86.o \ pci_cfgspace.o \ pci_mech1.o \ pci_mech2.o \
--- a/usr/src/uts/i86pc/Makefile.rules Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/Makefile.rules Fri Dec 04 12:16:40 2009 +0800 @@ -151,6 +151,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pciex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/os/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -360,6 +364,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/ppm/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/os/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- a/usr/src/uts/i86pc/io/pci/pci_tools.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/io/pci/pci_tools.c Fri Dec 04 12:16:40 2009 +0800 @@ -42,6 +42,7 @@ #include <sys/promif.h> #include <sys/x86_archext.h> #include <sys/cpuvar.h> +#include <sys/pci_cfgacc.h> #ifdef __xpv #include <sys/hypervisor.h> @@ -54,6 +55,7 @@ #define SUCCESS 0 +extern uint64_t mcfg_mem_base; int pcitool_debug = 0; /* @@ -75,14 +77,11 @@ static uint64_t max_cfg_size = PCI_CONF_HDR_SIZE; static uint64_t pcitool_swap_endian(uint64_t data, int size); -static int pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, - boolean_t write_flag); -static int pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, +static int pcitool_cfg_access(pcitool_reg_t *prg, boolean_t write_flag, + boolean_t io_access); +static int pcitool_io_access(pcitool_reg_t *prg, boolean_t write_flag); +static int pcitool_mem_access(pcitool_reg_t *prg, uint64_t virt_addr, boolean_t write_flag); -static int pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, - boolean_t write_flag); -static int pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, - uint64_t virt_addr, boolean_t write_flag); static uint64_t pcitool_map(uint64_t phys_addr, size_t size, size_t *num_pages); static void pcitool_unmap(uint64_t virt_addr, size_t num_pages); @@ -243,8 +242,6 @@ devs->dev_inst = ddi_get_instance(dip); } - -/*ARGSUSED*/ static int pcitool_get_intr(dev_info_t *dip, void *arg, int mode) { @@ -438,7 +435,6 @@ * Main function for handling interrupt CPU binding requests and queries. * Need to implement later */ -/*ARGSUSED*/ int pcitool_intr_admn(dev_info_t *dip, void *arg, int cmd, int mode) { @@ -466,16 +462,6 @@ return (rval); } - -/* - * A note about ontrap handling: - * - * X86 systems on which this module was tested return FFs instead of bus errors - * when accessing devices with invalid addresses. Ontrap handling, which - * gracefully handles kernel bus errors, is installed anyway, in case future - * X86 platforms require it. - */ - /* * Perform register accesses on the nexus device itself. * No explicit PCI nexus device for X86, so not applicable. @@ -511,158 +497,97 @@ return (returned_data.data64); } - /* - * Access device. prg is modified. + * A note about ontrap handling: * - * Extended config space is available only through memory-mapped access. - * Standard config space on pci express devices is available either way, - * so do it memory-mapped here too, for simplicity, if allowed by MCFG. - * If anything fails, return EINVAL so caller can try I/O access. + * X86 systems on which this module was tested return FFs instead of bus errors + * when accessing devices with invalid addresses. Ontrap handling, which + * gracefully handles kernel bus errors, is installed anyway for I/O and mem + * space accessing (not for pci config space), in case future X86 platforms + * require it. */ -/*ARGSUSED*/ -static int -pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, - boolean_t write_flag) -{ - int rval = SUCCESS; - uint64_t virt_addr; - size_t num_virt_pages; - int first_bus, last_bus; - int64_t *ecfginfo; - uint_t nelem; - - prg->status = PCITOOL_SUCCESS; - - if (ddi_prop_lookup_int64_array(DDI_DEV_T_ANY, dip, 0, - "ecfg", &ecfginfo, &nelem) == DDI_PROP_SUCCESS) { - - /* - * We must have a four-element property; base addr [0] must - * be nonzero. Also, segment [1] must be 0 for now; we don't - * handle nonzero segments (or create a property containing - * them) - */ - if ((nelem != 4) || (ecfginfo[0] == 0) || (ecfginfo[1] != 0)) { - ddi_prop_free(ecfginfo); - return (EINVAL); - } - - prg->phys_addr = ecfginfo[0]; - first_bus = ecfginfo[2]; - last_bus = ecfginfo[3]; - - ddi_prop_free(ecfginfo); - - if (prg->bus_no < first_bus || prg->bus_no > last_bus) - return (EINVAL); - } else { - return (EINVAL); - } - - prg->phys_addr += prg->offset + - ((prg->bus_no << PCIEX_REG_BUS_SHIFT) | - (prg->dev_no << PCIEX_REG_DEV_SHIFT) | - (prg->func_no << PCIEX_REG_FUNC_SHIFT)); - - virt_addr = pcitool_map(prg->phys_addr, - PCITOOL_ACC_ATTR_SIZE(prg->acc_attr), &num_virt_pages); - - if (virt_addr == NULL) - return (EINVAL); - - rval = pcitool_mem_access(dip, prg, virt_addr, write_flag); - pcitool_unmap(virt_addr, num_virt_pages); - return (rval); -} /* Access device. prg is modified. */ -/*ARGSUSED*/ static int -pcitool_cfg_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) +pcitool_cfg_access(pcitool_reg_t *prg, boolean_t write_flag, + boolean_t io_access) { int size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); int rval = SUCCESS; uint64_t local_data; + pci_cfgacc_req_t req; + uint32_t max_offset; + + if ((size <= 0) || (size > 8) || ((size & (size - 1)) != 0)) { + prg->status = PCITOOL_INVALID_SIZE; + return (ENOTSUP); + } /* * NOTE: there is no way to verify whether or not the address is * valid other than that it is within the maximum offset. The - * put functions return void and the get functions return ff on - * error. + * put functions return void and the get functions return -1 on error. */ - if (prg->offset + size - 1 > 0xFF) { + if (io_access) + max_offset = 0xFF; + else + max_offset = 0xFFF; + if (prg->offset + size - 1 > max_offset) { prg->status = PCITOOL_INVALID_ADDRESS; return (ENOTSUP); } prg->status = PCITOOL_SUCCESS; + req.rcdip = NULL; + req.bdf = PCI_GETBDF(prg->bus_no, prg->dev_no, prg->func_no); + req.offset = prg->offset; + req.size = size; + req.write = write_flag; + req.ioacc = io_access; if (write_flag) { - if (big_endian) { local_data = pcitool_swap_endian(prg->data, size); } else { local_data = prg->data; } - - switch (size) { - case 1: - (*pci_putb_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset, local_data); - break; - case 2: - (*pci_putw_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset, local_data); - break; - case 4: - (*pci_putl_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset, local_data); - break; - default: - rval = ENOTSUP; - prg->status = PCITOOL_INVALID_SIZE; - break; - } + VAL64(&req) = local_data; + pci_cfgacc_acc(&req); } else { - switch (size) { - case 1: - local_data = (*pci_getb_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset); - break; - case 2: - local_data = (*pci_getw_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset); - break; - case 4: - local_data = (*pci_getl_func)(prg->bus_no, prg->dev_no, - prg->func_no, prg->offset); - break; - default: - rval = ENOTSUP; - prg->status = PCITOOL_INVALID_SIZE; - break; - } - - if (rval == SUCCESS) { - if (big_endian) { - prg->data = - pcitool_swap_endian(local_data, size); - } else { - prg->data = local_data; - } + pci_cfgacc_acc(&req); + local_data = VAL64(&req); + if (big_endian) { + prg->data = + pcitool_swap_endian(local_data, size); + } else { + prg->data = local_data; } } - prg->phys_addr = 0; /* Config space is not memory mapped on X86. */ + /* + * Check if legacy IO config access is used, in which case + * only first 256 bytes are valid. + */ + if (req.ioacc && (prg->offset + size - 1 > 0xFF)) { + prg->status = PCITOOL_INVALID_ADDRESS; + return (ENOTSUP); + } + + /* Set phys_addr only if MMIO is used */ + prg->phys_addr = 0; + if (!req.ioacc && mcfg_mem_base != 0) { + prg->phys_addr = mcfg_mem_base + prg->offset + + ((prg->bus_no << PCIEX_REG_BUS_SHIFT) | + (prg->dev_no << PCIEX_REG_DEV_SHIFT) | + (prg->func_no << PCIEX_REG_FUNC_SHIFT)); + } + return (rval); } - -/*ARGSUSED*/ static int -pcitool_io_access(dev_info_t *dip, pcitool_reg_t *prg, boolean_t write_flag) +pcitool_io_access(pcitool_reg_t *prg, boolean_t write_flag) { int port = (int)prg->phys_addr; size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); @@ -750,10 +675,8 @@ return (rval); } -/*ARGSUSED*/ static int -pcitool_mem_access(dev_info_t *dip, pcitool_reg_t *prg, uint64_t virt_addr, - boolean_t write_flag) +pcitool_mem_access(pcitool_reg_t *prg, uint64_t virt_addr, boolean_t write_flag) { size_t size = PCITOOL_ACC_ATTR_SIZE(prg->acc_attr); boolean_t big_endian = PCITOOL_ACC_IS_BIG_ENDIAN(prg->acc_attr); @@ -922,10 +845,12 @@ /* Perform register accesses on PCI leaf devices. */ +/*ARGSUSED*/ int pcitool_dev_reg_ops(dev_info_t *dip, void *arg, int cmd, int mode) { boolean_t write_flag = B_FALSE; + boolean_t io_access = B_TRUE; int rval = 0; pcitool_reg_t prg; uint8_t size; @@ -989,36 +914,10 @@ rval = EINVAL; goto done_reg; } + if (max_cfg_size == PCIE_CONF_HDR_SIZE) + io_access = B_FALSE; - /* - * Access device. prg is modified. - * First, check for AMD K8 northbridges for I/O access - * (This fix will move in future to pcitool user-land) - * Next, check for PCIe devices and do - * memory-mapped access - * Lastly, check for PCI devices and do I/O access - */ - if ((prg.bus_no == 0) && - (prg.dev_no >= 0x18) && - (prg.dev_no < - (0x18 + ncpus/cpuid_get_ncpu_per_chip(CPU))) && - (cpuid_getvendor(CPU) == X86_VENDOR_AMD) && - (cpuid_getfamily(CPU) == 0xf)) { - rval = pcitool_cfg_access(dip, &prg, - write_flag); - } else if (max_cfg_size == PCIE_CONF_HDR_SIZE) { - rval = pcitool_pciex_cfg_access(dip, &prg, - write_flag); - if (rval == EINVAL) { - /* Not valid for MMIO; try IO */ - rval = pcitool_cfg_access(dip, &prg, - write_flag); - } - } else { - rval = pcitool_cfg_access(dip, &prg, - write_flag); - } - + rval = pcitool_cfg_access(&prg, write_flag, io_access); if (pcitool_debug) prom_printf( "config access: data:0x%" PRIx64 "\n", @@ -1047,7 +946,7 @@ * prg2.offset is the offset into config space of the * BAR desired. prg.status is modified on error. */ - rval = pcitool_cfg_access(dip, &prg2, B_FALSE); + rval = pcitool_cfg_access(&prg2, B_FALSE, B_TRUE); if (rval != SUCCESS) { if (pcitool_debug) prom_printf("BAR access failed\n"); @@ -1091,7 +990,7 @@ prg2.data &= PCI_BASE_IO_ADDR_M; prg.phys_addr = prg2.data + prg.offset; - rval = pcitool_io_access(dip, &prg, write_flag); + rval = pcitool_io_access(&prg, write_flag); if ((rval != SUCCESS) && (pcitool_debug)) prom_printf("IO access failed\n"); @@ -1129,7 +1028,8 @@ * prg2.status is modified on error. */ prg2.offset += 4; - rval = pcitool_cfg_access(dip, &prg2, B_FALSE); + rval = pcitool_cfg_access(&prg2, + B_FALSE, B_TRUE); if (rval != SUCCESS) { prg.status = prg2.status; goto done_reg; @@ -1199,8 +1099,7 @@ goto done_reg; } - rval = pcitool_mem_access(dip, &prg, virt_addr, - write_flag); + rval = pcitool_mem_access(&prg, virt_addr, write_flag); pcitool_unmap(virt_addr, num_virt_pages); } done_reg:
--- a/usr/src/uts/i86pc/io/pciex/npe.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/io/pciex/npe.c Fri Dec 04 12:16:40 2009 +0800 @@ -308,8 +308,6 @@ pcip->pci_dip = devi; pcip->pci_soft_state = PCI_SOFT_STATE_CLOSED; - pcie_rc_init_bus(devi); - if (pcie_init(devi, NULL) != DDI_SUCCESS) goto fail1; @@ -330,6 +328,7 @@ npe_query_acpi_mcfg(devi); ddi_report_dev(devi); + pcie_fab_init_bus(devi, PCIE_BUS_FINAL); return (DDI_SUCCESS); @@ -353,6 +352,7 @@ switch (cmd) { case DDI_DETACH: + pcie_fab_fini_bus(devi, PCIE_BUS_INITIAL); /* Uninitialize pcitool support. */ pcitool_uninit(devi); @@ -363,7 +363,6 @@ if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) ddi_fm_handler_unregister(devi); - pcie_rc_fini_bus(devi); pcie_rc_fini_pfd(PCIE_DIP2PFD(devi)); kmem_free(PCIE_DIP2PFD(devi), sizeof (pf_data_t)); @@ -939,7 +938,7 @@ pci_config_teardown(&cfg_hdl); } - bus_p = pcie_init_bus(child); + bus_p = PCIE_DIP2BUS(child); if (bus_p) { uint16_t device_id = (uint16_t)(bus_p->bus_dev_ven_id >> 16); uint16_t vendor_id = (uint16_t)(bus_p->bus_dev_ven_id & 0xFFFF);
--- a/usr/src/uts/i86pc/io/pciex/npe_misc.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/io/pciex/npe_misc.c Fri Dec 04 12:16:40 2009 +0800 @@ -39,6 +39,7 @@ #include <sys/x86_archext.h> #include <io/pciex/pcie_nvidia.h> #include <io/pciex/pcie_nb5000.h> +#include <sys/pci_cfgacc_x86.h> /* * Prototype declaration @@ -57,43 +58,6 @@ extern uint32_t npe_aer_uce_mask; -/* AMD's northbridges vendor-id and device-ids */ -#define AMD_NTBRDIGE_VID 0x1022 /* AMD vendor-id */ -#define AMD_HT_NTBRIDGE_DID 0x1100 /* HT Configuration */ -#define AMD_AM_NTBRIDGE_DID 0x1101 /* Address Map */ -#define AMD_DC_NTBRIDGE_DID 0x1102 /* DRAM Controller */ -#define AMD_MC_NTBRIDGE_DID 0x1103 /* Misc Controller */ -#define AMD_K10_NTBRIDGE_DID_0 0x1200 -#define AMD_K10_NTBRIDGE_DID_1 0x1201 -#define AMD_K10_NTBRIDGE_DID_2 0x1202 -#define AMD_K10_NTBRIDGE_DID_3 0x1203 -#define AMD_K10_NTBRIDGE_DID_4 0x1204 - -/* - * Check if the given device is an AMD northbridge - */ -#define IS_BAD_AMD_NTBRIDGE(vid, did) \ - (((vid) == AMD_NTBRDIGE_VID) && \ - (((did) == AMD_HT_NTBRIDGE_DID) || \ - ((did) == AMD_AM_NTBRIDGE_DID) || \ - ((did) == AMD_DC_NTBRIDGE_DID) || \ - ((did) == AMD_MC_NTBRIDGE_DID))) - -#define IS_K10_AMD_NTBRIDGE(vid, did) \ - (((vid) == AMD_NTBRDIGE_VID) && \ - (((did) == AMD_K10_NTBRIDGE_DID_0) || \ - ((did) == AMD_K10_NTBRIDGE_DID_1) || \ - ((did) == AMD_K10_NTBRIDGE_DID_2) || \ - ((did) == AMD_K10_NTBRIDGE_DID_3) || \ - ((did) == AMD_K10_NTBRIDGE_DID_4))) - -#define MSR_AMD_NB_MMIO_CFG_BADDR 0xc0010058 -#define AMD_MMIO_CFG_BADDR_ADDR_MASK 0xFFFFFFF00000ULL -#define AMD_MMIO_CFG_BADDR_ENA_MASK 0x000000000001ULL -#define AMD_MMIO_CFG_BADDR_ENA_ON 0x000000000001ULL -#define AMD_MMIO_CFG_BADDR_ENA_OFF 0x000000000000ULL - - /* * Query the MCFG table using ACPI. If MCFG is found, setup the * 'ecfg' property accordingly. Otherwise, set the values
--- a/usr/src/uts/i86pc/os/acpi_fw.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/os/acpi_fw.h Fri Dec 04 12:16:40 2009 +0800 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -161,6 +161,28 @@ */ extern struct slit *slit_ptr; +struct cfg_base_addr_alloc { + uint64_t base_addr; + uint16_t segment; + uint8_t start_bno; + uint8_t end_bno; + uint32_t reserved; +}; + +struct mcfg { + char Signature[4]; + uint32_t Length; + uint8_t Revision; + uint8_t Checksum; + char OemId[6]; + char OemTableId[8]; + uint32_t OemRevision; + char CreatorId[4]; + uint32_t CreatorRevision; + uint8_t Reserved[8]; + struct cfg_base_addr_alloc CfgBaseAddrAllocList[1]; +}; + struct dmar { struct table_header hdr; uint8_t width;
--- a/usr/src/uts/i86pc/os/fakebop.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/os/fakebop.c Fri Dec 04 12:16:40 2009 +0800 @@ -61,6 +61,7 @@ #include <sys/dmar_acpi.h> #include <sys/kobj.h> #include <sys/kobj_lex.h> +#include <sys/pci_cfgspace_impl.h> #include "acpi_fw.h" static int have_console = 0; /* set once primitive console is initialized */ @@ -230,7 +231,7 @@ /*NOTREACHED*/ } -static uintptr_t +uintptr_t alloc_vaddr(size_t size, paddr_t align) { uintptr_t rv; @@ -1861,7 +1862,6 @@ } -#ifndef __xpv /* * Set ACPI firmware properties */ @@ -1873,6 +1873,9 @@ caddr_t va; size_t len, page; +#ifdef __xpv + pa = pfn_to_pa(xen_assign_pfn(mmu_btop(pa))) | (pa & MMU_PAGEOFFSET); +#endif start = P2ALIGN(pa, MMU_PAGESIZE); end = P2ROUNDUP(pa + length, MMU_PAGESIZE); len = end - start; @@ -2047,6 +2050,30 @@ } static void +process_mcfg(struct mcfg *tp) +{ + struct cfg_base_addr_alloc *cfg_baap; + char *cfg_baa_endp; + int64_t ecfginfo[4]; + + cfg_baap = tp->CfgBaseAddrAllocList; + cfg_baa_endp = ((char *)tp) + tp->Length; + while ((char *)cfg_baap < cfg_baa_endp) { + if (cfg_baap->base_addr != 0 && cfg_baap->segment == 0) { + ecfginfo[0] = cfg_baap->base_addr; + ecfginfo[1] = cfg_baap->segment; + ecfginfo[2] = cfg_baap->start_bno; + ecfginfo[3] = cfg_baap->end_bno; + bsetprop(MCFG_PROPNAME, strlen(MCFG_PROPNAME), + ecfginfo, sizeof (ecfginfo)); + break; + } + cfg_baap++; + } +} + +#ifndef __xpv +static void process_madt(struct madt *tp) { struct madt_processor *cpu, *end; @@ -2235,9 +2262,9 @@ static void build_firmware_properties(void) { + struct table_header *tp = NULL; + #ifndef __xpv - struct table_header *tp; - if ((tp = find_fw_table("APIC")) != NULL) process_madt((struct madt *)tp); @@ -2249,9 +2276,14 @@ if (tp = find_fw_table("DMAR")) process_dmar((struct dmar *)tp); + tp = find_fw_table("MCFG"); #else /* __xpv */ enumerate_xen_cpus(); + if (DOMAIN_IS_INITDOMAIN(xen_info)) + tp = find_fw_table("MCFG"); #endif /* __xpv */ + if (tp != NULL) + process_mcfg((struct mcfg *)tp); } /*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/os/pci_cfgacc_x86.c Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,301 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/systm.h> +#include <sys/pci_cfgacc.h> +#include <sys/pci_cfgspace.h> +#include <sys/pci_cfgspace_impl.h> +#include <sys/sunddi.h> +#include <sys/sysmacros.h> +#include <sys/x86_archext.h> +#include <sys/pci.h> +#include <vm/hat_i86.h> +#include <vm/seg_kmem.h> +#include <vm/kboot_mmu.h> + +#define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) +#define PCI_BDF_BUS(bdf) ((((uint16_t)bdf) & 0xff00) >> 8) +#define PCI_BDF_DEV(bdf) ((((uint16_t)bdf) & 0xf8) >> 3) +#define PCI_BDF_FUNC(bdf) (((uint16_t)bdf) & 0x7) + +/* patchable variables */ +volatile boolean_t pci_cfgacc_force_io = B_FALSE; + +extern uintptr_t alloc_vaddr(size_t, paddr_t); + +void pci_cfgacc_acc(pci_cfgacc_req_t *); + +boolean_t pci_cfgacc_find_workaround(uint16_t); +/* + * IS_P2ALIGNED() is used to make sure offset is 'size'-aligned, so + * it's guaranteed that the access will not cross 4k page boundary. + * Thus only 1 page is allocated for all config space access, and the + * virtual address of that page is cached in pci_cfgacc_virt_base. + */ +static caddr_t pci_cfgacc_virt_base = NULL; + +static caddr_t +pci_cfgacc_map(paddr_t phys_addr) +{ +#ifdef __xpv + phys_addr = pfn_to_pa(xen_assign_pfn(mmu_btop(phys_addr))) | + (phys_addr & MMU_PAGEOFFSET); +#endif + if (khat_running) { + pfn_t pfn = mmu_btop(phys_addr); + /* + * pci_cfgacc_virt_base may hold address left from early + * boot, which points to low mem. Realloc virtual address + * in kernel space since it's already late in boot now. + * Note: no need to unmap first, clear_boot_mappings() will + * do that for us. + */ + if (pci_cfgacc_virt_base < (caddr_t)kernelbase) { + if ((pci_cfgacc_virt_base = vmem_alloc(heap_arena, + MMU_PAGESIZE, VM_NOSLEEP)) == NULL) + return (NULL); + } + hat_devload(kas.a_hat, pci_cfgacc_virt_base, + MMU_PAGESIZE, pfn, PROT_READ | PROT_WRITE | + HAT_STRICTORDER, HAT_LOAD_LOCK); + } else { + paddr_t pa_base = P2ALIGN(phys_addr, MMU_PAGESIZE); + + if (pci_cfgacc_virt_base == NULL) { + if ((pci_cfgacc_virt_base = (caddr_t) + alloc_vaddr(MMU_PAGESIZE, MMU_PAGESIZE)) == NULL) + return (NULL); + } + kbm_map((uintptr_t)pci_cfgacc_virt_base, pa_base, 0, 0); + } + + return (pci_cfgacc_virt_base + (phys_addr & MMU_PAGEOFFSET)); +} + +static void +pci_cfgacc_unmap() +{ + if (khat_running) + hat_unload(kas.a_hat, pci_cfgacc_virt_base, MMU_PAGESIZE, + HAT_UNLOAD_UNLOCK); +} + +static void +pci_cfgacc_io(pci_cfgacc_req_t *req) +{ + uint8_t bus, dev, func, ioacc_offset; + + if (req->offset > 0xff) { + if (!req->write) + VAL64(req) = (uint64_t)-1; + return; + } + + bus = PCI_BDF_BUS(req->bdf); + dev = PCI_BDF_DEV(req->bdf); + func = PCI_BDF_FUNC(req->bdf); + ioacc_offset = req->offset; + switch (req->size) { + case 1: + if (req->write) + (*pci_putb_func)(bus, dev, func, + ioacc_offset, VAL8(req)); + else + VAL8(req) = (*pci_getb_func)(bus, dev, func, + ioacc_offset); + break; + case 2: + if (req->write) + (*pci_putw_func)(bus, dev, func, + ioacc_offset, VAL16(req)); + else + VAL16(req) = (*pci_getw_func)(bus, dev, func, + ioacc_offset); + break; + case 4: + if (req->write) + (*pci_putl_func)(bus, dev, func, + ioacc_offset, VAL32(req)); + else + VAL32(req) = (*pci_getl_func)(bus, dev, func, + ioacc_offset); + break; + default: + return; + } +} + +static int +pci_cfgacc_mmio(pci_cfgacc_req_t *req) +{ + caddr_t vaddr; + paddr_t paddr; + int rval = DDI_SUCCESS; + + paddr = (paddr_t)req->bdf << 12; + paddr += mcfg_mem_base + req->offset; + + mutex_enter(&pcicfg_mutex); + if ((vaddr = pci_cfgacc_map(paddr)) == NULL) { + mutex_exit(&pcicfg_mutex); + return (DDI_FAILURE); + } + + switch (req->size) { + case 1: + if (req->write) + *((uint8_t *)vaddr) = VAL8(req); + else + VAL8(req) = *((uint8_t *)vaddr); + break; + case 2: + if (req->write) + *((uint16_t *)vaddr) = VAL16(req); + else + VAL16(req) = *((uint16_t *)vaddr); + break; + case 4: + if (req->write) + *((uint32_t *)vaddr) = VAL32(req); + else + VAL32(req) = *((uint32_t *)vaddr); + break; + case 8: + if (req->write) + *((uint64_t *)vaddr) = VAL64(req); + else + VAL64(req) = *((uint64_t *)vaddr); + break; + default: + rval = DDI_FAILURE; + } + pci_cfgacc_unmap(); + mutex_exit(&pcicfg_mutex); + + return (rval); +} + +static boolean_t +pci_cfgacc_valid(pci_cfgacc_req_t *req) +{ + return (IS_P2ALIGNED(req->offset, req->size) && + (req->offset < PCIE_CFG_SPACE_SIZE)); +} + +void +pci_cfgacc_acc(pci_cfgacc_req_t *req) +{ + uint8_t bus; + + if (!req->write) + VAL64(req) = 0; + + if (!pci_cfgacc_valid(req)) { + if (!req->write) + VAL64(req) = (uint64_t)-1; + return; + } + + bus = PCI_BDF_BUS(req->bdf); + if (pci_cfgacc_force_io || (mcfg_mem_base == NULL) || + (bus < mcfg_bus_start) || (bus > mcfg_bus_end)) + goto ioacc; + + if (req->ioacc) + goto ioacc; + + /* check if workaround is needed */ + if (pci_cfgacc_find_workaround(req->bdf)) + goto ioacc; + + if (pci_cfgacc_mmio(req) != DDI_SUCCESS) + goto ioacc; + + return; + +ioacc: + pci_cfgacc_io(req); + req->ioacc = B_TRUE; +} + +typedef struct cfgacc_bus_range { + struct cfgacc_bus_range *next; + uint16_t bdf; + uchar_t secbus; + uchar_t subbus; +} cfgacc_bus_range_t; + +cfgacc_bus_range_t *pci_cfgacc_bus_head = NULL; + +#define BUS_INSERT(prev, el) \ + el->next = *prev; \ + *prev = el; + +#define BUS_REMOVE(prev, el) \ + *prev = el->next; + +/* + * This function is only supposed to be called in device tree setup time, + * thus no lock is needed. + */ +void +pci_cfgacc_add_workaround(uint16_t bdf, uchar_t secbus, uchar_t subbus) +{ + cfgacc_bus_range_t *entry; + + entry = kmem_zalloc(sizeof (cfgacc_bus_range_t), KM_SLEEP); + entry->bdf = bdf; + entry->secbus = secbus; + entry->subbus = subbus; + BUS_INSERT(&pci_cfgacc_bus_head, entry); +} + +boolean_t +pci_cfgacc_find_workaround(uint16_t bdf) +{ + cfgacc_bus_range_t *entry; + uchar_t bus; + + for (entry = pci_cfgacc_bus_head; entry != NULL; + entry = entry->next) { + if (bdf == entry->bdf) { + /* found a device which is known to be broken */ + return (B_TRUE); + } + + bus = PCI_BDF_BUS(bdf); + if ((bus != 0) && (bus >= entry->secbus) && + (bus <= entry->subbus)) { + /* + * found a device whose parent/grandparent is + * known to be broken. + */ + return (B_TRUE); + } + } + + return (B_FALSE); +}
--- a/usr/src/uts/i86pc/os/pci_cfgspace.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/os/pci_cfgspace.c Fri Dec 04 12:16:40 2009 +0800 @@ -35,12 +35,14 @@ #include <sys/pci_impl.h> #include <sys/pci_cfgspace.h> #include <sys/pci_cfgspace_impl.h> +#include <sys/pci_cfgacc.h> #if defined(__xpv) #include <sys/hypervisor.h> +#endif + +#if defined(__xpv) int pci_max_nbus = 0xFE; #endif - - int pci_bios_cfg_type = PCI_MECHANISM_UNKNOWN; int pci_bios_maxbus; int pci_bios_mech; @@ -54,10 +56,21 @@ int PCI_PROBE_TYPE = 0; /* + * No valid mcfg_mem_base by default, and accessing pci config space + * in mem-mapped way is disabled. + */ +uint64_t mcfg_mem_base = 0; +uint8_t mcfg_bus_start = 0; +uint8_t mcfg_bus_end = 0xff; + +/* * These function pointers lead to the actual implementation routines * for configuration space access. Normally they lead to either the * pci_mech1_* or pci_mech2_* routines, but they can also lead to * routines that work around chipset bugs. + * These functions are accessing pci config space via I/O way. + * Pci_cfgacc_get/put functions shoul be used as more common interfaces, + * which also provide accessing pci config space via mem-mapped way. */ uint8_t (*pci_getb_func)(int bus, int dev, int func, int reg); uint16_t (*pci_getw_func)(int bus, int dev, int func, int reg); @@ -66,6 +79,8 @@ void (*pci_putw_func)(int bus, int dev, int func, int reg, uint16_t val); void (*pci_putl_func)(int bus, int dev, int func, int reg, uint32_t val); +extern void (*pci_cfgacc_acc_p)(pci_cfgacc_req_t *req); + /* * Internal routines */ @@ -96,13 +111,14 @@ } /* - * This code determines if this system supports PCI and which + * This code determines if this system supports PCI/PCIE and which * type of configuration access method is used */ - static int pci_check(void) { + uint64_t ecfginfo[4]; + /* * Only do this once. NB: If this is not a PCI system, and we * get called twice, we can't detect it and will probably die @@ -135,8 +151,6 @@ */ pci_bios_maxbus = pci_max_nbus; } - - return (TRUE); #else /* !__xpv */ pci_bios_cfg_type = pci_check_bios(); @@ -192,9 +206,24 @@ default: return (FALSE); } +#endif /* __xpv */ + + /* + * Try to get a valid mcfg_mem_base in early boot + * If failed, leave mem-mapped pci config space accessing disabled + * until pci boot code (pci_autoconfig) makes sure this is a PCIE + * platform. + */ + if (do_bsys_getprop(NULL, MCFG_PROPNAME, ecfginfo) != -1) { + mcfg_mem_base = ecfginfo[0]; + mcfg_bus_start = ecfginfo[2]; + mcfg_bus_end = ecfginfo[3]; + } + + /* See pci_cfgacc.c */ + pci_cfgacc_acc_p = pci_cfgacc_acc; return (TRUE); -#endif /* __xpv */ } #if !defined(__xpv)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/i86pc/sys/pci_cfgacc_x86.h Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,82 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_PCI_CFGACC_X86_H +#define _SYS_PCI_CFGACC_X86_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* AMD's northbridges vendor-id and device-ids */ +#define AMD_NTBRDIGE_VID 0x1022 /* AMD vendor-id */ +#define AMD_HT_NTBRIDGE_DID 0x1100 /* HT Configuration */ +#define AMD_AM_NTBRIDGE_DID 0x1101 /* Address Map */ +#define AMD_DC_NTBRIDGE_DID 0x1102 /* DRAM Controller */ +#define AMD_MC_NTBRIDGE_DID 0x1103 /* Misc Controller */ +#define AMD_K10_NTBRIDGE_DID_0 0x1200 +#define AMD_K10_NTBRIDGE_DID_1 0x1201 +#define AMD_K10_NTBRIDGE_DID_2 0x1202 +#define AMD_K10_NTBRIDGE_DID_3 0x1203 +#define AMD_K10_NTBRIDGE_DID_4 0x1204 + +/* AMD's 8132 chipset vendor-id and device-ids */ +#define AMD_8132_BRIDGE_DID 0x7458 /* 8132 PCI-X bridge */ +#define AMD_8132_IOAPIC_DID 0x7459 /* 8132 IO APIC */ + +/* + * Check if the given device is an AMD northbridge + */ +#define IS_BAD_AMD_NTBRIDGE(vid, did) \ + (((vid) == AMD_NTBRDIGE_VID) && \ + (((did) == AMD_HT_NTBRIDGE_DID) || \ + ((did) == AMD_AM_NTBRIDGE_DID) || \ + ((did) == AMD_DC_NTBRIDGE_DID) || \ + ((did) == AMD_MC_NTBRIDGE_DID))) + +#define IS_K10_AMD_NTBRIDGE(vid, did) \ + (((vid) == AMD_NTBRDIGE_VID) && \ + (((did) == AMD_K10_NTBRIDGE_DID_0) || \ + ((did) == AMD_K10_NTBRIDGE_DID_1) || \ + ((did) == AMD_K10_NTBRIDGE_DID_2) || \ + ((did) == AMD_K10_NTBRIDGE_DID_3) || \ + ((did) == AMD_K10_NTBRIDGE_DID_4))) + +#define IS_AMD_8132_CHIP(vid, did) \ + (((vid) == AMD_NTBRDIGE_VID) && \ + (((did) == AMD_8132_BRIDGE_DID)) || \ + (((did) == AMD_8132_IOAPIC_DID))) + +#define MSR_AMD_NB_MMIO_CFG_BADDR 0xc0010058 +#define AMD_MMIO_CFG_BADDR_ADDR_MASK 0xFFFFFFF00000ULL +#define AMD_MMIO_CFG_BADDR_ENA_MASK 0x000000000001ULL +#define AMD_MMIO_CFG_BADDR_ENA_ON 0x000000000001ULL +#define AMD_MMIO_CFG_BADDR_ENA_OFF 0x000000000000ULL + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_PCI_CFGACC_X86_H */
--- a/usr/src/uts/i86pc/sys/pci_cfgspace_impl.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86pc/sys/pci_cfgspace_impl.h Fri Dec 04 12:16:40 2009 +0800 @@ -20,15 +20,13 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_PCI_CFGSPACE_IMPL_H #define _SYS_PCI_CFGSPACE_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Routines to support particular PCI chipsets */ @@ -98,10 +96,13 @@ #define PCI_MECH1_SPEC_CYCLE_DEV 0x1f /* dev to request spec cyc */ #define PCI_MECH1_SPEC_CYCLE_FUNC 0x07 /* func to request spec cyc */ +extern uint64_t mcfg_mem_base; +extern uint8_t mcfg_bus_start; +extern uint8_t mcfg_bus_end; + /* * Mutex for all pci config space routines to share */ - extern kmutex_t pcicfg_mutex; /* @@ -131,6 +132,8 @@ #define N_PCI_IRQ_ROUTES 32 #define N_PCI_IRQ_ROUTES_MAX 255 +#define MCFG_PROPNAME "ecfg" + #define FP_OFF(fp) (((uintptr_t)(fp)) & 0xFFFF) #define FP_SEG(fp) ((((uintptr_t)(fp)) >> 16) & 0xFFFF)
--- a/usr/src/uts/i86xpv/Makefile.files Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86xpv/Makefile.files Fri Dec 04 12:16:40 2009 +0800 @@ -85,6 +85,8 @@ memscrub.o \ notes.o \ pci_bios.o \ + pci_cfgacc.o \ + pci_cfgacc_x86.o \ pci_cfgspace.o \ pci_mech1.o \ pci_mech2.o \
--- a/usr/src/uts/i86xpv/Makefile.rules Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/i86xpv/Makefile.rules Fri Dec 04 12:16:40 2009 +0800 @@ -106,6 +106,10 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pciex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + # We need this one to make sure we share dtrace_subr.c with i86pc # Otherwise we pick up common/os/dtrace_subr.c instead :( # Note that only the non-commented versions of this hack end up @@ -243,6 +247,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/xsvc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pciex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/xen/io/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- a/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/io/hotplug/pcicfg/pcicfg.c Fri Dec 04 12:16:40 2009 +0800 @@ -41,6 +41,8 @@ #include <sys/sunndi.h> #include <sys/hotplug/pci/pcicfg.h> #include <sys/ndi_impldefs.h> +#include <sys/pci_cfgacc.h> +#include <sys/pcie_impl.h> /* * ************************************************************************ @@ -219,7 +221,7 @@ static int pcicfg_add_config_reg(dev_info_t *, uint_t, uint_t, uint_t); static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t, - uint_t *, pcicfg_flags_t); + uint_t *, pcicfg_flags_t, boolean_t); static int pcicfg_match_dev(dev_info_t *, void *); static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t); static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *); @@ -238,7 +240,7 @@ static int pcicfg_set_busnode_props(dev_info_t *, uint8_t); static int pcicfg_free_bridge_resources(dev_info_t *); static int pcicfg_free_device_resources(dev_info_t *); -static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t); +static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t, boolean_t); static void pcicfg_reparent_node(dev_info_t *, dev_info_t *); static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *); static void pcicfg_config_teardown(ddi_acc_handle_t *); @@ -266,8 +268,9 @@ static int pcicfg_pcie_device_type(dev_info_t *, ddi_acc_handle_t); static int pcicfg_pcie_port_type(dev_info_t *, ddi_acc_handle_t); static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t, - uint_t *); + uint_t *, boolean_t); static int pcicfg_find_resource_end(dev_info_t *, void *); +static boolean_t is_pcie_fabric(dev_info_t *); static int pcicfg_populate_reg_props(dev_info_t *, ddi_acc_handle_t); static int pcicfg_populate_props_from_bar(dev_info_t *, ddi_acc_handle_t); @@ -560,6 +563,7 @@ int max_function = PCI_MAX_FUNCTIONS; int trans_device; dev_info_t *new_device; + boolean_t is_pcie; if (flags == PCICFG_FLAG_ENABLE_ARI) return (pcicfg_ari_configure(devi)); @@ -579,6 +583,8 @@ attach_point = devi; + is_pcie = is_pcie_fabric(devi); + ndi_devi_enter(devi, &circ); for (func = 0; func < max_function; ) { @@ -591,7 +597,8 @@ trans_device = device; switch (rv = pcicfg_probe_children(attach_point, - bus, trans_device, func & 7, &highest_bus, flags)) { + bus, trans_device, func & 7, &highest_bus, + flags, is_pcie)) { case PCICFG_NORESRC: case PCICFG_FAILURE: DEBUG2("configure failed: bus [0x%x] device " @@ -725,6 +732,15 @@ * probe handle - if not, no harm in calling this. */ (void) pcicfg_destroy_phdl(new_device); + if (is_pcie) { + /* + * free pcie_bus_t for the sub-tree + */ + if (ddi_get_child(new_device) != NULL) + pcie_fab_fini_bus(new_device, PCIE_BUS_ALL); + + pcie_fini_bus(new_device, PCIE_BUS_ALL); + } /* * This will free up the node */ @@ -1433,6 +1449,8 @@ int func; int i; int max_function, trans_device; + int circ; + boolean_t is_pcie; if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED) max_function = PCICFG_MAX_ARI_FUNCTION; @@ -1443,6 +1461,9 @@ * Cycle through devices to make sure none are busy. * If a single device is busy fail the whole unconfigure. */ + is_pcie = is_pcie_fabric(devi); + + ndi_devi_enter(devi, &circ); for (func = 0; func < max_function; func++) { if ((function != PCICFG_ALL_FUNC) && (function != func)) continue; @@ -1489,10 +1510,10 @@ if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { DEBUG0("Failed to put back devices state\n"); - return (PCICFG_FAILURE); + goto fail; } } - return (PCICFG_FAILURE); + goto fail; } /* @@ -1521,14 +1542,14 @@ PCICFG_SUCCESS) { cmn_err(CE_WARN, "ntbridge: unconfigure failed\n"); - return (PCICFG_FAILURE); + goto fail; } - if (pcicfg_teardown_device(child_dip, flags) + if (pcicfg_teardown_device(child_dip, flags, is_pcie) != PCICFG_SUCCESS) { DEBUG2("Failed to tear down device [0x%x]" "function [0x%x]\n", trans_device, func & 7); - return (PCICFG_FAILURE); + goto fail; } } @@ -1537,11 +1558,16 @@ (void) pcie_ari_disable(devi); } + ndi_devi_exit(devi, circ); return (PCICFG_SUCCESS); + +fail: + ndi_devi_exit(devi, circ); + return (PCICFG_FAILURE); } static int -pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags) +pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags, boolean_t is_pcie) { ddi_acc_handle_t handle; @@ -1561,6 +1587,16 @@ pcicfg_device_off(handle); pcicfg_config_teardown(&handle); + if (is_pcie) { + /* + * free pcie_bus_t for the sub-tree + */ + if (ddi_get_child(dip) != NULL) + pcie_fab_fini_bus(dip, PCIE_BUS_ALL); + + pcie_fini_bus(dip, PCIE_BUS_ALL); + } + /* * The framework provides this routine which can * tear down a sub-tree. @@ -3622,7 +3658,7 @@ static int pcicfg_probe_children(dev_info_t *parent, uint_t bus, uint_t device, - uint_t func, uint_t *highest_bus, pcicfg_flags_t flags) + uint_t func, uint_t *highest_bus, pcicfg_flags_t flags, boolean_t is_pcie) { dev_info_t *new_child; ddi_acc_handle_t config_handle; @@ -3655,6 +3691,10 @@ goto failedconfig; } + if (is_pcie) + (void) pcie_init_bus(new_child, PCI_GETBDF(bus, device, func), + PCIE_BUS_INITIAL); + /* * As soon as we have access to config space, * turn off device. It will get turned on @@ -3692,9 +3732,8 @@ */ if ((!(header_type & PCI_HEADER_MULTI)) && (func != 0)) { - (void) pcicfg_config_teardown(&config_handle); - (void) ndi_devi_free(new_child); - return (PCICFG_NODEVICE); + ret = PCICFG_NODEVICE; + goto failedchild; } /* @@ -3712,7 +3751,7 @@ goto failedchild; ret = pcicfg_probe_bridge(new_child, config_handle, bus, - highest_bus); + highest_bus, is_pcie); if (ret != PCICFG_SUCCESS) { (void) pcicfg_free_bridge_resources(new_child); goto failedchild; @@ -3765,6 +3804,13 @@ (void) pcicfg_config_teardown(&config_handle); + /* + * Properties have been setted up, so initialize the remaining + * bus_t fields + */ + if (is_pcie) + pcie_init_bus(new_child, 0, PCIE_BUS_FINAL); + return (PCICFG_SUCCESS); failedchild: @@ -3773,6 +3819,9 @@ */ (void) pcicfg_config_teardown(&config_handle); + if (is_pcie) + pcie_fini_bus(new_child, PCIE_BUS_FINAL); + failedconfig: (void) ndi_devi_free(new_child); @@ -3972,7 +4021,7 @@ static int pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, - uint_t *highest_bus) + uint_t *highest_bus, boolean_t is_pcie) { uint64_t next_bus; uint_t new_bus, num_slots; @@ -4415,6 +4464,8 @@ (void) pcicfg_device_on(h); + if (is_pcie) + pcie_init_bus(new_child, 0, PCIE_BUS_FINAL); if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG) != NDI_SUCCESS) { DEBUG0("Unable to online bridge\n"); @@ -4445,8 +4496,8 @@ trans_device = i; if ((rval = pcicfg_probe_children(new_child, - new_bus, trans_device, j & 7, highest_bus, 0)) - != PCICFG_SUCCESS) { + new_bus, trans_device, j & 7, highest_bus, + 0, is_pcie)) != PCICFG_SUCCESS) { if (rval == PCICFG_NODEVICE) { DEBUG3("No Device at bus [0x%x]" "device [0x%x] " @@ -4526,6 +4577,8 @@ */ VERIFY(ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG) == NDI_SUCCESS); + if (is_pcie) + pcie_fini_bus(new_child, PCIE_BUS_INITIAL); phdl.dip = new_child; phdl.memory_base = mem_answer; @@ -5123,3 +5176,33 @@ return (port_type); } + +/* + * Return true if the devinfo node is in a PCI Express hierarchy. + */ +static boolean_t +is_pcie_fabric(dev_info_t *dip) +{ + dev_info_t *root = ddi_root_node(); + dev_info_t *pdip; + boolean_t found = B_FALSE; + char *bus; + + /* + * Does this device reside in a pcie fabric ? + */ + for (pdip = dip; pdip && (pdip != root) && !found; + pdip = ddi_get_parent(pdip)) { + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &bus) != + DDI_PROP_SUCCESS) + break; + + if (strcmp(bus, "pciex") == 0) + found = B_TRUE; + + ddi_prop_free(bus); + } + + return (found); +}
--- a/usr/src/uts/intel/io/pci/pci_boot.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/io/pci/pci_boot.c Fri Dec 04 12:16:40 2009 +0800 @@ -29,10 +29,11 @@ #include <sys/sunndi.h> #include <sys/pci.h> #include <sys/pci_impl.h> -#include <sys/pci_cfgspace.h> +#include <sys/pcie_impl.h> #include <sys/memlist.h> #include <sys/bootconf.h> #include <io/pci/mps_table.h> +#include <sys/pci_cfgacc.h> #include <sys/pci_cfgspace.h> #include <sys/pci_cfgspace_impl.h> #include <sys/psw.h> @@ -45,6 +46,7 @@ #include <sys/intel_iommu.h> #include <sys/iommulib.h> #include <sys/devcache.h> +#include <sys/pci_cfgacc_x86.h> #define pci_getb (*pci_getb_func) #define pci_getw (*pci_getw_func) @@ -107,6 +109,9 @@ static struct pci_fixundo *undolist = NULL; static int num_root_bus = 0; /* count of root buses */ extern volatile int acpi_resource_discovery; +extern uint64_t mcfg_mem_base; +extern void pci_cfgacc_add_workaround(uint16_t, uchar_t, uchar_t); +extern dev_info_t *pcie_get_rc_dip(dev_info_t *); /* * Module prototypes @@ -134,6 +139,8 @@ static void populate_bus_res(uchar_t bus); static void memlist_remove_list(struct memlist **list, struct memlist *remove_list); +static boolean_t is_pcie_platform(void); +static void ck804_fix_aer_ptr(dev_info_t *, pcie_req_id_t); static void pci_scan_bbn(void); static int pci_unitaddr_cache_valid(void); @@ -453,6 +460,13 @@ { uint_t i, root_bus_addr = 0; + /* + * enable mem-mapped pci config space accessing, + * if failed to do so during early boot + */ + if ((mcfg_mem_base == NULL) && is_pcie_platform()) + mcfg_mem_base = 0xE0000000; + alloc_res_array(); for (i = 0; i <= pci_bios_maxbus; i++) { pci_bus_res[i].par_bus = (uchar_t)-1; @@ -1864,6 +1878,7 @@ ushort_t is_pci_bridge = 0; struct pci_devfunc *devlist = NULL, *entry = NULL; gfx_entry_t *gfxp; + pcie_req_id_t bdf; ushort_t deviceid = pci_getw(bus, dev, func, PCI_CONF_DEVID); @@ -1925,6 +1940,28 @@ &is_pci_bridge) == B_TRUE) pciex = 1; + bdf = PCI_GETBDF(bus, dev, func); + /* + * Record BAD AMD bridges which don't support MMIO config access. + */ + if (IS_BAD_AMD_NTBRIDGE(vendorid, deviceid) || + IS_AMD_8132_CHIP(vendorid, deviceid)) { + uchar_t secbus = 0; + uchar_t subbus = 0; + + if ((basecl == PCI_CLASS_BRIDGE) && + (subcl == PCI_BRIDGE_PCI)) { + secbus = pci_getb(bus, dev, func, PCI_BCNF_SECBUS); + subbus = pci_getb(bus, dev, func, PCI_BCNF_SUBBUS); + } + pci_cfgacc_add_workaround(bdf, secbus, subbus); + } + + if (mcfg_mem_base != NULL) { + ck804_fix_aer_ptr(dip, bdf); + (void) pcie_init_bus(dip, bdf, PCIE_BUS_INITIAL); + } + /* add properties */ (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "device-id", deviceid); (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "vendor-id", vendorid); @@ -3289,3 +3326,51 @@ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "slot-names", (int *)slotprop, len / sizeof (int)); } + +/* + * This is currently a hack, a better way is needed to determine if it + * is a PCIE platform. + */ +static boolean_t +is_pcie_platform() +{ + uint8_t bus; + + for (bus = 0; bus < pci_bios_maxbus; bus++) { + if (look_for_any_pciex_device(bus)) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Enable reporting of AER capability next pointer. + * This needs to be done only for CK8-04 devices + * by setting NV_XVR_VEND_CYA1 (offset 0xf40) bit 13 + * NOTE: BIOS is disabling this, it needs to be enabled temporarily + * + * This function is adapted from npe_ck804_fix_aer_ptr(), and is + * called from pci_boot.c. + */ +static void +ck804_fix_aer_ptr(dev_info_t *dip, pcie_req_id_t bdf) +{ + dev_info_t *rcdip; + ushort_t cya1; + + rcdip = pcie_get_rc_dip(dip); + ASSERT(rcdip != NULL); + + if ((pci_cfgacc_get16(rcdip, bdf, PCI_CONF_VENID) == + NVIDIA_VENDOR_ID) && + (pci_cfgacc_get16(rcdip, bdf, PCI_CONF_DEVID) == + NVIDIA_CK804_DEVICE_ID) && + (pci_cfgacc_get8(rcdip, bdf, PCI_CONF_REVID) >= + NVIDIA_CK804_AER_VALID_REVID)) { + cya1 = pci_cfgacc_get16(rcdip, bdf, NVIDIA_CK804_VEND_CYA1_OFF); + if (!(cya1 & ~NVIDIA_CK804_VEND_CYA1_ERPT_MASK)) + (void) pci_cfgacc_put16(rcdip, bdf, + NVIDIA_CK804_VEND_CYA1_OFF, + cya1 | NVIDIA_CK804_VEND_CYA1_ERPT_VAL); + } +}
--- a/usr/src/uts/intel/io/pci/pci_pci.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/io/pci/pci_pci.c Fri Dec 04 12:16:40 2009 +0800 @@ -684,7 +684,7 @@ * errors. */ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { - if (pcie_init_bus(child) == NULL) + if (pcie_init_cfghdl(child) != DDI_SUCCESS) return (DDI_FAILURE); } @@ -726,7 +726,7 @@ ddi_get_instance(ddi_get_parent(dip))); if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) - pcie_fini_bus(dip); + pcie_fini_cfghdl(dip); else if ((pdptr = ddi_get_parent_data(dip)) != NULL) { kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); ddi_set_parent_data(dip, NULL);
--- a/usr/src/uts/intel/io/pciex/pcie_nvidia.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/io/pciex/pcie_nvidia.c Fri Dec 04 12:16:40 2009 +0800 @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,17 +32,19 @@ #include <sys/pci.h> #include <sys/sunndi.h> #include <sys/pcie.h> +#include <sys/pcie_impl.h> #include <sys/pci_cfgspace.h> #include <io/pciex/pcie_nvidia.h> /* * PCI Configuration (Nvidia chipsets, PCIe) related library functions */ -static boolean_t look_for_any_pciex_device(uchar_t); /* Globals */ extern int pci_boot_debug; +extern uint64_t mcfg_mem_base; + boolean_t check_if_device_is_pciex(dev_info_t *cdip, uchar_t bus, uchar_t dev, uchar_t func, ushort_t *slot_number, ushort_t *is_pci_bridge) @@ -125,7 +127,7 @@ * PCI-Express device in the system. * If found, return B_TRUE else B_FALSE */ -static boolean_t +boolean_t look_for_any_pciex_device(uchar_t bus) { uchar_t dev, func; @@ -170,10 +172,11 @@ return (B_FALSE); } - boolean_t create_pcie_root_bus(uchar_t bus, dev_info_t *dip) { + pcie_bus_t *bus_p; + /* * Currently this is being hard-coded. * We need to figure out if the root bus does indeed @@ -192,6 +195,12 @@ (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "compatible", "pciex_root_complex"); + pcie_rc_init_bus(dip); + + /* save base addr in bus_t for pci_cfgacc_xxx() */ + bus_p = PCIE_DIP2BUS(dip); + bus_p->bus_cfgacc_base = mcfg_mem_base; + return (B_TRUE); }
--- a/usr/src/uts/intel/io/pciex/pcie_nvidia.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/io/pciex/pcie_nvidia.h Fri Dec 04 12:16:40 2009 +0800 @@ -20,15 +20,13 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _PCIEX_PCI_NVIDIA_H #define _PCIEX_PCI_NVIDIA_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -36,6 +34,7 @@ /* * PCI Configuration (Nvidia, PCIe) related library functions */ +boolean_t look_for_any_pciex_device(uchar_t); boolean_t check_if_device_is_pciex(dev_info_t *, uchar_t, uchar_t, uchar_t, ushort_t *, ushort_t *); boolean_t create_pcie_root_bus(uchar_t, dev_info_t *);
--- a/usr/src/uts/intel/pci_autoconfig/Makefile Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/intel/pci_autoconfig/Makefile Fri Dec 04 12:16:40 2009 +0800 @@ -56,7 +56,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # -# Depends on acpica ACPI CA interpreter +# Depends on acpica ACPI CA interpreter and PCI-E framework # LDFLAGS += -dy -Nmisc/acpica -Nmisc/pcie
--- a/usr/src/uts/sparc/Makefile.sparc.shared Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sparc/Makefile.sparc.shared Fri Dec 04 12:16:40 2009 +0800 @@ -287,7 +287,7 @@ DRV_KMODS += hci1394 av1394 scsa1394 dcam1394 DRV_KMODS += sbp2 DRV_KMODS += ib ibd rdsib sdp iser daplt hermon tavor -DRV_KMODS += pci_pci pcieb pcieb_bcm pcie +DRV_KMODS += pci_pci pcieb pcieb_bcm DRV_KMODS += i8042 kb8042 mouse8042 DRV_KMODS += fcode DRV_KMODS += mpt_sas
--- a/usr/src/uts/sparc/pcie/Makefile Thu Dec 03 19:18:04 2009 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# uts/sparc/pcie/Makefile -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# This makefile drives the production of the PCIE driver kernel module. -# -# sparc architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = pcie -OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \ - $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \ - $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) - -# -# Include common rules. -# -include $(UTSBASE)/sparc/Makefile.sparc - -# -# Define targets -# -ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Dependency -# -LDFLAGS += -dy -Nmisc/busra - -# -# Overrides -# -MODSTUBS_DIR = $(OBJS_DIR) -$(MODSTUBS_O) := AS_CPPFLAGS += -DPCIE_MODULE -CLEANFILES += $(MODSTUBS_O) - -# -# For now, disable these lint checks; maintainers should endeavor -# to investigate and remove these for maximum lint coverage. -# Please do not carry these forward to new Makefiles. -# -LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN -LINTTAGS += -erroff=E_STATIC_UNUSED -LINTTAGS += -erroff=E_SUSPICIOUS_COMPARISON - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# Include common targets. -# -include $(UTSBASE)/sparc/Makefile.targ
--- a/usr/src/uts/sun4/io/pcicfg.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4/io/pcicfg.c Fri Dec 04 12:16:40 2009 +0800 @@ -46,6 +46,7 @@ #include <sys/pci_cap.h> #include <sys/hotplug/pci/pcicfg.h> #include <sys/ndi_impldefs.h> +#include <sys/pci_cfgacc.h> #define PCICFG_DEVICE_TYPE_PCI 1 #define PCICFG_DEVICE_TYPE_PCIE 2 @@ -263,7 +264,7 @@ static int pcicfg_add_config_reg(dev_info_t *, uint_t, uint_t, uint_t); static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t, - uint_t *, pcicfg_flags_t); + uint_t *, pcicfg_flags_t, boolean_t); #ifdef PCICFG_INTERPRET_FCODE static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t, @@ -271,9 +272,9 @@ #endif static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t, - uint_t *, pcicfg_flags_t); + uint_t *, pcicfg_flags_t, boolean_t); static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t, - uint_t *); + uint_t *, boolean_t); static int pcicfg_free_all_resources(dev_info_t *); static int pcicfg_alloc_new_resources(dev_info_t *); static int pcicfg_match_dev(dev_info_t *, void *); @@ -302,7 +303,7 @@ static int pcicfg_set_busnode_props(dev_info_t *, uint8_t, int, int); static int pcicfg_free_bridge_resources(dev_info_t *); static int pcicfg_free_device_resources(dev_info_t *, pcicfg_flags_t); -static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t); +static int pcicfg_teardown_device(dev_info_t *, pcicfg_flags_t, boolean_t); static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *); static void pcicfg_config_teardown(ddi_acc_handle_t *); static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *); @@ -329,6 +330,7 @@ static int pcicfg_populate_props_from_bar(dev_info_t *, ddi_acc_handle_t); static int pcicfg_update_assigned_prop_value(dev_info_t *, uint32_t, uint32_t, uint32_t, uint_t); +static boolean_t is_pcie_fabric(dev_info_t *dip); #ifdef DEBUG static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle); @@ -721,6 +723,7 @@ uint_t highest_bus = 0; int ari_mode = B_FALSE; int max_function = PCICFG_MAX_FUNCTION; + boolean_t is_pcie; if (flags == PCICFG_FLAG_ENABLE_ARI) return (pcicfg_ari_configure(devi)); @@ -738,6 +741,8 @@ bus = pci_bus_range.lo; /* primary bus number of this bus node */ + is_pcie = is_pcie_fabric(devi); + ndi_devi_enter(devi, &circ); for (func = 0; func < max_function; ) { if ((function != PCICFG_ALL_FUNC) && (function != func)) @@ -755,7 +760,7 @@ * Try executing fcode if available. */ switch (rv = pcicfg_fcode_probe(devi, bus, trans_device, - func & 7, &highest_bus, flags)) { + func & 7, &highest_bus, flags, is_pcie)) { case PCICFG_FAILURE: DEBUG2("configure failed: " "bus [0x%x] device [0x%x]\n", @@ -865,6 +870,16 @@ * probe handle - if not, no harm in calling this. */ (void) pcicfg_destroy_phdl(new_device); + + if (is_pcie) { + /* + * Free bus_t structure + */ + if (ddi_get_child(new_device) != NULL) + pcie_fab_fini_bus(new_device, PCIE_BUS_ALL); + + pcie_fini_bus(new_device, PCIE_BUS_ALL); + } /* * This will free up the node */ @@ -1529,6 +1544,8 @@ int i; int max_function; int trans_device; + int circ; + boolean_t is_pcie; if (pcie_ari_is_enabled(devi) == PCIE_ARI_FORW_ENABLED) max_function = PCICFG_MAX_ARI_FUNCTION; @@ -1539,6 +1556,9 @@ * Cycle through devices to make sure none are busy. * If a single device is busy fail the whole unconfigure. */ + is_pcie = is_pcie_fabric(devi); + + ndi_devi_enter(devi, &circ); for (func = 0; func < max_function; func++) { if (max_function == PCICFG_MAX_ARI_FUNCTION) @@ -1583,10 +1603,10 @@ if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { DEBUG0("Failed to put back devices state\n"); - return (PCICFG_FAILURE); + goto fail; } } - return (PCICFG_FAILURE); + goto fail; } /* @@ -1613,15 +1633,15 @@ PCICFG_SUCCESS) { cmn_err(CE_WARN, "ntbridge: unconfigure failed\n"); - return (PCICFG_FAILURE); + goto fail; } - if (pcicfg_teardown_device(child_dip, flags) + if (pcicfg_teardown_device(child_dip, flags, is_pcie) != PCICFG_SUCCESS) { DEBUG2("Failed to tear down device [0x%x]" "function [0x%x]\n", trans_device, func & 7); - return (PCICFG_FAILURE); + goto fail; } } @@ -1630,11 +1650,16 @@ (void) pcie_ari_disable(devi); } + ndi_devi_exit(devi, circ); return (PCICFG_SUCCESS); + +fail: + ndi_devi_exit(devi, circ); + return (PCICFG_FAILURE); } static int -pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags) +pcicfg_teardown_device(dev_info_t *dip, pcicfg_flags_t flags, boolean_t is_pcie) { ddi_acc_handle_t config_handle; @@ -1657,6 +1682,16 @@ pci_config_teardown(&config_handle); /* + * free pcie_bus_t for the sub-tree + */ + if (is_pcie) { + if (ddi_get_child(dip) != NULL) + pcie_fab_fini_bus(dip, PCIE_BUS_ALL); + + pcie_fini_bus(dip, PCIE_BUS_ALL); + } + + /* * The framework provides this routine which can * tear down a sub-tree. */ @@ -3966,7 +4001,7 @@ static int pcicfg_probe_children(dev_info_t *parent, uint_t bus, uint_t device, - uint_t func, uint_t *highest_bus, pcicfg_flags_t flags) + uint_t func, uint_t *highest_bus, pcicfg_flags_t flags, boolean_t is_pcie) { dev_info_t *new_child; ddi_acc_handle_t config_handle; @@ -4008,6 +4043,10 @@ goto failedconfig; } + if (is_pcie) + (void) pcie_init_bus(new_child, PCI_GETBDF(bus, device, func), + PCIE_BUS_INITIAL); + /* * As soon as we have access to config space, * turn off device. It will get turned on @@ -4071,7 +4110,7 @@ goto failedchild; if (pcicfg_probe_bridge(new_child, config_handle, - bus, highest_bus) != PCICFG_SUCCESS) { + bus, highest_bus, is_pcie) != PCICFG_SUCCESS) { (void) pcicfg_free_bridge_resources(new_child); goto failedchild; } @@ -4125,11 +4164,21 @@ } (void) pcicfg_config_teardown(&config_handle); + + /* + * Properties have been setted up, so initilize the rest fields + * in bus_t. + */ + if (is_pcie) + pcie_init_bus(new_child, 0, PCIE_BUS_FINAL); + return (PCICFG_SUCCESS); failedchild: (void) pcicfg_config_teardown(&config_handle); + if (is_pcie) + pcie_fini_bus(new_child, PCIE_BUS_FINAL); failedconfig: @@ -4215,7 +4264,6 @@ } } - return (PCICFG_SUCCESS); failedchild: @@ -4224,7 +4272,7 @@ static int pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, uint_t device, - uint_t func, uint_t *highest_bus, pcicfg_flags_t flags) + uint_t func, uint_t *highest_bus, pcicfg_flags_t flags, boolean_t is_pcie) { dev_info_t *new_child; int8_t header_type; @@ -4332,6 +4380,11 @@ goto failed3; } #endif + + if (is_pcie) + (void) pcie_init_bus(new_child, PCI_GETBDF(bus, device, func), + PCIE_BUS_INITIAL); + DEBUG0("fcode_probe: conf space mapped.\n"); /* * As soon as we have access to config space, @@ -4396,7 +4449,7 @@ goto failed; if ((ret = pcicfg_probe_bridge(new_child, h, - bus, highest_bus)) != PCICFG_SUCCESS) + bus, highest_bus, is_pcie)) != PCICFG_SUCCESS) (void) pcicfg_free_bridge_resources(new_child); goto done; } else { @@ -4622,12 +4675,16 @@ #else pcicfg_unmap_phys(&h, &p); #endif + /* destroy the bus_t before the dev node is gone */ + if (is_pcie) + pcie_fini_bus(new_child, PCIE_BUS_FINAL); + (void) ndi_devi_free(new_child); DEBUG0("No Drop-in Probe device ourself\n"); ret = pcicfg_probe_children(parent, bus, device, func, - highest_bus, flags); + highest_bus, flags, is_pcie); if (ret != PCICFG_SUCCESS) { DEBUG0("Could not self probe child\n"); @@ -4680,7 +4737,8 @@ * warnings on retries of the failed configure. */ (void) pcicfg_ntbridge_unconfigure(new_child); - (void) pcicfg_teardown_device(new_child, flags); + (void) pcicfg_teardown_device(new_child, + flags, is_pcie); } #endif goto done2; @@ -4688,6 +4746,13 @@ } done: failed: + if (is_pcie) { + if (ret == PCICFG_SUCCESS) + pcie_init_bus(new_child, 0, PCIE_BUS_FINAL); + else + pcie_fini_bus(new_child, PCIE_BUS_FINAL); + } + #ifdef EFCODE21554 pcicfg_config_teardown(&h); #else @@ -4705,6 +4770,7 @@ } pci_config_teardown(&ph); } + return (ret); } @@ -4821,7 +4887,7 @@ static int pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, - uint_t *highest_bus) + uint_t *highest_bus, boolean_t is_pcie) { uint64_t next_bus; uint_t new_bus, num_slots; @@ -5154,6 +5220,8 @@ (void) pcicfg_device_on(h); + if (is_pcie) + pcie_init_bus(new_child, 0, PCIE_BUS_FINAL); if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG) != NDI_SUCCESS) { DEBUG0("Unable to online bridge\n"); @@ -5183,7 +5251,8 @@ trans_device = i; if ((rval = pcicfg_fcode_probe(new_child, - new_bus, trans_device, (j & 7), highest_bus, 0)) + new_bus, trans_device, (j & 7), highest_bus, + 0, is_pcie)) != PCICFG_SUCCESS) { if (rval == PCICFG_NODEVICE) { DEBUG3("No Device at bus [0x%x]" @@ -5268,6 +5337,8 @@ */ VERIFY(ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG) == NDI_SUCCESS); + if (is_pcie) + pcie_fini_bus(new_child, PCIE_BUS_INITIAL); phdl.dip = new_child; phdl.memory_base = mem_answer; @@ -6753,3 +6824,33 @@ cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); } #endif + +/* + * Return true if the devinfo node is in a PCI Express hierarchy. + */ +static boolean_t +is_pcie_fabric(dev_info_t *dip) +{ + dev_info_t *root = ddi_root_node(); + dev_info_t *pdip; + boolean_t found = B_FALSE; + char *bus; + + /* + * Does this device reside in a pcie fabric ? + */ + for (pdip = dip; pdip && (pdip != root) && !found; + pdip = ddi_get_parent(pdip)) { + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &bus) != + DDI_PROP_SUCCESS) + break; + + if (strcmp(bus, "pciex") == 0) + found = B_TRUE; + + ddi_prop_free(bus); + } + + return (found); +}
--- a/usr/src/uts/sun4/io/px/px.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4/io/px/px.c Fri Dec 04 12:16:40 2009 +0800 @@ -42,6 +42,7 @@ #include <sys/pci_tools.h> #include "px_tools_ext.h" #include <sys/pcie_pwr.h> +#include <sys/pci_cfgacc.h> /*LINTLIBRARY*/ @@ -56,11 +57,11 @@ static void px_cb_detach(px_t *); static int px_pwr_setup(dev_info_t *dip); static void px_pwr_teardown(dev_info_t *dip); - static void px_set_mps(px_t *px_p); +extern void pci_cfgacc_acc(pci_cfgacc_req_t *); extern int pcie_max_mps; - +extern void (*pci_cfgacc_acc_p)(pci_cfgacc_req_t *); /* * bus ops and dev ops structures: */ @@ -224,11 +225,15 @@ int ret = DDI_SUCCESS; devhandle_t dev_hdl = NULL; pcie_hp_regops_t regops; + pcie_bus_t *bus_p; switch (cmd) { case DDI_ATTACH: DBG(DBG_ATTACH, dip, "DDI_ATTACH\n"); + /* See pci_cfgacc.c */ + pci_cfgacc_acc_p = pci_cfgacc_acc; + /* * Allocate and get the per-px soft state structure. */ @@ -341,6 +346,25 @@ ddi_report_dev(dip); px_p->px_state = PX_ATTACHED; + + /* + * save base addr in bus_t for pci_cfgacc_xxx(), this + * depends of px structure being properly initialized. + */ + bus_p = PCIE_DIP2BUS(dip); + bus_p->bus_cfgacc_base = px_lib_get_cfgacc_base(dip); + + /* + * Partially populate bus_t for all devices in this fabric + * for device type macros to work. + */ + /* + * Populate bus_t for all devices in this fabric, after FMA + * is initializated, so that config access errors could + * trigger panic. + */ + pcie_fab_init_bus(dip, PCIE_BUS_ALL); + DBG(DBG_ATTACH, dip, "attach success\n"); break; @@ -449,6 +473,9 @@ return (DDI_FAILURE); } + /* Destroy bus_t for the whole fabric */ + pcie_fab_fini_bus(dip, PCIE_BUS_ALL); + /* * things which used to be done in obj_destroy * are now in-lined here.
--- a/usr/src/uts/sun4/io/px/px_ioapi.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4/io/px/px_ioapi.h Fri Dec 04 12:16:40 2009 +0800 @@ -232,19 +232,6 @@ IO_SYNC_CPU = (uint32_t)0x02 } io_sync_direction_t; -typedef enum pci_config_size { - PCI_CFG_SIZE_BYTE = 0, - PCI_CFG_SIZE_WORD, - PCI_CFG_SIZE_DWORD -} pci_config_size_t; - -typedef union pci_cfg_data { - uint8_t b; - uint16_t w; - uint32_t dw; - uint64_t qw; -} pci_cfg_data_t; - /* * MSI Definitions *
--- a/usr/src/uts/sun4/io/px/px_lib.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4/io/px/px_lib.h Fri Dec 04 12:16:40 2009 +0800 @@ -190,10 +190,6 @@ extern void px_err_rem_intr(px_fault_t *px_fault_p); extern int px_cb_add_intr(px_fault_t *); extern void px_cb_rem_intr(px_fault_t *); -extern uint32_t px_fab_get(px_t *px_p, pcie_req_id_t bdf, - uint16_t offset); -extern void px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, - uint32_t val); /* * CPR callback @@ -214,6 +210,11 @@ extern int px_lib_get_root_complex_mps(px_t *px_p, dev_info_t *dip, int *mps); extern int px_lib_set_root_complex_mps(px_t *px_p, dev_info_t *dip, int mps); +/* + * Config space access + */ +extern uint64_t px_lib_get_cfgacc_base(dev_info_t *dip); + #ifdef __cplusplus } #endif
--- a/usr/src/uts/sun4/io/px/px_util.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4/io/px/px_util.c Fri Dec 04 12:16:40 2009 +0800 @@ -525,8 +525,7 @@ ddi_node_name(child), ddi_get_name_addr(child)); ddi_set_parent_data(child, (void *)ppd); - if (pcie_init_bus(child)) - (void) pcie_initchild(child); + (void) pcie_initchild(child); /* * Handle chip specific init-child tasks.
--- a/usr/src/uts/sun4u/Makefile.files Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/Makefile.files Fri Dec 04 12:16:40 2009 +0800 @@ -182,6 +182,8 @@ SBD_OBJS += sbd.o sbd_cpu.o sbd_mem.o sbd_io.o +PCIE_MISC_OBJS += pci_cfgacc_4u.o pci_cfgacc.o + # # Brand modules #
--- a/usr/src/uts/sun4u/Makefile.rules Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/Makefile.rules Fri Dec 04 12:16:40 2009 +0800 @@ -92,6 +92,13 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/sun4u/io/px/%.s $(COMPILE.s) -o $@ $< +$(OBJS_DIR)/%.o: $(UTSBASE)/sun4u/io/pciex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/sun4u/io/pciex/%.s + $(COMPILE.s) -o $@ $< + $(OBJS_DIR)/%.o: $(UTSBASE)/sun4u/ml/%.s $(COMPILE.s) -o $@ $< @@ -212,6 +219,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/sun4u/io/px/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4u/io/pciex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4u/io/pciex/%.s + @($(LHEAD) $(LINT.s) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/sun4u/ml/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL))
--- a/usr/src/uts/sun4u/Makefile.sun4u.shared Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/Makefile.sun4u.shared Fri Dec 04 12:16:40 2009 +0800 @@ -434,6 +434,7 @@ MISC_KMODS += opl_cfg MISC_KMODS += zuluvm MISC_KMODS += gptwo_cpu gptwocfg +MISC_KMODS += pcie # # Brand modules
--- a/usr/src/uts/sun4u/io/pci/pci_pci.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/io/pci/pci_pci.c Fri Dec 04 12:16:40 2009 +0800 @@ -963,7 +963,7 @@ * errors. */ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { - if (pcie_init_bus(child) == NULL) { + if (pcie_init_cfghdl(child) != DDI_SUCCESS) { pci_config_teardown(&config_handle); return (DDI_FAILURE); } @@ -997,7 +997,7 @@ * SG OPL FMA specific */ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) - pcie_fini_bus(child); + pcie_fini_cfghdl(child); ppb_removechild(child); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4u/io/pciex/pci_cfgacc_4u.c Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,139 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/promif.h> +#include <sys/pci.h> +#include <sys/sysmacros.h> +#include <sys/pcie_impl.h> +#include <sys/machsystm.h> +#include <sys/byteorder.h> +#include <sys/pci_cfgacc.h> + +#define PCI_CFG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) +#define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) + +/* RC BDF Shift in a Phyiscal Address */ +#define RC_PA_BDF_SHIFT 12 +#define RC_BDF_TO_CFGADDR(bdf, offset) (((bdf) << RC_PA_BDF_SHIFT) + (offset)) + +static boolean_t +pci_cfgacc_valid(pci_cfgacc_req_t *req) +{ + /* do not support 64 bit pci config space access */ + return (IS_P2ALIGNED(req->offset, req->size) && + (req->offset < PCIE_CFG_SPACE_SIZE) && + ((req->size == 1) || (req->size == 2) || + (req->size == 4) || (req->size == 8))); +} + +/* + * Unprotected raw reads/writes of fabric device's config space. + */ +static uint64_t +pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size) +{ + pcie_bus_t *bus_p; + uint64_t base_addr; + uint64_t val; + + if ((bus_p = PCIE_DIP2DOWNBUS(dip)) == NULL) + return ((uint64_t)-1); + + base_addr = bus_p->bus_cfgacc_base; + base_addr += RC_BDF_TO_CFGADDR(bdf, offset); + + switch (size) { + case 1: + val = ldbphysio(base_addr); + break; + case 2: + val = ldhphysio(base_addr); + break; + case 4: + val = ldphysio(base_addr); + break; + case 8: + val = lddphysio(base_addr); + break; + default: + return ((uint64_t)-1); + } + + return (LE_64(val)); +} + +static void +pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size, + uint64_t val) +{ + pcie_bus_t *bus_p; + uint64_t base_addr; + + if ((bus_p = PCIE_DIP2DOWNBUS(dip)) == NULL) + return; + + base_addr = bus_p->bus_cfgacc_base; + base_addr += RC_BDF_TO_CFGADDR(bdf, offset); + + val = LE_64(val); + + switch (size) { + case 1: + stbphysio(base_addr, val); + break; + case 2: + sthphysio(base_addr, val); + break; + case 4: + stphysio(base_addr, val); + break; + case 8: + stdphysio(base_addr, val); + break; + default: + break; + } +} + +void +pci_cfgacc_acc(pci_cfgacc_req_t *req) +{ + /* is request valid? */ + if (!pci_cfgacc_valid(req)) { + if (!req->write) + VAL64(req) = (uint64_t)-1; + return; + } + + if (req->write) { + pci_cfgacc_set(req->rcdip, req->bdf, req->offset, + req->size, VAL64(req)); + } else { + VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf, + req->offset, req->size); + } +}
--- a/usr/src/uts/sun4u/io/px/px_lib4u.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/io/px/px_lib4u.c Fri Dec 04 12:16:40 2009 +0800 @@ -2344,45 +2344,6 @@ #endif /* FMA */ /* - * Unprotected raw reads/writes of fabric device's config space. - * Only used for temporary PCI-E Fabric Error Handling. - */ -uint32_t -px_fab_get(px_t *px_p, pcie_req_id_t bdf, uint16_t offset) -{ - pci_ranges_t *rp = px_p->px_ranges_p; - uint64_t range_prop, base_addr; - int bank = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); - uint32_t val; - - /* Get Fire's Physical Base Address */ - range_prop = px_get_range_prop(px_p, rp, bank); - - /* Get config space first. */ - base_addr = range_prop + PX_BDF_TO_CFGADDR(bdf, offset); - - val = ldphysio(base_addr); - - return (LE_32(val)); -} - -void -px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, - uint32_t val) { - pci_ranges_t *rp = px_p->px_ranges_p; - uint64_t range_prop, base_addr; - int bank = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); - - /* Get Fire's Physical Base Address */ - range_prop = px_get_range_prop(px_p, rp, bank); - - /* Get config space first. */ - base_addr = range_prop + PX_BDF_TO_CFGADDR(bdf, offset); - - stphysio(base_addr, LE_32(val)); -} - -/* * cpr callback * * disable fabric error msg interrupt prior to suspending @@ -2521,6 +2482,22 @@ } /* + * fetch the config space base addr of the root complex + * note this depends on px structure being initialized + */ +uint64_t +px_lib_get_cfgacc_base(dev_info_t *dip) +{ + int instance = DIP_TO_INST(dip); + px_t *px_p = INST_TO_STATE(instance); + pci_ranges_t *rp = px_p->px_ranges_p; + int bank = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); + + /* Get Fire's Physical Base Address */ + return (px_get_range_prop(px_p, rp, bank)); +} + +/* * add cpr callback */ void
--- a/usr/src/uts/sun4u/io/px/px_lib4u.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4u/io/px/px_lib4u.h Fri Dec 04 12:16:40 2009 +0800 @@ -280,10 +280,6 @@ /* TLU Control register bits */ #define TLU_REMAIN_DETECT_QUIET 8 -/* PX BDF Shift in a Phyiscal Address - used FMA Fabric only */ -#define PX_PA_BDF_SHIFT 12 -#define PX_BDF_TO_CFGADDR(bdf, offset) (((bdf) << PX_PA_BDF_SHIFT) + (offset)) - /* * Fire hardware specific version definitions. * All Fire versions > 2.0 will be numerically greater than FIRE_MOD_REV_20
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4u/pcie/Makefile Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,90 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# uts/sun4u/pcie/Makefile +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# This makefile drives the production of the sun4u/kernel/misc/sparcv9/pcie module +# for PCI-E Error handling support in PCI-E nexus drivers. +# +# sun4u implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcie +OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \ + $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \ + $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sun4u/Makefile.sun4u + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +# +LDFLAGS += -dy -Nmisc/busra + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sun4u/Makefile.targ
--- a/usr/src/uts/sun4v/Makefile.files Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/Makefile.files Fri Dec 04 12:16:40 2009 +0800 @@ -169,6 +169,7 @@ FAULT_ISO_OBJS = fault_iso.o OBPSYM_OBJS += obpsym.o obpsym_1275.o PLATSVC_OBJS = platsvc.o mdeg.o +PCIE_MISC_OBJS += pci_cfgacc_4v.o pci_cfgacc_asm.o pci_cfgacc.o # # Brand modules
--- a/usr/src/uts/sun4v/Makefile.rules Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/Makefile.rules Fri Dec 04 12:16:40 2009 +0800 @@ -98,6 +98,13 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/io/glvc/%.s $(COMPILE.s) -o $@ $< +$(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/io/pciex/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/io/pciex/%.s + $(COMPILE.s) -o $@ $< + $(OBJS_DIR)/%.o: $(UTSBASE)/sun4v/vm/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -202,6 +209,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/n2rng/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/pciex/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/io/pciex/%.s + @($(LHEAD) $(LINT.s) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/sun4v/ml/%.s @($(LHEAD) $(LINT.s) $< $(LTAIL))
--- a/usr/src/uts/sun4v/Makefile.sun4v.shared Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/Makefile.sun4v.shared Fri Dec 04 12:16:40 2009 +0800 @@ -396,6 +396,7 @@ MISC_KMODS += platmod MISC_KMODS += platsvc MISC_KMODS += vis +MISC_KMODS += pcie # md5 optimized for Niagara #
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/io/pciex/pci_cfgacc_4v.c Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,130 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Common PCI configuration space access routines + */ + +#include <sys/types.h> +#include <sys/ddi.h> +#include <sys/promif.h> +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/kmem.h> +#include <sys/obpdefs.h> +#include <sys/sysmacros.h> +#include <sys/pci.h> +#include <sys/spl.h> +#include <sys/pcie_impl.h> +#include <sys/pci_cfgacc_4v.h> + +#define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4) + +/* RC BDF Shift in a Phyiscal Address */ +#define RC_RA_BDF_SHIFT 8 + +static boolean_t +pci_cfgacc_valid(pci_cfgacc_req_t *req) +{ + /* do not support 64 bit pci config space access */ + return (IS_P2ALIGNED(req->offset, req->size) && + (req->offset < PCIE_CFG_SPACE_SIZE) && + ((req->size == 1) || (req->size == 2) || + (req->size == 4) || (req->size == 8))); +} + +/* + * Unprotected raw reads/writes of fabric device's config space. + */ +static uint64_t +pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size) +{ + pcie_bus_t *bus_p; + uint64_t devhdl; + uint64_t devaddr; + uint64_t data = 0; + + if ((bus_p = PCIE_DIP2DOWNBUS(dip)) == NULL) + return ((uint64_t)-1); + + devhdl = bus_p->bus_cfgacc_base; + devaddr = ((uint64_t)bdf) << RC_RA_BDF_SHIFT; + + (void) hvio_config_get(devhdl, devaddr, + offset, size, (pci_cfg_data_t *)&data); + + return (data); +} + +static void +pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size, + uint64_t val) +{ + pcie_bus_t *bus_p; + uint64_t devhdl; + uint64_t devaddr; + pci_cfg_data_t wdata = { 0 }; + + if ((bus_p = PCIE_DIP2DOWNBUS(dip)) == NULL) + return; + + devhdl = bus_p->bus_cfgacc_base; + devaddr = ((uint64_t)bdf) << RC_RA_BDF_SHIFT; + + wdata.qw = val; + (void) hvio_config_put(devhdl, devaddr, offset, size, wdata); +} + +void +pci_cfgacc_acc(pci_cfgacc_req_t *req) +{ + /* is request valid? */ + if (!pci_cfgacc_valid(req)) { + if (!req->write) + VAL64(req) = (uint64_t)-1; + return; + } + + if (req->write) { + pci_cfgacc_set(req->rcdip, req->bdf, req->offset, + req->size, VAL64(req)); + } else { + VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf, + req->offset, req->size); + switch (req->size) { + case 1: + VAL8(req) = (uint8_t)VAL64(req); + break; + case 2: + VAL16(req) = (uint16_t)VAL64(req); + break; + case 4: + VAL32(req) = (uint32_t)VAL64(req); + break; + default: + break; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/io/pciex/pci_cfgacc_asm.s Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,89 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Assembly language support for pci config space access on sun4v + */ + +#include <sys/asm_linkage.h> +#include <sys/hypervisor_api.h> +#include <sys/dditypes.h> +#include <sys/pci_cfgacc.h> +#include <io/px/px_ioapi.h> +#include <io/px/px_lib4v.h> + +#if defined(lint) || defined(__lint) + +/*ARGSUSED*/ +uint64_t +hvio_config_get(devhandle_t dev_hdl, pci_device_t bdf, pci_config_offset_t off, + pci_config_size_t size, pci_cfg_data_t *data_p) +{ return (0); } + +/*ARGSUSED*/ +uint64_t +hvio_config_put(devhandle_t dev_hdl, pci_device_t bdf, pci_config_offset_t off, + pci_config_size_t size, pci_cfg_data_t data) +{ return (0); } + +#else /* lint || __lint */ + + /* + * arg0 - devhandle + * arg1 - pci_device + * arg2 - pci_config_offset + * arg3 - pci_config_size (1, 2 or 4 byte) + * + * ret0 - status + * ret1 - error_flag + * ret2 - pci_cfg_data + */ + ENTRY(hvio_config_get) + mov HVIO_CONFIG_GET, %o5 + ta FAST_TRAP + movrnz %o1, -1, %o2 + stx %o2, [%o4] + retl + nop + SET_SIZE(hvio_config_get) + + /* + * arg0 - devhandle + * arg1 - pci_device + * arg2 - pci_config_offset + * arg3 - pci_config_size (1, 2 or 4 byte) + * arg4 - pci_cfg_data + * + * ret0 - status + * ret1 - error_flag + */ + ENTRY(hvio_config_put) + mov HVIO_CONFIG_PUT, %o5 + ta FAST_TRAP + retl + nop + SET_SIZE(hvio_config_put) +#endif
--- a/usr/src/uts/sun4v/io/px/px_hcall.s Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/io/px/px_hcall.s Fri Dec 04 12:16:40 2009 +0800 @@ -38,18 +38,6 @@ /*ARGSUSED*/ uint64_t -hvio_config_get(devhandle_t dev_hdl, pci_device_t bdf, pci_config_offset_t off, - pci_config_size_t size, pci_cfg_data_t *data_p) -{ return (0); } - -/*ARGSUSED*/ -uint64_t -hvio_config_put(devhandle_t dev_hdl, pci_device_t bdf, pci_config_offset_t off, - pci_config_size_t size, pci_cfg_data_t data) -{ return (0); } - -/*ARGSUSED*/ -uint64_t hvio_iommu_map(devhandle_t dev_hdl, tsbid_t tsbid, pages_t pages, io_attributes_t attr, io_page_list_t *io_page_list_p, pages_t *pages_mapped) @@ -218,42 +206,6 @@ /* * arg0 - devhandle - * arg1 - pci_device - * arg2 - pci_config_offset - * arg3 - pci_config_size - * - * ret0 - status - * ret1 - error_flag - * ret2 - pci_cfg_data - */ - ENTRY(hvio_config_get) - mov HVIO_CONFIG_GET, %o5 - ta FAST_TRAP - movrnz %o1, -1, %o2 - stx %o2, [%o4] - retl - nop - SET_SIZE(hvio_config_get) - - /* - * arg0 - devhandle - * arg1 - pci_device - * arg2 - pci_config_offset - * arg3 - pci_config_size - * arg4 - pci_cfg_data - * - * ret0 - status - * ret1 - error_flag - */ - ENTRY(hvio_config_put) - mov HVIO_CONFIG_PUT, %o5 - ta FAST_TRAP - retl - nop - SET_SIZE(hvio_config_put) - - /* - * arg0 - devhandle * arg1 - tsbid * arg2 - pages * arg3 - io_attributes
--- a/usr/src/uts/sun4v/io/px/px_lib4v.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/io/px/px_lib4v.c Fri Dec 04 12:16:40 2009 +0800 @@ -40,6 +40,9 @@ #include <sys/machsystm.h> #include "px_lib4v.h" #include "px_err.h" +#include <sys/pci_cfgacc.h> +#include <sys/pci_cfgacc_4v.h> + /* mask for the ranges property in calculating the real PFN range */ uint_t px_ranges_phi_mask = ((1 << 28) -1); @@ -1929,28 +1932,16 @@ } /* - * Unprotected raw reads/writes of fabric device's config space. - * Only used for temporary PCI-E Fabric Error Handling. + * fetch the config space base addr of the root complex + * note this depends on px structure being initialized */ -uint32_t -px_fab_get(px_t *px_p, pcie_req_id_t bdf, uint16_t offset) { - uint64_t data = 0; - - (void) hvio_config_get(px_p->px_dev_hdl, - (bdf << PX_RA_BDF_SHIFT), offset, 4, - (pci_cfg_data_t *)&data); +uint64_t +px_lib_get_cfgacc_base(dev_info_t *dip) +{ + int instance = DIP_TO_INST(dip); + px_t *px_p = INST_TO_STATE(instance); - return ((uint32_t)data); -} - -void -px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, - uint32_t val) { - pci_cfg_data_t wdata = { 0 }; - - wdata.qw = (uint32_t)val; - (void) hvio_config_put(px_p->px_dev_hdl, - (bdf << PX_RA_BDF_SHIFT), offset, 4, wdata); + return (px_p->px_dev_hdl); } /*ARGSUSED*/
--- a/usr/src/uts/sun4v/io/px/px_lib4v.h Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/io/px/px_lib4v.h Fri Dec 04 12:16:40 2009 +0800 @@ -79,9 +79,6 @@ */ #define DEVHDLE_MASK 0xFFFFFFF -/* PX BDF Shift in a Phyiscal Address - used FMA Fabric only */ -#define PX_RA_BDF_SHIFT 8 - #define PX_ADDR2PFN(addr, index, flags, i) \ ((flags & MMU_MAP_PFN) ? \ PX_GET_MP_PFN((ddi_dma_impl_t *)(addr), (index + i)) : \ @@ -99,11 +96,6 @@ #define PX_VPCI_MINOR_VER_1 0x1ull #define PX_VPCI_MINOR_VER PX_VPCI_MINOR_VER_1 -extern uint64_t hvio_config_get(devhandle_t dev_hdl, pci_device_t bdf, - pci_config_offset_t off, pci_config_size_t size, pci_cfg_data_t *data_p); -extern uint64_t hvio_config_put(devhandle_t dev_hdl, pci_device_t bdf, - pci_config_offset_t off, pci_config_size_t size, pci_cfg_data_t data); - extern uint64_t hvio_iommu_map(devhandle_t dev_hdl, tsbid_t tsbid, pages_t pages, io_attributes_t attr, io_page_list_t *io_page_list_p, pages_t *pages_mapped);
--- a/usr/src/uts/sun4v/io/px/px_libhv.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/io/px/px_libhv.c Fri Dec 04 12:16:40 2009 +0800 @@ -36,6 +36,8 @@ #include <sys/errno.h> #include <sys/hypervisor_api.h> #include <sys/hsvc.h> +#include <sys/pci_cfgacc.h> +#include <sys/pci_cfgacc_4v.h> #include <px_obj.h> #include <sys/machsystm.h> #include "px_lib4v.h"
--- a/usr/src/uts/sun4v/io/px/px_tools_4v.c Thu Dec 03 19:18:04 2009 -0800 +++ b/usr/src/uts/sun4v/io/px/px_tools_4v.c Fri Dec 04 12:16:40 2009 +0800 @@ -31,6 +31,7 @@ #include <sys/hsvc.h> #include <px_obj.h> #include <sys/pci_tools.h> +#include <sys/pci_cfgacc.h> #include <px_tools_var.h> #include "px_lib4v.h" #include <px_tools_ext.h> @@ -215,7 +216,6 @@ * prg_p->phys_addr isn't used. */ -/*ARGSUSED*/ int pxtool_pcicfg_access(px_t *px_p, pcitool_reg_t *prg_p, uint64_t *data_p, boolean_t is_write) @@ -224,9 +224,15 @@ on_trap_data_t otd; dev_info_t *dip = px_p->px_dip; px_pec_t *pec_p = px_p->px_pec_p; - pci_device_t bdf = PX_GET_BDF(prg_p); size_t size = PCITOOL_ACC_ATTR_SIZE(prg_p->acc_attr); int rval = 0; + pci_cfgacc_req_t req; + + if ((size <= 0) || (size > 8)) { + DBG(DBG_TOOLS, dip, "not supported size.\n"); + prg_p->status = PCITOOL_INVALID_SIZE; + return (ENOTSUP); + } /* Alignment checking. */ if (!IS_P2ALIGNED(prg_p->offset, size)) { @@ -238,6 +244,11 @@ mutex_enter(&pec_p->pec_pokefault_mutex); pec_p->pec_ontrap_data = &otd; + req.rcdip = dip; + req.bdf = PCI_GETBDF(prg_p->bus_no, prg_p->dev_no, prg_p->func_no); + req.offset = prg_p->offset; + req.size = size; + req.write = is_write; if (is_write) { if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr)) @@ -259,16 +270,17 @@ break; } - DBG(DBG_TOOLS, dip, "put: bdf:0x%x, off:0x%" PRIx64 ", size:" - "0x%" PRIx64 ", data:0x%" PRIx64 "\n", - bdf, prg_p->offset, size, data.qw); + DBG(DBG_TOOLS, dip, "put: bdf:%d,%d,%d, off:0x%"PRIx64", size:" + "0x%"PRIx64", data:0x%"PRIx64"\n", + prg_p->bus_no, prg_p->dev_no, prg_p->func_no, + prg_p->offset, size, data.qw); pec_p->pec_safeacc_type = DDI_FM_ERR_POKE; if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&poke_fault; - rval = hvio_config_put(px_p->px_dev_hdl, bdf, - prg_p->offset, size, data); + VAL64(&req) = data.qw; + pci_cfgacc_acc(&req); } else rval = H_EIO; @@ -283,14 +295,29 @@ if (!on_trap(&otd, OT_DATA_ACCESS)) { otd.ot_trampoline = (uintptr_t)&peek_fault; - rval = hvio_config_get(px_p->px_dev_hdl, bdf, - prg_p->offset, size, &data); + pci_cfgacc_acc(&req); + data.qw = VAL64(&req); } else rval = H_EIO; - DBG(DBG_TOOLS, dip, "get: bdf:0x%x, off:0x%" PRIx64 ", size:" - "0x%" PRIx64 ", data:0x%" PRIx64 "\n", - bdf, prg_p->offset, size, data.qw); + switch (size) { + case sizeof (uint8_t): + data.qw = (uint64_t)data.b; + break; + case sizeof (uint16_t): + data.qw = (uint64_t)data.w; + break; + case sizeof (uint32_t): + data.qw = (uint64_t)data.dw; + break; + case sizeof (uint64_t): + break; + } + + DBG(DBG_TOOLS, dip, "get: bdf:%d,%d,%d, off:0x%"PRIx64", size:" + "0x%"PRIx64", data:0x%"PRIx64"\n", + prg_p->bus_no, prg_p->dev_no, prg_p->func_no, + prg_p->offset, size, data.qw); *data_p = data.qw; if (PCITOOL_ACC_IS_BIG_ENDIAN(prg_p->acc_attr))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/pcie/Makefile Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,90 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# uts/sun4v/pcie/Makefile +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# This makefile drives the production of the sun4v/kernel/misc/sparcv9/pcie module +# for PCI-E Error handling support in PCI-E nexus drivers. +# +# sun4v implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcie +OBJECTS = $(PCIE_MISC_OBJS:%=$(OBJS_DIR)/%) \ + $(PCI_STRING_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIE_MISC_OBJS:%.o=$(LINTS_DIR)/%.ln) \ + $(PCI_STRING_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sun4v/Makefile.sun4v + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +# +LDFLAGS += -dy -Nmisc/busra + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sun4v/Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/sun4v/sys/pci_cfgacc_4v.h Fri Dec 04 12:16:40 2009 +0800 @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_PCI_CFGACC_4V_H +#define _SYS_PCI_CFGACC_4V_H + +#include <sys/pci.h> +#include <sys/pci_cfgacc.h> +#include "../../sun4/io/px/px_ioapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint64_t hvio_config_get(devhandle_t, pci_device_t, pci_config_offset_t, + pci_config_size_t, pci_cfg_data_t *); +extern uint64_t hvio_config_put(devhandle_t, pci_device_t, pci_config_offset_t, + pci_config_size_t, pci_cfg_data_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_PCI_CFGACC_4V_H */