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