1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  *      Library file that has miscellaneous support for npe(7d)
  28  */
  29 
  30 #include <sys/conf.h>
  31 #include <sys/pci.h>
  32 #include <sys/sunndi.h>
  33 #include <sys/acpi/acpi.h>
  34 #include <sys/acpi/acpi_pci.h>
  35 #include <sys/acpica.h>
  36 #include <sys/pci_cap.h>
  37 #include <sys/pcie_impl.h>
  38 #include <sys/x86_archext.h>
  39 #include <io/pciex/pcie_nvidia.h>
  40 #include <io/pciex/pcie_nb5000.h>
  41 #include <sys/pci_cfgacc_x86.h>
  42 #include <sys/cpuvar.h>
  43 
  44 /*
  45  * Prototype declaration
  46  */
  47 void    npe_query_acpi_mcfg(dev_info_t *dip);
  48 void    npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl);
  49 int     npe_disable_empty_bridges_workaround(dev_info_t *child);
  50 void    npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl);
  51 void    npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl);
  52 boolean_t npe_is_child_pci(dev_info_t *dip);
  53 int     npe_enable_htmsi(ddi_acc_handle_t cfg_hdl);
  54 void    npe_enable_htmsi_children(dev_info_t *dip);
  55 
  56 int     npe_enable_htmsi_flag = 1;
  57 
  58 /*
  59  * Default ecfga base address
  60  */
  61 int64_t npe_default_ecfga_base = 0xE0000000;
  62 
  63 extern uint32_t npe_aer_uce_mask;
  64 
  65 /*
  66  * Query the MCFG table using ACPI.  If MCFG is found, setup the
  67  * 'ecfg' property accordingly.  Otherwise, set the values
  68  * to the default values.
  69  */
  70 void
  71 npe_query_acpi_mcfg(dev_info_t *dip)
  72 {
  73         MCFG_TABLE *mcfgp;
  74         CFG_BASE_ADDR_ALLOC *cfg_baap;
  75         char *cfg_baa_endp;
  76         int64_t ecfginfo[4];
  77         int ecfg_found = 0;
  78 
  79         /* Query the MCFG table using ACPI */
  80         if (AcpiGetTable(ACPI_SIG_MCFG, 1,
  81             (ACPI_TABLE_HEADER **)&mcfgp) == AE_OK) {
  82 
  83                 cfg_baap = (CFG_BASE_ADDR_ALLOC *)mcfgp->CfgBaseAddrAllocList;
  84                 cfg_baa_endp = ((char *)mcfgp) + mcfgp->Length;
  85 
  86                 while ((char *)cfg_baap < cfg_baa_endp) {
  87                         if (cfg_baap->base_addr != (uint64_t)0 &&
  88                             cfg_baap->segment == 0) {
  89                                 /*
  90                                  * Set up the 'ecfg' property to hold
  91                                  * base_addr, segment, and first/last bus.
  92                                  * We only do the first entry that maps
  93                                  * segment 0; nonzero segments are not yet
  94                                  * known, or handled.  If they appear,
  95                                  * we'll need to figure out which bus node
  96                                  * should have which entry by examining the
  97                                  * ACPI _SEG method on each bus node.
  98                                  */
  99                                 ecfginfo[0] = cfg_baap->base_addr;
 100                                 ecfginfo[1] = cfg_baap->segment;
 101                                 ecfginfo[2] = cfg_baap->start_bno;
 102                                 ecfginfo[3] = cfg_baap->end_bno;
 103                                 (void) ndi_prop_update_int64_array(
 104                                     DDI_DEV_T_NONE, dip, "ecfg",
 105                                     ecfginfo, 4);
 106                                 ecfg_found = 1;
 107                                 break;
 108                         }
 109                         cfg_baap++;
 110                 }
 111         }
 112         if (ecfg_found)
 113                 return;
 114         /*
 115          * If MCFG is not found or ecfga_base is not found in MCFG table,
 116          * set the property to the default values.
 117          */
 118         ecfginfo[0] = npe_default_ecfga_base;
 119         ecfginfo[1] = 0;                /* segment 0 */
 120         ecfginfo[2] = 0;                /* first bus 0 */
 121         ecfginfo[3] = 0xff;             /* last bus ff */
 122         (void) ndi_prop_update_int64_array(DDI_DEV_T_NONE, dip,
 123             "ecfg", ecfginfo, 4);
 124 }
 125 
 126 
 127 /*
 128  * Enable reporting of AER capability next pointer.
 129  * This needs to be done only for CK8-04 devices
 130  * by setting NV_XVR_VEND_CYA1 (offset 0xf40) bit 13
 131  * NOTE: BIOS is disabling this, it needs to be enabled temporarily
 132  */
 133 void
 134 npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl)
 135 {
 136         ushort_t cya1;
 137 
 138         if ((pci_config_get16(cfg_hdl, PCI_CONF_VENID) == NVIDIA_VENDOR_ID) &&
 139             (pci_config_get16(cfg_hdl, PCI_CONF_DEVID) ==
 140             NVIDIA_CK804_DEVICE_ID) &&
 141             (pci_config_get8(cfg_hdl, PCI_CONF_REVID) >=
 142             NVIDIA_CK804_AER_VALID_REVID)) {
 143                 cya1 =  pci_config_get16(cfg_hdl, NVIDIA_CK804_VEND_CYA1_OFF);
 144                 if (!(cya1 & ~NVIDIA_CK804_VEND_CYA1_ERPT_MASK))
 145                         (void) pci_config_put16(cfg_hdl,
 146                             NVIDIA_CK804_VEND_CYA1_OFF,
 147                             cya1 | NVIDIA_CK804_VEND_CYA1_ERPT_VAL);
 148         }
 149 }
 150 
 151 /*
 152  * If the bridge is empty, disable it
 153  */
 154 int
 155 npe_disable_empty_bridges_workaround(dev_info_t *child)
 156 {
 157         /*
 158          * Do not bind drivers to empty bridges.
 159          * Fail above, if the bridge is found to be hotplug capable
 160          */
 161         if (ddi_driver_major(child) == ddi_name_to_major("pcieb") &&
 162             ddi_get_child(child) == NULL &&
 163             ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
 164             "pci-hotplug-type", INBAND_HPC_NONE) == INBAND_HPC_NONE)
 165                 return (1);
 166 
 167         return (0);
 168 }
 169 
 170 void
 171 npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl) {
 172         uint32_t regs;
 173         uint16_t vendor_id = pci_config_get16(cfg_hdl, PCI_CONF_VENID);
 174         uint16_t dev_id = pci_config_get16(cfg_hdl, PCI_CONF_DEVID);
 175 
 176         if ((vendor_id == NVIDIA_VENDOR_ID) && NVIDIA_PCIE_RC_DEV_ID(dev_id)) {
 177                 /* Disable ECRC for all devices */
 178                 regs = pcie_get_aer_uce_mask() | npe_aer_uce_mask |
 179                     PCIE_AER_UCE_ECRC;
 180                 pcie_set_aer_uce_mask(regs);
 181 
 182                 /*
 183                  * Turn full scan on since the Error Source ID register may not
 184                  * have the correct ID.
 185                  */
 186                 pcie_force_fullscan();
 187         }
 188 }
 189 
 190 void
 191 npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl) {
 192         uint32_t regs;
 193         uint16_t vendor_id = pci_config_get16(cfg_hdl, PCI_CONF_VENID);
 194         uint16_t dev_id = pci_config_get16(cfg_hdl, PCI_CONF_DEVID);
 195 
 196         if (vendor_id == INTEL_VENDOR_ID) {
 197                 /*
 198                  * Due to an errata in Intel's ESB2 southbridge, all ECRCs
 199                  * generation/checking need to be disabled.  There is a
 200                  * workaround by setting a proprietary bit in the ESB2, but it
 201                  * is not well documented or understood.  If that bit is set in
 202                  * the future, then ECRC generation/checking should be enabled
 203                  * again.
 204                  *
 205                  * Disable ECRC generation/checking by masking ECRC in the AER
 206                  * UE Mask.  The pcie misc module would then automatically
 207                  * disable ECRC generation/checking in the AER Control register.
 208                  */
 209                 regs = pcie_get_aer_uce_mask() | PCIE_AER_UCE_ECRC;
 210                 pcie_set_aer_uce_mask(regs);
 211 
 212                 if (INTEL_NB5500_PCIE_DEV_ID(dev_id) ||
 213                     INTEL_NB5520_PCIE_DEV_ID(dev_id)) {
 214                         /*
 215                          * Turn full scan on since the Error Source ID register
 216                          * may not have the correct ID. See Intel 5520 and
 217                          * Intel 5500 Chipsets errata #34 and #54 in the August
 218                          * 2009 specification update, document number
 219                          * 321329-006.
 220                          */
 221                         pcie_force_fullscan();
 222                 }
 223         }
 224 }
 225 
 226 /*
 227  * Check's if this child is a PCI device.
 228  * Child is a PCI device if:
 229  * parent has a dev_type of "pci"
 230  * -and-
 231  * child does not have a dev_type of "pciex"
 232  *
 233  * If the parent is not of dev_type "pci", then assume it is "pciex" and all
 234  * children should support using PCIe style MMCFG access.
 235  *
 236  * If parent's dev_type is "pci" and child is "pciex", then also enable using
 237  * PCIe style MMCFG access.  This covers the case where NPE is "pci" and a PCIe
 238  * RP is beneath.
 239  */
 240 boolean_t
 241 npe_child_is_pci(dev_info_t *dip) {
 242         char *dev_type;
 243         boolean_t parent_is_pci, child_is_pciex;
 244 
 245         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(dip),
 246             DDI_PROP_DONTPASS, "device_type", &dev_type) ==
 247             DDI_PROP_SUCCESS) {
 248                 parent_is_pci = (strcmp(dev_type, "pci") == 0);
 249                 ddi_prop_free(dev_type);
 250         } else {
 251                 parent_is_pci = B_FALSE;
 252         }
 253 
 254         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 255             "device_type", &dev_type) == DDI_PROP_SUCCESS) {
 256                 child_is_pciex = (strcmp(dev_type, "pciex") == 0);
 257                 ddi_prop_free(dev_type);
 258         } else {
 259                 child_is_pciex = B_FALSE;
 260         }
 261 
 262         return (parent_is_pci && !child_is_pciex);
 263 }
 264 
 265 /*
 266  * Checks to see if MMCFG is supported.
 267  * Returns: TRUE if MMCFG is supported, FALSE if not.
 268  *
 269  * If a device is attached to a parent whose "dev_type" is "pciex",
 270  * the device will support MMCFG access.  Otherwise, use legacy IOCFG access.
 271  *
 272  * Enable Legacy PCI config space access for AMD K8 north bridges.
 273  *      Host bridge: AMD HyperTransport Technology Configuration
 274  *      Host bridge: AMD Address Map
 275  *      Host bridge: AMD DRAM Controller
 276  *      Host bridge: AMD Miscellaneous Control
 277  * These devices do not support MMCFG access.
 278  */
 279 boolean_t
 280 npe_is_mmcfg_supported(dev_info_t *dip)
 281 {
 282         int vendor_id, device_id;
 283 
 284         vendor_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 285             "vendor-id", -1);
 286         device_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
 287             "device-id", -1);
 288 
 289         return !(npe_child_is_pci(dip) ||
 290             IS_BAD_AMD_NTBRIDGE(vendor_id, device_id));
 291 }
 292 
 293 int
 294 npe_enable_htmsi(ddi_acc_handle_t cfg_hdl)
 295 {
 296         uint16_t ptr;
 297         uint16_t reg;
 298 
 299         if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
 300             PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
 301                 return (DDI_FAILURE);
 302 
 303         reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
 304         reg |= PCI_HTCAP_MSIMAP_ENABLE;
 305 
 306         pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
 307         return (DDI_SUCCESS);
 308 }
 309 
 310 void
 311 npe_enable_htmsi_children(dev_info_t *dip)
 312 {
 313         dev_info_t *cdip = ddi_get_child(dip);
 314         ddi_acc_handle_t cfg_hdl;
 315 
 316         if (!npe_enable_htmsi_flag)
 317                 return;
 318 
 319         /*
 320          * Hypertransport MSI remapping only applies to AMD CPUs using
 321          * Hypertransport (K8 and above) and not other platforms with non-AMD
 322          * CPUs that may be using Hypertransport internally in the chipset(s)
 323          */
 324         if (!(cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
 325             cpuid_getfamily(CPU) >= 0xf))
 326                 return;
 327 
 328         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 329                 if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS) {
 330                         cmn_err(CE_NOTE, "!npe_enable_htmsi_children: "
 331                             "pci_config_setup failed for %s",
 332                             ddi_node_name(cdip));
 333                         return;
 334                 }
 335 
 336                 (void) npe_enable_htmsi(cfg_hdl);
 337                 pci_config_teardown(&cfg_hdl);
 338         }
 339 }
 340 
 341 /*
 342  * save config regs for HyperTransport devices without drivers of classes:
 343  * memory controller and hostbridge
 344  */
 345 int
 346 npe_save_htconfig_children(dev_info_t *dip)
 347 {
 348         dev_info_t *cdip = ddi_get_child(dip);
 349         ddi_acc_handle_t cfg_hdl;
 350         uint16_t ptr;
 351         int rval = DDI_SUCCESS;
 352         uint8_t cl, scl;
 353 
 354         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 355                 if (ddi_driver_major(cdip) != DDI_MAJOR_T_NONE)
 356                         continue;
 357 
 358                 if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS)
 359                         return (DDI_FAILURE);
 360 
 361                 cl = pci_config_get8(cfg_hdl, PCI_CONF_BASCLASS);
 362                 scl = pci_config_get8(cfg_hdl, PCI_CONF_SUBCLASS);
 363 
 364                 if (((cl == PCI_CLASS_MEM && scl == PCI_MEM_RAM) ||
 365                     (cl == PCI_CLASS_BRIDGE && scl == PCI_BRIDGE_HOST)) &&
 366                     pci_htcap_locate(cfg_hdl, 0, 0, &ptr) == DDI_SUCCESS) {
 367 
 368                         if (pci_save_config_regs(cdip) != DDI_SUCCESS) {
 369                                 cmn_err(CE_WARN, "Failed to save HT config "
 370                                     "regs for %s\n", ddi_node_name(cdip));
 371                                 rval = DDI_FAILURE;
 372 
 373                         } else if (ddi_prop_update_int(DDI_DEV_T_NONE, cdip,
 374                             "htconfig-saved", 1) != DDI_SUCCESS) {
 375                                 cmn_err(CE_WARN, "Failed to set htconfig-saved "
 376                                     "property for %s\n", ddi_node_name(cdip));
 377                                 rval = DDI_FAILURE;
 378                         }
 379                 }
 380 
 381                 pci_config_teardown(&cfg_hdl);
 382         }
 383 
 384         return (rval);
 385 }
 386 
 387 int
 388 npe_restore_htconfig_children(dev_info_t *dip)
 389 {
 390         dev_info_t *cdip = ddi_get_child(dip);
 391         int rval = DDI_SUCCESS;
 392 
 393         for (; cdip != NULL; cdip = ddi_get_next_sibling(cdip)) {
 394                 if (ddi_prop_get_int(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
 395                     "htconfig-saved", 0) == 0)
 396                         continue;
 397 
 398                 if (pci_restore_config_regs(cdip) != DDI_SUCCESS) {
 399                         cmn_err(CE_WARN, "Failed to restore HT config "
 400                             "regs for %s\n", ddi_node_name(cdip));
 401                         rval = DDI_FAILURE;
 402                 }
 403         }
 404 
 405         return (rval);
 406 }