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