xref: /illumos-gate/usr/src/uts/common/os/pcifm.c (revision 3fe80ca4a1f8a033d672a9a2e6e4babac651205a)
100d0963fSdilpreet /*
200d0963fSdilpreet  * CDDL HEADER START
300d0963fSdilpreet  *
400d0963fSdilpreet  * The contents of this file are subject to the terms of the
500d0963fSdilpreet  * Common Development and Distribution License (the "License").
600d0963fSdilpreet  * You may not use this file except in compliance with the License.
700d0963fSdilpreet  *
800d0963fSdilpreet  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
900d0963fSdilpreet  * or http://www.opensolaris.org/os/licensing.
1000d0963fSdilpreet  * See the License for the specific language governing permissions
1100d0963fSdilpreet  * and limitations under the License.
1200d0963fSdilpreet  *
1300d0963fSdilpreet  * When distributing Covered Code, include this CDDL HEADER in each
1400d0963fSdilpreet  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1500d0963fSdilpreet  * If applicable, add the following below this CDDL HEADER, with the
1600d0963fSdilpreet  * fields enclosed by brackets "[]" replaced with your own identifying
1700d0963fSdilpreet  * information: Portions Copyright [yyyy] [name of copyright owner]
1800d0963fSdilpreet  *
1900d0963fSdilpreet  * CDDL HEADER END
2000d0963fSdilpreet  */
2100d0963fSdilpreet 
2200d0963fSdilpreet /*
23392e836bSGavin Maltby  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2400d0963fSdilpreet  */
2500d0963fSdilpreet 
26*3fe80ca4SDan Cross /*
27*3fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
28*3fe80ca4SDan Cross  */
29*3fe80ca4SDan Cross 
3000d0963fSdilpreet #include <sys/types.h>
3100d0963fSdilpreet #include <sys/sunndi.h>
3200d0963fSdilpreet #include <sys/sysmacros.h>
3300d0963fSdilpreet #include <sys/ddifm_impl.h>
3400d0963fSdilpreet #include <sys/fm/util.h>
3500d0963fSdilpreet #include <sys/fm/protocol.h>
3600d0963fSdilpreet #include <sys/fm/io/pci.h>
3700d0963fSdilpreet #include <sys/fm/io/ddi.h>
3800d0963fSdilpreet #include <sys/pci.h>
3949fbdd30SErwin T Tsaur #include <sys/pci_cap.h>
4000d0963fSdilpreet #include <sys/pci_impl.h>
4100d0963fSdilpreet #include <sys/epm.h>
4200d0963fSdilpreet #include <sys/pcifm.h>
4300d0963fSdilpreet 
4400d0963fSdilpreet #define	PCIX_ECC_VER_CHECK(x)	(((x) == PCI_PCIX_VER_1) ||\
4500d0963fSdilpreet 				((x) == PCI_PCIX_VER_2))
4600d0963fSdilpreet 
4700d0963fSdilpreet errorq_t *pci_target_queue = NULL;
4800d0963fSdilpreet 
4900d0963fSdilpreet pci_fm_err_t pci_err_tbl[] = {
5000d0963fSdilpreet 	PCI_DET_PERR,	PCI_STAT_PERROR,	NULL,		DDI_FM_UNKNOWN,
5100d0963fSdilpreet 	PCI_MDPE,	PCI_STAT_S_PERROR,	PCI_TARG_MDPE,	DDI_FM_UNKNOWN,
5200d0963fSdilpreet 	PCI_SIG_SERR,	PCI_STAT_S_SYSERR,	NULL,		DDI_FM_FATAL,
5300d0963fSdilpreet 	PCI_MA,		PCI_STAT_R_MAST_AB,	PCI_TARG_MA,	DDI_FM_UNKNOWN,
5400d0963fSdilpreet 	PCI_REC_TA,	PCI_STAT_R_TARG_AB,	PCI_TARG_REC_TA, DDI_FM_UNKNOWN,
5500d0963fSdilpreet 	PCI_SIG_TA,	PCI_STAT_S_TARG_AB,	NULL,		DDI_FM_UNKNOWN,
567e12ceb3SToomas Soome 	NULL, 0, NULL, DDI_FM_OK,
5700d0963fSdilpreet };
5800d0963fSdilpreet 
5900d0963fSdilpreet pci_fm_err_t pci_bdg_err_tbl[] = {
6000d0963fSdilpreet 	PCI_DET_PERR,	PCI_STAT_PERROR,	NULL,		DDI_FM_UNKNOWN,
6100d0963fSdilpreet 	PCI_MDPE,	PCI_STAT_S_PERROR,	PCI_TARG_MDPE,	DDI_FM_UNKNOWN,
6200d0963fSdilpreet 	PCI_REC_SERR,	PCI_STAT_S_SYSERR,	NULL,		DDI_FM_UNKNOWN,
638aec9182Sstephh #if defined(__sparc)
6400d0963fSdilpreet 	PCI_MA,		PCI_STAT_R_MAST_AB,	PCI_TARG_MA,	DDI_FM_UNKNOWN,
650c64a9b4Sanish #endif
6600d0963fSdilpreet 	PCI_REC_TA,	PCI_STAT_R_TARG_AB,	PCI_TARG_REC_TA, DDI_FM_UNKNOWN,
6700d0963fSdilpreet 	PCI_SIG_TA,	PCI_STAT_S_TARG_AB,	NULL,		DDI_FM_UNKNOWN,
687e12ceb3SToomas Soome 	NULL, 0, NULL, DDI_FM_OK,
6900d0963fSdilpreet };
7000d0963fSdilpreet 
7100d0963fSdilpreet static pci_fm_err_t pcix_err_tbl[] = {
7200d0963fSdilpreet 	PCIX_SPL_DIS,		PCI_PCIX_SPL_DSCD,	NULL,	DDI_FM_UNKNOWN,
7300d0963fSdilpreet 	PCIX_UNEX_SPL,		PCI_PCIX_UNEX_SPL,	NULL,	DDI_FM_UNKNOWN,
7400d0963fSdilpreet 	PCIX_RX_SPL_MSG,	PCI_PCIX_RX_SPL_MSG,	NULL,   DDI_FM_UNKNOWN,
757e12ceb3SToomas Soome 	NULL, 0, NULL, DDI_FM_OK,
7600d0963fSdilpreet };
7700d0963fSdilpreet 
7800d0963fSdilpreet static pci_fm_err_t pcix_sec_err_tbl[] = {
7900d0963fSdilpreet 	PCIX_SPL_DIS,		PCI_PCIX_BSS_SPL_DSCD,	NULL,	DDI_FM_UNKNOWN,
8000d0963fSdilpreet 	PCIX_UNEX_SPL,		PCI_PCIX_BSS_UNEX_SPL,	NULL,	DDI_FM_UNKNOWN,
81ac4d633fSstephh 	PCIX_BSS_SPL_OR,	PCI_PCIX_BSS_SPL_OR,	NULL,	DDI_FM_OK,
82ac4d633fSstephh 	PCIX_BSS_SPL_DLY,	PCI_PCIX_BSS_SPL_DLY,	NULL,	DDI_FM_OK,
837e12ceb3SToomas Soome 	NULL, 0, NULL, DDI_FM_OK,
8400d0963fSdilpreet };
8500d0963fSdilpreet 
8600d0963fSdilpreet static int
pci_config_check(ddi_acc_handle_t handle,int fme_flag)878aec9182Sstephh pci_config_check(ddi_acc_handle_t handle, int fme_flag)
8800d0963fSdilpreet {
8900d0963fSdilpreet 	ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle);
9000d0963fSdilpreet 	ddi_fm_error_t de;
9100d0963fSdilpreet 
9200d0963fSdilpreet 	if (!(DDI_FM_ACC_ERR_CAP(ddi_fm_capable(hp->ah_dip))))
9300d0963fSdilpreet 		return (DDI_FM_OK);
9400d0963fSdilpreet 
9500d0963fSdilpreet 	de.fme_version = DDI_FME_VERSION;
9600d0963fSdilpreet 
9700d0963fSdilpreet 	ddi_fm_acc_err_get(handle, &de, de.fme_version);
9800d0963fSdilpreet 	if (de.fme_status != DDI_FM_OK) {
998aec9182Sstephh 		if (fme_flag == DDI_FM_ERR_UNEXPECTED) {
10000d0963fSdilpreet 			char buf[FM_MAX_CLASS];
10100d0963fSdilpreet 
1028aec9182Sstephh 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
1038aec9182Sstephh 			    PCI_ERROR_SUBCLASS, PCI_NR);
1048aec9182Sstephh 			ddi_fm_ereport_post(hp->ah_dip, buf, de.fme_ena,
1058aec9182Sstephh 			    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, NULL);
1068aec9182Sstephh 		}
10700d0963fSdilpreet 		ddi_fm_acc_err_clear(handle, de.fme_version);
10800d0963fSdilpreet 	}
10900d0963fSdilpreet 	return (de.fme_status);
11000d0963fSdilpreet }
11100d0963fSdilpreet 
11200d0963fSdilpreet static void
pcix_ecc_regs_gather(pci_erpt_t * erpt_p,pcix_ecc_regs_t * pcix_ecc_regs,uint8_t pcix_cap_ptr,int fme_flag)11300d0963fSdilpreet pcix_ecc_regs_gather(pci_erpt_t *erpt_p, pcix_ecc_regs_t *pcix_ecc_regs,
1148aec9182Sstephh     uint8_t pcix_cap_ptr, int fme_flag)
11500d0963fSdilpreet {
11600d0963fSdilpreet 	int bdg = erpt_p->pe_dflags & PCI_BRIDGE_DEV;
11700d0963fSdilpreet 
11800d0963fSdilpreet 	pcix_ecc_regs->pcix_ecc_ctlstat = pci_config_get32(erpt_p->pe_hdl,
11900d0963fSdilpreet 	    (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_STATUS :
12000d0963fSdilpreet 	    PCI_PCIX_ECC_STATUS)));
1218aec9182Sstephh 	if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
12200d0963fSdilpreet 		pcix_ecc_regs->pcix_ecc_vflags |= PCIX_ERR_ECC_STS_VALID;
12300d0963fSdilpreet 	else
12400d0963fSdilpreet 		return;
12500d0963fSdilpreet 	pcix_ecc_regs->pcix_ecc_fstaddr = pci_config_get32(erpt_p->pe_hdl,
12600d0963fSdilpreet 	    (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_FST_AD :
12700d0963fSdilpreet 	    PCI_PCIX_ECC_FST_AD)));
12800d0963fSdilpreet 	pcix_ecc_regs->pcix_ecc_secaddr = pci_config_get32(erpt_p->pe_hdl,
12900d0963fSdilpreet 	    (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_SEC_AD :
13000d0963fSdilpreet 	    PCI_PCIX_ECC_SEC_AD)));
13100d0963fSdilpreet 	pcix_ecc_regs->pcix_ecc_attr = pci_config_get32((
13200d0963fSdilpreet 	    ddi_acc_handle_t)erpt_p->pe_hdl,
13300d0963fSdilpreet 	    (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_ATTR : PCI_PCIX_ECC_ATTR)));
13400d0963fSdilpreet }
13500d0963fSdilpreet 
13600d0963fSdilpreet static void
pcix_regs_gather(pci_erpt_t * erpt_p,void * pe_regs,int fme_flag)1378aec9182Sstephh pcix_regs_gather(pci_erpt_t *erpt_p, void *pe_regs, int fme_flag)
13800d0963fSdilpreet {
13900d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
14000d0963fSdilpreet 		pcix_bdg_error_regs_t *pcix_bdg_regs =
14100d0963fSdilpreet 		    (pcix_bdg_error_regs_t *)pe_regs;
14200d0963fSdilpreet 		uint8_t pcix_bdg_cap_ptr;
14300d0963fSdilpreet 		int i;
14400d0963fSdilpreet 
14500d0963fSdilpreet 		pcix_bdg_cap_ptr = pcix_bdg_regs->pcix_bdg_cap_ptr;
14600d0963fSdilpreet 		pcix_bdg_regs->pcix_bdg_sec_stat = pci_config_get16(
14700d0963fSdilpreet 		    erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_SEC_STATUS));
1488aec9182Sstephh 		if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
14900d0963fSdilpreet 			pcix_bdg_regs->pcix_bdg_vflags |=
15000d0963fSdilpreet 			    PCIX_BDG_SEC_STATUS_VALID;
15100d0963fSdilpreet 		else
15200d0963fSdilpreet 			return;
15300d0963fSdilpreet 		pcix_bdg_regs->pcix_bdg_stat = pci_config_get32(erpt_p->pe_hdl,
15400d0963fSdilpreet 		    (pcix_bdg_cap_ptr + PCI_PCIX_BDG_STATUS));
1558aec9182Sstephh 		if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
15600d0963fSdilpreet 			pcix_bdg_regs->pcix_bdg_vflags |= PCIX_BDG_STATUS_VALID;
15700d0963fSdilpreet 		else
15800d0963fSdilpreet 			return;
15900d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) {
16000d0963fSdilpreet 			pcix_ecc_regs_t *pcix_bdg_ecc_regs;
16149fbdd30SErwin T Tsaur 
16200d0963fSdilpreet 			for (i = 0; i < 2; i++) {
16300d0963fSdilpreet 				pcix_bdg_ecc_regs =
16400d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_ecc_regs[i];
16500d0963fSdilpreet 				pci_config_put32(erpt_p->pe_hdl,
16600d0963fSdilpreet 				    (pcix_bdg_cap_ptr +
16700d0963fSdilpreet 				    PCI_PCIX_BDG_ECC_STATUS), i);
16800d0963fSdilpreet 				pcix_ecc_regs_gather(erpt_p,
16900d0963fSdilpreet 				    pcix_bdg_ecc_regs,
1708aec9182Sstephh 				    pcix_bdg_cap_ptr, fme_flag);
17100d0963fSdilpreet 			}
17200d0963fSdilpreet 		}
17300d0963fSdilpreet 	} else {
17400d0963fSdilpreet 		pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)pe_regs;
17500d0963fSdilpreet 		uint8_t pcix_cap_ptr;
17600d0963fSdilpreet 
17700d0963fSdilpreet 		pcix_cap_ptr = pcix_regs->pcix_cap_ptr;
17800d0963fSdilpreet 
17900d0963fSdilpreet 		pcix_regs->pcix_command = pci_config_get16(erpt_p->pe_hdl,
18000d0963fSdilpreet 		    (pcix_cap_ptr + PCI_PCIX_COMMAND));
18100d0963fSdilpreet 		pcix_regs->pcix_status = pci_config_get32(erpt_p->pe_hdl,
18200d0963fSdilpreet 		    (pcix_cap_ptr + PCI_PCIX_STATUS));
1838aec9182Sstephh 		if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
18400d0963fSdilpreet 			pcix_regs->pcix_vflags |= PCIX_ERR_STATUS_VALID;
18500d0963fSdilpreet 		else
18600d0963fSdilpreet 			return;
18700d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) {
18800d0963fSdilpreet 			pcix_ecc_regs_t *pcix_ecc_regs =
18900d0963fSdilpreet 			    pcix_regs->pcix_ecc_regs;
19000d0963fSdilpreet 
19100d0963fSdilpreet 			pcix_ecc_regs_gather(erpt_p, pcix_ecc_regs,
1928aec9182Sstephh 			    pcix_cap_ptr, fme_flag);
19300d0963fSdilpreet 		}
19400d0963fSdilpreet 	}
19500d0963fSdilpreet }
19600d0963fSdilpreet 
19700d0963fSdilpreet /*ARGSUSED*/
19800d0963fSdilpreet static void
pci_regs_gather(dev_info_t * dip,pci_erpt_t * erpt_p,int fme_flag)1998aec9182Sstephh pci_regs_gather(dev_info_t *dip, pci_erpt_t *erpt_p, int fme_flag)
20000d0963fSdilpreet {
20100d0963fSdilpreet 	pci_error_regs_t *pci_regs = erpt_p->pe_pci_regs;
20200d0963fSdilpreet 
20300d0963fSdilpreet 	/*
20400d0963fSdilpreet 	 * Start by reading all the error registers that are available for
20500d0963fSdilpreet 	 * pci and pci express and for leaf devices and bridges/switches
20600d0963fSdilpreet 	 */
20700d0963fSdilpreet 	pci_regs->pci_err_status = pci_config_get16(erpt_p->pe_hdl,
20800d0963fSdilpreet 	    PCI_CONF_STAT);
2098aec9182Sstephh 	if (pci_config_check(erpt_p->pe_hdl, fme_flag) != DDI_FM_OK)
21000d0963fSdilpreet 		return;
21100d0963fSdilpreet 	pci_regs->pci_vflags |= PCI_ERR_STATUS_VALID;
21200d0963fSdilpreet 	pci_regs->pci_cfg_comm = pci_config_get16(erpt_p->pe_hdl,
21300d0963fSdilpreet 	    PCI_CONF_COMM);
2148aec9182Sstephh 	if (pci_config_check(erpt_p->pe_hdl, fme_flag) != DDI_FM_OK)
21500d0963fSdilpreet 		return;
21600d0963fSdilpreet 
21700d0963fSdilpreet 	/*
21800d0963fSdilpreet 	 * If pci-pci bridge grab PCI bridge specific error registers.
21900d0963fSdilpreet 	 */
22000d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
22100d0963fSdilpreet 		pci_regs->pci_bdg_regs->pci_bdg_sec_stat =
22200d0963fSdilpreet 		    pci_config_get16(erpt_p->pe_hdl, PCI_BCNF_SEC_STATUS);
2238aec9182Sstephh 		if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
22400d0963fSdilpreet 			pci_regs->pci_bdg_regs->pci_bdg_vflags |=
22500d0963fSdilpreet 			    PCI_BDG_SEC_STAT_VALID;
22600d0963fSdilpreet 		pci_regs->pci_bdg_regs->pci_bdg_ctrl =
22700d0963fSdilpreet 		    pci_config_get16(erpt_p->pe_hdl, PCI_BCNF_BCNTRL);
2288aec9182Sstephh 		if (pci_config_check(erpt_p->pe_hdl, fme_flag) == DDI_FM_OK)
22900d0963fSdilpreet 			pci_regs->pci_bdg_regs->pci_bdg_vflags |=
23000d0963fSdilpreet 			    PCI_BDG_CTRL_VALID;
23100d0963fSdilpreet 	}
23200d0963fSdilpreet 
23349fbdd30SErwin T Tsaur 	/* If pci-x device grab error registers */
23449fbdd30SErwin T Tsaur 	if (erpt_p->pe_dflags & PCIX_DEV)
2358aec9182Sstephh 		pcix_regs_gather(erpt_p, erpt_p->pe_regs, fme_flag);
23600d0963fSdilpreet 
23700d0963fSdilpreet }
23800d0963fSdilpreet 
23900d0963fSdilpreet static void
pcix_regs_clear(pci_erpt_t * erpt_p,void * pe_regs)24000d0963fSdilpreet pcix_regs_clear(pci_erpt_t *erpt_p, void *pe_regs)
24100d0963fSdilpreet {
24200d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
24300d0963fSdilpreet 		pcix_bdg_error_regs_t *pcix_bdg_regs =
24400d0963fSdilpreet 		    (pcix_bdg_error_regs_t *)pe_regs;
24500d0963fSdilpreet 		uint8_t pcix_bdg_cap_ptr;
24600d0963fSdilpreet 		int i;
24700d0963fSdilpreet 
24800d0963fSdilpreet 		pcix_bdg_cap_ptr = pcix_bdg_regs->pcix_bdg_cap_ptr;
24900d0963fSdilpreet 
25000d0963fSdilpreet 		if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_SEC_STATUS_VALID)
25100d0963fSdilpreet 			pci_config_put16(erpt_p->pe_hdl,
25200d0963fSdilpreet 			    (pcix_bdg_cap_ptr + PCI_PCIX_SEC_STATUS),
25300d0963fSdilpreet 			    pcix_bdg_regs->pcix_bdg_sec_stat);
25400d0963fSdilpreet 
25500d0963fSdilpreet 		if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_STATUS_VALID)
25600d0963fSdilpreet 			pci_config_put32(erpt_p->pe_hdl,
25700d0963fSdilpreet 			    (pcix_bdg_cap_ptr + PCI_PCIX_BDG_STATUS),
25800d0963fSdilpreet 			    pcix_bdg_regs->pcix_bdg_stat);
25900d0963fSdilpreet 
26000d0963fSdilpreet 		pcix_bdg_regs->pcix_bdg_vflags = 0x0;
26100d0963fSdilpreet 
26200d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) {
26300d0963fSdilpreet 			pcix_ecc_regs_t *pcix_bdg_ecc_regs;
26400d0963fSdilpreet 			for (i = 0; i < 2; i++) {
26500d0963fSdilpreet 				pcix_bdg_ecc_regs =
26600d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_ecc_regs[i];
26700d0963fSdilpreet 
26800d0963fSdilpreet 				if (pcix_bdg_ecc_regs->pcix_ecc_vflags &
26900d0963fSdilpreet 				    PCIX_ERR_ECC_STS_VALID) {
27000d0963fSdilpreet 					pci_config_put32(erpt_p->pe_hdl,
27100d0963fSdilpreet 					    (pcix_bdg_cap_ptr +
27200d0963fSdilpreet 					    PCI_PCIX_BDG_ECC_STATUS),
27300d0963fSdilpreet 					    i);
27400d0963fSdilpreet 
27500d0963fSdilpreet 					pci_config_put32(erpt_p->pe_hdl,
27600d0963fSdilpreet 					    (pcix_bdg_cap_ptr +
27700d0963fSdilpreet 					    PCI_PCIX_BDG_ECC_STATUS),
27800d0963fSdilpreet 					    pcix_bdg_ecc_regs->
27900d0963fSdilpreet 					    pcix_ecc_ctlstat);
28000d0963fSdilpreet 				}
28100d0963fSdilpreet 				pcix_bdg_ecc_regs->pcix_ecc_vflags =
28200d0963fSdilpreet 				    0x0;
28300d0963fSdilpreet 			}
28400d0963fSdilpreet 		}
28500d0963fSdilpreet 	} else {
28600d0963fSdilpreet 		pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)pe_regs;
28700d0963fSdilpreet 		uint8_t pcix_cap_ptr;
28800d0963fSdilpreet 
28900d0963fSdilpreet 		pcix_cap_ptr = pcix_regs->pcix_cap_ptr;
29000d0963fSdilpreet 
29100d0963fSdilpreet 		if (pcix_regs->pcix_vflags & PCIX_ERR_STATUS_VALID)
29200d0963fSdilpreet 			pci_config_put32(erpt_p->pe_hdl,
29300d0963fSdilpreet 			    (pcix_cap_ptr + PCI_PCIX_STATUS),
29400d0963fSdilpreet 			    pcix_regs->pcix_status);
29500d0963fSdilpreet 
29600d0963fSdilpreet 		pcix_regs->pcix_vflags = 0x0;
29700d0963fSdilpreet 
29800d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) {
29900d0963fSdilpreet 			pcix_ecc_regs_t *pcix_ecc_regs =
30000d0963fSdilpreet 			    pcix_regs->pcix_ecc_regs;
30100d0963fSdilpreet 
30200d0963fSdilpreet 			if (pcix_ecc_regs->pcix_ecc_vflags &
30300d0963fSdilpreet 			    PCIX_ERR_ECC_STS_VALID)
30400d0963fSdilpreet 				pci_config_put32(erpt_p->pe_hdl,
30500d0963fSdilpreet 				    (pcix_cap_ptr + PCI_PCIX_ECC_STATUS),
30600d0963fSdilpreet 				    pcix_ecc_regs->pcix_ecc_ctlstat);
30700d0963fSdilpreet 
30800d0963fSdilpreet 			pcix_ecc_regs->pcix_ecc_vflags = 0x0;
30900d0963fSdilpreet 		}
31000d0963fSdilpreet 	}
31100d0963fSdilpreet }
31200d0963fSdilpreet 
31300d0963fSdilpreet static void
pci_regs_clear(pci_erpt_t * erpt_p)31400d0963fSdilpreet pci_regs_clear(pci_erpt_t *erpt_p)
31500d0963fSdilpreet {
31600d0963fSdilpreet 	/*
31700d0963fSdilpreet 	 * Finally clear the error bits
31800d0963fSdilpreet 	 */
31949fbdd30SErwin T Tsaur 	if (erpt_p->pe_dflags & PCIX_DEV)
32000d0963fSdilpreet 		pcix_regs_clear(erpt_p, erpt_p->pe_regs);
32100d0963fSdilpreet 
32200d0963fSdilpreet 	if (erpt_p->pe_pci_regs->pci_vflags & PCI_ERR_STATUS_VALID)
32300d0963fSdilpreet 		pci_config_put16(erpt_p->pe_hdl, PCI_CONF_STAT,
32400d0963fSdilpreet 		    erpt_p->pe_pci_regs->pci_err_status);
32500d0963fSdilpreet 
32600d0963fSdilpreet 	erpt_p->pe_pci_regs->pci_vflags = 0x0;
32700d0963fSdilpreet 
32800d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
32900d0963fSdilpreet 		if (erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags &
33000d0963fSdilpreet 		    PCI_BDG_SEC_STAT_VALID)
33100d0963fSdilpreet 			pci_config_put16(erpt_p->pe_hdl, PCI_BCNF_SEC_STATUS,
33200d0963fSdilpreet 			    erpt_p->pe_pci_regs->pci_bdg_regs->
33300d0963fSdilpreet 			    pci_bdg_sec_stat);
33400d0963fSdilpreet 		if (erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags &
33500d0963fSdilpreet 		    PCI_BDG_CTRL_VALID)
33600d0963fSdilpreet 			pci_config_put16(erpt_p->pe_hdl, PCI_BCNF_BCNTRL,
33700d0963fSdilpreet 			    erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_ctrl);
33800d0963fSdilpreet 
33900d0963fSdilpreet 		erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags = 0x0;
34000d0963fSdilpreet 	}
34100d0963fSdilpreet }
34200d0963fSdilpreet 
34300d0963fSdilpreet /*
34400d0963fSdilpreet  * pcix_ereport_setup: Allocate structures for PCI-X error handling and ereport
34500d0963fSdilpreet  * generation.
34600d0963fSdilpreet  */
34700d0963fSdilpreet /* ARGSUSED */
34800d0963fSdilpreet static void
pcix_ereport_setup(dev_info_t * dip,pci_erpt_t * erpt_p)34900d0963fSdilpreet pcix_ereport_setup(dev_info_t *dip, pci_erpt_t *erpt_p)
35000d0963fSdilpreet {
35149fbdd30SErwin T Tsaur 	uint16_t pcix_cap_ptr = PCI_CAP_NEXT_PTR_NULL;
35249fbdd30SErwin T Tsaur 	ddi_acc_handle_t eh;
35300d0963fSdilpreet 	int i;
35400d0963fSdilpreet 
355be84494cSKrishna Elango 	if (pci_config_setup(dip, &eh) == DDI_SUCCESS) {
35649fbdd30SErwin T Tsaur 		(void) PCI_CAP_LOCATE(eh, PCI_CAP_ID_PCIX, &pcix_cap_ptr);
357be84494cSKrishna Elango 		pci_config_teardown(&eh);
35849fbdd30SErwin T Tsaur 	}
35900d0963fSdilpreet 
36000d0963fSdilpreet 	if (pcix_cap_ptr != PCI_CAP_NEXT_PTR_NULL)
36100d0963fSdilpreet 		erpt_p->pe_dflags |= PCIX_DEV;
36200d0963fSdilpreet 	else
36300d0963fSdilpreet 		return;
36400d0963fSdilpreet 
36500d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
36600d0963fSdilpreet 		pcix_bdg_error_regs_t *pcix_bdg_regs;
36700d0963fSdilpreet 
36800d0963fSdilpreet 		erpt_p->pe_regs = kmem_zalloc(sizeof (pcix_bdg_error_regs_t),
36900d0963fSdilpreet 		    KM_SLEEP);
37000d0963fSdilpreet 		pcix_bdg_regs = (pcix_bdg_error_regs_t *)erpt_p->pe_regs;
37100d0963fSdilpreet 		pcix_bdg_regs->pcix_bdg_cap_ptr = pcix_cap_ptr;
37200d0963fSdilpreet 		pcix_bdg_regs->pcix_bdg_ver = pci_config_get16(erpt_p->pe_hdl,
37300d0963fSdilpreet 		    pcix_cap_ptr + PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK;
37400d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) {
37500d0963fSdilpreet 			for (i = 0; i < 2; i++) {
37600d0963fSdilpreet 				pcix_bdg_regs->pcix_bdg_ecc_regs[i] =
37700d0963fSdilpreet 				    kmem_zalloc(sizeof (pcix_ecc_regs_t),
37800d0963fSdilpreet 				    KM_SLEEP);
37900d0963fSdilpreet 			}
38000d0963fSdilpreet 		}
38100d0963fSdilpreet 	} else {
38200d0963fSdilpreet 		pcix_error_regs_t *pcix_regs;
38300d0963fSdilpreet 
38400d0963fSdilpreet 		erpt_p->pe_regs = kmem_zalloc(sizeof (pcix_error_regs_t),
38500d0963fSdilpreet 		    KM_SLEEP);
38600d0963fSdilpreet 		pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs;
38700d0963fSdilpreet 		pcix_regs->pcix_cap_ptr = pcix_cap_ptr;
38800d0963fSdilpreet 		pcix_regs->pcix_ver = pci_config_get16(erpt_p->pe_hdl,
38900d0963fSdilpreet 		    pcix_cap_ptr + PCI_PCIX_COMMAND) & PCI_PCIX_VER_MASK;
39000d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) {
39100d0963fSdilpreet 			pcix_regs->pcix_ecc_regs = kmem_zalloc(
39200d0963fSdilpreet 			    sizeof (pcix_ecc_regs_t), KM_SLEEP);
39300d0963fSdilpreet 		}
39400d0963fSdilpreet 	}
39500d0963fSdilpreet }
39600d0963fSdilpreet 
39700d0963fSdilpreet /*
39800d0963fSdilpreet  * pci_ereport_setup: Detect PCI device type and initialize structures to be
39900d0963fSdilpreet  * used to generate ereports based on detected generic device errors.
40000d0963fSdilpreet  */
40100d0963fSdilpreet void
pci_ereport_setup(dev_info_t * dip)40200d0963fSdilpreet pci_ereport_setup(dev_info_t *dip)
40300d0963fSdilpreet {
40400d0963fSdilpreet 	struct dev_info *devi = DEVI(dip);
40500d0963fSdilpreet 	struct i_ddi_fmhdl *fmhdl = devi->devi_fmhdl;
40600d0963fSdilpreet 	pci_erpt_t *erpt_p;
40700d0963fSdilpreet 	uint8_t pci_hdr_type;
40800d0963fSdilpreet 	uint16_t pci_status;
40900d0963fSdilpreet 	pci_regspec_t *pci_rp;
41000d0963fSdilpreet 	int32_t len;
41100d0963fSdilpreet 	uint32_t phys_hi;
41200d0963fSdilpreet 
41300d0963fSdilpreet 	/*
41400d0963fSdilpreet 	 * If device is not ereport capbable then report an error against the
41500d0963fSdilpreet 	 * driver for using this interface,
41600d0963fSdilpreet 	 */
41700d0963fSdilpreet 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) &&
41800d0963fSdilpreet 	    !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) {
41900d0963fSdilpreet 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
42000d0963fSdilpreet 		return;
42100d0963fSdilpreet 	}
42200d0963fSdilpreet 
42300d0963fSdilpreet 	/*
42400d0963fSdilpreet 	 * ASSERT fmhdl exists and fh_bus_specific is NULL.
42500d0963fSdilpreet 	 */
42600d0963fSdilpreet 	ASSERT(fmhdl && (fmhdl->fh_bus_specific == NULL));
42700d0963fSdilpreet 
42800d0963fSdilpreet 	erpt_p = kmem_zalloc(sizeof (pci_erpt_t), KM_SLEEP);
42900d0963fSdilpreet 
43000d0963fSdilpreet 	if (pci_config_setup(dip, &erpt_p->pe_hdl) != DDI_SUCCESS)
43100d0963fSdilpreet 		goto error;
43200d0963fSdilpreet 
43300d0963fSdilpreet 	erpt_p->pe_pci_regs = kmem_zalloc(sizeof (pci_error_regs_t), KM_SLEEP);
43400d0963fSdilpreet 
43500d0963fSdilpreet 	pci_status = pci_config_get16(erpt_p->pe_hdl, PCI_CONF_STAT);
4368aec9182Sstephh 	if (pci_config_check(erpt_p->pe_hdl, DDI_FM_ERR_UNEXPECTED) !=
4378aec9182Sstephh 	    DDI_FM_OK)
43800d0963fSdilpreet 		goto error;
43900d0963fSdilpreet 
44000d0963fSdilpreet 	/*
44100d0963fSdilpreet 	 * Get header type and record if device is a bridge.
44200d0963fSdilpreet 	 */
44300d0963fSdilpreet 	pci_hdr_type = pci_config_get8(erpt_p->pe_hdl, PCI_CONF_HEADER);
4448aec9182Sstephh 	if (pci_config_check(erpt_p->pe_hdl, DDI_FM_ERR_UNEXPECTED) !=
4458aec9182Sstephh 	    DDI_FM_OK)
44600d0963fSdilpreet 		goto error;
44700d0963fSdilpreet 
44800d0963fSdilpreet 	/*
44900d0963fSdilpreet 	 * Check to see if PCI device is a bridge, if so allocate pci bridge
45000d0963fSdilpreet 	 * error register structure.
45100d0963fSdilpreet 	 */
45200d0963fSdilpreet 	if ((pci_hdr_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) {
45300d0963fSdilpreet 		erpt_p->pe_dflags |= PCI_BRIDGE_DEV;
45400d0963fSdilpreet 		erpt_p->pe_pci_regs->pci_bdg_regs = kmem_zalloc(
45500d0963fSdilpreet 		    sizeof (pci_bdg_error_regs_t), KM_SLEEP);
45600d0963fSdilpreet 	}
45700d0963fSdilpreet 
45800d0963fSdilpreet 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg",
45900d0963fSdilpreet 	    (caddr_t)&pci_rp, &len) == DDI_SUCCESS) {
46000d0963fSdilpreet 		phys_hi = pci_rp->pci_phys_hi;
46100d0963fSdilpreet 		kmem_free(pci_rp, len);
46200d0963fSdilpreet 
46300d0963fSdilpreet 		erpt_p->pe_bdf = (uint16_t)(PCI_REG_BDFR_G(phys_hi) >>
46400d0963fSdilpreet 		    PCI_REG_FUNC_SHIFT);
46500d0963fSdilpreet 	}
46600d0963fSdilpreet 
46700d0963fSdilpreet 	if (!(pci_status & PCI_STAT_CAP)) {
46800d0963fSdilpreet 		goto done;
46900d0963fSdilpreet 	}
47000d0963fSdilpreet 
47149fbdd30SErwin T Tsaur 	/* Initialize structures for PCI-X devices. */
47200d0963fSdilpreet 	pcix_ereport_setup(dip, erpt_p);
47300d0963fSdilpreet 
47400d0963fSdilpreet done:
4758aec9182Sstephh 	pci_regs_gather(dip, erpt_p, DDI_FM_ERR_UNEXPECTED);
47600d0963fSdilpreet 	pci_regs_clear(erpt_p);
47700d0963fSdilpreet 
47800d0963fSdilpreet 	/*
47900d0963fSdilpreet 	 * Before returning set fh_bus_specific to completed pci_erpt_t
48000d0963fSdilpreet 	 * structure
48100d0963fSdilpreet 	 */
48200d0963fSdilpreet 	fmhdl->fh_bus_specific = (void *)erpt_p;
48300d0963fSdilpreet 
48400d0963fSdilpreet 	return;
48500d0963fSdilpreet error:
48600d0963fSdilpreet 	if (erpt_p->pe_pci_regs)
48700d0963fSdilpreet 		kmem_free(erpt_p->pe_pci_regs, sizeof (pci_error_regs_t));
48800d0963fSdilpreet 	kmem_free(erpt_p, sizeof (pci_erpt_t));
48900d0963fSdilpreet 	erpt_p = NULL;
49000d0963fSdilpreet }
49100d0963fSdilpreet 
49200d0963fSdilpreet static void
pcix_ereport_teardown(pci_erpt_t * erpt_p)49300d0963fSdilpreet pcix_ereport_teardown(pci_erpt_t *erpt_p)
49400d0963fSdilpreet {
49500d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
49600d0963fSdilpreet 		pcix_bdg_error_regs_t *pcix_bdg_regs;
49700d0963fSdilpreet 		uint16_t pcix_ver;
49800d0963fSdilpreet 
49900d0963fSdilpreet 		pcix_bdg_regs = (pcix_bdg_error_regs_t *)erpt_p->pe_regs;
50000d0963fSdilpreet 		pcix_ver = pcix_bdg_regs->pcix_bdg_ver;
50100d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_ver)) {
50200d0963fSdilpreet 			int i;
50300d0963fSdilpreet 			for (i = 0; i < 2; i++)
50400d0963fSdilpreet 				kmem_free(pcix_bdg_regs->pcix_bdg_ecc_regs[i],
50500d0963fSdilpreet 				    sizeof (pcix_ecc_regs_t));
50600d0963fSdilpreet 		}
50700d0963fSdilpreet 		kmem_free(erpt_p->pe_regs, sizeof (pcix_bdg_error_regs_t));
50800d0963fSdilpreet 	} else {
50900d0963fSdilpreet 		pcix_error_regs_t *pcix_regs;
51000d0963fSdilpreet 		uint16_t pcix_ver;
51100d0963fSdilpreet 
51200d0963fSdilpreet 		pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs;
51300d0963fSdilpreet 		pcix_ver = pcix_regs->pcix_ver;
51400d0963fSdilpreet 		if (PCIX_ECC_VER_CHECK(pcix_ver)) {
51500d0963fSdilpreet 			kmem_free(pcix_regs->pcix_ecc_regs,
51600d0963fSdilpreet 			    sizeof (pcix_ecc_regs_t));
51700d0963fSdilpreet 		}
51800d0963fSdilpreet 		kmem_free(erpt_p->pe_regs, sizeof (pcix_error_regs_t));
51900d0963fSdilpreet 	}
52000d0963fSdilpreet }
52100d0963fSdilpreet 
52200d0963fSdilpreet void
pci_ereport_teardown(dev_info_t * dip)52300d0963fSdilpreet pci_ereport_teardown(dev_info_t *dip)
52400d0963fSdilpreet {
52500d0963fSdilpreet 	struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl;
52600d0963fSdilpreet 	pci_erpt_t *erpt_p;
52700d0963fSdilpreet 
52800d0963fSdilpreet 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) &&
52900d0963fSdilpreet 	    !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) {
53000d0963fSdilpreet 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP);
53100d0963fSdilpreet 	}
53200d0963fSdilpreet 
53300d0963fSdilpreet 	ASSERT(fmhdl);
53400d0963fSdilpreet 
53500d0963fSdilpreet 	erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific;
53600d0963fSdilpreet 	if (erpt_p == NULL)
53700d0963fSdilpreet 		return;
53800d0963fSdilpreet 
53949fbdd30SErwin T Tsaur 	if (erpt_p->pe_dflags & PCIX_DEV)
54000d0963fSdilpreet 		pcix_ereport_teardown(erpt_p);
54100d0963fSdilpreet 	pci_config_teardown((ddi_acc_handle_t *)&erpt_p->pe_hdl);
54200d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV)
54300d0963fSdilpreet 		kmem_free(erpt_p->pe_pci_regs->pci_bdg_regs,
54400d0963fSdilpreet 		    sizeof (pci_bdg_error_regs_t));
54500d0963fSdilpreet 	kmem_free(erpt_p->pe_pci_regs, sizeof (pci_error_regs_t));
54600d0963fSdilpreet 	kmem_free(erpt_p, sizeof (pci_erpt_t));
54700d0963fSdilpreet 	fmhdl->fh_bus_specific = NULL;
548eae2e508Skrishnae 
54900d0963fSdilpreet 	/*
55000d0963fSdilpreet 	 * The following sparc specific code should be removed once the pci_cap
55100d0963fSdilpreet 	 * interfaces create the necessary properties for us.
55200d0963fSdilpreet 	 */
55300d0963fSdilpreet }
55400d0963fSdilpreet 
5558aec9182Sstephh /*ARGSUSED*/
55600d0963fSdilpreet static int
pcix_check_addr(dev_info_t * dip,ddi_fm_error_t * derr,pcix_ecc_regs_t * pcix_ecc_regs,int type)55700d0963fSdilpreet pcix_check_addr(dev_info_t *dip, ddi_fm_error_t *derr,
5588aec9182Sstephh     pcix_ecc_regs_t *pcix_ecc_regs, int type)
55900d0963fSdilpreet {
56000d0963fSdilpreet 	int cmd = (pcix_ecc_regs->pcix_ecc_ctlstat >> 16) & 0xf;
56100d0963fSdilpreet 	uint64_t addr;
5628aec9182Sstephh 	pci_fme_bus_specific_t *pci_fme_bsp =
5638aec9182Sstephh 	    (pci_fme_bus_specific_t *)derr->fme_bus_specific;
56400d0963fSdilpreet 
56500d0963fSdilpreet 	addr = pcix_ecc_regs->pcix_ecc_secaddr;
56600d0963fSdilpreet 	addr = addr << 32;
56700d0963fSdilpreet 	addr |= pcix_ecc_regs->pcix_ecc_fstaddr;
56800d0963fSdilpreet 
56900d0963fSdilpreet 	switch (cmd) {
57000d0963fSdilpreet 	case PCI_PCIX_CMD_INTR:
57100d0963fSdilpreet 	case PCI_PCIX_CMD_SPEC:
57200d0963fSdilpreet 		return (DDI_FM_FATAL);
57300d0963fSdilpreet 	case PCI_PCIX_CMD_IORD:
57400d0963fSdilpreet 	case PCI_PCIX_CMD_IOWR:
5758aec9182Sstephh 		pci_fme_bsp->pci_bs_addr = addr;
5768aec9182Sstephh 		pci_fme_bsp->pci_bs_flags |= PCI_BS_ADDR_VALID;
5778aec9182Sstephh 		pci_fme_bsp->pci_bs_type = type;
5788aec9182Sstephh 		return (DDI_FM_UNKNOWN);
57900d0963fSdilpreet 	case PCI_PCIX_CMD_DEVID:
58000d0963fSdilpreet 		return (DDI_FM_FATAL);
58100d0963fSdilpreet 	case PCI_PCIX_CMD_MEMRD_DW:
58200d0963fSdilpreet 	case PCI_PCIX_CMD_MEMWR:
58300d0963fSdilpreet 	case PCI_PCIX_CMD_MEMRD_BL:
58400d0963fSdilpreet 	case PCI_PCIX_CMD_MEMWR_BL:
5858aec9182Sstephh 		pci_fme_bsp->pci_bs_addr = addr;
5868aec9182Sstephh 		pci_fme_bsp->pci_bs_flags |= PCI_BS_ADDR_VALID;
5878aec9182Sstephh 		pci_fme_bsp->pci_bs_type = type;
5888aec9182Sstephh 		return (DDI_FM_UNKNOWN);
58900d0963fSdilpreet 	case PCI_PCIX_CMD_CFRD:
59000d0963fSdilpreet 	case PCI_PCIX_CMD_CFWR:
5918aec9182Sstephh 		/*
5928aec9182Sstephh 		 * for type 1 config transaction we can find bdf from address
5938aec9182Sstephh 		 */
5948aec9182Sstephh 		if ((addr & 3) == 1) {
5958aec9182Sstephh 			pci_fme_bsp->pci_bs_bdf = (addr >> 8) & 0xffffffff;
5968aec9182Sstephh 			pci_fme_bsp->pci_bs_flags |= PCI_BS_BDF_VALID;
5978aec9182Sstephh 			pci_fme_bsp->pci_bs_type = type;
5988aec9182Sstephh 		}
5998aec9182Sstephh 		return (DDI_FM_UNKNOWN);
60000d0963fSdilpreet 	case PCI_PCIX_CMD_SPL:
60100d0963fSdilpreet 	case PCI_PCIX_CMD_DADR:
6028aec9182Sstephh 		return (DDI_FM_UNKNOWN);
60300d0963fSdilpreet 	case PCI_PCIX_CMD_MEMRDBL:
60400d0963fSdilpreet 	case PCI_PCIX_CMD_MEMWRBL:
6058aec9182Sstephh 		pci_fme_bsp->pci_bs_addr = addr;
6068aec9182Sstephh 		pci_fme_bsp->pci_bs_flags |= PCI_BS_ADDR_VALID;
6078aec9182Sstephh 		pci_fme_bsp->pci_bs_type = type;
6088aec9182Sstephh 		return (DDI_FM_UNKNOWN);
60900d0963fSdilpreet 	default:
61000d0963fSdilpreet 		return (DDI_FM_FATAL);
61100d0963fSdilpreet 	}
61200d0963fSdilpreet }
61300d0963fSdilpreet 
61400d0963fSdilpreet /*ARGSUSED*/
61500d0963fSdilpreet static int
pci_bdg_error_report(dev_info_t * dip,ddi_fm_error_t * derr,pci_erpt_t * erpt_p)61600d0963fSdilpreet pci_bdg_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p)
61700d0963fSdilpreet {
61800d0963fSdilpreet 	pci_bdg_error_regs_t *pci_bdg_regs = erpt_p->pe_pci_regs->pci_bdg_regs;
61900d0963fSdilpreet 	int fatal = 0;
62000d0963fSdilpreet 	int nonfatal = 0;
62100d0963fSdilpreet 	int unknown = 0;
62200d0963fSdilpreet 	int ok = 0;
62300d0963fSdilpreet 	int ret = DDI_FM_OK;
62400d0963fSdilpreet 	char buf[FM_MAX_CLASS];
62500d0963fSdilpreet 	int i;
6268aec9182Sstephh 	pci_fme_bus_specific_t *pci_fme_bsp =
6278aec9182Sstephh 	    (pci_fme_bus_specific_t *)derr->fme_bus_specific;
62800d0963fSdilpreet 
62900d0963fSdilpreet 	if (derr->fme_flag != DDI_FM_ERR_UNEXPECTED)
63000d0963fSdilpreet 		goto done;
63100d0963fSdilpreet 
63200d0963fSdilpreet 	if ((pci_bdg_regs->pci_bdg_vflags & PCI_BDG_CTRL_VALID) &&
63300d0963fSdilpreet 	    (pci_bdg_regs->pci_bdg_ctrl & PCI_BCNF_BCNTRL_DTO_STAT)) {
63400d0963fSdilpreet 		(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
63500d0963fSdilpreet 		    PCI_ERROR_SUBCLASS, PCI_DTO);
63600d0963fSdilpreet 		ddi_fm_ereport_post(dip, buf, derr->fme_ena,
63700d0963fSdilpreet 		    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
63800d0963fSdilpreet 		    PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16,
63900d0963fSdilpreet 		    pci_bdg_regs->pci_bdg_sec_stat, PCI_BCNTRL,
64000d0963fSdilpreet 		    DATA_TYPE_UINT16, pci_bdg_regs->pci_bdg_ctrl, NULL);
64100d0963fSdilpreet 		unknown++;
64200d0963fSdilpreet 	}
64300d0963fSdilpreet 
64400d0963fSdilpreet 	if (pci_bdg_regs->pci_bdg_vflags & PCI_BDG_SEC_STAT_VALID) {
64500d0963fSdilpreet 		for (i = 0; pci_bdg_err_tbl[i].err_class != NULL; i++) {
64600d0963fSdilpreet 			if (pci_bdg_regs->pci_bdg_sec_stat &
64700d0963fSdilpreet 			    pci_bdg_err_tbl[i].reg_bit) {
64800d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s",
64900d0963fSdilpreet 				    PCI_ERROR_SUBCLASS, PCI_SEC_ERROR_SUBCLASS,
65000d0963fSdilpreet 				    pci_bdg_err_tbl[i].err_class);
65100d0963fSdilpreet 				ddi_fm_ereport_post(dip, buf, derr->fme_ena,
65200d0963fSdilpreet 				    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
65300d0963fSdilpreet 				    PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16,
65400d0963fSdilpreet 				    pci_bdg_regs->pci_bdg_sec_stat, PCI_BCNTRL,
65500d0963fSdilpreet 				    DATA_TYPE_UINT16,
65600d0963fSdilpreet 				    pci_bdg_regs->pci_bdg_ctrl, NULL);
657ac4d633fSstephh 				PCI_FM_SEV_INC(pci_bdg_err_tbl[i].flags);
6588aec9182Sstephh 				if (pci_fme_bsp && (pci_fme_bsp->pci_bs_flags &
6598aec9182Sstephh 				    PCI_BS_ADDR_VALID) &&
6608aec9182Sstephh 				    pci_fme_bsp->pci_bs_type == ACC_HANDLE &&
66100d0963fSdilpreet 				    pci_bdg_err_tbl[i].terr_class)
66200d0963fSdilpreet 					pci_target_enqueue(derr->fme_ena,
66300d0963fSdilpreet 					    pci_bdg_err_tbl[i].terr_class,
66400d0963fSdilpreet 					    PCI_ERROR_SUBCLASS,
6658aec9182Sstephh 					    pci_fme_bsp->pci_bs_addr);
66600d0963fSdilpreet 			}
66700d0963fSdilpreet 		}
66800d0963fSdilpreet 	}
66900d0963fSdilpreet 
67000d0963fSdilpreet done:
67100d0963fSdilpreet 	/*
67200d0963fSdilpreet 	 * Need to check for poke and cautious put. We already know peek
67300d0963fSdilpreet 	 * and cautious get errors occurred (as we got a trap) and we know
67400d0963fSdilpreet 	 * they are nonfatal.
67500d0963fSdilpreet 	 */
67600d0963fSdilpreet 	if (derr->fme_flag == DDI_FM_ERR_EXPECTED) {
67700d0963fSdilpreet 		/*
67800d0963fSdilpreet 		 * for cautious puts we treat all errors as nonfatal. Actually
67900d0963fSdilpreet 		 * we set nonfatal for cautious gets as well - doesn't do any
68000d0963fSdilpreet 		 * harm
68100d0963fSdilpreet 		 */
68200d0963fSdilpreet 		if (pci_bdg_regs->pci_bdg_sec_stat & (PCI_STAT_R_TARG_AB |
68300d0963fSdilpreet 		    PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR))
68400d0963fSdilpreet 			nonfatal++;
68500d0963fSdilpreet 	}
68600d0963fSdilpreet 	if (derr->fme_flag == DDI_FM_ERR_POKE) {
68700d0963fSdilpreet 		/*
68800d0963fSdilpreet 		 * special case for pokes - we only consider master abort
68900d0963fSdilpreet 		 * and target abort as nonfatal. Sserr with no master abort is
69000d0963fSdilpreet 		 * fatal, but master/target abort can come in on separate
69100d0963fSdilpreet 		 * instance, so return unknown and parent will determine if
69200d0963fSdilpreet 		 * nonfatal (if another child returned nonfatal - ie master
69300d0963fSdilpreet 		 * or target abort) or fatal otherwise
69400d0963fSdilpreet 		 */
69500d0963fSdilpreet 		if (pci_bdg_regs->pci_bdg_sec_stat & (PCI_STAT_R_TARG_AB |
69600d0963fSdilpreet 		    PCI_STAT_R_MAST_AB))
69700d0963fSdilpreet 			nonfatal++;
69800d0963fSdilpreet 		if (erpt_p->pe_pci_regs->pci_err_status & PCI_STAT_S_SYSERR)
69900d0963fSdilpreet 			unknown++;
70000d0963fSdilpreet 	}
70100d0963fSdilpreet 
70200d0963fSdilpreet 	/*
7038aec9182Sstephh 	 * now check children below the bridge
70400d0963fSdilpreet 	 */
70500d0963fSdilpreet 	ret = ndi_fm_handler_dispatch(dip, NULL, derr);
70600d0963fSdilpreet 	PCI_FM_SEV_INC(ret);
70700d0963fSdilpreet 	return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
70800d0963fSdilpreet 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
70900d0963fSdilpreet }
71000d0963fSdilpreet 
71100d0963fSdilpreet static int
pcix_ecc_error_report(dev_info_t * dip,ddi_fm_error_t * derr,pci_erpt_t * erpt_p,void * pe_regs)71200d0963fSdilpreet pcix_ecc_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p,
71300d0963fSdilpreet     void *pe_regs)
71400d0963fSdilpreet {
71500d0963fSdilpreet 	pcix_error_regs_t *pcix_regs;
71600d0963fSdilpreet 	pcix_bdg_error_regs_t *pcix_bdg_regs;
71700d0963fSdilpreet 	pcix_ecc_regs_t *pcix_ecc_regs;
71800d0963fSdilpreet 	int bridge;
71900d0963fSdilpreet 	int i;
72000d0963fSdilpreet 	int ecc_phase;
72100d0963fSdilpreet 	int ecc_corr;
72200d0963fSdilpreet 	int sec_ue;
72300d0963fSdilpreet 	int sec_ce;
72400d0963fSdilpreet 	int fatal = 0;
72500d0963fSdilpreet 	int nonfatal = 0;
72600d0963fSdilpreet 	int unknown = 0;
72700d0963fSdilpreet 	int ok = 0;
72800d0963fSdilpreet 	char buf[FM_MAX_CLASS];
72900d0963fSdilpreet 
73000d0963fSdilpreet 	if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
73100d0963fSdilpreet 		pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs;
73200d0963fSdilpreet 		bridge = 1;
73300d0963fSdilpreet 	} else {
73400d0963fSdilpreet 		pcix_regs = (pcix_error_regs_t *)pe_regs;
73500d0963fSdilpreet 		bridge = 0;
73600d0963fSdilpreet 	}
73700d0963fSdilpreet 
73800d0963fSdilpreet 	for (i = 0; i < (bridge ? 2 : 1); i++) {
73900d0963fSdilpreet 		int ret = DDI_FM_OK;
74000d0963fSdilpreet 		pcix_ecc_regs = bridge ? pcix_bdg_regs->pcix_bdg_ecc_regs[i] :
74100d0963fSdilpreet 		    pcix_regs->pcix_ecc_regs;
74200d0963fSdilpreet 		if (pcix_ecc_regs->pcix_ecc_vflags & PCIX_ERR_ECC_STS_VALID) {
74300d0963fSdilpreet 			ecc_phase = (pcix_ecc_regs->pcix_ecc_ctlstat &
74400d0963fSdilpreet 			    PCI_PCIX_ECC_PHASE) >> 0x4;
74500d0963fSdilpreet 			ecc_corr = (pcix_ecc_regs->pcix_ecc_ctlstat &
74600d0963fSdilpreet 			    PCI_PCIX_ECC_CORR);
74700d0963fSdilpreet 			sec_ue = (pcix_ecc_regs->pcix_ecc_ctlstat &
74800d0963fSdilpreet 			    PCI_PCIX_ECC_S_UE);
74900d0963fSdilpreet 			sec_ce = (pcix_ecc_regs->pcix_ecc_ctlstat &
75000d0963fSdilpreet 			    PCI_PCIX_ECC_S_CE);
75100d0963fSdilpreet 
75200d0963fSdilpreet 			switch (ecc_phase) {
75300d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_NOERR:
75400d0963fSdilpreet 				break;
75500d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_FADDR:
75600d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_SADDR:
757ac4d633fSstephh 				PCI_FM_SEV_INC(ecc_corr ?  DDI_FM_OK :
75800d0963fSdilpreet 				    DDI_FM_FATAL);
75900d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS,
76000d0963fSdilpreet 				    "%s.%s%s", PCIX_ERROR_SUBCLASS,
76100d0963fSdilpreet 				    i ? PCIX_SEC_ERROR_SUBCLASS : "",
76200d0963fSdilpreet 				    ecc_corr ? PCIX_ECC_CE_ADDR :
76300d0963fSdilpreet 				    PCIX_ECC_UE_ADDR);
76400d0963fSdilpreet 				break;
76500d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_ATTR:
76600d0963fSdilpreet 				PCI_FM_SEV_INC(ecc_corr ?
767ac4d633fSstephh 				    DDI_FM_OK : DDI_FM_FATAL);
76800d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS,
76900d0963fSdilpreet 				    "%s.%s%s", PCIX_ERROR_SUBCLASS,
77000d0963fSdilpreet 				    i ? PCIX_SEC_ERROR_SUBCLASS : "",
77100d0963fSdilpreet 				    ecc_corr ? PCIX_ECC_CE_ATTR :
77200d0963fSdilpreet 				    PCIX_ECC_UE_ATTR);
77300d0963fSdilpreet 				break;
77400d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_DATA32:
77500d0963fSdilpreet 			case PCI_PCIX_ECC_PHASE_DATA64:
77600d0963fSdilpreet 				if (ecc_corr)
777ac4d633fSstephh 					ret = DDI_FM_OK;
7788aec9182Sstephh 				else {
7798aec9182Sstephh 					int type;
7808aec9182Sstephh 					pci_error_regs_t *pci_regs =
7818aec9182Sstephh 					    erpt_p->pe_pci_regs;
7828aec9182Sstephh 
7838aec9182Sstephh 					if (i) {
7848aec9182Sstephh 						if (pci_regs->pci_bdg_regs->
7858aec9182Sstephh 						    pci_bdg_sec_stat &
7868aec9182Sstephh 						    PCI_STAT_S_PERROR)
7878aec9182Sstephh 							type = ACC_HANDLE;
78800d0963fSdilpreet 						else
7898aec9182Sstephh 							type = DMA_HANDLE;
7908aec9182Sstephh 					} else {
7918aec9182Sstephh 						if (pci_regs->pci_err_status &
7928aec9182Sstephh 						    PCI_STAT_S_PERROR)
7938aec9182Sstephh 							type = DMA_HANDLE;
7948aec9182Sstephh 						else
7958aec9182Sstephh 							type = ACC_HANDLE;
7968aec9182Sstephh 					}
79700d0963fSdilpreet 					ret = pcix_check_addr(dip, derr,
7988aec9182Sstephh 					    pcix_ecc_regs, type);
7998aec9182Sstephh 				}
80000d0963fSdilpreet 				PCI_FM_SEV_INC(ret);
80100d0963fSdilpreet 
80200d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS,
80300d0963fSdilpreet 				    "%s.%s%s", PCIX_ERROR_SUBCLASS,
80400d0963fSdilpreet 				    i ? PCIX_SEC_ERROR_SUBCLASS : "",
80500d0963fSdilpreet 				    ecc_corr ? PCIX_ECC_CE_DATA :
80600d0963fSdilpreet 				    PCIX_ECC_UE_DATA);
80700d0963fSdilpreet 				break;
80800d0963fSdilpreet 			}
80900d0963fSdilpreet 			if (ecc_phase)
81000d0963fSdilpreet 				if (bridge)
81100d0963fSdilpreet 					ddi_fm_ereport_post(dip, buf,
81200d0963fSdilpreet 					    derr->fme_ena,
81300d0963fSdilpreet 					    DDI_NOSLEEP, FM_VERSION,
81400d0963fSdilpreet 					    DATA_TYPE_UINT8, 0,
81500d0963fSdilpreet 					    PCIX_SEC_STATUS, DATA_TYPE_UINT16,
81600d0963fSdilpreet 					    pcix_bdg_regs->pcix_bdg_sec_stat,
81700d0963fSdilpreet 					    PCIX_BDG_STAT, DATA_TYPE_UINT32,
81800d0963fSdilpreet 					    pcix_bdg_regs->pcix_bdg_stat,
81900d0963fSdilpreet 					    PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32,
82000d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_ctlstat,
82100d0963fSdilpreet 					    PCIX_ECC_ATTR, DATA_TYPE_UINT32,
82200d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_attr, NULL);
82300d0963fSdilpreet 				else
82400d0963fSdilpreet 					ddi_fm_ereport_post(dip, buf,
82500d0963fSdilpreet 					    derr->fme_ena,
82600d0963fSdilpreet 					    DDI_NOSLEEP, FM_VERSION,
82700d0963fSdilpreet 					    DATA_TYPE_UINT8, 0,
82800d0963fSdilpreet 					    PCIX_COMMAND, DATA_TYPE_UINT16,
82900d0963fSdilpreet 					    pcix_regs->pcix_command,
83000d0963fSdilpreet 					    PCIX_STATUS, DATA_TYPE_UINT32,
83100d0963fSdilpreet 					    pcix_regs->pcix_status,
83200d0963fSdilpreet 					    PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32,
83300d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_ctlstat,
83400d0963fSdilpreet 					    PCIX_ECC_ATTR, DATA_TYPE_UINT32,
83500d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_attr, NULL);
83600d0963fSdilpreet 			if (sec_ce || sec_ue) {
83700d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS,
83800d0963fSdilpreet 				    "%s.%s%s", PCIX_ERROR_SUBCLASS,
83900d0963fSdilpreet 				    i ? PCIX_SEC_ERROR_SUBCLASS : "",
84000d0963fSdilpreet 				    sec_ce ? PCIX_ECC_S_CE : PCIX_ECC_S_UE);
84100d0963fSdilpreet 				if (bridge)
84200d0963fSdilpreet 					ddi_fm_ereport_post(dip, buf,
84300d0963fSdilpreet 					    derr->fme_ena,
84400d0963fSdilpreet 					    DDI_NOSLEEP, FM_VERSION,
84500d0963fSdilpreet 					    DATA_TYPE_UINT8, 0,
84600d0963fSdilpreet 					    PCIX_SEC_STATUS, DATA_TYPE_UINT16,
84700d0963fSdilpreet 					    pcix_bdg_regs->pcix_bdg_sec_stat,
84800d0963fSdilpreet 					    PCIX_BDG_STAT, DATA_TYPE_UINT32,
84900d0963fSdilpreet 					    pcix_bdg_regs->pcix_bdg_stat,
85000d0963fSdilpreet 					    PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32,
85100d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_ctlstat,
85200d0963fSdilpreet 					    PCIX_ECC_ATTR, DATA_TYPE_UINT32,
85300d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_attr, NULL);
85400d0963fSdilpreet 				else
85500d0963fSdilpreet 					ddi_fm_ereport_post(dip, buf,
85600d0963fSdilpreet 					    derr->fme_ena,
85700d0963fSdilpreet 					    DDI_NOSLEEP, FM_VERSION,
85800d0963fSdilpreet 					    DATA_TYPE_UINT8, 0,
85900d0963fSdilpreet 					    PCIX_COMMAND, DATA_TYPE_UINT16,
86000d0963fSdilpreet 					    pcix_regs->pcix_command,
86100d0963fSdilpreet 					    PCIX_STATUS, DATA_TYPE_UINT32,
86200d0963fSdilpreet 					    pcix_regs->pcix_status,
86300d0963fSdilpreet 					    PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32,
86400d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_ctlstat,
86500d0963fSdilpreet 					    PCIX_ECC_ATTR, DATA_TYPE_UINT32,
86600d0963fSdilpreet 					    pcix_ecc_regs->pcix_ecc_attr, NULL);
86700d0963fSdilpreet 				PCI_FM_SEV_INC(sec_ue ? DDI_FM_FATAL :
868ac4d633fSstephh 				    DDI_FM_OK);
86900d0963fSdilpreet 			}
87000d0963fSdilpreet 		}
87100d0963fSdilpreet 	}
87200d0963fSdilpreet 	return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
87300d0963fSdilpreet 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
87400d0963fSdilpreet }
87500d0963fSdilpreet 
87600d0963fSdilpreet static int
pcix_bdg_error_report(dev_info_t * dip,ddi_fm_error_t * derr,pci_erpt_t * erpt_p,void * pe_regs)87700d0963fSdilpreet pcix_bdg_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p,
87800d0963fSdilpreet     void *pe_regs)
87900d0963fSdilpreet {
88000d0963fSdilpreet 	pcix_bdg_error_regs_t *pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs;
88100d0963fSdilpreet 	int fatal = 0;
88200d0963fSdilpreet 	int nonfatal = 0;
88300d0963fSdilpreet 	int unknown = 0;
88400d0963fSdilpreet 	int ok = 0;
88500d0963fSdilpreet 	char buf[FM_MAX_CLASS];
88600d0963fSdilpreet 	int i;
88700d0963fSdilpreet 
88800d0963fSdilpreet 	if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_STATUS_VALID) {
88900d0963fSdilpreet 		for (i = 0; pcix_err_tbl[i].err_class != NULL; i++) {
89000d0963fSdilpreet 			if ((pcix_bdg_regs->pcix_bdg_stat &
89100d0963fSdilpreet 			    pcix_err_tbl[i].reg_bit)) {
89200d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
89300d0963fSdilpreet 				    PCIX_ERROR_SUBCLASS,
89400d0963fSdilpreet 				    pcix_err_tbl[i].err_class);
89500d0963fSdilpreet 				ddi_fm_ereport_post(dip, buf, derr->fme_ena,
89600d0963fSdilpreet 				    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
89700d0963fSdilpreet 				    PCIX_SEC_STATUS, DATA_TYPE_UINT16,
89800d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_sec_stat,
89900d0963fSdilpreet 				    PCIX_BDG_STAT, DATA_TYPE_UINT32,
90000d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_stat, NULL);
90100d0963fSdilpreet 				PCI_FM_SEV_INC(pcix_err_tbl[i].flags);
90200d0963fSdilpreet 			}
90300d0963fSdilpreet 		}
90400d0963fSdilpreet 	}
90500d0963fSdilpreet 
90600d0963fSdilpreet 	if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_SEC_STATUS_VALID) {
90700d0963fSdilpreet 		for (i = 0; pcix_sec_err_tbl[i].err_class != NULL; i++) {
90800d0963fSdilpreet 			if ((pcix_bdg_regs->pcix_bdg_sec_stat &
90900d0963fSdilpreet 			    pcix_sec_err_tbl[i].reg_bit)) {
91000d0963fSdilpreet 				(void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s",
91100d0963fSdilpreet 				    PCIX_ERROR_SUBCLASS,
91200d0963fSdilpreet 				    PCIX_SEC_ERROR_SUBCLASS,
91300d0963fSdilpreet 				    pcix_sec_err_tbl[i].err_class);
91400d0963fSdilpreet 				ddi_fm_ereport_post(dip, buf, derr->fme_ena,
91500d0963fSdilpreet 				    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
91600d0963fSdilpreet 				    PCIX_SEC_STATUS, DATA_TYPE_UINT16,
91700d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_sec_stat,
91800d0963fSdilpreet 				    PCIX_BDG_STAT, DATA_TYPE_UINT32,
91900d0963fSdilpreet 				    pcix_bdg_regs->pcix_bdg_stat, NULL);
92000d0963fSdilpreet 				PCI_FM_SEV_INC(pcix_sec_err_tbl[i].flags);
92100d0963fSdilpreet 			}
92200d0963fSdilpreet 		}
92300d0963fSdilpreet 	}
92400d0963fSdilpreet 
92500d0963fSdilpreet 	/* Log/Handle ECC errors */
92600d0963fSdilpreet 	if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) {
92700d0963fSdilpreet 		int ret;
92800d0963fSdilpreet 
92900d0963fSdilpreet 		ret = pcix_ecc_error_report(dip, derr, erpt_p,
93000d0963fSdilpreet 		    (void *)pcix_bdg_regs);
93100d0963fSdilpreet 		PCI_FM_SEV_INC(ret);
93200d0963fSdilpreet 	}
93300d0963fSdilpreet 	return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
93400d0963fSdilpreet 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
93500d0963fSdilpreet }
93600d0963fSdilpreet 
93700d0963fSdilpreet static int
pcix_error_report(dev_info_t * dip,ddi_fm_error_t * derr,pci_erpt_t * erpt_p)93800d0963fSdilpreet pcix_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p)
93900d0963fSdilpreet {
94000d0963fSdilpreet 	pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs;
94100d0963fSdilpreet 	int fatal = 0;
94200d0963fSdilpreet 	int nonfatal = 0;
94300d0963fSdilpreet 	int unknown = 0;
94400d0963fSdilpreet 	int ok = 0;
94500d0963fSdilpreet 	char buf[FM_MAX_CLASS];
94600d0963fSdilpreet 	int i;
94700d0963fSdilpreet 
94800d0963fSdilpreet 	if (pcix_regs->pcix_vflags & PCIX_ERR_STATUS_VALID) {
94900d0963fSdilpreet 		for (i = 0; pcix_err_tbl[i].err_class != NULL; i++) {
95000d0963fSdilpreet 			if (!(pcix_regs->pcix_status & pcix_err_tbl[i].reg_bit))
95100d0963fSdilpreet 				continue;
95200d0963fSdilpreet 
95300d0963fSdilpreet 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
95400d0963fSdilpreet 			    PCIX_ERROR_SUBCLASS, pcix_err_tbl[i].err_class);
95500d0963fSdilpreet 			ddi_fm_ereport_post(dip, buf, derr->fme_ena,
95600d0963fSdilpreet 			    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
95700d0963fSdilpreet 			    PCIX_COMMAND, DATA_TYPE_UINT16,
95800d0963fSdilpreet 			    pcix_regs->pcix_command, PCIX_STATUS,
95900d0963fSdilpreet 			    DATA_TYPE_UINT32, pcix_regs->pcix_status,
96000d0963fSdilpreet 			    NULL);
96100d0963fSdilpreet 			PCI_FM_SEV_INC(pcix_err_tbl[i].flags);
96200d0963fSdilpreet 		}
96300d0963fSdilpreet 	}
96400d0963fSdilpreet 	/* Log/Handle ECC errors */
96500d0963fSdilpreet 	if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) {
96600d0963fSdilpreet 		int ret = pcix_ecc_error_report(dip, derr, erpt_p,
96700d0963fSdilpreet 		    (void *)pcix_regs);
96800d0963fSdilpreet 		PCI_FM_SEV_INC(ret);
96900d0963fSdilpreet 	}
97000d0963fSdilpreet 
97100d0963fSdilpreet 	return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
97200d0963fSdilpreet 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
97300d0963fSdilpreet }
97400d0963fSdilpreet 
97500d0963fSdilpreet static void
pci_error_report(dev_info_t * dip,ddi_fm_error_t * derr,pci_erpt_t * erpt_p)97600d0963fSdilpreet pci_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p)
97700d0963fSdilpreet {
97800d0963fSdilpreet 	int fatal = 0;
97900d0963fSdilpreet 	int nonfatal = 0;
98000d0963fSdilpreet 	int unknown = 0;
98100d0963fSdilpreet 	int ok = 0;
98200d0963fSdilpreet 	char buf[FM_MAX_CLASS];
98300d0963fSdilpreet 	int i;
98400d0963fSdilpreet 
98500d0963fSdilpreet 	if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) {
98600d0963fSdilpreet 		/*
98700d0963fSdilpreet 		 * Log generic PCI errors.
98800d0963fSdilpreet 		 */
98900d0963fSdilpreet 		for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
99000d0963fSdilpreet 			if (!(erpt_p->pe_pci_regs->pci_err_status &
99100d0963fSdilpreet 			    pci_err_tbl[i].reg_bit) ||
99200d0963fSdilpreet 			    !(erpt_p->pe_pci_regs->pci_vflags &
99300d0963fSdilpreet 			    PCI_ERR_STATUS_VALID))
99400d0963fSdilpreet 				continue;
99500d0963fSdilpreet 			/*
99600d0963fSdilpreet 			 * Generate an ereport for this error bit.
99700d0963fSdilpreet 			 */
99800d0963fSdilpreet 			(void) snprintf(buf, FM_MAX_CLASS, "%s.%s",
99900d0963fSdilpreet 			    PCI_ERROR_SUBCLASS, pci_err_tbl[i].err_class);
100000d0963fSdilpreet 			ddi_fm_ereport_post(dip, buf, derr->fme_ena,
100100d0963fSdilpreet 			    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
100200d0963fSdilpreet 			    PCI_CONFIG_STATUS, DATA_TYPE_UINT16,
100300d0963fSdilpreet 			    erpt_p->pe_pci_regs->pci_err_status,
100400d0963fSdilpreet 			    PCI_CONFIG_COMMAND, DATA_TYPE_UINT16,
100500d0963fSdilpreet 			    erpt_p->pe_pci_regs->pci_cfg_comm, NULL);
100600d0963fSdilpreet 
100700d0963fSdilpreet 			PCI_FM_SEV_INC(pci_err_tbl[i].flags);
100800d0963fSdilpreet 		}
100949fbdd30SErwin T Tsaur 		if (erpt_p->pe_dflags & PCIX_DEV) {
101000d0963fSdilpreet 			if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) {
101100d0963fSdilpreet 				int ret = pcix_bdg_error_report(dip, derr,
101200d0963fSdilpreet 				    erpt_p, erpt_p->pe_regs);
101300d0963fSdilpreet 				PCI_FM_SEV_INC(ret);
101400d0963fSdilpreet 			} else {
101500d0963fSdilpreet 				int ret = pcix_error_report(dip, derr, erpt_p);
101600d0963fSdilpreet 				PCI_FM_SEV_INC(ret);
101700d0963fSdilpreet 			}
101800d0963fSdilpreet 		}
101900d0963fSdilpreet 	}
102000d0963fSdilpreet 
102100d0963fSdilpreet 	if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV)) {
102200d0963fSdilpreet 		int ret = pci_bdg_error_report(dip, derr, erpt_p);
102300d0963fSdilpreet 		PCI_FM_SEV_INC(ret);
102400d0963fSdilpreet 	}
102500d0963fSdilpreet 
10268aec9182Sstephh 	if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) {
10278aec9182Sstephh 		pci_fme_bus_specific_t *pci_fme_bsp;
10288aec9182Sstephh 		int ret = DDI_FM_UNKNOWN;
10298aec9182Sstephh 
10308aec9182Sstephh 		pci_fme_bsp = (pci_fme_bus_specific_t *)derr->fme_bus_specific;
10318aec9182Sstephh 		if (pci_fme_bsp->pci_bs_flags & PCI_BS_ADDR_VALID) {
10328aec9182Sstephh 			ret = ndi_fmc_entry_error(dip,
10338aec9182Sstephh 			    pci_fme_bsp->pci_bs_type, derr,
10348aec9182Sstephh 			    (void *)&pci_fme_bsp->pci_bs_addr);
10358aec9182Sstephh 			PCI_FM_SEV_INC(ret);
10368aec9182Sstephh 		}
10378aec9182Sstephh 		/*
10388aec9182Sstephh 		 * If we didn't find the handle using an addr, try using bdf.
10398aec9182Sstephh 		 * Note we don't do this where the bdf is for a
10408aec9182Sstephh 		 * device behind a pciex/pci bridge as the bridge may have
10418aec9182Sstephh 		 * fabricated the bdf.
10428aec9182Sstephh 		 */
10438aec9182Sstephh 		if (ret == DDI_FM_UNKNOWN &&
10448aec9182Sstephh 		    (pci_fme_bsp->pci_bs_flags & PCI_BS_BDF_VALID) &&
104549fbdd30SErwin T Tsaur 		    pci_fme_bsp->pci_bs_bdf == erpt_p->pe_bdf) {
10468aec9182Sstephh 			ret = ndi_fmc_entry_error_all(dip,
10478aec9182Sstephh 			    pci_fme_bsp->pci_bs_type, derr);
10488aec9182Sstephh 			PCI_FM_SEV_INC(ret);
10498aec9182Sstephh 		}
10508aec9182Sstephh 	}
10518aec9182Sstephh 
105200d0963fSdilpreet 	derr->fme_status = (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL :
105300d0963fSdilpreet 	    (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK)));
105400d0963fSdilpreet }
105500d0963fSdilpreet 
105600d0963fSdilpreet void
pci_ereport_post(dev_info_t * dip,ddi_fm_error_t * derr,uint16_t * xx_status)105700d0963fSdilpreet pci_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *xx_status)
105800d0963fSdilpreet {
105900d0963fSdilpreet 	struct i_ddi_fmhdl *fmhdl;
106000d0963fSdilpreet 	pci_erpt_t *erpt_p;
10618aec9182Sstephh 	ddi_fm_error_t de;
10628aec9182Sstephh 	pci_fme_bus_specific_t pci_fme_bs;
106300d0963fSdilpreet 
1064eae2e508Skrishnae 	/*
1065eae2e508Skrishnae 	 * On PCI Express systems, all error handling and ereport are done via
1066eae2e508Skrishnae 	 * the PCIe misc module.  This function is a no-op for PCIe Systems.  In
1067eae2e508Skrishnae 	 * order to tell if a system is a PCI or PCIe system, check that the
1068eae2e508Skrishnae 	 * bus_private_data exists.  If it exists, this is a PCIe system.
1069eae2e508Skrishnae 	 */
1070eae2e508Skrishnae 	if (ndi_get_bus_private(dip, B_TRUE)) {
1071eae2e508Skrishnae 		derr->fme_status = DDI_FM_OK;
1072eae2e508Skrishnae 		if (xx_status != NULL)
1073eae2e508Skrishnae 			*xx_status = 0x0;
1074eae2e508Skrishnae 
1075eae2e508Skrishnae 		return;
1076eae2e508Skrishnae 	}
1077eae2e508Skrishnae 
107800d0963fSdilpreet 	fmhdl = DEVI(dip)->devi_fmhdl;
107900d0963fSdilpreet 	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) &&
108000d0963fSdilpreet 	    !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) {
108100d0963fSdilpreet 		i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP);
108200d0963fSdilpreet 		return;
108300d0963fSdilpreet 	}
108400d0963fSdilpreet 
10858aec9182Sstephh 	/*
10868aec9182Sstephh 	 * copy in the ddi_fm_error_t structure in case it's VER0
10878aec9182Sstephh 	 */
10888aec9182Sstephh 	de.fme_version = derr->fme_version;
10898aec9182Sstephh 	de.fme_status = derr->fme_status;
10908aec9182Sstephh 	de.fme_flag = derr->fme_flag;
10918aec9182Sstephh 	de.fme_ena = derr->fme_ena;
10928aec9182Sstephh 	de.fme_acc_handle = derr->fme_acc_handle;
10938aec9182Sstephh 	de.fme_dma_handle = derr->fme_dma_handle;
10948aec9182Sstephh 	de.fme_bus_specific = derr->fme_bus_specific;
10958aec9182Sstephh 	if (derr->fme_version >= DDI_FME_VER1)
10968aec9182Sstephh 		de.fme_bus_type = derr->fme_bus_type;
10978aec9182Sstephh 	else
10988aec9182Sstephh 		de.fme_bus_type = DDI_FME_BUS_TYPE_DFLT;
10998aec9182Sstephh 	if (de.fme_bus_type == DDI_FME_BUS_TYPE_DFLT) {
11008aec9182Sstephh 		/*
11018aec9182Sstephh 		 * if this is the first pci device we've found convert
11028aec9182Sstephh 		 * fme_bus_specific to DDI_FME_BUS_TYPE_PCI
11038aec9182Sstephh 		 */
11048aec9182Sstephh 		bzero(&pci_fme_bs, sizeof (pci_fme_bs));
11058aec9182Sstephh 		if (de.fme_bus_specific) {
11068aec9182Sstephh 			/*
11078aec9182Sstephh 			 * the cpu passed us an addr - this can be used to look
11088aec9182Sstephh 			 * up an access handle
11098aec9182Sstephh 			 */
11108aec9182Sstephh 			pci_fme_bs.pci_bs_addr = (uintptr_t)de.fme_bus_specific;
11118aec9182Sstephh 			pci_fme_bs.pci_bs_type = ACC_HANDLE;
11128aec9182Sstephh 			pci_fme_bs.pci_bs_flags |= PCI_BS_ADDR_VALID;
11138aec9182Sstephh 		}
11148aec9182Sstephh 		de.fme_bus_specific = (void *)&pci_fme_bs;
11158aec9182Sstephh 		de.fme_bus_type = DDI_FME_BUS_TYPE_PCI;
11168aec9182Sstephh 	}
11178aec9182Sstephh 
111800d0963fSdilpreet 	ASSERT(fmhdl);
111900d0963fSdilpreet 
11207e12ceb3SToomas Soome 	if (de.fme_ena == 0)
11218aec9182Sstephh 		de.fme_ena = fm_ena_generate(0, FM_ENA_FMT1);
112200d0963fSdilpreet 
112300d0963fSdilpreet 	erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific;
1124ac4d633fSstephh 	if (erpt_p == NULL)
112500d0963fSdilpreet 		return;
112600d0963fSdilpreet 
11278aec9182Sstephh 	pci_regs_gather(dip, erpt_p, de.fme_flag);
11288aec9182Sstephh 	pci_error_report(dip, &de, erpt_p);
112900d0963fSdilpreet 	pci_regs_clear(erpt_p);
113000d0963fSdilpreet 
11318aec9182Sstephh 	derr->fme_status = de.fme_status;
11328aec9182Sstephh 	derr->fme_ena = de.fme_ena;
11338aec9182Sstephh 	derr->fme_acc_handle = de.fme_acc_handle;
11348aec9182Sstephh 	derr->fme_dma_handle = de.fme_dma_handle;
113500d0963fSdilpreet 	if (xx_status != NULL)
113600d0963fSdilpreet 		*xx_status = erpt_p->pe_pci_regs->pci_err_status;
113700d0963fSdilpreet }
113800d0963fSdilpreet 
113900d0963fSdilpreet /*
114000d0963fSdilpreet  * private version of walk_devs() that can be used during panic. No
114100d0963fSdilpreet  * sleeping or locking required.
114200d0963fSdilpreet  */
114300d0963fSdilpreet static int
pci_fm_walk_devs(dev_info_t * dip,int (* f)(dev_info_t *,void *),void * arg)114400d0963fSdilpreet pci_fm_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg)
114500d0963fSdilpreet {
114600d0963fSdilpreet 	while (dip) {
114700d0963fSdilpreet 		switch ((*f)(dip, arg)) {
114800d0963fSdilpreet 		case DDI_WALK_TERMINATE:
114900d0963fSdilpreet 			return (DDI_WALK_TERMINATE);
115000d0963fSdilpreet 		case DDI_WALK_CONTINUE:
115100d0963fSdilpreet 			if (pci_fm_walk_devs(ddi_get_child(dip), f,
115200d0963fSdilpreet 			    arg) == DDI_WALK_TERMINATE)
115300d0963fSdilpreet 				return (DDI_WALK_TERMINATE);
115400d0963fSdilpreet 			break;
115500d0963fSdilpreet 		case DDI_WALK_PRUNECHILD:
115600d0963fSdilpreet 			break;
115700d0963fSdilpreet 		}
115800d0963fSdilpreet 		dip = ddi_get_next_sibling(dip);
115900d0963fSdilpreet 	}
116000d0963fSdilpreet 	return (DDI_WALK_CONTINUE);
116100d0963fSdilpreet }
116200d0963fSdilpreet 
116300d0963fSdilpreet /*
116400d0963fSdilpreet  * need special version of ddi_fm_ereport_post() as the leaf driver may
116500d0963fSdilpreet  * not be hardened.
116600d0963fSdilpreet  */
116700d0963fSdilpreet static void
pci_fm_ereport_post(dev_info_t * dip,const char * error_class,uint64_t ena,uint8_t version,...)116800d0963fSdilpreet pci_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena,
116900d0963fSdilpreet     uint8_t version, ...)
117000d0963fSdilpreet {
117100d0963fSdilpreet 	char *name;
117200d0963fSdilpreet 	char device_path[MAXPATHLEN];
117300d0963fSdilpreet 	char ddi_error_class[FM_MAX_CLASS];
117400d0963fSdilpreet 	nvlist_t *ereport, *detector;
117500d0963fSdilpreet 	nv_alloc_t *nva;
117600d0963fSdilpreet 	errorq_elem_t *eqep;
117700d0963fSdilpreet 	va_list ap;
117800d0963fSdilpreet 
1179c6f039c7SToomas Soome 	eqep = NULL;
118000d0963fSdilpreet 	if (panicstr) {
118100d0963fSdilpreet 		eqep = errorq_reserve(ereport_errorq);
118200d0963fSdilpreet 		if (eqep == NULL)
118300d0963fSdilpreet 			return;
118400d0963fSdilpreet 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
118500d0963fSdilpreet 		nva = errorq_elem_nva(ereport_errorq, eqep);
118600d0963fSdilpreet 		detector = fm_nvlist_create(nva);
118700d0963fSdilpreet 	} else {
118800d0963fSdilpreet 		ereport = fm_nvlist_create(NULL);
118900d0963fSdilpreet 		detector = fm_nvlist_create(NULL);
119000d0963fSdilpreet 	}
119100d0963fSdilpreet 
119200d0963fSdilpreet 	(void) ddi_pathname(dip, device_path);
119300d0963fSdilpreet 	fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
1194392e836bSGavin Maltby 	    device_path, NULL, NULL);
119500d0963fSdilpreet 	(void) snprintf(ddi_error_class, FM_MAX_CLASS, "%s.%s",
119600d0963fSdilpreet 	    DDI_IO_CLASS, error_class);
119700d0963fSdilpreet 	fm_ereport_set(ereport, version, ddi_error_class, ena, detector, NULL);
119800d0963fSdilpreet 
119900d0963fSdilpreet 	va_start(ap, version);
120000d0963fSdilpreet 	name = va_arg(ap, char *);
120100d0963fSdilpreet 	(void) i_fm_payload_set(ereport, name, ap);
120200d0963fSdilpreet 	va_end(ap);
120300d0963fSdilpreet 
120400d0963fSdilpreet 	if (panicstr) {
120500d0963fSdilpreet 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
120600d0963fSdilpreet 	} else {
120700d0963fSdilpreet 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
120800d0963fSdilpreet 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
120900d0963fSdilpreet 		fm_nvlist_destroy(detector, FM_NVA_FREE);
121000d0963fSdilpreet 	}
121100d0963fSdilpreet }
121200d0963fSdilpreet 
121300d0963fSdilpreet static int
pci_check_regs(dev_info_t * dip,void * arg)121400d0963fSdilpreet pci_check_regs(dev_info_t *dip, void *arg)
121500d0963fSdilpreet {
121600d0963fSdilpreet 	int reglen;
121700d0963fSdilpreet 	int rn;
121800d0963fSdilpreet 	int totreg;
121900d0963fSdilpreet 	pci_regspec_t *drv_regp;
122000d0963fSdilpreet 	pci_target_err_t *tgt_err = (pci_target_err_t *)arg;
122100d0963fSdilpreet 
122200d0963fSdilpreet 	if (tgt_err->tgt_pci_space == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
122300d0963fSdilpreet 		/*
122400d0963fSdilpreet 		 * for config space, we need to check if the given address
122500d0963fSdilpreet 		 * is a valid config space address for this device - based
122600d0963fSdilpreet 		 * on pci_phys_hi of the config space entry in reg property.
122700d0963fSdilpreet 		 */
122800d0963fSdilpreet 		if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
122900d0963fSdilpreet 		    "reg", (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
123000d0963fSdilpreet 			return (DDI_WALK_CONTINUE);
123100d0963fSdilpreet 
123200d0963fSdilpreet 		totreg = reglen / sizeof (pci_regspec_t);
123300d0963fSdilpreet 		for (rn = 0; rn < totreg; rn++) {
123400d0963fSdilpreet 			if (tgt_err->tgt_pci_space ==
123500d0963fSdilpreet 			    PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi) &&
123600d0963fSdilpreet 			    (tgt_err->tgt_pci_addr & (PCI_REG_BUS_M |
123700d0963fSdilpreet 			    PCI_REG_DEV_M | PCI_REG_FUNC_M)) ==
123800d0963fSdilpreet 			    (drv_regp[rn].pci_phys_hi & (PCI_REG_BUS_M |
123900d0963fSdilpreet 			    PCI_REG_DEV_M | PCI_REG_FUNC_M))) {
124000d0963fSdilpreet 				tgt_err->tgt_dip = dip;
124100d0963fSdilpreet 				kmem_free(drv_regp, reglen);
124200d0963fSdilpreet 				return (DDI_WALK_TERMINATE);
124300d0963fSdilpreet 			}
124400d0963fSdilpreet 		}
124500d0963fSdilpreet 		kmem_free(drv_regp, reglen);
124600d0963fSdilpreet 	} else {
124700d0963fSdilpreet 		/*
124800d0963fSdilpreet 		 * for non config space, need to check reg to look
124900d0963fSdilpreet 		 * for any non-relocable mapping, otherwise check
125000d0963fSdilpreet 		 * assigned-addresses.
125100d0963fSdilpreet 		 */
125200d0963fSdilpreet 		if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
125300d0963fSdilpreet 		    "reg", (caddr_t)&drv_regp, &reglen) != DDI_SUCCESS)
125400d0963fSdilpreet 			return (DDI_WALK_CONTINUE);
125500d0963fSdilpreet 
125600d0963fSdilpreet 		totreg = reglen / sizeof (pci_regspec_t);
125700d0963fSdilpreet 		for (rn = 0; rn < totreg; rn++) {
125800d0963fSdilpreet 			if ((drv_regp[rn].pci_phys_hi & PCI_RELOCAT_B) &&
125900d0963fSdilpreet 			    (tgt_err->tgt_pci_space == TGT_PCI_SPACE_UNKNOWN ||
126000d0963fSdilpreet 			    tgt_err->tgt_pci_space ==
126100d0963fSdilpreet 			    PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi)) &&
126200d0963fSdilpreet 			    (tgt_err->tgt_pci_addr >=
126300d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_phys_low +
126400d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_phys_mid << 32)) &&
126500d0963fSdilpreet 			    (tgt_err->tgt_pci_addr <
126600d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_phys_low +
126700d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_phys_mid << 32) +
126800d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_size_low +
126900d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_size_hi << 32))) {
127000d0963fSdilpreet 				tgt_err->tgt_dip = dip;
127100d0963fSdilpreet 				kmem_free(drv_regp, reglen);
127200d0963fSdilpreet 				return (DDI_WALK_TERMINATE);
127300d0963fSdilpreet 			}
127400d0963fSdilpreet 		}
127500d0963fSdilpreet 		kmem_free(drv_regp, reglen);
127600d0963fSdilpreet 
127700d0963fSdilpreet 		if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
127800d0963fSdilpreet 		    "assigned-addresses", (caddr_t)&drv_regp, &reglen) !=
127900d0963fSdilpreet 		    DDI_SUCCESS)
128000d0963fSdilpreet 			return (DDI_WALK_CONTINUE);
128100d0963fSdilpreet 
128200d0963fSdilpreet 		totreg = reglen / sizeof (pci_regspec_t);
128300d0963fSdilpreet 		for (rn = 0; rn < totreg; rn++) {
128400d0963fSdilpreet 			if ((tgt_err->tgt_pci_space == TGT_PCI_SPACE_UNKNOWN ||
128500d0963fSdilpreet 			    tgt_err->tgt_pci_space ==
128600d0963fSdilpreet 			    PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi)) &&
128700d0963fSdilpreet 			    (tgt_err->tgt_pci_addr >=
128800d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_phys_low +
128900d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_phys_mid << 32)) &&
129000d0963fSdilpreet 			    (tgt_err->tgt_pci_addr <
129100d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_phys_low +
129200d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_phys_mid << 32) +
129300d0963fSdilpreet 			    (uint64_t)drv_regp[rn].pci_size_low +
129400d0963fSdilpreet 			    ((uint64_t)drv_regp[rn].pci_size_hi << 32))) {
129500d0963fSdilpreet 				tgt_err->tgt_dip = dip;
129600d0963fSdilpreet 				kmem_free(drv_regp, reglen);
129700d0963fSdilpreet 				return (DDI_WALK_TERMINATE);
129800d0963fSdilpreet 			}
129900d0963fSdilpreet 		}
130000d0963fSdilpreet 		kmem_free(drv_regp, reglen);
130100d0963fSdilpreet 	}
130200d0963fSdilpreet 	return (DDI_WALK_CONTINUE);
130300d0963fSdilpreet }
130400d0963fSdilpreet 
130500d0963fSdilpreet /*
130600d0963fSdilpreet  * impl_fix_ranges - fixes the config space entry of the "ranges"
130700d0963fSdilpreet  * property on psycho+ platforms.  (if changing this function please make sure
130800d0963fSdilpreet  * to change the pci_fix_ranges function in pcipsy.c)
130900d0963fSdilpreet  */
131000d0963fSdilpreet /*ARGSUSED*/
131100d0963fSdilpreet static void
pci_fix_ranges(dev_info_t * dip,pci_ranges_t * pci_ranges,int nrange)131200d0963fSdilpreet pci_fix_ranges(dev_info_t *dip, pci_ranges_t *pci_ranges, int nrange)
131300d0963fSdilpreet {
131400d0963fSdilpreet #if defined(__sparc)
131500d0963fSdilpreet 	char *name = ddi_binding_name(dip);
131600d0963fSdilpreet 
131700d0963fSdilpreet 	if ((strcmp(name, "pci108e,8000") == 0) ||
131800d0963fSdilpreet 	    (strcmp(name, "pci108e,a000") == 0) ||
131900d0963fSdilpreet 	    (strcmp(name, "pci108e,a001") == 0)) {
132000d0963fSdilpreet 		int i;
132100d0963fSdilpreet 		for (i = 0; i < nrange; i++, pci_ranges++)
132200d0963fSdilpreet 			if ((pci_ranges->child_high & PCI_REG_ADDR_M) ==
132300d0963fSdilpreet 			    PCI_ADDR_CONFIG)
132400d0963fSdilpreet 				pci_ranges->parent_low |=
132500d0963fSdilpreet 				    pci_ranges->child_high;
132600d0963fSdilpreet 	}
132700d0963fSdilpreet #endif
132800d0963fSdilpreet }
132900d0963fSdilpreet 
133000d0963fSdilpreet static int
pci_check_ranges(dev_info_t * dip,void * arg)133100d0963fSdilpreet pci_check_ranges(dev_info_t *dip, void *arg)
133200d0963fSdilpreet {
133300d0963fSdilpreet 	uint64_t range_parent_begin;
133400d0963fSdilpreet 	uint64_t range_parent_size;
133500d0963fSdilpreet 	uint64_t range_parent_end;
133600d0963fSdilpreet 	uint32_t space_type;
133700d0963fSdilpreet 	uint32_t bus_num;
133800d0963fSdilpreet 	uint32_t range_offset;
133900d0963fSdilpreet 	pci_ranges_t *pci_ranges, *rangep;
134000d0963fSdilpreet 	pci_bus_range_t *pci_bus_rangep;
134100d0963fSdilpreet 	int pci_ranges_length;
134200d0963fSdilpreet 	int nrange;
134300d0963fSdilpreet 	pci_target_err_t *tgt_err = (pci_target_err_t *)arg;
134400d0963fSdilpreet 	int i, size;
134500d0963fSdilpreet 	if (strcmp(ddi_node_name(dip), "pci") != 0 &&
134600d0963fSdilpreet 	    strcmp(ddi_node_name(dip), "pciex") != 0)
134700d0963fSdilpreet 		return (DDI_WALK_CONTINUE);
134800d0963fSdilpreet 
134900d0963fSdilpreet 	/*
135000d0963fSdilpreet 	 * Get the ranges property. Note we only look at the top level pci
135100d0963fSdilpreet 	 * node (hostbridge) which has a ranges property of type pci_ranges_t
135200d0963fSdilpreet 	 * not at pci-pci bridges.
135300d0963fSdilpreet 	 */
135400d0963fSdilpreet 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
135500d0963fSdilpreet 	    (caddr_t)&pci_ranges, &pci_ranges_length) != DDI_SUCCESS) {
135600d0963fSdilpreet 		/*
135700d0963fSdilpreet 		 * no ranges property - no translation needed
135800d0963fSdilpreet 		 */
135900d0963fSdilpreet 		tgt_err->tgt_pci_addr = tgt_err->tgt_err_addr;
136000d0963fSdilpreet 		tgt_err->tgt_pci_space = TGT_PCI_SPACE_UNKNOWN;
136100d0963fSdilpreet 		if (panicstr)
136200d0963fSdilpreet 			(void) pci_fm_walk_devs(ddi_get_child(dip),
136300d0963fSdilpreet 			    pci_check_regs, (void *)tgt_err);
136400d0963fSdilpreet 		else {
1365*3fe80ca4SDan Cross 			ndi_devi_enter(dip);
136600d0963fSdilpreet 			ddi_walk_devs(ddi_get_child(dip), pci_check_regs,
136700d0963fSdilpreet 			    (void *)tgt_err);
1368*3fe80ca4SDan Cross 			ndi_devi_exit(dip);
136900d0963fSdilpreet 		}
137000d0963fSdilpreet 		if (tgt_err->tgt_dip != NULL)
137100d0963fSdilpreet 			return (DDI_WALK_TERMINATE);
137200d0963fSdilpreet 		return (DDI_WALK_PRUNECHILD);
137300d0963fSdilpreet 	}
137400d0963fSdilpreet 	nrange = pci_ranges_length / sizeof (pci_ranges_t);
137500d0963fSdilpreet 	rangep = pci_ranges;
137600d0963fSdilpreet 
137700d0963fSdilpreet 	/* Need to fix the pci ranges property for psycho based systems */
137800d0963fSdilpreet 	pci_fix_ranges(dip, pci_ranges, nrange);
137900d0963fSdilpreet 
138000d0963fSdilpreet 	for (i = 0; i < nrange; i++, rangep++) {
138100d0963fSdilpreet 		range_parent_begin = ((uint64_t)rangep->parent_high << 32) +
138200d0963fSdilpreet 		    rangep->parent_low;
138300d0963fSdilpreet 		range_parent_size = ((uint64_t)rangep->size_high << 32) +
138400d0963fSdilpreet 		    rangep->size_low;
138500d0963fSdilpreet 		range_parent_end = range_parent_begin + range_parent_size - 1;
138600d0963fSdilpreet 
138700d0963fSdilpreet 		if ((tgt_err->tgt_err_addr < range_parent_begin) ||
138800d0963fSdilpreet 		    (tgt_err->tgt_err_addr > range_parent_end)) {
138900d0963fSdilpreet 			/* Not in range */
139000d0963fSdilpreet 			continue;
139100d0963fSdilpreet 		}
139200d0963fSdilpreet 		space_type = PCI_REG_ADDR_G(rangep->child_high);
139300d0963fSdilpreet 		if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
139400d0963fSdilpreet 			/* Config space address - check bus range */
139500d0963fSdilpreet 			range_offset = tgt_err->tgt_err_addr -
139600d0963fSdilpreet 			    range_parent_begin;
139700d0963fSdilpreet 			bus_num = PCI_REG_BUS_G(range_offset);
139800d0963fSdilpreet 			if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
139900d0963fSdilpreet 			    DDI_PROP_DONTPASS, "bus-range",
140000d0963fSdilpreet 			    (caddr_t)&pci_bus_rangep, &size) != DDI_SUCCESS) {
140100d0963fSdilpreet 				continue;
140200d0963fSdilpreet 			}
140300d0963fSdilpreet 			if ((bus_num < pci_bus_rangep->lo) ||
140400d0963fSdilpreet 			    (bus_num > pci_bus_rangep->hi)) {
140500d0963fSdilpreet 				/*
140600d0963fSdilpreet 				 * Bus number not appropriate for this
140700d0963fSdilpreet 				 * pci nexus.
140800d0963fSdilpreet 				 */
140900d0963fSdilpreet 				kmem_free(pci_bus_rangep, size);
141000d0963fSdilpreet 				continue;
141100d0963fSdilpreet 			}
141200d0963fSdilpreet 			kmem_free(pci_bus_rangep, size);
141300d0963fSdilpreet 		}
141400d0963fSdilpreet 
141500d0963fSdilpreet 		/* We have a match if we get here - compute pci address */
141600d0963fSdilpreet 		tgt_err->tgt_pci_addr = tgt_err->tgt_err_addr -
141700d0963fSdilpreet 		    range_parent_begin;
141800d0963fSdilpreet 		tgt_err->tgt_pci_addr += (((uint64_t)rangep->child_mid << 32) +
141900d0963fSdilpreet 		    rangep->child_low);
142000d0963fSdilpreet 		tgt_err->tgt_pci_space = space_type;
142100d0963fSdilpreet 		if (panicstr)
142200d0963fSdilpreet 			(void) pci_fm_walk_devs(ddi_get_child(dip),
142300d0963fSdilpreet 			    pci_check_regs, (void *)tgt_err);
142400d0963fSdilpreet 		else {
1425*3fe80ca4SDan Cross 			ndi_devi_enter(dip);
142600d0963fSdilpreet 			ddi_walk_devs(ddi_get_child(dip), pci_check_regs,
142700d0963fSdilpreet 			    (void *)tgt_err);
1428*3fe80ca4SDan Cross 			ndi_devi_exit(dip);
142900d0963fSdilpreet 		}
143000d0963fSdilpreet 		if (tgt_err->tgt_dip != NULL) {
143100d0963fSdilpreet 			kmem_free(pci_ranges, pci_ranges_length);
143200d0963fSdilpreet 			return (DDI_WALK_TERMINATE);
143300d0963fSdilpreet 		}
143400d0963fSdilpreet 	}
143500d0963fSdilpreet 	kmem_free(pci_ranges, pci_ranges_length);
143600d0963fSdilpreet 	return (DDI_WALK_PRUNECHILD);
143700d0963fSdilpreet }
143800d0963fSdilpreet 
143900d0963fSdilpreet /*
144000d0963fSdilpreet  * Function used to drain pci_target_queue, either during panic or after softint
144100d0963fSdilpreet  * is generated, to generate target device ereports based on captured physical
144200d0963fSdilpreet  * addresses
144300d0963fSdilpreet  */
144400d0963fSdilpreet /*ARGSUSED*/
144500d0963fSdilpreet static void
pci_target_drain(void * private_p,const void * err,const errorq_elem_t * arg __unused)1446b6e3994cSToomas Soome pci_target_drain(void *private_p, const void *err,
1447b6e3994cSToomas Soome     const errorq_elem_t *arg __unused)
144800d0963fSdilpreet {
1449b6e3994cSToomas Soome 	pci_target_err_t *tgt_err = (pci_target_err_t *)err;
145000d0963fSdilpreet 	char buf[FM_MAX_CLASS];
145100d0963fSdilpreet 
145200d0963fSdilpreet 	/*
145300d0963fSdilpreet 	 * The following assumes that all pci_pci bridge devices
145400d0963fSdilpreet 	 * are configured as transparant. Find the top-level pci
145500d0963fSdilpreet 	 * nexus which has tgt_err_addr in one of its ranges, converting this
145600d0963fSdilpreet 	 * to a pci address in the process. Then starting at this node do
145700d0963fSdilpreet 	 * another tree walk to find a device with the pci address we've
145800d0963fSdilpreet 	 * found within range of one of it's assigned-addresses properties.
145900d0963fSdilpreet 	 */
146000d0963fSdilpreet 	tgt_err->tgt_dip = NULL;
146100d0963fSdilpreet 	if (panicstr)
146200d0963fSdilpreet 		(void) pci_fm_walk_devs(ddi_root_node(), pci_check_ranges,
146300d0963fSdilpreet 		    (void *)tgt_err);
146400d0963fSdilpreet 	else
146500d0963fSdilpreet 		ddi_walk_devs(ddi_root_node(), pci_check_ranges,
146600d0963fSdilpreet 		    (void *)tgt_err);
146700d0963fSdilpreet 	if (tgt_err->tgt_dip == NULL)
146800d0963fSdilpreet 		return;
146900d0963fSdilpreet 
147000d0963fSdilpreet 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", tgt_err->tgt_bridge_type,
147100d0963fSdilpreet 	    tgt_err->tgt_err_class);
147200d0963fSdilpreet 	pci_fm_ereport_post(tgt_err->tgt_dip, buf, tgt_err->tgt_err_ena, 0,
147300d0963fSdilpreet 	    PCI_PA, DATA_TYPE_UINT64, tgt_err->tgt_err_addr, NULL);
147400d0963fSdilpreet }
147500d0963fSdilpreet 
147600d0963fSdilpreet void
pci_target_enqueue(uint64_t ena,char * class,char * bridge_type,uint64_t addr)147700d0963fSdilpreet pci_target_enqueue(uint64_t ena, char *class, char *bridge_type, uint64_t addr)
147800d0963fSdilpreet {
147900d0963fSdilpreet 	pci_target_err_t tgt_err;
148000d0963fSdilpreet 
148100d0963fSdilpreet 	tgt_err.tgt_err_ena = ena;
148200d0963fSdilpreet 	tgt_err.tgt_err_class = class;
148300d0963fSdilpreet 	tgt_err.tgt_bridge_type = bridge_type;
148400d0963fSdilpreet 	tgt_err.tgt_err_addr = addr;
148500d0963fSdilpreet 	errorq_dispatch(pci_target_queue, (void *)&tgt_err,
148600d0963fSdilpreet 	    sizeof (pci_target_err_t), ERRORQ_ASYNC);
148700d0963fSdilpreet }
148800d0963fSdilpreet 
148900d0963fSdilpreet void
pci_targetq_init(void)149000d0963fSdilpreet pci_targetq_init(void)
149100d0963fSdilpreet {
149200d0963fSdilpreet 	/*
149300d0963fSdilpreet 	 * PCI target errorq, to schedule async handling of generation of
149400d0963fSdilpreet 	 * target device ereports based on captured physical address.
149500d0963fSdilpreet 	 * The errorq is created here but destroyed when _fini is called
149600d0963fSdilpreet 	 * for the pci module.
149700d0963fSdilpreet 	 */
149800d0963fSdilpreet 	if (pci_target_queue == NULL) {
149900d0963fSdilpreet 		pci_target_queue = errorq_create("pci_target_queue",
1500b6e3994cSToomas Soome 		    pci_target_drain, (void *)NULL,
150100d0963fSdilpreet 		    TARGET_MAX_ERRS, sizeof (pci_target_err_t), FM_ERR_PIL,
150200d0963fSdilpreet 		    ERRORQ_VITAL);
150300d0963fSdilpreet 		if (pci_target_queue == NULL)
150400d0963fSdilpreet 			panic("failed to create required system error queue");
150500d0963fSdilpreet 	}
150600d0963fSdilpreet }
1507