xref: /illumos-gate/usr/src/uts/sun4/io/px/px_ib.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * PX Interrupt Block implementation
31*7c478bd9Sstevel@tonic-gate  */
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/async.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>		/* panicstr */
37*7c478bd9Sstevel@tonic-gate #include <sys/spl.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>	/* intr_dist_add */
40*7c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
42*7c478bd9Sstevel@tonic-gate #include "px_obj.h"
43*7c478bd9Sstevel@tonic-gate 
44*7c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate static void px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight);
47*7c478bd9Sstevel@tonic-gate static void px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
48*7c478bd9Sstevel@tonic-gate     boolean_t wait_flag);
49*7c478bd9Sstevel@tonic-gate static uint_t px_ib_intr_reset(void *arg);
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate int
52*7c478bd9Sstevel@tonic-gate px_ib_attach(px_t *px_p)
53*7c478bd9Sstevel@tonic-gate {
54*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
55*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p;
56*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
57*7c478bd9Sstevel@tonic-gate 	px_fault_t	*fault_p = &px_p->px_fault;
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_attach\n");
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(px_p->px_dip,
62*7c478bd9Sstevel@tonic-gate 	    px_p->px_inos[PX_FAULT_PEC], &sysino) != DDI_SUCCESS)
63*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate 	/*
66*7c478bd9Sstevel@tonic-gate 	 * Allocate interrupt block state structure and link it to
67*7c478bd9Sstevel@tonic-gate 	 * the px state structure.
68*7c478bd9Sstevel@tonic-gate 	 */
69*7c478bd9Sstevel@tonic-gate 	ib_p = kmem_zalloc(sizeof (px_ib_t), KM_SLEEP);
70*7c478bd9Sstevel@tonic-gate 	px_p->px_ib_p = ib_p;
71*7c478bd9Sstevel@tonic-gate 	ib_p->ib_px_p = px_p;
72*7c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = (px_ib_ino_info_t *)NULL;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL);
75*7c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL);
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate 	bus_func_register(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate 	intr_dist_add_weighted(px_ib_intr_redist, ib_p);
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate 	/*
82*7c478bd9Sstevel@tonic-gate 	 * Initialize PEC fault data structure
83*7c478bd9Sstevel@tonic-gate 	 */
84*7c478bd9Sstevel@tonic-gate 	fault_p->px_fh_dip = dip;
85*7c478bd9Sstevel@tonic-gate 	fault_p->px_fh_sysino = sysino;
86*7c478bd9Sstevel@tonic-gate 	fault_p->px_fh_lst = NULL;
87*7c478bd9Sstevel@tonic-gate 	mutex_init(&fault_p->px_fh_lock, NULL, MUTEX_DRIVER, NULL);
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate 	/* Register IMU error */
90*7c478bd9Sstevel@tonic-gate 	px_err_add_fh(fault_p, PX_ERR_IMU,
91*7c478bd9Sstevel@tonic-gate 	    (caddr_t)px_p->px_address[PX_REG_CSR]);
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
94*7c478bd9Sstevel@tonic-gate }
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate void
97*7c478bd9Sstevel@tonic-gate px_ib_detach(px_t *px_p)
98*7c478bd9Sstevel@tonic-gate {
99*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
100*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_detach\n");
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate 	px_err_rem(&px_p->px_fault, PX_FAULT_PEC);
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	bus_func_unregister(BF_TYPE_RESINTR, px_ib_intr_reset, ib_p);
107*7c478bd9Sstevel@tonic-gate 	intr_dist_rem_weighted(px_ib_intr_redist, ib_p);
108*7c478bd9Sstevel@tonic-gate 
109*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_ino_lst_mutex);
110*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_intr_lock);
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate 	px_ib_free_ino_all(ib_p);
113*7c478bd9Sstevel@tonic-gate 
114*7c478bd9Sstevel@tonic-gate 	px_p->px_ib_p = NULL;
115*7c478bd9Sstevel@tonic-gate 	kmem_free(ib_p, sizeof (px_ib_t));
116*7c478bd9Sstevel@tonic-gate }
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate static struct {
119*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_name;
120*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_type;
121*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_cpu;
122*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_pil;
123*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_time;
124*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_ino;
125*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_cookie;
126*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_devpath;
127*7c478bd9Sstevel@tonic-gate 	kstat_named_t ihks_buspath;
128*7c478bd9Sstevel@tonic-gate } px_ih_ks_template = {
129*7c478bd9Sstevel@tonic-gate 	{ "name",	KSTAT_DATA_CHAR },
130*7c478bd9Sstevel@tonic-gate 	{ "type",	KSTAT_DATA_CHAR },
131*7c478bd9Sstevel@tonic-gate 	{ "cpu",	KSTAT_DATA_UINT64 },
132*7c478bd9Sstevel@tonic-gate 	{ "pil",	KSTAT_DATA_UINT64 },
133*7c478bd9Sstevel@tonic-gate 	{ "time",	KSTAT_DATA_UINT64 },
134*7c478bd9Sstevel@tonic-gate 	{ "ino",	KSTAT_DATA_UINT64 },
135*7c478bd9Sstevel@tonic-gate 	{ "cookie",	KSTAT_DATA_UINT64 },
136*7c478bd9Sstevel@tonic-gate 	{ "devpath",	KSTAT_DATA_STRING },
137*7c478bd9Sstevel@tonic-gate 	{ "buspath",	KSTAT_DATA_STRING },
138*7c478bd9Sstevel@tonic-gate };
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate static uint32_t ih_instance;
141*7c478bd9Sstevel@tonic-gate static kmutex_t ih_ks_template_lock;
142*7c478bd9Sstevel@tonic-gate 
143*7c478bd9Sstevel@tonic-gate int
144*7c478bd9Sstevel@tonic-gate ih_ks_update(kstat_t *ksp, int rw)
145*7c478bd9Sstevel@tonic-gate {
146*7c478bd9Sstevel@tonic-gate 	px_ih_t *ih_p = ksp->ks_private;
147*7c478bd9Sstevel@tonic-gate 	int maxlen = sizeof (px_ih_ks_template.ihks_name.value.c);
148*7c478bd9Sstevel@tonic-gate 	px_ib_t *ib_p = ih_p->ih_ino_p->ino_ib_p;
149*7c478bd9Sstevel@tonic-gate 	px_t *px_p = ib_p->ib_px_p;
150*7c478bd9Sstevel@tonic-gate 	devino_t ino;
151*7c478bd9Sstevel@tonic-gate 	sysino_t sysino;
152*7c478bd9Sstevel@tonic-gate 	char ih_devpath[MAXPATHLEN];
153*7c478bd9Sstevel@tonic-gate 	char ih_buspath[MAXPATHLEN];
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate 	ino = ih_p->ih_ino_p->ino_ino;
156*7c478bd9Sstevel@tonic-gate 	(void) px_lib_intr_devino_to_sysino(px_p->px_dip, ino, &sysino);
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate 	(void) snprintf(px_ih_ks_template.ihks_name.value.c, maxlen, "%s%d",
159*7c478bd9Sstevel@tonic-gate 	    ddi_driver_name(ih_p->ih_dip),
160*7c478bd9Sstevel@tonic-gate 	    ddi_get_instance(ih_p->ih_dip));
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate 	(void) strcpy(px_ih_ks_template.ihks_type.value.c,
163*7c478bd9Sstevel@tonic-gate 	    (ih_p->ih_rec_type == 0) ? "fixed" : "msi");
164*7c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_cpu.value.ui64 = ih_p->ih_ino_p->ino_cpuid;
165*7c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_pil.value.ui64 = ih_p->ih_ino_p->ino_pil;
166*7c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_time.value.ui64 = ih_p->ih_nsec + (uint64_t)
167*7c478bd9Sstevel@tonic-gate 	    tick2ns((hrtime_t)ih_p->ih_ticks, ih_p->ih_ino_p->ino_cpuid);
168*7c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_ino.value.ui64 = ino;
169*7c478bd9Sstevel@tonic-gate 	px_ih_ks_template.ihks_cookie.value.ui64 = sysino;
170*7c478bd9Sstevel@tonic-gate 
171*7c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(ih_p->ih_dip, ih_devpath);
172*7c478bd9Sstevel@tonic-gate 	(void) ddi_pathname(px_p->px_dip, ih_buspath);
173*7c478bd9Sstevel@tonic-gate 	kstat_named_setstr(&px_ih_ks_template.ihks_devpath, ih_devpath);
174*7c478bd9Sstevel@tonic-gate 	kstat_named_setstr(&px_ih_ks_template.ihks_buspath, ih_buspath);
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate 	return (0);
177*7c478bd9Sstevel@tonic-gate }
178*7c478bd9Sstevel@tonic-gate 
179*7c478bd9Sstevel@tonic-gate void
180*7c478bd9Sstevel@tonic-gate px_ib_intr_enable(px_t *px_p, cpuid_t cpu_id, devino_t ino)
181*7c478bd9Sstevel@tonic-gate {
182*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
183*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 	/*
186*7c478bd9Sstevel@tonic-gate 	 * Determine the cpu for the interrupt
187*7c478bd9Sstevel@tonic-gate 	 */
188*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip,
191*7c478bd9Sstevel@tonic-gate 	    "px_ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id);
192*7c478bd9Sstevel@tonic-gate 
193*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(px_p->px_dip, ino,
194*7c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS) {
195*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, px_p->px_dip,
196*7c478bd9Sstevel@tonic-gate 		    "px_ib_intr_enable: px_intr_devino_to_sysino() failed\n");
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
199*7c478bd9Sstevel@tonic-gate 		return;
200*7c478bd9Sstevel@tonic-gate 	}
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(px_p->px_dip, sysino, cpu_id);
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
205*7c478bd9Sstevel@tonic-gate }
206*7c478bd9Sstevel@tonic-gate 
207*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
208*7c478bd9Sstevel@tonic-gate void
209*7c478bd9Sstevel@tonic-gate px_ib_intr_disable(px_ib_t *ib_p, devino_t ino, int wait)
210*7c478bd9Sstevel@tonic-gate {
211*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_disable: ino=%x\n", ino);
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
218*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino,
219*7c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS) {
220*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, ib_p->ib_px_p->px_dip,
221*7c478bd9Sstevel@tonic-gate 		    "px_ib_intr_disable: px_intr_devino_to_sysino() failed\n");
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
224*7c478bd9Sstevel@tonic-gate 		return;
225*7c478bd9Sstevel@tonic-gate 	}
226*7c478bd9Sstevel@tonic-gate 
227*7c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(ib_p->ib_px_p->px_dip, sysino);
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
230*7c478bd9Sstevel@tonic-gate }
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate static void
234*7c478bd9Sstevel@tonic-gate px_ib_intr_dist_en(dev_info_t *dip, cpuid_t cpu_id, devino_t ino,
235*7c478bd9Sstevel@tonic-gate     boolean_t wait_flag)
236*7c478bd9Sstevel@tonic-gate {
237*7c478bd9Sstevel@tonic-gate 	uint32_t	old_cpu_id;
238*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
239*7c478bd9Sstevel@tonic-gate 	intr_valid_state_t	enabled = 0;
240*7c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
241*7c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
242*7c478bd9Sstevel@tonic-gate 	int		e;
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_intr_dist_en: ino=0x%x\n", ino);
245*7c478bd9Sstevel@tonic-gate 
246*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(dip, ino, &sysino) != DDI_SUCCESS) {
247*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
248*7c478bd9Sstevel@tonic-gate 		    "px_intr_devino_to_sysino() failed, ino 0x%x\n", ino);
249*7c478bd9Sstevel@tonic-gate 		return;
250*7c478bd9Sstevel@tonic-gate 	}
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	/* Skip enabling disabled interrupts */
253*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_getvalid(dip, sysino, &enabled) != DDI_SUCCESS) {
254*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: px_intr_getvalid() "
255*7c478bd9Sstevel@tonic-gate 		    "failed, sysino 0x%x\n", sysino);
256*7c478bd9Sstevel@tonic-gate 		return;
257*7c478bd9Sstevel@tonic-gate 	}
258*7c478bd9Sstevel@tonic-gate 	if (!enabled)
259*7c478bd9Sstevel@tonic-gate 		return;
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 	/* Done if redistributed onto the same cpuid */
262*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_gettarget(dip, sysino, &old_cpu_id) != DDI_SUCCESS) {
263*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: "
264*7c478bd9Sstevel@tonic-gate 		    "px_intr_gettarget() failed\n");
265*7c478bd9Sstevel@tonic-gate 		return;
266*7c478bd9Sstevel@tonic-gate 	}
267*7c478bd9Sstevel@tonic-gate 	if (cpu_id == old_cpu_id)
268*7c478bd9Sstevel@tonic-gate 		return;
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	if (!wait_flag)
271*7c478bd9Sstevel@tonic-gate 		goto done;
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupts */
274*7c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(dip, sysino);
275*7c478bd9Sstevel@tonic-gate 
276*7c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
277*7c478bd9Sstevel@tonic-gate 	    ((e = px_lib_intr_getstate(dip, sysino, &intr_state)) ==
278*7c478bd9Sstevel@tonic-gate 		DDI_SUCCESS) &&
279*7c478bd9Sstevel@tonic-gate 	    (intr_state == INTR_DELIVERED_STATE); /* */) {
280*7c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
281*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
282*7c478bd9Sstevel@tonic-gate 			    "%s%d: px_ib_intr_dist_en: sysino 0x%x(ino 0x%x) "
283*7c478bd9Sstevel@tonic-gate 			    "from cpu id 0x%x to 0x%x timeout",
284*7c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
285*7c478bd9Sstevel@tonic-gate 			    sysino, ino, old_cpu_id, cpu_id);
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 			e = DDI_FAILURE;
288*7c478bd9Sstevel@tonic-gate 			break;
289*7c478bd9Sstevel@tonic-gate 		}
290*7c478bd9Sstevel@tonic-gate 	}
291*7c478bd9Sstevel@tonic-gate 
292*7c478bd9Sstevel@tonic-gate 	if (e != DDI_SUCCESS)
293*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_intr_dist_en: failed, "
294*7c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate done:
297*7c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(dip, sysino, cpu_id);
298*7c478bd9Sstevel@tonic-gate }
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate /*
302*7c478bd9Sstevel@tonic-gate  * Redistribute interrupts of the specified weight. The first call has a weight
303*7c478bd9Sstevel@tonic-gate  * of weight_max, which can be used to trigger initialization for
304*7c478bd9Sstevel@tonic-gate  * redistribution. The inos with weight [weight_max, inf.) should be processed
305*7c478bd9Sstevel@tonic-gate  * on the "weight == weight_max" call.  This first call is followed by calls
306*7c478bd9Sstevel@tonic-gate  * of decreasing weights, inos of that weight should be processed.  The final
307*7c478bd9Sstevel@tonic-gate  * call specifies a weight of zero, this can be used to trigger processing of
308*7c478bd9Sstevel@tonic-gate  * stragglers.
309*7c478bd9Sstevel@tonic-gate  */
310*7c478bd9Sstevel@tonic-gate static void
311*7c478bd9Sstevel@tonic-gate px_ib_intr_redist(void *arg, int32_t weight_max, int32_t weight)
312*7c478bd9Sstevel@tonic-gate {
313*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = (px_ib_t *)arg;
314*7c478bd9Sstevel@tonic-gate 	px_t		*px_p = ib_p->ib_px_p;
315*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
316*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t *ino_p;
317*7c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_lst;
318*7c478bd9Sstevel@tonic-gate 	int32_t		dweight = 0;
319*7c478bd9Sstevel@tonic-gate 	int		i;
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* Redistribute internal interrupts */
322*7c478bd9Sstevel@tonic-gate 	if (weight == 0) {
323*7c478bd9Sstevel@tonic-gate 		devino_t	ino_pec = px_p->px_inos[PX_INTR_PEC];
324*7c478bd9Sstevel@tonic-gate 		mutex_enter(&ib_p->ib_intr_lock);
325*7c478bd9Sstevel@tonic-gate 		px_ib_intr_dist_en(dip, intr_dist_cpuid(), ino_pec, B_FALSE);
326*7c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
327*7c478bd9Sstevel@tonic-gate 	}
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate 	/* Redistribute device interrupts */
330*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next) {
333*7c478bd9Sstevel@tonic-gate 		uint32_t orig_cpuid;
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate 		/*
336*7c478bd9Sstevel@tonic-gate 		 * Recomputes the sum of interrupt weights of devices that
337*7c478bd9Sstevel@tonic-gate 		 * share the same ino upon first call marked by
338*7c478bd9Sstevel@tonic-gate 		 * (weight == weight_max).
339*7c478bd9Sstevel@tonic-gate 		 */
340*7c478bd9Sstevel@tonic-gate 		if (weight == weight_max) {
341*7c478bd9Sstevel@tonic-gate 			ino_p->ino_intr_weight = 0;
342*7c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
343*7c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
344*7c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
345*7c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
346*7c478bd9Sstevel@tonic-gate 				if (dweight > 0)
347*7c478bd9Sstevel@tonic-gate 					ino_p->ino_intr_weight += dweight;
348*7c478bd9Sstevel@tonic-gate 			}
349*7c478bd9Sstevel@tonic-gate 		}
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 		/*
352*7c478bd9Sstevel@tonic-gate 		 * As part of redistributing weighted interrupts over cpus,
353*7c478bd9Sstevel@tonic-gate 		 * nexus redistributes device interrupts and updates
354*7c478bd9Sstevel@tonic-gate 		 * cpu weight. The purpose is for the most light weighted
355*7c478bd9Sstevel@tonic-gate 		 * cpu to take the next interrupt and gain weight, therefore
356*7c478bd9Sstevel@tonic-gate 		 * attention demanding device gains more cpu attention by
357*7c478bd9Sstevel@tonic-gate 		 * making itself heavy.
358*7c478bd9Sstevel@tonic-gate 		 */
359*7c478bd9Sstevel@tonic-gate 		if ((weight == ino_p->ino_intr_weight) ||
360*7c478bd9Sstevel@tonic-gate 		    ((weight >= weight_max) &&
361*7c478bd9Sstevel@tonic-gate 		    (ino_p->ino_intr_weight >= weight_max))) {
362*7c478bd9Sstevel@tonic-gate 			orig_cpuid = ino_p->ino_cpuid;
363*7c478bd9Sstevel@tonic-gate 			if (cpu[orig_cpuid] == NULL)
364*7c478bd9Sstevel@tonic-gate 				orig_cpuid = CPU->cpu_id;
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 			/* select cpuid to target and mark ino established */
367*7c478bd9Sstevel@tonic-gate 			ino_p->ino_cpuid = intr_dist_cpuid();
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 			/* Add device weight to targeted cpu. */
370*7c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
371*7c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
372*7c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
373*7c478bd9Sstevel@tonic-gate 				hrtime_t ticks;
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
376*7c478bd9Sstevel@tonic-gate 				intr_dist_cpuid_add_device_weight(
377*7c478bd9Sstevel@tonic-gate 				    ino_p->ino_cpuid, ih_lst->ih_dip, dweight);
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 				/*
380*7c478bd9Sstevel@tonic-gate 				 * different cpus may have different clock
381*7c478bd9Sstevel@tonic-gate 				 * speeds. to account for this, whenever an
382*7c478bd9Sstevel@tonic-gate 				 * interrupt is moved to a new CPU, we
383*7c478bd9Sstevel@tonic-gate 				 * convert the accumulated ticks into nsec,
384*7c478bd9Sstevel@tonic-gate 				 * based upon the clock rate of the prior
385*7c478bd9Sstevel@tonic-gate 				 * CPU.
386*7c478bd9Sstevel@tonic-gate 				 *
387*7c478bd9Sstevel@tonic-gate 				 * It is possible that the prior CPU no longer
388*7c478bd9Sstevel@tonic-gate 				 * exists. In this case, fall back to using
389*7c478bd9Sstevel@tonic-gate 				 * this CPU's clock rate.
390*7c478bd9Sstevel@tonic-gate 				 *
391*7c478bd9Sstevel@tonic-gate 				 * Note that the value in ih_ticks has already
392*7c478bd9Sstevel@tonic-gate 				 * been corrected for any power savings mode
393*7c478bd9Sstevel@tonic-gate 				 * which might have been in effect.
394*7c478bd9Sstevel@tonic-gate 				 *
395*7c478bd9Sstevel@tonic-gate 				 * because we are updating two fields in
396*7c478bd9Sstevel@tonic-gate 				 * ih_t we must lock ih_ks_template_lock to
397*7c478bd9Sstevel@tonic-gate 				 * prevent someone from reading the kstats
398*7c478bd9Sstevel@tonic-gate 				 * after we set ih_ticks to 0 and before we
399*7c478bd9Sstevel@tonic-gate 				 * increment ih_nsec to compensate.
400*7c478bd9Sstevel@tonic-gate 				 *
401*7c478bd9Sstevel@tonic-gate 				 * we must also protect against the interrupt
402*7c478bd9Sstevel@tonic-gate 				 * arriving and incrementing ih_ticks between
403*7c478bd9Sstevel@tonic-gate 				 * the time we read it and when we reset it
404*7c478bd9Sstevel@tonic-gate 				 * to 0. To do this we use atomic_swap.
405*7c478bd9Sstevel@tonic-gate 				 */
406*7c478bd9Sstevel@tonic-gate 
407*7c478bd9Sstevel@tonic-gate 				mutex_enter(&ih_ks_template_lock);
408*7c478bd9Sstevel@tonic-gate 				ticks = atomic_swap_64(&ih_lst->ih_ticks, 0);
409*7c478bd9Sstevel@tonic-gate 				ih_lst->ih_nsec += (uint64_t)
410*7c478bd9Sstevel@tonic-gate 				    tick2ns(ticks, orig_cpuid);
411*7c478bd9Sstevel@tonic-gate 				mutex_exit(&ih_ks_template_lock);
412*7c478bd9Sstevel@tonic-gate 			}
413*7c478bd9Sstevel@tonic-gate 
414*7c478bd9Sstevel@tonic-gate 			/* enable interrupt on new targeted cpu */
415*7c478bd9Sstevel@tonic-gate 			px_ib_intr_dist_en(dip, ino_p->ino_cpuid,
416*7c478bd9Sstevel@tonic-gate 			    ino_p->ino_ino, B_TRUE);
417*7c478bd9Sstevel@tonic-gate 		}
418*7c478bd9Sstevel@tonic-gate 	}
419*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
420*7c478bd9Sstevel@tonic-gate }
421*7c478bd9Sstevel@tonic-gate 
422*7c478bd9Sstevel@tonic-gate /*
423*7c478bd9Sstevel@tonic-gate  * Reset interrupts to IDLE.  This function is called during
424*7c478bd9Sstevel@tonic-gate  * panic handling after redistributing interrupts; it's needed to
425*7c478bd9Sstevel@tonic-gate  * support dumping to network devices after 'sync' from OBP.
426*7c478bd9Sstevel@tonic-gate  *
427*7c478bd9Sstevel@tonic-gate  * N.B.  This routine runs in a context where all other threads
428*7c478bd9Sstevel@tonic-gate  * are permanently suspended.
429*7c478bd9Sstevel@tonic-gate  */
430*7c478bd9Sstevel@tonic-gate static uint_t
431*7c478bd9Sstevel@tonic-gate px_ib_intr_reset(void *arg)
432*7c478bd9Sstevel@tonic-gate {
433*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = (px_ib_t *)arg;
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, ib_p->ib_px_p->px_dip, "px_ib_intr_reset\n");
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_reset(ib_p->ib_px_p->px_dip) != DDI_SUCCESS)
438*7c478bd9Sstevel@tonic-gate 		return (BF_FATAL);
439*7c478bd9Sstevel@tonic-gate 
440*7c478bd9Sstevel@tonic-gate 	return (BF_NONE);
441*7c478bd9Sstevel@tonic-gate }
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate /*
444*7c478bd9Sstevel@tonic-gate  * Locate ino_info structure on ib_p->ib_ino_lst according to ino#
445*7c478bd9Sstevel@tonic-gate  * returns NULL if not found.
446*7c478bd9Sstevel@tonic-gate  */
447*7c478bd9Sstevel@tonic-gate px_ib_ino_info_t *
448*7c478bd9Sstevel@tonic-gate px_ib_locate_ino(px_ib_t *ib_p, devino_t ino_num)
449*7c478bd9Sstevel@tonic-gate {
450*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*ino_p = ib_p->ib_ino_lst;
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
453*7c478bd9Sstevel@tonic-gate 
454*7c478bd9Sstevel@tonic-gate 	for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next);
455*7c478bd9Sstevel@tonic-gate 
456*7c478bd9Sstevel@tonic-gate 	return (ino_p);
457*7c478bd9Sstevel@tonic-gate }
458*7c478bd9Sstevel@tonic-gate 
459*7c478bd9Sstevel@tonic-gate px_ib_ino_info_t *
460*7c478bd9Sstevel@tonic-gate px_ib_new_ino(px_ib_t *ib_p, devino_t ino_num, px_ih_t *ih_p)
461*7c478bd9Sstevel@tonic-gate {
462*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*ino_p = kmem_alloc(sizeof (px_ib_ino_info_t),
463*7c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
464*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino;
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ino = ino_num;
467*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ib_p = ib_p;
468*7c478bd9Sstevel@tonic-gate 	ino_p->ino_unclaimed = 0;
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate 	if (px_lib_intr_devino_to_sysino(ib_p->ib_px_p->px_dip, ino_p->ino_ino,
471*7c478bd9Sstevel@tonic-gate 	    &sysino) != DDI_SUCCESS)
472*7c478bd9Sstevel@tonic-gate 		return (NULL);
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 	ino_p->ino_sysino = sysino;
475*7c478bd9Sstevel@tonic-gate 
476*7c478bd9Sstevel@tonic-gate 	/*
477*7c478bd9Sstevel@tonic-gate 	 * Cannot disable interrupt since we might share slot
478*7c478bd9Sstevel@tonic-gate 	 */
479*7c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ih_p;
480*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_head = ih_p;
481*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
482*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ih_p;
483*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size = 1;
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 	ino_p->ino_next = ib_p->ib_ino_lst;
486*7c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = ino_p;
487*7c478bd9Sstevel@tonic-gate 
488*7c478bd9Sstevel@tonic-gate 	return (ino_p);
489*7c478bd9Sstevel@tonic-gate }
490*7c478bd9Sstevel@tonic-gate 
491*7c478bd9Sstevel@tonic-gate /*
492*7c478bd9Sstevel@tonic-gate  * The ino_p is retrieved by previous call to px_ib_locate_ino().
493*7c478bd9Sstevel@tonic-gate  */
494*7c478bd9Sstevel@tonic-gate void
495*7c478bd9Sstevel@tonic-gate px_ib_delete_ino(px_ib_t *ib_p, px_ib_ino_info_t *ino_p)
496*7c478bd9Sstevel@tonic-gate {
497*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*list = ib_p->ib_ino_lst;
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 	if (list == ino_p)
502*7c478bd9Sstevel@tonic-gate 		ib_p->ib_ino_lst = list->ino_next;
503*7c478bd9Sstevel@tonic-gate 	else {
504*7c478bd9Sstevel@tonic-gate 		for (; list->ino_next != ino_p; list = list->ino_next);
505*7c478bd9Sstevel@tonic-gate 		list->ino_next = ino_p->ino_next;
506*7c478bd9Sstevel@tonic-gate 	}
507*7c478bd9Sstevel@tonic-gate }
508*7c478bd9Sstevel@tonic-gate 
509*7c478bd9Sstevel@tonic-gate /*
510*7c478bd9Sstevel@tonic-gate  * Free all ino when we are detaching.
511*7c478bd9Sstevel@tonic-gate  */
512*7c478bd9Sstevel@tonic-gate void
513*7c478bd9Sstevel@tonic-gate px_ib_free_ino_all(px_ib_t *ib_p)
514*7c478bd9Sstevel@tonic-gate {
515*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*tmp = ib_p->ib_ino_lst;
516*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t	*next = NULL;
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate 	while (tmp) {
519*7c478bd9Sstevel@tonic-gate 		next = tmp->ino_next;
520*7c478bd9Sstevel@tonic-gate 		kmem_free(tmp, sizeof (px_ib_ino_info_t));
521*7c478bd9Sstevel@tonic-gate 		tmp = next;
522*7c478bd9Sstevel@tonic-gate 	}
523*7c478bd9Sstevel@tonic-gate }
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate int
526*7c478bd9Sstevel@tonic-gate px_ib_ino_add_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
527*7c478bd9Sstevel@tonic-gate {
528*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = ino_p->ino_ib_p;
529*7c478bd9Sstevel@tonic-gate 	devino_t	ino = ino_p->ino_ino;
530*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino = ino_p->ino_sysino;
531*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
532*7c478bd9Sstevel@tonic-gate 	cpuid_t		curr_cpu;
533*7c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
534*7c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
535*7c478bd9Sstevel@tonic-gate 	int		ret = DDI_SUCCESS;
536*7c478bd9Sstevel@tonic-gate 
537*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
538*7c478bd9Sstevel@tonic-gate 	ASSERT(ib_p == px_p->px_ib_p);
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, dip, "px_ib_ino_add_intr ino=%x\n", ino_p->ino_ino);
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
543*7c478bd9Sstevel@tonic-gate 	if ((ret = px_lib_intr_gettarget(dip, sysino,
544*7c478bd9Sstevel@tonic-gate 	    &curr_cpu)) != DDI_SUCCESS) {
545*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip,
546*7c478bd9Sstevel@tonic-gate 		    "px_ib_ino_add_intr px_intr_gettarget() failed\n");
547*7c478bd9Sstevel@tonic-gate 
548*7c478bd9Sstevel@tonic-gate 		return (ret);
549*7c478bd9Sstevel@tonic-gate 	}
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(dip, sysino);
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupt */
554*7c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
555*7c478bd9Sstevel@tonic-gate 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
556*7c478bd9Sstevel@tonic-gate 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
557*7c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
558*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: px_ib_ino_add_intr: pending "
559*7c478bd9Sstevel@tonic-gate 			    "sysino 0x%x(ino 0x%x) timeout",
560*7c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
561*7c478bd9Sstevel@tonic-gate 			    sysino, ino);
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
564*7c478bd9Sstevel@tonic-gate 			break;
565*7c478bd9Sstevel@tonic-gate 		}
566*7c478bd9Sstevel@tonic-gate 	}
567*7c478bd9Sstevel@tonic-gate 
568*7c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
569*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_ino_add_intr: failed, "
570*7c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
571*7c478bd9Sstevel@tonic-gate 
572*7c478bd9Sstevel@tonic-gate 		return (ret);
573*7c478bd9Sstevel@tonic-gate 	}
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 	/* Link up px_ispec_t portion of the ppd */
576*7c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ino_p->ino_ih_head;
577*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail->ih_next = ih_p;
578*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
579*7c478bd9Sstevel@tonic-gate 
580*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
581*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size++;
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 	/*
584*7c478bd9Sstevel@tonic-gate 	 * If the interrupt was previously blocked (left in pending state)
585*7c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
586*7c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
587*7c478bd9Sstevel@tonic-gate 	 */
588*7c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
589*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
590*7c478bd9Sstevel@tonic-gate 		    "%s%d: px_ib_ino_add_intr: ino 0x%x has been unblocked",
591*7c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
594*7c478bd9Sstevel@tonic-gate 		if ((ret = px_lib_intr_setstate(dip, sysino,
595*7c478bd9Sstevel@tonic-gate 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
596*7c478bd9Sstevel@tonic-gate 			DBG(DBG_IB, px_p->px_dip,
597*7c478bd9Sstevel@tonic-gate 			    "px_ib_ino_add_intr px_intr_setstate failed\n");
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate 			return (ret);
600*7c478bd9Sstevel@tonic-gate 		}
601*7c478bd9Sstevel@tonic-gate 	}
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	/* Re-enable interrupt */
604*7c478bd9Sstevel@tonic-gate 	PX_INTR_ENABLE(dip, sysino, curr_cpu);
605*7c478bd9Sstevel@tonic-gate 
606*7c478bd9Sstevel@tonic-gate 	return (ret);
607*7c478bd9Sstevel@tonic-gate }
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate /*
610*7c478bd9Sstevel@tonic-gate  * Removes px_ispec_t from the ino's link list.
611*7c478bd9Sstevel@tonic-gate  * uses hardware mutex to lock out interrupt threads.
612*7c478bd9Sstevel@tonic-gate  * Side effects: interrupt belongs to that ino is turned off on return.
613*7c478bd9Sstevel@tonic-gate  * if we are sharing PX slot with other inos, the caller needs
614*7c478bd9Sstevel@tonic-gate  * to turn it back on.
615*7c478bd9Sstevel@tonic-gate  */
616*7c478bd9Sstevel@tonic-gate int
617*7c478bd9Sstevel@tonic-gate px_ib_ino_rem_intr(px_t *px_p, px_ib_ino_info_t *ino_p, px_ih_t *ih_p)
618*7c478bd9Sstevel@tonic-gate {
619*7c478bd9Sstevel@tonic-gate 	devino_t	ino = ino_p->ino_ino;
620*7c478bd9Sstevel@tonic-gate 	sysino_t	sysino = ino_p->ino_sysino;
621*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = px_p->px_dip;
622*7c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_lst = ino_p->ino_ih_head;
623*7c478bd9Sstevel@tonic-gate 	hrtime_t	start_time;
624*7c478bd9Sstevel@tonic-gate 	intr_state_t	intr_state;
625*7c478bd9Sstevel@tonic-gate 	int		i, ret = DDI_SUCCESS;
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex));
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip, "px_ib_ino_rem_intr ino=%x\n",
630*7c478bd9Sstevel@tonic-gate 	    ino_p->ino_ino);
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 	/* Disable the interrupt */
633*7c478bd9Sstevel@tonic-gate 	PX_INTR_DISABLE(px_p->px_dip, sysino);
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_size == 1) {
636*7c478bd9Sstevel@tonic-gate 		if (ih_lst != ih_p)
637*7c478bd9Sstevel@tonic-gate 			goto not_found;
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 		/* No need to set head/tail as ino_p will be freed */
640*7c478bd9Sstevel@tonic-gate 		goto reset;
641*7c478bd9Sstevel@tonic-gate 	}
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate 	/* Busy wait on pending interrupt */
644*7c478bd9Sstevel@tonic-gate 	for (start_time = gethrtime(); !panicstr &&
645*7c478bd9Sstevel@tonic-gate 	    ((ret = px_lib_intr_getstate(dip, sysino, &intr_state))
646*7c478bd9Sstevel@tonic-gate 	    == DDI_SUCCESS) && (intr_state == INTR_DELIVERED_STATE); /* */) {
647*7c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > px_intrpend_timeout) {
648*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: pending "
649*7c478bd9Sstevel@tonic-gate 			    "sysino 0x%x(ino 0x%x) timeout",
650*7c478bd9Sstevel@tonic-gate 			    ddi_driver_name(dip), ddi_get_instance(dip),
651*7c478bd9Sstevel@tonic-gate 			    sysino, ino);
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 			ret = DDI_FAILURE;
654*7c478bd9Sstevel@tonic-gate 			break;
655*7c478bd9Sstevel@tonic-gate 		}
656*7c478bd9Sstevel@tonic-gate 	}
657*7c478bd9Sstevel@tonic-gate 
658*7c478bd9Sstevel@tonic-gate 	if (ret != DDI_SUCCESS) {
659*7c478bd9Sstevel@tonic-gate 		DBG(DBG_IB, dip, "px_ib_ino_rem_intr: failed, "
660*7c478bd9Sstevel@tonic-gate 		    "ino 0x%x sysino 0x%x\n", ino, sysino);
661*7c478bd9Sstevel@tonic-gate 
662*7c478bd9Sstevel@tonic-gate 		return (ret);
663*7c478bd9Sstevel@tonic-gate 	}
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	/*
666*7c478bd9Sstevel@tonic-gate 	 * If the interrupt was previously blocked (left in pending state)
667*7c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
668*7c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
669*7c478bd9Sstevel@tonic-gate 	 */
670*7c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > px_unclaimed_intr_max) {
671*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "%s%d: px_ib_ino_rem_intr: "
672*7c478bd9Sstevel@tonic-gate 		    "ino 0x%x has been unblocked",
673*7c478bd9Sstevel@tonic-gate 		    ddi_driver_name(dip), ddi_get_instance(dip), ino);
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
676*7c478bd9Sstevel@tonic-gate 		if ((ret = px_lib_intr_setstate(dip, sysino,
677*7c478bd9Sstevel@tonic-gate 		    INTR_IDLE_STATE)) != DDI_SUCCESS) {
678*7c478bd9Sstevel@tonic-gate 			DBG(DBG_IB, px_p->px_dip,
679*7c478bd9Sstevel@tonic-gate 			    "px_ib_ino_rem_intr px_intr_setstate failed\n");
680*7c478bd9Sstevel@tonic-gate 
681*7c478bd9Sstevel@tonic-gate 			return (ret);
682*7c478bd9Sstevel@tonic-gate 		}
683*7c478bd9Sstevel@tonic-gate 	}
684*7c478bd9Sstevel@tonic-gate 
685*7c478bd9Sstevel@tonic-gate 	/* Search the link list for ih_p */
686*7c478bd9Sstevel@tonic-gate 	for (i = 0; (i < ino_p->ino_ih_size) &&
687*7c478bd9Sstevel@tonic-gate 	    (ih_lst->ih_next != ih_p); i++, ih_lst = ih_lst->ih_next);
688*7c478bd9Sstevel@tonic-gate 
689*7c478bd9Sstevel@tonic-gate 	if (ih_lst->ih_next != ih_p)
690*7c478bd9Sstevel@tonic-gate 		goto not_found;
691*7c478bd9Sstevel@tonic-gate 
692*7c478bd9Sstevel@tonic-gate 	/* Remove ih_p from the link list and maintain the head/tail */
693*7c478bd9Sstevel@tonic-gate 	ih_lst->ih_next = ih_p->ih_next;
694*7c478bd9Sstevel@tonic-gate 
695*7c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_head == ih_p)
696*7c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_head = ih_p->ih_next;
697*7c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_tail == ih_p)
698*7c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_tail = ih_lst;
699*7c478bd9Sstevel@tonic-gate 
700*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
701*7c478bd9Sstevel@tonic-gate 
702*7c478bd9Sstevel@tonic-gate reset:
703*7c478bd9Sstevel@tonic-gate 	if (ih_p->ih_config_handle)
704*7c478bd9Sstevel@tonic-gate 		pci_config_teardown(&ih_p->ih_config_handle);
705*7c478bd9Sstevel@tonic-gate 	if (ih_p->ih_ksp != NULL)
706*7c478bd9Sstevel@tonic-gate 		kstat_delete(ih_p->ih_ksp);
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate 	kmem_free(ih_p, sizeof (px_ih_t));
709*7c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size--;
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 	return (ret);
712*7c478bd9Sstevel@tonic-gate 
713*7c478bd9Sstevel@tonic-gate not_found:
714*7c478bd9Sstevel@tonic-gate 	DBG(DBG_R_INTX, ino_p->ino_ib_p->ib_px_p->px_dip,
715*7c478bd9Sstevel@tonic-gate 		"ino_p=%x does not have ih_p=%x\n", ino_p, ih_p);
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
718*7c478bd9Sstevel@tonic-gate }
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate px_ih_t *
721*7c478bd9Sstevel@tonic-gate px_ib_ino_locate_intr(px_ib_ino_info_t *ino_p, dev_info_t *rdip,
722*7c478bd9Sstevel@tonic-gate     uint32_t inum, msiq_rec_type_t rec_type, msgcode_t msg_code)
723*7c478bd9Sstevel@tonic-gate {
724*7c478bd9Sstevel@tonic-gate 	px_ih_t	*ih_lst = ino_p->ino_ih_head;
725*7c478bd9Sstevel@tonic-gate 	int	i;
726*7c478bd9Sstevel@tonic-gate 
727*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < ino_p->ino_ih_size; i++, ih_lst = ih_lst->ih_next) {
728*7c478bd9Sstevel@tonic-gate 		if ((ih_lst->ih_dip == rdip) && (ih_lst->ih_inum == inum) &&
729*7c478bd9Sstevel@tonic-gate 		    (ih_lst->ih_rec_type == rec_type) &&
730*7c478bd9Sstevel@tonic-gate 		    (ih_lst->ih_msg_code == msg_code))
731*7c478bd9Sstevel@tonic-gate 			return (ih_lst);
732*7c478bd9Sstevel@tonic-gate 	}
733*7c478bd9Sstevel@tonic-gate 
734*7c478bd9Sstevel@tonic-gate 	return ((px_ih_t *)NULL);
735*7c478bd9Sstevel@tonic-gate }
736*7c478bd9Sstevel@tonic-gate 
737*7c478bd9Sstevel@tonic-gate px_ih_t *
738*7c478bd9Sstevel@tonic-gate px_ib_alloc_ih(dev_info_t *rdip, uint32_t inum,
739*7c478bd9Sstevel@tonic-gate     uint_t (*int_handler)(caddr_t int_handler_arg1, caddr_t int_handler_arg2),
740*7c478bd9Sstevel@tonic-gate     caddr_t int_handler_arg1, caddr_t int_handler_arg2,
741*7c478bd9Sstevel@tonic-gate     msiq_rec_type_t rec_type, msgcode_t msg_code)
742*7c478bd9Sstevel@tonic-gate {
743*7c478bd9Sstevel@tonic-gate 	px_ih_t	*ih_p;
744*7c478bd9Sstevel@tonic-gate 
745*7c478bd9Sstevel@tonic-gate 	ih_p = kmem_alloc(sizeof (px_ih_t), KM_SLEEP);
746*7c478bd9Sstevel@tonic-gate 	ih_p->ih_dip = rdip;
747*7c478bd9Sstevel@tonic-gate 	ih_p->ih_inum = inum;
748*7c478bd9Sstevel@tonic-gate 	ih_p->ih_intr_state = PX_INTR_STATE_DISABLE;
749*7c478bd9Sstevel@tonic-gate 	ih_p->ih_handler = int_handler;
750*7c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg1 = int_handler_arg1;
751*7c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg2 = int_handler_arg2;
752*7c478bd9Sstevel@tonic-gate 	ih_p->ih_config_handle = NULL;
753*7c478bd9Sstevel@tonic-gate 	ih_p->ih_rec_type = rec_type;
754*7c478bd9Sstevel@tonic-gate 	ih_p->ih_msg_code = msg_code;
755*7c478bd9Sstevel@tonic-gate 	ih_p->ih_nsec = 0;
756*7c478bd9Sstevel@tonic-gate 	ih_p->ih_ticks = 0;
757*7c478bd9Sstevel@tonic-gate 
758*7c478bd9Sstevel@tonic-gate 	/*
759*7c478bd9Sstevel@tonic-gate 	 * Create pci_intrs::: kstats for all ih types except messages,
760*7c478bd9Sstevel@tonic-gate 	 * which represent unusual conditions and don't need to be tracked.
761*7c478bd9Sstevel@tonic-gate 	 */
762*7c478bd9Sstevel@tonic-gate 	ih_p->ih_ksp = NULL;
763*7c478bd9Sstevel@tonic-gate 	if (rec_type == 0 || rec_type == MSI32_REC || rec_type == MSI64_REC) {
764*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp = kstat_create("pci_intrs",
765*7c478bd9Sstevel@tonic-gate 		    atomic_inc_32_nv(&ih_instance), "config", "interrupts",
766*7c478bd9Sstevel@tonic-gate 		    KSTAT_TYPE_NAMED,
767*7c478bd9Sstevel@tonic-gate 		    sizeof (px_ih_ks_template) / sizeof (kstat_named_t),
768*7c478bd9Sstevel@tonic-gate 		    KSTAT_FLAG_VIRTUAL);
769*7c478bd9Sstevel@tonic-gate 	}
770*7c478bd9Sstevel@tonic-gate 	if (ih_p->ih_ksp != NULL) {
771*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_data_size += MAXPATHLEN * 2;
772*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_lock = &ih_ks_template_lock;
773*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_data = &px_ih_ks_template;
774*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_private = ih_p;
775*7c478bd9Sstevel@tonic-gate 		ih_p->ih_ksp->ks_update = ih_ks_update;
776*7c478bd9Sstevel@tonic-gate 	}
777*7c478bd9Sstevel@tonic-gate 
778*7c478bd9Sstevel@tonic-gate 	return (ih_p);
779*7c478bd9Sstevel@tonic-gate }
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate /*
782*7c478bd9Sstevel@tonic-gate  * Only used for fixed or legacy interrupts.
783*7c478bd9Sstevel@tonic-gate  */
784*7c478bd9Sstevel@tonic-gate int
785*7c478bd9Sstevel@tonic-gate px_ib_update_intr_state(px_t *px_p, dev_info_t *rdip,
786*7c478bd9Sstevel@tonic-gate     uint_t inum, devino_t ino, uint_t new_intr_state)
787*7c478bd9Sstevel@tonic-gate {
788*7c478bd9Sstevel@tonic-gate 	px_ib_t		*ib_p = px_p->px_ib_p;
789*7c478bd9Sstevel@tonic-gate 	px_ib_ino_info_t *ino_p;
790*7c478bd9Sstevel@tonic-gate 	px_ih_t		*ih_p;
791*7c478bd9Sstevel@tonic-gate 	int		ret = DDI_FAILURE;
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate 	DBG(DBG_IB, px_p->px_dip, "ib_update_intr_state: %s%d "
794*7c478bd9Sstevel@tonic-gate 	    "inum %x devino %x state %x\n", ddi_driver_name(rdip),
795*7c478bd9Sstevel@tonic-gate 	    ddi_get_instance(rdip), inum, ino, new_intr_state);
796*7c478bd9Sstevel@tonic-gate 
797*7c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
798*7c478bd9Sstevel@tonic-gate 
799*7c478bd9Sstevel@tonic-gate 	if (ino_p = px_ib_locate_ino(ib_p, ino)) {
800*7c478bd9Sstevel@tonic-gate 		if (ih_p = px_ib_ino_locate_intr(ino_p, rdip, inum, 0, 0)) {
801*7c478bd9Sstevel@tonic-gate 			ih_p->ih_intr_state = new_intr_state;
802*7c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
803*7c478bd9Sstevel@tonic-gate 		}
804*7c478bd9Sstevel@tonic-gate 	}
805*7c478bd9Sstevel@tonic-gate 
806*7c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
807*7c478bd9Sstevel@tonic-gate 	return (ret);
808*7c478bd9Sstevel@tonic-gate }
809*7c478bd9Sstevel@tonic-gate 
810*7c478bd9Sstevel@tonic-gate int
811*7c478bd9Sstevel@tonic-gate px_imu_intr(dev_info_t *dip, px_fh_t *fh_p)
812*7c478bd9Sstevel@tonic-gate {
813*7c478bd9Sstevel@tonic-gate 	uint32_t offset = px_fhd_tbl[fh_p->fh_err_id].fhd_st;
814*7c478bd9Sstevel@tonic-gate 	uint64_t stat = fh_p->fh_stat;
815*7c478bd9Sstevel@tonic-gate 	if (stat)
816*7c478bd9Sstevel@tonic-gate 		LOG(DBG_ERR_INTR, dip, "[%x]=%16llx imu stat\n", offset, stat);
817*7c478bd9Sstevel@tonic-gate 	return (stat ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
818*7c478bd9Sstevel@tonic-gate }
819