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 } 407