xref: /illumos-gate/usr/src/uts/i86pc/io/pciex/npe_misc.c (revision 772eca3305893e0fc7b9c13ec6a1a6df72251dbd)
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/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