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
npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl)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
npe_disable_empty_bridges_workaround(dev_info_t * child)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
npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl)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
npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl)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
npe_child_is_pci(dev_info_t * dip)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
npe_is_mmcfg_supported(dev_info_t * dip)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
npe_enable_htmsi(ddi_acc_handle_t cfg_hdl)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
npe_enable_htmsi_children(dev_info_t * dip)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
npe_save_htconfig_children(dev_info_t * dip)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
npe_restore_htconfig_children(dev_info_t * dip)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