/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* tavor.c
*
* Implements all the routines necessary for the attach, setup,
* initialization (and subsequent possible teardown and detach) of the
* Tavor InfiniBand HCA driver.
*/
/* Tavor HCA State Pointer */
void *tavor_statep;
/*
* The Tavor "userland resource database" is common to instances of the
* Tavor HCA driver. This structure "tavor_userland_rsrc_db" contains all
* the necessary information to maintain it.
*/
/* X86 fastreboot support */
static int tavor_intr_disable(tavor_state_t *);
static int tavor_quiesce(dev_info_t *);
tavor_open, /* open */
tavor_close, /* close */
nodev, /* strategy (block) */
nodev, /* print (block) */
nodev, /* dump (block) */
nodev, /* read */
nodev, /* write */
tavor_ioctl, /* ioctl */
tavor_devmap, /* devmap */
NULL, /* mmap */
nodev, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* prop_op */
NULL, /* streams */
D_DEVMAP, /* flags */
CB_REV /* rev */
};
/* Driver Operations */
DEVO_REV, /* struct rev */
0, /* refcnt */
tavor_getinfo, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
tavor_attach, /* attach */
tavor_detach, /* detach */
nodev, /* reset */
&tavor_cb_ops, /* cb_ops */
NULL, /* bus_ops */
nodev, /* power */
tavor_quiesce, /* devo_quiesce */
};
/* Module Driver Info */
"Tavor InfiniBand HCA Driver",
};
/* Module Linkage */
};
/*
* This extern refers to the ibc_operations_t function vector that is defined
* in the tavor_ci.c file.
*/
extern ibc_operations_t tavor_ibc_ops;
#ifndef NPROBE
extern int tnf_mod_load(void);
#endif
/*
* _init()
*/
int
_init()
{
int status;
#ifndef NPROBE
(void) tnf_mod_load();
#endif
if (status != 0) {
#ifndef NPROBE
(void) tnf_mod_unload(&tavor_modlinkage);
#endif
return (status);
}
if (status != 0) {
#ifndef NPROBE
(void) tnf_mod_unload(&tavor_modlinkage);
#endif
return (status);
}
if (status != 0) {
#ifndef NPROBE
(void) tnf_mod_unload(&tavor_modlinkage);
#endif
return (status);
}
/* Initialize the Tavor "userland resources database" */
return (status);
}
/*
* _info()
*/
int
{
int status;
return (status);
}
/*
* _fini()
*/
int
_fini()
{
int status;
if (status != 0) {
return (status);
}
/* Destroy the Tavor "userland resources database" */
#ifndef NPROBE
(void) tnf_mod_unload(&tavor_modlinkage);
#endif
return (status);
}
/*
* tavor_getinfo()
*/
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
break;
}
return (DDI_FAILURE);
}
/*
* tavor_open()
*/
/* ARGSUSED */
static int
{
int status;
return (ENXIO);
}
/*
* Only allow driver to be opened for character access, and verify
* whether exclusive access is allowed.
*/
secpolicy_excl_open(credp) != 0)) {
return (EINVAL);
}
/*
* Search for the current process PID in the "userland resources
* database". If it is not found, then attempt to allocate a UAR
* page and add the ("key", "value") pair to the database.
* Note: As a last step we always return a devp appropriate for
* the open. Either we return a new minor number (based on the
* instance and the UAR page index) or we return the current minor
* number for the given client process.
*
* We also add an entry to the database to allow for lookup from
* "dev_t" to the current process PID. This is necessary because,
* under certain circumstance, the process PID that calls the Tavor
* close() entry point may not be the same as the one who called
* open(). Specifically, this can happen if a child process calls
* the Tavor's open() entry point, gets a UAR page, maps it out (using
* mmap()), and then exits without calling munmap(). Because mmap()
* adds a reference to the file descriptor, at the exit of the child
* process the file descriptor is "inherited" by the parent (and will
* be close()'d by the parent's PID only when it exits).
*
* Note: We use the tavor_umap_db_find_nolock() and
* tavor_umap_db_add_nolock() database access routines below (with
* an explicit mutex_enter of the database lock - "tdl_umapdb_lock")
* to ensure that the multiple accesses (in this case searching for,
* and then adding _two_ database entries) can be done atomically.
*/
key = ddi_get_pid();
if (status != DDI_SUCCESS) {
/*
* If we are in 'maintenance mode', we cannot alloc a UAR page.
* But we still need some rsrcp value, and a mostly unique
* tr_indx value. So we set rsrcp to NULL for maintenance
* mode, and use a rolling count for tr_indx. The field
* 'ts_open_tr_indx' is used only in this maintenance mode
* condition.
*
* Otherwise, if we are in operational mode then we allocate
* the UAR page as normal, and use the rsrcp value and tr_indx
* value from that allocation.
*/
} else {
/* Allocate a new UAR page for this process */
TAVOR_NOSLEEP, &rsrcp);
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (EAGAIN);
}
}
/*
* Allocate an entry to track the UAR page resource in the
* "userland resources database".
*/
/* If in "maintenance mode", don't free the rsrc */
}
TAVOR_TNF_ERROR, "");
return (EAGAIN);
}
/*
* Create a new device number. Minor number is a function of
* the UAR page index (15 bits) and the device instance number
* (3 bits).
*/
/*
* Allocate another entry in the "userland resources database"
* to track the association of the device number (above) to
* the current process ID (in "key").
*/
/* If in "maintenance mode", don't free the rsrc */
}
TAVOR_TNF_ERROR, "");
return (EAGAIN);
}
/* Add the entries to the database */
} else {
/*
* Return the same device number as on the original open()
* call. This was calculated as a function of the UAR page
* index (top 16 bits) and the device instance number
*/
}
return (0);
}
/*
* tavor_close()
*/
/* ARGSUSED */
static int
{
int status;
return (ENXIO);
}
/*
* Search for "dev_t" in the "userland resources database". As
* explained above in tavor_open(), we can't depend on using the
* current process ID here to do the lookup because the process
* that ultimately closes may not be the same one who opened
* (because of inheritance).
* So we lookup the "dev_t" (which points to the PID of the process
* that opened), and we remove the entry from the database (and free
* it up). Then we do another query based on the PID value. And when
* we find that database entry, we free it up too and then free the
* Tavor UAR page resource.
*
* Note: We use the tavor_umap_db_find_nolock() database access
* routine below (with an explicit mutex_enter of the database lock)
* to ensure that the multiple accesses (which attempt to remove the
* two database entries) can be done atomically.
*
* This works the same in both maintenance mode and HCA mode, except
* for the call to tavor_rsrc_free(). In the case of maintenance mode,
* this call is not needed, as it was not allocated in tavor_open()
* above.
*/
if (status == DDI_SUCCESS) {
/*
* If the "tdb_priv" field is non-NULL, it indicates that
* some "on close" handling is still necessary. Call
* tavor_umap_db_handle_onclose_cb() to do the handling (i.e.
* to invoke all the registered callbacks). Then free up
* the resources associated with "tdb_priv" and continue
* closing.
*/
}
/*
* Now do another lookup using PID as the key (copy it from
* "value"). When this lookup is complete, the "value" field
* will contain the tavor_rsrc_t pointer for the UAR page
* resource.
*/
&umapdb);
if (status == DDI_SUCCESS) {
/* If in "maintenance mode", don't free the rsrc */
}
}
}
return (0);
}
/*
* tavor_attach()
* Context: Only called from attach() path context
*/
static int
{
int instance;
int status;
#ifdef __lock_lint
(void) tavor_quiesce(dip);
#endif
switch (cmd) {
case DDI_ATTACH:
if (status != DDI_SUCCESS) {
"attach_ssz_fail", instance);
goto fail_attach_nomsg;
}
"attach_gss_fail", instance);
goto fail_attach_nomsg;
}
/* clear the attach error buffer */
/*
* Initialize Tavor driver and hardware.
*
* Note: If this initialization fails we may still wish to
* create a device node and remain operational so that Tavor
* If this is the case, then "ts_operational_mode" will be
* equal to TAVOR_MAINTENANCE_MODE. We will not attempt to
* attach to the IBTF or register with the IBMF (i.e. no
* InfiniBand interfaces will be enabled).
*/
if ((status != DDI_SUCCESS) &&
TAVOR_TNF_ERROR, "");
goto fail_attach;
}
/* Create the minor node for device */
DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
"attach_create_mn_fail");
TAVOR_TNF_ERROR, "");
goto fail_attach;
}
/*
* If we are in "maintenance mode", then we don't want to
* register with the IBTF. All InfiniBand interfaces are
* uninitialized, and the device is only capable of handling
*/
/* Attach to InfiniBand Transport Framework (IBTF) */
&state->ts_ibtfinfo);
if (ibc_status != IBC_SUCCESS) {
TAVOR_TNF_ERROR, "");
"attach_ibcattach_fail");
goto fail_attach;
}
/*
* Now that we've successfully attached to the IBTF,
* we enable all appropriate asynch and CQ events to
* be forwarded to the IBTF.
*/
/* Register agents with IB Mgmt Framework (IBMF) */
if (status != DDI_SUCCESS) {
if (state->ts_in_evcallb != 0) {
"quiesce Tavor IBTF callbacks");
}
TAVOR_TNF_ERROR, "");
"attach_agentinit_fail");
goto fail_attach;
}
}
/* Report that driver was loaded */
/* Send device information to log file */
/* Report attach in maintenance mode, if appropriate */
}
return (DDI_SUCCESS);
case DDI_RESUME:
/* Add code here for DDI_RESUME XXX */
return (DDI_FAILURE);
default:
break;
}
return (DDI_FAILURE);
}
/*
* tavor_detach()
* Context: Only called from detach() path context
*/
static int
{
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
/*
* If we are in "maintenance mode", then we do not want to
* do teardown for any of the InfiniBand interfaces.
* Specifically, this means not detaching from IBTF (we never
* attached to begin with) and not deregistering from IBMF.
*/
/* Unregister agents from IB Mgmt Framework (IBMF) */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* Attempt the "pre-detach" from InfiniBand Transport
* Framework (IBTF). At this point the IBTF is still
* capable of handling incoming asynch and completion
* events. This "pre-detach" is primarily a mechanism
* to notify the appropriate IBTF clients that the
*/
if (ibc_status != IBC_SUCCESS) {
if (status != DDI_SUCCESS) {
"restart Tavor agents");
}
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* Before we can fully detach from the IBTF we need to
* ensure that we have handled all outstanding event
* callbacks. This is accomplished by quiescing the
* event callback mechanism. Note: if we are unable
* to successfully quiesce the callbacks, then this is
* an indication that something has probably gone
* seriously wrong. We print out a warning, but
* continue.
*/
if (state->ts_in_evcallb != 0) {
"IBTF callbacks");
}
/* Complete the detach from the IBTF */
}
/* Remove the minor node for device */
/*
* Only call tavor_drv_fini() if we are in Tavor HCA mode.
* (Because if we are in "maintenance mode", then we never
* successfully finished init.) Only report successful
* detach for normal HCA mode.
*/
/* Cleanup driver resources and shutdown hardware */
"detached\n");
}
return (DDI_SUCCESS);
case DDI_SUSPEND:
/* Add code here for DDI_SUSPEND XXX */
return (DDI_FAILURE);
default:
break;
}
return (DDI_FAILURE);
}
/*
* tavor_drv_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/* Save away devinfo and instance */
/*
* Check and set the operational mode of the device. If the driver is
* bound to the Tavor device in "maintenance mode", then this generally
* means that either the device has been specifically jumpered to
* start in this mode or the firmware boot process has failed to
* successfully load either the primary or the secondary firmware
* image.
*/
return (DDI_FAILURE);
} else {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* Initialize the Tavor hardware.
* Note: If this routine returns an error, it is often an reasonably
* good indication that something Tavor firmware-related has caused
* the failure. In order to give the user an opportunity (if desired)
* to update or reflash the Tavor firmware image, we set
* "ts_operational_mode" flag (described above) to indicate that we
* wish to enter maintenance mode.
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Setup Tavor interrupt handler */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Initialize Tavor softstate */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* tavor_drv_fini()
*/
static void
{
/* Cleanup Tavor softstate */
/* Teardown Tavor interrupts */
/* Cleanup Tavor resources and shutdown hardware */
}
/*
* tavor_drv_fini2()
*/
static void
{
/* TAVOR_DRV_CLEANUP_LEVEL1 */
if (state->ts_reg_cmdhdl) {
}
/* TAVOR_DRV_CLEANUP_LEVEL0 */
if (state->ts_pci_cfghdl) {
}
}
/*
* tavor_isr_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* Add a handler for the interrupt or MSI
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Enable the software interrupt. Note: Even though we are only
* the capability flag, we have to call either ddi_intr_block_enable()
* or ddi_intr_enable().
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
} else {
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
}
/*
* Now that the ISR has been setup, arm all the EQs for event
* generation.
*/
return (DDI_SUCCESS);
}
/*
* tavor_isr_fini()
*/
static void
{
/* Disable the software interrupt */
} else {
}
/*
* Remove the software handler for the interrupt or MSI
*/
}
/*
* tavor_fix_error_buf()
* Context: Only called from attach().
*
* The error_buf_addr returned from QUERY_FW is a PCI address.
* We need to convert it to an offset from the base address,
* which is stored in the assigned-addresses property.
*/
static int
{
int assigned_addr_len;
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* tavor_hw_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
int retries;
/* This is where driver initialization begins */
/* Setup device access attributes */
if (status != DDI_SUCCESS) {
"hw_init_PCI_config_space_regmap_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Map in Tavor registers (CMD, UAR, DDR) and setup offsets */
&state->ts_reg_cmdhdl);
if (status != DDI_SUCCESS) {
"hw_init_CMD_ddirms_fail");
return (DDI_FAILURE);
}
&state->ts_reg_uarhdl);
if (status != DDI_SUCCESS) {
"hw_init_UAR_ddirms_fail");
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
"(check HCA-attached DIMM memory?)\n");
TAVOR_TNF_ERROR, "");
"hw_init_DDR_ddi_regsize_fail");
return (DDI_FAILURE);
}
/*
* memory available, define a minimal memory footprint. This is
* specified in order to not take up too much resources, thus starving
* out others. Only specified if the HCA DIMM is equal to or greater
* than 256MB.
*
*/
#endif /* !(_ELF64) && !(__sparc) */
&state->ts_reg_ddrhdl);
/*
* On 32-bit platform testing (primarily x86), it was seen that the
* ddi_regs_map_setup() call would fail because there wasn't enough
* kernel virtual address space available to map in the entire 256MB
* DDR. So we add this check in here, so that if the 256 (or other
* larger value of DDR) map in fails, that we fallback to try the lower
* size of 128MB.
*
* Note: If we only have 128MB of DDR in the system in the first place,
* we don't try another ddi_regs_map_setup(), and just skip over this
* check and return failures.
*/
/* Try falling back to 128MB DDR mapping */
/*
* 128MB DDR mapping worked.
* Set the updated config profile setting here.
*/
if (status == DDI_SUCCESS) {
TAVOR_TNF_TRACE, "");
}
}
if (status != DDI_SUCCESS) {
if (status == DDI_ME_RNUMBER_RANGE) {
"(check HCA-attached DIMM memory?)\n");
}
"hw_init_DDR_ddirms_fail");
return (DDI_FAILURE);
}
/* Setup Tavor Host Command Register (HCR) */
/* Setup Tavor Event Cause Register (ecr and clr_ecr) */
/* Setup Tavor Software Reset register (sw_reset) */
/* Setup Tavor Clear Interrupt register (clr_int) */
/* Initialize the Phase1 Tavor configuration profile */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Do a software reset of the Tavor HW to ensure proper state */
if (status != TAVOR_CMD_SUCCESS) {
return (DDI_FAILURE);
}
/* Post the SYS_EN command to start the hardware */
if (status != TAVOR_CMD_SUCCESS) {
if ((status == TAVOR_CMD_BAD_NVMEM) ||
(status == TAVOR_CMD_DDR_MEM_ERR)) {
} else {
}
TAVOR_TNF_ERROR, "");
"hw_init_sys_en_cmd_fail");
return (DDI_FAILURE);
}
/* First phase of init for Tavor configuration/resources */
if (status != DDI_SUCCESS) {
"hw_init_rsrcinit1_fail");
return (DDI_FAILURE);
}
/* Query the DDR properties (e.g. total DDR size) */
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_query_ddr_cmd_fail");
return (DDI_FAILURE);
}
/* Figure out how big the firmware image (in DDR) is */
sizeof (tavor_hw_queryfw_t), TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_query_fw_cmd_fail");
return (DDI_FAILURE);
}
TAVOR_TNF_ERROR, "");
"hw_init_fixerrorbuf_fail");
return (DDI_FAILURE);
}
/* Validate that the FW version is appropriate */
if (status != DDI_SUCCESS) {
"expected: %04d.%04d.%04d, "
"actual: %04d.%04d.%04d\n",
"expected: %04d.%04d.%04d, "
"actual: %04d.%04d.%04d\n",
} else {
"%04d.%04d.%04d\n",
}
TAVOR_TNF_ERROR, "");
"hw_init_checkfwver_fail");
return (DDI_FAILURE);
}
drv_usecwait(10);
/* Call MOD_STAT_CFG to setup SRQ support (or disable) */
if (status != DDI_SUCCESS) {
if (retries > 0) {
drv_usecwait(1000);
retries--;
goto retry;
}
status);
TAVOR_TNF_ERROR, "");
"hw_init_mod_stat_cfg_cmd_fail");
return (DDI_FAILURE);
}
/* Figure out Tavor device limits */
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_query_devlim_cmd_fail");
return (DDI_FAILURE);
}
/* Initialize the Phase2 Tavor configuration profile */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Second phase of init for Tavor configuration/resources */
if (status != DDI_SUCCESS) {
"hw_init_rsrcinit2_fail");
return (DDI_FAILURE);
}
/* Miscellaneous query information */
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_query_adapter_cmd_fail");
return (DDI_FAILURE);
}
/* Prepare configuration for Tavor INIT_HCA command */
/* Post command to init Tavor HCA */
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_init_hca_cmd_fail");
return (DDI_FAILURE);
}
/* Allocate protection domain (PD) for Tavor internal use */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"hw_init_internal_pd_alloc_fail");
return (DDI_FAILURE);
}
/* Setup Tavor internal UAR pages (0 and 1) */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"hw_init_internal_uarpgs_alloc_fail");
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"intr_or_msi_init_fail");
return (DDI_FAILURE);
}
/* Setup all of the Tavor EQs */
if (status != DDI_SUCCESS) {
"hw_init_eqinitall_fail");
return (DDI_FAILURE);
}
/* Set aside contexts for QP0 and QP1 */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"hw_init_reserve_special_qp_fail");
return (DDI_FAILURE);
}
/* Initialize for multicast group handling */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Initialize the Tavor IB port(s) */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"hw_init_hca_port_init_fail");
return (DDI_FAILURE);
}
/* Determine NodeGUID and SystemImageGUID */
&nodeinfo);
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_getnodeinfo_cmd_fail");
return (DDI_FAILURE);
}
/*
* If the NodeGUID value was set in OBP properties, then we use that
* value. But we still print a message if the value we queried from
* firmware does not match this value.
*
* Otherwise if OBP value is not set then we use the value from
* firmware unconditionally.
*/
} else {
}
"does not match value set by device property");
}
/*
* If the SystemImageGUID value was set in OBP properties, then we use
* that value. But we still print a message if the value we queried
* from firmware does not match this value.
*
* Otherwise if OBP value is not set then we use the value from
* firmware unconditionally.
*/
} else {
}
"does not match value set by device property");
}
/* Get NodeDescription */
if (status != TAVOR_CMD_SUCCESS) {
status);
TAVOR_TNF_ERROR, "");
"hw_init_getnodedesc_cmd_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* tavor_hw_fini()
*/
static void
{
int status;
switch (cleanup) {
/*
* If we add more driver initialization steps that should be cleaned
* up here, we need to ensure that TAVOR_DRV_CLEANUP_ALL is still the
* first entry (i.e. corresponds to the last init step).
*/
case TAVOR_DRV_CLEANUP_ALL:
/* Shutdown the Tavor IB port(s) */
/* FALLTHROUGH */
/* Teardown resources used for multicast group handling */
/* FALLTHROUGH */
/* Unreserve the special QP contexts */
/* FALLTHROUGH */
/*
* Attempt to teardown all event queues (EQ). If we fail
* here then print a warning message and return. Something
* (either in HW or SW) has gone seriously wrong.
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return;
}
/* FALLTHROUGH */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return;
}
/* FALLTHROUGH */
/* Free the resources for the Tavor internal UAR pages */
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL9:
/*
* Free the PD that was used internally by Tavor software. If
* we fail here then print a warning and return. Something
* (probably software-related, but perhaps HW) has gone wrong.
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return;
}
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL8:
/*
* Post the CLOSE_HCA command to Tavor firmware. If we fail
* here then print a warning and return. Something (either in
* HW or SW) has gone seriously wrong.
*/
if (status != TAVOR_CMD_SUCCESS) {
TAVOR_TNF_ERROR, "");
return;
}
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL7:
/* Cleanup all the phase2 resources first */
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL6:
/* Then cleanup the phase1 resources */
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL5:
/*
* Post the SYS_DIS command to Tavor firmware to shut
* everything down again. If we fail here then print a
* warning and return. Something (probably in HW, but maybe
* in SW) has gone seriously wrong.
*/
if (status != TAVOR_CMD_SUCCESS) {
TAVOR_TNF_ERROR, "");
return;
}
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL4:
/* Teardown any resources allocated for the config profile */
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL3:
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL2:
/* FALLTHROUGH */
case TAVOR_DRV_CLEANUP_LEVEL1:
case TAVOR_DRV_CLEANUP_LEVEL0:
/*
* LEVEL1 and LEVEL0 resources are freed in
* tavor_drv_fini2().
*/
break;
default:
return;
}
}
/*
* tavor_soft_state_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* The ibc_hca_info_t struct is passed to the IBTF. This is the
* routine where we initialize it. Many of the init values come from
* either configuration variables or successful queries of the Tavor
* hardware abilities
*/
/*
* Determine HCA capabilities:
* No default support for IBT_HCA_RD, IBT_HCA_RAW_MULTICAST,
* IBT_HCA_ATOMICS_GLOBAL, IBT_HCA_RESIZE_CHAN, IBT_HCA_INIT_TYPE,
* or IBT_HCA_SHUTDOWN_PORT
* But IBT_HCA_AH_PORT_CHECK, IBT_HCA_SQD_RTS_PORT, IBT_HCA_SI_GUID,
* IBT_HCA_RNR_NAK, and IBT_HCA_CURRENT_QP_STATE are always
* supported
* All other features are conditionally supported, depending on the
* status return by the Tavor HCA (in QUERY_DEV_LIM)
*/
}
}
}
}
}
}
/* Determine VendorID, DeviceID, and revision ID */
/*
* Determine number of available QPs and max QP size. Number of
* available QPs is determined by subtracting the number of
* "reserved QPs" (i.e. reserved for firmware use) from the
* total number configured.
*/
"soft_state_init_maxqpsz_toobig_fail");
return (DDI_FAILURE);
}
/* Determine max scatter-gather size in WQEs */
"soft_state_init_toomanysgl_fail");
return (DDI_FAILURE);
}
/* If the rounded value for max SGL is too large, cap it */
} else {
}
/*
* Determine number of available CQs and max CQ size. Number of
* available CQs is determined by subtracting the number of
* "reserved CQs" (i.e. reserved for firmware use) from the
* total number configured.
*/
"soft_state_init_maxcqsz_toobig_fail");
return (DDI_FAILURE);
}
/*
* Determine number of available SRQs and max SRQ size. Number of
* available SRQs is determined by subtracting the number of
* "reserved SRQs" (i.e. reserved for firmware use) from the
* total number configured.
*/
"soft_state_init_maxsrqsz_toobig_fail");
return (DDI_FAILURE);
}
"soft_state_init_toomanysrqsgl_fail");
return (DDI_FAILURE);
}
/*
* Determine supported HCA page sizes
* XXX
* For now we simply return the system pagesize as the only supported
* pagesize
*/
/*
* Determine number of available MemReg, MemWin, and their max size.
* Number of available MRs and MWs is determined by subtracting
* the number of "reserved MPTs" (i.e. reserved for firmware use)
* from the total number configured for each.
*/
"soft_state_init_maxmrwsz_toobig_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_rdma_in_ee = 0;
hca_attr->hca_max_rdma_out_ee = 0;
/*
* Determine maximum number of raw IPv6 and Ether QPs. Set to 0
* because neither type of raw QP is supported
*/
hca_attr->hca_max_ipv6_qp = 0;
hca_attr->hca_max_ether_qp = 0;
/* Determine max number of MCGs and max QP-per-MCG */
/* Determine max number partitions (i.e. PKeys) */
"soft_state_init_toomanypkey_fail");
return (DDI_FAILURE);
}
/* Determine number of ports */
"soft_state_init_toomanyports_fail");
return (DDI_FAILURE);
}
/* Copy NodeGUID and SystemImageGUID from softstate */
/*
* Determine local ACK delay. Use the value suggested by the Tavor
* hardware (from the QUERY_DEV_LIM command)
*/
/* Determine max SGID table and PKey table sizes */
/* Determine max number of PDs */
"soft_state_init_toomanypd_fail");
return (DDI_FAILURE);
}
/* Determine max number of Address Handles */
"soft_state_init_toomanyah_fail");
return (DDI_FAILURE);
}
/* No RDDs or EECs (since Reliable Datagram is not supported) */
hca_attr->hca_max_rdd = 0;
hca_attr->hca_max_eec = 0;
/* Initialize lock for reserved UAR page access */
/* Initialize the flash fields */
state->ts_fw_flashstarted = 0;
/* Initialize the lock for the info ioctl */
/* Initialize the AVL tree for QP number support */
/* Initialize the kstat info structure */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
"soft_state_init_kstatinit_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* tavor_soft_state_fini()
* Context: Called only from detach() path context
*/
static void
{
/* Teardown the kstat info */
/* Teardown the AVL tree for QP number support */
/* Free up info ioctl mutex */
/* Free up flash mutex */
/* Free up the UAR page access mutex */
/* Free up the hca_attr struct */
}
/*
* tavor_hca_config_setup()
* Context: Only called from attach() path context
*/
static void
{
/* Set "host endianness". Default is big endian */
#ifdef _LITTLE_ENDIAN
inithca->big_endian = 0;
#else
#endif
/* No Address Vector Protection, but Port Checking on by default */
/* Setup QPC table */
/* Setup EEC table (initialize to zero - RD unsupported) */
/* Setup CQC table */
/* Setup SRQC table */
/* Setup EQPC table */
/* Setup EEEC table (initialize to zero - RD unsupported) */
/* Setup EQC table */
/* Setup RDB table */
/* Setup Multicast */
/* Setup TPT */
/* Setup UAR */
}
/*
* tavor_hca_port_init()
* Context: Only called from attach() path context
*/
static int
{
int i, status;
/* Get number of HCA ports */
/* Allocate space for Tavor port init struct(s) */
sizeof (tavor_hw_initib_t), KM_SLEEP);
/* Post command to initialize Tavor HCA port */
for (i = 0; i < num_ports; i++) {
/*
* Determine whether we need to override the firmware's
* default SystemImageGUID setting.
*/
if (sysimgguid != 0) {
}
/*
* Determine whether we need to override the firmware's
* default NodeGUID setting.
*/
if (nodeguid != 0) {
}
/*
* Determine whether we need to override the firmware's
* default PortGUID setting.
*/
if (portguid != 0) {
}
/* Validate max MTU size */
"MTU size exceeds device maximum", tnf_uint,
goto init_ports_fail;
}
/* Validate the max port width */
"port width exceeds device maximum", tnf_uint,
goto init_ports_fail;
}
/* Validate max VL cap size */
"VLcap size exceeds device maximum", tnf_uint,
goto init_ports_fail;
}
/* Validate max GID table size */
"GID table size exceeds device maximum", tnf_uint,
goto init_ports_fail;
}
/* Validate max PKey table size */
"PKey table size exceeds device maximum", tnf_uint,
maxpkeytbl, maxval);
goto init_ports_fail;
}
/*
* Post the INIT_IB command to Tavor firmware. When this
* command completes, the corresponding Tavor port will be
* physically "Up" and initialized.
*/
if (status != TAVOR_CMD_SUCCESS) {
goto init_ports_fail;
}
}
/* Free up the memory for Tavor port init struct(s), return success */
return (DDI_SUCCESS);
/*
* Free up the memory for Tavor port init struct(s), shutdown any
* successfully initialized ports, and return failure
*/
(void) tavor_hca_ports_shutdown(state, i);
return (DDI_FAILURE);
}
/*
* tavor_hca_ports_shutdown()
*/
static int
{
int i, status;
/*
* Post commands to shutdown all init'd Tavor HCA ports. Note: if
* any of these commands fail for any reason, it would be entirely
* unexpected and probably indicative a serious problem (HW or SW).
* Although we do return void from this function, this type of failure
* should not go unreported. That is why we have the warning message
* and the detailed TNF information.
*/
for (i = 0; i < num_init; i++) {
if (status != TAVOR_CMD_SUCCESS) {
return (status);
}
}
return (TAVOR_CMD_SUCCESS);
}
/*
* tavor_internal_uarpgs_init
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* Save away reserved Tavor UAR page #0. This UAR page is not to
* be used by software.
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Save away Tavor UAR page #1 (for internal use). This UAR page is
* the privileged UAR page through which all kernel generated
* doorbells will be rung.
*/
&state->ts_uarpg1_rsrc);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Setup pointer to UAR page #1 doorbells */
return (DDI_SUCCESS);
}
/*
* tavor_internal_uarpgs_fini
*/
static void
{
/* Free up Tavor UAR page #1 (kernel driver doorbells) */
/* Free up Tavor UAR page #0 (reserved) */
}
/*
* tavor_special_qp_contexts_reserve()
* Context: Only called from attach() path context
*/
static int
{
int status;
/* Initialize the lock used for special QP rsrc management */
/*
* Reserve contexts for QP0. These QP contexts will be setup to
* act as aliases for the real QP0. Note: We are required to grab
* two QPs (one per port) even if we are operating in single-port
* mode.
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* Reserve contexts for QP1. These QP contexts will be setup to
* act as aliases for the real QP1. Note: We are required to grab
* two QPs (one per port) even if we are operating in single-port
* mode.
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* tavor_special_qp_contexts_unreserve()
*/
static void
{
/* Unreserve contexts for QP1 */
/* Unreserve contexts for QP0 */
/* Destroy the lock used for special QP rsrc management */
}
/*
* tavor_sw_reset()
* Context: Currently called only from attach() path context
*/
static int
{
int status, i;
/*
* If the configured software reset delay is set to zero, then we
* will not attempt a software reset of the Tavor device.
*/
if (reset_delay == 0) {
return (DDI_SUCCESS);
}
/*
* Get dip for HCA device _and_ parent device as well. Parent access
* is necessary here because software reset of the Tavor hardware
* will reinitialize both the config registers of the PCI bridge
* (parent, if it exists) and the IB HCA (self)
*/
/* Query the PCI capabilities of the HCA device */
/*
* Read all PCI config info (reg0...reg63). Note: According to the
* Tavor software reset application note, we should not read or
* restore the values in reg22 and reg23.
*/
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
}
}
if (TAVOR_PARENT_IS_BRIDGE(pdip)) {
/*
*/
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* Read all PCI config info (reg0...reg63). Note: According to
* the Tavor software reset application note, we should not
* read or restore the values in reg22 and reg23.
*/
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
state->ts_cfg_pdata[i] =
}
}
}
/*
* Perform the software reset (by writing 1 at offset 0xF0010)
*/
if (TAVOR_PARENT_IS_BRIDGE(pdip)) {
/*
* Bridge exists, so wait for the bridge to become ready.
*
* The above delay is necessary to avoid system panic from
* Master Abort. If the device is accessed before this delay,
* device will not respond to config cycles and they will be
* terminate with a Master Abort which will panic the system.
* Below is the loop we use to poll status from the device to
* determine if it is OK to proceed.
*/
i = 0;
}
/*
* Write all the PCI config registers back into each device
* (except for reg22 and reg23 - see above)
*/
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
state->ts_cfg_pdata[i]);
}
}
/*
* Tear down the config setup (for bridge device)
*/
/* No Bridge Device */
} else {
/*
* Bridge does not exist, so instead wait for the device itself
* to become ready.
*
* The above delay is necessary to avoid system panic from
* Master Abort. If the device is accessed before this delay,
* device will not respond to config cycles and they will be
* terminate with a Master Abort which will panic the system.
* Below is the loop we use to poll status from the device to
* determine if it is OK to proceed.
*/
i = 0;
}
}
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
}
}
return (DDI_SUCCESS);
}
/*
* tavor_mcg_init()
* Context: Only called from attach() path context
*/
static int
{
/*
* Allocate space for the MCG temporary copy buffer. This is
*/
/*
* Initialize the multicast group mutex. This ensures atomic
* access to add, modify, and remove entries in the multicast
* group hash lists.
*/
return (DDI_SUCCESS);
}
/*
* tavor_mcg_fini()
*/
static void
{
/* Free up the space used for the MCG temporary copy buffer */
/* Destroy the multicast group mutex */
}
/*
* tavor_fw_version_check()
* Context: Only called from attach() path context
*/
static int
{
/*
* Depending on which version of driver we have attached, the firmware
* version checks will be different. We set up the comparison values
* for both HCA Mode (Tavor hardware) or COMPAT Mode (Arbel hardware
* running in tavor mode).
*/
switch (state->ts_operational_mode) {
case TAVOR_HCA_MODE:
break;
case TAVOR_COMPAT_MODE:
break;
default:
return (DDI_FAILURE);
}
/*
* If FW revision major number is less than acceptable,
* return failure, else if greater return success. If
* the major numbers are equal than check the minor number
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Do the same check as above, except for minor revision numbers
* If the minor numbers are equal than check the subminor number
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Once again we do the same check as above, except for the subminor
* revision number. If the subminor numbers are equal here, then
* these are the same firmware version, return success
*/
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* tavor_device_info_report()
* Context: Only called from attach() path context
*/
static void
{
}
/*
* tavor_pci_capability_list()
* Context: Only called from attach() path context
*/
static void
{
/*
* Check for the "PCI Capabilities" bit in the "Status Register".
* Bit 4 in this register indicates the presence of a "PCI
* Capabilities" list.
*/
if ((data & 0x10) == 0) {
return;
}
/*
* Starting from offset 0x34 in PCI config space, find the
* head of "PCI capabilities" list, and walk the list. If
* capabilities of a known type are encountered (e.g.
* "PCI-X Capability"), then call the appropriate handler
* function.
*/
while (offset != 0x0) {
/*
* Check for known capability types. Tavor has the
* following:
* o VPD Capability (0x03)
* o PCI-X Capability (0x07)
* o MSI Capability (0x05)
* o MSIX Capability (0x11)
*/
switch (data) {
case 0x03:
break;
case 0x07:
break;
case 0x05:
break;
default:
break;
}
/* Get offset of next entry in list */
}
}
/*
* tavor_pci_read_vpd()
* Context: Only called from attach() path context
* utility routine for tavor_pci_capability_vpd()
*/
static int
{
/*
* In order to read a 32-bit value from VPD, we are to write down
* the address (offset in the VPD itself) to the address register.
* To signal the read, we also clear bit 31. We then poll on bit 31
* and when it is set, we can then read our 4 bytes from the data
* register.
*/
do {
drv_usecwait(1000);
return (DDI_SUCCESS);
}
} while (--retry);
return (DDI_FAILURE);
}
/*
* tavor_pci_capability_vpd()
* Context: Only called from attach() path context
*/
static void
{
int i, err = 0;
int vpd_str_id = 0;
int vpd_ro_desc;
int vpd_ro_pn_desc;
#ifndef _LITTLE_ENDIAN
#endif /* _LITTLE_ENDIAN */
union {
} vpd;
/*
* Read Vital Product Data (VPD) from PCI-X capability.
*/
for (i = 0; i < TAVOR_VPD_HDR_DWSIZE; i++) {
if (err != DDI_SUCCESS) {
goto out;
}
}
#ifndef _LITTLE_ENDIAN
/*
* Need to swap bytes for big endian.
*/
for (i = 0; i < TAVOR_VPD_HDR_DWSIZE; i++) {
}
#endif /* _LITTLE_ENDIAN */
/* Check for VPD String ID Tag */
/* get the product name */
goto out;
}
/* get the part number */
/*
* Verify read-only tag and Part Number keyword.
*/
goto out;
}
goto out;
}
} else {
/* Wrong VPD String ID Tag */
goto out;
}
return;
out:
state->ts_hca_pn_len = 0;
}
/*
* tavor_pci_capability_pcix()
* Context: Only called from attach() path context
*/
static void
{
/*
* Query the current values for the PCI-X Command Register and
* the PCI-X Status Register.
*/
/*
* Check for config property specifying "maximum outstanding
* split transactions". If the property is defined and valid
* (i.e. no larger than the so-called "designed maximum"),
* then use the specified value to update the PCI-X Command Register.
* Otherwise, extract the value from the Tavor config profile.
*/
if ((max_out_splt_trans != -1) &&
((max_out_splt_trans < 0) ||
"split-trans\" (%d) invalid or exceeds device maximum"
} else if (max_out_splt_trans == -1) {
}
/*
* The config profile setting for max_out_splt_trans is determined
* based on arch. Check tavor_cfg.c for more information. A value of
* '-1' in the patchable variable means "do not change". A value of
* '0' means 1 outstanding splt trans and other values as defined by
* PCI. So we do one more check here, that if 'max_out_splt_trans' is
* -1 (ie: < 0) we do not set the PCI command and leave it at the
* default.
*/
if (max_out_splt_trans >= 0) {
}
/*
* Check for config property specifying "maximum memory read
* byte count. If the property is defined and valid
* (i.e. no larger than the so-called "designed maximum"),
* then use the specified value to update the PCI-X Command Register.
* Otherwise, extract the value from the Tavor config profile.
*/
if ((max_mem_rd_byte_cnt != -1) &&
((max_mem_rd_byte_cnt < 0) ||
"count\" (%d) invalid or exceeds device maximum"
} else if (max_mem_rd_byte_cnt == -1) {
}
/*
* The config profile setting for max_mem_rd_byte_cnt is determined
* based on arch. Check tavor_cfg.c for more information. A value of
* '-1' in the patchable variable means "do not change". A value of
* '0' means minimum (512B) read, and other values as defined by
* PCI. So we do one more check here, that if 'max_mem_rd_byte_cnt' is
* -1 (ie: < 0) we do not set the PCI command and leave it at the
* default.
*/
if (max_mem_rd_byte_cnt >= 0) {
}
/*
* Update the PCI-X Command Register with the newly configured
* values.
*/
}
/*
* tavor_intr_or_msi_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/* Query for the list of supported interrupt event types */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* hasn't been overridden by a configuration variable), then
* the default behavior is to use a single MSI. Otherwise,
* fallback to using legacy interrupts. Also, if MSI allocatis chosen,
* but fails for whatever reasons, then fallback to using legacy
* interrupts.
*/
if (status == DDI_SUCCESS) {
return (DDI_SUCCESS);
}
}
/*
* MSI interrupt allocation failed, or was not available. Fallback to
* legacy interrupt support.
*/
if (status == DDI_SUCCESS) {
return (DDI_SUCCESS);
}
}
/*
* Neither MSI or legacy interrupts were successful. return failure.
*/
return (DDI_FAILURE);
}
/*
* tavor_add_intrs()
* Context: Only called from attach() patch context
*/
static int
{
int status;
/* Get number of interrupts/MSI supported */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/* Get number of available interrupts/MSI */
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/* Ensure that we have at least one (1) usable MSI or interrupt */
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/* Ensure that we have allocated at least one (1) MSI or interrupt */
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
/*
* will be used later when initializing certain mutexes.
*/
&state->ts_intrmsi_pri);
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
&state->ts_intrmsi_cap);
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* tavor_intr_or_msi_fini()
*/
static int
{
int status;
if (status != DDI_SUCCESS) {
TAVOR_TNF_ERROR, "");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* Disable Tavor interrupts */
static int
{
/*
* Check if MSI interrupts are used. If so, disable MSI interupts.
* If not, since Tavor doesn't support MSI-X interrupts, assuming the
* legacy interrupt is used instead, disable the legacy interrupt.
*/
&caps_ctrl) == DDI_SUCCESS)) {
return (DDI_FAILURE);
}
if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
return (DDI_SUCCESS);
if (msi_ctrl & PCI_MSI_PVM_MASK) {
/* Clear all inums in MSI */
offset, 0x0);
}
/* Disable MSI interrupts */
msi_ctrl);
} else {
/* Disable the legacy interrupts */
}
return (DDI_SUCCESS);
}
/* Tavor quiesce(9F) entry */
static int
{
/* start fastreboot */
/* If it's in maintenance mode, do nothing but return with SUCCESS */
return (DDI_SUCCESS);
}
/* Shutdown HCA ports */
return (DDI_FAILURE);
}
/* Close HCA */
return (DDI_FAILURE);
}
/* Shutdown FW */
return (DDI_FAILURE);
}
/* Disable interrupts */
return (DDI_FAILURE);
}
/* SW-reset */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}