19e39c5baSBill Taylor /*
29e39c5baSBill Taylor  * CDDL HEADER START
39e39c5baSBill Taylor  *
49e39c5baSBill Taylor  * The contents of this file are subject to the terms of the
59e39c5baSBill Taylor  * Common Development and Distribution License (the "License").
69e39c5baSBill Taylor  * You may not use this file except in compliance with the License.
79e39c5baSBill Taylor  *
89e39c5baSBill Taylor  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99e39c5baSBill Taylor  * or http://www.opensolaris.org/os/licensing.
109e39c5baSBill Taylor  * See the License for the specific language governing permissions
119e39c5baSBill Taylor  * and limitations under the License.
129e39c5baSBill Taylor  *
139e39c5baSBill Taylor  * When distributing Covered Code, include this CDDL HEADER in each
149e39c5baSBill Taylor  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159e39c5baSBill Taylor  * If applicable, add the following below this CDDL HEADER, with the
169e39c5baSBill Taylor  * fields enclosed by brackets "[]" replaced with your own identifying
179e39c5baSBill Taylor  * information: Portions Copyright [yyyy] [name of copyright owner]
189e39c5baSBill Taylor  *
199e39c5baSBill Taylor  * CDDL HEADER END
209e39c5baSBill Taylor  */
219e39c5baSBill Taylor 
229e39c5baSBill Taylor /*
23*17a2b317SBill Taylor  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249e39c5baSBill Taylor  */
259e39c5baSBill Taylor 
269e39c5baSBill Taylor /*
279e39c5baSBill Taylor  * hermon_fm.c
289e39c5baSBill Taylor  *    Hermon (InfiniBand) HCA Driver Fault Management Routines
299e39c5baSBill Taylor  *
309e39c5baSBill Taylor  * [Hermon FM Implementation]
319e39c5baSBill Taylor  *
329e39c5baSBill Taylor  * Hermon FM recovers the system from a HW error situation and/or isolates a
339e39c5baSBill Taylor  * HW error by calling the FMA acc handle check functions. (calling
349e39c5baSBill Taylor  * ddi_fm_acc_err_get()) If a HW error is detected when either
359e39c5baSBill Taylor  * ddi_fm_acc_err_get() is called, to determine whether or not the error is
369e39c5baSBill Taylor  * transient, the I/O operation causing the error will retry up to three times.
379e39c5baSBill Taylor  *
389e39c5baSBill Taylor  * (Basic HW error recovery)
399e39c5baSBill Taylor  *
409e39c5baSBill Taylor  *        |
419e39c5baSBill Taylor  *  .---->*
429e39c5baSBill Taylor  *  |     |
439e39c5baSBill Taylor  *  |   issue an I/O request via PIO
449e39c5baSBill Taylor  *  |     |
459e39c5baSBill Taylor  *  |     |
469e39c5baSBill Taylor  *  |   check acc handle
479e39c5baSBill Taylor  *  |     |
489e39c5baSBill Taylor  *  |     |
499e39c5baSBill Taylor  *  `--< a HW error detected && retry count < 3 >
509e39c5baSBill Taylor  *        |
519e39c5baSBill Taylor  *        v
529e39c5baSBill Taylor  *
539e39c5baSBill Taylor  * When a HW error is detected, to provide the error information for users to
549e39c5baSBill Taylor  * isolate the faulted HW, Hermon FM issues Solaris FMA ereports as follows.
559e39c5baSBill Taylor  *
569e39c5baSBill Taylor  *  * PIO transient error
579e39c5baSBill Taylor  *         invalid_state => unaffected
589e39c5baSBill Taylor  *
599e39c5baSBill Taylor  *  * PIO persistent error
609e39c5baSBill Taylor  *         invalid_state => lost
619e39c5baSBill Taylor  *
629e39c5baSBill Taylor  *  * PIO fatal error
639e39c5baSBill Taylor  *         invalid_state => lost => panic
649e39c5baSBill Taylor  *
659e39c5baSBill Taylor  *  * Hermon HCA firmware error
669e39c5baSBill Taylor  *         invalid_state => degraded
679e39c5baSBill Taylor  *
689e39c5baSBill Taylor  *  * Other Hermon HCA specific errors
699e39c5baSBill Taylor  *	   uncorrect => unaffected
709e39c5baSBill Taylor  *		or
719e39c5baSBill Taylor  *	   correct => unaffected
729e39c5baSBill Taylor  *
739e39c5baSBill Taylor  * (Restrictions)
749e39c5baSBill Taylor  *
759e39c5baSBill Taylor  * The current implementation has the following restrictions.
769e39c5baSBill Taylor  *  * No runtime check/protection
779e39c5baSBill Taylor  *  * No detach time check/protection
789e39c5baSBill Taylor  *  * No DMA check/protection
799e39c5baSBill Taylor  *
809e39c5baSBill Taylor  * See the Hermon FMA portfolio in detail.
819e39c5baSBill Taylor  */
829e39c5baSBill Taylor 
839e39c5baSBill Taylor #include <sys/types.h>
849e39c5baSBill Taylor #include <sys/conf.h>
859e39c5baSBill Taylor #include <sys/ddi.h>
869e39c5baSBill Taylor #include <sys/sunddi.h>
879e39c5baSBill Taylor #include <sys/sysmacros.h>
889e39c5baSBill Taylor #include <sys/list.h>
899e39c5baSBill Taylor #include <sys/modhash.h>
909e39c5baSBill Taylor 
919e39c5baSBill Taylor #include <sys/ib/adapters/hermon/hermon.h>
929e39c5baSBill Taylor 
939e39c5baSBill Taylor /*
949e39c5baSBill Taylor  * Hermon driver has to disable its FM functionality
959e39c5baSBill Taylor  * if this "fm_capable" variable is defined or has a value
969e39c5baSBill Taylor  * in /kernel/drv/hermon.conf.
979e39c5baSBill Taylor  */
989e39c5baSBill Taylor static char *fm_cap = "fm-capable";	/* FM capability */
999e39c5baSBill Taylor 
1009e39c5baSBill Taylor static hermon_hca_fm_t hca_fm;		/* Hermon HCA FM Structure */
1019e39c5baSBill Taylor 
1029e39c5baSBill Taylor static void i_hca_fm_ereport(dev_info_t *, int, char *);
1039e39c5baSBill Taylor static void i_hca_fm_init(struct i_hca_fm *);
1049e39c5baSBill Taylor static void i_hca_fm_fini(struct i_hca_fm *);
1059e39c5baSBill Taylor static int i_hca_regs_map_setup(struct i_hca_fm *, dev_info_t *, uint_t,
1069e39c5baSBill Taylor     caddr_t *, offset_t, offset_t, ddi_device_acc_attr_t *, ddi_acc_handle_t *);
1079e39c5baSBill Taylor static void i_hca_regs_map_free(struct i_hca_fm *, ddi_acc_handle_t *);
1089e39c5baSBill Taylor static int i_hca_pci_config_setup(struct i_hca_fm *, dev_info_t *,
1099e39c5baSBill Taylor     ddi_acc_handle_t *);
1109e39c5baSBill Taylor static void i_hca_pci_config_teardown(struct i_hca_fm *, ddi_acc_handle_t *);
1119e39c5baSBill Taylor static int i_hca_pio_start(dev_info_t *, struct i_hca_acc_handle *,
1129e39c5baSBill Taylor     hermon_test_t *);
1139e39c5baSBill Taylor static int i_hca_pio_end(dev_info_t *, struct i_hca_acc_handle *, int *,
1149e39c5baSBill Taylor     hermon_test_t *);
1159e39c5baSBill Taylor static struct i_hca_acc_handle *i_hca_get_acc_handle(struct i_hca_fm *,
1169e39c5baSBill Taylor     ddi_acc_handle_t);
1179e39c5baSBill Taylor 
1189e39c5baSBill Taylor /* forward declaration for hermon_fm_{init, fini}() */
1199e39c5baSBill Taylor #ifdef FMA_TEST
1209e39c5baSBill Taylor static void i_hca_test_init(mod_hash_t **, mod_hash_t **);
1219e39c5baSBill Taylor static void i_hca_test_fini(mod_hash_t **, mod_hash_t **);
1229e39c5baSBill Taylor #endif /* FMA_TEST */
1239e39c5baSBill Taylor 
1249e39c5baSBill Taylor /*
1259e39c5baSBill Taylor  * Hermon FM Functions
1269e39c5baSBill Taylor  *
1279e39c5baSBill Taylor  * These functions are based on the HCA FM common interface
1289e39c5baSBill Taylor  * defined below, but specific to the Hermon HCA FM capabilities.
1299e39c5baSBill Taylor  */
1309e39c5baSBill Taylor 
1319e39c5baSBill Taylor /*
1329e39c5baSBill Taylor  *  void
1339e39c5baSBill Taylor  *  hermon_hca_fm_init(hermon_state_t *state, hermon_hca_fm_t *hca)
1349e39c5baSBill Taylor  *
1359e39c5baSBill Taylor  *  Overview
1369e39c5baSBill Taylor  *      hermon_hca_fm_init() initializes the Hermon FM resources.
1379e39c5baSBill Taylor  *
1389e39c5baSBill Taylor  *  Argument
1399e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1409e39c5baSBill Taylor  *      hca: pointer to Hermon FM structure
1419e39c5baSBill Taylor  *
1429e39c5baSBill Taylor  *  Return value
1439e39c5baSBill Taylor  *      Nothing
1449e39c5baSBill Taylor  *
1459e39c5baSBill Taylor  *  Caller's context
1469e39c5baSBill Taylor  *      hermon_hca_fm_init() can be called in user or kernel context only.
1479e39c5baSBill Taylor  */
1489e39c5baSBill Taylor static void
hermon_hca_fm_init(hermon_state_t * state,hermon_hca_fm_t * hca_fm)1499e39c5baSBill Taylor hermon_hca_fm_init(hermon_state_t *state, hermon_hca_fm_t *hca_fm)
1509e39c5baSBill Taylor {
1519e39c5baSBill Taylor 	state->hs_fm_hca_fm = hca_fm;
1529e39c5baSBill Taylor 	i_hca_fm_init((struct i_hca_fm *)hca_fm);
1539e39c5baSBill Taylor }
1549e39c5baSBill Taylor 
1559e39c5baSBill Taylor 
1569e39c5baSBill Taylor /*
1579e39c5baSBill Taylor  *  void
1589e39c5baSBill Taylor  *  hermon_hca_fm_fini(hermon_state_t *state)
1599e39c5baSBill Taylor  *
1609e39c5baSBill Taylor  *  Overview
1619e39c5baSBill Taylor  *      hermon_hca_fm_fini() releases the Hermon FM resources.
1629e39c5baSBill Taylor  *
1639e39c5baSBill Taylor  *  Argument
1649e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1659e39c5baSBill Taylor  *
1669e39c5baSBill Taylor  *  Return value
1679e39c5baSBill Taylor  *      Nothing
1689e39c5baSBill Taylor  *
1699e39c5baSBill Taylor  *  Caller's context
1709e39c5baSBill Taylor  *      hermon_hca_fm_fini() can be called in user or kernel context only.
1719e39c5baSBill Taylor  */
1729e39c5baSBill Taylor static void
hermon_hca_fm_fini(hermon_state_t * state)1739e39c5baSBill Taylor hermon_hca_fm_fini(hermon_state_t *state)
1749e39c5baSBill Taylor {
1759e39c5baSBill Taylor 	i_hca_fm_fini((struct i_hca_fm *)state->hs_fm_hca_fm);
1769e39c5baSBill Taylor 	state->hs_fm_hca_fm = NULL;
1779e39c5baSBill Taylor }
1789e39c5baSBill Taylor 
1799e39c5baSBill Taylor /*
1809e39c5baSBill Taylor  *  void
1819e39c5baSBill Taylor  *  hermon_clr_state_nolock(hermon_state_t *state, int fm_state)
1829e39c5baSBill Taylor  *
1839e39c5baSBill Taylor  *  Overview
1849e39c5baSBill Taylor  *      hermon_clr_state() drops the specified state from Hermon FM state
1859e39c5baSBill Taylor  *      without the mutex locks.
1869e39c5baSBill Taylor  *
1879e39c5baSBill Taylor  *  Argument
1889e39c5baSBill Taylor  *      state: pointer to Hermon state structure
1899e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
1909e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
1919e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
1929e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
1939e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
1949e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
1959e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
1969e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
1979e39c5baSBill Taylor  *
1989e39c5baSBill Taylor  *  Return value
1999e39c5baSBill Taylor  *  	Nothing
2009e39c5baSBill Taylor  *
2019e39c5baSBill Taylor  *  Caller's context
2029e39c5baSBill Taylor  *      hermon_clr_state() can be called in user, kernel, interrupt context
2039e39c5baSBill Taylor  *      or high interrupt context.
2049e39c5baSBill Taylor  */
2059e39c5baSBill Taylor void
hermon_clr_state_nolock(hermon_state_t * state,int fm_state)2069e39c5baSBill Taylor hermon_clr_state_nolock(hermon_state_t *state, int fm_state)
2079e39c5baSBill Taylor {
2089e39c5baSBill Taylor 	extern void membar_sync(void);
2099e39c5baSBill Taylor 
2109e39c5baSBill Taylor 	state->hs_fm_state &= ~fm_state;
2119e39c5baSBill Taylor 	membar_sync();
2129e39c5baSBill Taylor }
2139e39c5baSBill Taylor 
2149e39c5baSBill Taylor 
2159e39c5baSBill Taylor /*
2169e39c5baSBill Taylor  *  void
2179e39c5baSBill Taylor  *  hermon_clr_state(hermon_state_t *state, int fm_state)
2189e39c5baSBill Taylor  *
2199e39c5baSBill Taylor  *  Overview
2209e39c5baSBill Taylor  *      hermon_clr_state() drops the specified state from Hermon FM state.
2219e39c5baSBill Taylor  *
2229e39c5baSBill Taylor  *  Argument
2239e39c5baSBill Taylor  *      state: pointer to Hermon state structure
2249e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
2259e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
2269e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
2279e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
2289e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
2299e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
2309e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
2319e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
2329e39c5baSBill Taylor  *
2339e39c5baSBill Taylor  *  Return value
2349e39c5baSBill Taylor  *  	Nothing
2359e39c5baSBill Taylor  *
2369e39c5baSBill Taylor  *  Caller's context
2379e39c5baSBill Taylor  *      hermon_clr_state() can be called in user, kernel or interrupt context.
2389e39c5baSBill Taylor  */
2399e39c5baSBill Taylor static void
hermon_clr_state(hermon_state_t * state,int fm_state)2409e39c5baSBill Taylor hermon_clr_state(hermon_state_t *state, int fm_state)
2419e39c5baSBill Taylor {
2429e39c5baSBill Taylor 	ASSERT(fm_state != HCA_NO_FM);
2439e39c5baSBill Taylor 
2449e39c5baSBill Taylor 	mutex_enter(&state->hs_fm_lock);
2459e39c5baSBill Taylor 	hermon_clr_state_nolock(state, fm_state);
2469e39c5baSBill Taylor 	mutex_exit(&state->hs_fm_lock);
2479e39c5baSBill Taylor }
2489e39c5baSBill Taylor 
2499e39c5baSBill Taylor 
2509e39c5baSBill Taylor /*
2519e39c5baSBill Taylor  *  void
2529e39c5baSBill Taylor  *  hermon_set_state(hermon_state_t *state, int fm_state)
2539e39c5baSBill Taylor  *
2549e39c5baSBill Taylor  *  Overview
2559e39c5baSBill Taylor  *      hermon_set_state() sets Hermon FM state.
2569e39c5baSBill Taylor  *
2579e39c5baSBill Taylor  *  Argument
2589e39c5baSBill Taylor  *      state: pointer to Hermon state structure
2599e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
2609e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
2619e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
2629e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
2639e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
2649e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
2659e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
2669e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
2679e39c5baSBill Taylor  *
2689e39c5baSBill Taylor  *  Return value
2699e39c5baSBill Taylor  *  	Nothing
2709e39c5baSBill Taylor  *
2719e39c5baSBill Taylor  *  Caller's context
2729e39c5baSBill Taylor  *      hermon_set_state() can be called in user, kernel or interrupt context.
2739e39c5baSBill Taylor  */
2749e39c5baSBill Taylor static void
hermon_set_state(hermon_state_t * state,int fm_state)2759e39c5baSBill Taylor hermon_set_state(hermon_state_t *state, int fm_state)
2769e39c5baSBill Taylor {
2779e39c5baSBill Taylor 	extern void membar_sync(void);
2789e39c5baSBill Taylor 
2799e39c5baSBill Taylor 	mutex_enter(&state->hs_fm_lock);
2809e39c5baSBill Taylor 	if (fm_state == HCA_NO_FM) {
2819e39c5baSBill Taylor 		state->hs_fm_state = HCA_NO_FM;
2829e39c5baSBill Taylor 	} else {
2839e39c5baSBill Taylor 		state->hs_fm_state |= fm_state;
2849e39c5baSBill Taylor 	}
2859e39c5baSBill Taylor 	membar_sync();
2869e39c5baSBill Taylor 	mutex_exit(&state->hs_fm_lock);
2879e39c5baSBill Taylor }
2889e39c5baSBill Taylor 
2899e39c5baSBill Taylor 
2909e39c5baSBill Taylor /*
2919e39c5baSBill Taylor  *  int
2929e39c5baSBill Taylor  *  hermon_get_state(hermon_state_t *state)
2939e39c5baSBill Taylor  *
2949e39c5baSBill Taylor  *  Overview
2959e39c5baSBill Taylor  *      hermon_get_state() returns the current Hermon FM state.
2969e39c5baSBill Taylor  *
2979e39c5baSBill Taylor  *  Argument
2989e39c5baSBill Taylor  *      state: pointer to Hermon state structure
2999e39c5baSBill Taylor  *
3009e39c5baSBill Taylor  *  Return value
3019e39c5baSBill Taylor  *      fm_state: Hermon FM state, which is composed of:
3029e39c5baSBill Taylor  *		HCA_NO_FM	Hermom FM is not supported
3039e39c5baSBill Taylor  *		HCA_PIO_FM	PIO is fma-protected
3049e39c5baSBill Taylor  *		HCA_DMA_FM	DMA is fma-protected
3059e39c5baSBill Taylor  *		HCA_EREPORT_FM	FMA ereport is available
3069e39c5baSBill Taylor  *		HCA_ERRCB_FM	FMA error callback is supported
3079e39c5baSBill Taylor  *		HCA_ATTCH_FM	HCA FM attach mode
3089e39c5baSBill Taylor  *		HCA_RUNTM_FM	HCA FM runtime mode
3099e39c5baSBill Taylor  *
3109e39c5baSBill Taylor  *  Caller's context
3119e39c5baSBill Taylor  *      hermon_get_state() can be called in user, kernel or interrupt context.
3129e39c5baSBill Taylor  */
3139e39c5baSBill Taylor int
hermon_get_state(hermon_state_t * state)3149e39c5baSBill Taylor hermon_get_state(hermon_state_t *state)
3159e39c5baSBill Taylor {
3169e39c5baSBill Taylor 	return (state->hs_fm_state);
3179e39c5baSBill Taylor }
3189e39c5baSBill Taylor 
3199e39c5baSBill Taylor 
3209e39c5baSBill Taylor /*
3219e39c5baSBill Taylor  *  void
3229e39c5baSBill Taylor  *  hermon_fm_init(hermon_state_t *state)
3239e39c5baSBill Taylor  *
3249e39c5baSBill Taylor  *  Overview
3259e39c5baSBill Taylor  *      hermon_fm_init() is a Hermon FM initialization function which registers
3269e39c5baSBill Taylor  *      some FMA functions such as the ereport and the acc check capabilities
3279e39c5baSBill Taylor  *      for Hermon. If the "fm_disable" property in /kernel/drv/hermon.conf is
3289e39c5baSBill Taylor  *      defined (and/or its value is set), then the Hermon FM capabilities will
3299e39c5baSBill Taylor  *      drop, and only the default capabilities (the ereport and error callback
3309e39c5baSBill Taylor  *      capabilities) are available (and the action against HW errors is
3319e39c5baSBill Taylor  *      issuing an ereport then panicking the system).
3329e39c5baSBill Taylor  *
3339e39c5baSBill Taylor  *  Argument
3349e39c5baSBill Taylor  *      state: pointer to Hermon state structure
3359e39c5baSBill Taylor  *
3369e39c5baSBill Taylor  *  Return value
3379e39c5baSBill Taylor  *      Nothing
3389e39c5baSBill Taylor  *
3399e39c5baSBill Taylor  *  Caller's context
3409e39c5baSBill Taylor  *      hermon_fm_init() can be called in user or kernel context only.
3419e39c5baSBill Taylor  */
3429e39c5baSBill Taylor void
hermon_fm_init(hermon_state_t * state)3439e39c5baSBill Taylor hermon_fm_init(hermon_state_t *state)
3449e39c5baSBill Taylor {
3459e39c5baSBill Taylor 	ddi_iblock_cookie_t iblk;
3469e39c5baSBill Taylor 
3479e39c5baSBill Taylor 	/*
3489e39c5baSBill Taylor 	 * Check the "fm_disable" property. If it's defined,
3499e39c5baSBill Taylor 	 * use the Solaris FMA default action for Hermon.
3509e39c5baSBill Taylor 	 */
3519e39c5baSBill Taylor 	if (ddi_getprop(DDI_DEV_T_NONE, state->hs_dip, DDI_PROP_DONTPASS,
3529e39c5baSBill Taylor 	    "fm_disable", 0) != 0) {
3539e39c5baSBill Taylor 		state->hs_fm_disable = 1;
3549e39c5baSBill Taylor 	}
3559e39c5baSBill Taylor 
3569e39c5baSBill Taylor 	/* If hs_fm_diable is set, then skip the rest */
3579e39c5baSBill Taylor 	if (state->hs_fm_disable) {
3589e39c5baSBill Taylor 		hermon_set_state(state, HCA_NO_FM);
3599e39c5baSBill Taylor 		return;
3609e39c5baSBill Taylor 	}
3619e39c5baSBill Taylor 
3629e39c5baSBill Taylor 	/* Set the Hermon FM attach mode */
3639e39c5baSBill Taylor 	hermon_set_state(state, HCA_ATTCH_FM);
3649e39c5baSBill Taylor 
3659e39c5baSBill Taylor 	/* Initialize the Solaris FMA capabilities for the Hermon FM support */
3669e39c5baSBill Taylor 	state->hs_fm_capabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
3679e39c5baSBill Taylor 	    state->hs_dip, DDI_PROP_DONTPASS, fm_cap,
3689e39c5baSBill Taylor 	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE);
3699e39c5baSBill Taylor 
3709e39c5baSBill Taylor 	/*
3719e39c5baSBill Taylor 	 * The Hermon FM uses the ereport and acc check capabilites only,
3729e39c5baSBill Taylor 	 * but both of them should be available. If either is not, turn
3739e39c5baSBill Taylor 	 * hs_fm_disable on and behave in the same way as the "fm_diable"
3749e39c5baSBill Taylor 	 * property is set.
3759e39c5baSBill Taylor 	 */
3769e39c5baSBill Taylor 	if (state->hs_fm_capabilities !=
3779e39c5baSBill Taylor 	    (DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE)) {
3789e39c5baSBill Taylor 		state->hs_fm_disable = 1;
3799e39c5baSBill Taylor 		hermon_set_state(state, HCA_NO_FM);
3809e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
3819e39c5baSBill Taylor 		    "Hermon FM capability fails");
3829e39c5baSBill Taylor 		return;
3839e39c5baSBill Taylor 	}
3849e39c5baSBill Taylor 
3859e39c5baSBill Taylor 	/* Initialize the HCA FM resources */
3869e39c5baSBill Taylor 	hermon_hca_fm_init(state, &hca_fm);
3879e39c5baSBill Taylor 
3889e39c5baSBill Taylor 	/* Initialize the fm state lock */
3899e39c5baSBill Taylor 	mutex_init(&state->hs_fm_lock, NULL, MUTEX_DRIVER, NULL);
3909e39c5baSBill Taylor 
3919e39c5baSBill Taylor 	/* Register the capabilities with the IO fault services */
3929e39c5baSBill Taylor 	ddi_fm_init(state->hs_dip, &state->hs_fm_capabilities, &iblk);
3939e39c5baSBill Taylor 
3949e39c5baSBill Taylor 	/* Set up the pci ereport capabilities if the ereport is capable */
3959e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
3969e39c5baSBill Taylor 		pci_ereport_setup(state->hs_dip);
3979e39c5baSBill Taylor 	}
3989e39c5baSBill Taylor 
3999e39c5baSBill Taylor 	/* Set the Hermon FM state */
4009e39c5baSBill Taylor 	hermon_set_state(state, HCA_PIO_FM | HCA_EREPORT_FM);
4019e39c5baSBill Taylor 
4029e39c5baSBill Taylor #ifdef FMA_TEST
4039e39c5baSBill Taylor 	i_hca_test_init(&state->hs_fm_test_hash, &state->hs_fm_id_hash);
4049e39c5baSBill Taylor #endif /* FMA_TEST */
4059e39c5baSBill Taylor }
4069e39c5baSBill Taylor 
4079e39c5baSBill Taylor 
4089e39c5baSBill Taylor /*
4099e39c5baSBill Taylor  *  void
4109e39c5baSBill Taylor  *  hermon_fm_fini(hermon_state_t *state)
4119e39c5baSBill Taylor  *
4129e39c5baSBill Taylor  *  Overview
4139e39c5baSBill Taylor  *      hermon_fm_fini() is a Hermon FM finalization function which de-registers
4149e39c5baSBill Taylor  *      Solaris FMA functions set to Hermon.
4159e39c5baSBill Taylor  *
4169e39c5baSBill Taylor  *  Argument
4179e39c5baSBill Taylor  *      state: pointer to Hermon state structure
4189e39c5baSBill Taylor  *
4199e39c5baSBill Taylor  *  Return value
4209e39c5baSBill Taylor  *      Nothing
4219e39c5baSBill Taylor  *
4229e39c5baSBill Taylor  *  Caller's context
4239e39c5baSBill Taylor  *      hermon_fm_fini() can be called in user or kernel context only.
4249e39c5baSBill Taylor  */
4259e39c5baSBill Taylor void
hermon_fm_fini(hermon_state_t * state)4269e39c5baSBill Taylor hermon_fm_fini(hermon_state_t *state)
4279e39c5baSBill Taylor {
4289e39c5baSBill Taylor 	/*
4299e39c5baSBill Taylor 	 * If hermon_fm_diable is set or there is no FM service provided,
4309e39c5baSBill Taylor 	 * then skip the rest.
4319e39c5baSBill Taylor 	 */
4329e39c5baSBill Taylor 	if (state->hs_fm_disable || hermon_get_state(state) == HCA_NO_FM) {
4339e39c5baSBill Taylor 		return;
4349e39c5baSBill Taylor 	}
4359e39c5baSBill Taylor 
4369e39c5baSBill Taylor 	ASSERT(!(hermon_get_state(state) & HCA_ERRCB_FM));
4379e39c5baSBill Taylor 
4389e39c5baSBill Taylor #ifdef FMA_TEST
4399e39c5baSBill Taylor 	i_hca_test_fini(&state->hs_fm_test_hash, &state->hs_fm_id_hash);
4409e39c5baSBill Taylor #endif /* FMA_TEST */
4419e39c5baSBill Taylor 
4429e39c5baSBill Taylor 	/* Set the Hermon FM state to no support */
4439e39c5baSBill Taylor 	hermon_set_state(state, HCA_NO_FM);
4449e39c5baSBill Taylor 
4459e39c5baSBill Taylor 	/* Release HCA FM resources */
4469e39c5baSBill Taylor 	hermon_hca_fm_fini(state);
4479e39c5baSBill Taylor 
4489e39c5baSBill Taylor 	/*
4499e39c5baSBill Taylor 	 * Release any resources allocated by pci_ereport_setup()
4509e39c5baSBill Taylor 	 */
4519e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
4529e39c5baSBill Taylor 		pci_ereport_teardown(state->hs_dip);
4539e39c5baSBill Taylor 	}
4549e39c5baSBill Taylor 
4559e39c5baSBill Taylor 	/* De-register the Hermon FM from the IO fault services */
4569e39c5baSBill Taylor 	ddi_fm_fini(state->hs_dip);
4579e39c5baSBill Taylor }
4589e39c5baSBill Taylor 
4599e39c5baSBill Taylor 
4609e39c5baSBill Taylor /*
4619e39c5baSBill Taylor  *  int
4629e39c5baSBill Taylor  *  hermon_fm_ereport_init(hermon_state_t *state)
4639e39c5baSBill Taylor  *
4649e39c5baSBill Taylor  *  Overview
4659e39c5baSBill Taylor  *      hermon_fm_ereport_init() changes the Hermon FM state to the ereport
4669e39c5baSBill Taylor  *      only mode during the driver attach.
4679e39c5baSBill Taylor  *
4689e39c5baSBill Taylor  *  Argument
4699e39c5baSBill Taylor  *      state: pointer to Hermon state structure
4709e39c5baSBill Taylor  *
4719e39c5baSBill Taylor  *  Return value
4729e39c5baSBill Taylor  *      DDI_SUCCESS
4739e39c5baSBill Taylor  *      DDI_FAILURE
4749e39c5baSBill Taylor  *
4759e39c5baSBill Taylor  *  Caller's context
4769e39c5baSBill Taylor  *      hermon_fm_ereport_init() can be called in user or kernel context only.
4779e39c5baSBill Taylor  */
4789e39c5baSBill Taylor int
hermon_fm_ereport_init(hermon_state_t * state)4799e39c5baSBill Taylor hermon_fm_ereport_init(hermon_state_t *state)
4809e39c5baSBill Taylor {
4819e39c5baSBill Taylor 	ddi_iblock_cookie_t iblk;
4829e39c5baSBill Taylor 	hermon_cfg_profile_t *cfgprof;
4839e39c5baSBill Taylor 	hermon_hw_querydevlim_t	*devlim;
4849e39c5baSBill Taylor 	hermon_rsrc_hw_entry_info_t entry_info;
4859e39c5baSBill Taylor 	hermon_rsrc_pool_info_t	*rsrc_pool;
4869e39c5baSBill Taylor 	uint64_t offset, num, max, num_prealloc;
4879e39c5baSBill Taylor 	ddi_device_acc_attr_t dev_attr = {
4889e39c5baSBill Taylor 		DDI_DEVICE_ATTR_V0,
4899e39c5baSBill Taylor 		DDI_STRUCTURE_LE_ACC,
4909e39c5baSBill Taylor 		DDI_STRICTORDER_ACC,
4919e39c5baSBill Taylor 		DDI_DEFAULT_ACC
4929e39c5baSBill Taylor 	};
4939e39c5baSBill Taylor 	char *rsrc_name;
4949e39c5baSBill Taylor 	extern void membar_sync(void);
4959e39c5baSBill Taylor 
4969e39c5baSBill Taylor 	/* Stop the poll thread while the FM state is being changed */
4979e39c5baSBill Taylor 	state->hs_fm_poll_suspend = B_TRUE;
4989e39c5baSBill Taylor 	membar_sync();
4999e39c5baSBill Taylor 
5009e39c5baSBill Taylor 	/*
5019e39c5baSBill Taylor 	 * Disable the Hermon interrupt after the interrupt capability flag
5029e39c5baSBill Taylor 	 * is checked.
5039e39c5baSBill Taylor 	 */
5049e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
5059e39c5baSBill Taylor 		if (ddi_intr_block_disable
5069e39c5baSBill Taylor 		    (&state->hs_intrmsi_hdl[0], 1) != DDI_SUCCESS) {
5079e39c5baSBill Taylor 			return (DDI_FAILURE);
5089e39c5baSBill Taylor 		}
5099e39c5baSBill Taylor 	} else {
5109e39c5baSBill Taylor 		if (ddi_intr_disable
5119e39c5baSBill Taylor 		    (state->hs_intrmsi_hdl[0]) != DDI_SUCCESS) {
5129e39c5baSBill Taylor 			return (DDI_FAILURE);
5139e39c5baSBill Taylor 		}
5149e39c5baSBill Taylor 	}
5159e39c5baSBill Taylor 
5169e39c5baSBill Taylor 	/*
5179e39c5baSBill Taylor 	 * Release any resources allocated by pci_ereport_setup()
5189e39c5baSBill Taylor 	 */
5199e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
5209e39c5baSBill Taylor 		pci_ereport_teardown(state->hs_dip);
5219e39c5baSBill Taylor 	}
5229e39c5baSBill Taylor 
5239e39c5baSBill Taylor 	/* De-register the Hermon FM from the IO fault services */
5249e39c5baSBill Taylor 	ddi_fm_fini(state->hs_dip);
5259e39c5baSBill Taylor 
5269e39c5baSBill Taylor 	/* Re-initialize fm ereport with the ereport only */
5279e39c5baSBill Taylor 	state->hs_fm_capabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
5289e39c5baSBill Taylor 	    state->hs_dip, DDI_PROP_DONTPASS, fm_cap,
5299e39c5baSBill Taylor 	    DDI_FM_EREPORT_CAPABLE);
5309e39c5baSBill Taylor 
5319e39c5baSBill Taylor 	/*
5329e39c5baSBill Taylor 	 * Now that the Hermon FM uses the ereport capability only,
5339e39c5baSBill Taylor 	 * If it's not set, turn hs_fm_disable on and behave in the
5349e39c5baSBill Taylor 	 * same way as the "fm_diable" property is set.
5359e39c5baSBill Taylor 	 */
5369e39c5baSBill Taylor 	if (state->hs_fm_capabilities != DDI_FM_EREPORT_CAPABLE) {
5379e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
5389e39c5baSBill Taylor 		    "Hermon FM ereport fails (ereport mode)");
5399e39c5baSBill Taylor 		goto error;
5409e39c5baSBill Taylor 	}
5419e39c5baSBill Taylor 
5429e39c5baSBill Taylor 	/* Re-register the ereport capability with the IO fault services */
5439e39c5baSBill Taylor 	ddi_fm_init(state->hs_dip, &state->hs_fm_capabilities, &iblk);
5449e39c5baSBill Taylor 
5459e39c5baSBill Taylor 	/* Initialize the pci ereport capabilities if the ereport is capable */
5469e39c5baSBill Taylor 	if (DDI_FM_EREPORT_CAP(state->hs_fm_capabilities)) {
5479e39c5baSBill Taylor 		pci_ereport_setup(state->hs_dip);
5489e39c5baSBill Taylor 	}
5499e39c5baSBill Taylor 
5509e39c5baSBill Taylor 	/* Setup for PCI config read/write of HCA device */
5519e39c5baSBill Taylor 	if (pci_config_setup(state->hs_dip, &state->hs_reg_pcihdl) !=
5529e39c5baSBill Taylor 	    DDI_SUCCESS) {
5539e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
5549e39c5baSBill Taylor 		    "PCI config mapping fails (ereport mode)");
5559e39c5baSBill Taylor 		goto error;
5569e39c5baSBill Taylor 	}
5579e39c5baSBill Taylor 
5589e39c5baSBill Taylor 	/* Allocate the regular access handle for MSI-X tables */
5599e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, state->hs_msix_tbl_rnumber,
5609e39c5baSBill Taylor 	    (caddr_t *)&state->hs_msix_tbl_addr, state->hs_msix_tbl_offset,
5619e39c5baSBill Taylor 	    state->hs_msix_tbl_size, &dev_attr,
5629e39c5baSBill Taylor 	    &state->hs_reg_msix_tblhdl) != DDI_SUCCESS) {
5639e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
5649e39c5baSBill Taylor 		    "MSI-X Table mapping fails (ereport mode)");
5659e39c5baSBill Taylor 		goto error;
5669e39c5baSBill Taylor 	}
5679e39c5baSBill Taylor 
5689e39c5baSBill Taylor 	/* Allocate the regular access handle for MSI-X PBA */
5699e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, state->hs_msix_pba_rnumber,
5709e39c5baSBill Taylor 	    (caddr_t *)&state->hs_msix_pba_addr, state->hs_msix_pba_offset,
5719e39c5baSBill Taylor 	    state->hs_msix_pba_size, &dev_attr,
5729e39c5baSBill Taylor 	    &state->hs_reg_msix_pbahdl) != DDI_SUCCESS) {
5739e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
5749e39c5baSBill Taylor 		    "MSI-X PBA mapping fails (ereport mode)");
5759e39c5baSBill Taylor 		goto error;
5769e39c5baSBill Taylor 	}
5779e39c5baSBill Taylor 
5789e39c5baSBill Taylor 	/* Allocate the regular access handle for Hermon CMD I/O space */
5799e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, HERMON_CMD_BAR,
5809e39c5baSBill Taylor 	    &state->hs_reg_cmd_baseaddr, 0, 0, &state->hs_reg_accattr,
5819e39c5baSBill Taylor 	    &state->hs_reg_cmdhdl) != DDI_SUCCESS) {
5829e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
5839e39c5baSBill Taylor 		    "CMD_BAR mapping fails (ereport mode)");
5849e39c5baSBill Taylor 		goto error;
5859e39c5baSBill Taylor 	}
5869e39c5baSBill Taylor 
5879e39c5baSBill Taylor 	/* Reset the host command register */
5889e39c5baSBill Taylor 	state->hs_cmd_regs.hcr = (hermon_hw_hcr_t *)
5899e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr + HERMON_CMD_HCR_OFFSET);
5909e39c5baSBill Taylor 
5919e39c5baSBill Taylor 	/* Reset the software reset register */
5929e39c5baSBill Taylor 	state->hs_cmd_regs.sw_reset = (uint32_t *)
5939e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr +
5949e39c5baSBill Taylor 	    HERMON_CMD_SW_RESET_OFFSET);
5959e39c5baSBill Taylor 
5969e39c5baSBill Taylor 	/* Reset the software reset register semaphore */
5979e39c5baSBill Taylor 	state->hs_cmd_regs.sw_semaphore = (uint32_t *)
5989e39c5baSBill Taylor 	    ((uintptr_t)state->hs_reg_cmd_baseaddr +
5999e39c5baSBill Taylor 	    HERMON_CMD_SW_SEMAPHORE_OFFSET);
6009e39c5baSBill Taylor 
6019e39c5baSBill Taylor 	/* Calculate the clear interrupt register offset */
6029e39c5baSBill Taylor 	offset = state->hs_fw.clr_intr_offs & HERMON_CMD_OFFSET_MASK;
6039e39c5baSBill Taylor 
6049e39c5baSBill Taylor 	/* Reset the clear interrupt address */
6059e39c5baSBill Taylor 	state->hs_cmd_regs.clr_intr = (uint64_t *)
6069e39c5baSBill Taylor 	    (uintptr_t)(state->hs_reg_cmd_baseaddr + offset);
6079e39c5baSBill Taylor 
6089e39c5baSBill Taylor 	/* Reset the internal error buffer address */
6099e39c5baSBill Taylor 	state->hs_cmd_regs.fw_err_buf = (uint32_t *)(uintptr_t)
6109e39c5baSBill Taylor 	    (state->hs_reg_cmd_baseaddr + state->hs_fw.error_buf_addr);
6119e39c5baSBill Taylor 
6129e39c5baSBill Taylor 	/* Check if the blue flame is enabled, and set the offset value */
6139e39c5baSBill Taylor 	if (state->hs_devlim.blu_flm) {
6149e39c5baSBill Taylor 		offset = (uint64_t)1 <<
6159e39c5baSBill Taylor 		    (state->hs_devlim.log_max_uar_sz + 20);
6169e39c5baSBill Taylor 	} else {
6179e39c5baSBill Taylor 		offset = 0;
6189e39c5baSBill Taylor 	}
6199e39c5baSBill Taylor 
6209e39c5baSBill Taylor 	/* Allocate the regular access handle for Hermon UAR I/O space */
6219e39c5baSBill Taylor 	if (ddi_regs_map_setup(state->hs_dip, HERMON_UAR_BAR,
6229e39c5baSBill Taylor 	    &state->hs_reg_uar_baseaddr, 0, offset,
6239e39c5baSBill Taylor 	    &state->hs_reg_accattr, &state->hs_reg_uarhdl) != DDI_SUCCESS) {
6249e39c5baSBill Taylor 		HERMON_ATTACH_MSG(state->hs_attach_buf,
6259e39c5baSBill Taylor 		    "UAR BAR mapping fails (ereport mode)");
6269e39c5baSBill Taylor 		goto error;
6279e39c5baSBill Taylor 	}
6289e39c5baSBill Taylor 
629*17a2b317SBill Taylor 	hermon_eq_reset_uar_baseaddr(state);
630*17a2b317SBill Taylor 
6319e39c5baSBill Taylor 	/* Drop the Hermon FM Attach Mode */
6329e39c5baSBill Taylor 	hermon_clr_state(state, HCA_ATTCH_FM);
6339e39c5baSBill Taylor 
6349e39c5baSBill Taylor 	/* Set the Hermon FM Runtime Mode */
6359e39c5baSBill Taylor 	hermon_set_state(state, HCA_RUNTM_FM);
6369e39c5baSBill Taylor 
6379e39c5baSBill Taylor 	/* Free up Hermon UAR page #1 */
6389e39c5baSBill Taylor 	hermon_rsrc_free(state, &state->hs_uarkpg_rsrc);
6399e39c5baSBill Taylor 
6409e39c5baSBill Taylor 	/* Free up the UAR pool */
6419e39c5baSBill Taylor 	entry_info.hwi_rsrcpool = &state->hs_rsrc_hdl[HERMON_UARPG];
6429e39c5baSBill Taylor 	hermon_rsrc_hw_entries_fini(state, &entry_info);
6439e39c5baSBill Taylor 
6449e39c5baSBill Taylor 	/* Re-allocate the UAR pool */
6459e39c5baSBill Taylor 	cfgprof = state->hs_cfg_profile;
6469e39c5baSBill Taylor 	devlim	= &state->hs_devlim;
6479e39c5baSBill Taylor 	num			  = ((uint64_t)1 << cfgprof->cp_log_num_uar);
6489e39c5baSBill Taylor 	max			  = num;
6499e39c5baSBill Taylor 	num_prealloc		  = max(devlim->num_rsvd_uar, 128);
6509e39c5baSBill Taylor 	rsrc_pool		  = &state->hs_rsrc_hdl[HERMON_UARPG];
6519e39c5baSBill Taylor 	rsrc_pool->rsrc_type	  = HERMON_UARPG;
6529e39c5baSBill Taylor 	rsrc_pool->rsrc_loc	  = HERMON_IN_UAR;
6539e39c5baSBill Taylor 	rsrc_pool->rsrc_pool_size = (num << PAGESHIFT);
6549e39c5baSBill Taylor 	rsrc_pool->rsrc_shift	  = PAGESHIFT;
6559e39c5baSBill Taylor 	rsrc_pool->rsrc_quantum	  = (uint_t)PAGESIZE;
6569e39c5baSBill Taylor 	rsrc_pool->rsrc_align	  = PAGESIZE;
6579e39c5baSBill Taylor 	rsrc_pool->rsrc_state	  = state;
6589e39c5baSBill Taylor 	rsrc_pool->rsrc_start	  = (void *)state->hs_reg_uar_baseaddr;
6599e39c5baSBill Taylor 	rsrc_name = (char *)kmem_zalloc(HERMON_RSRC_NAME_MAXLEN, KM_SLEEP);
6609e39c5baSBill Taylor 	HERMON_RSRC_NAME(rsrc_name, HERMON_UAR_PAGE_VMEM_RUNTM);
6619e39c5baSBill Taylor 	entry_info.hwi_num	  = num;
6629e39c5baSBill Taylor 	entry_info.hwi_max	  = max;
6639e39c5baSBill Taylor 	entry_info.hwi_prealloc	  = num_prealloc;
6649e39c5baSBill Taylor 	entry_info.hwi_rsrcpool	  = rsrc_pool;
6659e39c5baSBill Taylor 	entry_info.hwi_rsrcname	  = rsrc_name;
6669e39c5baSBill Taylor 	if (hermon_rsrc_hw_entries_init(state, &entry_info) != DDI_SUCCESS) {
6679e39c5baSBill Taylor 		kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
6689e39c5baSBill Taylor 		goto error;
6699e39c5baSBill Taylor 	}
6709e39c5baSBill Taylor 	kmem_free(rsrc_name, HERMON_RSRC_NAME_MAXLEN);
6719e39c5baSBill Taylor 
6729e39c5baSBill Taylor 	/* Re-allocate the kernel UAR page */
6739e39c5baSBill Taylor 	if (hermon_rsrc_alloc(state, HERMON_UARPG, 1, HERMON_SLEEP,
6749e39c5baSBill Taylor 	    &state->hs_uarkpg_rsrc) != DDI_SUCCESS) {
6759e39c5baSBill Taylor 		goto error;
6769e39c5baSBill Taylor 	}
6779e39c5baSBill Taylor 
6789e39c5baSBill Taylor 	/* Setup pointer to kernel UAR page */
6799e39c5baSBill Taylor 	state->hs_uar = (hermon_hw_uar_t *)state->hs_uarkpg_rsrc->hr_addr;
6809e39c5baSBill Taylor 
6819e39c5baSBill Taylor 	/* Now drop the the Hermon PIO FM */
6829e39c5baSBill Taylor 	hermon_clr_state(state, HCA_PIO_FM);
6839e39c5baSBill Taylor 
6849e39c5baSBill Taylor 	/* Release the MSI-X Table access handle */
6859e39c5baSBill Taylor 	if (state->hs_fm_msix_tblhdl) {
6869e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_msix_tblhdl);
6879e39c5baSBill Taylor 		state->hs_fm_msix_tblhdl = NULL;
6889e39c5baSBill Taylor 	}
6899e39c5baSBill Taylor 
6909e39c5baSBill Taylor 	/* Release the MSI-X PBA access handle */
6919e39c5baSBill Taylor 	if (state->hs_fm_msix_pbahdl) {
6929e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_msix_pbahdl);
6939e39c5baSBill Taylor 		state->hs_fm_msix_pbahdl = NULL;
6949e39c5baSBill Taylor 	}
6959e39c5baSBill Taylor 
6969e39c5baSBill Taylor 	/* Release the pci config space access handle */
6979e39c5baSBill Taylor 	if (state->hs_fm_pcihdl) {
6989e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_pcihdl);
6999e39c5baSBill Taylor 		state->hs_fm_pcihdl = NULL;
7009e39c5baSBill Taylor 	}
7019e39c5baSBill Taylor 
7029e39c5baSBill Taylor 	/* Release the cmd protected access handle */
7039e39c5baSBill Taylor 	if (state->hs_fm_cmdhdl) {
7049e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_cmdhdl);
7059e39c5baSBill Taylor 		state->hs_fm_cmdhdl = NULL;
7069e39c5baSBill Taylor 	}
7079e39c5baSBill Taylor 
7089e39c5baSBill Taylor 	/* Release the uar fma-protected access handle */
7099e39c5baSBill Taylor 	if (state->hs_fm_uarhdl) {
7109e39c5baSBill Taylor 		hermon_regs_map_free(state, &state->hs_fm_uarhdl);
7119e39c5baSBill Taylor 		state->hs_fm_uarhdl = NULL;
7129e39c5baSBill Taylor 	}
7139e39c5baSBill Taylor 
7149e39c5baSBill Taylor 	/* Enable the Hermon interrupt again */
7159e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
7169e39c5baSBill Taylor 		if (ddi_intr_block_enable
7179e39c5baSBill Taylor 		    (&state->hs_intrmsi_hdl[0], 1) != DDI_SUCCESS) {
7189e39c5baSBill Taylor 			return (DDI_FAILURE);
7199e39c5baSBill Taylor 		}
7209e39c5baSBill Taylor 	} else {
7219e39c5baSBill Taylor 		if (ddi_intr_enable
7229e39c5baSBill Taylor 		    (state->hs_intrmsi_hdl[0]) != DDI_SUCCESS) {
7239e39c5baSBill Taylor 			return (DDI_FAILURE);
7249e39c5baSBill Taylor 		}
7259e39c5baSBill Taylor 	}
7269e39c5baSBill Taylor 
7279e39c5baSBill Taylor 	/* Restart the poll thread */
7289e39c5baSBill Taylor 	state->hs_fm_poll_suspend = B_FALSE;
7299e39c5baSBill Taylor 
7309e39c5baSBill Taylor 	return (DDI_SUCCESS);
7319e39c5baSBill Taylor 
7329e39c5baSBill Taylor error:
7339e39c5baSBill Taylor 	/* Enable the Hermon interrupt again */
7349e39c5baSBill Taylor 	if (state->hs_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
7359e39c5baSBill Taylor 		(void) ddi_intr_block_enable(&state->hs_intrmsi_hdl[0], 1);
7369e39c5baSBill Taylor 	} else {
7379e39c5baSBill Taylor 		(void) ddi_intr_enable(state->hs_intrmsi_hdl[0]);
7389e39c5baSBill Taylor 	}
7399e39c5baSBill Taylor 	return (DDI_FAILURE);
7409e39c5baSBill Taylor }
7419e39c5baSBill Taylor 
7429e39c5baSBill Taylor 
7439e39c5baSBill Taylor /*
7449e39c5baSBill Taylor  *  int
7459e39c5baSBill Taylor  *  hermon_regs_map_setup(hermon_state_t *state, uint_t rnumber, caddr_t *addrp,
7469e39c5baSBill Taylor  *	offset_t offset, offset_t len, ddi_device_acc_attr_t *accattrp,
7479e39c5baSBill Taylor  *	ddi_acc_handle_t *handle)
7489e39c5baSBill Taylor  *
7499e39c5baSBill Taylor  *  Overview
7509e39c5baSBill Taylor  *      This is a wrapper function of i_hca_regs_map_setup() for Hermon FM so
7519e39c5baSBill Taylor  *      that it calls i_hca_regs_map_setup() inside after it checks the
7529e39c5baSBill Taylor  *      "fm_disable" configuration property. If the "fm_disable" is described
7539e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls ddi_regs_map_setup()
7549e39c5baSBill Taylor  *      directly instead.
7559e39c5baSBill Taylor  *      See i_hca_regs_map_setup() in detail.
7569e39c5baSBill Taylor  *
7579e39c5baSBill Taylor  *  Argument
7589e39c5baSBill Taylor  *      state: pointer to Hermon state structure
7599e39c5baSBill Taylor  *      rnumber: index number to the register address space set
7609e39c5baSBill Taylor  *      addrp: platform-dependent value (same as ddi_regs_map_setup())
7619e39c5baSBill Taylor  *      offset: offset into the register address space
7629e39c5baSBill Taylor  *      len: address space length to be mapped
7639e39c5baSBill Taylor  *      accattrp: pointer to device access attribute structure
7649e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
7659e39c5baSBill Taylor  *
7669e39c5baSBill Taylor  *  Return value
7679e39c5baSBill Taylor  *      ddi function status value which are:
7689e39c5baSBill Taylor  *      	DDI_SUCCESS
7699e39c5baSBill Taylor  *      	DDI_FAILURE
7709e39c5baSBill Taylor  *      	DDI_ME_RNUMBER_RNGE
7719e39c5baSBill Taylor  *      	DDI_REGS_ACC_CONFLICT
7729e39c5baSBill Taylor  *
7739e39c5baSBill Taylor  *  Caller's context
7749e39c5baSBill Taylor  *      hermon_regs_map_setup() can be called in user or kernel context only.
7759e39c5baSBill Taylor  */
7769e39c5baSBill Taylor int
hermon_regs_map_setup(hermon_state_t * state,uint_t rnumber,caddr_t * addrp,offset_t offset,offset_t len,ddi_device_acc_attr_t * accattrp,ddi_acc_handle_t * handle)7779e39c5baSBill Taylor hermon_regs_map_setup(hermon_state_t *state, uint_t rnumber, caddr_t *addrp,
7789e39c5baSBill Taylor 	offset_t offset, offset_t len, ddi_device_acc_attr_t *accattrp,
7799e39c5baSBill Taylor 	ddi_acc_handle_t *handle)
7809e39c5baSBill Taylor {
7819e39c5baSBill Taylor 	if (state->hs_fm_disable) {
7829e39c5baSBill Taylor 		return (ddi_regs_map_setup(state->hs_dip, rnumber, addrp,
7839e39c5baSBill Taylor 		    offset, len, accattrp, handle));
7849e39c5baSBill Taylor 	} else {
7859e39c5baSBill Taylor 		return (i_hca_regs_map_setup(state->hs_fm_hca_fm, state->hs_dip,
7869e39c5baSBill Taylor 		    rnumber, addrp, offset, len, accattrp, handle));
7879e39c5baSBill Taylor 	}
7889e39c5baSBill Taylor }
7899e39c5baSBill Taylor 
7909e39c5baSBill Taylor 
7919e39c5baSBill Taylor /*
7929e39c5baSBill Taylor  *  void
7939e39c5baSBill Taylor  *  hermon_regs_map_free(hermon_state_t *state, ddi_acc_handle_t *handlep)
7949e39c5baSBill Taylor  *
7959e39c5baSBill Taylor  *  Overview
7969e39c5baSBill Taylor  *      This is a wrapper function of i_hca_regs_map_free() for Hermon FM so
7979e39c5baSBill Taylor  *      that it calls i_hca_regs_map_free() inside after it checks the
7989e39c5baSBill Taylor  *      "fm_disable" configuration property. If the "fm_disable" is described
7999e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls ddi_regs_map_fre()
8009e39c5baSBill Taylor  *      directly instead.  See i_hca_regs_map_free() in detail.
8019e39c5baSBill Taylor  *
8029e39c5baSBill Taylor  *  Argument
8039e39c5baSBill Taylor  *      state: pointer to Hermon state structure
8049e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
8059e39c5baSBill Taylor  *
8069e39c5baSBill Taylor  *  Return value
8079e39c5baSBill Taylor  *      Nothing
8089e39c5baSBill Taylor  *
8099e39c5baSBill Taylor  *  Caller's context
8109e39c5baSBill Taylor  *      hermon_regs_map_free() can be called in user or kernel context only.
8119e39c5baSBill Taylor  *
8129e39c5baSBill Taylor  *  Note that the handle passed to hermon_regs_map_free() is NULL-cleared
8139e39c5baSBill Taylor  *  after this function is called.
8149e39c5baSBill Taylor  */
8159e39c5baSBill Taylor void
hermon_regs_map_free(hermon_state_t * state,ddi_acc_handle_t * handle)8169e39c5baSBill Taylor hermon_regs_map_free(hermon_state_t *state, ddi_acc_handle_t *handle)
8179e39c5baSBill Taylor {
8189e39c5baSBill Taylor 	if (state->hs_fm_disable) {
8199e39c5baSBill Taylor 		ddi_regs_map_free(handle);
8209e39c5baSBill Taylor 		*handle = NULL;
8219e39c5baSBill Taylor 	} else {
8229e39c5baSBill Taylor 		i_hca_regs_map_free(state->hs_fm_hca_fm, handle);
8239e39c5baSBill Taylor 	}
8249e39c5baSBill Taylor }
8259e39c5baSBill Taylor 
8269e39c5baSBill Taylor 
8279e39c5baSBill Taylor /*
8289e39c5baSBill Taylor  *  int
8299e39c5baSBill Taylor  *  hermon_pci_config_setup(hermon_state_t *state, ddi_acc_handle_t *handle)
8309e39c5baSBill Taylor  *
8319e39c5baSBill Taylor  *  Overview
8329e39c5baSBill Taylor  *      This is a wrapper function of i_hca_pci_config_setup() for Hermon FM so
8339e39c5baSBill Taylor  *      that it calls i_hca_pci_config_setup() inside after it checks the
8349e39c5baSBill Taylor  *      "fm-disable" configuration property. If the "fm_disable" is described
8359e39c5baSBill Taylor  *      in /kernel/drv/hermon.conf, the function calls pci_config_setup()
8369e39c5baSBill Taylor  *      directly instead. See i_hca_pci_config_setup() in detail.
8379e39c5baSBill Taylor  *
8389e39c5baSBill Taylor  *  Argument
8399e39c5baSBill Taylor  *      state: pointer to Hermon state structure
8409e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
8419e39c5baSBill Taylor  *
8429e39c5baSBill Taylor  *  Return value
8439e39c5baSBill Taylor  *      ddi function status value which are:
8449e39c5baSBill Taylor  *      	DDI_SUCCESS
8459e39c5baSBill Taylor  *      	DDI_FAILURE
8469e39c5baSBill Taylor  *
8479e39c5baSBill Taylor  *  Caller's context
8489e39c5baSBill Taylor  *      hermon_pci_config_setup() can be called in user or kernel context only.
8499e39c5baSBill Taylor  */
8509e39c5baSBill Taylor int
hermon_pci_config_setup(hermon_state_t * state,ddi_acc_handle_t * handle)8519e39c5baSBill Taylor hermon_pci_config_setup(hermon_state_t *state, ddi_acc_handle_t *handle)
8529e39c5baSBill Taylor {
8539e39c5baSBill Taylor 	if (state->hs_fm_disable) {
8549e39c5baSBill Taylor 		return (pci_config_setup(state->hs_dip, handle));
8559e39c5baSBill Taylor 	} else {
8569e39c5baSBill Taylor 		/* Check Hermon FM and Solaris FMA capability flags */
8579e39c5baSBill Taylor 		ASSERT((hermon_get_state(state) & HCA_PIO_FM &&
8589e39c5baSBill Taylor 		    DDI_FM_ACC_ERR_CAP(ddi_fm_capable(state->hs_dip))) ||
8599e39c5baSBill Taylor 		    (!(hermon_get_state(state) & HCA_PIO_FM) &&
8609e39c5baSBill Taylor 		    !DDI_FM_ACC_ERR_CAP(ddi_fm_capable(state->hs_dip))));
8619e39c5baSBill Taylor 		return (i_hca_pci_config_setup(state->hs_fm_hca_fm,
8629e39c5baSBill Taylor 		    state->hs_dip, handle));
8639e39c5baSBill Taylor 	}
8649e39c5baSBill Taylor }
8659e39c5baSBill Taylor 
8669e39c5baSBill Taylor 
8679e39c5baSBill Taylor /*
8689e39c5baSBill Taylor  *  void
8699e39c5baSBill Taylor  *  hermon_pci_config_teardown(hermon_state_t *state, ddi_acc_handle_t *handle)
8709e39c5baSBill Taylor  *
8719e39c5baSBill Taylor  *  Overview
8729e39c5baSBill Taylor  *      This is a wrapper function of i_hca_pci_config_teardown() for Hermon
8739e39c5baSBill Taylor  *      FM so that it calls i_hca_pci_config_teardown() inside after it checks
8749e39c5baSBill Taylor  *      the "fm-disable" configuration property. If the "fm_disable" is
8759e39c5baSBill Taylor  *      described in /kernel/drv/hermon.conf, the function calls
8769e39c5baSBill Taylor  *      pci_config_teardown() directly instead.
8779e39c5baSBill Taylor  *      See i_hca_pci_config_teardown() in detail.
8789e39c5baSBill Taylor  *
8799e39c5baSBill Taylor  *  Argument
8809e39c5baSBill Taylor  *      state: pointer to Hermon state structure
8819e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
8829e39c5baSBill Taylor  *
8839e39c5baSBill Taylor  *  Return value
8849e39c5baSBill Taylor  *      Nothing
8859e39c5baSBill Taylor  *
8869e39c5baSBill Taylor  *  Caller's context
8879e39c5baSBill Taylor  *      hermon_pci_config_teardown() can be called in user or kernel context
8889e39c5baSBill Taylor  *      only.
8899e39c5baSBill Taylor  */
8909e39c5baSBill Taylor void
hermon_pci_config_teardown(hermon_state_t * state,ddi_acc_handle_t * handle)8919e39c5baSBill Taylor hermon_pci_config_teardown(hermon_state_t *state, ddi_acc_handle_t *handle)
8929e39c5baSBill Taylor {
8939e39c5baSBill Taylor 	if (state->hs_fm_disable) {
8949e39c5baSBill Taylor 		pci_config_teardown(handle);
8959e39c5baSBill Taylor 		*handle = NULL;
8969e39c5baSBill Taylor 	} else {
8979e39c5baSBill Taylor 		i_hca_pci_config_teardown(state->hs_fm_hca_fm, handle);
8989e39c5baSBill Taylor 	}
8999e39c5baSBill Taylor }
9009e39c5baSBill Taylor 
9019e39c5baSBill Taylor 
9029e39c5baSBill Taylor /*
9039e39c5baSBill Taylor  *  boolean_t
9049e39c5baSBill Taylor  *  hermon_init_failure(hermon_state_t *state)
9059e39c5baSBill Taylor  *
9069e39c5baSBill Taylor  *  Overview
9079e39c5baSBill Taylor  *      hermon_init_failure() tells if HW errors are detected in
9089e39c5baSBill Taylor  *      the Hermon driver attach.
9099e39c5baSBill Taylor  *
9109e39c5baSBill Taylor  *  Argument
9119e39c5baSBill Taylor  *      state: pointer to Hermon state structure
9129e39c5baSBill Taylor  *
9139e39c5baSBill Taylor  *  Return value
9149e39c5baSBill Taylor  *  	B_TRUE		HW errors detected during attach
9159e39c5baSBill Taylor  *  	B_FALSE		No HW errors during attach
9169e39c5baSBill Taylor  *
9179e39c5baSBill Taylor  *  Caller's context
9189e39c5baSBill Taylor  *      hermon_init_failure() can be called in user, kernel, interrupt
9199e39c5baSBill Taylor  *      context or high interrupt context.
9209e39c5baSBill Taylor  */
9219e39c5baSBill Taylor boolean_t
hermon_init_failure(hermon_state_t * state)9229e39c5baSBill Taylor hermon_init_failure(hermon_state_t *state)
9239e39c5baSBill Taylor {
9249e39c5baSBill Taylor 	ddi_acc_handle_t hdl;
9259e39c5baSBill Taylor 	ddi_fm_error_t derr;
9269e39c5baSBill Taylor 
9279e39c5baSBill Taylor 	if (!(hermon_get_state(state) & HCA_PIO_FM))
9289e39c5baSBill Taylor 		return (B_FALSE);
9299e39c5baSBill Taylor 
930332f545bSEiji Ota 	/* check if fatal errors occur during attach */
931332f545bSEiji Ota 	if (state->hs_fm_async_fatal)
932332f545bSEiji Ota 		return (B_TRUE);
933332f545bSEiji Ota 
9349e39c5baSBill Taylor 	hdl = hermon_get_uarhdl(state);
9359e39c5baSBill Taylor 	/* Get the PIO error against UAR I/O space */
9369e39c5baSBill Taylor 	ddi_fm_acc_err_get(hdl, &derr, DDI_FME_VERSION);
9379e39c5baSBill Taylor 	if (derr.fme_status != DDI_FM_OK) {
9389e39c5baSBill Taylor 		return (B_TRUE);
9399e39c5baSBill Taylor 	}
9409e39c5baSBill Taylor 
9419e39c5baSBill Taylor 	hdl = hermon_get_cmdhdl(state);
9429e39c5baSBill Taylor 	/* Get the PIO error againsts CMD I/O space */
9439e39c5baSBill Taylor 	ddi_fm_acc_err_get(hdl, &derr, DDI_FME_VERSION);
9449e39c5baSBill Taylor 	if (derr.fme_status != DDI_FM_OK) {
9459e39c5baSBill Taylor 		return (B_TRUE);
9469e39c5baSBill Taylor 	}
9479e39c5baSBill Taylor 
9489e39c5baSBill Taylor 	return (B_FALSE);
9499e39c5baSBill Taylor }
9509e39c5baSBill Taylor 
9519e39c5baSBill Taylor 
9529e39c5baSBill Taylor /*
9539e39c5baSBill Taylor  *  void
9549e39c5baSBill Taylor  *  hermon_fm_ereport(hermon_state_t *state, int type, int detail)
9559e39c5baSBill Taylor  *
9569e39c5baSBill Taylor  *  Overview
9579e39c5baSBill Taylor  *      hermon_fm_ereport() is a Hermon FM ereport function used
9589e39c5baSBill Taylor  *      to issue a Solaris FMA ereport. See Hermon FM comments at the
9599e39c5baSBill Taylor  *      beginning of this file in detail.
9609e39c5baSBill Taylor  *
9619e39c5baSBill Taylor  *  Argument
9629e39c5baSBill Taylor  *      state: pointer to Hermon state structure
9639e39c5baSBill Taylor  *      type: error type
9649e39c5baSBill Taylor  *		HCA_SYS_ERR	FMA reporting HW error
9659e39c5baSBill Taylor  *		HCA_IBA_ERR	HCA specific HW error
9669e39c5baSBill Taylor  *      detail: HW error hint implying which ereport is issued
9679e39c5baSBill Taylor  * 		HCA_ERR_TRANSIENT	HW transienet error
9689e39c5baSBill Taylor  * 		HCA_ERR_NON_FATAL	HW persistent error
9699e39c5baSBill Taylor  * 		HCA_ERR_FATAL		HW fatal error
9709e39c5baSBill Taylor  * 		HCA_ERR_SRV_LOST	IB service lost due to HW error
9719e39c5baSBill Taylor  * 		HCA_ERR_DEGRADED	Hermon driver and/or uDAPL degraded
9729e39c5baSBill Taylor  * 					due to HW error
9739e39c5baSBill Taylor  * 		HCA_ERR_IOCTL		HW error detected in user conetxt
9749e39c5baSBill Taylor  * 					(especially in ioctl())
9759e39c5baSBill Taylor  *
9769e39c5baSBill Taylor  *  Return value
9779e39c5baSBill Taylor  *      Nothing
9789e39c5baSBill Taylor  *
9799e39c5baSBill Taylor  *  Caller's context
9809e39c5baSBill Taylor  *      hermon_fm_ereport() can be called in user, kernel, interrupt context
9819e39c5baSBill Taylor  *      or high interrupt context.
9829e39c5baSBill Taylor  */
9839e39c5baSBill Taylor void
hermon_fm_ereport(hermon_state_t * state,int type,int detail)9849e39c5baSBill Taylor hermon_fm_ereport(hermon_state_t *state, int type, int detail)
9859e39c5baSBill Taylor {
9869e39c5baSBill Taylor 	/*
9879e39c5baSBill Taylor 	 * If hermon_fm_diable is set or there is no FM ereport service
9889e39c5baSBill Taylor 	 * provided, then skip the rest.
9899e39c5baSBill Taylor 	 */
9909e39c5baSBill Taylor 	if (state->hs_fm_disable ||
9919e39c5baSBill Taylor 	    !(hermon_get_state(state) & HCA_EREPORT_FM)) {
9929e39c5baSBill Taylor 		return;
9939e39c5baSBill Taylor 	}
9949e39c5baSBill Taylor 
9959e39c5baSBill Taylor 	switch (type) {
9969e39c5baSBill Taylor 
9979e39c5baSBill Taylor 	case HCA_SYS_ERR:
9989e39c5baSBill Taylor 		switch (detail) {
9999e39c5baSBill Taylor 		case HCA_ERR_TRANSIENT:
10009e39c5baSBill Taylor 		case HCA_ERR_IOCTL:
10019e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10029e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
10039e39c5baSBill Taylor 			break;
10049e39c5baSBill Taylor 		case HCA_ERR_NON_FATAL:
10059e39c5baSBill Taylor 			/* Nothing */
10069e39c5baSBill Taylor 			break;
10079e39c5baSBill Taylor 		case HCA_ERR_SRV_LOST:
10089e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10099e39c5baSBill Taylor 			    DDI_SERVICE_LOST);
10109e39c5baSBill Taylor 			break;
10119e39c5baSBill Taylor 		case HCA_ERR_DEGRADED:
101232c5adfdSEiji Ota 			switch (state->hs_fm_degraded_reason) {
101332c5adfdSEiji Ota 			case HCA_FW_CORRUPT:
101432c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
101532c5adfdSEiji Ota 				    DDI_FM_DEVICE_FW_CORRUPT);
101632c5adfdSEiji Ota 				break;
101732c5adfdSEiji Ota 			case HCA_FW_MISMATCH:
101832c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
101932c5adfdSEiji Ota 				    DDI_FM_DEVICE_FW_MISMATCH);
102032c5adfdSEiji Ota 				break;
102132c5adfdSEiji Ota 			case HCA_FW_MISC:
102232c5adfdSEiji Ota 			default:
102332c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
102432c5adfdSEiji Ota 				    DDI_FM_DEVICE_INTERN_UNCORR);
102532c5adfdSEiji Ota 				break;
102632c5adfdSEiji Ota 			}
10279e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10289e39c5baSBill Taylor 			    DDI_SERVICE_DEGRADED);
10299e39c5baSBill Taylor 			break;
10309e39c5baSBill Taylor 		case HCA_ERR_FATAL:
10319e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10329e39c5baSBill Taylor 			    DDI_SERVICE_LOST);
10339e39c5baSBill Taylor 			state->hs_fm_async_fatal = B_TRUE;
10349e39c5baSBill Taylor 			break;
10359e39c5baSBill Taylor 		default:
10369e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: Unknown error. "
10379e39c5baSBill Taylor 			    "type = %d, detail = %d\n.", type, detail);
10389e39c5baSBill Taylor 		}
10399e39c5baSBill Taylor 		break;
10409e39c5baSBill Taylor 
10419e39c5baSBill Taylor 	case HCA_IBA_ERR:
10429e39c5baSBill Taylor 		switch (detail) {
10439e39c5baSBill Taylor 		case HCA_ERR_TRANSIENT:
10449e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, type,
10459e39c5baSBill Taylor 			    DDI_FM_DEVICE_INTERN_UNCORR);
10469e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10479e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
10489e39c5baSBill Taylor 			break;
10499e39c5baSBill Taylor 		case HCA_ERR_SRV_LOST:
10509e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: not supported "
10519e39c5baSBill Taylor 			    "error. type = %d, detail = %d\n.", type, detail);
10529e39c5baSBill Taylor 			break;
10539e39c5baSBill Taylor 		case HCA_ERR_DEGRADED:
105432c5adfdSEiji Ota 			switch (state->hs_fm_degraded_reason) {
105532c5adfdSEiji Ota 			case HCA_FW_CORRUPT:
105632c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
105732c5adfdSEiji Ota 				    DDI_FM_DEVICE_FW_CORRUPT);
105832c5adfdSEiji Ota 				break;
105932c5adfdSEiji Ota 			case HCA_FW_MISMATCH:
106032c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
106132c5adfdSEiji Ota 				    DDI_FM_DEVICE_FW_MISMATCH);
106232c5adfdSEiji Ota 				break;
106332c5adfdSEiji Ota 			case HCA_FW_MISC:
106432c5adfdSEiji Ota 			default:
106532c5adfdSEiji Ota 				i_hca_fm_ereport(state->hs_dip, type,
106632c5adfdSEiji Ota 				    DDI_FM_DEVICE_INTERN_UNCORR);
106732c5adfdSEiji Ota 				break;
106832c5adfdSEiji Ota 			}
10699e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10709e39c5baSBill Taylor 			    DDI_SERVICE_DEGRADED);
10719e39c5baSBill Taylor 			break;
10729e39c5baSBill Taylor 		case HCA_ERR_IOCTL:
10739e39c5baSBill Taylor 		case HCA_ERR_NON_FATAL:
10749e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, type,
10759e39c5baSBill Taylor 			    DDI_FM_DEVICE_INTERN_UNCORR);
10769e39c5baSBill Taylor 			ddi_fm_service_impact(state->hs_dip,
10779e39c5baSBill Taylor 			    DDI_SERVICE_UNAFFECTED);
10789e39c5baSBill Taylor 			break;
10799e39c5baSBill Taylor 		case HCA_ERR_FATAL:
10809e39c5baSBill Taylor 			if (hermon_get_state(state) & HCA_PIO_FM) {
10819e39c5baSBill Taylor 				if (servicing_interrupt()) {
10829e39c5baSBill Taylor 					atomic_inc_32(&state->
10839e39c5baSBill Taylor 					    hs_fm_async_errcnt);
10849e39c5baSBill Taylor 				} else {
10859e39c5baSBill Taylor 					i_hca_fm_ereport(state->hs_dip, type,
10869e39c5baSBill Taylor 					    DDI_FM_DEVICE_INTERN_UNCORR);
10879e39c5baSBill Taylor 					ddi_fm_service_impact(state->hs_dip,
10889e39c5baSBill Taylor 					    DDI_SERVICE_LOST);
10899e39c5baSBill Taylor 				}
10909e39c5baSBill Taylor 				state->hs_fm_async_fatal = B_TRUE;
10919e39c5baSBill Taylor 			} else {
10929e39c5baSBill Taylor 				i_hca_fm_ereport(state->hs_dip, type,
10939e39c5baSBill Taylor 				    DDI_FM_DEVICE_INTERN_UNCORR);
10949e39c5baSBill Taylor 				ddi_fm_service_impact(state->hs_dip,
10959e39c5baSBill Taylor 				    DDI_SERVICE_LOST);
10969e39c5baSBill Taylor 				cmn_err(CE_PANIC,
10979e39c5baSBill Taylor 				    "Hermon Fatal Internal Error. "
10989e39c5baSBill Taylor 				    "Hermon state=0x%p", (void *)state);
10999e39c5baSBill Taylor 			}
11009e39c5baSBill Taylor 			break;
11019e39c5baSBill Taylor 		default:
11029e39c5baSBill Taylor 			cmn_err(CE_WARN, "hermon_fm_ereport: Unknown error. "
11039e39c5baSBill Taylor 			    "type = %d, detail = %d\n.", type, detail);
11049e39c5baSBill Taylor 		}
11059e39c5baSBill Taylor 		break;
11069e39c5baSBill Taylor 
11079e39c5baSBill Taylor 	default:
11089e39c5baSBill Taylor 		cmn_err(CE_WARN, "hermon_fm_ereport: Unknown type "
11099e39c5baSBill Taylor 		    "type = %d, detail = %d\n.", type, detail);
11109e39c5baSBill Taylor 		break;
11119e39c5baSBill Taylor 	}
11129e39c5baSBill Taylor }
11139e39c5baSBill Taylor 
11149e39c5baSBill Taylor 
11159e39c5baSBill Taylor /*
11169e39c5baSBill Taylor  *  uchar_t
11179e39c5baSBill Taylor  *  hermon_devacc_attr_version(hermon_state_t *)
11189e39c5baSBill Taylor  *
11199e39c5baSBill Taylor  *  Overview
11209e39c5baSBill Taylor  *      hermon_devacc_attr_version() returns the ddi device attribute
11219e39c5baSBill Taylor  *      version.
11229e39c5baSBill Taylor  *
11239e39c5baSBill Taylor  *  Argument
11249e39c5baSBill Taylor  *      state: pointer to Hermon state structure
11259e39c5baSBill Taylor  *
11269e39c5baSBill Taylor  *  Return value
11279e39c5baSBill Taylor  *      dev_acc_attr_version value
11289e39c5baSBill Taylor  *      	DDI_DEVICE_ATTR_V0	Hermon FM disabled
11299e39c5baSBill Taylor  *      	DDI_DEVICE_ATTR_V1	Hermon FM enabled
11309e39c5baSBill Taylor  *
11319e39c5baSBill Taylor  *  Caller's context
11329e39c5baSBill Taylor  *      hermon_devacc_attr_version() can be called in user, kernel, interrupt
11339e39c5baSBill Taylor  *      context or high interrupt context.
11349e39c5baSBill Taylor  */
11359e39c5baSBill Taylor ushort_t
hermon_devacc_attr_version(hermon_state_t * state)11369e39c5baSBill Taylor hermon_devacc_attr_version(hermon_state_t *state)
11379e39c5baSBill Taylor {
11389e39c5baSBill Taylor 	if (state->hs_fm_disable) {
11399e39c5baSBill Taylor 		return (DDI_DEVICE_ATTR_V0);
11409e39c5baSBill Taylor 	} else {
11419e39c5baSBill Taylor 		return (DDI_DEVICE_ATTR_V1);
11429e39c5baSBill Taylor 	}
11439e39c5baSBill Taylor }
11449e39c5baSBill Taylor 
11459e39c5baSBill Taylor 
11469e39c5baSBill Taylor /*
11479e39c5baSBill Taylor  *  uchar_t
11489e39c5baSBill Taylor  *  hermon_devacc_attr_access(hermon_state_t *)
11499e39c5baSBill Taylor  *
11509e39c5baSBill Taylor  *  Overview
11519e39c5baSBill Taylor  *      hermon_devacc_attr_access() returns devacc_attr_access error
11529e39c5baSBill Taylor  *      protection types.
11539e39c5baSBill Taylor  *
11549e39c5baSBill Taylor  *  Argument
11559e39c5baSBill Taylor  *      state: pointer to Hermon state structure
11569e39c5baSBill Taylor  *
11579e39c5baSBill Taylor  *  Return value
11589e39c5baSBill Taylor  *      dev_acc_attr_access error protection type
11599e39c5baSBill Taylor  *      	DDI_DEFAULT_ACC		Hermon FM disabled for PIO
11609e39c5baSBill Taylor  *      	DDI_FLAGERR_ACC		Hermon FM enabled for PIO
11619e39c5baSBill Taylor  *
11629e39c5baSBill Taylor  *  Caller's context
11639e39c5baSBill Taylor  *      hermon_devacc_attr_access() can be called in user, kernel, interrupt
11649e39c5baSBill Taylor  *      context or high interrupt context.
11659e39c5baSBill Taylor  */
11669e39c5baSBill Taylor uchar_t
hermon_devacc_attr_access(hermon_state_t * state)11679e39c5baSBill Taylor hermon_devacc_attr_access(hermon_state_t *state)
11689e39c5baSBill Taylor {
11699e39c5baSBill Taylor 	if (state->hs_fm_disable) {
11709e39c5baSBill Taylor 		return (DDI_DEFAULT_ACC);
11719e39c5baSBill Taylor 	} else {
11729e39c5baSBill Taylor 		return (DDI_FLAGERR_ACC);
11739e39c5baSBill Taylor 	}
11749e39c5baSBill Taylor }
11759e39c5baSBill Taylor 
11769e39c5baSBill Taylor 
11779e39c5baSBill Taylor /*
11789e39c5baSBill Taylor  *  int
11799e39c5baSBill Taylor  *  hermon_PIO_start(hermon_state_t *state, ddi_acc_handle_t handle,
11809e39c5baSBill Taylor  *      hermon_test_t *tst)
11819e39c5baSBill Taylor  *
11829e39c5baSBill Taylor  *  Overview
11839e39c5baSBill Taylor  *      hermon_PIO_start() should be called before Hermon driver issues PIOs
11849e39c5baSBill Taylor  *      against I/O space. If Hermon FM is disabled, this function returns
11859e39c5baSBill Taylor  *      HCA_PIO_OK always. See i_hca_pio_start() in detail.
11869e39c5baSBill Taylor  *
11879e39c5baSBill Taylor  *  Argument
11889e39c5baSBill Taylor  *      state: pointer to Hermon state structure
11899e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
11909e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
11919e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
11929e39c5baSBill Taylor  *
11939e39c5baSBill Taylor  *  Return value
11949e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
11959e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
11969e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
11979e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
11989e39c5baSBill Taylor  *
11999e39c5baSBill Taylor  *  Caller's context
12009e39c5baSBill Taylor  *      hermon_PIO_start() can be called in user, kernel or interrupt context.
12019e39c5baSBill Taylor  */
12029e39c5baSBill Taylor int
hermon_PIO_start(hermon_state_t * state,ddi_acc_handle_t handle,hermon_test_t * tst)12039e39c5baSBill Taylor hermon_PIO_start(hermon_state_t *state, ddi_acc_handle_t handle,
12049e39c5baSBill Taylor     hermon_test_t *tst)
12059e39c5baSBill Taylor {
12069e39c5baSBill Taylor 	if (state->hs_fm_disable) {
12079e39c5baSBill Taylor 		return (HCA_PIO_OK);
12089e39c5baSBill Taylor 	} else {
12099e39c5baSBill Taylor 		struct i_hca_acc_handle *handlep =
12109e39c5baSBill Taylor 		    i_hca_get_acc_handle(state->hs_fm_hca_fm, handle);
12119e39c5baSBill Taylor 		ASSERT(handlep != NULL);
12129e39c5baSBill Taylor 		return (i_hca_pio_start(state->hs_dip, handlep, tst));
12139e39c5baSBill Taylor 	}
12149e39c5baSBill Taylor }
12159e39c5baSBill Taylor 
12169e39c5baSBill Taylor 
12179e39c5baSBill Taylor /*
12189e39c5baSBill Taylor  *  int
12199e39c5baSBill Taylor  *  hermon_PIO_end(hermon_state_t *state, ddi_acc_handle_t handle, int *cnt,
12209e39c5baSBill Taylor  *      hermon_test_t *tst)
12219e39c5baSBill Taylor  *
12229e39c5baSBill Taylor  *  Overview
12239e39c5baSBill Taylor  *      hermon_PIO_end() should be called after Hermon driver issues PIOs
12249e39c5baSBill Taylor  *      against I/O space. If Hermon FM is disabled, this function returns
12259e39c5baSBill Taylor  *      HCA_PIO_OK always. See i_hca_pio_end() in detail.
12269e39c5baSBill Taylor  *
12279e39c5baSBill Taylor  *  Argument
12289e39c5baSBill Taylor  *      state: pointer to Hermon state structure
12299e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
12309e39c5baSBill Taylor  *	cnt: pointer to the counter variable which holds the nubmer of retry
12319e39c5baSBill Taylor  *	     (HCA_PIO_RETRY_CNT) when a HW error is detected.
12329e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
12339e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
12349e39c5baSBill Taylor  *
12359e39c5baSBill Taylor  *  Return value
12369e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
12379e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
12389e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
12399e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
12409e39c5baSBill Taylor  *
12419e39c5baSBill Taylor  *  Caller's context
12429e39c5baSBill Taylor  *      hermon_PIO_end() can be called in user, kernel or interrupt context.
12439e39c5baSBill Taylor  */
12449e39c5baSBill Taylor int
hermon_PIO_end(hermon_state_t * state,ddi_acc_handle_t handle,int * cnt,hermon_test_t * tst)12459e39c5baSBill Taylor hermon_PIO_end(hermon_state_t *state, ddi_acc_handle_t handle, int *cnt,
12469e39c5baSBill Taylor     hermon_test_t *tst)
12479e39c5baSBill Taylor {
12489e39c5baSBill Taylor 	if (state->hs_fm_disable) {
12499e39c5baSBill Taylor 		return (HCA_PIO_OK);
12509e39c5baSBill Taylor 	} else {
12519e39c5baSBill Taylor 		struct i_hca_acc_handle *handlep =
12529e39c5baSBill Taylor 		    i_hca_get_acc_handle(state->hs_fm_hca_fm, handle);
12539e39c5baSBill Taylor 		ASSERT(handlep != NULL);
12549e39c5baSBill Taylor 		return (i_hca_pio_end(state->hs_dip, handlep, cnt, tst));
12559e39c5baSBill Taylor 	}
12569e39c5baSBill Taylor }
12579e39c5baSBill Taylor 
12589e39c5baSBill Taylor 
12599e39c5baSBill Taylor /*
12609e39c5baSBill Taylor  *  ddi_acc_handle_t
12619e39c5baSBill Taylor  *  hermon_get_cmdhdl(hermon_state_t *state)
12629e39c5baSBill Taylor  *
12639e39c5baSBill Taylor  *  Overview
12649e39c5baSBill Taylor  *      hermon_get_cmdhdl() returns either the fma-protected access handle or
12659e39c5baSBill Taylor  *      the regular ddi-access handle depending on the Hermon FM state for
12669e39c5baSBill Taylor  *      Hermon command I/O space.
12679e39c5baSBill Taylor  *
12689e39c5baSBill Taylor  *  Argument
12699e39c5baSBill Taylor  *      state: pointer to Hermon state structure
12709e39c5baSBill Taylor  *
12719e39c5baSBill Taylor  *  Return value
12729e39c5baSBill Taylor  *  	the access handle for pio requests
12739e39c5baSBill Taylor  *
12749e39c5baSBill Taylor  *  Caller's context
12759e39c5baSBill Taylor  *      hermon_get_cmdhdl() can be called in user, kernel, interrupt context
12769e39c5baSBill Taylor  *      or high interrupt context.
12779e39c5baSBill Taylor  */
12789e39c5baSBill Taylor ddi_acc_handle_t
hermon_get_cmdhdl(hermon_state_t * state)12799e39c5baSBill Taylor hermon_get_cmdhdl(hermon_state_t *state)
12809e39c5baSBill Taylor {
12819e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_PIO_FM ?
12829e39c5baSBill Taylor 	    state->hs_fm_cmdhdl : state->hs_reg_cmdhdl);
12839e39c5baSBill Taylor }
12849e39c5baSBill Taylor 
12859e39c5baSBill Taylor 
12869e39c5baSBill Taylor /*
12879e39c5baSBill Taylor  *  ddi_acc_handle_t
12889e39c5baSBill Taylor  *  hermon_get_uarhdl(hermon_state_t *state)
12899e39c5baSBill Taylor  *
12909e39c5baSBill Taylor  *  Overview
12919e39c5baSBill Taylor  *      hermon_get_uarhdl() returns either the fma-protected access handle or
12929e39c5baSBill Taylor  *      the regular ddi-access handle depending on the Hermon FM state for
12939e39c5baSBill Taylor  *      Hermon UAR I/O space.
12949e39c5baSBill Taylor  *
12959e39c5baSBill Taylor  *  Argument
12969e39c5baSBill Taylor  *      state: pointer to Hermon state structure
12979e39c5baSBill Taylor  *
12989e39c5baSBill Taylor  *  Return value
12999e39c5baSBill Taylor  *  	the access handle for pio requests
13009e39c5baSBill Taylor  *
13019e39c5baSBill Taylor  *  Caller's context
13029e39c5baSBill Taylor  *      hermon_get_uarhdl() can be called in user, kernel, interrupt context
13039e39c5baSBill Taylor  *      or high interrupt context.
13049e39c5baSBill Taylor  */
13059e39c5baSBill Taylor ddi_acc_handle_t
hermon_get_uarhdl(hermon_state_t * state)13069e39c5baSBill Taylor hermon_get_uarhdl(hermon_state_t *state)
13079e39c5baSBill Taylor {
13089e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_PIO_FM ?
13099e39c5baSBill Taylor 	    state->hs_fm_uarhdl : state->hs_reg_uarhdl);
13109e39c5baSBill Taylor }
13119e39c5baSBill Taylor 
13129e39c5baSBill Taylor 
13139e39c5baSBill Taylor /*
13149e39c5baSBill Taylor  *  ddi_acc_handle_t
13159e39c5baSBill Taylor  *  hermon_rsrc_alloc_uarhdl(hermon_state_t *state)
13169e39c5baSBill Taylor  *
13179e39c5baSBill Taylor  *  Overview
13189e39c5baSBill Taylor  *      hermon_rsrc_alloc_uarhdl() returns either the fma-protected access
13199e39c5baSBill Taylor  *      handle or the regular ddi-access handle depending on the Hermon FM
13209e39c5baSBill Taylor  *      state for Hermon UAR I/O space as well as hermon_get_uarhdl(), but
13219e39c5baSBill Taylor  *      this function is dedicated to the UAR resource allocator.
13229e39c5baSBill Taylor  *
13239e39c5baSBill Taylor  *  Argument
13249e39c5baSBill Taylor  *      state: pointer to Hermon state structure
13259e39c5baSBill Taylor  *
13269e39c5baSBill Taylor  *  Return value
13279e39c5baSBill Taylor  *  	the access handle for pio requests
13289e39c5baSBill Taylor  *
13299e39c5baSBill Taylor  *  Caller's context
13309e39c5baSBill Taylor  *      hermon_rsrc_alloc_uarhdl() can be called in user, kernel, interrupt
13319e39c5baSBill Taylor  *      or high interrupt context.
13329e39c5baSBill Taylor  */
13339e39c5baSBill Taylor ddi_acc_handle_t
hermon_rsrc_alloc_uarhdl(hermon_state_t * state)13349e39c5baSBill Taylor hermon_rsrc_alloc_uarhdl(hermon_state_t *state)
13359e39c5baSBill Taylor {
13369e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
13379e39c5baSBill Taylor 	    state->hs_fm_uarhdl : state->hs_reg_uarhdl);
13389e39c5baSBill Taylor }
13399e39c5baSBill Taylor 
13409e39c5baSBill Taylor /*
13419e39c5baSBill Taylor  *  ddi_acc_handle_t
13429e39c5baSBill Taylor  *  hermon_get_pcihdl(hermon_state_t *state)
13439e39c5baSBill Taylor  *
13449e39c5baSBill Taylor  *  Overview
13459e39c5baSBill Taylor  *      hermon_get_pcihdl() returns either the fma-protected access
13469e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the PCI config
13479e39c5baSBill Taylor  *      space. Whether or not which handle is returned at the moment depends
13489e39c5baSBill Taylor  *      on the Hermon FM state.
13499e39c5baSBill Taylor  *
13509e39c5baSBill Taylor  *  Argument
13519e39c5baSBill Taylor  *      state: pointer to Hermon state structure
13529e39c5baSBill Taylor  *
13539e39c5baSBill Taylor  *  Return value
13549e39c5baSBill Taylor  *  	the access handle to PCI config space
13559e39c5baSBill Taylor  *
13569e39c5baSBill Taylor  *  Caller's context
13579e39c5baSBill Taylor  *      hermon_get_pcihdl() can be called in user, kernel, interrupt
13589e39c5baSBill Taylor  *      or high interrupt context.
13599e39c5baSBill Taylor  */
13609e39c5baSBill Taylor ddi_acc_handle_t
hermon_get_pcihdl(hermon_state_t * state)13619e39c5baSBill Taylor hermon_get_pcihdl(hermon_state_t *state)
13629e39c5baSBill Taylor {
13639e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
13649e39c5baSBill Taylor 	    state->hs_fm_pcihdl : state->hs_reg_pcihdl);
13659e39c5baSBill Taylor }
13669e39c5baSBill Taylor 
13679e39c5baSBill Taylor 
13689e39c5baSBill Taylor /*
13699e39c5baSBill Taylor  *  ddi_acc_handle_t
13709e39c5baSBill Taylor  *  hermon_get_msix_tblhdl(hermon_state_t *state)
13719e39c5baSBill Taylor  *
13729e39c5baSBill Taylor  *  Overview
13739e39c5baSBill Taylor  *      hermon_get_msix_tblhdl() returns either the fma-protected access
13749e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the MSI-X tables.
13759e39c5baSBill Taylor  *      Whether or not which handle is returned at the moment depends on
13769e39c5baSBill Taylor  *      the Hermon FM state.
13779e39c5baSBill Taylor  *
13789e39c5baSBill Taylor  *  Argument
13799e39c5baSBill Taylor  *      state: pointer to Hermon state structure
13809e39c5baSBill Taylor  *
13819e39c5baSBill Taylor  *  Return value
13829e39c5baSBill Taylor  *  	the access handle to MSI-X tables
13839e39c5baSBill Taylor  *
13849e39c5baSBill Taylor  *  Caller's context
13859e39c5baSBill Taylor  *      hermon_get_msix_tblhdl() can be called in user, kernel, interrupt
13869e39c5baSBill Taylor  *      context or high interrupt context.
13879e39c5baSBill Taylor  */
13889e39c5baSBill Taylor ddi_acc_handle_t
hermon_get_msix_tblhdl(hermon_state_t * state)13899e39c5baSBill Taylor hermon_get_msix_tblhdl(hermon_state_t *state)
13909e39c5baSBill Taylor {
13919e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
13929e39c5baSBill Taylor 	    state->hs_fm_msix_tblhdl : state->hs_reg_msix_tblhdl);
13939e39c5baSBill Taylor }
13949e39c5baSBill Taylor 
13959e39c5baSBill Taylor 
13969e39c5baSBill Taylor /*
13979e39c5baSBill Taylor  *  ddi_acc_handle_t
13989e39c5baSBill Taylor  *  hermon_get_msix_pbahdl(hermon_state_t *state)
13999e39c5baSBill Taylor  *
14009e39c5baSBill Taylor  *  Overview
14019e39c5baSBill Taylor  *      hermon_get_msix_pbahdl() returns either the fma-protected access
14029e39c5baSBill Taylor  *      handle or the regular ddi-access handle to access the MSI-X PBA.
14039e39c5baSBill Taylor  *      Whether or not which handle is returned at the moment depends on
14049e39c5baSBill Taylor  *      the Hermon FM state.
14059e39c5baSBill Taylor  *
14069e39c5baSBill Taylor  *  Argument
14079e39c5baSBill Taylor  *      state: pointer to Hermon state structure
14089e39c5baSBill Taylor  *
14099e39c5baSBill Taylor  *  Return value
14109e39c5baSBill Taylor  *  	the access handle to MSI-X PBA
14119e39c5baSBill Taylor  *
14129e39c5baSBill Taylor  *  Caller's context
14139e39c5baSBill Taylor  *      hermon_get_msix_pbahdl() can be called in user, kernel, interrupt
14149e39c5baSBill Taylor  *      context or high interrupt context.
14159e39c5baSBill Taylor  */
14169e39c5baSBill Taylor ddi_acc_handle_t
hermon_get_msix_pbahdl(hermon_state_t * state)14179e39c5baSBill Taylor hermon_get_msix_pbahdl(hermon_state_t *state)
14189e39c5baSBill Taylor {
14199e39c5baSBill Taylor 	return (state->hs_fm_disable || hermon_get_state(state) & HCA_ATTCH_FM ?
14209e39c5baSBill Taylor 	    state->hs_fm_msix_pbahdl : state->hs_reg_msix_pbahdl);
14219e39c5baSBill Taylor }
14229e39c5baSBill Taylor 
14239e39c5baSBill Taylor 
14249e39c5baSBill Taylor /*
14259e39c5baSBill Taylor  *  void
14269e39c5baSBill Taylor  *  hermon_inter_err_chk(void *arg)
14279e39c5baSBill Taylor  *
14289e39c5baSBill Taylor  *  Overview
14299e39c5baSBill Taylor  *      hermon_inter_err_chk() periodically checks the internal error buffer
14309e39c5baSBill Taylor  *      to pick up a Hermon asynchronous internal error.
14319e39c5baSBill Taylor  *
14329e39c5baSBill Taylor  *      Note that this internal error can be notified if the interrupt is
14339e39c5baSBill Taylor  *      registered, but even so there are some cases that an interrupt against
14349e39c5baSBill Taylor  *      it cannot be raised so that Hermon RPM recommeds to poll this internal
14359e39c5baSBill Taylor  *      error buffer periodically instead. This function is invoked at
14369e39c5baSBill Taylor  *      10ms interval in kernel context though the function itself can be
14379e39c5baSBill Taylor  *      called in interrupt context.
14389e39c5baSBill Taylor  *
14399e39c5baSBill Taylor  *  Argument
14409e39c5baSBill Taylor  *      arg: pointer to Hermon state structure
14419e39c5baSBill Taylor  *
14429e39c5baSBill Taylor  *  Return value
14439e39c5baSBill Taylor  *  	Nothing
14449e39c5baSBill Taylor  *
14459e39c5baSBill Taylor  *  Caller's context
14469e39c5baSBill Taylor  *      hermon_inter_err_chk() can be called in user, kernel, interrupt
14479e39c5baSBill Taylor  *      context or high interrupt context.
14489e39c5baSBill Taylor  *
14499e39c5baSBill Taylor  */
14509e39c5baSBill Taylor void
hermon_inter_err_chk(void * arg)14519e39c5baSBill Taylor hermon_inter_err_chk(void *arg)
14529e39c5baSBill Taylor {
14539e39c5baSBill Taylor 	uint32_t	word;
14549e39c5baSBill Taylor 	ddi_acc_handle_t cmdhdl;
14559e39c5baSBill Taylor 	hermon_state_t *state = (hermon_state_t *)arg;
14569e39c5baSBill Taylor 
14579e39c5baSBill Taylor 	/* initialize the FMA retry loop */
14589e39c5baSBill Taylor 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test);
14599e39c5baSBill Taylor 
14609e39c5baSBill Taylor #ifdef FMA_TEST
14619e39c5baSBill Taylor 	if (hermon_test_num != 0) {
14629e39c5baSBill Taylor 		return;
14639e39c5baSBill Taylor 	}
14649e39c5baSBill Taylor #endif
14659e39c5baSBill Taylor 	if (state->hs_fm_poll_suspend) {
14669e39c5baSBill Taylor 		return;
14679e39c5baSBill Taylor 	}
14689e39c5baSBill Taylor 
14699e39c5baSBill Taylor 	/* Get the access handle for Hermon CMD I/O space */
14709e39c5baSBill Taylor 	cmdhdl = hermon_get_cmdhdl(state);
14719e39c5baSBill Taylor 
14729e39c5baSBill Taylor 	/* the FMA retry loop starts. */
14739e39c5baSBill Taylor 	hermon_pio_start(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
14749e39c5baSBill Taylor 	    fm_test);
14759e39c5baSBill Taylor 
14769e39c5baSBill Taylor 	word = ddi_get32(cmdhdl, state->hs_cmd_regs.fw_err_buf);
14779e39c5baSBill Taylor 
14789e39c5baSBill Taylor 	/* the FMA retry loop ends. */
14799e39c5baSBill Taylor 	hermon_pio_end(state, cmdhdl, pio_error, fm_loop_cnt, fm_status,
14809e39c5baSBill Taylor 	    fm_test);
14819e39c5baSBill Taylor 
14829e39c5baSBill Taylor 	if (word != 0) {
14839e39c5baSBill Taylor 		HERMON_FMANOTE(state, HERMON_FMA_INTERNAL);
1484332f545bSEiji Ota 		/* if fm_disable is on, Hermon FM functions don't work */
1485332f545bSEiji Ota 		if (state->hs_fm_disable) {
1486332f545bSEiji Ota 			cmn_err(CE_PANIC,
1487332f545bSEiji Ota 			    "Hermon Fatal Internal Error. "
1488332f545bSEiji Ota 			    "Hermon state=0x%p", (void *)state);
1489332f545bSEiji Ota 		} else {
1490332f545bSEiji Ota 			hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
1491332f545bSEiji Ota 		}
14929e39c5baSBill Taylor 	}
14939e39c5baSBill Taylor 
14949e39c5baSBill Taylor 	/* issue the ereport pended in the interrupt context */
14959e39c5baSBill Taylor 	if (state->hs_fm_async_errcnt > 0) {
14969e39c5baSBill Taylor 		hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
14979e39c5baSBill Taylor 		atomic_dec_32(&state->hs_fm_async_errcnt);
14989e39c5baSBill Taylor 	}
14999e39c5baSBill Taylor 
15009e39c5baSBill Taylor 	return;
15019e39c5baSBill Taylor 
15029e39c5baSBill Taylor pio_error:
15039e39c5baSBill Taylor 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_FATAL);
15049e39c5baSBill Taylor }
15059e39c5baSBill Taylor 
15069e39c5baSBill Taylor 
15079e39c5baSBill Taylor /*
15089e39c5baSBill Taylor  *  boolean_t
15099e39c5baSBill Taylor  *  hermon_cmd_retry_ok(hermon_cmd_post_t *cmd, int status)
15109e39c5baSBill Taylor  *
15119e39c5baSBill Taylor  *  Overview
15129e39c5baSBill Taylor  *  	In the case that a HW error is detected, if it can be isolated
15139e39c5baSBill Taylor  *  	enough, Hermon FM retries the operation which caused the error.
15149e39c5baSBill Taylor  *  	However, this retry can induce another error; since the retry is
15159e39c5baSBill Taylor  *  	achieved as a block basis, not a statement basis, once the state
15169e39c5baSBill Taylor  *  	was set inside the Hermon HW already in the previous operation, the
15179e39c5baSBill Taylor  *  	retry can cause for example, a CMD_BAD_SYS_STATE error, as a result.
15189e39c5baSBill Taylor  *  	In this case, CMD_BAD_SYS_STATE should be taken as a side effect
15199e39c5baSBill Taylor  *  	but a harmless result. hermon_cmd_retry_ok() checks this kind of
15209e39c5baSBill Taylor  *  	situation then returns if the state Hermon CMD returns is OK or not.
15219e39c5baSBill Taylor  *
15229e39c5baSBill Taylor  *  Argument
15239e39c5baSBill Taylor  *      cmd: pointer to hermon_cmd_post_t structure
15249e39c5baSBill Taylor  *      status: Hermon CMD status
15259e39c5baSBill Taylor  *
15269e39c5baSBill Taylor  *  Return value
15279e39c5baSBill Taylor  *  	B_TRUE		this state is no problem
15289e39c5baSBill Taylor  *  	B_FALSE		this state should be taken as an error
15299e39c5baSBill Taylor  *
15309e39c5baSBill Taylor  *  Caller's context
15319e39c5baSBill Taylor  *      hermon_cmd_retry_ok() can be called in user, kernel, interrupt
15329e39c5baSBill Taylor  *      context or high interrupt context.
15339e39c5baSBill Taylor  *
15349e39c5baSBill Taylor  *  Note that status except for HERMON_CMD_SUCCESS shouldn't be accepted
15359e39c5baSBill Taylor  *  in the debug module to catch a hidden software bug, so that ASSERT()
15369e39c5baSBill Taylor  *  is enabled in the case.
15379e39c5baSBill Taylor  */
15389e39c5baSBill Taylor boolean_t
hermon_cmd_retry_ok(hermon_cmd_post_t * cmd,int status)15399e39c5baSBill Taylor hermon_cmd_retry_ok(hermon_cmd_post_t *cmd, int status)
15409e39c5baSBill Taylor {
15419e39c5baSBill Taylor 	if (status == HERMON_CMD_SUCCESS)
15429e39c5baSBill Taylor 		return (B_TRUE);
15439e39c5baSBill Taylor 
15449e39c5baSBill Taylor 	/*
15459e39c5baSBill Taylor 	 * The wrong status such as HERMON_CMD_BAD_SYS_STATE or
15469e39c5baSBill Taylor 	 * HERMON_CMD_BAD_RES_STATE can return as a side effect
15479e39c5baSBill Taylor 	 * because of the Hermon FM operation retry when a PIO
15489e39c5baSBill Taylor 	 * error is detected during the I/O transaction. In the
15499e39c5baSBill Taylor 	 * case, the driver may set the same value in Hermon
15509e39c5baSBill Taylor 	 * though it was set already, then Hermon returns HERMON_
15519e39c5baSBill Taylor 	 * CMD_BAD_{RES,SYS}_STATE as a result, which should be
15529e39c5baSBill Taylor 	 * taken as OK.
15539e39c5baSBill Taylor 	 */
15549e39c5baSBill Taylor 	switch (cmd->cp_opcode) {
15559e39c5baSBill Taylor 	case INIT_HCA:
15569e39c5baSBill Taylor 		/*
15579e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of
15589e39c5baSBill Taylor 		 * ICM not mapped or HCA already initialized.
15599e39c5baSBill Taylor 		 */
15609e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
15619e39c5baSBill Taylor 			return (B_TRUE);
15629e39c5baSBill Taylor 		return (B_FALSE);
15639e39c5baSBill Taylor 
15649e39c5baSBill Taylor 	case CLOSE_HCA:
15659e39c5baSBill Taylor 		/*
15669e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of Firmware
15679e39c5baSBill Taylor 		 * area is not mapped or HCA already closed.
15689e39c5baSBill Taylor 		 */
15699e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
15709e39c5baSBill Taylor 			return (B_TRUE);
15719e39c5baSBill Taylor 		return (B_FALSE);
15729e39c5baSBill Taylor 
15739e39c5baSBill Taylor 	case CLOSE_PORT:
15749e39c5baSBill Taylor 		/*
15759e39c5baSBill Taylor 		 * HERMON_CMD_BAD_SYS_STATE can be gotten in case of HCA not
15769e39c5baSBill Taylor 		 * initialized or in case that IB ports are already down.
15779e39c5baSBill Taylor 		 */
15789e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_SYS_STATE)
15799e39c5baSBill Taylor 			return (B_TRUE);
15809e39c5baSBill Taylor 		return (B_FALSE);
15819e39c5baSBill Taylor 
15829e39c5baSBill Taylor 	case SW2HW_MPT:
15839e39c5baSBill Taylor 		/*
15849e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
15859e39c5baSBill Taylor 		 * entry already in hardware ownership.
15869e39c5baSBill Taylor 		 */
15879e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
15889e39c5baSBill Taylor 			return (B_TRUE);
15899e39c5baSBill Taylor 		return (B_FALSE);
15909e39c5baSBill Taylor 
15919e39c5baSBill Taylor 	case HW2SW_MPT:
15929e39c5baSBill Taylor 		/*
15939e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of MPT
15949e39c5baSBill Taylor 		 * entry already in software ownership.
15959e39c5baSBill Taylor 		 */
15969e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
15979e39c5baSBill Taylor 			return (B_TRUE);
15989e39c5baSBill Taylor 		return (B_FALSE);
15999e39c5baSBill Taylor 
16009e39c5baSBill Taylor 	case SW2HW_EQ:
16019e39c5baSBill Taylor 		/*
16029e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
16039e39c5baSBill Taylor 		 * entry already in hardware ownership.
16049e39c5baSBill Taylor 		 */
16059e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16069e39c5baSBill Taylor 			return (B_TRUE);
16079e39c5baSBill Taylor 		return (B_FALSE);
16089e39c5baSBill Taylor 
16099e39c5baSBill Taylor 	case HW2SW_EQ:
16109e39c5baSBill Taylor 		/*
16119e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of EQ
16129e39c5baSBill Taylor 		 * entry already in software ownership.
16139e39c5baSBill Taylor 		 */
16149e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16159e39c5baSBill Taylor 			return (B_TRUE);
16169e39c5baSBill Taylor 		return (B_FALSE);
16179e39c5baSBill Taylor 
16189e39c5baSBill Taylor 	case SW2HW_CQ:
16199e39c5baSBill Taylor 		/*
16209e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
16219e39c5baSBill Taylor 		 * entry already in hardware ownership.
16229e39c5baSBill Taylor 		 */
16239e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16249e39c5baSBill Taylor 			return (B_TRUE);
16259e39c5baSBill Taylor 		return (B_FALSE);
16269e39c5baSBill Taylor 
16279e39c5baSBill Taylor 	case HW2SW_CQ:
16289e39c5baSBill Taylor 		/*
16299e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of CQ
16309e39c5baSBill Taylor 		 * entry already in software ownership.
16319e39c5baSBill Taylor 		 */
16329e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16339e39c5baSBill Taylor 			return (B_TRUE);
16349e39c5baSBill Taylor 		return (B_FALSE);
16359e39c5baSBill Taylor 
16369e39c5baSBill Taylor 	case SW2HW_SRQ:
16379e39c5baSBill Taylor 		/*
16389e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
16399e39c5baSBill Taylor 		 * entry already in hardware ownership.
16409e39c5baSBill Taylor 		 */
16419e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16429e39c5baSBill Taylor 			return (B_TRUE);
16439e39c5baSBill Taylor 		return (B_FALSE);
16449e39c5baSBill Taylor 
16459e39c5baSBill Taylor 	case HW2SW_SRQ:
16469e39c5baSBill Taylor 		/*
16479e39c5baSBill Taylor 		 * HERMON_CMD_BAD_RES_STATE can be gotten in case of SRQ
16489e39c5baSBill Taylor 		 * entry already in software ownership.
16499e39c5baSBill Taylor 		 */
16509e39c5baSBill Taylor 		if (status == HERMON_CMD_BAD_RES_STATE)
16519e39c5baSBill Taylor 			return (B_TRUE);
16529e39c5baSBill Taylor 		return (B_FALSE);
16539e39c5baSBill Taylor 	default:
16549e39c5baSBill Taylor 		break;
16559e39c5baSBill Taylor 	}
16569e39c5baSBill Taylor 
16579e39c5baSBill Taylor 	/* other cases */
16589e39c5baSBill Taylor 	return (B_FALSE);
16599e39c5baSBill Taylor }
16609e39c5baSBill Taylor 
16619e39c5baSBill Taylor 
16629e39c5baSBill Taylor #ifdef FMA_TEST
16639e39c5baSBill Taylor 
16649e39c5baSBill Taylor /*
16659e39c5baSBill Taylor  * Hermon FMA test variables
16669e39c5baSBill Taylor  */
16679e39c5baSBill Taylor #define	FMA_TEST_HASHSZ	64
16689e39c5baSBill Taylor int hermon_test_num;			/* predefined testset */
16699e39c5baSBill Taylor 
16709e39c5baSBill Taylor static struct i_hca_fm_test *i_hca_test_register(char *, int, int,
16719e39c5baSBill Taylor     void (*)(struct i_hca_fm_test *, ddi_fm_error_t *),
16729e39c5baSBill Taylor     void *, mod_hash_t *, mod_hash_t *, int);
16739e39c5baSBill Taylor static void i_hca_test_free_item(mod_hash_val_t);
16749e39c5baSBill Taylor static void i_hca_test_set_item(int, struct i_hca_fm_test *);
16759e39c5baSBill Taylor static void hermon_trigger_pio_error(hermon_test_t *, ddi_fm_error_t *);
16769e39c5baSBill Taylor 
16779e39c5baSBill Taylor /*
16789e39c5baSBill Taylor  * Hermon FMA Function Test Interface
16799e39c5baSBill Taylor  */
16809e39c5baSBill Taylor 
16819e39c5baSBill Taylor /* Attach Errors */
16829e39c5baSBill Taylor 
16839e39c5baSBill Taylor #define	ATTACH_TS	(HCA_TEST_TRANSIENT | HCA_TEST_ATTACH | HCA_TEST_START)
16849e39c5baSBill Taylor #define	ATTACH_TE	(HCA_TEST_TRANSIENT | HCA_TEST_ATTACH | HCA_TEST_END)
16859e39c5baSBill Taylor 
16869e39c5baSBill Taylor #define	ATTACH_PS	(HCA_TEST_PERSISTENT | HCA_TEST_ATTACH | HCA_TEST_START)
16879e39c5baSBill Taylor #define	ATTACH_PE	(HCA_TEST_PERSISTENT | HCA_TEST_ATTACH | HCA_TEST_END)
16889e39c5baSBill Taylor 
16899e39c5baSBill Taylor static hermon_test_t testset[] = {
16909e39c5baSBill Taylor /* Initial Value */
16919e39c5baSBill Taylor {0, 0, 0, NULL, 0, 0, NULL, NULL, NULL},	/* 0 */
16929e39c5baSBill Taylor 
16939e39c5baSBill Taylor /* PIO Transient Errors */
16949e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_TS, NULL, /* attach/transient/start/propagate */
16959e39c5baSBill Taylor     HCA_PIO_RETRY_CNT, 0, NULL, NULL, NULL},	/* 1 */
16969e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_TE, NULL, /* attach/transient/end/propagate */
16979e39c5baSBill Taylor     HCA_PIO_RETRY_CNT, 0, NULL, NULL, NULL},	/* 2 */
16989e39c5baSBill Taylor 
16999e39c5baSBill Taylor /* PIO Persistent Errors */
17009e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_PS, NULL, /* attach/persistent/start/propagate */
17019e39c5baSBill Taylor     0, 0, NULL, NULL, NULL},			/* 3 */
17029e39c5baSBill Taylor {0, HCA_TEST_PIO, ATTACH_PE, NULL, /* attach/persistent/end/propagate */
17039e39c5baSBill Taylor     0, 0, NULL, NULL, NULL},			/* 4 */
17049e39c5baSBill Taylor 
17059e39c5baSBill Taylor };
17069e39c5baSBill Taylor 
17079e39c5baSBill Taylor 
17089e39c5baSBill Taylor /*
17099e39c5baSBill Taylor  *  void
17109e39c5baSBill Taylor  *  hermon_trigger_pio_error(hermon_test_t *tst, ddi_fm_error_t *derr)
17119e39c5baSBill Taylor  *
17129e39c5baSBill Taylor  *  Overview
17139e39c5baSBill Taylor  *      hermon_trigger_pio_error() is a PIO error injection function
17149e39c5baSBill Taylor  *      to cause a pseduo PIO error.
17159e39c5baSBill Taylor  *
17169e39c5baSBill Taylor  *  Argument
17179e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
17189e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
17199e39c5baSBill Taylor  *      derr: pointer to ddi_fm_error_t structure
17209e39c5baSBill Taylor  *
17219e39c5baSBill Taylor  *  Return value
17229e39c5baSBill Taylor  *      Nothing
17239e39c5baSBill Taylor  *
17249e39c5baSBill Taylor  *  Caller's context
17259e39c5baSBill Taylor  *      hermon_trigger_pio_error() can be called in user, kernel, interrupt
17269e39c5baSBill Taylor  *      context or high interrupt context.
17279e39c5baSBill Taylor  */
17289e39c5baSBill Taylor static void
hermon_trigger_pio_error(hermon_test_t * tst,ddi_fm_error_t * derr)17299e39c5baSBill Taylor hermon_trigger_pio_error(hermon_test_t *tst, ddi_fm_error_t *derr)
17309e39c5baSBill Taylor {
17319e39c5baSBill Taylor 	hermon_state_t *state = (hermon_state_t *)tst->private;
17329e39c5baSBill Taylor 	derr->fme_status = DDI_FM_OK;
17339e39c5baSBill Taylor 
17349e39c5baSBill Taylor 	if (tst->type != HCA_TEST_PIO) {
17359e39c5baSBill Taylor 		return;
17369e39c5baSBill Taylor 	}
17379e39c5baSBill Taylor 
17389e39c5baSBill Taylor 	if ((tst->trigger & HCA_TEST_ATTACH &&
17399e39c5baSBill Taylor 	    i_ddi_node_state(state->hs_dip) < DS_ATTACHED &&
17409e39c5baSBill Taylor 	    hermon_get_state(state) & HCA_PIO_FM)) {
17419e39c5baSBill Taylor 		if (tst->trigger & HCA_TEST_PERSISTENT) {
17429e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, HCA_IBA_ERR,
17439e39c5baSBill Taylor 			    DDI_FM_DEVICE_INVAL_STATE);
17449e39c5baSBill Taylor 			derr->fme_status = DDI_FM_NONFATAL;
17459e39c5baSBill Taylor 			return;
17469e39c5baSBill Taylor 		} else if (tst->trigger & HCA_TEST_TRANSIENT &&
17479e39c5baSBill Taylor 		    tst->errcnt) {
17489e39c5baSBill Taylor 			i_hca_fm_ereport(state->hs_dip, HCA_IBA_ERR,
17499e39c5baSBill Taylor 			    DDI_FM_DEVICE_INVAL_STATE);
17509e39c5baSBill Taylor 			derr->fme_status = DDI_FM_NONFATAL;
17519e39c5baSBill Taylor 			tst->errcnt--;
17529e39c5baSBill Taylor 			return;
17539e39c5baSBill Taylor 		}
17549e39c5baSBill Taylor 	}
17559e39c5baSBill Taylor }
17569e39c5baSBill Taylor 
17579e39c5baSBill Taylor 
17589e39c5baSBill Taylor /*
17599e39c5baSBill Taylor  *  struct hermon_fm_test *
17609e39c5baSBill Taylor  *  hermon_test_register(hermon_state_t *state, char *filename, int linenum,
17619e39c5baSBill Taylor  *      int type)
17629e39c5baSBill Taylor  *
17639e39c5baSBill Taylor  *  Overview
17649e39c5baSBill Taylor  *      hermon_test_register() registers a Hermon FM test item for the
17659e39c5baSBill Taylor  *      function test.
17669e39c5baSBill Taylor  *
17679e39c5baSBill Taylor  *  Argument
17689e39c5baSBill Taylor  *      state: pointer to Hermon state structure
17699e39c5baSBill Taylor  *  	filename: source file name where the function call is implemented
17709e39c5baSBill Taylor  *		  This value is usually a __FILE__  pre-defined macro.
17719e39c5baSBill Taylor  *  	linenum: line number where the function call is described in the
17729e39c5baSBill Taylor  *		 file specified above.
17739e39c5baSBill Taylor  *		 This value is usually a __LINE__ pre-defined macro.
17749e39c5baSBill Taylor  *	type: HW error type
17759e39c5baSBill Taylor  *			HCA_TEST_PIO	pio error
17769e39c5baSBill Taylor  *			HCA_TEST_IBA	ib specific error
17779e39c5baSBill Taylor  *
17789e39c5baSBill Taylor  *  Return value
17799e39c5baSBill Taylor  *      pointer to Hermon FM function test structure registered.
17809e39c5baSBill Taylor  *
17819e39c5baSBill Taylor  *  Caller's context
17829e39c5baSBill Taylor  *      hermon_test_register() can be called in user, kernel or interrupt
17839e39c5baSBill Taylor  *      context.
17849e39c5baSBill Taylor  *
17859e39c5baSBill Taylor  *  Note that no test item is registered if Hermon FM is disabled.
17869e39c5baSBill Taylor  */
17879e39c5baSBill Taylor hermon_test_t *
hermon_test_register(hermon_state_t * state,char * filename,int linenum,int type)17889e39c5baSBill Taylor hermon_test_register(hermon_state_t *state, char *filename, int linenum,
17899e39c5baSBill Taylor     int type)
17909e39c5baSBill Taylor {
17919e39c5baSBill Taylor 	void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *) =
17929e39c5baSBill Taylor 	    (void (*)(struct i_hca_fm_test *, ddi_fm_error_t *))
17939e39c5baSBill Taylor 	    hermon_trigger_pio_error;
17949e39c5baSBill Taylor 
17959e39c5baSBill Taylor 	if (state->hs_fm_disable)
17969e39c5baSBill Taylor 		return (NULL);
17979e39c5baSBill Taylor 
17989e39c5baSBill Taylor 	return ((hermon_test_t *)i_hca_test_register(filename, linenum, type,
17999e39c5baSBill Taylor 	    pio_injection, (void *)state, state->hs_fm_test_hash,
18009e39c5baSBill Taylor 	    state->hs_fm_id_hash, hermon_test_num));
18019e39c5baSBill Taylor }
18029e39c5baSBill Taylor #endif /* FMA_TEST */
18039e39c5baSBill Taylor 
18049e39c5baSBill Taylor 
18059e39c5baSBill Taylor /*
18069e39c5baSBill Taylor  * HCA FM Common Interface
18079e39c5baSBill Taylor  *
18089e39c5baSBill Taylor  * These functions should be used for any HCA drivers, but probably
18099e39c5baSBill Taylor  * customized for their own HW design and/or FM implementation.
18109e39c5baSBill Taylor  * Customized functins should have the driver name prefix such as
18119e39c5baSBill Taylor  * hermon_xxxx() and be defined separately but whose functions should
18129e39c5baSBill Taylor  * call the common interface inside.
18139e39c5baSBill Taylor  */
18149e39c5baSBill Taylor 
18159e39c5baSBill Taylor /*
18169e39c5baSBill Taylor  *  void
18179e39c5baSBill Taylor  *  i_hca_fm_init(struct i_hca_fm *hca_fm)
18189e39c5baSBill Taylor  *
18199e39c5baSBill Taylor  *  Overview
18209e39c5baSBill Taylor  *      i_hca_fm_init() is an initialization function which sets up the acc
18219e39c5baSBill Taylor  *      handle kmem_cache if this function is called the first time.
18229e39c5baSBill Taylor  *
18239e39c5baSBill Taylor  *  Argument
18249e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
18259e39c5baSBill Taylor  *
18269e39c5baSBill Taylor  *  Return value
18279e39c5baSBill Taylor  *      Nothing
18289e39c5baSBill Taylor  *
18299e39c5baSBill Taylor  *  Caller's context
18309e39c5baSBill Taylor  *      i_hca_fm_init() can be called in user or kernel context, but cannot
18319e39c5baSBill Taylor  *      be called in interrupt context.
18329e39c5baSBill Taylor  */
18339e39c5baSBill Taylor static void
i_hca_fm_init(struct i_hca_fm * hca_fm)18349e39c5baSBill Taylor i_hca_fm_init(struct i_hca_fm *hca_fm)
18359e39c5baSBill Taylor {
18369e39c5baSBill Taylor 
18379e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
18389e39c5baSBill Taylor 
18399e39c5baSBill Taylor 	++hca_fm->ref_cnt;
18409e39c5baSBill Taylor 	if (hca_fm->fm_acc_cache == NULL) {
18419e39c5baSBill Taylor 		hca_fm->fm_acc_cache = kmem_cache_create("hca_fm_acc_handle",
18429e39c5baSBill Taylor 		    sizeof (struct i_hca_acc_handle), 0, NULL,
18439e39c5baSBill Taylor 		    NULL, NULL, NULL, NULL, 0);
18449e39c5baSBill Taylor 	}
18459e39c5baSBill Taylor 
18469e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
18479e39c5baSBill Taylor }
18489e39c5baSBill Taylor 
18499e39c5baSBill Taylor 
18509e39c5baSBill Taylor /*
18519e39c5baSBill Taylor  *  void
18529e39c5baSBill Taylor  *  i_hca_fm_fini(struct i_hca_fm *hca_fm)
18539e39c5baSBill Taylor  *
18549e39c5baSBill Taylor  *  Overview
18559e39c5baSBill Taylor  *      i_hca_fm_fini() is a finalization function which frees up the acc
18569e39c5baSBill Taylor  *      handle kmem_cache if this function is called the last time.
18579e39c5baSBill Taylor  *
18589e39c5baSBill Taylor  *  Argument
18599e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
18609e39c5baSBill Taylor  *
18619e39c5baSBill Taylor  *  Return value
18629e39c5baSBill Taylor  *      Nothing
18639e39c5baSBill Taylor  *
18649e39c5baSBill Taylor  *  Caller's context
18659e39c5baSBill Taylor  *      i_hca_fm_fini() can be called in user or kernel context, but cannot
18669e39c5baSBill Taylor  *      be called in interrupt context.
18679e39c5baSBill Taylor  */
18689e39c5baSBill Taylor static void
i_hca_fm_fini(struct i_hca_fm * hca_fm)18699e39c5baSBill Taylor i_hca_fm_fini(struct i_hca_fm *hca_fm)
18709e39c5baSBill Taylor {
18719e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
18729e39c5baSBill Taylor 
18739e39c5baSBill Taylor 	if (--hca_fm->ref_cnt == 0) {
18749e39c5baSBill Taylor 
18759e39c5baSBill Taylor 		if (hca_fm->fm_acc_cache) {
18769e39c5baSBill Taylor 			kmem_cache_destroy(hca_fm->fm_acc_cache);
18779e39c5baSBill Taylor 			hca_fm->fm_acc_cache = NULL;
18789e39c5baSBill Taylor 		}
18799e39c5baSBill Taylor 	}
18809e39c5baSBill Taylor 
18819e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
18829e39c5baSBill Taylor }
18839e39c5baSBill Taylor 
18849e39c5baSBill Taylor 
18859e39c5baSBill Taylor /*
18869e39c5baSBill Taylor  *  void
18879e39c5baSBill Taylor  *  i_hca_fm_ereport(dev_info_t *dip, int type, char *detail)
18889e39c5baSBill Taylor  *
18899e39c5baSBill Taylor  *  Overview
18909e39c5baSBill Taylor  *      i_hca_fm_ereport() is a wrapper function of ddi_fm_ereport_post() but
18919e39c5baSBill Taylor  *      generates an ena before it calls ddi_fm_ereport_post() for HCA
18929e39c5baSBill Taylor  *      specific HW errors.
18939e39c5baSBill Taylor  *
18949e39c5baSBill Taylor  *  Argument
18959e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
18969e39c5baSBill Taylor  *      type: error type
18979e39c5baSBill Taylor  *		HCA_SYS_ERR	FMA reporting HW error
18989e39c5baSBill Taylor  *		HCA_IBA_ERR	HCA specific HW error
18999e39c5baSBill Taylor  *      detail: definition of leaf driver detected ereports which is one of:
19009e39c5baSBill Taylor  *      	DDI_FM_DEVICE_INVAL_STATE
19019e39c5baSBill Taylor  *		DDI_FM_DEVICE_NO_RESPONSE
19029e39c5baSBill Taylor  *		DDI_FM_DEVICE_STALL
19039e39c5baSBill Taylor  *		DDI_FM_DEVICE_BADINT_LIMIT
19049e39c5baSBill Taylor  *		DDI_FM_DEVICE_INTERN_CORR
19059e39c5baSBill Taylor  *		DDI_FM_DEVICE_INTERN_UNCORR
19069e39c5baSBill Taylor  *
19079e39c5baSBill Taylor  *  Return value
19089e39c5baSBill Taylor  *      Nothing
19099e39c5baSBill Taylor  *
19109e39c5baSBill Taylor  *  Caller's context
19119e39c5baSBill Taylor  *      i_hca_fm_ereport() can be called in user, kernel or interrupt context.
19129e39c5baSBill Taylor  */
19139e39c5baSBill Taylor static void
i_hca_fm_ereport(dev_info_t * dip,int type,char * detail)19149e39c5baSBill Taylor i_hca_fm_ereport(dev_info_t *dip, int type, char *detail)
19159e39c5baSBill Taylor {
19169e39c5baSBill Taylor 	uint64_t ena;
19179e39c5baSBill Taylor 	char buf[FM_MAX_CLASS];
19189e39c5baSBill Taylor 
19199e39c5baSBill Taylor 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
19209e39c5baSBill Taylor 
19219e39c5baSBill Taylor 	ena = fm_ena_generate(0, FM_ENA_FMT1);
19229e39c5baSBill Taylor 	if (type == HCA_IBA_ERR) {
19239e39c5baSBill Taylor 		/* this is an error of its own */
19249e39c5baSBill Taylor 		ena = fm_ena_increment(ena);
19259e39c5baSBill Taylor 	}
19269e39c5baSBill Taylor 
19279e39c5baSBill Taylor 	ddi_fm_ereport_post(dip, buf, ena, DDI_NOSLEEP,
19289e39c5baSBill Taylor 	    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
19299e39c5baSBill Taylor }
19309e39c5baSBill Taylor 
19319e39c5baSBill Taylor 
19329e39c5baSBill Taylor /*
19339e39c5baSBill Taylor  * struct i_hca_acc_handle *
19349e39c5baSBill Taylor  * i_hca_get_acc_handle(struct i_hca_fm *hca_fm, ddi_acc_handle_t handle)
19359e39c5baSBill Taylor  *
19369e39c5baSBill Taylor  *  Overview
19379e39c5baSBill Taylor  *      i_hca_get_acc_handle() returns ddi_acc_handle_t used for HCA FM.
19389e39c5baSBill Taylor  *
19399e39c5baSBill Taylor  *  Argument
19409e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
19419e39c5baSBill Taylor  *      handle: ddi_acc_handle_t
19429e39c5baSBill Taylor  *
19439e39c5baSBill Taylor  *  Return value
19449e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
19459e39c5baSBill Taylor  *
19469e39c5baSBill Taylor  *  Caller's context
19479e39c5baSBill Taylor  *      i_hca_get_acc_handle() can be called in user, kernel or interrupt
19489e39c5baSBill Taylor  *      context.
19499e39c5baSBill Taylor  */
19509e39c5baSBill Taylor static struct i_hca_acc_handle *
i_hca_get_acc_handle(struct i_hca_fm * hca_fm,ddi_acc_handle_t handle)19519e39c5baSBill Taylor i_hca_get_acc_handle(struct i_hca_fm *hca_fm, ddi_acc_handle_t handle)
19529e39c5baSBill Taylor {
19539e39c5baSBill Taylor 	struct i_hca_acc_handle *hdlp;
19549e39c5baSBill Taylor 
19559e39c5baSBill Taylor 	/* Retrieve the HCA FM access handle */
19569e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
19579e39c5baSBill Taylor 
19589e39c5baSBill Taylor 	for (hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
19599e39c5baSBill Taylor 		if (hdlp->save_hdl == handle) {
19609e39c5baSBill Taylor 			mutex_exit(&hca_fm->lock);
19619e39c5baSBill Taylor 			return (hdlp);
19629e39c5baSBill Taylor 		}
19639e39c5baSBill Taylor 	}
19649e39c5baSBill Taylor 
19659e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
19669e39c5baSBill Taylor 	return (hdlp);
19679e39c5baSBill Taylor }
19689e39c5baSBill Taylor 
19699e39c5baSBill Taylor 
19709e39c5baSBill Taylor /*
19719e39c5baSBill Taylor  *  int
19729e39c5baSBill Taylor  *  i_hca_regs_map_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
19739e39c5baSBill Taylor  *      uint_t rnumber, caddr_t *addrp, offset_t offset, offset_t len,
19749e39c5baSBill Taylor  *      ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handle)
19759e39c5baSBill Taylor  *
19769e39c5baSBill Taylor  *  Overview
19779e39c5baSBill Taylor  *      i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_setup(),
19789e39c5baSBill Taylor  *      but allocates the HCA FM acc handle structure and initializes it.
19799e39c5baSBill Taylor  *
19809e39c5baSBill Taylor  *  Argument
19819e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
19829e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
19839e39c5baSBill Taylor  *      rnumber: index number to the register address space set
19849e39c5baSBill Taylor  *      addrp: platform-dependent value (same as ddi_regs_map_setup())
19859e39c5baSBill Taylor  *      offset: offset into the register address space
19869e39c5baSBill Taylor  *      len: address space length to be mapped
19879e39c5baSBill Taylor  *      accattrp: pointer to device access attribute structure
19889e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
19899e39c5baSBill Taylor  *
19909e39c5baSBill Taylor  *  Return value
19919e39c5baSBill Taylor  *      ddi function status value which are:
19929e39c5baSBill Taylor  *      	DDI_SUCCESS
19939e39c5baSBill Taylor  *      	DDI_FAILURE
19949e39c5baSBill Taylor  *      	DDI_ME_RNUMBER_RNGE
19959e39c5baSBill Taylor  *      	DDI_REGS_ACC_CONFLICT
19969e39c5baSBill Taylor  *
19979e39c5baSBill Taylor  *  Caller's context
19989e39c5baSBill Taylor  *      i_hca_regs_map_setup() can be called in user or kernel context only.
19999e39c5baSBill Taylor  */
20009e39c5baSBill Taylor static int
i_hca_regs_map_setup(struct i_hca_fm * hca_fm,dev_info_t * dip,uint_t rnumber,caddr_t * addrp,offset_t offset,offset_t len,ddi_device_acc_attr_t * accattrp,ddi_acc_handle_t * handle)20019e39c5baSBill Taylor i_hca_regs_map_setup(struct i_hca_fm *hca_fm, dev_info_t *dip, uint_t rnumber,
20029e39c5baSBill Taylor     caddr_t *addrp, offset_t offset, offset_t len,
20039e39c5baSBill Taylor     ddi_device_acc_attr_t *accattrp, ddi_acc_handle_t *handle)
20049e39c5baSBill Taylor {
20059e39c5baSBill Taylor 	int status;
20069e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *last;
20079e39c5baSBill Taylor 
20089e39c5baSBill Taylor 	/* Allocate an access handle */
20099e39c5baSBill Taylor 	if ((status = ddi_regs_map_setup(dip, rnumber, addrp, offset,
20109e39c5baSBill Taylor 	    len, accattrp, handle)) != DDI_SUCCESS) {
20119e39c5baSBill Taylor 		return (status);
20129e39c5baSBill Taylor 	}
20139e39c5baSBill Taylor 
20149e39c5baSBill Taylor 	/* Allocate HCA FM acc handle structure */
20159e39c5baSBill Taylor 	handlep = kmem_cache_alloc(hca_fm->fm_acc_cache, KM_SLEEP);
20169e39c5baSBill Taylor 
20179e39c5baSBill Taylor 	/* Initialize fields */
20189e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*handlep))
20199e39c5baSBill Taylor 	handlep->next = NULL;
20209e39c5baSBill Taylor 	handlep->save_hdl = (*handle);
20219e39c5baSBill Taylor 	handlep->thread_cnt = 0;
20229e39c5baSBill Taylor 	mutex_init(&handlep->lock, NULL, MUTEX_DRIVER, NULL);
20239e39c5baSBill Taylor 
20249e39c5baSBill Taylor 	/* Register this handle */
20259e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
20269e39c5baSBill Taylor 	for (last = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
20279e39c5baSBill Taylor 		last = hdlp;
20289e39c5baSBill Taylor 	}
20299e39c5baSBill Taylor 	if (last == NULL) {
20309e39c5baSBill Taylor 		hca_fm->hdl = handlep;
20319e39c5baSBill Taylor 	} else {
20329e39c5baSBill Taylor 		last->next = handlep;
20339e39c5baSBill Taylor 	}
20349e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
20359e39c5baSBill Taylor 
20369e39c5baSBill Taylor 	return (status);
20379e39c5baSBill Taylor }
20389e39c5baSBill Taylor 
20399e39c5baSBill Taylor 
20409e39c5baSBill Taylor /*
20419e39c5baSBill Taylor  *  void
20429e39c5baSBill Taylor  *  i_hca_regs_map_free(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handlep)
20439e39c5baSBill Taylor  *
20449e39c5baSBill Taylor  *  Overview
20459e39c5baSBill Taylor  *      i_hca_regs_map_setup() is a wrapper function of ddi_regs_map_free(),
20469e39c5baSBill Taylor  *      and frees the HCA FM acc handle structure allocated by
20479e39c5baSBill Taylor  *      i_hca_regs_map_setup().
20489e39c5baSBill Taylor  *
20499e39c5baSBill Taylor  *  Argument
20509e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
20519e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
20529e39c5baSBill Taylor  *
20539e39c5baSBill Taylor  *  Return value
20549e39c5baSBill Taylor  *      Nothing
20559e39c5baSBill Taylor  *
20569e39c5baSBill Taylor  *  Caller's context
20579e39c5baSBill Taylor  *      i_hca_regs_map_free() can be called in user or kernel context only.
20589e39c5baSBill Taylor  *
20599e39c5baSBill Taylor  *  Note that the handle passed to i_hca_regs_map_free() is NULL-cleared
20609e39c5baSBill Taylor  *  after this function is called.
20619e39c5baSBill Taylor  */
20629e39c5baSBill Taylor static void
i_hca_regs_map_free(struct i_hca_fm * hca_fm,ddi_acc_handle_t * handle)20639e39c5baSBill Taylor i_hca_regs_map_free(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handle)
20649e39c5baSBill Taylor {
20659e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *prev;
20669e39c5baSBill Taylor 
20679e39c5baSBill Taylor 	/* De-register this handle */
20689e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
20699e39c5baSBill Taylor 	for (prev = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
20709e39c5baSBill Taylor 		if (hdlp->save_hdl == *handle)
20719e39c5baSBill Taylor 			break;
20729e39c5baSBill Taylor 		prev = hdlp;
20739e39c5baSBill Taylor 	}
20749e39c5baSBill Taylor 	ASSERT(prev != NULL && hdlp != NULL);
20759e39c5baSBill Taylor 	if (hdlp != prev) {
20769e39c5baSBill Taylor 		prev->next = hdlp->next;
20779e39c5baSBill Taylor 	} else {
20789e39c5baSBill Taylor 		hca_fm->hdl = hdlp->next;
20799e39c5baSBill Taylor 	}
20809e39c5baSBill Taylor 	handlep = hdlp;
20819e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
20829e39c5baSBill Taylor 
20839e39c5baSBill Taylor 	mutex_destroy(&handlep->lock);
20849e39c5baSBill Taylor 	handlep->save_hdl = NULL;
20859e39c5baSBill Taylor 	kmem_cache_free(hca_fm->fm_acc_cache, handlep);
20869e39c5baSBill Taylor 
20879e39c5baSBill Taylor 	/* Release this handle */
20889e39c5baSBill Taylor 	ddi_regs_map_free(handle);
20899e39c5baSBill Taylor 	*handle = NULL;
20909e39c5baSBill Taylor }
20919e39c5baSBill Taylor 
20929e39c5baSBill Taylor 
20939e39c5baSBill Taylor /*
20949e39c5baSBill Taylor  *  int
20959e39c5baSBill Taylor  *  i_hca_pci_config_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
20969e39c5baSBill Taylor  *      ddi_acc_handle_t *handle, boolean_t fm_protect)
20979e39c5baSBill Taylor  *
20989e39c5baSBill Taylor  *  Overview
20999e39c5baSBill Taylor  *      i_hca_pci_config_setup() is a wrapper function of pci_config_setup(),
21009e39c5baSBill Taylor  *      but allocates the HCA FM acc handle structure and initializes it.
21019e39c5baSBill Taylor  *
21029e39c5baSBill Taylor  *  Argument
21039e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
21049e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
21059e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA PCI config space
21069e39c5baSBill Taylor  *		with FMA
21079e39c5baSBill Taylor  *	fm_protect: flag to tell if an fma-protected access handle should
21089e39c5baSBill Taylor  *		be used
21099e39c5baSBill Taylor  *
21109e39c5baSBill Taylor  *  Return value
21119e39c5baSBill Taylor  *      ddi function status value which are:
21129e39c5baSBill Taylor  *      	DDI_SUCCESS
21139e39c5baSBill Taylor  *      	DDI_FAILURE
21149e39c5baSBill Taylor  *
21159e39c5baSBill Taylor  *  Caller's context
21169e39c5baSBill Taylor  *      i_hca_pci_config_setup() can be called in user or kernel context only.
21179e39c5baSBill Taylor  */
21189e39c5baSBill Taylor static int
i_hca_pci_config_setup(struct i_hca_fm * hca_fm,dev_info_t * dip,ddi_acc_handle_t * handle)21199e39c5baSBill Taylor i_hca_pci_config_setup(struct i_hca_fm *hca_fm, dev_info_t *dip,
21209e39c5baSBill Taylor     ddi_acc_handle_t *handle)
21219e39c5baSBill Taylor {
21229e39c5baSBill Taylor 	int status;
21239e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *last;
21249e39c5baSBill Taylor 
21259e39c5baSBill Taylor 	/* Allocate an access handle */
21269e39c5baSBill Taylor 	if ((status = pci_config_setup(dip, handle)) != DDI_SUCCESS) {
21279e39c5baSBill Taylor 		return (status);
21289e39c5baSBill Taylor 	}
21299e39c5baSBill Taylor 
21309e39c5baSBill Taylor 	/* Allocate HCA FM acc handle structure */
21319e39c5baSBill Taylor 	handlep = kmem_cache_alloc(hca_fm->fm_acc_cache, KM_SLEEP);
21329e39c5baSBill Taylor 
21339e39c5baSBill Taylor 	/* Initialize fields */
21349e39c5baSBill Taylor 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*handlep))
21359e39c5baSBill Taylor 	handlep->next = NULL;
21369e39c5baSBill Taylor 	handlep->save_hdl = (*handle);
21379e39c5baSBill Taylor 	handlep->thread_cnt = 0;
21389e39c5baSBill Taylor 	mutex_init(&handlep->lock, NULL, MUTEX_DRIVER, NULL);
21399e39c5baSBill Taylor 
21409e39c5baSBill Taylor 	/* Register this handle */
21419e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
21429e39c5baSBill Taylor 	for (last = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
21439e39c5baSBill Taylor 		last = hdlp;
21449e39c5baSBill Taylor 	}
21459e39c5baSBill Taylor 	if (last == NULL) {
21469e39c5baSBill Taylor 		hca_fm->hdl = handlep;
21479e39c5baSBill Taylor 	} else {
21489e39c5baSBill Taylor 		last->next = handlep;
21499e39c5baSBill Taylor 	}
21509e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
21519e39c5baSBill Taylor 
21529e39c5baSBill Taylor 	return (status);
21539e39c5baSBill Taylor }
21549e39c5baSBill Taylor 
21559e39c5baSBill Taylor 
21569e39c5baSBill Taylor /*
21579e39c5baSBill Taylor  *  void
21589e39c5baSBill Taylor  *  i_hca_pci_config_teardown(struct i_hca_fm *hca_fm,
21599e39c5baSBill Taylor  *      ddi_acc_handle_t *handlep)
21609e39c5baSBill Taylor  *
21619e39c5baSBill Taylor  *  Overview
21629e39c5baSBill Taylor  *      i_hca_pci_config_teardown() is a wrapper function of
21639e39c5baSBill Taylor  *      pci_config_teardown(), and frees the HCA FM acc handle structure
21649e39c5baSBill Taylor  *      allocated by i_hca_pci_config_setup().
21659e39c5baSBill Taylor  *
21669e39c5baSBill Taylor  *  Argument
21679e39c5baSBill Taylor  *      hca_fm: pointer to HCA FM structure
21689e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
21699e39c5baSBill Taylor  *
21709e39c5baSBill Taylor  *  Return value
21719e39c5baSBill Taylor  *      Nothing
21729e39c5baSBill Taylor  *
21739e39c5baSBill Taylor  *  Caller's context
21749e39c5baSBill Taylor  *      i_hca_pci_config_teardown() can be called in user or kernel context
21759e39c5baSBill Taylor  *      only.
21769e39c5baSBill Taylor  *
21779e39c5baSBill Taylor  *  Note that the handle passed to i_hca_pci_config_teardown() is NULL-cleared
21789e39c5baSBill Taylor  *  after this function is called.
21799e39c5baSBill Taylor  */
21809e39c5baSBill Taylor static void
i_hca_pci_config_teardown(struct i_hca_fm * hca_fm,ddi_acc_handle_t * handle)21819e39c5baSBill Taylor i_hca_pci_config_teardown(struct i_hca_fm *hca_fm, ddi_acc_handle_t *handle)
21829e39c5baSBill Taylor {
21839e39c5baSBill Taylor 	struct i_hca_acc_handle *handlep, *hdlp, *prev;
21849e39c5baSBill Taylor 
21859e39c5baSBill Taylor 	/* De-register this handle */
21869e39c5baSBill Taylor 	mutex_enter(&hca_fm->lock);
21879e39c5baSBill Taylor 	for (prev = hdlp = hca_fm->hdl; hdlp != NULL; hdlp = hdlp->next) {
21889e39c5baSBill Taylor 		if (hdlp->save_hdl == *handle)
21899e39c5baSBill Taylor 			break;
21909e39c5baSBill Taylor 		prev = hdlp;
21919e39c5baSBill Taylor 	}
21929e39c5baSBill Taylor 	ASSERT(prev != NULL && hdlp != NULL);
21939e39c5baSBill Taylor 	if (hdlp != prev) {
21949e39c5baSBill Taylor 		prev->next = hdlp->next;
21959e39c5baSBill Taylor 	} else {
21969e39c5baSBill Taylor 		hca_fm->hdl = hdlp->next;
21979e39c5baSBill Taylor 	}
21989e39c5baSBill Taylor 	handlep = hdlp;
21999e39c5baSBill Taylor 	mutex_exit(&hca_fm->lock);
22009e39c5baSBill Taylor 
22019e39c5baSBill Taylor 	mutex_destroy(&handlep->lock);
22029e39c5baSBill Taylor 	handlep->save_hdl = NULL;
22039e39c5baSBill Taylor 	kmem_cache_free(hca_fm->fm_acc_cache, handlep);
22049e39c5baSBill Taylor 
22059e39c5baSBill Taylor 	/* Release this handle */
22069e39c5baSBill Taylor 	pci_config_teardown(handle);
22079e39c5baSBill Taylor 	*handle = NULL;
22089e39c5baSBill Taylor }
22099e39c5baSBill Taylor 
22109e39c5baSBill Taylor 
22119e39c5baSBill Taylor /*
22129e39c5baSBill Taylor  *  int
22139e39c5baSBill Taylor  *  i_hca_pio_start(dev_info_t *dip, struct i_acc_handle *handle,
22149e39c5baSBill Taylor  *      struct i_hca_fm_test *tst)
22159e39c5baSBill Taylor  *
22169e39c5baSBill Taylor  *  Overview
22179e39c5baSBill Taylor  *      i_hca_pio_start() is one of a pair of HCA FM fuctions for PIO, which
22189e39c5baSBill Taylor  *      should be called before HCA drivers issue PIOs against I/O space.
22199e39c5baSBill Taylor  *      See HCA FM comments at the beginning of this file in detail.
22209e39c5baSBill Taylor  *
22219e39c5baSBill Taylor  *  Argument
22229e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
22239e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
22249e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
22259e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
22269e39c5baSBill Taylor  *
22279e39c5baSBill Taylor  *  Return value
22289e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
22299e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
22309e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
22319e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
22329e39c5baSBill Taylor  *
22339e39c5baSBill Taylor  *  Caller's context
22349e39c5baSBill Taylor  *      i_hca_pio_start() can be called in user, kernel or interrupt context.
22359e39c5baSBill Taylor  */
22369e39c5baSBill Taylor /* ARGSUSED */
22379e39c5baSBill Taylor static int
i_hca_pio_start(dev_info_t * dip,struct i_hca_acc_handle * hdlp,struct i_hca_fm_test * tst)22389e39c5baSBill Taylor i_hca_pio_start(dev_info_t *dip, struct i_hca_acc_handle *hdlp,
22399e39c5baSBill Taylor     struct i_hca_fm_test *tst)
22409e39c5baSBill Taylor {
22419e39c5baSBill Taylor 	ddi_fm_error_t derr;
22429e39c5baSBill Taylor 
22439e39c5baSBill Taylor 	/* Count up the number of threads issuing this PIO */
22449e39c5baSBill Taylor 	mutex_enter(&hdlp->lock);
22459e39c5baSBill Taylor 	hdlp->thread_cnt++;
22469e39c5baSBill Taylor 	mutex_exit(&hdlp->lock);
22479e39c5baSBill Taylor 
22489e39c5baSBill Taylor 	/* Get the PIO error via FMA */
22499e39c5baSBill Taylor 	ddi_fm_acc_err_get(fm_acc_hdl(hdlp), &derr, DDI_FME_VERSION);
22509e39c5baSBill Taylor 
22519e39c5baSBill Taylor #ifdef FMA_TEST
22529e39c5baSBill Taylor 	/* Trigger PIO errors */
22539e39c5baSBill Taylor 	if (tst != NULL && tst->trigger & HCA_TEST_START) {
22549e39c5baSBill Taylor 		(*tst->pio_injection)(tst, &derr);
22559e39c5baSBill Taylor 	}
22569e39c5baSBill Taylor #endif /* FMA_TEST */
22579e39c5baSBill Taylor 
22589e39c5baSBill Taylor 	switch (derr.fme_status) {
22599e39c5baSBill Taylor 	case DDI_FM_OK:
22609e39c5baSBill Taylor 		/* Not have to clear the fma error log */
22619e39c5baSBill Taylor 		return (HCA_PIO_OK);
22629e39c5baSBill Taylor 
22639e39c5baSBill Taylor 	case DDI_FM_NONFATAL:
22649e39c5baSBill Taylor 		/* Now clear this error */
22659e39c5baSBill Taylor 		ddi_fm_acc_err_clear(fm_acc_hdl(hdlp), DDI_FME_VERSION);
22669e39c5baSBill Taylor 
22679e39c5baSBill Taylor 		/* Log this error and notify it as a persistent error */
22689e39c5baSBill Taylor 		ddi_fm_service_impact(dip, DDI_SERVICE_LOST);
22699e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
22709e39c5baSBill Taylor 
22719e39c5baSBill Taylor 	/* In theory, this shouldn't happen */
22729e39c5baSBill Taylor 	case DDI_FM_FATAL:
22739e39c5baSBill Taylor 	case DDI_FM_UNKNOWN:
22749e39c5baSBill Taylor 	default:
22759e39c5baSBill Taylor 		cmn_err(CE_WARN, "Unknown HCA HW error status (%d)",
22769e39c5baSBill Taylor 		    derr.fme_status);
22779e39c5baSBill Taylor 		/* Return this as a persistent error */
22789e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
22799e39c5baSBill Taylor 	}
22809e39c5baSBill Taylor }
22819e39c5baSBill Taylor 
22829e39c5baSBill Taylor 
22839e39c5baSBill Taylor /*
22849e39c5baSBill Taylor  *  int
22859e39c5baSBill Taylor  *  i_hca_pio_end(dev_info_t *dip, ddi_acc_handle_t handle, int *cnt,
22869e39c5baSBill Taylor  *      struct i_hca_fm_test *tst)
22879e39c5baSBill Taylor  *
22889e39c5baSBill Taylor  *  Overview
22899e39c5baSBill Taylor  *      i_hca_pio_end() is the other of a pair of HCA FM fuctions for PIO,
22909e39c5baSBill Taylor  *      which should be called after HCA drivers issue PIOs against I/O space.
22919e39c5baSBill Taylor  *      See HCA FM comments at the beginning of this file in detail.
22929e39c5baSBill Taylor  *
22939e39c5baSBill Taylor  *  Argument
22949e39c5baSBill Taylor  *      dip: pointer to this device dev_info structure
22959e39c5baSBill Taylor  *	handle: pointer to ddi_acc_handle_t used for HCA FM
22969e39c5baSBill Taylor  *	cnt: pointer to the counter variable which holds the nubmer of retry
22979e39c5baSBill Taylor  *	     when a HW error is detected.
22989e39c5baSBill Taylor  *      tst: pointer to HCA FM function test structure. If the structure
22999e39c5baSBill Taylor  *           is not used, the NULL value must be passed instead.
23009e39c5baSBill Taylor  *
23019e39c5baSBill Taylor  *  Return value
23029e39c5baSBill Taylor  *  	error status showing whether or not this error can retry
23039e39c5baSBill Taylor  *	HCA_PIO_OK		No HW errors
23049e39c5baSBill Taylor  *	HCA_PIO_TRANSIENT	This error could be transient
23059e39c5baSBill Taylor  *	HCA_PIO_PERSISTENT	This error is persistent
23069e39c5baSBill Taylor  *
23079e39c5baSBill Taylor  *  Caller's context
23089e39c5baSBill Taylor  *      i_hca_pio_end() can be called in user, kernel or interrupt context.
23099e39c5baSBill Taylor  */
23109e39c5baSBill Taylor /* ARGSUSED */
23119e39c5baSBill Taylor static int
i_hca_pio_end(dev_info_t * dip,struct i_hca_acc_handle * hdlp,int * cnt,struct i_hca_fm_test * tst)23129e39c5baSBill Taylor i_hca_pio_end(dev_info_t *dip, struct i_hca_acc_handle *hdlp, int *cnt,
23139e39c5baSBill Taylor     struct i_hca_fm_test *tst)
23149e39c5baSBill Taylor {
23159e39c5baSBill Taylor 	ddi_fm_error_t derr;
23169e39c5baSBill Taylor 
23179e39c5baSBill Taylor 	/* Get the PIO error via FMA */
23189e39c5baSBill Taylor 	ddi_fm_acc_err_get(fm_acc_hdl(hdlp), &derr, DDI_FME_VERSION);
23199e39c5baSBill Taylor 
23209e39c5baSBill Taylor #ifdef FMA_TEST
23219e39c5baSBill Taylor 	/* Trigger PIO errors */
23229e39c5baSBill Taylor 	if (tst != NULL && tst->trigger & HCA_TEST_END) {
23239e39c5baSBill Taylor 		(*tst->pio_injection)(tst, &derr);
23249e39c5baSBill Taylor 	}
23259e39c5baSBill Taylor #endif /* FMA_TEST */
23269e39c5baSBill Taylor 
23279e39c5baSBill Taylor 	/* Evaluate the PIO error */
23289e39c5baSBill Taylor 	switch (derr.fme_status) {
23299e39c5baSBill Taylor 	case DDI_FM_OK:
23309e39c5baSBill Taylor 		/* Count down the number of threads issuing this PIO */
23319e39c5baSBill Taylor 		mutex_enter(&hdlp->lock);
23329e39c5baSBill Taylor 		hdlp->thread_cnt--;
23339e39c5baSBill Taylor 		mutex_exit(&hdlp->lock);
23349e39c5baSBill Taylor 
23359e39c5baSBill Taylor 		/* Not have to clear the fma error log */
23369e39c5baSBill Taylor 		return (HCA_PIO_OK);
23379e39c5baSBill Taylor 
23389e39c5baSBill Taylor 	case DDI_FM_NONFATAL:
23399e39c5baSBill Taylor 		/* Now clear this error */
23409e39c5baSBill Taylor 		ddi_fm_acc_err_clear(fm_acc_hdl(hdlp), DDI_FME_VERSION);
23419e39c5baSBill Taylor 
23429e39c5baSBill Taylor 		/*
23439e39c5baSBill Taylor 		 * Check if this error comes from another thread running
23449e39c5baSBill Taylor 		 * with the same handle almost at the same time.
23459e39c5baSBill Taylor 		 */
23469e39c5baSBill Taylor 		mutex_enter(&hdlp->lock);
23479e39c5baSBill Taylor 		if (hdlp->thread_cnt > 1) {
23489e39c5baSBill Taylor 			/* Count down the number of threads */
23499e39c5baSBill Taylor 			hdlp->thread_cnt--;
23509e39c5baSBill Taylor 			mutex_exit(&hdlp->lock);
23519e39c5baSBill Taylor 
23529e39c5baSBill Taylor 			/* Return this as a persistent error */
23539e39c5baSBill Taylor 			return (HCA_PIO_PERSISTENT);
23549e39c5baSBill Taylor 		}
23559e39c5baSBill Taylor 		mutex_exit(&hdlp->lock);
23569e39c5baSBill Taylor 
23579e39c5baSBill Taylor 		/* Now determine if this error is persistent or not */
23589e39c5baSBill Taylor 		if (--(*cnt) >= 0)  {
23599e39c5baSBill Taylor 			return (HCA_PIO_TRANSIENT);
23609e39c5baSBill Taylor 		} else {
23619e39c5baSBill Taylor 			/* Count down the number of threads */
23629e39c5baSBill Taylor 			mutex_enter(&hdlp->lock);
23639e39c5baSBill Taylor 			hdlp->thread_cnt--;
23649e39c5baSBill Taylor 			mutex_exit(&hdlp->lock);
23659e39c5baSBill Taylor 			return (HCA_PIO_PERSISTENT);
23669e39c5baSBill Taylor 		}
23679e39c5baSBill Taylor 
23689e39c5baSBill Taylor 	/* In theory, this shouldn't happen */
23699e39c5baSBill Taylor 	case DDI_FM_FATAL:
23709e39c5baSBill Taylor 	case DDI_FM_UNKNOWN:
23719e39c5baSBill Taylor 	default:
23729e39c5baSBill Taylor 		cmn_err(CE_WARN, "Unknown HCA HW error status (%d)",
23739e39c5baSBill Taylor 		    derr.fme_status);
23749e39c5baSBill Taylor 		/* Return this as a persistent error */
23759e39c5baSBill Taylor 		return (HCA_PIO_PERSISTENT);
23769e39c5baSBill Taylor 	}
23779e39c5baSBill Taylor }
23789e39c5baSBill Taylor 
23799e39c5baSBill Taylor 
23809e39c5baSBill Taylor /*
23819e39c5baSBill Taylor  * HCA FM Test Interface
23829e39c5baSBill Taylor  *
23839e39c5baSBill Taylor  * These functions should be used for any HCA drivers, but probably
23849e39c5baSBill Taylor  * customized for their own HW design and/or FM implementation.
23859e39c5baSBill Taylor  * Customized functins should have the driver name prefix such as
23869e39c5baSBill Taylor  * hermon_xxxx() and be defined separately but whose function should
23879e39c5baSBill Taylor  * call the common interface inside.
23889e39c5baSBill Taylor  */
23899e39c5baSBill Taylor 
23909e39c5baSBill Taylor #ifdef FMA_TEST
23919e39c5baSBill Taylor static int test_num;		/* serial number */
23929e39c5baSBill Taylor static kmutex_t i_hca_test_lock; 	/* lock for serial numer */
23939e39c5baSBill Taylor 
23949e39c5baSBill Taylor /*
23959e39c5baSBill Taylor  *  void
23969e39c5baSBill Taylor  *  i_hca_test_init(mod_hash_t **strHashp, mod_hash_t **idHashp)
23979e39c5baSBill Taylor  *
23989e39c5baSBill Taylor  *  Overview
23999e39c5baSBill Taylor  *      i_hca_test_init() creates two hash tables, one of which is for string,
24009e39c5baSBill Taylor  *      and the other of which is for ID, then saves pointers to arguments
24019e39c5baSBill Taylor  *      passed. This function uses the mod_hash utilities to manage the
24029e39c5baSBill Taylor  *      hash tables. About the mod_hash, see common/os/modhash.c.
24039e39c5baSBill Taylor  *
24049e39c5baSBill Taylor  *  Argument
24059e39c5baSBill Taylor  *      strHashp: pointer to String hash table pointer
24069e39c5baSBill Taylor  *      idHashp: pointer to ID hash table pointer
24079e39c5baSBill Taylor  *
24089e39c5baSBill Taylor  *  Return value
24099e39c5baSBill Taylor  *      Nothing
24109e39c5baSBill Taylor  *
24119e39c5baSBill Taylor  *  Caller's context
24129e39c5baSBill Taylor  *      i_hca_test_init() can be called in user or kernel context only.
24139e39c5baSBill Taylor  */
24149e39c5baSBill Taylor static void
i_hca_test_init(mod_hash_t ** strHashp,mod_hash_t ** idHashp)24159e39c5baSBill Taylor i_hca_test_init(mod_hash_t **strHashp, mod_hash_t **idHashp)
24169e39c5baSBill Taylor {
24179e39c5baSBill Taylor 	*idHashp = mod_hash_create_idhash("HCA_FMA_id_hash",
24189e39c5baSBill Taylor 	    FMA_TEST_HASHSZ, mod_hash_null_valdtor);
24199e39c5baSBill Taylor 
24209e39c5baSBill Taylor 	*strHashp = mod_hash_create_strhash("HCA_FMA_test_hash",
24219e39c5baSBill Taylor 	    FMA_TEST_HASHSZ, i_hca_test_free_item);
24229e39c5baSBill Taylor }
24239e39c5baSBill Taylor 
24249e39c5baSBill Taylor 
24259e39c5baSBill Taylor /*
24269e39c5baSBill Taylor  *  void
24279e39c5baSBill Taylor  *  i_hca_test_fini(mod_hash_t **strHashp, mod_hash_t **idHashp)
24289e39c5baSBill Taylor  *
24299e39c5baSBill Taylor  *  Overview
24309e39c5baSBill Taylor  *      i_hca_test_fini() releases two hash tables used for HCA FM test.
24319e39c5baSBill Taylor  *
24329e39c5baSBill Taylor  *  Argument
24339e39c5baSBill Taylor  *      strHashp: pointer to String hash table pointer
24349e39c5baSBill Taylor  *      idHashp: pointer to ID hash table pointer
24359e39c5baSBill Taylor  *
24369e39c5baSBill Taylor  *  Return value
24379e39c5baSBill Taylor  *      Nothing
24389e39c5baSBill Taylor  *
24399e39c5baSBill Taylor  *  Caller's context
24409e39c5baSBill Taylor  *      i_hca_test_fini() can be called in user, kernel or interrupt context.
24419e39c5baSBill Taylor  *
24429e39c5baSBill Taylor  */
24439e39c5baSBill Taylor static void
i_hca_test_fini(mod_hash_t ** strHashp,mod_hash_t ** idHashp)24449e39c5baSBill Taylor i_hca_test_fini(mod_hash_t **strHashp, mod_hash_t **idHashp)
24459e39c5baSBill Taylor {
24469e39c5baSBill Taylor 	mod_hash_destroy_hash(*strHashp);
24479e39c5baSBill Taylor 	*strHashp = NULL;
24489e39c5baSBill Taylor 
24499e39c5baSBill Taylor 	mod_hash_destroy_hash(*idHashp);
24509e39c5baSBill Taylor 	*idHashp = NULL;
24519e39c5baSBill Taylor }
24529e39c5baSBill Taylor 
24539e39c5baSBill Taylor 
24549e39c5baSBill Taylor /*
24559e39c5baSBill Taylor  *  struct i_hca_fm_test *
24569e39c5baSBill Taylor  *  i_hca_test_register(char *filename, int linenum, int type,
24579e39c5baSBill Taylor  *      void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *),
24589e39c5baSBill Taylor  *      void *private, mod_hash_t *strHash, mod_hash_t *idHash, int preTestNum)
24599e39c5baSBill Taylor  *
24609e39c5baSBill Taylor  *  Overview
24619e39c5baSBill Taylor  *      i_hca_test_register() registers an HCA FM test item against HCA FM
24629e39c5baSBill Taylor  *      function callings specified with the file name and the line number
24639e39c5baSBill Taylor  *      (passed as the arguments).
24649e39c5baSBill Taylor  *
24659e39c5baSBill Taylor  *  Argument
24669e39c5baSBill Taylor  *  	filename: source file name where the function call is implemented
24679e39c5baSBill Taylor  *		  This value is usually a __FILE__  pre-defined macro.
24689e39c5baSBill Taylor  *  	linenum: line number where the function call is described in the
24699e39c5baSBill Taylor  *		 file specified above.
24709e39c5baSBill Taylor  *		 This value is usually a __LINE__ pre-defined macro.
24719e39c5baSBill Taylor  *	type: HW error type
24729e39c5baSBill Taylor  *			HCA_TEST_PIO	pio error
24739e39c5baSBill Taylor  *			HCA_TEST_IBA	ib specific error
24749e39c5baSBill Taylor  *	pio_injection: pio error injection callback function invoked when the
24759e39c5baSBill Taylor  *		       function specified above (with the file name and the
24769e39c5baSBill Taylor  *		       line number) is executed. If the function is not a PIO,
24779e39c5baSBill Taylor  *		       request, this parameter should be NULL.
24789e39c5baSBill Taylor  *	private: the argument passed to either of injection functions when
24799e39c5baSBill Taylor  *		 they're invoked.
24809e39c5baSBill Taylor  *      strHashp: pointer to String hash table
24819e39c5baSBill Taylor  *      idHashp: pointer to ID hash table
24829e39c5baSBill Taylor  *      preTestNum: the index of the pre-defined testset for this test item.
24839e39c5baSBill Taylor  *
24849e39c5baSBill Taylor  *  Return value
24859e39c5baSBill Taylor  *      pointer to HCA FM function test structure registered.
24869e39c5baSBill Taylor  *
24879e39c5baSBill Taylor  *  Caller's context
24889e39c5baSBill Taylor  *      i_hca_test_register() can be called in user, kernel or interrupt
24899e39c5baSBill Taylor  *      context.
24909e39c5baSBill Taylor  *
24919e39c5baSBill Taylor  */
24929e39c5baSBill Taylor static struct i_hca_fm_test *
i_hca_test_register(char * filename,int linenum,int type,void (* pio_injection)(struct i_hca_fm_test *,ddi_fm_error_t *),void * private,mod_hash_t * strHash,mod_hash_t * idHash,int preTestNum)24939e39c5baSBill Taylor i_hca_test_register(char *filename, int linenum, int type,
24949e39c5baSBill Taylor     void (*pio_injection)(struct i_hca_fm_test *, ddi_fm_error_t *),
24959e39c5baSBill Taylor     void *private, mod_hash_t *strHash, mod_hash_t *idHash, int preTestNum)
24969e39c5baSBill Taylor {
24979e39c5baSBill Taylor 	struct i_hca_fm_test *t_item;
24989e39c5baSBill Taylor 	char key_buf[255], *hash_key;
24999e39c5baSBill Taylor 	int status;
25009e39c5baSBill Taylor 
25019e39c5baSBill Taylor 	(void) sprintf(key_buf, "%s:%d", filename, linenum);
25029e39c5baSBill Taylor 	hash_key = kmem_zalloc(strlen(key_buf) + 1, KM_NOSLEEP);
25039e39c5baSBill Taylor 
25049e39c5baSBill Taylor 	if (hash_key == NULL)
25059e39c5baSBill Taylor 		cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
25069e39c5baSBill Taylor 
25079e39c5baSBill Taylor 	bcopy(key_buf, hash_key, strlen(key_buf));
25089e39c5baSBill Taylor 
25099e39c5baSBill Taylor 	status = mod_hash_find(strHash, (mod_hash_key_t)hash_key,
25109e39c5baSBill Taylor 	    (mod_hash_val_t *)&t_item);
25119e39c5baSBill Taylor 
25129e39c5baSBill Taylor 	switch (status) {
25139e39c5baSBill Taylor 	case MH_ERR_NOTFOUND:
25149e39c5baSBill Taylor 		t_item = (struct i_hca_fm_test *)
25159e39c5baSBill Taylor 		    kmem_alloc(sizeof (struct i_hca_fm_test), KM_NOSLEEP);
25169e39c5baSBill Taylor 		if (t_item == NULL)
25179e39c5baSBill Taylor 			cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
25189e39c5baSBill Taylor 
25199e39c5baSBill Taylor 		/* Set the error number */
25209e39c5baSBill Taylor 		mutex_enter(&i_hca_test_lock);
25219e39c5baSBill Taylor 		t_item->num = test_num++;
25229e39c5baSBill Taylor 		mutex_exit(&i_hca_test_lock);
25239e39c5baSBill Taylor 
25249e39c5baSBill Taylor 		/* Set type and other static information */
25259e39c5baSBill Taylor 		t_item->type = type;
25269e39c5baSBill Taylor 		t_item->line_num = linenum;
25279e39c5baSBill Taylor 		t_item->file_name = filename;
25289e39c5baSBill Taylor 		t_item->hash_key = hash_key;
25299e39c5baSBill Taylor 		t_item->private = private;
25309e39c5baSBill Taylor 		t_item->pio_injection = pio_injection;
25319e39c5baSBill Taylor 
25329e39c5baSBill Taylor 		/* Set the pre-defined hermon test item */
25339e39c5baSBill Taylor 		i_hca_test_set_item(preTestNum, (struct i_hca_fm_test *)t_item);
25349e39c5baSBill Taylor 
25359e39c5baSBill Taylor 		status = mod_hash_insert(strHash, (mod_hash_key_t)
25369e39c5baSBill Taylor 		    hash_key, (mod_hash_val_t)t_item);
25379e39c5baSBill Taylor 		ASSERT(status == 0);
25389e39c5baSBill Taylor 
25399e39c5baSBill Taylor 		status = mod_hash_insert(idHash, (mod_hash_key_t)
25409e39c5baSBill Taylor 		    (uintptr_t)t_item->num, (mod_hash_val_t)t_item);
25419e39c5baSBill Taylor 		ASSERT(status == 0);
25429e39c5baSBill Taylor 		break;
25439e39c5baSBill Taylor 
25449e39c5baSBill Taylor 	case MH_ERR_NOMEM:
25459e39c5baSBill Taylor 		cmn_err(CE_PANIC, "No memory for HCA FMA Test.");
25469e39c5baSBill Taylor 		break;
25479e39c5baSBill Taylor 
25489e39c5baSBill Taylor 	case MH_ERR_DUPLICATE:
25499e39c5baSBill Taylor 		cmn_err(CE_PANIC, "HCA FMA Test Internal Error.");
25509e39c5baSBill Taylor 		break;
25519e39c5baSBill Taylor 	default:
25529e39c5baSBill Taylor 		/* OK, this is already registered. */
25539e39c5baSBill Taylor 		kmem_free(hash_key, strlen(key_buf) + 1);
25549e39c5baSBill Taylor 		break;
25559e39c5baSBill Taylor 	}
25569e39c5baSBill Taylor 	return (t_item);
25579e39c5baSBill Taylor }
25589e39c5baSBill Taylor 
25599e39c5baSBill Taylor 
25609e39c5baSBill Taylor /*
25619e39c5baSBill Taylor  *  void
25629e39c5baSBill Taylor  *  i_hca_test_set_item(int num, struct i_hca_fm_test *t_item)
25639e39c5baSBill Taylor  *
25649e39c5baSBill Taylor  *  Overview
25659e39c5baSBill Taylor  *      i_hca_test_set_item() is a private function used in
25669e39c5baSBill Taylor  *      i_hca_test_register() above. This function sets the testset specified
25679e39c5baSBill Taylor  *      (with the index number) to HCA FM function test structure.
25689e39c5baSBill Taylor  *
25699e39c5baSBill Taylor  *  Argument
25709e39c5baSBill Taylor  *      num: index to test set (testset structure array)
25719e39c5baSBill Taylor  *      t_item: pointer to HCA fM function test structure
25729e39c5baSBill Taylor  *
25739e39c5baSBill Taylor  *  Return value
25749e39c5baSBill Taylor  *      Nothing
25759e39c5baSBill Taylor  *
25769e39c5baSBill Taylor  *  Caller's context
25779e39c5baSBill Taylor  *      i_hca_test_set_item() can be called in user, kernel, interrupt
25789e39c5baSBill Taylor  *      context or hight interrupt context.
25799e39c5baSBill Taylor  *
25809e39c5baSBill Taylor  */
25819e39c5baSBill Taylor static void
i_hca_test_set_item(int num,struct i_hca_fm_test * t_item)25829e39c5baSBill Taylor i_hca_test_set_item(int num, struct i_hca_fm_test *t_item)
25839e39c5baSBill Taylor {
25849e39c5baSBill Taylor 	if (num < 0 || num >= sizeof (testset) / sizeof (hermon_test_t) ||
25859e39c5baSBill Taylor 	    testset[num].type != t_item->type) {
25869e39c5baSBill Taylor 		t_item->trigger = testset[0].trigger;
25879e39c5baSBill Taylor 		t_item->errcnt = testset[0].errcnt;
25889e39c5baSBill Taylor 		return;
25899e39c5baSBill Taylor 	}
25909e39c5baSBill Taylor 
25919e39c5baSBill Taylor 	/* Set the testsuite */
25929e39c5baSBill Taylor 	t_item->trigger = testset[num].trigger;
25939e39c5baSBill Taylor 	t_item->errcnt = testset[num].errcnt;
25949e39c5baSBill Taylor }
25959e39c5baSBill Taylor 
25969e39c5baSBill Taylor 
25979e39c5baSBill Taylor /*
25989e39c5baSBill Taylor  *  void
25999e39c5baSBill Taylor  *  i_hca_test_free_item(mod_hash_val_t val)
26009e39c5baSBill Taylor  *
26019e39c5baSBill Taylor  *  Overview
26029e39c5baSBill Taylor  *      i_hca_test_free_item() is a private function used to free HCA FM
26039e39c5baSBill Taylor  *      function test structure when i_hca_test_fini() is called. This function
26049e39c5baSBill Taylor  *      is registered as a destructor when the hash table is created in
26059e39c5baSBill Taylor  *      i_hca_test_init().
26069e39c5baSBill Taylor  *
26079e39c5baSBill Taylor  *  Argument
26089e39c5baSBill Taylor  *      val: pointer to the value stored in hash table (pointer to HCA FM
26099e39c5baSBill Taylor  *           function test structure)
26109e39c5baSBill Taylor  *
26119e39c5baSBill Taylor  *  Return value
26129e39c5baSBill Taylor  *      Nothing
26139e39c5baSBill Taylor  *
26149e39c5baSBill Taylor  *  Caller's context
26159e39c5baSBill Taylor  *      i_hca_test_free_item() can be called in user, kernel or interrupt
26169e39c5baSBill Taylor  *      context.
26179e39c5baSBill Taylor  *
26189e39c5baSBill Taylor  */
26199e39c5baSBill Taylor static void
i_hca_test_free_item(mod_hash_val_t val)26209e39c5baSBill Taylor i_hca_test_free_item(mod_hash_val_t val)
26219e39c5baSBill Taylor {
26229e39c5baSBill Taylor 	struct i_hca_fm_test *t_item = (struct i_hca_fm_test *)val;
26239e39c5baSBill Taylor 	kmem_free(t_item, sizeof (struct i_hca_fm_test));
26249e39c5baSBill Taylor }
26259e39c5baSBill Taylor #endif /* FMA_TEST */
2626