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