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