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