1*25cf1a30Sjl /*
2*25cf1a30Sjl  * CDDL HEADER START
3*25cf1a30Sjl  *
4*25cf1a30Sjl  * The contents of this file are subject to the terms of the
5*25cf1a30Sjl  * Common Development and Distribution License (the "License").
6*25cf1a30Sjl  * You may not use this file except in compliance with the License.
7*25cf1a30Sjl  *
8*25cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*25cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
10*25cf1a30Sjl  * See the License for the specific language governing permissions
11*25cf1a30Sjl  * and limitations under the License.
12*25cf1a30Sjl  *
13*25cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
14*25cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*25cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
16*25cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
17*25cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
18*25cf1a30Sjl  *
19*25cf1a30Sjl  * CDDL HEADER END
20*25cf1a30Sjl  */
21*25cf1a30Sjl /*
22*25cf1a30Sjl  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*25cf1a30Sjl  * Use is subject to license terms.
24*25cf1a30Sjl  */
25*25cf1a30Sjl 
26*25cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*25cf1a30Sjl 
28*25cf1a30Sjl /*
29*25cf1a30Sjl  * CMU-CH PBM implementation:
30*25cf1a30Sjl  *	initialization
31*25cf1a30Sjl  *	Bus error interrupt handler
32*25cf1a30Sjl  */
33*25cf1a30Sjl 
34*25cf1a30Sjl #include <sys/types.h>
35*25cf1a30Sjl #include <sys/kmem.h>
36*25cf1a30Sjl #include <sys/spl.h>
37*25cf1a30Sjl #include <sys/sysmacros.h>
38*25cf1a30Sjl #include <sys/sunddi.h>
39*25cf1a30Sjl #include <sys/fm/protocol.h>
40*25cf1a30Sjl #include <sys/fm/util.h>
41*25cf1a30Sjl #include <sys/machsystm.h>
42*25cf1a30Sjl #include <sys/async.h>
43*25cf1a30Sjl #include <sys/ddi_impldefs.h>
44*25cf1a30Sjl #include <sys/ontrap.h>
45*25cf1a30Sjl #include <sys/pcicmu/pcicmu.h>
46*25cf1a30Sjl #include <sys/membar.h>
47*25cf1a30Sjl #include <sys/ivintr.h>
48*25cf1a30Sjl 
49*25cf1a30Sjl /*LINTLIBRARY*/
50*25cf1a30Sjl 
51*25cf1a30Sjl static uint_t pcmu_pbm_error_intr(caddr_t a);
52*25cf1a30Sjl 
53*25cf1a30Sjl /* The nexus interrupt priority values */
54*25cf1a30Sjl int pcmu_pil[] = {14, 14, 14, 14, 14, 14};
55*25cf1a30Sjl 
56*25cf1a30Sjl void
57*25cf1a30Sjl pcmu_pbm_create(pcmu_t *pcmu_p)
58*25cf1a30Sjl {
59*25cf1a30Sjl 	pcmu_pbm_t *pcbm_p;
60*25cf1a30Sjl 	int len;
61*25cf1a30Sjl 	dev_info_t *dip = pcmu_p->pcmu_dip;
62*25cf1a30Sjl 
63*25cf1a30Sjl 	/*
64*25cf1a30Sjl 	 * Allocate a state structure for the PBM and cross-link it
65*25cf1a30Sjl 	 * to its per pci node state structure.
66*25cf1a30Sjl 	 */
67*25cf1a30Sjl 	pcbm_p = (pcmu_pbm_t *)kmem_zalloc(sizeof (pcmu_pbm_t), KM_SLEEP);
68*25cf1a30Sjl 	pcmu_p->pcmu_pcbm_p = pcbm_p;
69*25cf1a30Sjl 	pcbm_p->pcbm_pcmu_p = pcmu_p;
70*25cf1a30Sjl 
71*25cf1a30Sjl 	len = snprintf(pcbm_p->pcbm_nameinst_str,
72*25cf1a30Sjl 	    sizeof (pcbm_p->pcbm_nameinst_str), "%s%d", NAMEINST(dip));
73*25cf1a30Sjl 	pcbm_p->pcbm_nameaddr_str = pcbm_p->pcbm_nameinst_str + ++len;
74*25cf1a30Sjl 	(void) snprintf(pcbm_p->pcbm_nameaddr_str,
75*25cf1a30Sjl 	    sizeof (pcbm_p->pcbm_nameinst_str) - len, "%s@%s", NAMEADDR(dip));
76*25cf1a30Sjl 
77*25cf1a30Sjl 	pcmu_pbm_setup(pcbm_p);
78*25cf1a30Sjl 
79*25cf1a30Sjl 	PCMU_DBG4(PCMU_DBG_ATTACH, dip,
80*25cf1a30Sjl 	    "pcmu_pbm_create: ctrl=%x, afsr=%x, afar=%x, diag=%x\n",
81*25cf1a30Sjl 	    pcbm_p->pcbm_ctrl_reg, pcbm_p->pcbm_async_flt_status_reg,
82*25cf1a30Sjl 	    pcbm_p->pcbm_async_flt_addr_reg, pcbm_p->pcbm_diag_reg);
83*25cf1a30Sjl 	PCMU_DBG1(PCMU_DBG_ATTACH, dip, "pcmu_pbm_create: conf=%x\n",
84*25cf1a30Sjl 	    pcbm_p->pcbm_config_header);
85*25cf1a30Sjl 
86*25cf1a30Sjl 	/*
87*25cf1a30Sjl 	 * Register a function to disable pbm error interrupts during a panic.
88*25cf1a30Sjl 	 */
89*25cf1a30Sjl 	bus_func_register(BF_TYPE_ERRDIS,
90*25cf1a30Sjl 	    (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
91*25cf1a30Sjl 
92*25cf1a30Sjl 	/*
93*25cf1a30Sjl 	 * create the interrupt-priorities property if it doesn't
94*25cf1a30Sjl 	 * already exist to provide a hint as to the PIL level for
95*25cf1a30Sjl 	 * our interrupt.
96*25cf1a30Sjl 	 */
97*25cf1a30Sjl 	if (ddi_getproplen(DDI_DEV_T_ANY, dip,
98*25cf1a30Sjl 	    DDI_PROP_DONTPASS, "interrupt-priorities",
99*25cf1a30Sjl 	    &len) != DDI_PROP_SUCCESS) {
100*25cf1a30Sjl 		/* Create the interrupt-priorities property. */
101*25cf1a30Sjl 		(void) ddi_prop_create(DDI_DEV_T_NONE, dip,
102*25cf1a30Sjl 		    DDI_PROP_CANSLEEP, "interrupt-priorities",
103*25cf1a30Sjl 		    (caddr_t)pcmu_pil, sizeof (pcmu_pil));
104*25cf1a30Sjl 	}
105*25cf1a30Sjl 	pcmu_pbm_configure(pcbm_p);
106*25cf1a30Sjl }
107*25cf1a30Sjl 
108*25cf1a30Sjl int
109*25cf1a30Sjl pcmu_pbm_register_intr(pcmu_pbm_t *pcbm_p)
110*25cf1a30Sjl {
111*25cf1a30Sjl 	pcmu_t		*pcmu_p = pcbm_p->pcbm_pcmu_p;
112*25cf1a30Sjl 	uint32_t	mondo;
113*25cf1a30Sjl 	int		r = DDI_SUCCESS;
114*25cf1a30Sjl 
115*25cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
116*25cf1a30Sjl 
117*25cf1a30Sjl 	/*
118*25cf1a30Sjl 	 * Install the PCI error interrupt handler.
119*25cf1a30Sjl 	 */
120*25cf1a30Sjl 	mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
121*25cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM]);
122*25cf1a30Sjl 
123*25cf1a30Sjl 	VERIFY(add_ivintr(mondo, pcmu_pil[CBNINTR_PBM], pcmu_pbm_error_intr,
124*25cf1a30Sjl 			(caddr_t)pcmu_p, NULL) == 0);
125*25cf1a30Sjl 
126*25cf1a30Sjl 	pcbm_p->pcbm_iblock_cookie = (void *)(uintptr_t)pcmu_pil[CBNINTR_PBM];
127*25cf1a30Sjl 
128*25cf1a30Sjl 	/*
129*25cf1a30Sjl 	 * Create the pokefault mutex at the PIL below the error interrupt.
130*25cf1a30Sjl 	 */
131*25cf1a30Sjl 
132*25cf1a30Sjl 	mutex_init(&pcbm_p->pcbm_pokeflt_mutex, NULL, MUTEX_DRIVER,
133*25cf1a30Sjl 	    (void *)(uintptr_t)ipltospl(spltoipl(
134*25cf1a30Sjl 	    (int)(uintptr_t)pcbm_p->pcbm_iblock_cookie) - 1));
135*25cf1a30Sjl 
136*25cf1a30Sjl 	return (PCMU_ATTACH_RETCODE(PCMU_PBM_OBJ, PCMU_OBJ_INTR_ADD, r));
137*25cf1a30Sjl }
138*25cf1a30Sjl 
139*25cf1a30Sjl void
140*25cf1a30Sjl pcmu_pbm_destroy(pcmu_t *pcmu_p)
141*25cf1a30Sjl {
142*25cf1a30Sjl 	pcmu_pbm_t		*pcbm_p = pcmu_p->pcmu_pcbm_p;
143*25cf1a30Sjl 	pcmu_ib_t		*pib_p = pcmu_p->pcmu_ib_p;
144*25cf1a30Sjl 	uint32_t	mondo;
145*25cf1a30Sjl 
146*25cf1a30Sjl 	PCMU_DBG0(PCMU_DBG_DETACH, pcmu_p->pcmu_dip, "pcmu_pbm_destroy:\n");
147*25cf1a30Sjl 
148*25cf1a30Sjl 	mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p,
149*25cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM]);
150*25cf1a30Sjl 
151*25cf1a30Sjl 	/*
152*25cf1a30Sjl 	 * Free the pokefault mutex.
153*25cf1a30Sjl 	 */
154*25cf1a30Sjl 	mutex_destroy(&pcbm_p->pcbm_pokeflt_mutex);
155*25cf1a30Sjl 
156*25cf1a30Sjl 	/*
157*25cf1a30Sjl 	 * Remove the error interrupt.
158*25cf1a30Sjl 	 */
159*25cf1a30Sjl 	intr_dist_rem(pcmu_pbm_intr_dist, pcbm_p);
160*25cf1a30Sjl 	pcmu_ib_intr_disable(pib_p,
161*25cf1a30Sjl 	    pcmu_p->pcmu_inos[CBNINTR_PBM], PCMU_IB_INTR_WAIT);
162*25cf1a30Sjl 	rem_ivintr(mondo, NULL);
163*25cf1a30Sjl 
164*25cf1a30Sjl 	/*
165*25cf1a30Sjl 	 * Remove the error disable function.
166*25cf1a30Sjl 	 */
167*25cf1a30Sjl 	bus_func_unregister(BF_TYPE_ERRDIS,
168*25cf1a30Sjl 	    (busfunc_t)pcmu_pbm_disable_errors, pcbm_p);
169*25cf1a30Sjl 
170*25cf1a30Sjl 	pcmu_pbm_teardown(pcbm_p);
171*25cf1a30Sjl 
172*25cf1a30Sjl 	/*
173*25cf1a30Sjl 	 * Free the pbm state structure.
174*25cf1a30Sjl 	 */
175*25cf1a30Sjl 	kmem_free(pcbm_p, sizeof (pcmu_pbm_t));
176*25cf1a30Sjl 	pcmu_p->pcmu_pcbm_p = NULL;
177*25cf1a30Sjl }
178*25cf1a30Sjl 
179*25cf1a30Sjl static uint_t
180*25cf1a30Sjl pcmu_pbm_error_intr(caddr_t a)
181*25cf1a30Sjl {
182*25cf1a30Sjl 	pcmu_t *pcmu_p = (pcmu_t *)a;
183*25cf1a30Sjl 	pcmu_pbm_t *pcbm_p = pcmu_p->pcmu_pcbm_p;
184*25cf1a30Sjl 	ddi_fm_error_t derr;
185*25cf1a30Sjl 	int err = DDI_FM_OK;
186*25cf1a30Sjl 	on_trap_data_t *otp = pcbm_p->pcbm_ontrap_data;
187*25cf1a30Sjl 
188*25cf1a30Sjl 	bzero(&derr, sizeof (ddi_fm_error_t));
189*25cf1a30Sjl 	derr.fme_version = DDI_FME_VERSION;
190*25cf1a30Sjl 	mutex_enter(&pcmu_p->pcmu_err_mutex);
191*25cf1a30Sjl 	if ((otp != NULL) && (otp->ot_prot & OT_DATA_ACCESS)) {
192*25cf1a30Sjl 		/*
193*25cf1a30Sjl 		 * ddi_poke protection, check nexus and children for
194*25cf1a30Sjl 		 * expected errors.
195*25cf1a30Sjl 		 */
196*25cf1a30Sjl 		otp->ot_trap |= OT_DATA_ACCESS;
197*25cf1a30Sjl 		membar_sync();
198*25cf1a30Sjl 		derr.fme_flag = DDI_FM_ERR_POKE;
199*25cf1a30Sjl 		err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
200*25cf1a30Sjl 		    (void *)pcmu_p, PCI_INTR_CALL);
201*25cf1a30Sjl 	} else if (pcmu_check_error(pcmu_p) != 0) {
202*25cf1a30Sjl 		/*
203*25cf1a30Sjl 		 * unprotected error, check for all errors.
204*25cf1a30Sjl 		 */
205*25cf1a30Sjl 		if (pcmu_errtrig_pa) {
206*25cf1a30Sjl 			(void) ldphysio(pcmu_errtrig_pa);
207*25cf1a30Sjl 		}
208*25cf1a30Sjl 		derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
209*25cf1a30Sjl 		err = pcmu_pbm_err_handler(pcmu_p->pcmu_dip, &derr,
210*25cf1a30Sjl 		    (void *)pcmu_p, PCI_INTR_CALL);
211*25cf1a30Sjl 	}
212*25cf1a30Sjl 
213*25cf1a30Sjl 	if (err == DDI_FM_FATAL) {
214*25cf1a30Sjl 		if (pcmu_panic_on_fatal_errors) {
215*25cf1a30Sjl 			mutex_exit(&pcmu_p->pcmu_err_mutex);
216*25cf1a30Sjl 			cmn_err(CE_PANIC, "%s-%d: Fatal PCI bus error(s)\n",
217*25cf1a30Sjl 			    ddi_driver_name(pcmu_p->pcmu_dip),
218*25cf1a30Sjl 			    ddi_get_instance(pcmu_p->pcmu_dip));
219*25cf1a30Sjl 		}
220*25cf1a30Sjl 	}
221*25cf1a30Sjl 
222*25cf1a30Sjl 	mutex_exit(&pcmu_p->pcmu_err_mutex);
223*25cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, pcmu_p->pcmu_inos[CBNINTR_PBM]);
224*25cf1a30Sjl 	return (DDI_INTR_CLAIMED);
225*25cf1a30Sjl }
226*25cf1a30Sjl 
227*25cf1a30Sjl void
228*25cf1a30Sjl pcmu_pbm_suspend(pcmu_pbm_t *pcbm_p)
229*25cf1a30Sjl {
230*25cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
231*25cf1a30Sjl 	pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
232*25cf1a30Sjl 	pcbm_p->pcbm_imr_save = *ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino);
233*25cf1a30Sjl }
234*25cf1a30Sjl 
235*25cf1a30Sjl void
236*25cf1a30Sjl pcmu_pbm_resume(pcmu_pbm_t *pcbm_p)
237*25cf1a30Sjl {
238*25cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
239*25cf1a30Sjl 	pcmu_ib_ino_t ino = pcmu_p->pcmu_inos[CBNINTR_PBM];
240*25cf1a30Sjl 
241*25cf1a30Sjl 	pcmu_ib_nintr_clear(pcmu_p->pcmu_ib_p, ino);
242*25cf1a30Sjl 	*ib_intr_map_reg_addr(pcmu_p->pcmu_ib_p, ino) = pcbm_p->pcbm_imr_save;
243*25cf1a30Sjl }
244*25cf1a30Sjl 
245*25cf1a30Sjl void
246*25cf1a30Sjl pcmu_pbm_intr_dist(void *arg)
247*25cf1a30Sjl {
248*25cf1a30Sjl 	pcmu_pbm_t *pcbm_p = (pcmu_pbm_t *)arg;
249*25cf1a30Sjl 	pcmu_t *pcmu_p = pcbm_p->pcbm_pcmu_p;
250*25cf1a30Sjl 	pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
251*25cf1a30Sjl 	pcmu_ib_ino_t ino =
252*25cf1a30Sjl 	    PCMU_IB_MONDO_TO_INO(pcmu_p->pcmu_inos[CBNINTR_PBM]);
253*25cf1a30Sjl 	mutex_enter(&pib_p->pib_intr_lock);
254*25cf1a30Sjl 	pcmu_ib_intr_dist_nintr(pib_p, ino, ib_intr_map_reg_addr(pib_p, ino));
255*25cf1a30Sjl 	mutex_exit(&pib_p->pib_intr_lock);
256*25cf1a30Sjl }
257*25cf1a30Sjl 
258*25cf1a30Sjl /*
259*25cf1a30Sjl  * Function used to log PBM AFSR register bits and to lookup and fault
260*25cf1a30Sjl  * handle associated with PBM AFAR register. Called by
261*25cf1a30Sjl  * pcmu_pbm_err_handler with pcmu_err_mutex held.
262*25cf1a30Sjl  */
263*25cf1a30Sjl int
264*25cf1a30Sjl pcmu_pbm_afsr_report(dev_info_t *dip, uint64_t fme_ena,
265*25cf1a30Sjl     pcmu_pbm_errstate_t *pbm_err_p)
266*25cf1a30Sjl {
267*25cf1a30Sjl 	int fatal = 0;
268*25cf1a30Sjl 	/* LINTED variable */
269*25cf1a30Sjl 	pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip));
270*25cf1a30Sjl 
271*25cf1a30Sjl 	ASSERT(MUTEX_HELD(&pcmu_p->pcmu_err_mutex));
272*25cf1a30Sjl 
273*25cf1a30Sjl 	pbm_err_p->pcbm_pri = PBM_PRIMARY;
274*25cf1a30Sjl 	(void) pcmu_pbm_classify(pbm_err_p);
275*25cf1a30Sjl 
276*25cf1a30Sjl 	/*
277*25cf1a30Sjl 	 * We are currently not dealing with the multiple error
278*25cf1a30Sjl 	 * case, for any secondary errors we will panic.
279*25cf1a30Sjl 	 */
280*25cf1a30Sjl 	pbm_err_p->pcbm_pri = PBM_SECONDARY;
281*25cf1a30Sjl 	if (pcmu_pbm_classify(pbm_err_p)) {
282*25cf1a30Sjl 		fatal++;
283*25cf1a30Sjl 		pcmu_pbm_ereport_post(dip, fme_ena, pbm_err_p);
284*25cf1a30Sjl 	}
285*25cf1a30Sjl 
286*25cf1a30Sjl 	if (fatal) {
287*25cf1a30Sjl 		return (DDI_FM_FATAL);
288*25cf1a30Sjl 	}
289*25cf1a30Sjl 	return (DDI_FM_NONFATAL);
290*25cf1a30Sjl }
291