xref: /illumos-gate/usr/src/uts/intel/io/pciex/pcieb_x86.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /* x86 specific code used by the pcieb driver */
27 
28 #include <sys/types.h>
29 #include <sys/ddi.h>
30 #include <sys/kmem.h>
31 #include <sys/sysmacros.h>
32 #include <sys/sunddi.h>
33 #include <sys/sunndi.h>
34 #include <sys/pcie.h>
35 #include <sys/pci_cap.h>
36 #include <sys/pcie_impl.h>
37 #include <sys/pcie_acpi.h>
38 #include <sys/hotplug/hpctrl.h>
39 #include <io/pciex/pcieb.h>
40 #include <io/pciex/pcie_nb5000.h>
41 
42 /* Flag to turn off intel error handling workarounds */
43 int pcieb_intel_workaround_disable = 0;
44 
45 void
46 pcieb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
47 	(void) pf_scan_fabric(dip, derr, NULL);
48 }
49 
50 int
51 pcieb_plat_peekpoke(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
52     void *arg, void *result)
53 {
54 	pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state,
55 	    ddi_get_instance(dip));
56 
57 	if (!PCIE_IS_RP(PCIE_DIP2BUS(dip)))
58 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
59 
60 	return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
61 	    ddi_ctlops, &pcieb->pcieb_err_mutex,
62 	    &pcieb->pcieb_peek_poke_mutex,
63 	    pcieb_peekpoke_cb));
64 }
65 
66 /* x86 specific workarounds needed at the end of pcieb attach */
67 void
68 pcieb_plat_attach_workaround(dev_info_t *dip)
69 {
70 	/* Must apply workaround only after all initialization is done */
71 	pcieb_intel_error_workaround(dip);
72 	pcieb_intel_mps_workaround(dip);
73 
74 }
75 
76 /* Workarounds to enable error handling on certain Intel chipsets */
77 void
78 pcieb_intel_error_workaround(dev_info_t *dip)
79 {
80 	pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state,
81 	    ddi_get_instance(dip));
82 
83 	pcieb_intel_serr_workaround(dip, pcieb->pcieb_no_aer_msi);
84 	pcieb_intel_rber_workaround(dip);
85 	pcieb_intel_sw_workaround(dip);
86 }
87 
88 int
89 pcieb_plat_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
90     ddi_intr_handle_impl_t *hdlp, void *result)
91 {
92 	return (i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result));
93 }
94 
95 /* shpc is not supported on x86 */
96 /*ARGSUSED*/
97 int
98 pcieb_plat_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle)
99 {
100 	return (DDI_FAILURE);
101 }
102 
103 /*
104  * Dummy functions to get around the fact that there's no shpc module on x86
105  * today
106  */
107 /*ARGSUSED*/
108 int
109 pcishpc_init(dev_info_t *dip)
110 {
111 	return (DDI_FAILURE);
112 }
113 
114 /*ARGSUSED*/
115 int
116 pcishpc_uninit(dev_info_t *dip)
117 {
118 	return (DDI_FAILURE);
119 }
120 
121 /*ARGSUSED*/
122 int
123 pcishpc_intr(dev_info_t *dip)
124 {
125 	return (DDI_INTR_UNCLAIMED);
126 }
127 
128 /*ARGSUSED*/
129 boolean_t
130 pcieb_plat_pwr_disable(dev_info_t *dip)
131 {
132 	/* Always disable on x86 */
133 	return (B_TRUE);
134 }
135 
136 boolean_t
137 pcieb_plat_msi_supported(dev_info_t *dip)
138 {
139 	pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip);
140 	uint16_t vendor_id, device_id;
141 	vendor_id = bus_p->bus_dev_ven_id & 0xFFFF;
142 	device_id = bus_p->bus_dev_ven_id >> 16;
143 
144 	/*
145 	 * Intel ESB2 switches have a errata which prevents using MSIs
146 	 * for hotplug.
147 	 */
148 	return (((vendor_id == INTEL_VENDOR_ID) &&
149 	    INTEL_ESB2_SW_PCIE_DEV_ID(device_id)) ? B_FALSE : B_TRUE);
150 }
151 
152 void
153 pcieb_plat_intr_attach(pcieb_devstate_t *pcieb)
154 {
155 	/*
156 	 *  _OSC initialization needs to be done before interrupts are
157 	 *  initialized.
158 	 */
159 	pcieb_init_osc(pcieb->pcieb_dip);
160 }
161 
162 void
163 pcieb_plat_initchild(dev_info_t *child)
164 {
165 	struct ddi_parent_private_data *pdptr;
166 	if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts",
167 	    -1) != -1) {
168 		pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
169 		    sizeof (struct intrspec)), KM_SLEEP);
170 		pdptr->par_intr = (struct intrspec *)(pdptr + 1);
171 		pdptr->par_nintr = 1;
172 		ddi_set_parent_data(child, pdptr);
173 	} else
174 		ddi_set_parent_data(child, NULL);
175 }
176 
177 void
178 pcieb_plat_uninitchild(dev_info_t *child)
179 {
180 	struct ddi_parent_private_data	*pdptr;
181 
182 	if ((pdptr = ddi_get_parent_data(child)) != NULL)
183 		kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
184 
185 	ddi_set_parent_data(child, NULL);
186 }
187 
188 /* _OSC related */
189 void
190 pcieb_init_osc(dev_info_t *devi) {
191 	pcie_bus_t	*bus_p = PCIE_DIP2UPBUS(devi);
192 	uint32_t	osc_flags = OSC_CONTROL_PCIE_ADV_ERR;
193 
194 	/*
195 	 * Call _OSC method for 2 reasons:
196 	 * 1. Hotplug: To determine if it is native or ACPI mode.
197 	 *
198 	 * 2. Error handling: Inform firmware that OS can support AER error
199 	 * handling. Currently we don't care for what the BIOS response was
200 	 * and instead setup interrupts for error handling as if it were
201 	 * supported.
202 	 *
203 	 * For hotpluggable slots the _OSC method has already been called as
204 	 * part of the hotplug initialization.
205 	 * For non-hotpluggable slots we need to call the _OSC method only for
206 	 * Root Ports (for AER support).
207 	 */
208 	if (!pcie_is_osc(devi) && PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p))
209 		(void) pcie_acpi_osc(devi, &osc_flags);
210 }
211 
212 /*
213  * Intel chip specific workarounds. Right now they're limited to the 5000, 5400
214  * and 7300 series chipsets.
215  */
216 typedef struct x86_error_reg {
217 	uint32_t	offset;
218 	uint_t		size;
219 	uint32_t	mask;
220 	uint32_t	value1;	/* Value for MSI case */
221 	uint32_t	value2; /* Value for machinecheck case */
222 } x86_error_reg_t;
223 
224 typedef struct x86_error_tbl {
225 	uint16_t	vendor_id;
226 	uint16_t	device_id_low;
227 	uint16_t	device_id_high;
228 	uint8_t		rev_id_low;
229 	uint8_t		rev_id_high;
230 	x86_error_reg_t	*error_regs;
231 	int		error_regs_len;
232 } x86_error_tbl_t;
233 
234 /*
235  * Chipset and device specific settings that are required for error handling
236  * (reporting, fowarding, and response at the RC) beyond the standard
237  * registers in the PCIE and AER caps.
238  *
239  * The Northbridge Root Port settings also apply to the ESI port.  The ESI
240  * port is a special leaf device but functions like a root port connected
241  * to the Southbridge and receives all the onboard Southbridge errors
242  * including those from Southbridge Root Ports.  However, this does not
243  * include the Southbridge Switch Ports which act like normal switch ports
244  * and is connected to the Northbridge through a separate link.
245  *
246  * PCIE errors from the ESB2 Southbridge RPs are simply fowarded to the ESI
247  * port on the Northbridge.
248  *
249  * If MSIs don't work we want UEs (Fatal and Non-Fatal) to panic the system,
250  * except for URs.  We do this by having the Root Ports respond with a System
251  * Error and having that trigger a Machine Check (MCE).
252  */
253 
254 /*
255  * 7300 Northbridge Root Ports
256  */
257 static x86_error_reg_t intel_7300_rp_regs[] = {
258 	/* Command Register - Enable SERR */
259 	{0x4,   16, 0xFFFF,	0x0,	PCI_COMM_SERR_ENABLE},
260 
261 	/* Root Control Register - SERR on NFE/FE */
262 	{0x88,  16, 0x0,	0x0,	PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
263 					PCIE_ROOTCTL_SYS_ERR_ON_FE_EN},
264 
265 	/* AER UE Mask - Mask UR */
266 	{0x108, 32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR},
267 
268 	/* PEXCTRL[21] check for certain malformed TLP types and MSI enable */
269 	{0x48,	32, 0xFFFFFFFF, 0xC0200000, 0x200000},
270 	/* PEXCTRL3[7]. MSI RAS error enable */
271 	{0x4D,	32, 0xFFFFFFFF, 0x1, 0x0},
272 
273 	/* PEX_ERR_DOCMD[7:0] */
274 	{0x144,	8,  0x0,	0x0,	0xF0},
275 
276 	/* EMASK_UNCOR_PEX[21:0] UE mask */
277 	{0x148,	32, 0x0, PCIE_AER_UCE_UR, PCIE_AER_UCE_UR},
278 
279 	/* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */
280 	{0x150,	8,  0x0,	0x0,	0x1},
281 };
282 #define	INTEL_7300_RP_REGS_LEN \
283 	(sizeof (intel_7300_rp_regs) / sizeof (x86_error_reg_t))
284 
285 /*
286  * 5000 Northbridge Root Ports
287  */
288 static x86_error_reg_t intel_5000_rp_regs[] = {
289 	/* Command Register - Enable SERR */
290 	{0x4,   16, 0xFFFF,	PCI_COMM_SERR_ENABLE,	PCI_COMM_SERR_ENABLE},
291 
292 	/* Root Control Register - SERR on NFE/FE/CE */
293 	{0x88,  16, 0x0,	PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
294 				PCIE_ROOTCTL_SYS_ERR_ON_FE_EN |
295 				PCIE_ROOTCTL_SYS_ERR_ON_CE_EN,
296 				PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
297 				PCIE_ROOTCTL_SYS_ERR_ON_FE_EN},
298 
299 	/* AER UE Mask - Mask UR */
300 	{0x108, 32, 0x0,	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
301 
302 	/* PEXCTRL[21] check for certain malformed TLP type */
303 	{0x48,	32, 0xFFFFFFFF, 0xC0200000, 0x200000},
304 	/* PEXCTRL3[7]. MSI RAS error enable. */
305 	{0x4D,	32, 0xFFFFFFFF,	0x1,	0x0},
306 
307 	/* PEX_ERR_DOCMD[7:0] */
308 	{0x144,	8,  0x0,	0x0,	0xF0},
309 
310 	/* EMASK_UNCOR_PEX[21:0] UE mask */
311 	{0x148,	32, 0x0, 	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
312 
313 	/* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */
314 	{0x150,	8,  0x0, 	0x0,	0x1},
315 };
316 #define	INTEL_5000_RP_REGS_LEN \
317 	(sizeof (intel_5000_rp_regs) / sizeof (x86_error_reg_t))
318 
319 /*
320  * 5400 Northbridge Root Ports.
321  */
322 static x86_error_reg_t intel_5400_rp_regs[] = {
323 	/* Command Register - Enable SERR */
324 	{0x4,   16, 0xFFFF,	PCI_COMM_SERR_ENABLE, PCI_COMM_SERR_ENABLE},
325 
326 	/* Root Control Register - SERR on NFE/FE */
327 	{0x88,  16, 0x0, PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
328 			    PCIE_ROOTCTL_SYS_ERR_ON_FE_EN |
329 			    PCIE_ROOTCTL_SYS_ERR_ON_CE_EN,
330 			    PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
331 			    PCIE_ROOTCTL_SYS_ERR_ON_FE_EN},
332 
333 	/* AER UE Mask - Mask UR */
334 	{0x108, 32, 0x0,	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
335 
336 	/* PEXCTRL[21] check for certain malformed TLP types */
337 	{0x48,	32, 0xFFFFFFFF,	0xC0200000, 0x200000},
338 	/* PEXCTRL3. MSI RAS error enable. */
339 	{0x4E,	8, 0x0,	0x1,	0x0},
340 
341 	/* PEX_ERR_DOCMD[11:0] */
342 	{0x144,	16,  0x0, 	0x0,	0xFF0},
343 
344 	/* PEX_ERR_PIN_MASK[4:0] do not mask ERR[2:0] pins used by DOCMD */
345 	{0x146,	16,  0x0,	0x10,	0x10},
346 
347 	/* EMASK_UNCOR_PEX[21:0] UE mask */
348 	{0x148,	32, 0x0, 	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
349 
350 	/* EMASK_RP_PEX[2:0] FE, UE, CE message detect mask */
351 	{0x150,	8,  0x0, 	0x0,	0x1},
352 };
353 #define	INTEL_5400_RP_REGS_LEN \
354 	(sizeof (intel_5400_rp_regs) / sizeof (x86_error_reg_t))
355 
356 
357 /*
358  * ESB2 Southbridge Root Ports
359  */
360 static x86_error_reg_t intel_esb2_rp_regs[] = {
361 	/* Command Register - Enable SERR */
362 	{0x4,   16, 0xFFFF,	PCI_COMM_SERR_ENABLE,	PCI_COMM_SERR_ENABLE},
363 
364 	/* Root Control Register - SERR on NFE/FE */
365 	{0x5c,  16, 0x0,	PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
366 				PCIE_ROOTCTL_SYS_ERR_ON_FE_EN |
367 				PCIE_ROOTCTL_SYS_ERR_ON_CE_EN,
368 				PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
369 				PCIE_ROOTCTL_SYS_ERR_ON_FE_EN},
370 
371 	/* UEM[20:0] UE mask (write-once) */
372 	{0x148, 32, 0x0,	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
373 };
374 #define	INTEL_ESB2_RP_REGS_LEN \
375 	(sizeof (intel_esb2_rp_regs) / sizeof (x86_error_reg_t))
376 
377 
378 /*
379  * ESB2 Southbridge Switch Ports
380  */
381 static x86_error_reg_t intel_esb2_sw_regs[] = {
382 	/* Command Register - Enable SERR */
383 	{0x4,   16, 0xFFFF,	PCI_COMM_SERR_ENABLE,	PCI_COMM_SERR_ENABLE},
384 
385 	/* AER UE Mask - Mask UR */
386 	{0x108, 32, 0x0,	PCIE_AER_UCE_UR,	PCIE_AER_UCE_UR},
387 };
388 #define	INTEL_ESB2_SW_REGS_LEN \
389 	(sizeof (intel_esb2_sw_regs) / sizeof (x86_error_reg_t))
390 
391 
392 x86_error_tbl_t x86_error_init_tbl[] = {
393 	/* Intel 7300: 3600 = ESI, 3604-360A = NB root ports */
394 	{0x8086, 0x3600, 0x3600, 0x0, 0xFF,
395 		intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN},
396 	{0x8086, 0x3604, 0x360A, 0x0, 0xFF,
397 		intel_7300_rp_regs, INTEL_7300_RP_REGS_LEN},
398 
399 	/* Intel 5000: 25C0, 25D0, 25D4, 25D8 = ESI */
400 	{0x8086, 0x25C0, 0x25C0, 0x0, 0xFF,
401 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
402 	{0x8086, 0x25D0, 0x25D0, 0x0, 0xFF,
403 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
404 	{0x8086, 0x25D4, 0x25D4, 0x0, 0xFF,
405 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
406 	{0x8086, 0x25D8, 0x25D8, 0x0, 0xFF,
407 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
408 
409 	/* Intel 5000: 25E2-25E7 and 25F7-25FA = NB root ports */
410 	{0x8086, 0x25E2, 0x25E7, 0x0, 0xFF,
411 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
412 	{0x8086, 0x25F7, 0x25FA, 0x0, 0xFF,
413 		intel_5000_rp_regs, INTEL_5000_RP_REGS_LEN},
414 
415 	/* Intel 5400: 4000-4001, 4003 = ESI and 4021-4029 = NB root ports */
416 	{0x8086, 0x4000, 0x4001, 0x0, 0xFF,
417 		intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN},
418 	{0x8086, 0x4003, 0x4003, 0x0, 0xFF,
419 		intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN},
420 	{0x8086, 0x4021, 0x4029, 0x0, 0xFF,
421 		intel_5400_rp_regs, INTEL_5400_RP_REGS_LEN},
422 
423 	/* Intel 631xESB/632xESB aka ESB2: 2690-2697 = SB root ports */
424 	{0x8086, 0x2690, 0x2697, 0x0, 0xFF,
425 		intel_esb2_rp_regs, INTEL_ESB2_RP_REGS_LEN},
426 
427 	/* Intel Switches on esb2: 3500-3503, 3510-351B */
428 	{0x8086, 0x3500, 0x3503, 0x0, 0xFF,
429 		intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN},
430 	{0x8086, 0x3510, 0x351B, 0x0, 0xFF,
431 		intel_esb2_sw_regs, INTEL_ESB2_SW_REGS_LEN},
432 
433 	/* XXX Intel PCIe-PCIx on esb2: 350C */
434 };
435 static int x86_error_init_tbl_len =
436 	sizeof (x86_error_init_tbl) / sizeof (x86_error_tbl_t);
437 
438 /*
439  * The main goal of this workaround is to set chipset specific settings if
440  * MSIs happen to be enabled on this device. Otherwise make the system
441  * Machine Check/Panic if an UE is detected in the fabric.
442  */
443 void
444 pcieb_intel_serr_workaround(dev_info_t *dip, boolean_t mcheck)
445 {
446 	uint16_t		vid, did;
447 	uint8_t			rid;
448 	int			i, j;
449 	x86_error_tbl_t		*tbl;
450 	x86_error_reg_t		*reg;
451 	pcie_bus_t		*bus_p = PCIE_DIP2UPBUS(dip);
452 	ddi_acc_handle_t	cfg_hdl = bus_p->bus_cfg_hdl;
453 	uint16_t		bdf = bus_p->bus_bdf;
454 
455 	if (pcieb_intel_workaround_disable)
456 		return;
457 
458 	vid = bus_p->bus_dev_ven_id & 0xFFFF;
459 	did = bus_p->bus_dev_ven_id >> 16;
460 	rid = bus_p->bus_rev_id;
461 
462 	PCIEB_DEBUG(DBG_ATTACH, dip, "VID:0x%x DID:0x%x RID:0x%x bdf=0x%x\n",
463 	    vid, did, rid, bdf);
464 
465 	tbl = x86_error_init_tbl;
466 	for (i = 0; i < x86_error_init_tbl_len; i++, tbl++) {
467 		if (!((vid == tbl->vendor_id) &&
468 		    (did >= tbl->device_id_low) &&
469 		    (did <= tbl->device_id_high) &&
470 		    (rid >= tbl->rev_id_low) &&
471 		    (rid <= tbl->rev_id_high)))
472 			continue;
473 
474 		if (mcheck && PCIE_IS_RP(bus_p))
475 			pcie_set_rber_fatal(dip, B_TRUE);
476 
477 		reg = tbl->error_regs;
478 		for (j = 0; j < tbl->error_regs_len; j++, reg++) {
479 			uint32_t data = 0xDEADBEEF;
480 			uint32_t value = 0xDEADBEEF;
481 			switch (reg->size) {
482 			case 32:
483 				data = (uint32_t)pci_config_get32(cfg_hdl,
484 				    reg->offset);
485 				value = (mcheck ?
486 				    ((data & reg->mask) | reg->value2) :
487 				    ((data & reg->mask) | reg->value1));
488 				pci_config_put32(cfg_hdl, reg->offset, value);
489 				value = (uint32_t)pci_config_get32(cfg_hdl,
490 				    reg->offset);
491 				break;
492 			case 16:
493 				data = (uint32_t)pci_config_get16(cfg_hdl,
494 				    reg->offset);
495 				value = (mcheck ?
496 				    ((data & reg->mask) | reg->value2) :
497 				    ((data & reg->mask) | reg->value1));
498 				pci_config_put16(cfg_hdl, reg->offset,
499 				    (uint16_t)value);
500 				value = (uint32_t)pci_config_get16(cfg_hdl,
501 				    reg->offset);
502 				break;
503 			case 8:
504 				data = (uint32_t)pci_config_get8(cfg_hdl,
505 				    reg->offset);
506 				value = (mcheck ?
507 				    ((data & reg->mask) | reg->value2) :
508 				    ((data & reg->mask) | reg->value1));
509 				pci_config_put8(cfg_hdl, reg->offset,
510 				    (uint8_t)value);
511 				value = (uint32_t)pci_config_get8(cfg_hdl,
512 				    reg->offset);
513 				break;
514 			}
515 
516 			PCIEB_DEBUG(DBG_ATTACH, dip, "bdf:%x mcheck:%d size:%d "
517 			    "off:0x%x mask:0x%x value:0x%x + orig:0x%x -> "
518 			    "0x%x\n", bdf, mcheck, reg->size, reg->offset,
519 			    reg->mask, (mcheck ?  reg->value2 : reg->value1),
520 			    data, value);
521 		}
522 	}
523 }
524 
525 /*
526  * For devices that support Role Base Errors, make several UE have a FATAL
527  * severity.  That way a Fatal Message will be sent instead of a Correctable
528  * Message.  Without full FMA support, CEs will be ignored.
529  */
530 uint32_t pcieb_rber_sev = (PCIE_AER_UCE_TRAINING | PCIE_AER_UCE_DLP |
531     PCIE_AER_UCE_SD | PCIE_AER_UCE_PTLP | PCIE_AER_UCE_FCP | PCIE_AER_UCE_TO |
532     PCIE_AER_UCE_CA | PCIE_AER_UCE_RO | PCIE_AER_UCE_MTLP | PCIE_AER_UCE_ECRC);
533 
534 void
535 pcieb_intel_rber_workaround(dev_info_t *dip)
536 {
537 	uint32_t rber;
538 	pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip);
539 
540 	if (pcieb_intel_workaround_disable)
541 		return;
542 
543 	/*
544 	 * Check Root Port's machinecheck setting to determine if this
545 	 * workaround is needed or not.
546 	 */
547 	if (!pcie_get_rber_fatal(dip))
548 		return;
549 
550 	if (!PCIE_IS_PCIE(bus_p) || !PCIE_HAS_AER(bus_p))
551 		return;
552 
553 	rber = PCIE_CAP_GET(16, bus_p, PCIE_DEVCAP) &
554 	    PCIE_DEVCAP_ROLE_BASED_ERR_REP;
555 	if (!rber)
556 		return;
557 
558 	PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_SERV, pcieb_rber_sev);
559 }
560 
561 /*
562  * The Intel 5000 Chipset has an errata that requires read completion
563  * coalescing to be disabled if the Max Payload Size is set to 256 bytes.
564  */
565 void
566 pcieb_intel_mps_workaround(dev_info_t *dip)
567 {
568 	uint16_t		vid, did;
569 	uint32_t		pexctrl;
570 	pcie_bus_t		*bus_p = PCIE_DIP2UPBUS(dip);
571 
572 	vid = bus_p->bus_dev_ven_id & 0xFFFF;
573 	did = bus_p->bus_dev_ven_id >> 16;
574 
575 	if ((vid == INTEL_VENDOR_ID) && (INTEL_NB5000_PCIE_DEV_ID(did) ||
576 	    INTEL_NB5100_PCIE_DEV_ID(did))) {
577 
578 		pexctrl = pci_config_get32(bus_p->bus_cfg_hdl,
579 		    INTEL_NB5000_PEXCTRL_OFFSET);
580 		/*
581 		 * Turn off coalescing (bit 10)
582 		 */
583 		pexctrl &= ~INTEL_NB5000_PEXCTRL_COALESCE_EN;
584 
585 		pci_config_put32(bus_p->bus_cfg_hdl,
586 		    INTEL_NB5000_PEXCTRL_OFFSET, pexctrl);
587 	}
588 }
589 
590 /*
591  * Workaround for certain switches regardless of platform
592  */
593 void
594 pcieb_intel_sw_workaround(dev_info_t *dip)
595 {
596 	uint16_t		vid, regw;
597 	pcie_bus_t		*bus_p = PCIE_DIP2UPBUS(dip);
598 	ddi_acc_handle_t	cfg_hdl = bus_p->bus_cfg_hdl;
599 
600 	if (pcieb_intel_workaround_disable)
601 		return;
602 
603 	if (!PCIE_IS_SW(PCIE_DIP2BUS(dip)))
604 		return;
605 
606 	vid = bus_p->bus_dev_ven_id & 0xFFFF;
607 	/*
608 	 * Intel and PLX switches require SERR in CMD reg to foward error
609 	 * messages, though this is not PCIE spec-compliant behavior.
610 	 * To prevent the switches themselves from reporting errors on URs
611 	 * when the CMD reg has SERR enabled (which is expected according to
612 	 * the PCIE spec) we rely on masking URs in the AER cap.
613 	 */
614 	if (vid == 0x8086 || vid == 0x10B5) {
615 		regw = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
616 		pci_config_put16(cfg_hdl, PCI_CONF_COMM,
617 		    regw | PCI_COMM_SERR_ENABLE);
618 	}
619 }
620 
621 int
622 pcieb_plat_ctlops(dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg)
623 {
624 	struct detachspec *ds;
625 	struct attachspec *as;
626 
627 	switch (ctlop) {
628 	case DDI_CTLOPS_DETACH:
629 		ds = (struct detachspec *)arg;
630 		switch (ds->when) {
631 		case DDI_POST:
632 			if (ds->cmd == DDI_SUSPEND) {
633 				if (pci_post_suspend(rdip) != DDI_SUCCESS)
634 					return (DDI_FAILURE);
635 			}
636 			break;
637 		default:
638 			break;
639 		}
640 		break;
641 	case DDI_CTLOPS_ATTACH:
642 		as = (struct attachspec *)arg;
643 		switch (as->when) {
644 		case DDI_PRE:
645 			if (as->cmd == DDI_RESUME) {
646 				if (pci_pre_resume(rdip) != DDI_SUCCESS)
647 					return (DDI_FAILURE);
648 			}
649 			break;
650 		case DDI_POST:
651 			/*
652 			 * For leaf devices supporting RBER and AER, we
653 			 * need to apply this workaround on them after
654 			 * attach to be notified of UEs that would
655 			 * otherwise be ignored as CEs on Intel chipsets
656 			 * currently
657 			 */
658 			pcieb_intel_rber_workaround(rdip);
659 			break;
660 		default:
661 			break;
662 		}
663 		break;
664 	default:
665 		break;
666 	}
667 
668 	return (DDI_SUCCESS);
669 }
670