125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2125cf1a30Sjl /*
2225cf1a30Sjl  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
2325cf1a30Sjl  * Use is subject to license terms.
2425cf1a30Sjl  */
2525cf1a30Sjl 
2625cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
2725cf1a30Sjl 
2825cf1a30Sjl /*
2925cf1a30Sjl  * CMU-CH PBM implementation:
3025cf1a30Sjl  *	initialization
3125cf1a30Sjl  *	Bus error interrupt handler
3225cf1a30Sjl  */
3325cf1a30Sjl 
3425cf1a30Sjl #include <sys/types.h>
3525cf1a30Sjl #include <sys/kmem.h>
3625cf1a30Sjl #include <sys/spl.h>
3725cf1a30Sjl #include <sys/sysmacros.h>
3825cf1a30Sjl #include <sys/sunddi.h>
3925cf1a30Sjl #include <sys/fm/protocol.h>
4025cf1a30Sjl #include <sys/fm/util.h>
4125cf1a30Sjl #include <sys/machsystm.h>
4225cf1a30Sjl #include <sys/async.h>
4325cf1a30Sjl #include <sys/ddi_impldefs.h>
4425cf1a30Sjl #include <sys/ontrap.h>
4525cf1a30Sjl #include <sys/pcicmu/pcicmu.h>
4625cf1a30Sjl #include <sys/membar.h>
4725cf1a30Sjl #include <sys/ivintr.h>
4825cf1a30Sjl 
4925cf1a30Sjl static uint_t pcmu_pbm_error_intr(caddr_t a);
5025cf1a30Sjl 
5125cf1a30Sjl /* The nexus interrupt priority values */
5225cf1a30Sjl int pcmu_pil[] = {14, 14, 14, 14, 14, 14};
5325cf1a30Sjl 
5425cf1a30Sjl void
pcmu_pbm_create(pcmu_t * pcmu_p)5525cf1a30Sjl pcmu_pbm_create(pcmu_t *pcmu_p)
5625cf1a30Sjl {
5725cf1a30Sjl 	pcmu_pbm_t *pcbm_p;
5825cf1a30Sjl 	int len;
5925cf1a30Sjl 	dev_info_t *dip = pcmu_p->pcmu_dip;
6025cf1a30Sjl 
6125cf1a30Sjl 	/*
6225cf1a30Sjl 	 * Allocate a state structure for the PBM and cross-link it
6325cf1a30Sjl 	 * to its per pci node state structure.
6425cf1a30Sjl 	 */
6525cf1a30Sjl 	pcbm_p = (pcmu_pbm_t *)kmem_zalloc(sizeof (pcmu_pbm_t), KM_SLEEP);
6625cf1a30Sjl 	pcmu_p->pcmu_pcbm_p = pcbm_p;
6725cf1a30Sjl 	pcbm_p->pcbm_pcmu_p = pcmu_p;
6825cf1a30Sjl 
6925cf1a30Sjl 	len = snprintf(pcbm_p->pcbm_nameinst_str,
7025cf1a30Sjl 	    sizeof (pcbm_p->pcbm_nameinst_str), "%s%d", NAMEINST(dip));
7125cf1a30Sjl 	pcbm_p->pcbm_nameaddr_str = pcbm_p->pcbm_nameinst_str + ++len;
7225cf1a30Sjl 	(void) snprintf(pcbm_p->pcbm_nameaddr_str,
7325cf1a30Sjl 	    sizeof (pcbm_p->pcbm_nameinst_str) - len, "%s@%s", NAMEADDR(dip));
7425cf1a30Sjl 
7525cf1a30Sjl 	pcmu_pbm_setup(pcbm_p);
7625cf1a30Sjl 
7725cf1a30Sjl 	PCMU_DBG4(PCMU_DBG_ATTACH, dip,
7825cf1a30Sjl 	    "pcmu_pbm_create: ctrl=%x, afsr=%x, afar=%x, diag=%x\n",
7925cf1a30Sjl 	    pcbm_p->pcbm_ctrl_reg, pcbm_p->pcbm_async_flt_status_reg,
8025cf1a30Sjl 	    pcbm_p->pcbm_async_flt_addr_reg, pcbm_p->pcbm_diag_reg);
8125cf1a30Sjl 	PCMU_DBG1(PCMU_DBG_ATTACH, dip, "pcmu_pbm_create: conf=%x\n",
8225cf1a30Sjl 	    pcbm_p->pcbm_config_header);
8325cf1a30Sjl 
8425cf1a30Sjl 	/*
8525cf1a30Sjl 	 * Register a function to disable pbm error interrupts during a panic.
8625cf1a30Sjl 	 */
8725cf1a30Sjl 	bus_func_register(BF_TYPE_ERRDIS,
8825cf1a30Sjl 	    (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
8925cf1a30Sjl 
9025cf1a30Sjl 	/*
9125cf1a30Sjl 	 * create the interrupt-priorities property if it doesn't
9225cf1a30Sjl 	 * already exist to provide a hint as to the PIL level for
9325cf1a30Sjl 	 * our interrupt.
9425cf1a30Sjl 	 */
9525cf1a30Sjl 	if (ddi_getproplen(DDI_DEV_T_ANY, dip,
9625cf1a30Sjl 	    DDI_PROP_DONTPASS, "interrupt-priorities",
9725cf1a30Sjl 	    &len) != DDI_PROP_SUCCESS) {
9825cf1a30Sjl 		/* Create the interrupt-priorities property. */
9925cf1a30Sjl 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
10025cf1a30Sjl 		    DDI_PROP_CANSLEEP, "interrupt-priorities",
10125cf1a30Sjl 		    (caddr_t)pcmu_pil, sizeof (pcmu_pil));
10225cf1a30Sjl 	}
10325cf1a30Sjl 	pcmu_pbm_configure(pcbm_p);
10425cf1a30Sjl }
10525cf1a30Sjl 
10625cf1a30Sjl int
pcmu_pbm_register_intr(pcmu_pbm_t * pcbm_p)10725cf1a30Sjl pcmu_pbm_register_intr(pcmu_pbm_t *pcbm_p)
10825cf1a30Sjl {
10925cf1a30Sjl 	pcmu_t		*pcmu_p = pcbm_p->pcbm_pcmu_p;
11025cf1a30Sjl 	uint32_t	mondo;
11125cf1a30Sjl 	int		r = DDI_SUCCESS;
11225cf1a30Sjl 
11325cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
11425cf1a30Sjl 
11525cf1a30Sjl 	/*
11625cf1a30Sjl 	 * Install the PCI error interrupt handler.
11725cf1a30Sjl 	 */
11825cf1a30Sjl 	mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
11925cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM]);
12025cf1a30Sjl 
121*b0fc0e77Sgovinda 	VERIFY(add_ivintr(mondo, pcmu_pil[CBNINTR_PBM],
122*b0fc0e77Sgovinda 	    (intrfunc)pcmu_pbm_error_intr, (caddr_t)pcmu_p, NULL, NULL) == 0);
12325cf1a30Sjl 
12425cf1a30Sjl 	pcbm_p->pcbm_iblock_cookie = (void *)(uintptr_t)pcmu_pil[CBNINTR_PBM];
12525cf1a30Sjl 
12625cf1a30Sjl 	/*
12725cf1a30Sjl 	 * Create the pokefault mutex at the PIL below the error interrupt.
12825cf1a30Sjl 	 */
12925cf1a30Sjl 
13025cf1a30Sjl 	mutex_init(&pcbm_p->pcbm_pokeflt_mutex, NULL, MUTEX_DRIVER,
13125cf1a30Sjl 	    (void *)(uintptr_t)ipltospl(spltoipl(
13225cf1a30Sjl 	    (int)(uintptr_t)pcbm_p->pcbm_iblock_cookie) - 1));
13325cf1a30Sjl 
13425cf1a30Sjl 	return (PCMU_ATTACH_RETCODE(PCMU_PBM_OBJ, PCMU_OBJ_INTR_ADD, r));
13525cf1a30Sjl }
13625cf1a30Sjl 
13725cf1a30Sjl void
pcmu_pbm_destroy(pcmu_t * pcmu_p)13825cf1a30Sjl pcmu_pbm_destroy(pcmu_t *pcmu_p)
13925cf1a30Sjl {
14025cf1a30Sjl 	pcmu_pbm_t		*pcbm_p = pcmu_p->pcmu_pcbm_p;
14125cf1a30Sjl 	pcmu_ib_t		*pib_p = pcmu_p->pcmu_ib_p;
14225cf1a30Sjl 	uint32_t	mondo;
14325cf1a30Sjl 
14425cf1a30Sjl 	PCMU_DBG0(PCMU_DBG_DETACH, pcmu_p->pcmu_dip, "pcmu_pbm_destroy:\n");
14525cf1a30Sjl 
14625cf1a30Sjl 	mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
14725cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM]);
14825cf1a30Sjl 
14925cf1a30Sjl 	/*
15025cf1a30Sjl 	 * Free the pokefault mutex.
15125cf1a30Sjl 	 */
15225cf1a30Sjl 	mutex_destroy(&pcbm_p->pcbm_pokeflt_mutex);
15325cf1a30Sjl 
15425cf1a30Sjl 	/*
15525cf1a30Sjl 	 * Remove the error interrupt.
15625cf1a30Sjl 	 */
15725cf1a30Sjl 	intr_dist_rem(pcmu_pbm_intr_dist, pcbm_p);
15825cf1a30Sjl 	pcmu_ib_intr_disable(pib_p,
15925cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM], PCMU_IB_INTR_WAIT);
160*b0fc0e77Sgovinda 
161*b0fc0e77Sgovinda 	VERIFY(rem_ivintr(mondo, pcmu_pil[CBNINTR_PBM]) == 0);
16225cf1a30Sjl 
16325cf1a30Sjl 	/*
16425cf1a30Sjl 	 * Remove the error disable function.
16525cf1a30Sjl 	 */
16625cf1a30Sjl 	bus_func_unregister(BF_TYPE_ERRDIS,
16725cf1a30Sjl 	    (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
16825cf1a30Sjl 
16925cf1a30Sjl 	pcmu_pbm_teardown(pcbm_p);
17025cf1a30Sjl 
17125cf1a30Sjl 	/*
17225cf1a30Sjl 	 * Free the pbm state structure.
17325cf1a30Sjl 	 */
17425cf1a30Sjl 	kmem_free(pcbm_p, sizeof (pcmu_pbm_t));
17525cf1a30Sjl 	pcmu_p->pcmu_pcbm_p = NULL;
17625cf1a30Sjl }
17725cf1a30Sjl 
17825cf1a30Sjl static uint_t
pcmu_pbm_error_intr(caddr_t a)17925cf1a30Sjl pcmu_pbm_error_intr(caddr_t a)
18025cf1a30Sjl {
18125cf1a30Sjl 	pcmu_t *pcmu_p = (pcmu_t *)a;
18225cf1a30Sjl 	pcmu_pbm_t *pcbm_p = pcmu_p->pcmu_pcbm_p;
18325cf1a30Sjl 	ddi_fm_error_t derr;
18425cf1a30Sjl 	int err = DDI_FM_OK;
18525cf1a30Sjl 	on_trap_data_t *otp = pcbm_p->pcbm_ontrap_data;
18625cf1a30Sjl 
18725cf1a30Sjl 	bzero(&derr, sizeof (ddi_fm_error_t));
18825cf1a30Sjl 	derr.fme_version = DDI_FME_VERSION;
18925cf1a30Sjl 	mutex_enter(&pcmu_p->pcmu_err_mutex);
19025cf1a30Sjl 	if ((otp != NULL) && (otp->ot_prot & OT_DATA_ACCESS)) {
19125cf1a30Sjl 		/*
19225cf1a30Sjl 		 * ddi_poke protection, check nexus and children for
19325cf1a30Sjl 		 * expected errors.
19425cf1a30Sjl 		 */
19525cf1a30Sjl 		otp->ot_trap |= OT_DATA_ACCESS;
19625cf1a30Sjl 		membar_sync();
19725cf1a30Sjl 		derr.fme_flag = DDI_FM_ERR_POKE;
19825cf1a30Sjl 		err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
19925cf1a30Sjl 		    (void *)pcmu_p, PCI_INTR_CALL);
20025cf1a30Sjl 	} else if (pcmu_check_error(pcmu_p) != 0) {
20125cf1a30Sjl 		/*
20225cf1a30Sjl 		 * unprotected error, check for all errors.
20325cf1a30Sjl 		 */
20425cf1a30Sjl 		if (pcmu_errtrig_pa) {
20525cf1a30Sjl 			(void) ldphysio(pcmu_errtrig_pa);
20625cf1a30Sjl 		}
20725cf1a30Sjl 		derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
20825cf1a30Sjl 		err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
20925cf1a30Sjl 		    (void *)pcmu_p, PCI_INTR_CALL);
21025cf1a30Sjl 	}
21125cf1a30Sjl 
21225cf1a30Sjl 	if (err == DDI_FM_FATAL) {
21325cf1a30Sjl 		if (pcmu_panic_on_fatal_errors) {
21425cf1a30Sjl 			mutex_exit(&pcmu_p->pcmu_err_mutex);
21525cf1a30Sjl 			cmn_err(CE_PANIC, "%s-%d: Fatal PCI bus error(s)\n",
21625cf1a30Sjl 			    ddi_driver_name(pcmu_p->pcmu_dip),
21725cf1a30Sjl 			    ddi_get_instance(pcmu_p->pcmu_dip));
21825cf1a30Sjl 		}
21925cf1a30Sjl 	}
22025cf1a30Sjl 
22125cf1a30Sjl 	mutex_exit(&pcmu_p->pcmu_err_mutex);
22225cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
22325cf1a30Sjl 	return (DDI_INTR_CLAIMED);
22425cf1a30Sjl }
22525cf1a30Sjl 
22625cf1a30Sjl void
pcmu_pbm_suspend(pcmu_pbm_t * pcbm_p)22725cf1a30Sjl pcmu_pbm_suspend(pcmu_pbm_t *pcbm_p)
22825cf1a30Sjl {
22925cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
23025cf1a30Sjl 	pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
23125cf1a30Sjl 	pcbm_p->pcbm_imr_save = *ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino);
23225cf1a30Sjl }
23325cf1a30Sjl 
23425cf1a30Sjl void
pcmu_pbm_resume(pcmu_pbm_t * pcbm_p)23525cf1a30Sjl pcmu_pbm_resume(pcmu_pbm_t *pcbm_p)
23625cf1a30Sjl {
23725cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
23825cf1a30Sjl 	pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
23925cf1a30Sjl 
24025cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, ino);
24125cf1a30Sjl 	*ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino) = pcbm_p->pcbm_imr_save;
24225cf1a30Sjl }
24325cf1a30Sjl 
24425cf1a30Sjl void
pcmu_pbm_intr_dist(void * arg)24525cf1a30Sjl pcmu_pbm_intr_dist(void *arg)
24625cf1a30Sjl {
24725cf1a30Sjl 	pcmu_pbm_t *pcbm_p = (pcmu_pbm_t *)arg;
24825cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
24925cf1a30Sjl 	pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
25025cf1a30Sjl 	pcmu_ib_ino_t ino =
25125cf1a30Sjl 	    PCMU_IB_MONDO_TO_INO(pcmu_p->pcmu_inos[CBNINTR_PBM]);
25225cf1a30Sjl 	mutex_enter(&pib_p->pib_intr_lock);
25325cf1a30Sjl 	pcmu_ib_intr_dist_nintr(pib_p, ino, ib_intr_map_reg_addr(pib_p, ino));
25425cf1a30Sjl 	mutex_exit(&pib_p->pib_intr_lock);
25525cf1a30Sjl }
25625cf1a30Sjl 
25725cf1a30Sjl /*
25825cf1a30Sjl  * Function used to log PBM AFSR register bits and to lookup and fault
25925cf1a30Sjl  * handle associated with PBM AFAR register. Called by
26025cf1a30Sjl  * pcmu_pbm_err_handler with pcmu_err_mutex held.
26125cf1a30Sjl  */
26225cf1a30Sjl int
pcmu_pbm_afsr_report(dev_info_t * dip,uint64_t fme_ena,pcmu_pbm_errstate_t * pbm_err_p)26325cf1a30Sjl pcmu_pbm_afsr_report(dev_info_t *dip, uint64_t fme_ena,
26425cf1a30Sjl     pcmu_pbm_errstate_t *pbm_err_p)
26525cf1a30Sjl {
26625cf1a30Sjl 	int fatal = 0;
26725cf1a30Sjl 	/* LINTED variable */
26825cf1a30Sjl 	pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip));
26925cf1a30Sjl 
27025cf1a30Sjl 	ASSERT(MUTEX_HELD(&pcmu_p->pcmu_err_mutex));
27125cf1a30Sjl 
27225cf1a30Sjl 	pbm_err_p->pcbm_pri = PBM_PRIMARY;
27325cf1a30Sjl 	(void) pcmu_pbm_classify(pbm_err_p);
27425cf1a30Sjl 
27525cf1a30Sjl 	/*
27625cf1a30Sjl 	 * We are currently not dealing with the multiple error
27725cf1a30Sjl 	 * case, for any secondary errors we will panic.
27825cf1a30Sjl 	 */
27925cf1a30Sjl 	pbm_err_p->pcbm_pri = PBM_SECONDARY;
28025cf1a30Sjl 	if (pcmu_pbm_classify(pbm_err_p)) {
28125cf1a30Sjl 		fatal++;
28225cf1a30Sjl 		pcmu_pbm_ereport_post(dip, fme_ena, pbm_err_p);
28325cf1a30Sjl 	}
28425cf1a30Sjl 
28525cf1a30Sjl 	if (fatal) {
28625cf1a30Sjl 		return (DDI_FM_FATAL);
28725cf1a30Sjl 	}
28825cf1a30Sjl 	return (DDI_FM_NONFATAL);
28925cf1a30Sjl }
290