xref: /onnv-gate/usr/src/uts/common/os/sunpci.c (revision 240:add91ef1762e)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/sunndi.h>
310Sstevel@tonic-gate #include <sys/ddifm_impl.h>
320Sstevel@tonic-gate #include <sys/fm/util.h>
330Sstevel@tonic-gate #include <sys/fm/protocol.h>
340Sstevel@tonic-gate #include <sys/fm/io/pci.h>
350Sstevel@tonic-gate #include <sys/fm/io/ddi.h>
360Sstevel@tonic-gate #include <sys/pci.h>
37*240Stimh #include <sys/pcie.h>
380Sstevel@tonic-gate #include <sys/pci_impl.h>
390Sstevel@tonic-gate #include <sys/epm.h>
400Sstevel@tonic-gate 
410Sstevel@tonic-gate 
420Sstevel@tonic-gate int
430Sstevel@tonic-gate pci_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
440Sstevel@tonic-gate {
450Sstevel@tonic-gate 	caddr_t	cfgaddr;
460Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
470Sstevel@tonic-gate 
480Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
490Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
500Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate 	/* Check for fault management capabilities */
530Sstevel@tonic-gate 	if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(dip)))
540Sstevel@tonic-gate 		attr.devacc_attr_access = DDI_FLAGERR_ACC;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate 	return (ddi_regs_map_setup(dip, 0, &cfgaddr, 0, 0, &attr, handle));
570Sstevel@tonic-gate }
580Sstevel@tonic-gate 
590Sstevel@tonic-gate void
600Sstevel@tonic-gate pci_config_teardown(ddi_acc_handle_t *handle)
610Sstevel@tonic-gate {
620Sstevel@tonic-gate 	ddi_regs_map_free(handle);
630Sstevel@tonic-gate }
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate  * pci_ereport_setup, pci_ereport_teardown, pci_ereport_post:
670Sstevel@tonic-gate  * Interfaces to be used by ereport capable PCI device drivers to setup,
680Sstevel@tonic-gate  * teardown, and post generic PCI error reports. This is to guarantee a
690Sstevel@tonic-gate  * consistant error report model for all PCI devices. Please see
700Sstevel@tonic-gate  * PSARC/2004/391.
710Sstevel@tonic-gate  */
720Sstevel@tonic-gate 
730Sstevel@tonic-gate typedef struct pci_erpt {
740Sstevel@tonic-gate 	caddr_t pci_cfg_addr;		/* Config space address */
750Sstevel@tonic-gate 	ddi_acc_handle_t pci_cfg_hdl;	/* Config space access handle */
760Sstevel@tonic-gate } pci_erpt_t;
770Sstevel@tonic-gate 
780Sstevel@tonic-gate pci_fm_err_t pci_err_tbl[] = {
790Sstevel@tonic-gate 	PCI_DET_PERR,	PCI_STAT_PERROR,	NULL,
800Sstevel@tonic-gate 	PCI_MDPE,	PCI_STAT_S_PERROR,	PCI_TARG_MDPE,
810Sstevel@tonic-gate 	PCI_SIG_SERR,	PCI_STAT_S_SYSERR,	NULL,
820Sstevel@tonic-gate 	PCI_MA,		PCI_STAT_R_MAST_AB,	PCI_TARG_MA,
830Sstevel@tonic-gate 	PCI_REC_TA,	PCI_STAT_R_TARG_AB,	PCI_TARG_REC_TA,
840Sstevel@tonic-gate 	PCI_SIG_TA,	PCI_STAT_S_TARG_AB,	NULL,
850Sstevel@tonic-gate 	NULL, NULL,
860Sstevel@tonic-gate };
870Sstevel@tonic-gate 
880Sstevel@tonic-gate pci_fm_err_t pci_bdg_err_tbl[] = {
890Sstevel@tonic-gate 	PCI_DET_PERR,	PCI_STAT_PERROR,	NULL,
900Sstevel@tonic-gate 	PCI_MDPE,	PCI_STAT_S_PERROR,	NULL,
910Sstevel@tonic-gate 	PCI_REC_SERR,	PCI_STAT_S_SYSERR,	NULL,
920Sstevel@tonic-gate 	PCI_MA,		PCI_STAT_R_MAST_AB,	NULL,
930Sstevel@tonic-gate 	PCI_REC_TA,	PCI_STAT_R_TARG_AB,	NULL,
940Sstevel@tonic-gate 	PCI_SIG_TA,	PCI_STAT_S_TARG_AB,	NULL,
950Sstevel@tonic-gate 	NULL, NULL,
960Sstevel@tonic-gate };
97*240Stimh 
980Sstevel@tonic-gate void
990Sstevel@tonic-gate pci_ereport_setup(dev_info_t *dip)
1000Sstevel@tonic-gate {
1010Sstevel@tonic-gate 	struct dev_info *devi = DEVI(dip);
1020Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = devi->devi_fmhdl;
1030Sstevel@tonic-gate 	pci_erpt_t *erpt_p;
1040Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
105*240Stimh 	uint16_t pci_devstat = 0;
106*240Stimh 	uint16_t pcie_cap = 0;
107*240Stimh 	uint8_t ecap_ptr = 0;
108*240Stimh 	uint8_t cap_ptr = 0;
109*240Stimh 	uint8_t cap_id = 0;
110*240Stimh 	int have_pciex;
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) {
1130Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
1140Sstevel@tonic-gate 		return;
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	ASSERT(fmhdl);
1180Sstevel@tonic-gate 	ASSERT(fmhdl->fh_bus_specific == NULL);
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	if ((erpt_p = kmem_zalloc(sizeof (pci_erpt_t), KM_SLEEP)) == NULL)
1210Sstevel@tonic-gate 		return;
1220Sstevel@tonic-gate 
123*240Stimh 	/*
124*240Stimh 	 * Setup config space and store config address
125*240Stimh 	 * in pci_erpt struct.
126*240Stimh 	 */
1270Sstevel@tonic-gate 	if (pci_config_setup(dip, &erpt_p->pci_cfg_hdl) == DDI_SUCCESS) {
1280Sstevel@tonic-gate 		hp = impl_acc_hdl_get(erpt_p->pci_cfg_hdl);
1290Sstevel@tonic-gate 		erpt_p->pci_cfg_addr = (caddr_t)hp->ah_addr;
1300Sstevel@tonic-gate 		fmhdl->fh_bus_specific = (void *)erpt_p;
131*240Stimh 	} else {
132*240Stimh 		return;
133*240Stimh 	}
134*240Stimh 
135*240Stimh 	/*
136*240Stimh 	 * Determine if this device supports a capabilities list.  We
137*240Stimh 	 * do so by looking at a bit in the status register. If we are
138*240Stimh 	 * unable to retrieve the status register, something is horribly
139*240Stimh 	 * wrong and we should just bail.
140*240Stimh 	 */
141*240Stimh 	if ((pci_devstat = ddi_get16(erpt_p->pci_cfg_hdl,
142*240Stimh 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_STAT))) == 0xff)
143*240Stimh 		return;
144*240Stimh 	if ((pci_devstat & PCI_STAT_CAP) == 0)
145*240Stimh 		return;
146*240Stimh 
147*240Stimh 	/*
148*240Stimh 	 * Determine if we are on a machine with pci express.  We do so
149*240Stimh 	 * by looping through the capabilities of the device and looking
150*240Stimh 	 * to see if one of those capabilities is support of PCI
151*240Stimh 	 * express.
152*240Stimh 	 */
153*240Stimh 	have_pciex = 0;
154*240Stimh 	if ((cap_ptr = ddi_get8(erpt_p->pci_cfg_hdl,
155*240Stimh 	    (uint8_t *)(erpt_p->pci_cfg_addr + PCI_CONF_CAP_PTR))) !=
156*240Stimh 	    0xff) {
157*240Stimh 		while ((cap_id = ddi_get8(erpt_p->pci_cfg_hdl,
158*240Stimh 		    (uint8_t *)(erpt_p->pci_cfg_addr + cap_ptr))) !=
159*240Stimh 		    0xff) {
160*240Stimh 			if (cap_id == PCI_CAP_ID_PCI_E) {
161*240Stimh 				ecap_ptr = cap_ptr;
162*240Stimh 				have_pciex = 1;
163*240Stimh 				break;
164*240Stimh 			}
165*240Stimh 			if ((cap_ptr = ddi_get8(erpt_p->pci_cfg_hdl,
166*240Stimh 			    (uint8_t *)(erpt_p->pci_cfg_addr +
167*240Stimh 			    cap_ptr + 1))) == 0xff || cap_ptr == 0)
168*240Stimh 				break;
169*240Stimh 		}
170*240Stimh 	}
171*240Stimh 
172*240Stimh 	/*
173*240Stimh 	 * If not pci express, we're done
174*240Stimh 	 */
175*240Stimh 	if (have_pciex == 0)
176*240Stimh 		return;
177*240Stimh 
178*240Stimh 	/*
179*240Stimh 	 * Save and export the pci express capabilities reg.
180*240Stimh 	 */
181*240Stimh 	pcie_cap = ddi_get16(erpt_p->pci_cfg_hdl,
182*240Stimh 	    (uint16_t *)(erpt_p->pci_cfg_addr + ecap_ptr + PCIE_PCIECAP));
183*240Stimh 	(void) ndi_prop_update_int(DDI_DEV_T_NONE,
184*240Stimh 	    dip, SAVED_PCIEX_CAP_REG, pcie_cap);
185*240Stimh 
186*240Stimh 	/*
187*240Stimh 	 * Find and export any slot capabilities register
188*240Stimh 	 */
189*240Stimh 	if (pcie_cap & PCIE_PCIECAP_SLOT_IMPL) {
190*240Stimh 		int sltcap = ddi_get32(erpt_p->pci_cfg_hdl,
191*240Stimh 		    (uint32_t *)
192*240Stimh 		    (erpt_p->pci_cfg_addr + ecap_ptr + PCIE_SLOTCAP));
193*240Stimh 		(void) ndi_prop_update_int(DDI_DEV_T_NONE,
194*240Stimh 		    dip, SAVED_PCIEX_SLOTCAP_REG, sltcap);
1950Sstevel@tonic-gate 	}
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate void
1990Sstevel@tonic-gate pci_ereport_teardown(dev_info_t *dip)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
2020Sstevel@tonic-gate 	pci_erpt_t *erpt_p;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) {
2050Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
2060Sstevel@tonic-gate 	}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 	ASSERT(fmhdl);
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific;
2110Sstevel@tonic-gate 	if (erpt_p == NULL)
2120Sstevel@tonic-gate 		return;
2130Sstevel@tonic-gate 
214*240Stimh 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_PCIEX_CAP_REG);
215*240Stimh 	(void) ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_PCIEX_SLOTCAP_REG);
2160Sstevel@tonic-gate 	pci_config_teardown(&erpt_p->pci_cfg_hdl);
2170Sstevel@tonic-gate 	kmem_free(erpt_p, sizeof (pci_erpt_t));
2180Sstevel@tonic-gate 	fmhdl->fh_bus_specific = NULL;
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate void
2220Sstevel@tonic-gate pci_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *status)
2230Sstevel@tonic-gate {
2240Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
2250Sstevel@tonic-gate 	pci_erpt_t *erpt_p;
2260Sstevel@tonic-gate 	char buf[FM_MAX_CLASS];
2270Sstevel@tonic-gate 	uint16_t cfg_comm = 0xffff;
2280Sstevel@tonic-gate 	uint16_t cfg_stat = 0xffff;
2290Sstevel@tonic-gate 	int i;
2300Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) {
2330Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
2340Sstevel@tonic-gate 		return;
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	ASSERT(fmhdl);
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	derr->fme_ena = derr->fme_ena ? derr->fme_ena : fm_ena_generate(0,
2400Sstevel@tonic-gate 	    FM_ENA_FMT1);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific;
2430Sstevel@tonic-gate 	if (erpt_p == NULL) {
2440Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
2450Sstevel@tonic-gate 		return;
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	if ((cfg_stat = ddi_get16(erpt_p->pci_cfg_hdl,
2490Sstevel@tonic-gate 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_STAT))) == 0xffff) {
2500Sstevel@tonic-gate 		(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
2510Sstevel@tonic-gate 		    PCI_ERROR_SUBCLASS, PCI_NR);
2520Sstevel@tonic-gate 		ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP,
2530Sstevel@tonic-gate 		    FM_VERSION, DATA_TYPE_UINT8, 0, NULL);
2540Sstevel@tonic-gate 		goto done;
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 	if ((cfg_comm = ddi_get16(erpt_p->pci_cfg_hdl,
2570Sstevel@tonic-gate 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_COMM))) == 0xffff) {
2580Sstevel@tonic-gate 		(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
2590Sstevel@tonic-gate 		    PCI_ERROR_SUBCLASS, PCI_NR);
2600Sstevel@tonic-gate 		ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP,
2610Sstevel@tonic-gate 		    FM_VERSION, DATA_TYPE_UINT8, 0, NULL);
2620Sstevel@tonic-gate 		goto done;
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) {
2660Sstevel@tonic-gate 		for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
2670Sstevel@tonic-gate 			if (cfg_stat & pci_err_tbl[i].reg_bit) {
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 				/*
2700Sstevel@tonic-gate 				 * Generate an ereport for this error bit.
2710Sstevel@tonic-gate 				 */
2720Sstevel@tonic-gate 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
2730Sstevel@tonic-gate 				    PCI_ERROR_SUBCLASS,
2740Sstevel@tonic-gate 				    pci_err_tbl[i].err_class);
2750Sstevel@tonic-gate 				ddi_fm_ereport_post(dip, buf, derr->fme_ena,
2760Sstevel@tonic-gate 				    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
2770Sstevel@tonic-gate 				    PCI_CONFIG_STATUS, DATA_TYPE_UINT16,
2780Sstevel@tonic-gate 				    cfg_stat, PCI_CONFIG_COMMAND,
2790Sstevel@tonic-gate 				    DATA_TYPE_UINT16, cfg_comm, NULL);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 				/*
2820Sstevel@tonic-gate 				 * Generate a corresponding ereport on behalf
2830Sstevel@tonic-gate 				 * of the target (the parent dip) of the
2840Sstevel@tonic-gate 				 * transaction.
2850Sstevel@tonic-gate 				 */
2860Sstevel@tonic-gate 				if (pci_err_tbl[i].terr_class != NULL &&
2870Sstevel@tonic-gate 				    DDI_FM_EREPORT_CAP(ddi_fm_capable(
2880Sstevel@tonic-gate 				    (dev_info_t *)DEVI(dip)->devi_parent))) {
2890Sstevel@tonic-gate 					(void) snprintf(buf, FM_MAX_CLASS,
2900Sstevel@tonic-gate 					    "%s.%s", PCI_ERROR_SUBCLASS,
2910Sstevel@tonic-gate 					    pci_err_tbl[i].terr_class);
2920Sstevel@tonic-gate 					ddi_fm_ereport_post((dev_info_t *)
2930Sstevel@tonic-gate 					    DEVI(dip)->devi_parent, buf,
2940Sstevel@tonic-gate 					    derr->fme_ena, DDI_NOSLEEP,
2950Sstevel@tonic-gate 					    FM_VERSION, DATA_TYPE_UINT8, 0,
2960Sstevel@tonic-gate 					    NULL);
2970Sstevel@tonic-gate 				}
2980Sstevel@tonic-gate 			}
2990Sstevel@tonic-gate 		}
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/*
3030Sstevel@tonic-gate 	 * Clear error bits
3040Sstevel@tonic-gate 	 */
3050Sstevel@tonic-gate 	ddi_put16(erpt_p->pci_cfg_hdl,
3060Sstevel@tonic-gate 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_STAT),
3070Sstevel@tonic-gate 	    (uint16_t)cfg_stat);
3080Sstevel@tonic-gate done:
3090Sstevel@tonic-gate 	if (status != NULL)
3100Sstevel@tonic-gate 		*status = cfg_stat;
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /*
3140Sstevel@tonic-gate  * Generic pci-pci bridge error report function
3150Sstevel@tonic-gate  */
3160Sstevel@tonic-gate void
3170Sstevel@tonic-gate pci_bdg_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *status)
3180Sstevel@tonic-gate {
3190Sstevel@tonic-gate 	struct i_ddi_fmhdl *fmhdl;
3200Sstevel@tonic-gate 	pci_erpt_t *erpt_p;
3210Sstevel@tonic-gate 	char buf[FM_MAX_CLASS];
3220Sstevel@tonic-gate 	uint16_t bdg_ctrl = 0xffff;
3230Sstevel@tonic-gate 	uint16_t cfg_sec_stat = 0xffff;
3240Sstevel@tonic-gate 	int i;
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) {
3270Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
3280Sstevel@tonic-gate 		return;
3290Sstevel@tonic-gate 	}
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	fmhdl = DEVI(dip)->devi_fmhdl;
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	ASSERT(fmhdl);
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	derr->fme_ena = derr->fme_ena ? derr->fme_ena : fm_ena_generate(0,
3360Sstevel@tonic-gate 	    FM_ENA_FMT1);
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific;
3390Sstevel@tonic-gate 	if (erpt_p == NULL) {
3400Sstevel@tonic-gate 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
3410Sstevel@tonic-gate 		return;
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	if ((cfg_sec_stat = ddi_get16(erpt_p->pci_cfg_hdl,
3450Sstevel@tonic-gate 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_BCNF_SEC_STATUS)))
3460Sstevel@tonic-gate 	    == 0xffff) {
3470Sstevel@tonic-gate 		(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS,
3480Sstevel@tonic-gate 		    PCI_NR);
3490Sstevel@tonic-gate 		ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP,
3500Sstevel@tonic-gate 		    FM_VERSION, DATA_TYPE_UINT8, 0, NULL);
3510Sstevel@tonic-gate 		goto done;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	if ((bdg_ctrl = ddi_get16(erpt_p->pci_cfg_hdl,
3550Sstevel@tonic-gate 	    (uint16_t *)(erpt_p->pci_cfg_addr + PCI_BCNF_BCNTRL))) == 0xffff) {
3560Sstevel@tonic-gate 		(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS,
3570Sstevel@tonic-gate 		    PCI_NR);
3580Sstevel@tonic-gate 		ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP,
3590Sstevel@tonic-gate 		    FM_VERSION, DATA_TYPE_UINT8, 0, NULL);
3600Sstevel@tonic-gate 		goto done;
3610Sstevel@tonic-gate 	}
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) {
3640Sstevel@tonic-gate 		if (bdg_ctrl & PCI_BCNF_BCNTRL_DTO_STAT) {
3650Sstevel@tonic-gate 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
3660Sstevel@tonic-gate 			    PCI_ERROR_SUBCLASS, PCI_DTO);
3670Sstevel@tonic-gate 			ddi_fm_ereport_post(dip, buf, derr->fme_ena,
3680Sstevel@tonic-gate 			    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
3690Sstevel@tonic-gate 			    PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16,
3700Sstevel@tonic-gate 			    cfg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16,
3710Sstevel@tonic-gate 			    bdg_ctrl, NULL);
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		for (i = 0; pci_bdg_err_tbl[i].err_class != NULL; i++) {
3750Sstevel@tonic-gate 			if (cfg_sec_stat & pci_bdg_err_tbl[i].reg_bit) {
3760Sstevel@tonic-gate 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s",
3770Sstevel@tonic-gate 				    PCI_ERROR_SUBCLASS, PCI_SEC_ERROR_SUBCLASS,
3780Sstevel@tonic-gate 				    pci_bdg_err_tbl[i].err_class);
3790Sstevel@tonic-gate 				ddi_fm_ereport_post(dip, buf, derr->fme_ena,
3800Sstevel@tonic-gate 				    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
3810Sstevel@tonic-gate 				    PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16,
3820Sstevel@tonic-gate 				    cfg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16,
3830Sstevel@tonic-gate 				    bdg_ctrl, NULL);
3840Sstevel@tonic-gate 			}
3850Sstevel@tonic-gate 		}
3860Sstevel@tonic-gate 	}
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	/*
3890Sstevel@tonic-gate 	 * Clear error bits
3900Sstevel@tonic-gate 	 */
3910Sstevel@tonic-gate 	ddi_put16(erpt_p->pci_cfg_hdl, (uint16_t *)
3920Sstevel@tonic-gate 	    (erpt_p->pci_cfg_addr + PCI_BCNF_SEC_STATUS),
3930Sstevel@tonic-gate 	    (uint16_t)cfg_sec_stat);
3940Sstevel@tonic-gate 	ddi_put16(erpt_p->pci_cfg_hdl, (uint16_t *)
3950Sstevel@tonic-gate 	    (erpt_p->pci_cfg_addr + PCI_BCNF_BCNTRL),
3960Sstevel@tonic-gate 	    (uint16_t)bdg_ctrl);
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate done:
3990Sstevel@tonic-gate 	if (status != NULL)
4000Sstevel@tonic-gate 		*status = cfg_sec_stat;
4010Sstevel@tonic-gate }
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate /*
4040Sstevel@tonic-gate  * Generic pci-pci bridge error analysis function
4050Sstevel@tonic-gate  */
4060Sstevel@tonic-gate int
4070Sstevel@tonic-gate pci_bdg_check_status(dev_info_t *dip, ddi_fm_error_t *derr,
4080Sstevel@tonic-gate     uint16_t pci_cfg_stat, uint16_t pci_cfg_sec_stat)
4090Sstevel@tonic-gate {
4100Sstevel@tonic-gate 	int ret;
4110Sstevel@tonic-gate 	int fatal = 0;
4120Sstevel@tonic-gate 	int nonfatal = 0;
4130Sstevel@tonic-gate 	int unknown = 0;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	if (derr->fme_flag == DDI_FM_ERR_POKE) {
4160Sstevel@tonic-gate 		/*
4170Sstevel@tonic-gate 		 * special case for pokes - we only consider master abort
4180Sstevel@tonic-gate 		 * and target abort as nonfatal. Sserr with no master abort is
4190Sstevel@tonic-gate 		 * fatal, but master/target abort can come in on separate
4200Sstevel@tonic-gate 		 * instance, so return unknown and parent will determine if
4210Sstevel@tonic-gate 		 * nonfatal (if another child returned nonfatal - ie master
4220Sstevel@tonic-gate 		 * or target abort) or fatal otherwise
4230Sstevel@tonic-gate 		 */
4240Sstevel@tonic-gate 		if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB |
4250Sstevel@tonic-gate 		    PCI_STAT_R_MAST_AB))
4260Sstevel@tonic-gate 			nonfatal++;
4270Sstevel@tonic-gate 		if (pci_cfg_stat & PCI_STAT_S_SYSERR)
4280Sstevel@tonic-gate 			unknown++;
4290Sstevel@tonic-gate 	} else if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) {
4300Sstevel@tonic-gate 		/*
4310Sstevel@tonic-gate 		 * Only sserr on primary bus is considered fatal.
4320Sstevel@tonic-gate 		 * In all other conditions, the bridge has been able to notify
4330Sstevel@tonic-gate 		 * the initiator of the error condition, so let the initiator
4340Sstevel@tonic-gate 		 * (be it the host for PIO or the leaf device for DMA) handle it
4350Sstevel@tonic-gate 		 */
4360Sstevel@tonic-gate 		if (pci_cfg_stat & PCI_STAT_S_SYSERR)
4370Sstevel@tonic-gate 			fatal++;
4380Sstevel@tonic-gate 		if (pci_cfg_stat & (PCI_STAT_PERROR |
4390Sstevel@tonic-gate 		    PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR |
4400Sstevel@tonic-gate 		    PCI_STAT_R_TARG_AB | PCI_STAT_S_TARG_AB))
4410Sstevel@tonic-gate 			nonfatal++;
4420Sstevel@tonic-gate 		if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB |
4430Sstevel@tonic-gate 		    PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR |
4440Sstevel@tonic-gate 		    PCI_STAT_PERROR | PCI_STAT_S_TARG_AB))
4450Sstevel@tonic-gate 			nonfatal++;
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	/*
4490Sstevel@tonic-gate 	 * now check children below the bridge
4500Sstevel@tonic-gate 	 */
4510Sstevel@tonic-gate 	ret = ndi_fm_handler_dispatch(dip, NULL, derr);
4520Sstevel@tonic-gate 	if (ret == DDI_FM_FATAL)
4530Sstevel@tonic-gate 		fatal++;
4540Sstevel@tonic-gate 	else if (ret == DDI_FM_NONFATAL)
4550Sstevel@tonic-gate 		nonfatal++;
4560Sstevel@tonic-gate 	else if (ret == DDI_FM_UNKNOWN)
4570Sstevel@tonic-gate 		unknown++;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
4600Sstevel@tonic-gate 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate #ifdef _LP64
4640Sstevel@tonic-gate uint8_t
4650Sstevel@tonic-gate pci_config_get8(ddi_acc_handle_t handle, off_t offset)
4660Sstevel@tonic-gate #else /* _ILP32 */
4670Sstevel@tonic-gate uint8_t
4680Sstevel@tonic-gate pci_config_getb(ddi_acc_handle_t handle, off_t offset)
4690Sstevel@tonic-gate #endif
4700Sstevel@tonic-gate {
4710Sstevel@tonic-gate 	caddr_t	cfgaddr;
4720Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
4750Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
4760Sstevel@tonic-gate 	return (ddi_get8(handle, (uint8_t *)cfgaddr));
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate #ifdef _LP64
4800Sstevel@tonic-gate uint16_t
4810Sstevel@tonic-gate pci_config_get16(ddi_acc_handle_t handle, off_t offset)
4820Sstevel@tonic-gate #else /* _ILP32 */
4830Sstevel@tonic-gate uint16_t
4840Sstevel@tonic-gate pci_config_getw(ddi_acc_handle_t handle, off_t offset)
4850Sstevel@tonic-gate #endif
4860Sstevel@tonic-gate {
4870Sstevel@tonic-gate 	caddr_t	cfgaddr;
4880Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
4910Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
4920Sstevel@tonic-gate 	return (ddi_get16(handle, (uint16_t *)cfgaddr));
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate #ifdef _LP64
4960Sstevel@tonic-gate uint32_t
4970Sstevel@tonic-gate pci_config_get32(ddi_acc_handle_t handle, off_t offset)
4980Sstevel@tonic-gate #else /* _ILP32 */
4990Sstevel@tonic-gate uint32_t
5000Sstevel@tonic-gate pci_config_getl(ddi_acc_handle_t handle, off_t offset)
5010Sstevel@tonic-gate #endif
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate 	caddr_t	cfgaddr;
5040Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5070Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5080Sstevel@tonic-gate 	return (ddi_get32(handle, (uint32_t *)cfgaddr));
5090Sstevel@tonic-gate }
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate #ifdef _LP64
5120Sstevel@tonic-gate uint64_t
5130Sstevel@tonic-gate pci_config_get64(ddi_acc_handle_t handle, off_t offset)
5140Sstevel@tonic-gate #else /* _ILP32 */
5150Sstevel@tonic-gate uint64_t
5160Sstevel@tonic-gate pci_config_getll(ddi_acc_handle_t handle, off_t offset)
5170Sstevel@tonic-gate #endif
5180Sstevel@tonic-gate {
5190Sstevel@tonic-gate 	caddr_t	cfgaddr;
5200Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5230Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5240Sstevel@tonic-gate 	return (ddi_get64(handle, (uint64_t *)cfgaddr));
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate #ifdef _LP64
5280Sstevel@tonic-gate void
5290Sstevel@tonic-gate pci_config_put8(ddi_acc_handle_t handle, off_t offset, uint8_t value)
5300Sstevel@tonic-gate #else /* _ILP32 */
5310Sstevel@tonic-gate void
5320Sstevel@tonic-gate pci_config_putb(ddi_acc_handle_t handle, off_t offset, uint8_t value)
5330Sstevel@tonic-gate #endif
5340Sstevel@tonic-gate {
5350Sstevel@tonic-gate 	caddr_t	cfgaddr;
5360Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5390Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5400Sstevel@tonic-gate 	ddi_put8(handle, (uint8_t *)cfgaddr, value);
5410Sstevel@tonic-gate }
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate #ifdef _LP64
5440Sstevel@tonic-gate void
5450Sstevel@tonic-gate pci_config_put16(ddi_acc_handle_t handle, off_t offset, uint16_t value)
5460Sstevel@tonic-gate #else /* _ILP32 */
5470Sstevel@tonic-gate void
5480Sstevel@tonic-gate pci_config_putw(ddi_acc_handle_t handle, off_t offset, uint16_t value)
5490Sstevel@tonic-gate #endif
5500Sstevel@tonic-gate {
5510Sstevel@tonic-gate 	caddr_t	cfgaddr;
5520Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5550Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5560Sstevel@tonic-gate 	ddi_put16(handle, (uint16_t *)cfgaddr, value);
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate #ifdef _LP64
5600Sstevel@tonic-gate void
5610Sstevel@tonic-gate pci_config_put32(ddi_acc_handle_t handle, off_t offset, uint32_t value)
5620Sstevel@tonic-gate #else /* _ILP32 */
5630Sstevel@tonic-gate void
5640Sstevel@tonic-gate pci_config_putl(ddi_acc_handle_t handle, off_t offset, uint32_t value)
5650Sstevel@tonic-gate #endif
5660Sstevel@tonic-gate {
5670Sstevel@tonic-gate 	caddr_t	cfgaddr;
5680Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5710Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5720Sstevel@tonic-gate 	ddi_put32(handle, (uint32_t *)cfgaddr, value);
5730Sstevel@tonic-gate }
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate #ifdef _LP64
5760Sstevel@tonic-gate void
5770Sstevel@tonic-gate pci_config_put64(ddi_acc_handle_t handle, off_t offset, uint64_t value)
5780Sstevel@tonic-gate #else /* _ILP32 */
5790Sstevel@tonic-gate void
5800Sstevel@tonic-gate pci_config_putll(ddi_acc_handle_t handle, off_t offset, uint64_t value)
5810Sstevel@tonic-gate #endif
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate 	caddr_t	cfgaddr;
5840Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	hp = impl_acc_hdl_get(handle);
5870Sstevel@tonic-gate 	cfgaddr = hp->ah_addr + offset;
5880Sstevel@tonic-gate 	ddi_put64(handle, (uint64_t *)cfgaddr, value);
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate /*ARGSUSED*/
5920Sstevel@tonic-gate int
5930Sstevel@tonic-gate pci_report_pmcap(dev_info_t *dip, int cap, void *arg)
5940Sstevel@tonic-gate {
5950Sstevel@tonic-gate 	return (DDI_SUCCESS);
5960Sstevel@tonic-gate }
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate /*
5990Sstevel@tonic-gate  * Note about saving and restoring config space.
6000Sstevel@tonic-gate  * PCI devices have only upto 256 bytes of config space while PCI Express
6010Sstevel@tonic-gate  * devices can have upto 4k config space. In case of PCI Express device,
6020Sstevel@tonic-gate  * we save all 4k config space and restore it even if it doesn't make use
6030Sstevel@tonic-gate  * of all 4k. But some devices don't respond to reads to non-existent
6040Sstevel@tonic-gate  * registers within the config space. To avoid any panics, we use ddi_peek
6050Sstevel@tonic-gate  * to do the reads. A bit mask is used to indicate which words of the
6060Sstevel@tonic-gate  * config space are accessible. While restoring the config space, only those
6070Sstevel@tonic-gate  * readable words are restored. We do all this in 32 bit size words.
6080Sstevel@tonic-gate  */
6090Sstevel@tonic-gate #define	INDEX_SHIFT		3
6100Sstevel@tonic-gate #define	BITMASK			0x7
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate static uint32_t pci_save_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf,
6130Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp);
6140Sstevel@tonic-gate static void pci_restore_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf,
6150Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t elements);
6160Sstevel@tonic-gate static uint32_t pci_generic_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
6170Sstevel@tonic-gate     uint32_t *regbuf, uint32_t nwords);
6180Sstevel@tonic-gate static uint32_t pci_msi_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
6190Sstevel@tonic-gate     uint32_t *regbuf, uint32_t notused);
6200Sstevel@tonic-gate static uint32_t pci_pcix_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
6210Sstevel@tonic-gate     uint32_t *regbuf, uint32_t notused);
6220Sstevel@tonic-gate static uint32_t pci_pcie_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
6230Sstevel@tonic-gate     uint32_t *regbuf, uint32_t notused);
6240Sstevel@tonic-gate static void pci_fill_buf(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
6250Sstevel@tonic-gate     uint32_t *regbuf, uint32_t nwords);
6260Sstevel@tonic-gate static uint32_t cap_walk_and_save(ddi_acc_handle_t confhdl, uint32_t *regbuf,
6270Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp, int xspace);
6280Sstevel@tonic-gate static void pci_pmcap_check(ddi_acc_handle_t confhdl, uint32_t *regbuf,
6290Sstevel@tonic-gate     uint16_t pmcap_offset);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate /*
6320Sstevel@tonic-gate  * Table below specifies the number of registers to be saved for each PCI
6330Sstevel@tonic-gate  * capability. pci_generic_save saves the number of words specified in the
6340Sstevel@tonic-gate  * table. Any special considerations will be taken care by the capability
6350Sstevel@tonic-gate  * specific save function e.g. use pci_msi_save to save registers associated
6360Sstevel@tonic-gate  * with MSI capability. PCI_UNKNOWN_SIZE indicates that number of registers
6370Sstevel@tonic-gate  * to be saved is variable and will be determined by the specific save function.
6380Sstevel@tonic-gate  * Currently we save/restore all the registers associated with the capability
6390Sstevel@tonic-gate  * including read only registers. Regsiters are saved and restored in 32 bit
6400Sstevel@tonic-gate  * size words.
6410Sstevel@tonic-gate  */
6420Sstevel@tonic-gate static pci_cap_entry_t pci_cap_table[] = {
6430Sstevel@tonic-gate 	{PCI_CAP_ID_PM, PCI_PMCAP_NDWORDS, pci_generic_save},
6440Sstevel@tonic-gate 	{PCI_CAP_ID_AGP, PCI_AGP_NDWORDS, pci_generic_save},
6450Sstevel@tonic-gate 	{PCI_CAP_ID_SLOT_ID, PCI_SLOTID_NDWORDS, pci_generic_save},
6460Sstevel@tonic-gate 	{PCI_CAP_ID_MSI_X, PCI_MSIX_NDWORDS, pci_generic_save},
6470Sstevel@tonic-gate 	{PCI_CAP_ID_MSI, PCI_CAP_SZUNKNOWN, pci_msi_save},
6480Sstevel@tonic-gate 	{PCI_CAP_ID_PCIX, PCI_CAP_SZUNKNOWN, pci_pcix_save},
6490Sstevel@tonic-gate 	{PCI_CAP_ID_PCI_E, PCI_CAP_SZUNKNOWN, pci_pcie_save},
6500Sstevel@tonic-gate 	/*
6510Sstevel@tonic-gate 	 * {PCI_CAP_ID_cPCI_CRC, 0, NULL},
6520Sstevel@tonic-gate 	 * {PCI_CAP_ID_VPD, 0, NULL},
6530Sstevel@tonic-gate 	 * {PCI_CAP_ID_cPCI_HS, 0, NULL},
6540Sstevel@tonic-gate 	 * {PCI_CAP_ID_PCI_HOTPLUG, 0, NULL},
6550Sstevel@tonic-gate 	 * {PCI_CAP_ID_AGP_8X, 0, NULL},
6560Sstevel@tonic-gate 	 * {PCI_CAP_ID_SECURE_DEV, 0, NULL},
6570Sstevel@tonic-gate 	 */
6580Sstevel@tonic-gate 	{PCI_CAP_NEXT_PTR_NULL, 0, NULL}
6590Sstevel@tonic-gate };
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate /*
6620Sstevel@tonic-gate  * Save the configuration registers for cdip as a property
6630Sstevel@tonic-gate  * so that it persists after detach/uninitchild.
6640Sstevel@tonic-gate  */
6650Sstevel@tonic-gate int
6660Sstevel@tonic-gate pci_save_config_regs(dev_info_t *dip)
6670Sstevel@tonic-gate {
6680Sstevel@tonic-gate 	ddi_acc_handle_t confhdl;
6690Sstevel@tonic-gate 	pci_config_header_state_t *chsp;
6700Sstevel@tonic-gate 	pci_cap_save_desc_t *pci_cap_descp;
6710Sstevel@tonic-gate 	int ret;
6720Sstevel@tonic-gate 	uint32_t i, ncaps, nwords;
6730Sstevel@tonic-gate 	uint32_t *regbuf, *p;
6740Sstevel@tonic-gate 	uint8_t *maskbuf;
6750Sstevel@tonic-gate 	size_t maskbufsz, regbufsz, capbufsz;
6760Sstevel@tonic-gate 	ddi_acc_hdl_t *hp;
6770Sstevel@tonic-gate 	off_t offset = 0;
6780Sstevel@tonic-gate 	uint8_t cap_ptr, cap_id;
6790Sstevel@tonic-gate 	int pcie = 0;
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) {
6820Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d can't get config handle",
6830Sstevel@tonic-gate 			ddi_driver_name(dip), ddi_get_instance(dip));
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 		return (DDI_FAILURE);
6860Sstevel@tonic-gate 	}
6870Sstevel@tonic-gate 	/*
6880Sstevel@tonic-gate 	 * Determine if it is a pci express device. If it is, save entire
6890Sstevel@tonic-gate 	 * 4k config space treating it as a array of 32 bit integers.
6900Sstevel@tonic-gate 	 * If it is not, do it in a usual PCI way.
6910Sstevel@tonic-gate 	 */
6920Sstevel@tonic-gate 	cap_ptr = pci_config_get8(confhdl, PCI_BCNF_CAP_PTR);
6930Sstevel@tonic-gate 	/*
6940Sstevel@tonic-gate 	 * Walk the capabilities searching for pci express capability
6950Sstevel@tonic-gate 	 */
6960Sstevel@tonic-gate 	while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
6970Sstevel@tonic-gate 		cap_id = pci_config_get8(confhdl,
6980Sstevel@tonic-gate 		    cap_ptr + PCI_CAP_ID);
6990Sstevel@tonic-gate 		if (cap_id == PCI_CAP_ID_PCI_E) {
7000Sstevel@tonic-gate 			pcie = 1;
7010Sstevel@tonic-gate 			break;
7020Sstevel@tonic-gate 		}
7030Sstevel@tonic-gate 		cap_ptr = pci_config_get8(confhdl,
7040Sstevel@tonic-gate 		    cap_ptr + PCI_CAP_NEXT_PTR);
7050Sstevel@tonic-gate 	}
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	if (pcie) {
7080Sstevel@tonic-gate 		/* PCI express device. Can have data in all 4k space */
7090Sstevel@tonic-gate 		regbuf = (uint32_t *)kmem_zalloc((size_t)PCIE_CONF_HDR_SIZE,
7100Sstevel@tonic-gate 			    KM_SLEEP);
7110Sstevel@tonic-gate 		p = regbuf;
7120Sstevel@tonic-gate 		/*
7130Sstevel@tonic-gate 		 * Allocate space for mask.
7140Sstevel@tonic-gate 		 * mask size is 128 bytes (4096 / 4 / 8 )
7150Sstevel@tonic-gate 		 */
7160Sstevel@tonic-gate 		maskbufsz = (size_t)((PCIE_CONF_HDR_SIZE/ sizeof (uint32_t)) >>
7170Sstevel@tonic-gate 		    INDEX_SHIFT);
7180Sstevel@tonic-gate 		maskbuf = (uint8_t *)kmem_zalloc(maskbufsz, KM_SLEEP);
7190Sstevel@tonic-gate 		hp = impl_acc_hdl_get(confhdl);
7200Sstevel@tonic-gate 		for (i = 0; i < (PCIE_CONF_HDR_SIZE / sizeof (uint32_t)); i++) {
7210Sstevel@tonic-gate 			if (ddi_peek32(dip, (int32_t *)(hp->ah_addr + offset),
7220Sstevel@tonic-gate 			    (int32_t *)p) == DDI_SUCCESS) {
7230Sstevel@tonic-gate 				/* it is readable register. set the bit */
7240Sstevel@tonic-gate 				maskbuf[i >> INDEX_SHIFT] |=
7250Sstevel@tonic-gate 				    (uint8_t)(1 << (i & BITMASK));
7260Sstevel@tonic-gate 			}
7270Sstevel@tonic-gate 			p++;
7280Sstevel@tonic-gate 			offset += sizeof (uint32_t);
7290Sstevel@tonic-gate 		}
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 		if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
7320Sstevel@tonic-gate 		    SAVED_CONFIG_REGS_MASK, (uchar_t *)maskbuf,
7330Sstevel@tonic-gate 		    maskbufsz)) != DDI_PROP_SUCCESS) {
7340Sstevel@tonic-gate 			cmn_err(CE_WARN, "couldn't create %s property while"
7350Sstevel@tonic-gate 			    "saving config space for %s@%d\n",
7360Sstevel@tonic-gate 			    SAVED_CONFIG_REGS_MASK, ddi_driver_name(dip),
7370Sstevel@tonic-gate 			    ddi_get_instance(dip));
7380Sstevel@tonic-gate 		} else if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE,
7390Sstevel@tonic-gate 		    dip, SAVED_CONFIG_REGS, (uchar_t *)regbuf,
7400Sstevel@tonic-gate 		    (size_t)PCIE_CONF_HDR_SIZE)) != DDI_PROP_SUCCESS) {
7410Sstevel@tonic-gate 			(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
7420Sstevel@tonic-gate 			    SAVED_CONFIG_REGS_MASK);
7430Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d can't update prop %s",
7440Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
7450Sstevel@tonic-gate 			    SAVED_CONFIG_REGS);
7460Sstevel@tonic-gate 		}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 		kmem_free(maskbuf, (size_t)maskbufsz);
7490Sstevel@tonic-gate 		kmem_free(regbuf, (size_t)PCIE_CONF_HDR_SIZE);
7500Sstevel@tonic-gate 	} else {
7510Sstevel@tonic-gate 		regbuf = (uint32_t *)kmem_zalloc((size_t)PCI_CONF_HDR_SIZE,
7520Sstevel@tonic-gate 			    KM_SLEEP);
7530Sstevel@tonic-gate 		chsp = (pci_config_header_state_t *)regbuf;
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		chsp->chs_command = pci_config_get16(confhdl, PCI_CONF_COMM);
7560Sstevel@tonic-gate 		chsp->chs_header_type =	pci_config_get8(confhdl,
7570Sstevel@tonic-gate 			    PCI_CONF_HEADER);
7580Sstevel@tonic-gate 		if ((chsp->chs_header_type & PCI_HEADER_TYPE_M) ==
7590Sstevel@tonic-gate 		    PCI_HEADER_ONE)
7600Sstevel@tonic-gate 			chsp->chs_bridge_control =
7610Sstevel@tonic-gate 			    pci_config_get16(confhdl, PCI_BCNF_BCNTRL);
7620Sstevel@tonic-gate 		chsp->chs_cache_line_size = pci_config_get8(confhdl,
7630Sstevel@tonic-gate 		    PCI_CONF_CACHE_LINESZ);
7640Sstevel@tonic-gate 		chsp->chs_latency_timer = pci_config_get8(confhdl,
7650Sstevel@tonic-gate 		    PCI_CONF_LATENCY_TIMER);
7660Sstevel@tonic-gate 		if ((chsp->chs_header_type & PCI_HEADER_TYPE_M) ==
7670Sstevel@tonic-gate 		    PCI_HEADER_ONE) {
7680Sstevel@tonic-gate 			chsp->chs_sec_latency_timer =
7690Sstevel@tonic-gate 			    pci_config_get8(confhdl, PCI_BCNF_LATENCY_TIMER);
7700Sstevel@tonic-gate 		}
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		chsp->chs_base0 = pci_config_get32(confhdl, PCI_CONF_BASE0);
7730Sstevel@tonic-gate 		chsp->chs_base1 = pci_config_get32(confhdl, PCI_CONF_BASE1);
7740Sstevel@tonic-gate 		chsp->chs_base2 = pci_config_get32(confhdl, PCI_CONF_BASE2);
7750Sstevel@tonic-gate 		chsp->chs_base3 = pci_config_get32(confhdl, PCI_CONF_BASE3);
7760Sstevel@tonic-gate 		chsp->chs_base4 = pci_config_get32(confhdl, PCI_CONF_BASE4);
7770Sstevel@tonic-gate 		chsp->chs_base5 = pci_config_get32(confhdl, PCI_CONF_BASE5);
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 		/*
7800Sstevel@tonic-gate 		 * Allocate maximum space required for capability descriptions.
7810Sstevel@tonic-gate 		 * The maximum number of capabilties saved is the number of
7820Sstevel@tonic-gate 		 * capabilities listed in the pci_cap_table.
7830Sstevel@tonic-gate 		 */
7840Sstevel@tonic-gate 		ncaps = (sizeof (pci_cap_table) / sizeof (pci_cap_entry_t));
7850Sstevel@tonic-gate 		capbufsz = ncaps * sizeof (pci_cap_save_desc_t);
7860Sstevel@tonic-gate 		pci_cap_descp = (pci_cap_save_desc_t *)kmem_zalloc(
7870Sstevel@tonic-gate 		    capbufsz, KM_SLEEP);
7880Sstevel@tonic-gate 		p = (uint32_t *)((caddr_t)regbuf +
7890Sstevel@tonic-gate 		    sizeof (pci_config_header_state_t));
7900Sstevel@tonic-gate 		nwords = pci_save_caps(confhdl, p, pci_cap_descp, &ncaps);
7910Sstevel@tonic-gate 		regbufsz = sizeof (pci_config_header_state_t) +
7920Sstevel@tonic-gate 		    nwords * sizeof (uint32_t);
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 		if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
7950Sstevel@tonic-gate 		    SAVED_CONFIG_REGS, (uchar_t *)regbuf, regbufsz)) !=
7960Sstevel@tonic-gate 		    DDI_PROP_SUCCESS) {
7970Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d can't update prop %s",
7980Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
7990Sstevel@tonic-gate 			    SAVED_CONFIG_REGS);
8000Sstevel@tonic-gate 		} else if (ncaps) {
8010Sstevel@tonic-gate 			ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
8020Sstevel@tonic-gate 			    SAVED_CONFIG_REGS_CAPINFO, (uchar_t *)pci_cap_descp,
8030Sstevel@tonic-gate 			    ncaps * sizeof (pci_cap_save_desc_t));
8040Sstevel@tonic-gate 			if (ret != DDI_PROP_SUCCESS)
8050Sstevel@tonic-gate 				(void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
8060Sstevel@tonic-gate 				    SAVED_CONFIG_REGS);
8070Sstevel@tonic-gate 		}
8080Sstevel@tonic-gate 		kmem_free(regbuf, (size_t)PCI_CONF_HDR_SIZE);
8090Sstevel@tonic-gate 		kmem_free(pci_cap_descp, capbufsz);
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate 	pci_config_teardown(&confhdl);
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	if (ret != DDI_PROP_SUCCESS)
8140Sstevel@tonic-gate 		return (DDI_FAILURE);
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	return (DDI_SUCCESS);
8170Sstevel@tonic-gate }
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate /*
8200Sstevel@tonic-gate  * Saves registers associated with PCI capabilities.
8210Sstevel@tonic-gate  * Returns number of 32 bit words saved.
8220Sstevel@tonic-gate  * Number of capabilities saved is returned in ncapsp.
8230Sstevel@tonic-gate  */
8240Sstevel@tonic-gate static uint32_t
8250Sstevel@tonic-gate pci_save_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf,
8260Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	return (cap_walk_and_save(confhdl, regbuf, cap_descp, ncapsp, 0));
8290Sstevel@tonic-gate }
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate static uint32_t
8320Sstevel@tonic-gate cap_walk_and_save(ddi_acc_handle_t confhdl, uint32_t *regbuf,
8330Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp, int xspace)
8340Sstevel@tonic-gate {
8350Sstevel@tonic-gate 	pci_cap_entry_t *pci_cap_entp;
8360Sstevel@tonic-gate 	uint16_t cap_id, offset;
8370Sstevel@tonic-gate 	uint32_t words_saved = 0, nwords = 0;
8380Sstevel@tonic-gate 	uint16_t cap_ptr = PCI_CAP_NEXT_PTR_NULL;
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	*ncapsp = 0;
8410Sstevel@tonic-gate 	if (!xspace)
8420Sstevel@tonic-gate 		cap_ptr = pci_config_get8(confhdl, PCI_BCNF_CAP_PTR);
8430Sstevel@tonic-gate 	/*
8440Sstevel@tonic-gate 	 * Walk the capabilities
8450Sstevel@tonic-gate 	 */
8460Sstevel@tonic-gate 	while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
8470Sstevel@tonic-gate 		cap_id = CAP_ID(confhdl, cap_ptr, xspace);
8480Sstevel@tonic-gate 		/* Search for this cap id in our table */
8490Sstevel@tonic-gate 		if (!xspace)
8500Sstevel@tonic-gate 			pci_cap_entp = pci_cap_table;
8510Sstevel@tonic-gate 		while (pci_cap_entp->cap_id != PCI_CAP_NEXT_PTR_NULL &&
8520Sstevel@tonic-gate 		    pci_cap_entp->cap_id != cap_id)
8530Sstevel@tonic-gate 			pci_cap_entp++;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		offset = cap_ptr;
8560Sstevel@tonic-gate 		cap_ptr = NEXT_CAP(confhdl, cap_ptr, xspace);
8570Sstevel@tonic-gate 		/*
8580Sstevel@tonic-gate 		 * If this cap id is not found in the table, there is nothing
8590Sstevel@tonic-gate 		 * to save.
8600Sstevel@tonic-gate 		 */
8610Sstevel@tonic-gate 		if (pci_cap_entp->cap_id == PCI_CAP_NEXT_PTR_NULL)
8620Sstevel@tonic-gate 			continue;
8630Sstevel@tonic-gate 		if (pci_cap_entp->cap_save_func) {
8640Sstevel@tonic-gate 			if ((nwords = pci_cap_entp->cap_save_func(confhdl,
8650Sstevel@tonic-gate 			    offset, regbuf, pci_cap_entp->cap_ndwords))) {
8660Sstevel@tonic-gate 				cap_descp->cap_nregs = nwords;
8670Sstevel@tonic-gate 				cap_descp->cap_offset = offset;
8680Sstevel@tonic-gate 				cap_descp->cap_id = cap_id;
8690Sstevel@tonic-gate 				regbuf += nwords;
8700Sstevel@tonic-gate 				cap_descp++;
8710Sstevel@tonic-gate 				words_saved += nwords;
8720Sstevel@tonic-gate 				(*ncapsp)++;
8730Sstevel@tonic-gate 			}
8740Sstevel@tonic-gate 		}
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	}
8770Sstevel@tonic-gate 	return (words_saved);
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate static void
8810Sstevel@tonic-gate pci_fill_buf(ddi_acc_handle_t confhdl, uint16_t cap_ptr,
8820Sstevel@tonic-gate     uint32_t *regbuf, uint32_t nwords)
8830Sstevel@tonic-gate {
8840Sstevel@tonic-gate 	int i;
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 	for (i = 0; i < nwords; i++) {
8870Sstevel@tonic-gate 		*regbuf = pci_config_get32(confhdl, cap_ptr);
8880Sstevel@tonic-gate 		regbuf++;
8890Sstevel@tonic-gate 		cap_ptr += 4;
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate }
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate static uint32_t
8940Sstevel@tonic-gate pci_generic_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf,
8950Sstevel@tonic-gate     uint32_t nwords)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate 	pci_fill_buf(confhdl, cap_ptr, regbuf, nwords);
8980Sstevel@tonic-gate 	return (nwords);
8990Sstevel@tonic-gate }
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate /*ARGSUSED*/
9020Sstevel@tonic-gate static uint32_t
9030Sstevel@tonic-gate pci_msi_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf,
9040Sstevel@tonic-gate     uint32_t notused)
9050Sstevel@tonic-gate {
9060Sstevel@tonic-gate 	uint32_t nwords = PCI_MSI_MIN_WORDS;
9070Sstevel@tonic-gate 	uint16_t msi_ctrl;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	/* Figure out how many registers to be saved */
9100Sstevel@tonic-gate 	msi_ctrl = pci_config_get16(confhdl, cap_ptr + PCI_MSI_CTRL);
9110Sstevel@tonic-gate 	/* If 64 bit address capable add one word */
9120Sstevel@tonic-gate 	if (msi_ctrl & PCI_MSI_64BIT_MASK)
9130Sstevel@tonic-gate 		nwords++;
9140Sstevel@tonic-gate 	/* If per vector masking capable, add two more words */
9150Sstevel@tonic-gate 	if (msi_ctrl & PCI_MSI_PVM_MASK)
9160Sstevel@tonic-gate 		nwords += 2;
9170Sstevel@tonic-gate 	pci_fill_buf(confhdl, cap_ptr, regbuf, nwords);
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	return (nwords);
9200Sstevel@tonic-gate }
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate /*ARGSUSED*/
9230Sstevel@tonic-gate static uint32_t
9240Sstevel@tonic-gate pci_pcix_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf,
9250Sstevel@tonic-gate     uint32_t notused)
9260Sstevel@tonic-gate {
9270Sstevel@tonic-gate 	uint32_t nwords = PCI_PCIX_MIN_WORDS;
9280Sstevel@tonic-gate 	uint16_t pcix_command;
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	/* Figure out how many registers to be saved */
9310Sstevel@tonic-gate 	pcix_command = pci_config_get16(confhdl, cap_ptr + PCI_PCIX_COMMAND);
9320Sstevel@tonic-gate 	/* If it is version 1 or version 2, add 4 words */
9330Sstevel@tonic-gate 	if (((pcix_command & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_1) ||
9340Sstevel@tonic-gate 	    ((pcix_command & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2))
9350Sstevel@tonic-gate 		nwords += 4;
9360Sstevel@tonic-gate 	pci_fill_buf(confhdl, cap_ptr, regbuf, nwords);
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 	return (nwords);
9390Sstevel@tonic-gate }
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate /*ARGSUSED*/
9420Sstevel@tonic-gate static uint32_t
9430Sstevel@tonic-gate pci_pcie_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf,
9440Sstevel@tonic-gate     uint32_t notused)
9450Sstevel@tonic-gate {
9460Sstevel@tonic-gate 	return (0);
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate static void
9500Sstevel@tonic-gate pci_pmcap_check(ddi_acc_handle_t confhdl, uint32_t *regbuf,
9510Sstevel@tonic-gate     uint16_t pmcap_offset)
9520Sstevel@tonic-gate {
9530Sstevel@tonic-gate 	uint16_t pmcsr;
9540Sstevel@tonic-gate 	uint16_t pmcsr_offset = pmcap_offset + PCI_PMCSR;
9550Sstevel@tonic-gate 	uint32_t *saved_pmcsrp = (uint32_t *)((caddr_t)regbuf + PCI_PMCSR);
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	/*
9580Sstevel@tonic-gate 	 * Copy the power state bits from the PMCSR to our saved copy.
9590Sstevel@tonic-gate 	 * This is to make sure that we don't change the D state when
9600Sstevel@tonic-gate 	 * we restore config space of the device.
9610Sstevel@tonic-gate 	 */
9620Sstevel@tonic-gate 	pmcsr = pci_config_get16(confhdl, pmcsr_offset);
9630Sstevel@tonic-gate 	(*saved_pmcsrp) &= ~PCI_PMCSR_STATE_MASK;
9640Sstevel@tonic-gate 	(*saved_pmcsrp) |= (pmcsr & PCI_PMCSR_STATE_MASK);
9650Sstevel@tonic-gate }
9660Sstevel@tonic-gate 
9670Sstevel@tonic-gate static void
9680Sstevel@tonic-gate pci_restore_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf,
9690Sstevel@tonic-gate     pci_cap_save_desc_t *cap_descp, uint32_t elements)
9700Sstevel@tonic-gate {
9710Sstevel@tonic-gate 	int i, j;
9720Sstevel@tonic-gate 	uint16_t offset;
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	for (i = 0; i < (elements / sizeof (pci_cap_save_desc_t)); i++) {
9750Sstevel@tonic-gate 		offset = cap_descp->cap_offset;
9760Sstevel@tonic-gate 		if (cap_descp->cap_id == PCI_CAP_ID_PM)
9770Sstevel@tonic-gate 			pci_pmcap_check(confhdl, regbuf, offset);
9780Sstevel@tonic-gate 		for (j = 0; j < cap_descp->cap_nregs; j++) {
9790Sstevel@tonic-gate 			pci_config_put32(confhdl, offset, *regbuf);
9800Sstevel@tonic-gate 			regbuf++;
9810Sstevel@tonic-gate 			offset += 4;
9820Sstevel@tonic-gate 		}
9830Sstevel@tonic-gate 		cap_descp++;
9840Sstevel@tonic-gate 	}
9850Sstevel@tonic-gate }
9860Sstevel@tonic-gate 
9870Sstevel@tonic-gate /*
9880Sstevel@tonic-gate  * Restore config_regs from a single devinfo node.
9890Sstevel@tonic-gate  */
9900Sstevel@tonic-gate int
9910Sstevel@tonic-gate pci_restore_config_regs(dev_info_t *dip)
9920Sstevel@tonic-gate {
9930Sstevel@tonic-gate 	ddi_acc_handle_t confhdl;
9940Sstevel@tonic-gate 	pci_config_header_state_t *chs_p;
9950Sstevel@tonic-gate 	pci_cap_save_desc_t *cap_descp;
9960Sstevel@tonic-gate 	uint32_t elements, i;
9970Sstevel@tonic-gate 	uint8_t *maskbuf;
9980Sstevel@tonic-gate 	uint32_t *regbuf, *p;
9990Sstevel@tonic-gate 	off_t offset = 0;
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 	if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) {
10020Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d can't get config handle",
10030Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip));
10040Sstevel@tonic-gate 		return (DDI_FAILURE);
10050Sstevel@tonic-gate 	}
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
10080Sstevel@tonic-gate 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS_MASK,
10090Sstevel@tonic-gate 	    (uchar_t **)&maskbuf, &elements) == DDI_PROP_SUCCESS) {
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
10120Sstevel@tonic-gate 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS,
10130Sstevel@tonic-gate 		    (uchar_t **)&regbuf, &elements) != DDI_PROP_SUCCESS) {
10140Sstevel@tonic-gate 			goto restoreconfig_err;
10150Sstevel@tonic-gate 		}
10160Sstevel@tonic-gate 		ASSERT(elements == PCIE_CONF_HDR_SIZE);
10170Sstevel@tonic-gate 		/* pcie device and has 4k config space saved */
10180Sstevel@tonic-gate 		p = regbuf;
10190Sstevel@tonic-gate 		for (i = 0; i < PCIE_CONF_HDR_SIZE / sizeof (uint32_t); i++) {
10200Sstevel@tonic-gate 			/* If the word is readable then restore it */
10210Sstevel@tonic-gate 			if (maskbuf[i >> INDEX_SHIFT] &
10220Sstevel@tonic-gate 			    (uint8_t)(1 << (i & BITMASK)))
10230Sstevel@tonic-gate 				pci_config_put32(confhdl, offset, *p);
10240Sstevel@tonic-gate 			p++;
10250Sstevel@tonic-gate 			offset += sizeof (uint32_t);
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		ddi_prop_free(regbuf);
10280Sstevel@tonic-gate 		ddi_prop_free(maskbuf);
10290Sstevel@tonic-gate 		if (ndi_prop_remove(DDI_DEV_T_NONE, dip,
10300Sstevel@tonic-gate 		    SAVED_CONFIG_REGS_MASK) != DDI_PROP_SUCCESS) {
10310Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d can't remove prop %s",
10320Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
10330Sstevel@tonic-gate 			    SAVED_CONFIG_REGS_MASK);
10340Sstevel@tonic-gate 		}
10350Sstevel@tonic-gate 	} else {
10360Sstevel@tonic-gate 		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
10370Sstevel@tonic-gate 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS,
10380Sstevel@tonic-gate 		    (uchar_t **)&regbuf, &elements) != DDI_PROP_SUCCESS) {
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 			pci_config_teardown(&confhdl);
10410Sstevel@tonic-gate 			return (DDI_FAILURE);
10420Sstevel@tonic-gate 		}
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate 		chs_p = (pci_config_header_state_t *)regbuf;
10450Sstevel@tonic-gate 		pci_config_put16(confhdl, PCI_CONF_COMM,
10460Sstevel@tonic-gate 		    chs_p->chs_command);
10470Sstevel@tonic-gate 		if ((chs_p->chs_header_type & PCI_HEADER_TYPE_M) ==
10480Sstevel@tonic-gate 		    PCI_HEADER_ONE) {
10490Sstevel@tonic-gate 			pci_config_put16(confhdl, PCI_BCNF_BCNTRL,
10500Sstevel@tonic-gate 			    chs_p->chs_bridge_control);
10510Sstevel@tonic-gate 		}
10520Sstevel@tonic-gate 		pci_config_put8(confhdl, PCI_CONF_CACHE_LINESZ,
10530Sstevel@tonic-gate 		    chs_p->chs_cache_line_size);
10540Sstevel@tonic-gate 		pci_config_put8(confhdl, PCI_CONF_LATENCY_TIMER,
10550Sstevel@tonic-gate 		    chs_p->chs_latency_timer);
10560Sstevel@tonic-gate 		if ((chs_p->chs_header_type & PCI_HEADER_TYPE_M) ==
10570Sstevel@tonic-gate 		    PCI_HEADER_ONE)
10580Sstevel@tonic-gate 			pci_config_put8(confhdl, PCI_BCNF_LATENCY_TIMER,
10590Sstevel@tonic-gate 			    chs_p->chs_sec_latency_timer);
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE0, chs_p->chs_base0);
10620Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE1, chs_p->chs_base1);
10630Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE2, chs_p->chs_base2);
10640Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE3, chs_p->chs_base3);
10650Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE4, chs_p->chs_base4);
10660Sstevel@tonic-gate 		pci_config_put32(confhdl, PCI_CONF_BASE5, chs_p->chs_base5);
10670Sstevel@tonic-gate 
10680Sstevel@tonic-gate 		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip,
10690Sstevel@tonic-gate 		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
10700Sstevel@tonic-gate 		    SAVED_CONFIG_REGS_CAPINFO,
10710Sstevel@tonic-gate 		    (uchar_t **)&cap_descp, &elements) == DDI_PROP_SUCCESS) {
10720Sstevel@tonic-gate 			/*
10730Sstevel@tonic-gate 			 * PCI capability related regsiters are saved.
10740Sstevel@tonic-gate 			 * Restore them based on the description.
10750Sstevel@tonic-gate 			 */
10760Sstevel@tonic-gate 			p = (uint32_t *)((caddr_t)regbuf +
10770Sstevel@tonic-gate 			    sizeof (pci_config_header_state_t));
10780Sstevel@tonic-gate 			pci_restore_caps(confhdl, p, cap_descp, elements);
10790Sstevel@tonic-gate 			ddi_prop_free(cap_descp);
10800Sstevel@tonic-gate 		}
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 		ddi_prop_free(regbuf);
10830Sstevel@tonic-gate 	}
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	/*
10860Sstevel@tonic-gate 	 * Make sure registers are flushed
10870Sstevel@tonic-gate 	 */
10880Sstevel@tonic-gate 	(void) pci_config_get32(confhdl, PCI_CONF_BASE5);
10890Sstevel@tonic-gate 
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	if (ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS) !=
10920Sstevel@tonic-gate 	    DDI_PROP_SUCCESS) {
10930Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d can't remove prop %s",
10940Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip),
10950Sstevel@tonic-gate 		    SAVED_CONFIG_REGS);
10960Sstevel@tonic-gate 	}
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 	pci_config_teardown(&confhdl);
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	return (DDI_SUCCESS);
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate restoreconfig_err:
11030Sstevel@tonic-gate 	ddi_prop_free(maskbuf);
11040Sstevel@tonic-gate 	if (ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS_MASK) !=
11050Sstevel@tonic-gate 	    DDI_PROP_SUCCESS) {
11060Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d can't remove prop %s",
11070Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip),
11080Sstevel@tonic-gate 		    SAVED_CONFIG_REGS_MASK);
11090Sstevel@tonic-gate 	}
11100Sstevel@tonic-gate 	pci_config_teardown(&confhdl);
11110Sstevel@tonic-gate 	return (DDI_FAILURE);
11120Sstevel@tonic-gate }
1113