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