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