1*9e39c5baSBill Taylor /*
2*9e39c5baSBill Taylor  * CDDL HEADER START
3*9e39c5baSBill Taylor  *
4*9e39c5baSBill Taylor  * The contents of this file are subject to the terms of the
5*9e39c5baSBill Taylor  * Common Development and Distribution License (the "License").
6*9e39c5baSBill Taylor  * You may not use this file except in compliance with the License.
7*9e39c5baSBill Taylor  *
8*9e39c5baSBill Taylor  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*9e39c5baSBill Taylor  * or http://www.opensolaris.org/os/licensing.
10*9e39c5baSBill Taylor  * See the License for the specific language governing permissions
11*9e39c5baSBill Taylor  * and limitations under the License.
12*9e39c5baSBill Taylor  *
13*9e39c5baSBill Taylor  * When distributing Covered Code, include this CDDL HEADER in each
14*9e39c5baSBill Taylor  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*9e39c5baSBill Taylor  * If applicable, add the following below this CDDL HEADER, with the
16*9e39c5baSBill Taylor  * fields enclosed by brackets "[]" replaced with your own identifying
17*9e39c5baSBill Taylor  * information: Portions Copyright [yyyy] [name of copyright owner]
18*9e39c5baSBill Taylor  *
19*9e39c5baSBill Taylor  * CDDL HEADER END
20*9e39c5baSBill Taylor  */
21*9e39c5baSBill Taylor 
22*9e39c5baSBill Taylor /*
23*9e39c5baSBill Taylor  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*9e39c5baSBill Taylor  * Use is subject to license terms.
25*9e39c5baSBill Taylor  */
26*9e39c5baSBill Taylor 
27*9e39c5baSBill Taylor /*
28*9e39c5baSBill Taylor  * hermon_fm.c
29*9e39c5baSBill Taylor  *    Hermon (InfiniBand) HCA Driver Fault Management Routines
30*9e39c5baSBill Taylor  *
31*9e39c5baSBill Taylor  * [Hermon FM Implementation]
32*9e39c5baSBill Taylor  *
33*9e39c5baSBill Taylor  * Hermon FM recovers the system from a HW error situation and/or isolates a
34*9e39c5baSBill Taylor  * HW error by calling the FMA acc handle check functions. (calling
35*9e39c5baSBill Taylor  * ddi_fm_acc_err_get()) If a HW error is detected when either
36*9e39c5baSBill Taylor  * ddi_fm_acc_err_get() is called, to determine whether or not the error is
37*9e39c5baSBill Taylor  * transient, the I/O operation causing the error will retry up to three times.
38*9e39c5baSBill Taylor  *
39*9e39c5baSBill Taylor  * (Basic HW error recovery)
40*9e39c5baSBill Taylor  *
41*9e39c5baSBill Taylor  *        |
42*9e39c5baSBill Taylor  *  .---->*
43*9e39c5baSBill Taylor  *  |     |
44*9e39c5baSBill Taylor  *  |   issue an I/O request via PIO
45*9e39c5baSBill Taylor  *  |     |
46*9e39c5baSBill Taylor  *  |     |
47*9e39c5baSBill Taylor  *  |   check acc handle
48*9e39c5baSBill Taylor  *  |     |
49*9e39c5baSBill Taylor  *  |     |
50*9e39c5baSBill Taylor  *  `--< a HW error detected && retry count < 3 >
51*9e39c5baSBill Taylor  *        |
52*9e39c5baSBill Taylor  *        v
53*9e39c5baSBill Taylor  *
54*9e39c5baSBill Taylor  * When a HW error is detected, to provide the error information for users to
55*9e39c5baSBill Taylor  * isolate the faulted HW, Hermon FM issues Solaris FMA ereports as follows.
56*9e39c5baSBill Taylor  *
57*9e39c5baSBill Taylor  *  * PIO transient error
58*9e39c5baSBill Taylor  *         invalid_state => unaffected
59*9e39c5baSBill Taylor  *
60*9e39c5baSBill Taylor  *  * PIO persistent error
61*9e39c5baSBill Taylor  *         invalid_state => lost
62*9e39c5baSBill Taylor  *
63*9e39c5baSBill Taylor  *  * PIO fatal error
64*9e39c5baSBill Taylor  *         invalid_state => lost => panic
65*9e39c5baSBill Taylor  *
66*9e39c5baSBill Taylor  *  * Hermon HCA firmware error
67*9e39c5baSBill Taylor  *         invalid_state => degraded
68*9e39c5baSBill Taylor  *
69*9e39c5baSBill Taylor  *  * Other Hermon HCA specific errors
70*9e39c5baSBill Taylor  *	   uncorrect => unaffected
71*9e39c5baSBill Taylor  *		or
72*9e39c5baSBill Taylor  *	   correct => unaffected
73*9e39c5baSBill Taylor  *
74*9e39c5baSBill Taylor  * (Restrictions)
75*9e39c5baSBill Taylor  *
76*9e39c5baSBill Taylor  * The current implementation has the following restrictions.
77*9e39c5baSBill Taylor  *  * No runtime check/protection
78*9e39c5baSBill Taylor  *  * No detach time check/protection
79*9e39c5baSBill Taylor  *  * No DMA check/protection
80*9e39c5baSBill Taylor  *
81*9e39c5baSBill Taylor  * See the Hermon FMA portfolio in detail.
82*9e39c5baSBill Taylor  */
83*9e39c5baSBill Taylor 
84*9e39c5baSBill Taylor #include <sys/types.h>
85*9e39c5baSBill Taylor #include <sys/conf.h>
86*9e39c5baSBill Taylor #include <sys/ddi.h>
87*9e39c5baSBill Taylor #include <sys/sunddi.h>
88*9e39c5baSBill Taylor #include <sys/sysmacros.h>
89*9e39c5baSBill Taylor #include <sys/list.h>
90*9e39c5baSBill Taylor #include <sys/modhash.h>
91*9e39c5baSBill Taylor 
92*9e39c5baSBill Taylor #include <sys/ib/adapters/hermon/hermon.h>
93*9e39c5baSBill Taylor 
94*9e39c5baSBill Taylor /*
95*9e39c5baSBill Taylor  * Hermon driver has to disable its FM functionality
96*9e39c5baSBill Taylor  * if this "fm_capable" variable is defined or has a value
97*9e39c5baSBill Taylor  * in /kernel/drv/hermon.conf.
98*9e39c5baSBill Taylor  */
99*9e39c5baSBill Taylor static char *fm_cap = "fm-capable";	/* FM capability */
100*9e39c5baSBill Taylor 
101*9e39c5baSBill Taylor static hermon_hca_fm_t hca_fm;		/* Hermon HCA FM Structure */
102*9e39c5baSBill Taylor 
103*9e39c5baSBill Taylor static void i_hca_fm_ereport(dev_info_t *, int, char *);
104*9e39c5baSBill Taylor static void i_hca_fm_init(struct i_hca_fm *);
105*9e39c5baSBill Taylor static void i_hca_fm_fini(struct i_hca_fm *);
106*9e39c5baSBill Taylor static int i_hca_regs_map_setup(struct i_hca_fm *, dev_info_t *, uint_t,
107*9e39c5baSBill Taylor     caddr_t *, offset_t, offset_t, ddi_device_acc_attr_t *, ddi_acc_handle_t *);
108*9e39c5baSBill Taylor static void i_hca_regs_map_free(struct i_hca_fm *, ddi_acc_handle_t *);
109*9e39c5baSBill Taylor static int i_hca_pci_config_setup(struct i_hca_fm *, dev_info_t *,
110*9e39c5baSBill Taylor     ddi_acc_handle_t *);
111*9e39c5baSBill Taylor static void i_hca_pci_config_teardown(struct i_hca_fm *, ddi_acc_handle_t *);
112*9e39c5baSBill Taylor static int i_hca_pio_start(dev_info_t *, struct i_hca_acc_handle *,
113*9e39c5baSBill Taylor     hermon_test_t *);
114*9e39c5baSBill Taylor static int i_hca_pio_end(dev_info_t *, struct i_hca_acc_handle *, int *,
115*9e39c5baSBill Taylor     hermon_test_t *);
116*9e39c5baSBill Taylor static struct i_hca_acc_handle *i_hca_get_acc_handle(struct i_hca_fm *,
117*9e39c5baSBill Taylor     ddi_acc_handle_t);
118*9e39c5baSBill Taylor 
119*9e39c5baSBill Taylor /* forward declaration for hermon_fm_{init, fini}() */
120*9e39c5baSBill Taylor #ifdef FMA_TEST
121*9e39c5baSBill Taylor static void i_hca_test_init(mod_hash_t **, mod_hash_t **);
122*9e39c5baSBill Taylor static void i_hca_test_fini(mod_hash_t **, mod_hash_t **);
123*9e39c5baSBill Taylor #endif /* FMA_TEST */
124*9e39c5baSBill Taylor 
125*9e39c5baSBill Taylor /*
126*9e39c5baSBill Taylor  * Hermon FM Functions
127*9e39c5baSBill Taylor  *
128*9e39c5baSBill Taylor  * These functions are based on the HCA FM common interface
129*9e39c5baSBill Taylor  * defined below, but specific to the Hermon HCA FM capabilities.
130*9e39c5baSBill Taylor  */
131*9e39c5baSBill Taylor 
132*9e39c5baSBill Taylor /*
133*9e39c5baSBill Taylor  *  void
134*9e39c5baSBill Taylor  *  hermon_hca_fm_init(hermon_state_t *state, hermon_hca_fm_t *hca)
135*9e39c5baSBill Taylor  *
136*9e39c5baSBill Taylor  *  Overview
137*9e39c5baSBill Taylor  *      hermon_hca_fm_init() initializes the Hermon FM resources.
138*9e39c5baSBill Taylor  *
139*9e39c5baSBill Taylor  *  Argument
140*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
141*9e39c5baSBill Taylor  *      hca: pointer to Hermon FM structure
142*9e39c5baSBill Taylor  *
143*9e39c5baSBill Taylor  *  Return value
144*9e39c5baSBill Taylor  *      Nothing
145*9e39c5baSBill Taylor  *
146*9e39c5baSBill Taylor  *  Caller's context
147*9e39c5baSBill Taylor  *      hermon_hca_fm_init() can be called in user or kernel context only.
148*9e39c5baSBill Taylor  */
149*9e39c5baSBill Taylor static void
150*9e39c5baSBill Taylor hermon_hca_fm_init(hermon_state_t *state, hermon_hca_fm_t *hca_fm)
151*9e39c5baSBill Taylor {
152*9e39c5baSBill Taylor 	state->hs_fm_hca_fm = hca_fm;
153*9e39c5baSBill Taylor 	i_hca_fm_init((struct i_hca_fm *)hca_fm);
154*9e39c5baSBill Taylor }
155*9e39c5baSBill Taylor 
156*9e39c5baSBill Taylor 
157*9e39c5baSBill Taylor /*
158*9e39c5baSBill Taylor  *  void
159*9e39c5baSBill Taylor  *  hermon_hca_fm_fini(hermon_state_t *state)
160*9e39c5baSBill Taylor  *
161*9e39c5baSBill Taylor  *  Overview
162*9e39c5baSBill Taylor  *      hermon_hca_fm_fini() releases the Hermon FM resources.
163*9e39c5baSBill Taylor  *
164*9e39c5baSBill Taylor  *  Argument
165*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
166*9e39c5baSBill Taylor  *
167*9e39c5baSBill Taylor  *  Return value
168*9e39c5baSBill Taylor  *      Nothing
169*9e39c5baSBill Taylor  *
170*9e39c5baSBill Taylor  *  Caller's context
171*9e39c5baSBill Taylor  *      hermon_hca_fm_fini() can be called in user or kernel context only.
172*9e39c5baSBill Taylor  */
173*9e39c5baSBill Taylor static void
174*9e39c5baSBill Taylor hermon_hca_fm_fini(hermon_state_t *state)
175*9e39c5baSBill Taylor {
176*9e39c5baSBill Taylor 	i_hca_fm_fini((struct i_hca_fm *)state->hs_fm_hca_fm);
177*9e39c5baSBill Taylor 	state->hs_fm_hca_fm = NULL;
178*9e39c5baSBill Taylor }
179*9e39c5baSBill Taylor 
180*9e39c5baSBill Taylor /*
181*9e39c5baSBill Taylor  *  void
182*9e39c5baSBill Taylor  *  hermon_clr_state_nolock(hermon_state_t *state, int fm_state)
183*9e39c5baSBill Taylor  *
184*9e39c5baSBill Taylor  *  Overview
185*9e39c5baSBill Taylor  *      hermon_clr_state() drops the specified state from Hermon FM state
186*9e39c5baSBill Taylor  *      without the mutex locks.
187*9e39c5baSBill Taylor  *
188*9e39c5baSBill Taylor  *  Argument
189*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
190*9e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
191*9e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
192*9e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
193*9e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
194*9e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
195*9e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
196*9e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
197*9e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
198*9e39c5baSBill Taylor  *
199*9e39c5baSBill Taylor  *  Return value
200*9e39c5baSBill Taylor  *  	Nothing
201*9e39c5baSBill Taylor  *
202*9e39c5baSBill Taylor  *  Caller's context
203*9e39c5baSBill Taylor  *      hermon_clr_state() can be called in user, kernel, interrupt context
204*9e39c5baSBill Taylor  *      or high interrupt context.
205*9e39c5baSBill Taylor  */
206*9e39c5baSBill Taylor void
207*9e39c5baSBill Taylor hermon_clr_state_nolock(hermon_state_t *state, int fm_state)
208*9e39c5baSBill Taylor {
209*9e39c5baSBill Taylor 	extern void membar_sync(void);
210*9e39c5baSBill Taylor 
211*9e39c5baSBill Taylor 	state->hs_fm_state &= ~fm_state;
212*9e39c5baSBill Taylor 	membar_sync();
213*9e39c5baSBill Taylor }
214*9e39c5baSBill Taylor 
215*9e39c5baSBill Taylor 
216*9e39c5baSBill Taylor /*
217*9e39c5baSBill Taylor  *  void
218*9e39c5baSBill Taylor  *  hermon_clr_state(hermon_state_t *state, int fm_state)
219*9e39c5baSBill Taylor  *
220*9e39c5baSBill Taylor  *  Overview
221*9e39c5baSBill Taylor  *      hermon_clr_state() drops the specified state from Hermon FM state.
222*9e39c5baSBill Taylor  *
223*9e39c5baSBill Taylor  *  Argument
224*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
225*9e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
226*9e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
227*9e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
228*9e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
229*9e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
230*9e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
231*9e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
232*9e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
233*9e39c5baSBill Taylor  *
234*9e39c5baSBill Taylor  *  Return value
235*9e39c5baSBill Taylor  *  	Nothing
236*9e39c5baSBill Taylor  *
237*9e39c5baSBill Taylor  *  Caller's context
238*9e39c5baSBill Taylor  *      hermon_clr_state() can be called in user, kernel or interrupt context.
239*9e39c5baSBill Taylor  */
240*9e39c5baSBill Taylor static void
241*9e39c5baSBill Taylor hermon_clr_state(hermon_state_t *state, int fm_state)
242*9e39c5baSBill Taylor {
243*9e39c5baSBill Taylor 	ASSERT(fm_state != HCA_NO_FM);
244*9e39c5baSBill Taylor 
245*9e39c5baSBill Taylor 	mutex_enter(&state->hs_fm_lock);
246*9e39c5baSBill Taylor 	hermon_clr_state_nolock(state, fm_state);
247*9e39c5baSBill Taylor 	mutex_exit(&state->hs_fm_lock);
248*9e39c5baSBill Taylor }
249*9e39c5baSBill Taylor 
250*9e39c5baSBill Taylor 
251*9e39c5baSBill Taylor /*
252*9e39c5baSBill Taylor  *  void
253*9e39c5baSBill Taylor  *  hermon_set_state(hermon_state_t *state, int fm_state)
254*9e39c5baSBill Taylor  *
255*9e39c5baSBill Taylor  *  Overview
256*9e39c5baSBill Taylor  *      hermon_set_state() sets Hermon FM state.
257*9e39c5baSBill Taylor  *
258*9e39c5baSBill Taylor  *  Argument
259*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
260*9e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
261*9e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
262*9e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
263*9e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
264*9e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
265*9e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
266*9e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
267*9e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
268*9e39c5baSBill Taylor  *
269*9e39c5baSBill Taylor  *  Return value
270*9e39c5baSBill Taylor  *  	Nothing
271*9e39c5baSBill Taylor  *
272*9e39c5baSBill Taylor  *  Caller's context
273*9e39c5baSBill Taylor  *      hermon_set_state() can be called in user, kernel or interrupt context.
274*9e39c5baSBill Taylor  */
275*9e39c5baSBill Taylor static void
276*9e39c5baSBill Taylor hermon_set_state(hermon_state_t *state, int fm_state)
277*9e39c5baSBill Taylor {
278*9e39c5baSBill Taylor 	extern void membar_sync(void);
279*9e39c5baSBill Taylor 
280*9e39c5baSBill Taylor 	mutex_enter(&state->hs_fm_lock);
281*9e39c5baSBill Taylor 	if (fm_state == HCA_NO_FM) {
282*9e39c5baSBill Taylor 		state->hs_fm_state = HCA_NO_FM;
283*9e39c5baSBill Taylor 	} else {
284*9e39c5baSBill Taylor 		state->hs_fm_state |= fm_state;
285*9e39c5baSBill Taylor 	}
286*9e39c5baSBill Taylor 	membar_sync();
287*9e39c5baSBill Taylor 	mutex_exit(&state->hs_fm_lock);
288*9e39c5baSBill Taylor }
289*9e39c5baSBill Taylor 
290*9e39c5baSBill Taylor 
291*9e39c5baSBill Taylor /*
292*9e39c5baSBill Taylor  *  int
293*9e39c5baSBill Taylor  *  hermon_get_state(hermon_state_t *state)
294*9e39c5baSBill Taylor  *
295*9e39c5baSBill Taylor  *  Overview
296*9e39c5baSBill Taylor  *      hermon_get_state() returns the current Hermon FM state.
297*9e39c5baSBill Taylor  *
298*9e39c5baSBill Taylor  *  Argument
299*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
300*9e39c5baSBill Taylor  *
301*9e39c5baSBill Taylor  *  Return value
302*9e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
303*9e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
304*9e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
305*9e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
306*9e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
307*9e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
308*9e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
309*9e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
310*9e39c5baSBill Taylor  *
311*9e39c5baSBill Taylor  *  Caller's context
312*9e39c5baSBill Taylor  *      hermon_get_state() can be called in user, kernel or interrupt context.
313*9e39c5baSBill Taylor  */
314*9e39c5baSBill Taylor int
315*9e39c5baSBill Taylor hermon_get_state(hermon_state_t *state)
316*9e39c5baSBill Taylor {
317*9e39c5baSBill Taylor 	return (state->hs_fm_state);
318*9e39c5baSBill Taylor }
319*9e39c5baSBill Taylor 
320*9e39c5baSBill Taylor 
321*9e39c5baSBill Taylor /*
322*9e39c5baSBill Taylor  *  void
323*9e39c5baSBill Taylor  *  hermon_fm_init(hermon_state_t *state)
324*9e39c5baSBill Taylor  *
325*9e39c5baSBill Taylor  *  Overview
326*9e39c5baSBill Taylor  *      hermon_fm_init() is a Hermon FM initialization function which registers
327*9e39c5baSBill Taylor  *      some FMA functions such as the ereport and the acc check capabilities
328*9e39c5baSBill Taylor  *      for Hermon. If the "fm_disable" property in /kernel/drv/hermon.conf is
329*9e39c5baSBill Taylor  *      defined (and/or its value is set), then the Hermon FM capabilities will
330*9e39c5baSBill Taylor  *      drop, and only the default capabilities (the ereport and error callback
331*9e39c5baSBill Taylor  *      capabilities) are available (and the action against HW errors is
332*9e39c5baSBill Taylor  *      issuing an ereport then panicking the system).
333*9e39c5baSBill Taylor  *
334*9e39c5baSBill Taylor  *  Argument
335*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
336*9e39c5baSBill Taylor  *
337*9e39c5baSBill Taylor  *  Return value
338*9e39c5baSBill Taylor  *      Nothing
339*9e39c5baSBill Taylor  *
340*9e39c5baSBill Taylor  *  Caller's context
341*9e39c5baSBill Taylor  *      hermon_fm_init() can be called in user or kernel context only.
342*9e39c5baSBill Taylor  */
343*9e39c5baSBill Taylor void
344*9e39c5baSBill Taylor hermon_fm_init(hermon_state_t *state)
345*9e39c5baSBill Taylor {
346*9e39c5baSBill Taylor 	ddi_iblock_cookie_t iblk;
347*9e39c5baSBill Taylor 
348*9e39c5baSBill Taylor 	/*
349*9e39c5baSBill Taylor 	 * Check the "fm_disable" property. If it's defined,
350*9e39c5baSBill Taylor 	 * use the Solaris FMA default action for Hermon.
351*9e39c5baSBill Taylor 	 */
352*9e39c5baSBill Taylor 	if (ddi_getprop(DDI_DEV_T_NONE, state->hs_dip, DDI_PROP_DONTPASS,
353*9e39c5baSBill Taylor 	    "fm_disable", 0) != 0) {
354*9e39c5baSBill Taylor 		state->hs_fm_disable = 1;
355*9e39c5baSBill Taylor 	}
356*9e39c5baSBill Taylor 
357*9e39c5baSBill Taylor 	/* If hs_fm_diable is set, then skip the rest */
358*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
359*9e39c5baSBill Taylor 		hermon_set_state(state, HCA_NO_FM);
360*9e39c5baSBill Taylor 		return;
361*9e39c5baSBill Taylor 	}
362*9e39c5baSBill Taylor 
363*9e39c5baSBill Taylor 	/* Set the Hermon FM attach mode */
364*9e39c5baSBill Taylor 	hermon_set_state(state, HCA_ATTCH_FM);
365*9e39c5baSBill Taylor 
366*9e39c5baSBill Taylor 	/* Initialize the Solaris FMA capabilities for the Hermon FM support */
367*9e39c5baSBill Taylor 	state->hs_fm_capabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
368*9e39c5baSBill Taylor 	    state->hs_dip, DDI_PROP_DONTPASS, fm_cap,
369*9e39c5baSBill Taylor 	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE);
370*9e39c5baSBill Taylor 
371*9e39c5baSBill Taylor 	/*
372*9e39c5baSBill Taylor 	 * The Hermon FM uses the ereport and acc check capabilites only,
373*9e39c5baSBill Taylor 	 * but both of them should be available. If either is not, turn
374*9e39c5baSBill Taylor 	 * hs_fm_disable on and behave in the same way as the "fm_diable"
375*9e39c5baSBill Taylor 	 * property is set.
376*9e39c5baSBill Taylor 	 */
377*9e39c5baSBill Taylor 	if (state->hs_fm_capabilities !=
378*9e39c5baSBill Taylor 	    (DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE)) {
379*9e39c5baSBill Taylor 		state->hs_fm_disable = 1;
380*9e39c5baSBill Taylor 		hermon_set_state(state, HCA_NO_FM);
381*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
382*9e39c5baSBill Taylor 		    "Hermon FM capability fails");
383*9e39c5baSBill Taylor 		return;
384*9e39c5baSBill Taylor 	}
385*9e39c5baSBill Taylor 
386*9e39c5baSBill Taylor 	/* Initialize the HCA FM resources */
387*9e39c5baSBill Taylor 	hermon_hca_fm_init(state, &hca_fm);
388*9e39c5baSBill Taylor 
389*9e39c5baSBill Taylor 	/* Initialize the fm state lock */
390*9e39c5baSBill Taylor 	mutex_init(&state->hs_fm_lock, NULL, MUTEX_DRIVER, NULL);
391*9e39c5baSBill Taylor 
392*9e39c5baSBill Taylor 	/* Register the capabilities with the IO fault services */
393*9e39c5baSBill Taylor 	ddi_fm_init(state->hs_dip, &state->hs_fm_capabilities, &iblk);
394*9e39c5baSBill Taylor 
395*9e39c5baSBill Taylor 	/* Set up the pci ereport capabilities if the ereport is capable */
396*9e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
397*9e39c5baSBill Taylor 		pci_ereport_setup(state->hs_dip);
398*9e39c5baSBill Taylor 	}
399*9e39c5baSBill Taylor 
400*9e39c5baSBill Taylor 	/* Set the Hermon FM state */
401*9e39c5baSBill Taylor 	hermon_set_state(state, HCA_PIO_FM | HCA_EREPORT_FM);
402*9e39c5baSBill Taylor 
403*9e39c5baSBill Taylor #ifdef FMA_TEST
404*9e39c5baSBill Taylor 	i_hca_test_init(&state->hs_fm_test_hash, &state->hs_fm_id_hash);
405*9e39c5baSBill Taylor #endif /* FMA_TEST */
406*9e39c5baSBill Taylor }
407*9e39c5baSBill Taylor 
408*9e39c5baSBill Taylor 
409*9e39c5baSBill Taylor /*
410*9e39c5baSBill Taylor  *  void
411*9e39c5baSBill Taylor  *  hermon_fm_fini(hermon_state_t *state)
412*9e39c5baSBill Taylor  *
413*9e39c5baSBill Taylor  *  Overview
414*9e39c5baSBill Taylor  *      hermon_fm_fini() is a Hermon FM finalization function which de-registers
415*9e39c5baSBill Taylor  *      Solaris FMA functions set to Hermon.
416*9e39c5baSBill Taylor  *
417*9e39c5baSBill Taylor  *  Argument
418*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
419*9e39c5baSBill Taylor  *
420*9e39c5baSBill Taylor  *  Return value
421*9e39c5baSBill Taylor  *      Nothing
422*9e39c5baSBill Taylor  *
423*9e39c5baSBill Taylor  *  Caller's context
424*9e39c5baSBill Taylor  *      hermon_fm_fini() can be called in user or kernel context only.
425*9e39c5baSBill Taylor  */
426*9e39c5baSBill Taylor void
427*9e39c5baSBill Taylor hermon_fm_fini(hermon_state_t *state)
428*9e39c5baSBill Taylor {
429*9e39c5baSBill Taylor 	/*
430*9e39c5baSBill Taylor 	 * If hermon_fm_diable is set or there is no FM service provided,
431*9e39c5baSBill Taylor 	 * then skip the rest.
432*9e39c5baSBill Taylor 	 */
433*9e39c5baSBill Taylor 	if (state->hs_fm_disable || hermon_get_state(state) == HCA_NO_FM) {
434*9e39c5baSBill Taylor 		return;
435*9e39c5baSBill Taylor 	}
436*9e39c5baSBill Taylor 
437*9e39c5baSBill Taylor 	ASSERT(!(hermon_get_state(state) & HCA_ERRCB_FM));
438*9e39c5baSBill Taylor 
439*9e39c5baSBill Taylor #ifdef FMA_TEST
440*9e39c5baSBill Taylor 	i_hca_test_fini(&state->hs_fm_test_hash, &state->hs_fm_id_hash);
441*9e39c5baSBill Taylor #endif /* FMA_TEST */
442*9e39c5baSBill Taylor 
443*9e39c5baSBill Taylor 	/* Set the Hermon FM state to no support */
444*9e39c5baSBill Taylor 	hermon_set_state(state, HCA_NO_FM);
445*9e39c5baSBill Taylor 
446*9e39c5baSBill Taylor 	/* Release HCA FM resources */
447*9e39c5baSBill Taylor 	hermon_hca_fm_fini(state);
448*9e39c5baSBill Taylor 
449*9e39c5baSBill Taylor 	/*
450*9e39c5baSBill Taylor 	 * Release any resources allocated by pci_ereport_setup()
451*9e39c5baSBill Taylor 	 */
452*9e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
453*9e39c5baSBill Taylor 		pci_ereport_teardown(state->hs_dip);
454*9e39c5baSBill Taylor 	}
455*9e39c5baSBill Taylor 
456*9e39c5baSBill Taylor 	/* De-register the Hermon FM from the IO fault services */
457*9e39c5baSBill Taylor 	ddi_fm_fini(state->hs_dip);
458*9e39c5baSBill Taylor }
459*9e39c5baSBill Taylor 
460*9e39c5baSBill Taylor 
461*9e39c5baSBill Taylor /*
462*9e39c5baSBill Taylor  *  int
463*9e39c5baSBill Taylor  *  hermon_fm_ereport_init(hermon_state_t *state)
464*9e39c5baSBill Taylor  *
465*9e39c5baSBill Taylor  *  Overview
466*9e39c5baSBill Taylor  *      hermon_fm_ereport_init() changes the Hermon FM state to the ereport
467*9e39c5baSBill Taylor  *      only mode during the driver attach.
468*9e39c5baSBill Taylor  *
469*9e39c5baSBill Taylor  *  Argument
470*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
471*9e39c5baSBill Taylor  *
472*9e39c5baSBill Taylor  *  Return value
473*9e39c5baSBill Taylor  *      DDI_SUCCESS
474*9e39c5baSBill Taylor  *      DDI_FAILURE
475*9e39c5baSBill Taylor  *
476*9e39c5baSBill Taylor  *  Caller's context
477*9e39c5baSBill Taylor  *      hermon_fm_ereport_init() can be called in user or kernel context only.
478*9e39c5baSBill Taylor  */
479*9e39c5baSBill Taylor int
480*9e39c5baSBill Taylor hermon_fm_ereport_init(hermon_state_t *state)
481*9e39c5baSBill Taylor {
482*9e39c5baSBill Taylor 	ddi_iblock_cookie_t iblk;
483*9e39c5baSBill Taylor 	hermon_cfg_profile_t *cfgprof;
484*9e39c5baSBill Taylor 	hermon_hw_querydevlim_t	*devlim;
485*9e39c5baSBill Taylor 	hermon_rsrc_hw_entry_info_t entry_info;
486*9e39c5baSBill Taylor 	hermon_rsrc_pool_info_t	*rsrc_pool;
487*9e39c5baSBill Taylor 	uint64_t offset, num, max, num_prealloc;
488*9e39c5baSBill Taylor 	ddi_device_acc_attr_t dev_attr = {
489*9e39c5baSBill Taylor 		DDI_DEVICE_ATTR_V0,
490*9e39c5baSBill Taylor 		DDI_STRUCTURE_LE_ACC,
491*9e39c5baSBill Taylor 		DDI_STRICTORDER_ACC,
492*9e39c5baSBill Taylor 		DDI_DEFAULT_ACC
493*9e39c5baSBill Taylor 	};
494*9e39c5baSBill Taylor 	char *rsrc_name;
495*9e39c5baSBill Taylor 	extern void membar_sync(void);
496*9e39c5baSBill Taylor 
497*9e39c5baSBill Taylor 	/* Stop the poll thread while the FM state is being changed */
498*9e39c5baSBill Taylor 	state->hs_fm_poll_suspend = B_TRUE;
499*9e39c5baSBill Taylor 	membar_sync();
500*9e39c5baSBill Taylor 
501*9e39c5baSBill Taylor 	/*
502*9e39c5baSBill Taylor 	 * Disable the Hermon interrupt after the interrupt capability flag
503*9e39c5baSBill Taylor 	 * is checked.
504*9e39c5baSBill Taylor 	 */
505*9e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
506*9e39c5baSBill Taylor 		if (ddi_intr_block_disable
507*9e39c5baSBill Taylor 		    (&state->hs_intrmsi_hdl[0], 1) != DDI_SUCCESS) {
508*9e39c5baSBill Taylor 			return (DDI_FAILURE);
509*9e39c5baSBill Taylor 		}
510*9e39c5baSBill Taylor 	} else {
511*9e39c5baSBill Taylor 		if (ddi_intr_disable
512*9e39c5baSBill Taylor 		    (state->hs_intrmsi_hdl[0]) != DDI_SUCCESS) {
513*9e39c5baSBill Taylor 			return (DDI_FAILURE);
514*9e39c5baSBill Taylor 		}
515*9e39c5baSBill Taylor 	}
516*9e39c5baSBill Taylor 
517*9e39c5baSBill Taylor 	/*
518*9e39c5baSBill Taylor 	 * Release any resources allocated by pci_ereport_setup()
519*9e39c5baSBill Taylor 	 */
520*9e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
521*9e39c5baSBill Taylor 		pci_ereport_teardown(state->hs_dip);
522*9e39c5baSBill Taylor 	}
523*9e39c5baSBill Taylor 
524*9e39c5baSBill Taylor 	/* De-register the Hermon FM from the IO fault services */
525*9e39c5baSBill Taylor 	ddi_fm_fini(state->hs_dip);
526*9e39c5baSBill Taylor 
527*9e39c5baSBill Taylor 	/* Re-initialize fm ereport with the ereport only */
528*9e39c5baSBill Taylor 	state->hs_fm_capabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
529*9e39c5baSBill Taylor 	    state->hs_dip, DDI_PROP_DONTPASS, fm_cap,
530*9e39c5baSBill Taylor 	    DDI_FM_EREPORT_CAPABLE);
531*9e39c5baSBill Taylor 
532*9e39c5baSBill Taylor 	/*
533*9e39c5baSBill Taylor 	 * Now that the Hermon FM uses the ereport capability only,
534*9e39c5baSBill Taylor 	 * If it's not set, turn hs_fm_disable on and behave in the
535*9e39c5baSBill Taylor 	 * same way as the "fm_diable" property is set.
536*9e39c5baSBill Taylor 	 */
537*9e39c5baSBill Taylor 	if (state->hs_fm_capabilities != DDI_FM_EREPORT_CAPABLE) {
538*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
539*9e39c5baSBill Taylor 		    "Hermon FM ereport fails (ereport mode)");
540*9e39c5baSBill Taylor 		goto error;
541*9e39c5baSBill Taylor 	}
542*9e39c5baSBill Taylor 
543*9e39c5baSBill Taylor 	/* Re-register the ereport capability with the IO fault services */
544*9e39c5baSBill Taylor 	ddi_fm_init(state->hs_dip, &state->hs_fm_capabilities, &iblk);
545*9e39c5baSBill Taylor 
546*9e39c5baSBill Taylor 	/* Initialize the pci ereport capabilities if the ereport is capable */
547*9e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
548*9e39c5baSBill Taylor 		pci_ereport_setup(state->hs_dip);
549*9e39c5baSBill Taylor 	}
550*9e39c5baSBill Taylor 
551*9e39c5baSBill Taylor 	/* Setup for PCI config read/write of HCA device */
552*9e39c5baSBill Taylor 	if (pci_config_setup(state->hs_dip, &state->hs_reg_pcihdl) !=
553*9e39c5baSBill Taylor 	    DDI_SUCCESS) {
554*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
555*9e39c5baSBill Taylor 		    "PCI config mapping fails (ereport mode)");
556*9e39c5baSBill Taylor 		goto error;
557*9e39c5baSBill Taylor 	}
558*9e39c5baSBill Taylor 
559*9e39c5baSBill Taylor 	/* Allocate the regular access handle for MSI-X tables */
560*9e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, state->hs_msix_tbl_rnumber,
561*9e39c5baSBill Taylor 	    (caddr_t *)&state->hs_msix_tbl_addr, state->hs_msix_tbl_offset,
562*9e39c5baSBill Taylor 	    state->hs_msix_tbl_size, &dev_attr,
563*9e39c5baSBill Taylor 	    &state->hs_reg_msix_tblhdl) != DDI_SUCCESS) {
564*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
565*9e39c5baSBill Taylor 		    "MSI-X Table mapping fails (ereport mode)");
566*9e39c5baSBill Taylor 		goto error;
567*9e39c5baSBill Taylor 	}
568*9e39c5baSBill Taylor 
569*9e39c5baSBill Taylor 	/* Allocate the regular access handle for MSI-X PBA */
570*9e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, state->hs_msix_pba_rnumber,
571*9e39c5baSBill Taylor 	    (caddr_t *)&state->hs_msix_pba_addr, state->hs_msix_pba_offset,
572*9e39c5baSBill Taylor 	    state->hs_msix_pba_size, &dev_attr,
573*9e39c5baSBill Taylor 	    &state->hs_reg_msix_pbahdl) != DDI_SUCCESS) {
574*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
575*9e39c5baSBill Taylor 		    "MSI-X PBA mapping fails (ereport mode)");
576*9e39c5baSBill Taylor 		goto error;
577*9e39c5baSBill Taylor 	}
578*9e39c5baSBill Taylor 
579*9e39c5baSBill Taylor 	/* Allocate the regular access handle for Hermon CMD I/O space */
580*9e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, HERMON_CMD_BAR,
581*9e39c5baSBill Taylor 	    &state->hs_reg_cmd_baseaddr, 0, 0, &state->hs_reg_accattr,
582*9e39c5baSBill Taylor 	    &state->hs_reg_cmdhdl) != DDI_SUCCESS) {
583*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
584*9e39c5baSBill Taylor 		    "CMD_BAR mapping fails (ereport mode)");
585*9e39c5baSBill Taylor 		goto error;
586*9e39c5baSBill Taylor 	}
587*9e39c5baSBill Taylor 
588*9e39c5baSBill Taylor 	/* Reset the host command register */
589*9e39c5baSBill Taylor 	state->hs_cmd_regs.hcr = (hermon_hw_hcr_t *)
590*9e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr + HERMON_CMD_HCR_OFFSET);
591*9e39c5baSBill Taylor 
592*9e39c5baSBill Taylor 	/* Reset the software reset register */
593*9e39c5baSBill Taylor 	state->hs_cmd_regs.sw_reset = (uint32_t *)
594*9e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr +
595*9e39c5baSBill Taylor 	    HERMON_CMD_SW_RESET_OFFSET);
596*9e39c5baSBill Taylor 
597*9e39c5baSBill Taylor 	/* Reset the software reset register semaphore */
598*9e39c5baSBill Taylor 	state->hs_cmd_regs.sw_semaphore = (uint32_t *)
599*9e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr +
600*9e39c5baSBill Taylor 	    HERMON_CMD_SW_SEMAPHORE_OFFSET);
601*9e39c5baSBill Taylor 
602*9e39c5baSBill Taylor 	/* Calculate the clear interrupt register offset */
603*9e39c5baSBill Taylor 	offset = state->hs_fw.clr_intr_offs & HERMON_CMD_OFFSET_MASK;
604*9e39c5baSBill Taylor 
605*9e39c5baSBill Taylor 	/* Reset the clear interrupt address */
606*9e39c5baSBill Taylor 	state->hs_cmd_regs.clr_intr = (uint64_t *)
607*9e39c5baSBill Taylor 	    (uintptr_t)(state->hs_reg_cmd_baseaddr + offset);
608*9e39c5baSBill Taylor 
609*9e39c5baSBill Taylor 	/* Reset the internal error buffer address */
610*9e39c5baSBill Taylor 	state->hs_cmd_regs.fw_err_buf = (uint32_t *)(uintptr_t)
611*9e39c5baSBill Taylor 	    (state->hs_reg_cmd_baseaddr + state->hs_fw.error_buf_addr);
612*9e39c5baSBill Taylor 
613*9e39c5baSBill Taylor 	/* Check if the blue flame is enabled, and set the offset value */
614*9e39c5baSBill Taylor 	if (state->hs_devlim.blu_flm) {
615*9e39c5baSBill Taylor 		offset = (uint64_t)1 <<
616*9e39c5baSBill Taylor 		    (state->hs_devlim.log_max_uar_sz + 20);
617*9e39c5baSBill Taylor 	} else {
618*9e39c5baSBill Taylor 		offset = 0;
619*9e39c5baSBill Taylor 	}
620*9e39c5baSBill Taylor 
621*9e39c5baSBill Taylor 	/* Allocate the regular access handle for Hermon UAR I/O space */
622*9e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, HERMON_UAR_BAR,
623*9e39c5baSBill Taylor 	    &state->hs_reg_uar_baseaddr, 0, offset,
624*9e39c5baSBill Taylor 	    &state->hs_reg_accattr, &state->hs_reg_uarhdl) != DDI_SUCCESS) {
625*9e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
626*9e39c5baSBill Taylor 		    "UAR BAR mapping fails (ereport mode)");
627*9e39c5baSBill Taylor 		goto error;
628*9e39c5baSBill Taylor 	}
629*9e39c5baSBill Taylor 
630*9e39c5baSBill Taylor 	/* Drop the Hermon FM Attach Mode */
631*9e39c5baSBill Taylor 	hermon_clr_state(state, HCA_ATTCH_FM);
632*9e39c5baSBill Taylor 
633*9e39c5baSBill Taylor 	/* Set the Hermon FM Runtime Mode */
634*9e39c5baSBill Taylor 	hermon_set_state(state, HCA_RUNTM_FM);
635*9e39c5baSBill Taylor 
636*9e39c5baSBill Taylor 	/* Free up Hermon UAR page #1 */
637*9e39c5baSBill Taylor 	hermon_rsrc_free(state, &state->hs_uarkpg_rsrc);
638*9e39c5baSBill Taylor 
639*9e39c5baSBill Taylor 	/* Free up the UAR pool */
640*9e39c5baSBill Taylor 	entry_info.hwi_rsrcpool = &state->hs_rsrc_hdl[HERMON_UARPG];
641*9e39c5baSBill Taylor 	hermon_rsrc_hw_entries_fini(state, &entry_info);
642*9e39c5baSBill Taylor 
643*9e39c5baSBill Taylor 	/* Re-allocate the UAR pool */
644*9e39c5baSBill Taylor 	cfgprof = state->hs_cfg_profile;
645*9e39c5baSBill Taylor 	devlim	= &state->hs_devlim;
646*9e39c5baSBill Taylor 	num			  = ((uint64_t)1 << cfgprof->cp_log_num_uar);
647*9e39c5baSBill Taylor 	max			  = num;
648*9e39c5baSBill Taylor 	num_prealloc		  = max(devlim->num_rsvd_uar, 128);
649*9e39c5baSBill Taylor 	rsrc_pool		  = &state->hs_rsrc_hdl[HERMON_UARPG];
650*9e39c5baSBill Taylor 	rsrc_pool->rsrc_type	  = HERMON_UARPG;
651*9e39c5baSBill Taylor 	rsrc_pool->rsrc_loc	  = HERMON_IN_UAR;
652*9e39c5baSBill Taylor 	rsrc_pool->rsrc_pool_size = (num << PAGESHIFT);
653*9e39c5baSBill Taylor 	rsrc_pool->rsrc_shift	  = PAGESHIFT;
654*9e39c5baSBill Taylor 	rsrc_pool->rsrc_quantum	  = (uint_t)PAGESIZE;
655*9e39c5baSBill Taylor 	rsrc_pool->rsrc_align	  = PAGESIZE;
656*9e39c5baSBill Taylor 	rsrc_pool->rsrc_state	  = state;
657*9e39c5baSBill Taylor 	rsrc_pool->rsrc_start	  = (void *)state->hs_reg_uar_baseaddr;
658*9e39c5baSBill Taylor 	rsrc_name = (char *)kmem_zalloc(HERMON_RSRC_NAME_MAXLEN, KM_SLEEP);
659*9e39c5baSBill Taylor 	HERMON_RSRC_NAME(rsrc_name, HERMON_UAR_PAGE_VMEM_RUNTM);
660*9e39c5baSBill Taylor 	entry_info.hwi_num	  = num;
661*9e39c5baSBill Taylor 	entry_info.hwi_max	  = max;
662*9e39c5baSBill Taylor 	entry_info.hwi_prealloc	  = num_prealloc;
663*9e39c5baSBill Taylor 	entry_info.hwi_rsrcpool	  = rsrc_pool;
664*9e39c5baSBill Taylor 	entry_info.hwi_rsrcname	  = rsrc_name;
665*9e39c5baSBill Taylor 	if (hermon_rsrc_hw_entries_init(state, &entry_info) != DDI_SUCCESS) {
666*9e39c5baSBill Taylor 		kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
667*9e39c5baSBill Taylor 		goto error;
668*9e39c5baSBill Taylor 	}
669*9e39c5baSBill Taylor 	kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
670*9e39c5baSBill Taylor 
671*9e39c5baSBill Taylor 	/* Re-allocate the kernel UAR page */
672*9e39c5baSBill Taylor 	if (hermon_rsrc_alloc(state, HERMON_UARPG, 1, HERMON_SLEEP,
673*9e39c5baSBill Taylor 	    &state->hs_uarkpg_rsrc) != DDI_SUCCESS) {
674*9e39c5baSBill Taylor 		goto error;
675*9e39c5baSBill Taylor 	}
676*9e39c5baSBill Taylor 
677*9e39c5baSBill Taylor 	/* Setup pointer to kernel UAR page */
678*9e39c5baSBill Taylor 	state->hs_uar = (hermon_hw_uar_t *)state->hs_uarkpg_rsrc->hr_addr;
679*9e39c5baSBill Taylor 
680*9e39c5baSBill Taylor 	/* Now drop the the Hermon PIO FM */
681*9e39c5baSBill Taylor 	hermon_clr_state(state, HCA_PIO_FM);
682*9e39c5baSBill Taylor 
683*9e39c5baSBill Taylor 	/* Release the MSI-X Table access handle */
684*9e39c5baSBill Taylor 	if (state->hs_fm_msix_tblhdl) {
685*9e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_msix_tblhdl);
686*9e39c5baSBill Taylor 		state->hs_fm_msix_tblhdl = NULL;
687*9e39c5baSBill Taylor 	}
688*9e39c5baSBill Taylor 
689*9e39c5baSBill Taylor 	/* Release the MSI-X PBA access handle */
690*9e39c5baSBill Taylor 	if (state->hs_fm_msix_pbahdl) {
691*9e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_msix_pbahdl);
692*9e39c5baSBill Taylor 		state->hs_fm_msix_pbahdl = NULL;
693*9e39c5baSBill Taylor 	}
694*9e39c5baSBill Taylor 
695*9e39c5baSBill Taylor 	/* Release the pci config space access handle */
696*9e39c5baSBill Taylor 	if (state->hs_fm_pcihdl) {
697*9e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_pcihdl);
698*9e39c5baSBill Taylor 		state->hs_fm_pcihdl = NULL;
699*9e39c5baSBill Taylor 	}
700*9e39c5baSBill Taylor 
701*9e39c5baSBill Taylor 	/* Release the cmd protected access handle */
702*9e39c5baSBill Taylor 	if (state->hs_fm_cmdhdl) {
703*9e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_cmdhdl);
704*9e39c5baSBill Taylor 		state->hs_fm_cmdhdl = NULL;
705*9e39c5baSBill Taylor 	}
706*9e39c5baSBill Taylor 
707*9e39c5baSBill Taylor 	/* Release the uar fma-protected access handle */
708*9e39c5baSBill Taylor 	if (state->hs_fm_uarhdl) {
709*9e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_uarhdl);
710*9e39c5baSBill Taylor 		state->hs_fm_uarhdl = NULL;
711*9e39c5baSBill Taylor 	}
712*9e39c5baSBill Taylor 
713*9e39c5baSBill Taylor 	/* Enable the Hermon interrupt again */
714*9e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
715*9e39c5baSBill Taylor 		if (ddi_intr_block_enable
716*9e39c5baSBill Taylor 		    (&state->hs_intrmsi_hdl[0], 1) != DDI_SUCCESS) {
717*9e39c5baSBill Taylor 			return (DDI_FAILURE);
718*9e39c5baSBill Taylor 		}
719*9e39c5baSBill Taylor 	} else {
720*9e39c5baSBill Taylor 		if (ddi_intr_enable
721*9e39c5baSBill Taylor 		    (state->hs_intrmsi_hdl[0]) != DDI_SUCCESS) {
722*9e39c5baSBill Taylor 			return (DDI_FAILURE);
723*9e39c5baSBill Taylor 		}
724*9e39c5baSBill Taylor 	}
725*9e39c5baSBill Taylor 
726*9e39c5baSBill Taylor 	/* Restart the poll thread */
727*9e39c5baSBill Taylor 	state->hs_fm_poll_suspend = B_FALSE;
728*9e39c5baSBill Taylor 
729*9e39c5baSBill Taylor 	return (DDI_SUCCESS);
730*9e39c5baSBill Taylor 
731*9e39c5baSBill Taylor error:
732*9e39c5baSBill Taylor 	/* Enable the Hermon interrupt again */
733*9e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
734*9e39c5baSBill Taylor 		(void) ddi_intr_block_enable(&state->hs_intrmsi_hdl[0], 1);
735*9e39c5baSBill Taylor 	} else {
736*9e39c5baSBill Taylor 		(void) ddi_intr_enable(state->hs_intrmsi_hdl[0]);
737*9e39c5baSBill Taylor 	}
738*9e39c5baSBill Taylor 	return (DDI_FAILURE);
739*9e39c5baSBill Taylor }
740*9e39c5baSBill Taylor 
741*9e39c5baSBill Taylor 
742*9e39c5baSBill Taylor /*
743*9e39c5baSBill Taylor  *  int
744*9e39c5baSBill Taylor  *  hermon_regs_map_setup(hermon_state_t *state, uint_t rnumber, caddr_t *addrp,
745*9e39c5baSBill Taylor  *	offset_t offset, offset_t len, ddi_device_acc_attr_t *accattrp,
746*9e39c5baSBill Taylor  *	ddi_acc_handle_t *handle)
747*9e39c5baSBill Taylor  *
748*9e39c5baSBill Taylor  *  Overview
749*9e39c5baSBill Taylor  *      This is a wrapper function of i_hca_regs_map_setup() for Hermon FM so
750*9e39c5baSBill Taylor  *      that it calls i_hca_regs_map_setup() inside after it checks the
751*9e39c5baSBill Taylor  *      "fm_disable" configuration property. If the "fm_disable" is described
752*9e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls ddi_regs_map_setup()
753*9e39c5baSBill Taylor  *      directly instead.
754*9e39c5baSBill Taylor  *      See i_hca_regs_map_setup() in detail.
755*9e39c5baSBill Taylor  *
756*9e39c5baSBill Taylor  *  Argument
757*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
758*9e39c5baSBill Taylor  *      rnumber: index number to the register address space set
759*9e39c5baSBill Taylor  *      addrp: platform-dependent value (same as ddi_regs_map_setup())
760*9e39c5baSBill Taylor  *      offset: offset into the register address space
761*9e39c5baSBill Taylor  *      len: address space length to be mapped
762*9e39c5baSBill Taylor  *      accattrp: pointer to device access attribute structure
763*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
764*9e39c5baSBill Taylor  *
765*9e39c5baSBill Taylor  *  Return value
766*9e39c5baSBill Taylor  *      ddi function status value which are:
767*9e39c5baSBill Taylor  *      	DDI_SUCCESS
768*9e39c5baSBill Taylor  *      	DDI_FAILURE
769*9e39c5baSBill Taylor  *      	DDI_ME_RNUMBER_RNGE
770*9e39c5baSBill Taylor  *      	DDI_REGS_ACC_CONFLICT
771*9e39c5baSBill Taylor  *
772*9e39c5baSBill Taylor  *  Caller's context
773*9e39c5baSBill Taylor  *      hermon_regs_map_setup() can be called in user or kernel context only.
774*9e39c5baSBill Taylor  */
775*9e39c5baSBill Taylor int
776*9e39c5baSBill Taylor hermon_regs_map_setup(hermon_state_t *state, uint_t rnumber, caddr_t *addrp,
777*9e39c5baSBill Taylor 	offset_t offset, offset_t len, ddi_device_acc_attr_t *accattrp,
778*9e39c5baSBill Taylor 	ddi_acc_handle_t *handle)
779*9e39c5baSBill Taylor {
780*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
781*9e39c5baSBill Taylor 		return (ddi_regs_map_setup(state->hs_dip, rnumber, addrp,
782*9e39c5baSBill Taylor 		    offset, len, accattrp, handle));
783*9e39c5baSBill Taylor 	} else {
784*9e39c5baSBill Taylor 		return (i_hca_regs_map_setup(state->hs_fm_hca_fm, state->hs_dip,
785*9e39c5baSBill Taylor 		    rnumber, addrp, offset, len, accattrp, handle));
786*9e39c5baSBill Taylor 	}
787*9e39c5baSBill Taylor }
788*9e39c5baSBill Taylor 
789*9e39c5baSBill Taylor 
790*9e39c5baSBill Taylor /*
791*9e39c5baSBill Taylor  *  void
792*9e39c5baSBill Taylor  *  hermon_regs_map_free(hermon_state_t *state, ddi_acc_handle_t *handlep)
793*9e39c5baSBill Taylor  *
794*9e39c5baSBill Taylor  *  Overview
795*9e39c5baSBill Taylor  *      This is a wrapper function of i_hca_regs_map_free() for Hermon FM so
796*9e39c5baSBill Taylor  *      that it calls i_hca_regs_map_free() inside after it checks the
797*9e39c5baSBill Taylor  *      "fm_disable" configuration property. If the "fm_disable" is described
798*9e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls ddi_regs_map_fre()
799*9e39c5baSBill Taylor  *      directly instead.  See i_hca_regs_map_free() in detail.
800*9e39c5baSBill Taylor  *
801*9e39c5baSBill Taylor  *  Argument
802*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
803*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
804*9e39c5baSBill Taylor  *
805*9e39c5baSBill Taylor  *  Return value
806*9e39c5baSBill Taylor  *      Nothing
807*9e39c5baSBill Taylor  *
808*9e39c5baSBill Taylor  *  Caller's context
809*9e39c5baSBill Taylor  *      hermon_regs_map_free() can be called in user or kernel context only.
810*9e39c5baSBill Taylor  *
811*9e39c5baSBill Taylor  *  Note that the handle passed to hermon_regs_map_free() is NULL-cleared
812*9e39c5baSBill Taylor  *  after this function is called.
813*9e39c5baSBill Taylor  */
814*9e39c5baSBill Taylor void
815*9e39c5baSBill Taylor hermon_regs_map_free(hermon_state_t *state, ddi_acc_handle_t *handle)
816*9e39c5baSBill Taylor {
817*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
818*9e39c5baSBill Taylor 		ddi_regs_map_free(handle);
819*9e39c5baSBill Taylor 		*handle = NULL;
820*9e39c5baSBill Taylor 	} else {
821*9e39c5baSBill Taylor 		i_hca_regs_map_free(state->hs_fm_hca_fm, handle);
822*9e39c5baSBill Taylor 	}
823*9e39c5baSBill Taylor }
824*9e39c5baSBill Taylor 
825*9e39c5baSBill Taylor 
826*9e39c5baSBill Taylor /*
827*9e39c5baSBill Taylor  *  int
828*9e39c5baSBill Taylor  *  hermon_pci_config_setup(hermon_state_t *state, ddi_acc_handle_t *handle)
829*9e39c5baSBill Taylor  *
830*9e39c5baSBill Taylor  *  Overview
831*9e39c5baSBill Taylor  *      This is a wrapper function of i_hca_pci_config_setup() for Hermon FM so
832*9e39c5baSBill Taylor  *      that it calls i_hca_pci_config_setup() inside after it checks the
833*9e39c5baSBill Taylor  *      "fm-disable" configuration property. If the "fm_disable" is described
834*9e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls pci_config_setup()
835*9e39c5baSBill Taylor  *      directly instead. See i_hca_pci_config_setup() in detail.
836*9e39c5baSBill Taylor  *
837*9e39c5baSBill Taylor  *  Argument
838*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
839*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
840*9e39c5baSBill Taylor  *
841*9e39c5baSBill Taylor  *  Return value
842*9e39c5baSBill Taylor  *      ddi function status value which are:
843*9e39c5baSBill Taylor  *      	DDI_SUCCESS
844*9e39c5baSBill Taylor  *      	DDI_FAILURE
845*9e39c5baSBill Taylor  *
846*9e39c5baSBill Taylor  *  Caller's context
847*9e39c5baSBill Taylor  *      hermon_pci_config_setup() can be called in user or kernel context only.
848*9e39c5baSBill Taylor  */
849*9e39c5baSBill Taylor int
850*9e39c5baSBill Taylor hermon_pci_config_setup(hermon_state_t *state, ddi_acc_handle_t *handle)
851*9e39c5baSBill Taylor {
852*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
853*9e39c5baSBill Taylor 		return (pci_config_setup(state->hs_dip, handle));
854*9e39c5baSBill Taylor 	} else {
855*9e39c5baSBill Taylor 		/* Check Hermon FM and Solaris FMA capability flags */
856*9e39c5baSBill Taylor 		ASSERT((hermon_get_state(state) & HCA_PIO_FM &&
857*9e39c5baSBill Taylor 		    DDI_FM_ACC_ERR_CAP(ddi_fm_capable(state->hs_dip))) ||
858*9e39c5baSBill Taylor 		    (!(hermon_get_state(state) & HCA_PIO_FM) &&
859*9e39c5baSBill Taylor 		    !DDI_FM_ACC_ERR_CAP(ddi_fm_capable(state->hs_dip))));
860*9e39c5baSBill Taylor 		return (i_hca_pci_config_setup(state->hs_fm_hca_fm,
861*9e39c5baSBill Taylor 		    state->hs_dip, handle));
862*9e39c5baSBill Taylor 	}
863*9e39c5baSBill Taylor }
864*9e39c5baSBill Taylor 
865*9e39c5baSBill Taylor 
866*9e39c5baSBill Taylor /*
867*9e39c5baSBill Taylor  *  void
868*9e39c5baSBill Taylor  *  hermon_pci_config_teardown(hermon_state_t *state, ddi_acc_handle_t *handle)
869*9e39c5baSBill Taylor  *
870*9e39c5baSBill Taylor  *  Overview
871*9e39c5baSBill Taylor  *      This is a wrapper function of i_hca_pci_config_teardown() for Hermon
872*9e39c5baSBill Taylor  *      FM so that it calls i_hca_pci_config_teardown() inside after it checks
873*9e39c5baSBill Taylor  *      the "fm-disable" configuration property. If the "fm_disable" is
874*9e39c5baSBill Taylor  *      described in /kernel/drv/hermon.conf, the function calls
875*9e39c5baSBill Taylor  *      pci_config_teardown() directly instead.
876*9e39c5baSBill Taylor  *      See i_hca_pci_config_teardown() in detail.
877*9e39c5baSBill Taylor  *
878*9e39c5baSBill Taylor  *  Argument
879*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
880*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
881*9e39c5baSBill Taylor  *
882*9e39c5baSBill Taylor  *  Return value
883*9e39c5baSBill Taylor  *      Nothing
884*9e39c5baSBill Taylor  *
885*9e39c5baSBill Taylor  *  Caller's context
886*9e39c5baSBill Taylor  *      hermon_pci_config_teardown() can be called in user or kernel context
887*9e39c5baSBill Taylor  *      only.
888*9e39c5baSBill Taylor  */
889*9e39c5baSBill Taylor void
890*9e39c5baSBill Taylor hermon_pci_config_teardown(hermon_state_t *state, ddi_acc_handle_t *handle)
891*9e39c5baSBill Taylor {
892*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
893*9e39c5baSBill Taylor 		pci_config_teardown(handle);
894*9e39c5baSBill Taylor 		*handle = NULL;
895*9e39c5baSBill Taylor 	} else {
896*9e39c5baSBill Taylor 		i_hca_pci_config_teardown(state->hs_fm_hca_fm, handle);
897*9e39c5baSBill Taylor 	}
898*9e39c5baSBill Taylor }
899*9e39c5baSBill Taylor 
900*9e39c5baSBill Taylor 
901*9e39c5baSBill Taylor /*
902*9e39c5baSBill Taylor  *  boolean_t
903*9e39c5baSBill Taylor  *  hermon_init_failure(hermon_state_t *state)
904*9e39c5baSBill Taylor  *
905*9e39c5baSBill Taylor  *  Overview
906*9e39c5baSBill Taylor  *      hermon_init_failure() tells if HW errors are detected in
907*9e39c5baSBill Taylor  *      the Hermon driver attach.
908*9e39c5baSBill Taylor  *
909*9e39c5baSBill Taylor  *  Argument
910*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
911*9e39c5baSBill Taylor  *
912*9e39c5baSBill Taylor  *  Return value
913*9e39c5baSBill Taylor  *  	B_TRUE		HW errors detected during attach
914*9e39c5baSBill Taylor  *  	B_FALSE		No HW errors during attach
915*9e39c5baSBill Taylor  *
916*9e39c5baSBill Taylor  *  Caller's context
917*9e39c5baSBill Taylor  *      hermon_init_failure() can be called in user, kernel, interrupt
918*9e39c5baSBill Taylor  *      context or high interrupt context.
919*9e39c5baSBill Taylor  */
920*9e39c5baSBill Taylor boolean_t
921*9e39c5baSBill Taylor hermon_init_failure(hermon_state_t *state)
922*9e39c5baSBill Taylor {
923*9e39c5baSBill Taylor 	ddi_acc_handle_t hdl;
924*9e39c5baSBill Taylor 	ddi_fm_error_t derr;
925*9e39c5baSBill Taylor 
926*9e39c5baSBill Taylor 	if (!(hermon_get_state(state) & HCA_PIO_FM))
927*9e39c5baSBill Taylor 		return (B_FALSE);
928*9e39c5baSBill Taylor 
929*9e39c5baSBill Taylor 	hdl = hermon_get_uarhdl(state);
930*9e39c5baSBill Taylor 	/* Get the PIO error against UAR I/O space */
931*9e39c5baSBill Taylor 	ddi_fm_acc_err_get(hdl, &derr, DDI_FME_VERSION);
932*9e39c5baSBill Taylor 	if (derr.fme_status != DDI_FM_OK) {
933*9e39c5baSBill Taylor 		return (B_TRUE);
934*9e39c5baSBill Taylor 	}
935*9e39c5baSBill Taylor 
936*9e39c5baSBill Taylor 	hdl = hermon_get_cmdhdl(state);
937*9e39c5baSBill Taylor 	/* Get the PIO error againsts CMD I/O space */
938*9e39c5baSBill Taylor 	ddi_fm_acc_err_get(hdl, &derr, DDI_FME_VERSION);
939*9e39c5baSBill Taylor 	if (derr.fme_status != DDI_FM_OK) {
940*9e39c5baSBill Taylor 		return (B_TRUE);
941*9e39c5baSBill Taylor 	}
942*9e39c5baSBill Taylor 
943*9e39c5baSBill Taylor 	return (B_FALSE);
944*9e39c5baSBill Taylor }
945*9e39c5baSBill Taylor 
946*9e39c5baSBill Taylor 
947*9e39c5baSBill Taylor /*
948*9e39c5baSBill Taylor  *  void
949*9e39c5baSBill Taylor  *  hermon_fm_ereport(hermon_state_t *state, int type, int detail)
950*9e39c5baSBill Taylor  *
951*9e39c5baSBill Taylor  *  Overview
952*9e39c5baSBill Taylor  *      hermon_fm_ereport() is a Hermon FM ereport function used
953*9e39c5baSBill Taylor  *      to issue a Solaris FMA ereport. See Hermon FM comments at the
954*9e39c5baSBill Taylor  *      beginning of this file in detail.
955*9e39c5baSBill Taylor  *
956*9e39c5baSBill Taylor  *  Argument
957*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
958*9e39c5baSBill Taylor  *      type: error type
959*9e39c5baSBill Taylor  *		HCA_SYS_ERR	FMA reporting HW error
960*9e39c5baSBill Taylor  *		HCA_IBA_ERR	HCA specific HW error
961*9e39c5baSBill Taylor  *      detail: HW error hint implying which ereport is issued
962*9e39c5baSBill Taylor  * 		HCA_ERR_TRANSIENT	HW transienet error
963*9e39c5baSBill Taylor  * 		HCA_ERR_NON_FATAL	HW persistent error
964*9e39c5baSBill Taylor  * 		HCA_ERR_FATAL		HW fatal error
965*9e39c5baSBill Taylor  * 		HCA_ERR_SRV_LOST	IB service lost due to HW error
966*9e39c5baSBill Taylor  * 		HCA_ERR_DEGRADED	Hermon driver and/or uDAPL degraded
967*9e39c5baSBill Taylor  * 					due to HW error
968*9e39c5baSBill Taylor  * 		HCA_ERR_IOCTL		HW error detected in user conetxt
969*9e39c5baSBill Taylor  * 					(especially in ioctl())
970*9e39c5baSBill Taylor  *
971*9e39c5baSBill Taylor  *  Return value
972*9e39c5baSBill Taylor  *      Nothing
973*9e39c5baSBill Taylor  *
974*9e39c5baSBill Taylor  *  Caller's context
975*9e39c5baSBill Taylor  *      hermon_fm_ereport() can be called in user, kernel, interrupt context
976*9e39c5baSBill Taylor  *      or high interrupt context.
977*9e39c5baSBill Taylor  */
978*9e39c5baSBill Taylor void
979*9e39c5baSBill Taylor hermon_fm_ereport(hermon_state_t *state, int type, int detail)
980*9e39c5baSBill Taylor {
981*9e39c5baSBill Taylor 	/*
982*9e39c5baSBill Taylor 	 * If hermon_fm_diable is set or there is no FM ereport service
983*9e39c5baSBill Taylor 	 * provided, then skip the rest.
984*9e39c5baSBill Taylor 	 */
985*9e39c5baSBill Taylor 	if (state->hs_fm_disable ||
986*9e39c5baSBill Taylor 	    !(hermon_get_state(state) & HCA_EREPORT_FM)) {
987*9e39c5baSBill Taylor 		return;
988*9e39c5baSBill Taylor 	}
989*9e39c5baSBill Taylor 
990*9e39c5baSBill Taylor 	switch (type) {
991*9e39c5baSBill Taylor 
992*9e39c5baSBill Taylor 	case HCA_SYS_ERR:
993*9e39c5baSBill Taylor 		switch (detail) {
994*9e39c5baSBill Taylor 		case HCA_ERR_TRANSIENT:
995*9e39c5baSBill Taylor 		case HCA_ERR_IOCTL:
996*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
997*9e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
998*9e39c5baSBill Taylor 			break;
999*9e39c5baSBill Taylor 		case HCA_ERR_NON_FATAL:
1000*9e39c5baSBill Taylor 			/* Nothing */
1001*9e39c5baSBill Taylor 			break;
1002*9e39c5baSBill Taylor 		case HCA_ERR_SRV_LOST:
1003*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1004*9e39c5baSBill Taylor 			    DDI_SERVICE_LOST);
1005*9e39c5baSBill Taylor 			break;
1006*9e39c5baSBill Taylor 		case HCA_ERR_DEGRADED:
1007*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1008*9e39c5baSBill Taylor 			    DDI_SERVICE_DEGRADED);
1009*9e39c5baSBill Taylor 			break;
1010*9e39c5baSBill Taylor 		case HCA_ERR_FATAL:
1011*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1012*9e39c5baSBill Taylor 			    DDI_SERVICE_LOST);
1013*9e39c5baSBill Taylor 			state->hs_fm_async_fatal = B_TRUE;
1014*9e39c5baSBill Taylor 			break;
1015*9e39c5baSBill Taylor 		default:
1016*9e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: Unknown error. "
1017*9e39c5baSBill Taylor 			    "type = %d, detail = %d\n.", type, detail);
1018*9e39c5baSBill Taylor 		}
1019*9e39c5baSBill Taylor 		break;
1020*9e39c5baSBill Taylor 
1021*9e39c5baSBill Taylor 	case HCA_IBA_ERR:
1022*9e39c5baSBill Taylor 		switch (detail) {
1023*9e39c5baSBill Taylor 		case HCA_ERR_TRANSIENT:
1024*9e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, type,
1025*9e39c5baSBill Taylor 			    DDI_FM_DEVICE_INTERN_UNCORR);
1026*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1027*9e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
1028*9e39c5baSBill Taylor 			break;
1029*9e39c5baSBill Taylor 		case HCA_ERR_SRV_LOST:
1030*9e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: not supported "
1031*9e39c5baSBill Taylor 			    "error. type = %d, detail = %d\n.", type, detail);
1032*9e39c5baSBill Taylor 			break;
1033*9e39c5baSBill Taylor 		case HCA_ERR_DEGRADED:
1034*9e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, type,
1035*9e39c5baSBill Taylor 			    DDI_FM_DEVICE_INTERN_UNCORR);
1036*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1037*9e39c5baSBill Taylor 			    DDI_SERVICE_DEGRADED);
1038*9e39c5baSBill Taylor 			break;
1039*9e39c5baSBill Taylor 		case HCA_ERR_IOCTL:
1040*9e39c5baSBill Taylor 		case HCA_ERR_NON_FATAL:
1041*9e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, type,
1042*9e39c5baSBill Taylor 			    DDI_FM_DEVICE_INTERN_UNCORR);
1043*9e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
1044*9e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
1045*9e39c5baSBill Taylor 			break;
1046*9e39c5baSBill Taylor 		case HCA_ERR_FATAL:
1047*9e39c5baSBill Taylor 			if (hermon_get_state(state) & HCA_PIO_FM) {
1048*9e39c5baSBill Taylor 				if (servicing_interrupt()) {
1049*9e39c5baSBill Taylor 					atomic_inc_32(&state->
1050*9e39c5baSBill Taylor 					    hs_fm_async_errcnt);
1051*9e39c5baSBill Taylor 				} else {
1052*9e39c5baSBill Taylor 					i_hca_fm_ereport(state->hs_dip, type,
1053*9e39c5baSBill Taylor 					    DDI_FM_DEVICE_INTERN_UNCORR);
1054*9e39c5baSBill Taylor 					ddi_fm_service_impact(state->hs_dip,
1055*9e39c5baSBill Taylor 					    DDI_SERVICE_LOST);
1056*9e39c5baSBill Taylor 				}
1057*9e39c5baSBill Taylor 				state->hs_fm_async_fatal = B_TRUE;
1058*9e39c5baSBill Taylor 			} else {
1059*9e39c5baSBill Taylor 				i_hca_fm_ereport(state->hs_dip, type,
1060*9e39c5baSBill Taylor 				    DDI_FM_DEVICE_INTERN_UNCORR);
1061*9e39c5baSBill Taylor 				ddi_fm_service_impact(state->hs_dip,
1062*9e39c5baSBill Taylor 				    DDI_SERVICE_LOST);
1063*9e39c5baSBill Taylor 				cmn_err(CE_PANIC,
1064*9e39c5baSBill Taylor 				    "Hermon Fatal Internal Error. "
1065*9e39c5baSBill Taylor 				    "Hermon state=0x%p", (void *)state);
1066*9e39c5baSBill Taylor 			}
1067*9e39c5baSBill Taylor 			break;
1068*9e39c5baSBill Taylor 		default:
1069*9e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: Unknown error. "
1070*9e39c5baSBill Taylor 			    "type = %d, detail = %d\n.", type, detail);
1071*9e39c5baSBill Taylor 		}
1072*9e39c5baSBill Taylor 		break;
1073*9e39c5baSBill Taylor 
1074*9e39c5baSBill Taylor 	default:
1075*9e39c5baSBill Taylor 		cmn_err(CE_WARN, "hermon_fm_ereport: Unknown type "
1076*9e39c5baSBill Taylor 		    "type = %d, detail = %d\n.", type, detail);
1077*9e39c5baSBill Taylor 		break;
1078*9e39c5baSBill Taylor 	}
1079*9e39c5baSBill Taylor }
1080*9e39c5baSBill Taylor 
1081*9e39c5baSBill Taylor 
1082*9e39c5baSBill Taylor /*
1083*9e39c5baSBill Taylor  *  uchar_t
1084*9e39c5baSBill Taylor  *  hermon_devacc_attr_version(hermon_state_t *)
1085*9e39c5baSBill Taylor  *
1086*9e39c5baSBill Taylor  *  Overview
1087*9e39c5baSBill Taylor  *      hermon_devacc_attr_version() returns the ddi device attribute
1088*9e39c5baSBill Taylor  *      version.
1089*9e39c5baSBill Taylor  *
1090*9e39c5baSBill Taylor  *  Argument
1091*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1092*9e39c5baSBill Taylor  *
1093*9e39c5baSBill Taylor  *  Return value
1094*9e39c5baSBill Taylor  *      dev_acc_attr_version value
1095*9e39c5baSBill Taylor  *      	DDI_DEVICE_ATTR_V0	Hermon FM disabled
1096*9e39c5baSBill Taylor  *      	DDI_DEVICE_ATTR_V1	Hermon FM enabled
1097*9e39c5baSBill Taylor  *
1098*9e39c5baSBill Taylor  *  Caller's context
1099*9e39c5baSBill Taylor  *      hermon_devacc_attr_version() can be called in user, kernel, interrupt
1100*9e39c5baSBill Taylor  *      context or high interrupt context.
1101*9e39c5baSBill Taylor  */
1102*9e39c5baSBill Taylor ushort_t
1103*9e39c5baSBill Taylor hermon_devacc_attr_version(hermon_state_t *state)
1104*9e39c5baSBill Taylor {
1105*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
1106*9e39c5baSBill Taylor 		return (DDI_DEVICE_ATTR_V0);
1107*9e39c5baSBill Taylor 	} else {
1108*9e39c5baSBill Taylor 		return (DDI_DEVICE_ATTR_V1);
1109*9e39c5baSBill Taylor 	}
1110*9e39c5baSBill Taylor }
1111*9e39c5baSBill Taylor 
1112*9e39c5baSBill Taylor 
1113*9e39c5baSBill Taylor /*
1114*9e39c5baSBill Taylor  *  uchar_t
1115*9e39c5baSBill Taylor  *  hermon_devacc_attr_access(hermon_state_t *)
1116*9e39c5baSBill Taylor  *
1117*9e39c5baSBill Taylor  *  Overview
1118*9e39c5baSBill Taylor  *      hermon_devacc_attr_access() returns devacc_attr_access error
1119*9e39c5baSBill Taylor  *      protection types.
1120*9e39c5baSBill Taylor  *
1121*9e39c5baSBill Taylor  *  Argument
1122*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1123*9e39c5baSBill Taylor  *
1124*9e39c5baSBill Taylor  *  Return value
1125*9e39c5baSBill Taylor  *      dev_acc_attr_access error protection type
1126*9e39c5baSBill Taylor  *      	DDI_DEFAULT_ACC		Hermon FM disabled for PIO
1127*9e39c5baSBill Taylor  *      	DDI_FLAGERR_ACC		Hermon FM enabled for PIO
1128*9e39c5baSBill Taylor  *
1129*9e39c5baSBill Taylor  *  Caller's context
1130*9e39c5baSBill Taylor  *      hermon_devacc_attr_access() can be called in user, kernel, interrupt
1131*9e39c5baSBill Taylor  *      context or high interrupt context.
1132*9e39c5baSBill Taylor  */
1133*9e39c5baSBill Taylor uchar_t
1134*9e39c5baSBill Taylor hermon_devacc_attr_access(hermon_state_t *state)
1135*9e39c5baSBill Taylor {
1136*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
1137*9e39c5baSBill Taylor 		return (DDI_DEFAULT_ACC);
1138*9e39c5baSBill Taylor 	} else {
1139*9e39c5baSBill Taylor 		return (DDI_FLAGERR_ACC);
1140*9e39c5baSBill Taylor 	}
1141*9e39c5baSBill Taylor }
1142*9e39c5baSBill Taylor 
1143*9e39c5baSBill Taylor 
1144*9e39c5baSBill Taylor /*
1145*9e39c5baSBill Taylor  *  int
1146*9e39c5baSBill Taylor  *  hermon_PIO_start(hermon_state_t *state, ddi_acc_handle_t handle,
1147*9e39c5baSBill Taylor  *      hermon_test_t *tst)
1148*9e39c5baSBill Taylor  *
1149*9e39c5baSBill Taylor  *  Overview
1150*9e39c5baSBill Taylor  *      hermon_PIO_start() should be called before Hermon driver issues PIOs
1151*9e39c5baSBill Taylor  *      against I/O space. If Hermon FM is disabled, this function returns
1152*9e39c5baSBill Taylor  *      HCA_PIO_OK always. See i_hca_pio_start() in detail.
1153*9e39c5baSBill Taylor  *
1154*9e39c5baSBill Taylor  *  Argument
1155*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1156*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
1157*9e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
1158*9e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
1159*9e39c5baSBill Taylor  *
1160*9e39c5baSBill Taylor  *  Return value
1161*9e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
1162*9e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
1163*9e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
1164*9e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
1165*9e39c5baSBill Taylor  *
1166*9e39c5baSBill Taylor  *  Caller's context
1167*9e39c5baSBill Taylor  *      hermon_PIO_start() can be called in user, kernel or interrupt context.
1168*9e39c5baSBill Taylor  */
1169*9e39c5baSBill Taylor int
1170*9e39c5baSBill Taylor hermon_PIO_start(hermon_state_t *state, ddi_acc_handle_t handle,
1171*9e39c5baSBill Taylor     hermon_test_t *tst)
1172*9e39c5baSBill Taylor {
1173*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
1174*9e39c5baSBill Taylor 		return (HCA_PIO_OK);
1175*9e39c5baSBill Taylor 	} else {
1176*9e39c5baSBill Taylor 		struct i_hca_acc_handle *handlep =
1177*9e39c5baSBill Taylor 		    i_hca_get_acc_handle(state->hs_fm_hca_fm, handle);
1178*9e39c5baSBill Taylor 		ASSERT(handlep != NULL);
1179*9e39c5baSBill Taylor 		return (i_hca_pio_start(state->hs_dip, handlep, tst));
1180*9e39c5baSBill Taylor 	}
1181*9e39c5baSBill Taylor }
1182*9e39c5baSBill Taylor 
1183*9e39c5baSBill Taylor 
1184*9e39c5baSBill Taylor /*
1185*9e39c5baSBill Taylor  *  int
1186*9e39c5baSBill Taylor  *  hermon_PIO_end(hermon_state_t *state, ddi_acc_handle_t handle, int *cnt,
1187*9e39c5baSBill Taylor  *      hermon_test_t *tst)
1188*9e39c5baSBill Taylor  *
1189*9e39c5baSBill Taylor  *  Overview
1190*9e39c5baSBill Taylor  *      hermon_PIO_end() should be called after Hermon driver issues PIOs
1191*9e39c5baSBill Taylor  *      against I/O space. If Hermon FM is disabled, this function returns
1192*9e39c5baSBill Taylor  *      HCA_PIO_OK always. See i_hca_pio_end() in detail.
1193*9e39c5baSBill Taylor  *
1194*9e39c5baSBill Taylor  *  Argument
1195*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1196*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
1197*9e39c5baSBill Taylor  *	cnt: pointer to the counter variable which holds the nubmer of retry
1198*9e39c5baSBill Taylor  *	     (HCA_PIO_RETRY_CNT) when a HW error is detected.
1199*9e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
1200*9e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
1201*9e39c5baSBill Taylor  *
1202*9e39c5baSBill Taylor  *  Return value
1203*9e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
1204*9e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
1205*9e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
1206*9e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
1207*9e39c5baSBill Taylor  *
1208*9e39c5baSBill Taylor  *  Caller's context
1209*9e39c5baSBill Taylor  *      hermon_PIO_end() can be called in user, kernel or interrupt context.
1210*9e39c5baSBill Taylor  */
1211*9e39c5baSBill Taylor int
1212*9e39c5baSBill Taylor hermon_PIO_end(hermon_state_t *state, ddi_acc_handle_t handle, int *cnt,
1213*9e39c5baSBill Taylor     hermon_test_t *tst)
1214*9e39c5baSBill Taylor {
1215*9e39c5baSBill Taylor 	if (state->hs_fm_disable) {
1216*9e39c5baSBill Taylor 		return (HCA_PIO_OK);
1217*9e39c5baSBill Taylor 	} else {
1218*9e39c5baSBill Taylor 		struct i_hca_acc_handle *handlep =
1219*9e39c5baSBill Taylor 		    i_hca_get_acc_handle(state->hs_fm_hca_fm, handle);
1220*9e39c5baSBill Taylor 		ASSERT(handlep != NULL);
1221*9e39c5baSBill Taylor 		return (i_hca_pio_end(state->hs_dip, handlep, cnt, tst));
1222*9e39c5baSBill Taylor 	}
1223*9e39c5baSBill Taylor }
1224*9e39c5baSBill Taylor 
1225*9e39c5baSBill Taylor 
1226*9e39c5baSBill Taylor /*
1227*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1228*9e39c5baSBill Taylor  *  hermon_get_cmdhdl(hermon_state_t *state)
1229*9e39c5baSBill Taylor  *
1230*9e39c5baSBill Taylor  *  Overview
1231*9e39c5baSBill Taylor  *      hermon_get_cmdhdl() returns either the fma-protected access handle or
1232*9e39c5baSBill Taylor  *      the regular ddi-access handle depending on the Hermon FM state for
1233*9e39c5baSBill Taylor  *      Hermon command I/O space.
1234*9e39c5baSBill Taylor  *
1235*9e39c5baSBill Taylor  *  Argument
1236*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1237*9e39c5baSBill Taylor  *
1238*9e39c5baSBill Taylor  *  Return value
1239*9e39c5baSBill Taylor  *  	the access handle for pio requests
1240*9e39c5baSBill Taylor  *
1241*9e39c5baSBill Taylor  *  Caller's context
1242*9e39c5baSBill Taylor  *      hermon_get_cmdhdl() can be called in user, kernel, interrupt context
1243*9e39c5baSBill Taylor  *      or high interrupt context.
1244*9e39c5baSBill Taylor  */
1245*9e39c5baSBill Taylor ddi_acc_handle_t
1246*9e39c5baSBill Taylor hermon_get_cmdhdl(hermon_state_t *state)
1247*9e39c5baSBill Taylor {
1248*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_PIO_FM ?
1249*9e39c5baSBill Taylor 	    state->hs_fm_cmdhdl : state->hs_reg_cmdhdl);
1250*9e39c5baSBill Taylor }
1251*9e39c5baSBill Taylor 
1252*9e39c5baSBill Taylor 
1253*9e39c5baSBill Taylor /*
1254*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1255*9e39c5baSBill Taylor  *  hermon_get_uarhdl(hermon_state_t *state)
1256*9e39c5baSBill Taylor  *
1257*9e39c5baSBill Taylor  *  Overview
1258*9e39c5baSBill Taylor  *      hermon_get_uarhdl() returns either the fma-protected access handle or
1259*9e39c5baSBill Taylor  *      the regular ddi-access handle depending on the Hermon FM state for
1260*9e39c5baSBill Taylor  *      Hermon UAR I/O space.
1261*9e39c5baSBill Taylor  *
1262*9e39c5baSBill Taylor  *  Argument
1263*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1264*9e39c5baSBill Taylor  *
1265*9e39c5baSBill Taylor  *  Return value
1266*9e39c5baSBill Taylor  *  	the access handle for pio requests
1267*9e39c5baSBill Taylor  *
1268*9e39c5baSBill Taylor  *  Caller's context
1269*9e39c5baSBill Taylor  *      hermon_get_uarhdl() can be called in user, kernel, interrupt context
1270*9e39c5baSBill Taylor  *      or high interrupt context.
1271*9e39c5baSBill Taylor  */
1272*9e39c5baSBill Taylor ddi_acc_handle_t
1273*9e39c5baSBill Taylor hermon_get_uarhdl(hermon_state_t *state)
1274*9e39c5baSBill Taylor {
1275*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_PIO_FM ?
1276*9e39c5baSBill Taylor 	    state->hs_fm_uarhdl : state->hs_reg_uarhdl);
1277*9e39c5baSBill Taylor }
1278*9e39c5baSBill Taylor 
1279*9e39c5baSBill Taylor 
1280*9e39c5baSBill Taylor /*
1281*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1282*9e39c5baSBill Taylor  *  hermon_rsrc_alloc_uarhdl(hermon_state_t *state)
1283*9e39c5baSBill Taylor  *
1284*9e39c5baSBill Taylor  *  Overview
1285*9e39c5baSBill Taylor  *      hermon_rsrc_alloc_uarhdl() returns either the fma-protected access
1286*9e39c5baSBill Taylor  *      handle or the regular ddi-access handle depending on the Hermon FM
1287*9e39c5baSBill Taylor  *      state for Hermon UAR I/O space as well as hermon_get_uarhdl(), but
1288*9e39c5baSBill Taylor  *      this function is dedicated to the UAR resource allocator.
1289*9e39c5baSBill Taylor  *
1290*9e39c5baSBill Taylor  *  Argument
1291*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1292*9e39c5baSBill Taylor  *
1293*9e39c5baSBill Taylor  *  Return value
1294*9e39c5baSBill Taylor  *  	the access handle for pio requests
1295*9e39c5baSBill Taylor  *
1296*9e39c5baSBill Taylor  *  Caller's context
1297*9e39c5baSBill Taylor  *      hermon_rsrc_alloc_uarhdl() can be called in user, kernel, interrupt
1298*9e39c5baSBill Taylor  *      or high interrupt context.
1299*9e39c5baSBill Taylor  */
1300*9e39c5baSBill Taylor ddi_acc_handle_t
1301*9e39c5baSBill Taylor hermon_rsrc_alloc_uarhdl(hermon_state_t *state)
1302*9e39c5baSBill Taylor {
1303*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
1304*9e39c5baSBill Taylor 	    state->hs_fm_uarhdl : state->hs_reg_uarhdl);
1305*9e39c5baSBill Taylor }
1306*9e39c5baSBill Taylor 
1307*9e39c5baSBill Taylor /*
1308*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1309*9e39c5baSBill Taylor  *  hermon_get_pcihdl(hermon_state_t *state)
1310*9e39c5baSBill Taylor  *
1311*9e39c5baSBill Taylor  *  Overview
1312*9e39c5baSBill Taylor  *      hermon_get_pcihdl() returns either the fma-protected access
1313*9e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the PCI config
1314*9e39c5baSBill Taylor  *      space. Whether or not which handle is returned at the moment depends
1315*9e39c5baSBill Taylor  *      on the Hermon FM state.
1316*9e39c5baSBill Taylor  *
1317*9e39c5baSBill Taylor  *  Argument
1318*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1319*9e39c5baSBill Taylor  *
1320*9e39c5baSBill Taylor  *  Return value
1321*9e39c5baSBill Taylor  *  	the access handle to PCI config space
1322*9e39c5baSBill Taylor  *
1323*9e39c5baSBill Taylor  *  Caller's context
1324*9e39c5baSBill Taylor  *      hermon_get_pcihdl() can be called in user, kernel, interrupt
1325*9e39c5baSBill Taylor  *      or high interrupt context.
1326*9e39c5baSBill Taylor  */
1327*9e39c5baSBill Taylor ddi_acc_handle_t
1328*9e39c5baSBill Taylor hermon_get_pcihdl(hermon_state_t *state)
1329*9e39c5baSBill Taylor {
1330*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
1331*9e39c5baSBill Taylor 	    state->hs_fm_pcihdl : state->hs_reg_pcihdl);
1332*9e39c5baSBill Taylor }
1333*9e39c5baSBill Taylor 
1334*9e39c5baSBill Taylor 
1335*9e39c5baSBill Taylor /*
1336*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1337*9e39c5baSBill Taylor  *  hermon_get_msix_tblhdl(hermon_state_t *state)
1338*9e39c5baSBill Taylor  *
1339*9e39c5baSBill Taylor  *  Overview
1340*9e39c5baSBill Taylor  *      hermon_get_msix_tblhdl() returns either the fma-protected access
1341*9e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the MSI-X tables.
1342*9e39c5baSBill Taylor  *      Whether or not which handle is returned at the moment depends on
1343*9e39c5baSBill Taylor  *      the Hermon FM state.
1344*9e39c5baSBill Taylor  *
1345*9e39c5baSBill Taylor  *  Argument
1346*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1347*9e39c5baSBill Taylor  *
1348*9e39c5baSBill Taylor  *  Return value
1349*9e39c5baSBill Taylor  *  	the access handle to MSI-X tables
1350*9e39c5baSBill Taylor  *
1351*9e39c5baSBill Taylor  *  Caller's context
1352*9e39c5baSBill Taylor  *      hermon_get_msix_tblhdl() can be called in user, kernel, interrupt
1353*9e39c5baSBill Taylor  *      context or high interrupt context.
1354*9e39c5baSBill Taylor  */
1355*9e39c5baSBill Taylor ddi_acc_handle_t
1356*9e39c5baSBill Taylor hermon_get_msix_tblhdl(hermon_state_t *state)
1357*9e39c5baSBill Taylor {
1358*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
1359*9e39c5baSBill Taylor 	    state->hs_fm_msix_tblhdl : state->hs_reg_msix_tblhdl);
1360*9e39c5baSBill Taylor }
1361*9e39c5baSBill Taylor 
1362*9e39c5baSBill Taylor 
1363*9e39c5baSBill Taylor /*
1364*9e39c5baSBill Taylor  *  ddi_acc_handle_t
1365*9e39c5baSBill Taylor  *  hermon_get_msix_pbahdl(hermon_state_t *state)
1366*9e39c5baSBill Taylor  *
1367*9e39c5baSBill Taylor  *  Overview
1368*9e39c5baSBill Taylor  *      hermon_get_msix_pbahdl() returns either the fma-protected access
1369*9e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the MSI-X PBA.
1370*9e39c5baSBill Taylor  *      Whether or not which handle is returned at the moment depends on
1371*9e39c5baSBill Taylor  *      the Hermon FM state.
1372*9e39c5baSBill Taylor  *
1373*9e39c5baSBill Taylor  *  Argument
1374*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1375*9e39c5baSBill Taylor  *
1376*9e39c5baSBill Taylor  *  Return value
1377*9e39c5baSBill Taylor  *  	the access handle to MSI-X PBA
1378*9e39c5baSBill Taylor  *
1379*9e39c5baSBill Taylor  *  Caller's context
1380*9e39c5baSBill Taylor  *      hermon_get_msix_pbahdl() can be called in user, kernel, interrupt
1381*9e39c5baSBill Taylor  *      context or high interrupt context.
1382*9e39c5baSBill Taylor  */
1383*9e39c5baSBill Taylor ddi_acc_handle_t
1384*9e39c5baSBill Taylor hermon_get_msix_pbahdl(hermon_state_t *state)
1385*9e39c5baSBill Taylor {
1386*9e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
1387*9e39c5baSBill Taylor 	    state->hs_fm_msix_pbahdl : state->hs_reg_msix_pbahdl);
1388*9e39c5baSBill Taylor }
1389*9e39c5baSBill Taylor 
1390*9e39c5baSBill Taylor 
1391*9e39c5baSBill Taylor /*
1392*9e39c5baSBill Taylor  *  void
1393*9e39c5baSBill Taylor  *  hermon_inter_err_chk(void *arg)
1394*9e39c5baSBill Taylor  *
1395*9e39c5baSBill Taylor  *  Overview
1396*9e39c5baSBill Taylor  *      hermon_inter_err_chk() periodically checks the internal error buffer
1397*9e39c5baSBill Taylor  *      to pick up a Hermon asynchronous internal error.
1398*9e39c5baSBill Taylor  *
1399*9e39c5baSBill Taylor  *      Note that this internal error can be notified if the interrupt is
1400*9e39c5baSBill Taylor  *      registered, but even so there are some cases that an interrupt against
1401*9e39c5baSBill Taylor  *      it cannot be raised so that Hermon RPM recommeds to poll this internal
1402*9e39c5baSBill Taylor  *      error buffer periodically instead. This function is invoked at
1403*9e39c5baSBill Taylor  *      10ms interval in kernel context though the function itself can be
1404*9e39c5baSBill Taylor  *      called in interrupt context.
1405*9e39c5baSBill Taylor  *
1406*9e39c5baSBill Taylor  *  Argument
1407*9e39c5baSBill Taylor  *      arg: pointer to Hermon state structure
1408*9e39c5baSBill Taylor  *
1409*9e39c5baSBill Taylor  *  Return value
1410*9e39c5baSBill Taylor  *  	Nothing
1411*9e39c5baSBill Taylor  *
1412*9e39c5baSBill Taylor  *  Caller's context
1413*9e39c5baSBill Taylor  *      hermon_inter_err_chk() can be called in user, kernel, interrupt
1414*9e39c5baSBill Taylor  *      context or high interrupt context.
1415*9e39c5baSBill Taylor  *
1416*9e39c5baSBill Taylor  */
1417*9e39c5baSBill Taylor void
1418*9e39c5baSBill Taylor hermon_inter_err_chk(void *arg)
1419*9e39c5baSBill Taylor {
1420*9e39c5baSBill Taylor 	uint32_t	word;
1421*9e39c5baSBill Taylor 	ddi_acc_handle_t cmdhdl;
1422*9e39c5baSBill Taylor 	hermon_state_t *state = (hermon_state_t *)arg;
1423*9e39c5baSBill Taylor 
1424*9e39c5baSBill Taylor 	/* initialize the FMA retry loop */
1425*9e39c5baSBill Taylor 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test);
1426*9e39c5baSBill Taylor 
1427*9e39c5baSBill Taylor #ifdef FMA_TEST
1428*9e39c5baSBill Taylor 	if (hermon_test_num != 0) {
1429*9e39c5baSBill Taylor 		return;
1430*9e39c5baSBill Taylor 	}
1431*9e39c5baSBill Taylor #endif
1432*9e39c5baSBill Taylor 	if (state->hs_fm_poll_suspend) {
1433*9e39c5baSBill Taylor 		return;
1434*9e39c5baSBill Taylor 	}
1435*9e39c5baSBill Taylor 
1436*9e39c5baSBill Taylor 	/* Get the access handle for Hermon CMD I/O space */
1437*9e39c5baSBill Taylor 	cmdhdl = hermon_get_cmdhdl(state);
1438*9e39c5baSBill Taylor 
1439*9e39c5baSBill Taylor 	/* the FMA retry loop starts. */
1440*9e39c5baSBill Taylor 	hermon_pio_start(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1441*9e39c5baSBill Taylor 	    fm_test);
1442*9e39c5baSBill Taylor 
1443*9e39c5baSBill Taylor 	word = ddi_get32(cmdhdl, state->hs_cmd_regs.fw_err_buf);
1444*9e39c5baSBill Taylor 
1445*9e39c5baSBill Taylor 	/* the FMA retry loop ends. */
1446*9e39c5baSBill Taylor 	hermon_pio_end(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
1447*9e39c5baSBill Taylor 	    fm_test);
1448*9e39c5baSBill Taylor 
1449*9e39c5baSBill Taylor 	if (word != 0) {
1450*9e39c5baSBill Taylor 		HERMON_FMANOTE(state, HERMON_FMA_INTERNAL);
1451*9e39c5baSBill Taylor 		hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
1452*9e39c5baSBill Taylor 	}
1453*9e39c5baSBill Taylor 
1454*9e39c5baSBill Taylor 	/* issue the ereport pended in the interrupt context */
1455*9e39c5baSBill Taylor 	if (state->hs_fm_async_errcnt > 0) {
1456*9e39c5baSBill Taylor 		hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
1457*9e39c5baSBill Taylor 		atomic_dec_32(&state->hs_fm_async_errcnt);
1458*9e39c5baSBill Taylor 	}
1459*9e39c5baSBill Taylor 
1460*9e39c5baSBill Taylor 	return;
1461*9e39c5baSBill Taylor 
1462*9e39c5baSBill Taylor pio_error:
1463*9e39c5baSBill Taylor 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_FATAL);
1464*9e39c5baSBill Taylor }
1465*9e39c5baSBill Taylor 
1466*9e39c5baSBill Taylor 
1467*9e39c5baSBill Taylor /*
1468*9e39c5baSBill Taylor  *  boolean_t
1469*9e39c5baSBill Taylor  *  hermon_cmd_retry_ok(hermon_cmd_post_t *cmd, int status)
1470*9e39c5baSBill Taylor  *
1471*9e39c5baSBill Taylor  *  Overview
1472*9e39c5baSBill Taylor  *  	In the case that a HW error is detected, if it can be isolated
1473*9e39c5baSBill Taylor  *  	enough, Hermon FM retries the operation which caused the error.
1474*9e39c5baSBill Taylor  *  	However, this retry can induce another error; since the retry is
1475*9e39c5baSBill Taylor  *  	achieved as a block basis, not a statement basis, once the state
1476*9e39c5baSBill Taylor  *  	was set inside the Hermon HW already in the previous operation, the
1477*9e39c5baSBill Taylor  *  	retry can cause for example, a CMD_BAD_SYS_STATE error, as a result.
1478*9e39c5baSBill Taylor  *  	In this case, CMD_BAD_SYS_STATE should be taken as a side effect
1479*9e39c5baSBill Taylor  *  	but a harmless result. hermon_cmd_retry_ok() checks this kind of
1480*9e39c5baSBill Taylor  *  	situation then returns if the state Hermon CMD returns is OK or not.
1481*9e39c5baSBill Taylor  *
1482*9e39c5baSBill Taylor  *  Argument
1483*9e39c5baSBill Taylor  *      cmd: pointer to hermon_cmd_post_t structure
1484*9e39c5baSBill Taylor  *      status: Hermon CMD status
1485*9e39c5baSBill Taylor  *
1486*9e39c5baSBill Taylor  *  Return value
1487*9e39c5baSBill Taylor  *  	B_TRUE		this state is no problem
1488*9e39c5baSBill Taylor  *  	B_FALSE		this state should be taken as an error
1489*9e39c5baSBill Taylor  *
1490*9e39c5baSBill Taylor  *  Caller's context
1491*9e39c5baSBill Taylor  *      hermon_cmd_retry_ok() can be called in user, kernel, interrupt
1492*9e39c5baSBill Taylor  *      context or high interrupt context.
1493*9e39c5baSBill Taylor  *
1494*9e39c5baSBill Taylor  *  Note that status except for HERMON_CMD_SUCCESS shouldn't be accepted
1495*9e39c5baSBill Taylor  *  in the debug module to catch a hidden software bug, so that ASSERT()
1496*9e39c5baSBill Taylor  *  is enabled in the case.
1497*9e39c5baSBill Taylor  */
1498*9e39c5baSBill Taylor boolean_t
1499*9e39c5baSBill Taylor hermon_cmd_retry_ok(hermon_cmd_post_t *cmd, int status)
1500*9e39c5baSBill Taylor {
1501*9e39c5baSBill Taylor 	if (status == HERMON_CMD_SUCCESS)
1502*9e39c5baSBill Taylor 		return (B_TRUE);
1503*9e39c5baSBill Taylor 
1504*9e39c5baSBill Taylor 	/*
1505*9e39c5baSBill Taylor 	 * The wrong status such as HERMON_CMD_BAD_SYS_STATE or
1506*9e39c5baSBill Taylor 	 * HERMON_CMD_BAD_RES_STATE can return as a side effect
1507*9e39c5baSBill Taylor 	 * because of the Hermon FM operation retry when a PIO
1508*9e39c5baSBill Taylor 	 * error is detected during the I/O transaction. In the
1509*9e39c5baSBill Taylor 	 * case, the driver may set the same value in Hermon
1510*9e39c5baSBill Taylor 	 * though it was set already, then Hermon returns HERMON_
1511*9e39c5baSBill Taylor 	 * CMD_BAD_{RES,SYS}_STATE as a result, which should be
1512*9e39c5baSBill Taylor 	 * taken as OK.
1513*9e39c5baSBill Taylor 	 */
1514*9e39c5baSBill Taylor 	switch (cmd->cp_opcode) {
1515*9e39c5baSBill Taylor 	case INIT_HCA:
1516*9e39c5baSBill Taylor 		/*
1517*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of
1518*9e39c5baSBill Taylor 		 * ICM not mapped or HCA already initialized.
1519*9e39c5baSBill Taylor 		 */
1520*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
1521*9e39c5baSBill Taylor 			return (B_TRUE);
1522*9e39c5baSBill Taylor 		return (B_FALSE);
1523*9e39c5baSBill Taylor 
1524*9e39c5baSBill Taylor 	case CLOSE_HCA:
1525*9e39c5baSBill Taylor 		/*
1526*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of Firmware
1527*9e39c5baSBill Taylor 		 * area is not mapped or HCA already closed.
1528*9e39c5baSBill Taylor 		 */
1529*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
1530*9e39c5baSBill Taylor 			return (B_TRUE);
1531*9e39c5baSBill Taylor 		return (B_FALSE);
1532*9e39c5baSBill Taylor 
1533*9e39c5baSBill Taylor 	case CLOSE_PORT:
1534*9e39c5baSBill Taylor 		/*
1535*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of HCA not
1536*9e39c5baSBill Taylor 		 * initialized or in case that IB ports are already down.
1537*9e39c5baSBill Taylor 		 */
1538*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
1539*9e39c5baSBill Taylor 			return (B_TRUE);
1540*9e39c5baSBill Taylor 		return (B_FALSE);
1541*9e39c5baSBill Taylor 
1542*9e39c5baSBill Taylor 	case SW2HW_MPT:
1543*9e39c5baSBill Taylor 		/*
1544*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
1545*9e39c5baSBill Taylor 		 * entry already in hardware ownership.
1546*9e39c5baSBill Taylor 		 */
1547*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1548*9e39c5baSBill Taylor 			return (B_TRUE);
1549*9e39c5baSBill Taylor 		return (B_FALSE);
1550*9e39c5baSBill Taylor 
1551*9e39c5baSBill Taylor 	case HW2SW_MPT:
1552*9e39c5baSBill Taylor 		/*
1553*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
1554*9e39c5baSBill Taylor 		 * entry already in software ownership.
1555*9e39c5baSBill Taylor 		 */
1556*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1557*9e39c5baSBill Taylor 			return (B_TRUE);
1558*9e39c5baSBill Taylor 		return (B_FALSE);
1559*9e39c5baSBill Taylor 
1560*9e39c5baSBill Taylor 	case SW2HW_EQ:
1561*9e39c5baSBill Taylor 		/*
1562*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
1563*9e39c5baSBill Taylor 		 * entry already in hardware ownership.
1564*9e39c5baSBill Taylor 		 */
1565*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1566*9e39c5baSBill Taylor 			return (B_TRUE);
1567*9e39c5baSBill Taylor 		return (B_FALSE);
1568*9e39c5baSBill Taylor 
1569*9e39c5baSBill Taylor 	case HW2SW_EQ:
1570*9e39c5baSBill Taylor 		/*
1571*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
1572*9e39c5baSBill Taylor 		 * entry already in software ownership.
1573*9e39c5baSBill Taylor 		 */
1574*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1575*9e39c5baSBill Taylor 			return (B_TRUE);
1576*9e39c5baSBill Taylor 		return (B_FALSE);
1577*9e39c5baSBill Taylor 
1578*9e39c5baSBill Taylor 	case SW2HW_CQ:
1579*9e39c5baSBill Taylor 		/*
1580*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
1581*9e39c5baSBill Taylor 		 * entry already in hardware ownership.
1582*9e39c5baSBill Taylor 		 */
1583*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1584*9e39c5baSBill Taylor 			return (B_TRUE);
1585*9e39c5baSBill Taylor 		return (B_FALSE);
1586*9e39c5baSBill Taylor 
1587*9e39c5baSBill Taylor 	case HW2SW_CQ:
1588*9e39c5baSBill Taylor 		/*
1589*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
1590*9e39c5baSBill Taylor 		 * entry already in software ownership.
1591*9e39c5baSBill Taylor 		 */
1592*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1593*9e39c5baSBill Taylor 			return (B_TRUE);
1594*9e39c5baSBill Taylor 		return (B_FALSE);
1595*9e39c5baSBill Taylor 
1596*9e39c5baSBill Taylor 	case SW2HW_SRQ:
1597*9e39c5baSBill Taylor 		/*
1598*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
1599*9e39c5baSBill Taylor 		 * entry already in hardware ownership.
1600*9e39c5baSBill Taylor 		 */
1601*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1602*9e39c5baSBill Taylor 			return (B_TRUE);
1603*9e39c5baSBill Taylor 		return (B_FALSE);
1604*9e39c5baSBill Taylor 
1605*9e39c5baSBill Taylor 	case HW2SW_SRQ:
1606*9e39c5baSBill Taylor 		/*
1607*9e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
1608*9e39c5baSBill Taylor 		 * entry already in software ownership.
1609*9e39c5baSBill Taylor 		 */
1610*9e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
1611*9e39c5baSBill Taylor 			return (B_TRUE);
1612*9e39c5baSBill Taylor 		return (B_FALSE);
1613*9e39c5baSBill Taylor 	default:
1614*9e39c5baSBill Taylor 		break;
1615*9e39c5baSBill Taylor 	}
1616*9e39c5baSBill Taylor 
1617*9e39c5baSBill Taylor 	/* other cases */
1618*9e39c5baSBill Taylor 	return (B_FALSE);
1619*9e39c5baSBill Taylor }
1620*9e39c5baSBill Taylor 
1621*9e39c5baSBill Taylor 
1622*9e39c5baSBill Taylor #ifdef FMA_TEST
1623*9e39c5baSBill Taylor 
1624*9e39c5baSBill Taylor /*
1625*9e39c5baSBill Taylor  * Hermon FMA test variables
1626*9e39c5baSBill Taylor  */
1627*9e39c5baSBill Taylor #define	FMA_TEST_HASHSZ	64
1628*9e39c5baSBill Taylor int hermon_test_num;			/* predefined testset */
1629*9e39c5baSBill Taylor 
1630*9e39c5baSBill Taylor static struct i_hca_fm_test *i_hca_test_register(char *, int, int,
1631*9e39c5baSBill Taylor     void (*)(struct i_hca_fm_test *, ddi_fm_error_t *),
1632*9e39c5baSBill Taylor     void *, mod_hash_t *, mod_hash_t *, int);
1633*9e39c5baSBill Taylor static void i_hca_test_free_item(mod_hash_val_t);
1634*9e39c5baSBill Taylor static void i_hca_test_set_item(int, struct i_hca_fm_test *);
1635*9e39c5baSBill Taylor static void hermon_trigger_pio_error(hermon_test_t *, ddi_fm_error_t *);
1636*9e39c5baSBill Taylor 
1637*9e39c5baSBill Taylor /*
1638*9e39c5baSBill Taylor  * Hermon FMA Function Test Interface
1639*9e39c5baSBill Taylor  */
1640*9e39c5baSBill Taylor 
1641*9e39c5baSBill Taylor /* Attach Errors */
1642*9e39c5baSBill Taylor 
1643*9e39c5baSBill Taylor #define	ATTACH_TS	(HCA_TEST_TRANSIENT | HCA_TEST_ATTACH | HCA_TEST_START)
1644*9e39c5baSBill Taylor #define	ATTACH_TE	(HCA_TEST_TRANSIENT | HCA_TEST_ATTACH | HCA_TEST_END)
1645*9e39c5baSBill Taylor 
1646*9e39c5baSBill Taylor #define	ATTACH_PS	(HCA_TEST_PERSISTENT | HCA_TEST_ATTACH | HCA_TEST_START)
1647*9e39c5baSBill Taylor #define	ATTACH_PE	(HCA_TEST_PERSISTENT | HCA_TEST_ATTACH | HCA_TEST_END)
1648*9e39c5baSBill Taylor 
1649*9e39c5baSBill Taylor static hermon_test_t testset[] = {
1650*9e39c5baSBill Taylor /* Initial Value */
1651*9e39c5baSBill Taylor {0, 0, 0, NULL, 0, 0, NULL, NULL, NULL},	/* 0 */
1652*9e39c5baSBill Taylor 
1653*9e39c5baSBill Taylor /* PIO Transient Errors */
1654*9e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_TS, NULL, /* attach/transient/start/propagate */
1655*9e39c5baSBill Taylor     HCA_PIO_RETRY_CNT, 0, NULL, NULL, NULL},	/* 1 */
1656*9e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_TE, NULL, /* attach/transient/end/propagate */
1657*9e39c5baSBill Taylor     HCA_PIO_RETRY_CNT, 0, NULL, NULL, NULL},	/* 2 */
1658*9e39c5baSBill Taylor 
1659*9e39c5baSBill Taylor /* PIO Persistent Errors */
1660*9e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_PS, NULL, /* attach/persistent/start/propagate */
1661*9e39c5baSBill Taylor     0, 0, NULL, NULL, NULL},			/* 3 */
1662*9e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_PE, NULL, /* attach/persistent/end/propagate */
1663*9e39c5baSBill Taylor     0, 0, NULL, NULL, NULL},			/* 4 */
1664*9e39c5baSBill Taylor 
1665*9e39c5baSBill Taylor };
1666*9e39c5baSBill Taylor 
1667*9e39c5baSBill Taylor 
1668*9e39c5baSBill Taylor /*
1669*9e39c5baSBill Taylor  *  void
1670*9e39c5baSBill Taylor  *  hermon_trigger_pio_error(hermon_test_t *tst, ddi_fm_error_t *derr)
1671*9e39c5baSBill Taylor  *
1672*9e39c5baSBill Taylor  *  Overview
1673*9e39c5baSBill Taylor  *      hermon_trigger_pio_error() is a PIO error injection function
1674*9e39c5baSBill Taylor  *      to cause a pseduo PIO error.
1675*9e39c5baSBill Taylor  *
1676*9e39c5baSBill Taylor  *  Argument
1677*9e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
1678*9e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
1679*9e39c5baSBill Taylor  *      derr: pointer to ddi_fm_error_t structure
1680*9e39c5baSBill Taylor  *
1681*9e39c5baSBill Taylor  *  Return value
1682*9e39c5baSBill Taylor  *      Nothing
1683*9e39c5baSBill Taylor  *
1684*9e39c5baSBill Taylor  *  Caller's context
1685*9e39c5baSBill Taylor  *      hermon_trigger_pio_error() can be called in user, kernel, interrupt
1686*9e39c5baSBill Taylor  *      context or high interrupt context.
1687*9e39c5baSBill Taylor  */
1688*9e39c5baSBill Taylor static void
1689*9e39c5baSBill Taylor hermon_trigger_pio_error(hermon_test_t *tst, ddi_fm_error_t *derr)
1690*9e39c5baSBill Taylor {
1691*9e39c5baSBill Taylor 	hermon_state_t *state = (hermon_state_t *)tst->private;
1692*9e39c5baSBill Taylor 	derr->fme_status = DDI_FM_OK;
1693*9e39c5baSBill Taylor 
1694*9e39c5baSBill Taylor 	if (tst->type != HCA_TEST_PIO) {
1695*9e39c5baSBill Taylor 		return;
1696*9e39c5baSBill Taylor 	}
1697*9e39c5baSBill Taylor 
1698*9e39c5baSBill Taylor 	if ((tst->trigger & HCA_TEST_ATTACH &&
1699*9e39c5baSBill Taylor 	    i_ddi_node_state(state->hs_dip) < DS_ATTACHED &&
1700*9e39c5baSBill Taylor 	    hermon_get_state(state) & HCA_PIO_FM)) {
1701*9e39c5baSBill Taylor 		if (tst->trigger & HCA_TEST_PERSISTENT) {
1702*9e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, HCA_IBA_ERR,
1703*9e39c5baSBill Taylor 			    DDI_FM_DEVICE_INVAL_STATE);
1704*9e39c5baSBill Taylor 			derr->fme_status = DDI_FM_NONFATAL;
1705*9e39c5baSBill Taylor 			return;
1706*9e39c5baSBill Taylor 		} else if (tst->trigger & HCA_TEST_TRANSIENT &&
1707*9e39c5baSBill Taylor 		    tst->errcnt) {
1708*9e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, HCA_IBA_ERR,
1709*9e39c5baSBill Taylor 			    DDI_FM_DEVICE_INVAL_STATE);
1710*9e39c5baSBill Taylor 			derr->fme_status = DDI_FM_NONFATAL;
1711*9e39c5baSBill Taylor 			tst->errcnt--;
1712*9e39c5baSBill Taylor 			return;
1713*9e39c5baSBill Taylor 		}
1714*9e39c5baSBill Taylor 	}
1715*9e39c5baSBill Taylor }
1716*9e39c5baSBill Taylor 
1717*9e39c5baSBill Taylor 
1718*9e39c5baSBill Taylor /*
1719*9e39c5baSBill Taylor  *  struct hermon_fm_test *
1720*9e39c5baSBill Taylor  *  hermon_test_register(hermon_state_t *state, char *filename, int linenum,
1721*9e39c5baSBill Taylor  *      int type)
1722*9e39c5baSBill Taylor  *
1723*9e39c5baSBill Taylor  *  Overview
1724*9e39c5baSBill Taylor  *      hermon_test_register() registers a Hermon FM test item for the
1725*9e39c5baSBill Taylor  *      function test.
1726*9e39c5baSBill Taylor  *
1727*9e39c5baSBill Taylor  *  Argument
1728*9e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1729*9e39c5baSBill Taylor  *  	filename: source file name where the function call is implemented
1730*9e39c5baSBill Taylor  *		  This value is usually a __FILE__  pre-defined macro.
1731*9e39c5baSBill Taylor  *  	linenum: line number where the function call is described in the
1732*9e39c5baSBill Taylor  *		 file specified above.
1733*9e39c5baSBill Taylor  *		 This value is usually a __LINE__ pre-defined macro.
1734*9e39c5baSBill Taylor  *	type: HW error type
1735*9e39c5baSBill Taylor  *			HCA_TEST_PIO	pio error
1736*9e39c5baSBill Taylor  *			HCA_TEST_IBA	ib specific error
1737*9e39c5baSBill Taylor  *
1738*9e39c5baSBill Taylor  *  Return value
1739*9e39c5baSBill Taylor  *      pointer to Hermon FM function test structure registered.
1740*9e39c5baSBill Taylor  *
1741*9e39c5baSBill Taylor  *  Caller's context
1742*9e39c5baSBill Taylor  *      hermon_test_register() can be called in user, kernel or interrupt
1743*9e39c5baSBill Taylor  *      context.
1744*9e39c5baSBill Taylor  *
1745*9e39c5baSBill Taylor  *  Note that no test item is registered if Hermon FM is disabled.
1746*9e39c5baSBill Taylor  */
1747*9e39c5baSBill Taylor hermon_test_t *
1748*9e39c5baSBill Taylor hermon_test_register(hermon_state_t *state, char *filename, int linenum,
1749*9e39c5baSBill Taylor     int type)
1750*9e39c5baSBill Taylor {
1751*9e39c5baSBill Taylor 	void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *) =
1752*9e39c5baSBill Taylor 	    (void (*)(struct i_hca_fm_test *, ddi_fm_error_t *))
1753*9e39c5baSBill Taylor 	    hermon_trigger_pio_error;
1754*9e39c5baSBill Taylor 
1755*9e39c5baSBill Taylor 	if (state->hs_fm_disable)
1756*9e39c5baSBill Taylor 		return (NULL);
1757*9e39c5baSBill Taylor 
1758*9e39c5baSBill Taylor 	return ((hermon_test_t *)i_hca_test_register(filename, linenum, type,
1759*9e39c5baSBill Taylor 	    pio_injection, (void *)state, state->hs_fm_test_hash,
1760*9e39c5baSBill Taylor 	    state->hs_fm_id_hash, hermon_test_num));
1761*9e39c5baSBill Taylor }
1762*9e39c5baSBill Taylor #endif /* FMA_TEST */
1763*9e39c5baSBill Taylor 
1764*9e39c5baSBill Taylor 
1765*9e39c5baSBill Taylor /*
1766*9e39c5baSBill Taylor  * HCA FM Common Interface
1767*9e39c5baSBill Taylor  *
1768*9e39c5baSBill Taylor  * These functions should be used for any HCA drivers, but probably
1769*9e39c5baSBill Taylor  * customized for their own HW design and/or FM implementation.
1770*9e39c5baSBill Taylor  * Customized functins should have the driver name prefix such as
1771*9e39c5baSBill Taylor  * hermon_xxxx() and be defined separately but whose functions should
1772*9e39c5baSBill Taylor  * call the common interface inside.
1773*9e39c5baSBill Taylor  */
1774*9e39c5baSBill Taylor 
1775*9e39c5baSBill Taylor /*
1776*9e39c5baSBill Taylor  *  void
1777*9e39c5baSBill Taylor  *  i_hca_fm_init(struct i_hca_fm *hca_fm)
1778*9e39c5baSBill Taylor  *
1779*9e39c5baSBill Taylor  *  Overview
1780*9e39c5baSBill Taylor  *      i_hca_fm_init() is an initialization function which sets up the acc
1781*9e39c5baSBill Taylor  *      handle kmem_cache if this function is called the first time.
1782*9e39c5baSBill Taylor  *
1783*9e39c5baSBill Taylor  *  Argument
1784*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
1785*9e39c5baSBill Taylor  *
1786*9e39c5baSBill Taylor  *  Return value
1787*9e39c5baSBill Taylor  *      Nothing
1788*9e39c5baSBill Taylor  *
1789*9e39c5baSBill Taylor  *  Caller's context
1790*9e39c5baSBill Taylor  *      i_hca_fm_init() can be called in user or kernel context, but cannot
1791*9e39c5baSBill Taylor  *      be called in interrupt context.
1792*9e39c5baSBill Taylor  */
1793*9e39c5baSBill Taylor static void
1794*9e39c5baSBill Taylor i_hca_fm_init(struct i_hca_fm *hca_fm)
1795*9e39c5baSBill Taylor {
1796*9e39c5baSBill Taylor 
1797*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
1798*9e39c5baSBill Taylor 
1799*9e39c5baSBill Taylor 	++hca_fm->ref_cnt;
1800*9e39c5baSBill Taylor 	if (hca_fm->fm_acc_cache == NULL) {
1801*9e39c5baSBill Taylor 		hca_fm->fm_acc_cache = kmem_cache_create("hca_fm_acc_handle",
1802*9e39c5baSBill Taylor 		    sizeof (struct i_hca_acc_handle), 0, NULL,
1803*9e39c5baSBill Taylor 		    NULL, NULL, NULL, NULL, 0);
1804*9e39c5baSBill Taylor 	}
1805*9e39c5baSBill Taylor 
1806*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
1807*9e39c5baSBill Taylor }
1808*9e39c5baSBill Taylor 
1809*9e39c5baSBill Taylor 
1810*9e39c5baSBill Taylor /*
1811*9e39c5baSBill Taylor  *  void
1812*9e39c5baSBill Taylor  *  i_hca_fm_fini(struct i_hca_fm *hca_fm)
1813*9e39c5baSBill Taylor  *
1814*9e39c5baSBill Taylor  *  Overview
1815*9e39c5baSBill Taylor  *      i_hca_fm_fini() is a finalization function which frees up the acc
1816*9e39c5baSBill Taylor  *      handle kmem_cache if this function is called the last time.
1817*9e39c5baSBill Taylor  *
1818*9e39c5baSBill Taylor  *  Argument
1819*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
1820*9e39c5baSBill Taylor  *
1821*9e39c5baSBill Taylor  *  Return value
1822*9e39c5baSBill Taylor  *      Nothing
1823*9e39c5baSBill Taylor  *
1824*9e39c5baSBill Taylor  *  Caller's context
1825*9e39c5baSBill Taylor  *      i_hca_fm_fini() can be called in user or kernel context, but cannot
1826*9e39c5baSBill Taylor  *      be called in interrupt context.
1827*9e39c5baSBill Taylor  */
1828*9e39c5baSBill Taylor static void
1829*9e39c5baSBill Taylor i_hca_fm_fini(struct i_hca_fm *hca_fm)
1830*9e39c5baSBill Taylor {
1831*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
1832*9e39c5baSBill Taylor 
1833*9e39c5baSBill Taylor 	if (--hca_fm->ref_cnt == 0) {
1834*9e39c5baSBill Taylor 
1835*9e39c5baSBill Taylor 		if (hca_fm->fm_acc_cache) {
1836*9e39c5baSBill Taylor 			kmem_cache_destroy(hca_fm->fm_acc_cache);
1837*9e39c5baSBill Taylor 			hca_fm->fm_acc_cache = NULL;
1838*9e39c5baSBill Taylor 		}
1839*9e39c5baSBill Taylor 	}
1840*9e39c5baSBill Taylor 
1841*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
1842*9e39c5baSBill Taylor }
1843*9e39c5baSBill Taylor 
1844*9e39c5baSBill Taylor 
1845*9e39c5baSBill Taylor /*
1846*9e39c5baSBill Taylor  *  void
1847*9e39c5baSBill Taylor  *  i_hca_fm_ereport(dev_info_t *dip, int type, char *detail)
1848*9e39c5baSBill Taylor  *
1849*9e39c5baSBill Taylor  *  Overview
1850*9e39c5baSBill Taylor  *      i_hca_fm_ereport() is a wrapper function of ddi_fm_ereport_post() but
1851*9e39c5baSBill Taylor  *      generates an ena before it calls ddi_fm_ereport_post() for HCA
1852*9e39c5baSBill Taylor  *      specific HW errors.
1853*9e39c5baSBill Taylor  *
1854*9e39c5baSBill Taylor  *  Argument
1855*9e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
1856*9e39c5baSBill Taylor  *      type: error type
1857*9e39c5baSBill Taylor  *		HCA_SYS_ERR	FMA reporting HW error
1858*9e39c5baSBill Taylor  *		HCA_IBA_ERR	HCA specific HW error
1859*9e39c5baSBill Taylor  *      detail: definition of leaf driver detected ereports which is one of:
1860*9e39c5baSBill Taylor  *      	DDI_FM_DEVICE_INVAL_STATE
1861*9e39c5baSBill Taylor  *		DDI_FM_DEVICE_NO_RESPONSE
1862*9e39c5baSBill Taylor  *		DDI_FM_DEVICE_STALL
1863*9e39c5baSBill Taylor  *		DDI_FM_DEVICE_BADINT_LIMIT
1864*9e39c5baSBill Taylor  *		DDI_FM_DEVICE_INTERN_CORR
1865*9e39c5baSBill Taylor  *		DDI_FM_DEVICE_INTERN_UNCORR
1866*9e39c5baSBill Taylor  *
1867*9e39c5baSBill Taylor  *  Return value
1868*9e39c5baSBill Taylor  *      Nothing
1869*9e39c5baSBill Taylor  *
1870*9e39c5baSBill Taylor  *  Caller's context
1871*9e39c5baSBill Taylor  *      i_hca_fm_ereport() can be called in user, kernel or interrupt context.
1872*9e39c5baSBill Taylor  */
1873*9e39c5baSBill Taylor static void
1874*9e39c5baSBill Taylor i_hca_fm_ereport(dev_info_t *dip, int type, char *detail)
1875*9e39c5baSBill Taylor {
1876*9e39c5baSBill Taylor 	uint64_t ena;
1877*9e39c5baSBill Taylor 	char buf[FM_MAX_CLASS];
1878*9e39c5baSBill Taylor 
1879*9e39c5baSBill Taylor 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
1880*9e39c5baSBill Taylor 
1881*9e39c5baSBill Taylor 	ena = fm_ena_generate(0, FM_ENA_FMT1);
1882*9e39c5baSBill Taylor 	if (type == HCA_IBA_ERR) {
1883*9e39c5baSBill Taylor 		/* this is an error of its own */
1884*9e39c5baSBill Taylor 		ena = fm_ena_increment(ena);
1885*9e39c5baSBill Taylor 	}
1886*9e39c5baSBill Taylor 
1887*9e39c5baSBill Taylor 	ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
1888*9e39c5baSBill Taylor 	    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
1889*9e39c5baSBill Taylor }
1890*9e39c5baSBill Taylor 
1891*9e39c5baSBill Taylor 
1892*9e39c5baSBill Taylor /*
1893*9e39c5baSBill Taylor  * struct i_hca_acc_handle *
1894*9e39c5baSBill Taylor  * i_hca_get_acc_handle(struct i_hca_fm *hca_fm, ddi_acc_handle_t handle)
1895*9e39c5baSBill Taylor  *
1896*9e39c5baSBill Taylor  *  Overview
1897*9e39c5baSBill Taylor  *      i_hca_get_acc_handle() returns ddi_acc_handle_t used for HCA FM.
1898*9e39c5baSBill Taylor  *
1899*9e39c5baSBill Taylor  *  Argument
1900*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
1901*9e39c5baSBill Taylor  *      handle: ddi_acc_handle_t
1902*9e39c5baSBill Taylor  *
1903*9e39c5baSBill Taylor  *  Return value
1904*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
1905*9e39c5baSBill Taylor  *
1906*9e39c5baSBill Taylor  *  Caller's context
1907*9e39c5baSBill Taylor  *      i_hca_get_acc_handle() can be called in user, kernel or interrupt
1908*9e39c5baSBill Taylor  *      context.
1909*9e39c5baSBill Taylor  */
1910*9e39c5baSBill Taylor static struct i_hca_acc_handle *
1911*9e39c5baSBill Taylor i_hca_get_acc_handle(struct i_hca_fm *hca_fm, ddi_acc_handle_t handle)
1912*9e39c5baSBill Taylor {
1913*9e39c5baSBill Taylor 	struct i_hca_acc_handle *hdlp;
1914*9e39c5baSBill Taylor 
1915*9e39c5baSBill Taylor 	/* Retrieve the HCA FM access handle */
1916*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
1917*9e39c5baSBill Taylor 
1918*9e39c5baSBill Taylor 	for (hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
1919*9e39c5baSBill Taylor 		if (hdlp->save_hdl == handle) {
1920*9e39c5baSBill Taylor 			mutex_exit(&hca_fm->lock);
1921*9e39c5baSBill Taylor 			return (hdlp);
1922*9e39c5baSBill Taylor 		}
1923*9e39c5baSBill Taylor 	}
1924*9e39c5baSBill Taylor 
1925*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
1926*9e39c5baSBill Taylor 	return (hdlp);
1927*9e39c5baSBill Taylor }
1928*9e39c5baSBill Taylor 
1929*9e39c5baSBill Taylor 
1930*9e39c5baSBill Taylor /*
1931*9e39c5baSBill Taylor  *  int
1932*9e39c5baSBill Taylor  *  i_hca_regs_map_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
1933*9e39c5baSBill Taylor  *      uint_t rnumber, caddr_t *addrp, offset_t offset, offset_t len,
1934*9e39c5baSBill Taylor  *      ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handle)
1935*9e39c5baSBill Taylor  *
1936*9e39c5baSBill Taylor  *  Overview
1937*9e39c5baSBill Taylor  *      i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_setup(),
1938*9e39c5baSBill Taylor  *      but allocates the HCA FM acc handle structure and initializes it.
1939*9e39c5baSBill Taylor  *
1940*9e39c5baSBill Taylor  *  Argument
1941*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
1942*9e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
1943*9e39c5baSBill Taylor  *      rnumber: index number to the register address space set
1944*9e39c5baSBill Taylor  *      addrp: platform-dependent value (same as ddi_regs_map_setup())
1945*9e39c5baSBill Taylor  *      offset: offset into the register address space
1946*9e39c5baSBill Taylor  *      len: address space length to be mapped
1947*9e39c5baSBill Taylor  *      accattrp: pointer to device access attribute structure
1948*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
1949*9e39c5baSBill Taylor  *
1950*9e39c5baSBill Taylor  *  Return value
1951*9e39c5baSBill Taylor  *      ddi function status value which are:
1952*9e39c5baSBill Taylor  *      	DDI_SUCCESS
1953*9e39c5baSBill Taylor  *      	DDI_FAILURE
1954*9e39c5baSBill Taylor  *      	DDI_ME_RNUMBER_RNGE
1955*9e39c5baSBill Taylor  *      	DDI_REGS_ACC_CONFLICT
1956*9e39c5baSBill Taylor  *
1957*9e39c5baSBill Taylor  *  Caller's context
1958*9e39c5baSBill Taylor  *      i_hca_regs_map_setup() can be called in user or kernel context only.
1959*9e39c5baSBill Taylor  */
1960*9e39c5baSBill Taylor static int
1961*9e39c5baSBill Taylor i_hca_regs_map_setup(struct i_hca_fm *hca_fm, dev_info_t *dip, uint_t rnumber,
1962*9e39c5baSBill Taylor     caddr_t *addrp, offset_t offset, offset_t len,
1963*9e39c5baSBill Taylor     ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handle)
1964*9e39c5baSBill Taylor {
1965*9e39c5baSBill Taylor 	int status;
1966*9e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *last;
1967*9e39c5baSBill Taylor 
1968*9e39c5baSBill Taylor 	/* Allocate an access handle */
1969*9e39c5baSBill Taylor 	if ((status = ddi_regs_map_setup(dip, rnumber, addrp, offset,
1970*9e39c5baSBill Taylor 	    len, accattrp, handle)) != DDI_SUCCESS) {
1971*9e39c5baSBill Taylor 		return (status);
1972*9e39c5baSBill Taylor 	}
1973*9e39c5baSBill Taylor 
1974*9e39c5baSBill Taylor 	/* Allocate HCA FM acc handle structure */
1975*9e39c5baSBill Taylor 	handlep = kmem_cache_alloc(hca_fm->fm_acc_cache, KM_SLEEP);
1976*9e39c5baSBill Taylor 
1977*9e39c5baSBill Taylor 	/* Initialize fields */
1978*9e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*handlep))
1979*9e39c5baSBill Taylor 	handlep->next = NULL;
1980*9e39c5baSBill Taylor 	handlep->save_hdl = (*handle);
1981*9e39c5baSBill Taylor 	handlep->thread_cnt = 0;
1982*9e39c5baSBill Taylor 	mutex_init(&handlep->lock, NULL, MUTEX_DRIVER, NULL);
1983*9e39c5baSBill Taylor 
1984*9e39c5baSBill Taylor 	/* Register this handle */
1985*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
1986*9e39c5baSBill Taylor 	for (last = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
1987*9e39c5baSBill Taylor 		last = hdlp;
1988*9e39c5baSBill Taylor 	}
1989*9e39c5baSBill Taylor 	if (last == NULL) {
1990*9e39c5baSBill Taylor 		hca_fm->hdl = handlep;
1991*9e39c5baSBill Taylor 	} else {
1992*9e39c5baSBill Taylor 		last->next = handlep;
1993*9e39c5baSBill Taylor 	}
1994*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
1995*9e39c5baSBill Taylor 
1996*9e39c5baSBill Taylor 	return (status);
1997*9e39c5baSBill Taylor }
1998*9e39c5baSBill Taylor 
1999*9e39c5baSBill Taylor 
2000*9e39c5baSBill Taylor /*
2001*9e39c5baSBill Taylor  *  void
2002*9e39c5baSBill Taylor  *  i_hca_regs_map_free(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handlep)
2003*9e39c5baSBill Taylor  *
2004*9e39c5baSBill Taylor  *  Overview
2005*9e39c5baSBill Taylor  *      i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_free(),
2006*9e39c5baSBill Taylor  *      and frees the HCA FM acc handle structure allocated by
2007*9e39c5baSBill Taylor  *      i_hca_regs_map_setup().
2008*9e39c5baSBill Taylor  *
2009*9e39c5baSBill Taylor  *  Argument
2010*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
2011*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
2012*9e39c5baSBill Taylor  *
2013*9e39c5baSBill Taylor  *  Return value
2014*9e39c5baSBill Taylor  *      Nothing
2015*9e39c5baSBill Taylor  *
2016*9e39c5baSBill Taylor  *  Caller's context
2017*9e39c5baSBill Taylor  *      i_hca_regs_map_free() can be called in user or kernel context only.
2018*9e39c5baSBill Taylor  *
2019*9e39c5baSBill Taylor  *  Note that the handle passed to i_hca_regs_map_free() is NULL-cleared
2020*9e39c5baSBill Taylor  *  after this function is called.
2021*9e39c5baSBill Taylor  */
2022*9e39c5baSBill Taylor static void
2023*9e39c5baSBill Taylor i_hca_regs_map_free(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handle)
2024*9e39c5baSBill Taylor {
2025*9e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *prev;
2026*9e39c5baSBill Taylor 
2027*9e39c5baSBill Taylor 	/* De-register this handle */
2028*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
2029*9e39c5baSBill Taylor 	for (prev = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
2030*9e39c5baSBill Taylor 		if (hdlp->save_hdl == *handle)
2031*9e39c5baSBill Taylor 			break;
2032*9e39c5baSBill Taylor 		prev = hdlp;
2033*9e39c5baSBill Taylor 	}
2034*9e39c5baSBill Taylor 	ASSERT(prev != NULL && hdlp != NULL);
2035*9e39c5baSBill Taylor 	if (hdlp != prev) {
2036*9e39c5baSBill Taylor 		prev->next = hdlp->next;
2037*9e39c5baSBill Taylor 	} else {
2038*9e39c5baSBill Taylor 		hca_fm->hdl = hdlp->next;
2039*9e39c5baSBill Taylor 	}
2040*9e39c5baSBill Taylor 	handlep = hdlp;
2041*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
2042*9e39c5baSBill Taylor 
2043*9e39c5baSBill Taylor 	mutex_destroy(&handlep->lock);
2044*9e39c5baSBill Taylor 	handlep->save_hdl = NULL;
2045*9e39c5baSBill Taylor 	kmem_cache_free(hca_fm->fm_acc_cache, handlep);
2046*9e39c5baSBill Taylor 
2047*9e39c5baSBill Taylor 	/* Release this handle */
2048*9e39c5baSBill Taylor 	ddi_regs_map_free(handle);
2049*9e39c5baSBill Taylor 	*handle = NULL;
2050*9e39c5baSBill Taylor }
2051*9e39c5baSBill Taylor 
2052*9e39c5baSBill Taylor 
2053*9e39c5baSBill Taylor /*
2054*9e39c5baSBill Taylor  *  int
2055*9e39c5baSBill Taylor  *  i_hca_pci_config_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
2056*9e39c5baSBill Taylor  *      ddi_acc_handle_t *handle, boolean_t fm_protect)
2057*9e39c5baSBill Taylor  *
2058*9e39c5baSBill Taylor  *  Overview
2059*9e39c5baSBill Taylor  *      i_hca_pci_config_setup() is a wrapper function of pci_config_setup(),
2060*9e39c5baSBill Taylor  *      but allocates the HCA FM acc handle structure and initializes it.
2061*9e39c5baSBill Taylor  *
2062*9e39c5baSBill Taylor  *  Argument
2063*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
2064*9e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
2065*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA PCI config space
2066*9e39c5baSBill Taylor  *		with FMA
2067*9e39c5baSBill Taylor  *	fm_protect: flag to tell if an fma-protected access handle should
2068*9e39c5baSBill Taylor  *		be used
2069*9e39c5baSBill Taylor  *
2070*9e39c5baSBill Taylor  *  Return value
2071*9e39c5baSBill Taylor  *      ddi function status value which are:
2072*9e39c5baSBill Taylor  *      	DDI_SUCCESS
2073*9e39c5baSBill Taylor  *      	DDI_FAILURE
2074*9e39c5baSBill Taylor  *
2075*9e39c5baSBill Taylor  *  Caller's context
2076*9e39c5baSBill Taylor  *      i_hca_pci_config_setup() can be called in user or kernel context only.
2077*9e39c5baSBill Taylor  */
2078*9e39c5baSBill Taylor static int
2079*9e39c5baSBill Taylor i_hca_pci_config_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
2080*9e39c5baSBill Taylor     ddi_acc_handle_t *handle)
2081*9e39c5baSBill Taylor {
2082*9e39c5baSBill Taylor 	int status;
2083*9e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *last;
2084*9e39c5baSBill Taylor 
2085*9e39c5baSBill Taylor 	/* Allocate an access handle */
2086*9e39c5baSBill Taylor 	if ((status = pci_config_setup(dip, handle)) != DDI_SUCCESS) {
2087*9e39c5baSBill Taylor 		return (status);
2088*9e39c5baSBill Taylor 	}
2089*9e39c5baSBill Taylor 
2090*9e39c5baSBill Taylor 	/* Allocate HCA FM acc handle structure */
2091*9e39c5baSBill Taylor 	handlep = kmem_cache_alloc(hca_fm->fm_acc_cache, KM_SLEEP);
2092*9e39c5baSBill Taylor 
2093*9e39c5baSBill Taylor 	/* Initialize fields */
2094*9e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*handlep))
2095*9e39c5baSBill Taylor 	handlep->next = NULL;
2096*9e39c5baSBill Taylor 	handlep->save_hdl = (*handle);
2097*9e39c5baSBill Taylor 	handlep->thread_cnt = 0;
2098*9e39c5baSBill Taylor 	mutex_init(&handlep->lock, NULL, MUTEX_DRIVER, NULL);
2099*9e39c5baSBill Taylor 
2100*9e39c5baSBill Taylor 	/* Register this handle */
2101*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
2102*9e39c5baSBill Taylor 	for (last = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
2103*9e39c5baSBill Taylor 		last = hdlp;
2104*9e39c5baSBill Taylor 	}
2105*9e39c5baSBill Taylor 	if (last == NULL) {
2106*9e39c5baSBill Taylor 		hca_fm->hdl = handlep;
2107*9e39c5baSBill Taylor 	} else {
2108*9e39c5baSBill Taylor 		last->next = handlep;
2109*9e39c5baSBill Taylor 	}
2110*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
2111*9e39c5baSBill Taylor 
2112*9e39c5baSBill Taylor 	return (status);
2113*9e39c5baSBill Taylor }
2114*9e39c5baSBill Taylor 
2115*9e39c5baSBill Taylor 
2116*9e39c5baSBill Taylor /*
2117*9e39c5baSBill Taylor  *  void
2118*9e39c5baSBill Taylor  *  i_hca_pci_config_teardown(struct i_hca_fm *hca_fm,
2119*9e39c5baSBill Taylor  *      ddi_acc_handle_t *handlep)
2120*9e39c5baSBill Taylor  *
2121*9e39c5baSBill Taylor  *  Overview
2122*9e39c5baSBill Taylor  *      i_hca_pci_config_teardown() is a wrapper function of
2123*9e39c5baSBill Taylor  *      pci_config_teardown(), and frees the HCA FM acc handle structure
2124*9e39c5baSBill Taylor  *      allocated by i_hca_pci_config_setup().
2125*9e39c5baSBill Taylor  *
2126*9e39c5baSBill Taylor  *  Argument
2127*9e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
2128*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
2129*9e39c5baSBill Taylor  *
2130*9e39c5baSBill Taylor  *  Return value
2131*9e39c5baSBill Taylor  *      Nothing
2132*9e39c5baSBill Taylor  *
2133*9e39c5baSBill Taylor  *  Caller's context
2134*9e39c5baSBill Taylor  *      i_hca_pci_config_teardown() can be called in user or kernel context
2135*9e39c5baSBill Taylor  *      only.
2136*9e39c5baSBill Taylor  *
2137*9e39c5baSBill Taylor  *  Note that the handle passed to i_hca_pci_config_teardown() is NULL-cleared
2138*9e39c5baSBill Taylor  *  after this function is called.
2139*9e39c5baSBill Taylor  */
2140*9e39c5baSBill Taylor static void
2141*9e39c5baSBill Taylor i_hca_pci_config_teardown(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handle)
2142*9e39c5baSBill Taylor {
2143*9e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *prev;
2144*9e39c5baSBill Taylor 
2145*9e39c5baSBill Taylor 	/* De-register this handle */
2146*9e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
2147*9e39c5baSBill Taylor 	for (prev = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
2148*9e39c5baSBill Taylor 		if (hdlp->save_hdl == *handle)
2149*9e39c5baSBill Taylor 			break;
2150*9e39c5baSBill Taylor 		prev = hdlp;
2151*9e39c5baSBill Taylor 	}
2152*9e39c5baSBill Taylor 	ASSERT(prev != NULL && hdlp != NULL);
2153*9e39c5baSBill Taylor 	if (hdlp != prev) {
2154*9e39c5baSBill Taylor 		prev->next = hdlp->next;
2155*9e39c5baSBill Taylor 	} else {
2156*9e39c5baSBill Taylor 		hca_fm->hdl = hdlp->next;
2157*9e39c5baSBill Taylor 	}
2158*9e39c5baSBill Taylor 	handlep = hdlp;
2159*9e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
2160*9e39c5baSBill Taylor 
2161*9e39c5baSBill Taylor 	mutex_destroy(&handlep->lock);
2162*9e39c5baSBill Taylor 	handlep->save_hdl = NULL;
2163*9e39c5baSBill Taylor 	kmem_cache_free(hca_fm->fm_acc_cache, handlep);
2164*9e39c5baSBill Taylor 
2165*9e39c5baSBill Taylor 	/* Release this handle */
2166*9e39c5baSBill Taylor 	pci_config_teardown(handle);
2167*9e39c5baSBill Taylor 	*handle = NULL;
2168*9e39c5baSBill Taylor }
2169*9e39c5baSBill Taylor 
2170*9e39c5baSBill Taylor 
2171*9e39c5baSBill Taylor /*
2172*9e39c5baSBill Taylor  *  int
2173*9e39c5baSBill Taylor  *  i_hca_pio_start(dev_info_t *dip, struct i_acc_handle *handle,
2174*9e39c5baSBill Taylor  *      struct i_hca_fm_test *tst)
2175*9e39c5baSBill Taylor  *
2176*9e39c5baSBill Taylor  *  Overview
2177*9e39c5baSBill Taylor  *      i_hca_pio_start() is one of a pair of HCA FM fuctions for PIO, which
2178*9e39c5baSBill Taylor  *      should be called before HCA drivers issue PIOs against I/O space.
2179*9e39c5baSBill Taylor  *      See HCA FM comments at the beginning of this file in detail.
2180*9e39c5baSBill Taylor  *
2181*9e39c5baSBill Taylor  *  Argument
2182*9e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
2183*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
2184*9e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
2185*9e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
2186*9e39c5baSBill Taylor  *
2187*9e39c5baSBill Taylor  *  Return value
2188*9e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
2189*9e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
2190*9e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
2191*9e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
2192*9e39c5baSBill Taylor  *
2193*9e39c5baSBill Taylor  *  Caller's context
2194*9e39c5baSBill Taylor  *      i_hca_pio_start() can be called in user, kernel or interrupt context.
2195*9e39c5baSBill Taylor  */
2196*9e39c5baSBill Taylor /* ARGSUSED */
2197*9e39c5baSBill Taylor static int
2198*9e39c5baSBill Taylor i_hca_pio_start(dev_info_t *dip, struct i_hca_acc_handle *hdlp,
2199*9e39c5baSBill Taylor     struct i_hca_fm_test *tst)
2200*9e39c5baSBill Taylor {
2201*9e39c5baSBill Taylor 	ddi_fm_error_t derr;
2202*9e39c5baSBill Taylor 
2203*9e39c5baSBill Taylor 	/* Count up the number of threads issuing this PIO */
2204*9e39c5baSBill Taylor 	mutex_enter(&hdlp->lock);
2205*9e39c5baSBill Taylor 	hdlp->thread_cnt++;
2206*9e39c5baSBill Taylor 	mutex_exit(&hdlp->lock);
2207*9e39c5baSBill Taylor 
2208*9e39c5baSBill Taylor 	/* Get the PIO error via FMA */
2209*9e39c5baSBill Taylor 	ddi_fm_acc_err_get(fm_acc_hdl(hdlp), &derr, DDI_FME_VERSION);
2210*9e39c5baSBill Taylor 
2211*9e39c5baSBill Taylor #ifdef FMA_TEST
2212*9e39c5baSBill Taylor 	/* Trigger PIO errors */
2213*9e39c5baSBill Taylor 	if (tst != NULL && tst->trigger & HCA_TEST_START) {
2214*9e39c5baSBill Taylor 		(*tst->pio_injection)(tst, &derr);
2215*9e39c5baSBill Taylor 	}
2216*9e39c5baSBill Taylor #endif /* FMA_TEST */
2217*9e39c5baSBill Taylor 
2218*9e39c5baSBill Taylor 	switch (derr.fme_status) {
2219*9e39c5baSBill Taylor 	case DDI_FM_OK:
2220*9e39c5baSBill Taylor 		/* Not have to clear the fma error log */
2221*9e39c5baSBill Taylor 		return (HCA_PIO_OK);
2222*9e39c5baSBill Taylor 
2223*9e39c5baSBill Taylor 	case DDI_FM_NONFATAL:
2224*9e39c5baSBill Taylor 		/* Now clear this error */
2225*9e39c5baSBill Taylor 		ddi_fm_acc_err_clear(fm_acc_hdl(hdlp), DDI_FME_VERSION);
2226*9e39c5baSBill Taylor 
2227*9e39c5baSBill Taylor 		/* Log this error and notify it as a persistent error */
2228*9e39c5baSBill Taylor 		ddi_fm_service_impact(dip, DDI_SERVICE_LOST);
2229*9e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
2230*9e39c5baSBill Taylor 
2231*9e39c5baSBill Taylor 	/* In theory, this shouldn't happen */
2232*9e39c5baSBill Taylor 	case DDI_FM_FATAL:
2233*9e39c5baSBill Taylor 	case DDI_FM_UNKNOWN:
2234*9e39c5baSBill Taylor 	default:
2235*9e39c5baSBill Taylor 		cmn_err(CE_WARN, "Unknown HCA HW error status (%d)",
2236*9e39c5baSBill Taylor 		    derr.fme_status);
2237*9e39c5baSBill Taylor 		/* Return this as a persistent error */
2238*9e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
2239*9e39c5baSBill Taylor 	}
2240*9e39c5baSBill Taylor }
2241*9e39c5baSBill Taylor 
2242*9e39c5baSBill Taylor 
2243*9e39c5baSBill Taylor /*
2244*9e39c5baSBill Taylor  *  int
2245*9e39c5baSBill Taylor  *  i_hca_pio_end(dev_info_t *dip, ddi_acc_handle_t handle, int *cnt,
2246*9e39c5baSBill Taylor  *      struct i_hca_fm_test *tst)
2247*9e39c5baSBill Taylor  *
2248*9e39c5baSBill Taylor  *  Overview
2249*9e39c5baSBill Taylor  *      i_hca_pio_end() is the other of a pair of HCA FM fuctions for PIO,
2250*9e39c5baSBill Taylor  *      which should be called after HCA drivers issue PIOs against I/O space.
2251*9e39c5baSBill Taylor  *      See HCA FM comments at the beginning of this file in detail.
2252*9e39c5baSBill Taylor  *
2253*9e39c5baSBill Taylor  *  Argument
2254*9e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
2255*9e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
2256*9e39c5baSBill Taylor  *	cnt: pointer to the counter variable which holds the nubmer of retry
2257*9e39c5baSBill Taylor  *	     when a HW error is detected.
2258*9e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
2259*9e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
2260*9e39c5baSBill Taylor  *
2261*9e39c5baSBill Taylor  *  Return value
2262*9e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
2263*9e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
2264*9e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
2265*9e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
2266*9e39c5baSBill Taylor  *
2267*9e39c5baSBill Taylor  *  Caller's context
2268*9e39c5baSBill Taylor  *      i_hca_pio_end() can be called in user, kernel or interrupt context.
2269*9e39c5baSBill Taylor  */
2270*9e39c5baSBill Taylor /* ARGSUSED */
2271*9e39c5baSBill Taylor static int
2272*9e39c5baSBill Taylor i_hca_pio_end(dev_info_t *dip, struct i_hca_acc_handle *hdlp, int *cnt,
2273*9e39c5baSBill Taylor     struct i_hca_fm_test *tst)
2274*9e39c5baSBill Taylor {
2275*9e39c5baSBill Taylor 	ddi_fm_error_t derr;
2276*9e39c5baSBill Taylor 
2277*9e39c5baSBill Taylor 	/* Get the PIO error via FMA */
2278*9e39c5baSBill Taylor 	ddi_fm_acc_err_get(fm_acc_hdl(hdlp), &derr, DDI_FME_VERSION);
2279*9e39c5baSBill Taylor 
2280*9e39c5baSBill Taylor #ifdef FMA_TEST
2281*9e39c5baSBill Taylor 	/* Trigger PIO errors */
2282*9e39c5baSBill Taylor 	if (tst != NULL && tst->trigger & HCA_TEST_END) {
2283*9e39c5baSBill Taylor 		(*tst->pio_injection)(tst, &derr);
2284*9e39c5baSBill Taylor 	}
2285*9e39c5baSBill Taylor #endif /* FMA_TEST */
2286*9e39c5baSBill Taylor 
2287*9e39c5baSBill Taylor 	/* Evaluate the PIO error */
2288*9e39c5baSBill Taylor 	switch (derr.fme_status) {
2289*9e39c5baSBill Taylor 	case DDI_FM_OK:
2290*9e39c5baSBill Taylor 		/* Count down the number of threads issuing this PIO */
2291*9e39c5baSBill Taylor 		mutex_enter(&hdlp->lock);
2292*9e39c5baSBill Taylor 		hdlp->thread_cnt--;
2293*9e39c5baSBill Taylor 		mutex_exit(&hdlp->lock);
2294*9e39c5baSBill Taylor 
2295*9e39c5baSBill Taylor 		/* Not have to clear the fma error log */
2296*9e39c5baSBill Taylor 		return (HCA_PIO_OK);
2297*9e39c5baSBill Taylor 
2298*9e39c5baSBill Taylor 	case DDI_FM_NONFATAL:
2299*9e39c5baSBill Taylor 		/* Now clear this error */
2300*9e39c5baSBill Taylor 		ddi_fm_acc_err_clear(fm_acc_hdl(hdlp), DDI_FME_VERSION);
2301*9e39c5baSBill Taylor 
2302*9e39c5baSBill Taylor 		/*
2303*9e39c5baSBill Taylor 		 * Check if this error comes from another thread running
2304*9e39c5baSBill Taylor 		 * with the same handle almost at the same time.
2305*9e39c5baSBill Taylor 		 */
2306*9e39c5baSBill Taylor 		mutex_enter(&hdlp->lock);
2307*9e39c5baSBill Taylor 		if (hdlp->thread_cnt > 1) {
2308*9e39c5baSBill Taylor 			/* Count down the number of threads */
2309*9e39c5baSBill Taylor 			hdlp->thread_cnt--;
2310*9e39c5baSBill Taylor 			mutex_exit(&hdlp->lock);
2311*9e39c5baSBill Taylor 
2312*9e39c5baSBill Taylor 			/* Return this as a persistent error */
2313*9e39c5baSBill Taylor 			return (HCA_PIO_PERSISTENT);
2314*9e39c5baSBill Taylor 		}
2315*9e39c5baSBill Taylor 		mutex_exit(&hdlp->lock);
2316*9e39c5baSBill Taylor 
2317*9e39c5baSBill Taylor 		/* Now determine if this error is persistent or not */
2318*9e39c5baSBill Taylor 		if (--(*cnt) >= 0)  {
2319*9e39c5baSBill Taylor 			return (HCA_PIO_TRANSIENT);
2320*9e39c5baSBill Taylor 		} else {
2321*9e39c5baSBill Taylor 			/* Count down the number of threads */
2322*9e39c5baSBill Taylor 			mutex_enter(&hdlp->lock);
2323*9e39c5baSBill Taylor 			hdlp->thread_cnt--;
2324*9e39c5baSBill Taylor 			mutex_exit(&hdlp->lock);
2325*9e39c5baSBill Taylor 			return (HCA_PIO_PERSISTENT);
2326*9e39c5baSBill Taylor 		}
2327*9e39c5baSBill Taylor 
2328*9e39c5baSBill Taylor 	/* In theory, this shouldn't happen */
2329*9e39c5baSBill Taylor 	case DDI_FM_FATAL:
2330*9e39c5baSBill Taylor 	case DDI_FM_UNKNOWN:
2331*9e39c5baSBill Taylor 	default:
2332*9e39c5baSBill Taylor 		cmn_err(CE_WARN, "Unknown HCA HW error status (%d)",
2333*9e39c5baSBill Taylor 		    derr.fme_status);
2334*9e39c5baSBill Taylor 		/* Return this as a persistent error */
2335*9e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
2336*9e39c5baSBill Taylor 	}
2337*9e39c5baSBill Taylor }
2338*9e39c5baSBill Taylor 
2339*9e39c5baSBill Taylor 
2340*9e39c5baSBill Taylor /*
2341*9e39c5baSBill Taylor  * HCA FM Test Interface
2342*9e39c5baSBill Taylor  *
2343*9e39c5baSBill Taylor  * These functions should be used for any HCA drivers, but probably
2344*9e39c5baSBill Taylor  * customized for their own HW design and/or FM implementation.
2345*9e39c5baSBill Taylor  * Customized functins should have the driver name prefix such as
2346*9e39c5baSBill Taylor  * hermon_xxxx() and be defined separately but whose function should
2347*9e39c5baSBill Taylor  * call the common interface inside.
2348*9e39c5baSBill Taylor  */
2349*9e39c5baSBill Taylor 
2350*9e39c5baSBill Taylor #ifdef FMA_TEST
2351*9e39c5baSBill Taylor static int test_num;		/* serial number */
2352*9e39c5baSBill Taylor static kmutex_t i_hca_test_lock; 	/* lock for serial numer */
2353*9e39c5baSBill Taylor 
2354*9e39c5baSBill Taylor /*
2355*9e39c5baSBill Taylor  *  void
2356*9e39c5baSBill Taylor  *  i_hca_test_init(mod_hash_t **strHashp, mod_hash_t **idHashp)
2357*9e39c5baSBill Taylor  *
2358*9e39c5baSBill Taylor  *  Overview
2359*9e39c5baSBill Taylor  *      i_hca_test_init() creates two hash tables, one of which is for string,
2360*9e39c5baSBill Taylor  *      and the other of which is for ID, then saves pointers to arguments
2361*9e39c5baSBill Taylor  *      passed. This function uses the mod_hash utilities to manage the
2362*9e39c5baSBill Taylor  *      hash tables. About the mod_hash, see common/os/modhash.c.
2363*9e39c5baSBill Taylor  *
2364*9e39c5baSBill Taylor  *  Argument
2365*9e39c5baSBill Taylor  *      strHashp: pointer to String hash table pointer
2366*9e39c5baSBill Taylor  *      idHashp: pointer to ID hash table pointer
2367*9e39c5baSBill Taylor  *
2368*9e39c5baSBill Taylor  *  Return value
2369*9e39c5baSBill Taylor  *      Nothing
2370*9e39c5baSBill Taylor  *
2371*9e39c5baSBill Taylor  *  Caller's context
2372*9e39c5baSBill Taylor  *      i_hca_test_init() can be called in user or kernel context only.
2373*9e39c5baSBill Taylor  */
2374*9e39c5baSBill Taylor static void
2375*9e39c5baSBill Taylor i_hca_test_init(mod_hash_t **strHashp, mod_hash_t **idHashp)
2376*9e39c5baSBill Taylor {
2377*9e39c5baSBill Taylor 	*idHashp = mod_hash_create_idhash("HCA_FMA_id_hash",
2378*9e39c5baSBill Taylor 	    FMA_TEST_HASHSZ, mod_hash_null_valdtor);
2379*9e39c5baSBill Taylor 
2380*9e39c5baSBill Taylor 	*strHashp = mod_hash_create_strhash("HCA_FMA_test_hash",
2381*9e39c5baSBill Taylor 	    FMA_TEST_HASHSZ, i_hca_test_free_item);
2382*9e39c5baSBill Taylor }
2383*9e39c5baSBill Taylor 
2384*9e39c5baSBill Taylor 
2385*9e39c5baSBill Taylor /*
2386*9e39c5baSBill Taylor  *  void
2387*9e39c5baSBill Taylor  *  i_hca_test_fini(mod_hash_t **strHashp, mod_hash_t **idHashp)
2388*9e39c5baSBill Taylor  *
2389*9e39c5baSBill Taylor  *  Overview
2390*9e39c5baSBill Taylor  *      i_hca_test_fini() releases two hash tables used for HCA FM test.
2391*9e39c5baSBill Taylor  *
2392*9e39c5baSBill Taylor  *  Argument
2393*9e39c5baSBill Taylor  *      strHashp: pointer to String hash table pointer
2394*9e39c5baSBill Taylor  *      idHashp: pointer to ID hash table pointer
2395*9e39c5baSBill Taylor  *
2396*9e39c5baSBill Taylor  *  Return value
2397*9e39c5baSBill Taylor  *      Nothing
2398*9e39c5baSBill Taylor  *
2399*9e39c5baSBill Taylor  *  Caller's context
2400*9e39c5baSBill Taylor  *      i_hca_test_fini() can be called in user, kernel or interrupt context.
2401*9e39c5baSBill Taylor  *
2402*9e39c5baSBill Taylor  */
2403*9e39c5baSBill Taylor static void
2404*9e39c5baSBill Taylor i_hca_test_fini(mod_hash_t **strHashp, mod_hash_t **idHashp)
2405*9e39c5baSBill Taylor {
2406*9e39c5baSBill Taylor 	mod_hash_destroy_hash(*strHashp);
2407*9e39c5baSBill Taylor 	*strHashp = NULL;
2408*9e39c5baSBill Taylor 
2409*9e39c5baSBill Taylor 	mod_hash_destroy_hash(*idHashp);
2410*9e39c5baSBill Taylor 	*idHashp = NULL;
2411*9e39c5baSBill Taylor }
2412*9e39c5baSBill Taylor 
2413*9e39c5baSBill Taylor 
2414*9e39c5baSBill Taylor /*
2415*9e39c5baSBill Taylor  *  struct i_hca_fm_test *
2416*9e39c5baSBill Taylor  *  i_hca_test_register(char *filename, int linenum, int type,
2417*9e39c5baSBill Taylor  *      void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *),
2418*9e39c5baSBill Taylor  *      void *private, mod_hash_t *strHash, mod_hash_t *idHash, int preTestNum)
2419*9e39c5baSBill Taylor  *
2420*9e39c5baSBill Taylor  *  Overview
2421*9e39c5baSBill Taylor  *      i_hca_test_register() registers an HCA FM test item against HCA FM
2422*9e39c5baSBill Taylor  *      function callings specified with the file name and the line number
2423*9e39c5baSBill Taylor  *      (passed as the arguments).
2424*9e39c5baSBill Taylor  *
2425*9e39c5baSBill Taylor  *  Argument
2426*9e39c5baSBill Taylor  *  	filename: source file name where the function call is implemented
2427*9e39c5baSBill Taylor  *		  This value is usually a __FILE__  pre-defined macro.
2428*9e39c5baSBill Taylor  *  	linenum: line number where the function call is described in the
2429*9e39c5baSBill Taylor  *		 file specified above.
2430*9e39c5baSBill Taylor  *		 This value is usually a __LINE__ pre-defined macro.
2431*9e39c5baSBill Taylor  *	type: HW error type
2432*9e39c5baSBill Taylor  *			HCA_TEST_PIO	pio error
2433*9e39c5baSBill Taylor  *			HCA_TEST_IBA	ib specific error
2434*9e39c5baSBill Taylor  *	pio_injection: pio error injection callback function invoked when the
2435*9e39c5baSBill Taylor  *		       function specified above (with the file name and the
2436*9e39c5baSBill Taylor  *		       line number) is executed. If the function is not a PIO,
2437*9e39c5baSBill Taylor  *		       request, this parameter should be NULL.
2438*9e39c5baSBill Taylor  *	private: the argument passed to either of injection functions when
2439*9e39c5baSBill Taylor  *		 they're invoked.
2440*9e39c5baSBill Taylor  *      strHashp: pointer to String hash table
2441*9e39c5baSBill Taylor  *      idHashp: pointer to ID hash table
2442*9e39c5baSBill Taylor  *      preTestNum: the index of the pre-defined testset for this test item.
2443*9e39c5baSBill Taylor  *
2444*9e39c5baSBill Taylor  *  Return value
2445*9e39c5baSBill Taylor  *      pointer to HCA FM function test structure registered.
2446*9e39c5baSBill Taylor  *
2447*9e39c5baSBill Taylor  *  Caller's context
2448*9e39c5baSBill Taylor  *      i_hca_test_register() can be called in user, kernel or interrupt
2449*9e39c5baSBill Taylor  *      context.
2450*9e39c5baSBill Taylor  *
2451*9e39c5baSBill Taylor  */
2452*9e39c5baSBill Taylor static struct i_hca_fm_test *
2453*9e39c5baSBill Taylor i_hca_test_register(char *filename, int linenum, int type,
2454*9e39c5baSBill Taylor     void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *),
2455*9e39c5baSBill Taylor     void *private, mod_hash_t *strHash, mod_hash_t *idHash, int preTestNum)
2456*9e39c5baSBill Taylor {
2457*9e39c5baSBill Taylor 	struct i_hca_fm_test *t_item;
2458*9e39c5baSBill Taylor 	char key_buf[255], *hash_key;
2459*9e39c5baSBill Taylor 	int status;
2460*9e39c5baSBill Taylor 
2461*9e39c5baSBill Taylor 	(void) sprintf(key_buf, "%s:%d", filename, linenum);
2462*9e39c5baSBill Taylor 	hash_key = kmem_zalloc(strlen(key_buf) + 1, KM_NOSLEEP);
2463*9e39c5baSBill Taylor 
2464*9e39c5baSBill Taylor 	if (hash_key == NULL)
2465*9e39c5baSBill Taylor 		cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
2466*9e39c5baSBill Taylor 
2467*9e39c5baSBill Taylor 	bcopy(key_buf, hash_key, strlen(key_buf));
2468*9e39c5baSBill Taylor 
2469*9e39c5baSBill Taylor 	status = mod_hash_find(strHash, (mod_hash_key_t)hash_key,
2470*9e39c5baSBill Taylor 	    (mod_hash_val_t *)&t_item);
2471*9e39c5baSBill Taylor 
2472*9e39c5baSBill Taylor 	switch (status) {
2473*9e39c5baSBill Taylor 	case MH_ERR_NOTFOUND:
2474*9e39c5baSBill Taylor 		t_item = (struct i_hca_fm_test *)
2475*9e39c5baSBill Taylor 		    kmem_alloc(sizeof (struct i_hca_fm_test), KM_NOSLEEP);
2476*9e39c5baSBill Taylor 		if (t_item == NULL)
2477*9e39c5baSBill Taylor 			cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
2478*9e39c5baSBill Taylor 
2479*9e39c5baSBill Taylor 		/* Set the error number */
2480*9e39c5baSBill Taylor 		mutex_enter(&i_hca_test_lock);
2481*9e39c5baSBill Taylor 		t_item->num = test_num++;
2482*9e39c5baSBill Taylor 		mutex_exit(&i_hca_test_lock);
2483*9e39c5baSBill Taylor 
2484*9e39c5baSBill Taylor 		/* Set type and other static information */
2485*9e39c5baSBill Taylor 		t_item->type = type;
2486*9e39c5baSBill Taylor 		t_item->line_num = linenum;
2487*9e39c5baSBill Taylor 		t_item->file_name = filename;
2488*9e39c5baSBill Taylor 		t_item->hash_key = hash_key;
2489*9e39c5baSBill Taylor 		t_item->private = private;
2490*9e39c5baSBill Taylor 		t_item->pio_injection = pio_injection;
2491*9e39c5baSBill Taylor 
2492*9e39c5baSBill Taylor 		/* Set the pre-defined hermon test item */
2493*9e39c5baSBill Taylor 		i_hca_test_set_item(preTestNum, (struct i_hca_fm_test *)t_item);
2494*9e39c5baSBill Taylor 
2495*9e39c5baSBill Taylor 		status = mod_hash_insert(strHash, (mod_hash_key_t)
2496*9e39c5baSBill Taylor 		    hash_key, (mod_hash_val_t)t_item);
2497*9e39c5baSBill Taylor 		ASSERT(status == 0);
2498*9e39c5baSBill Taylor 
2499*9e39c5baSBill Taylor 		status = mod_hash_insert(idHash, (mod_hash_key_t)
2500*9e39c5baSBill Taylor 		    (uintptr_t)t_item->num, (mod_hash_val_t)t_item);
2501*9e39c5baSBill Taylor 		ASSERT(status == 0);
2502*9e39c5baSBill Taylor 		break;
2503*9e39c5baSBill Taylor 
2504*9e39c5baSBill Taylor 	case MH_ERR_NOMEM:
2505*9e39c5baSBill Taylor 		cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
2506*9e39c5baSBill Taylor 		break;
2507*9e39c5baSBill Taylor 
2508*9e39c5baSBill Taylor 	case MH_ERR_DUPLICATE:
2509*9e39c5baSBill Taylor 		cmn_err(CE_PANIC, "HCA FMA Test Internal Error.");
2510*9e39c5baSBill Taylor 		break;
2511*9e39c5baSBill Taylor 	default:
2512*9e39c5baSBill Taylor 		/* OK, this is already registered. */
2513*9e39c5baSBill Taylor 		kmem_free(hash_key, strlen(key_buf) + 1);
2514*9e39c5baSBill Taylor 		break;
2515*9e39c5baSBill Taylor 	}
2516*9e39c5baSBill Taylor 	return (t_item);
2517*9e39c5baSBill Taylor }
2518*9e39c5baSBill Taylor 
2519*9e39c5baSBill Taylor 
2520*9e39c5baSBill Taylor /*
2521*9e39c5baSBill Taylor  *  void
2522*9e39c5baSBill Taylor  *  i_hca_test_set_item(int num, struct i_hca_fm_test *t_item)
2523*9e39c5baSBill Taylor  *
2524*9e39c5baSBill Taylor  *  Overview
2525*9e39c5baSBill Taylor  *      i_hca_test_set_item() is a private function used in
2526*9e39c5baSBill Taylor  *      i_hca_test_register() above. This function sets the testset specified
2527*9e39c5baSBill Taylor  *      (with the index number) to HCA FM function test structure.
2528*9e39c5baSBill Taylor  *
2529*9e39c5baSBill Taylor  *  Argument
2530*9e39c5baSBill Taylor  *      num: index to test set (testset structure array)
2531*9e39c5baSBill Taylor  *      t_item: pointer to HCA fM function test structure
2532*9e39c5baSBill Taylor  *
2533*9e39c5baSBill Taylor  *  Return value
2534*9e39c5baSBill Taylor  *      Nothing
2535*9e39c5baSBill Taylor  *
2536*9e39c5baSBill Taylor  *  Caller's context
2537*9e39c5baSBill Taylor  *      i_hca_test_set_item() can be called in user, kernel, interrupt
2538*9e39c5baSBill Taylor  *      context or hight interrupt context.
2539*9e39c5baSBill Taylor  *
2540*9e39c5baSBill Taylor  */
2541*9e39c5baSBill Taylor static void
2542*9e39c5baSBill Taylor i_hca_test_set_item(int num, struct i_hca_fm_test *t_item)
2543*9e39c5baSBill Taylor {
2544*9e39c5baSBill Taylor 	if (num < 0 || num >= sizeof (testset) / sizeof (hermon_test_t) ||
2545*9e39c5baSBill Taylor 	    testset[num].type != t_item->type) {
2546*9e39c5baSBill Taylor 		t_item->trigger = testset[0].trigger;
2547*9e39c5baSBill Taylor 		t_item->errcnt = testset[0].errcnt;
2548*9e39c5baSBill Taylor 		return;
2549*9e39c5baSBill Taylor 	}
2550*9e39c5baSBill Taylor 
2551*9e39c5baSBill Taylor 	/* Set the testsuite */
2552*9e39c5baSBill Taylor 	t_item->trigger = testset[num].trigger;
2553*9e39c5baSBill Taylor 	t_item->errcnt = testset[num].errcnt;
2554*9e39c5baSBill Taylor }
2555*9e39c5baSBill Taylor 
2556*9e39c5baSBill Taylor 
2557*9e39c5baSBill Taylor /*
2558*9e39c5baSBill Taylor  *  void
2559*9e39c5baSBill Taylor  *  i_hca_test_free_item(mod_hash_val_t val)
2560*9e39c5baSBill Taylor  *
2561*9e39c5baSBill Taylor  *  Overview
2562*9e39c5baSBill Taylor  *      i_hca_test_free_item() is a private function used to free HCA FM
2563*9e39c5baSBill Taylor  *      function test structure when i_hca_test_fini() is called. This function
2564*9e39c5baSBill Taylor  *      is registered as a destructor when the hash table is created in
2565*9e39c5baSBill Taylor  *      i_hca_test_init().
2566*9e39c5baSBill Taylor  *
2567*9e39c5baSBill Taylor  *  Argument
2568*9e39c5baSBill Taylor  *      val: pointer to the value stored in hash table (pointer to HCA FM
2569*9e39c5baSBill Taylor  *           function test structure)
2570*9e39c5baSBill Taylor  *
2571*9e39c5baSBill Taylor  *  Return value
2572*9e39c5baSBill Taylor  *      Nothing
2573*9e39c5baSBill Taylor  *
2574*9e39c5baSBill Taylor  *  Caller's context
2575*9e39c5baSBill Taylor  *      i_hca_test_free_item() can be called in user, kernel or interrupt
2576*9e39c5baSBill Taylor  *      context.
2577*9e39c5baSBill Taylor  *
2578*9e39c5baSBill Taylor  */
2579*9e39c5baSBill Taylor static void
2580*9e39c5baSBill Taylor i_hca_test_free_item(mod_hash_val_t val)
2581*9e39c5baSBill Taylor {
2582*9e39c5baSBill Taylor 	struct i_hca_fm_test *t_item = (struct i_hca_fm_test *)val;
2583*9e39c5baSBill Taylor 	kmem_free(t_item, sizeof (struct i_hca_fm_test));
2584*9e39c5baSBill Taylor }
2585*9e39c5baSBill Taylor #endif /* FMA_TEST */
2586