hermon.c revision dd9e16da4243358c2e9251a4ca5d50f56e0adc68
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* hermon.c
*
* Implements all the routines necessary for the attach, setup,
* initialization (and subsequent possible teardown and detach) of the
* Hermon InfiniBand HCA driver.
*/
/* The following works around a problem in pre-2_7_000 firmware. */
#define HERMON_FW_WORKAROUND
int hermon_verbose = 0;
/* Hermon HCA State Pointer */
void *hermon_statep;
int debug_vpd = 0;
/* Disable the internal error-check polling thread */
int hermon_no_inter_err_chk = 0;
/*
* The Hermon "userland resource database" is common to instances of the
* Hermon HCA driver. This structure "hermon_userland_rsrc_db" contains all
* the necessary information to maintain it.
*/
int instance);
/* X86 fastreboot support */
static void hermon_set_msix_info(hermon_state_t *);
static int hermon_intr_disable(hermon_state_t *);
static int hermon_quiesce(dev_info_t *);
static struct cb_ops hermon_cb_ops = {
hermon_open, /* open */
hermon_close, /* close */
nodev, /* strategy (block) */
nodev, /* print (block) */
nodev, /* dump (block) */
nodev, /* read */
nodev, /* write */
hermon_ioctl, /* ioctl */
hermon_devmap, /* devmap */
NULL, /* mmap */
nodev, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* prop_op */
NULL, /* streams */
D_64BIT | /* D_HOTPLUG | */
D_DEVMAP, /* flags */
CB_REV /* rev */
};
/* Driver Operations */
static struct dev_ops hermon_ops = {
DEVO_REV, /* struct rev */
0, /* refcnt */
hermon_getinfo, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
hermon_attach, /* attach */
hermon_detach, /* detach */
nodev, /* reset */
&hermon_cb_ops, /* cb_ops */
NULL, /* bus_ops */
nodev, /* power */
hermon_quiesce, /* devo_quiesce */
};
/* Module Driver Info */
static struct modldrv hermon_modldrv = {
"ConnectX IB Driver",
};
/* Module Linkage */
static struct modlinkage hermon_modlinkage = {
};
/*
* This extern refers to the ibc_operations_t function vector that is defined
* in the hermon_ci.c file.
*/
extern ibc_operations_t hermon_ibc_ops;
/*
* _init()
*/
int
_init()
{
int status;
if (status != 0) {
return (status);
}
if (status != 0) {
return (status);
}
if (status != 0) {
return (status);
}
/* Initialize the Hermon "userland resources database" */
return (status);
}
/*
* _info()
*/
int
{
int status;
return (status);
}
/*
* _fini()
*/
int
_fini()
{
int status;
if (status != 0) {
return (status);
}
/* Destroy the Hermon "userland resources database" */
return (status);
}
/*
* hermon_getinfo()
*/
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
break;
}
return (DDI_FAILURE);
}
/*
* hermon_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 Hermon
* close() entry point may not be the same as the one who called
* open(). Specifically, this can happen if a child process calls
* the Hermon'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 hermon_umap_db_find_nolock() and
* hermon_umap_db_add_nolock() database access routines below (with
* an explicit mutex_enter of the database lock - "hdl_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
* hr_indx value. So we set rsrcp to NULL for maintenance
* mode, and use a rolling count for hr_indx. The field
* 'hs_open_hr_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 */
HERMON_NOSLEEP, &rsrcp);
if (status != DDI_SUCCESS) {
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 */
}
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 */
}
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);
}
/*
* hermon_close()
*/
/* ARGSUSED */
static int
{
int status, reset_status = 0;
return (ENXIO);
}
/*
* Search for "dev_t" in the "userland resources database". As
* explained above in hermon_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
* Hermon UAR page resource.
*
* Note: We use the hermon_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 hermon_rsrc_free(). In the case of maintenance mode,
* this call is not needed, as it was not allocated in hermon_open()
* above.
*/
if (status == DDI_SUCCESS) {
/*
* If the "hdb_priv" field is non-NULL, it indicates that
* some "on close" handling is still necessary. Call
* hermon_umap_db_handle_onclose_cb() to do the handling (i.e.
* to invoke all the registered callbacks). Then free up
* the resources associated with "hdb_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 hermon_rsrc_t pointer for the UAR page
* resource.
*/
&umapdb);
if (status == DDI_SUCCESS) {
/* If in "maintenance mode", don't free the rsrc */
}
}
}
return (reset_status);
}
/*
* hermon_attach()
* Context: Only called from attach() path context
*/
static int
{
int instance;
int status;
#ifdef __lock_lint
(void) hermon_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 */
/* Save away devinfo and instance before hermon_fm_init() */
/*
* Initialize Hermon driver and hardware.
*
* Note: If this initialization fails we may still wish to
* create a device node and remain operational so that Hermon
* If this is the case, then "hs_operational_mode" will be
* equal to HERMON_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) &&
goto fail_attach;
}
/*
* Change the Hermon FM mode
*/
/*
* Now we wait for 50ms to give an opportunity
* to Solaris FMA so that HW errors can be notified.
* Then check if there are HW errors or not. If
* a HW error is detected, the Hermon attachment
* must be failed.
*/
if (hermon_init_failure(state)) {
"attach Hermon due to a HW error");
"hermon_attach_failure");
goto fail_attach;
}
/*
* There seems no HW errors during the attachment,
* so let's change the Hermon FM state to the
* ereport only mode.
*/
/* unwind the resources */
"hermon_attach_failure");
goto fail_attach;
}
}
/* Create the minor node for device */
DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
"attach_create_mn_fail");
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->hs_ibtfinfo);
if (ibc_status != IBC_SUCCESS) {
"failed\n");
"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->hs_in_evcallb != 0) {
"quiesce Hermon IBTF callbacks");
}
"attach_agentinit_fail");
goto fail_attach;
}
}
/* Report attach in maintenance mode, if appropriate */
}
/* Report that driver was loaded */
/* Send device information to log file */
/* DEBUG PRINT */
return (DDI_SUCCESS);
case DDI_RESUME:
/* Add code here for DDI_RESUME XXX */
return (DDI_FAILURE);
default:
break;
}
}
return (DDI_FAILURE);
}
/*
* hermon_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) {
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 Hermon agents");
}
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->hs_in_evcallb != 0) {
"Hermon IBTF callbacks");
}
/* Complete the detach from the IBTF */
}
/* Remove the minor node for device */
/*
* Only call hermon_drv_fini() if we are in Hermon 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);
}
/*
* hermon_dma_attr_init()
* Context: Can be called from interrupt or base context.
*/
/* ARGSUSED */
void
{
dma_attr->dma_attr_addr_lo = 0;
dma_attr->dma_attr_flags = 0;
}
/*
* hermon_dma_alloc()
* Context: Can be called from base context.
*/
int
{
int status;
/* Allocate a DMA handle */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Allocate DMA memory */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Bind the memory to the handle */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Package the hermon_dma_info contents and return */
/* Pass the mapping information to the firmware */
if (status != DDI_SUCCESS) {
char *s;
switch (opcode) {
case MAP_ICM:
s = "MAP_ICM";
break;
case MAP_FA:
s = "MAP_FA";
break;
case MAP_ICM_AUX:
s = "MAP_ICM_AUX";
break;
default:
s = "UNKNOWN";
}
s, status);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_dma_free()
* Context: Can be called from base context.
*/
void
{
/* Unbind the handles and free the memory */
}
/* These macros are valid for use only in hermon_icm_alloc/hermon_icm_free. */
#define HERMON_ICM_ALLOC(rsrc) \
#define HERMON_ICM_FREE(rsrc) \
/*
* hermon_icm_alloc()
* Context: Can be called from base context.
*
* Only one thread can be here for a given hermon_rsrc_type_t "type".
*/
int
{
int status;
if (hermon_verbose) {
}
switch (type) {
case HERMON_QPC:
if (status != DDI_SUCCESS) {
return (status);
}
return (status);
}
return (status);
}
return (status);
}
break;
case HERMON_SRQC:
if (status != DDI_SUCCESS) {
return (status);
}
break;
case HERMON_CQC:
if (status != DDI_SUCCESS) {
return (status);
}
break;
case HERMON_EQC:
return (status);
}
break;
}
/* ensure existence of bitmap and dmainfo, sets "dma_info" */
/* Set up the DMA handle for allocation and mapping */
if (hermon_verbose) {
"rsrc (0x%x) index (%x, %x) "
}
/* Allocate and map memory for this span */
if (status != DDI_SUCCESS) {
"allocation failed, status 0x%x", status);
switch (type) {
case HERMON_QPC:
break;
case HERMON_SRQC:
break;
case HERMON_CQC:
break;
case HERMON_EQC:
break;
}
return (DDI_FAILURE);
}
if (hermon_verbose) {
"rsrc_type (0x%x) index (0x%x, 0x%x) alloc length (0x%x) "
}
/* Set the bit for this slot in the table bitmap */
return (DDI_SUCCESS);
}
/*
* hermon_icm_free()
* Context: Can be called from base context.
*
* ICM resources have been successfully returned from hermon_icm_alloc().
* Associated dma_info is no longer in use. Free the ICM backing memory.
*/
void
{
int status;
if (hermon_verbose) {
}
/* The following only happens if attach() is failing. */
return;
/* Unmap the ICM allocation, then free the backing DMA memory */
if (status != DDI_SUCCESS) {
}
/* Clear the bit in the ICM table bitmap */
switch (type) {
case HERMON_QPC:
break;
case HERMON_SRQC:
break;
case HERMON_CQC:
break;
case HERMON_EQC:
break;
}
}
/*
* hermon_drv_init()
* Context: Only called from attach() path context
*/
/* ARGSUSED */
static int
{
int status;
/*
* Check and set the operational mode of the device. If the driver is
* bound to the Hermon 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 {
return (DDI_FAILURE);
}
/*
* Initialize the Hermon hardware.
*
* Note: If this routine returns an error, it is often a reasonably
* good indication that something Hermon firmware-related has caused
* the failure or some HW related errors have caused the failure.
* (also there are few possibilities that SW (e.g. SW resource
* shortage) can cause the failure, but the majority case is due to
* either a firmware related error or a HW related one) In order to
* give the user an opportunity (if desired) to update or reflash
* the Hermon firmware image, we set "hs_operational_mode" flag
* (described above) to indicate that we wish to enter maintenance
* mode in case of the firmware-related issue.
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Now that the ISR has been setup, arm all the EQs for event
* generation.
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* test interrupts and event queues */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Initialize Hermon softstate */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_drv_fini()
*/
static void
{
/* Cleanup Hermon softstate */
/* Cleanup Hermon resources and shutdown hardware */
}
/*
* hermon_drv_fini2()
*/
static void
{
if (state->hs_fm_poll_thread) {
}
/* HERMON_DRV_CLEANUP_LEVEL1 */
if (state->hs_fm_cmdhdl) {
}
if (state->hs_reg_cmdhdl) {
}
/* HERMON_DRV_CLEANUP_LEVEL0 */
if (state->hs_msix_tbl_entries) {
}
if (state->hs_msix_pba_entries) {
}
if (state->hs_fm_msix_tblhdl) {
}
if (state->hs_reg_msix_tblhdl) {
}
if (state->hs_fm_msix_pbahdl) {
}
if (state->hs_reg_msix_pbahdl) {
}
if (state->hs_fm_pcihdl) {
}
if (state->hs_reg_pcihdl) {
}
}
/*
* hermon_isr_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
int intr;
/*
* Add a handler for the interrupt or MSI
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Enable the software interrupt. Note: depending on the value
* returned in the capability flag, we have to call either
* ddi_intr_block_enable() or ddi_intr_enable().
*/
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
} else {
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
}
/*
* Now that the ISR has been enabled, defer arm_all EQs for event
* generation until later, in case MSIX is enabled
*/
return (DDI_SUCCESS);
}
/*
* hermon_isr_fini()
*/
static void
{
int intr;
/* Disable the software interrupt */
(void) ddi_intr_block_disable(
} else {
}
/*
* Remove the software handler for the interrupt or MSI
*/
}
}
/*
* Sum of ICM configured values:
* cMPT, dMPT, MTT, QPC, SRQC, RDB, CQC, ALTC, AUXC, EQC, MCG
*
*/
static uint64_t
{
#ifndef HERMON_FW_WORKAROUND
#endif
/* number of respective entries */
#ifndef HERMON_FW_WORKAROUND
#endif
size =
#ifdef HERMON_FW_WORKAROUND
0x80000000ull +
#else
#endif
return (size);
}
/*
* hermon_hw_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/* This is where driver initialization begins */
/* Setup device access attributes */
/* Setup fma-protected access attributes */
/* set acc err protection type */
if (status != DDI_SUCCESS) {
"hw_init_PCI_config_space_regmap_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Map in Hermon registers (CMD, UAR, MSIX) and setup offsets */
&state->hs_fm_cmdhdl);
if (status != DDI_SUCCESS) {
"hw_init_CMD_BAR_regmap_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/*
* We defer UAR-BAR mapping until later. Need to know if
* blueflame mapping is to be done, and don't know that until after
* we get the dev_caps, so do it right after that
*/
/*
* There is a third BAR defined for Hermon - it is for MSIX
*
* [es] Temporary mapping maybe
*/
#ifdef HERMON_SUPPORTS_MSIX_BAR
&state->hs_reg_msihdl);
if (status != DDI_SUCCESS) {
"hw_init_MSIX_BAR_regmap_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
#endif
/*
* Save interesting registers away. The offsets of the first two
* here (HCR and sw_reset) are detailed in the PRM, the others are
* derived from values in the QUERY_FW output, so we'll save them
* off later.
*/
/* Host Command Register (HCR) */
/* Software Reset register (sw_reset) and semaphore */
/* Retrieve PCI device, vendor and rev IDs */
/* make sure init'd before we start filling things in */
/* Initialize the Phase1 configuration profile */
if (status != DDI_SUCCESS) {
"hw_init_cfginit1_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Do a software reset of the adapter to ensure proper state */
if (status != HERMON_CMD_SUCCESS) {
"hw_init_sw_reset_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Initialize mailboxes */
if (status != DDI_SUCCESS) {
"hw_init_rsrcinit1_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Post QUERY_FW */
sizeof (hermon_hw_queryfw_t), HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
"hw_init_query_fw_cmd_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
"expected: %04d.%04d.%04d, "
"actual: %04d.%04d.%04d\n",
} else {
"%04d.%04d.%04d\n",
}
"hw_init_checkfwver_fail");
/* This case is the degraded one */
return (HERMON_CMD_BAD_NVMEM);
}
/*
* Save off the rest of the interesting registers that we'll be using.
* Setup the offsets for the other registers.
*/
/*
* Hermon does the intr_offset from the BAR - technically should get the
* BAR info from the response, but PRM says it's from BAR0-1, which is
* for us the CMD BAR
*/
/* Save Clear Interrupt address */
/*
* Set the error buffer also into the structure - used in hermon_event.c
* to check for internal error on the HCA, not reported in eqe or
* (necessarily) by interrupt
*/
/*
* Invoke a polling thread to check the error buffer periodically.
*/
if (!hermon_no_inter_err_chk) {
}
/*
* Allocate, map, and run the HCA Firmware.
*/
/* Allocate memory for the firmware to load into and map it */
/* get next higher power of 2 */
if (status != DDI_SUCCESS) {
"hw_init_dma_alloc_fw_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Invoke the RUN_FW cmd to run the firmware */
if (status != DDI_SUCCESS) {
if (status == HERMON_CMD_BAD_NVMEM) {
}
/*
* If the status is HERMON_CMD_BAD_NVMEM, it's likely the
* firmware is corrupted, so the mode falls into the
* maintenance mode.
*/
}
/*
* QUERY DEVICE LIMITS/CAPABILITIES
* NOTE - in Hermon, the command is changed to QUERY_DEV_CAP,
* but for familiarity we have kept the structure name the
*/
if (status != HERMON_CMD_SUCCESS) {
status);
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* now we have enough info to map in the UAR BAR */
/*
* First, we figure out how to map the BAR for UAR - use only half if
* BlueFlame is enabled - in that case the mapped length is 1/2 the
* log_max_uar_sz (max__uar - 1) * 1MB ( +20).
*/
} else {
offset = 0; /* a zero length means map the whole thing */
}
&state->hs_fm_uarhdl);
if (status != DDI_SUCCESS) {
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* and if BlueFlame is enabled, map the other half there */
if (status != DDI_SUCCESS) {
"BlueFlame BAR mapping");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* This will be used in hw_fini if we fail to init. */
}
/* Hermon has a couple of things needed for phase 2 in query port */
if (status != HERMON_CMD_SUCCESS) {
status);
"hw_init_queryport_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Initialize the Phase2 Hermon configuration profile */
if (status != DDI_SUCCESS) {
"hw_init_cfginit2_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Determine and set the ICM size */
if (status != DDI_SUCCESS) {
status);
"hw_init_seticmsz_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* alloc icm aux physical memory and map it */
if (status != DDI_SUCCESS) {
"hw_init_dma_alloc_icm_aux_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Allocate an array of structures to house the ICM tables */
sizeof (hermon_icm_table_t), KM_SLEEP);
/* Set up the ICM address space and the INIT_HCA command input */
if (status != HERMON_CMD_SUCCESS) {
"hw_init_icm_config_setup_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Initialize the adapter with the INIT_HCA cmd */
if (status != HERMON_CMD_SUCCESS) {
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Enter the second phase of init for Hermon configuration/resources */
if (status != DDI_SUCCESS) {
"hw_init_rsrcinit2_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Query the adapter via QUERY_ADAPTER */
if (status != HERMON_CMD_SUCCESS) {
status);
"hw_init_query_adapter_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Allocate protection domain (PD) for Hermon internal use */
if (status != DDI_SUCCESS) {
"hw_init_internal_pd_alloc_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Setup UAR page for kernel use */
if (status != DDI_SUCCESS) {
"hw_init_internal_uarpg_alloc_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
"hw_init_intr_or_msi_init_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
"hw_init_isrinit_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Setup the event queues */
if (status != DDI_SUCCESS) {
"hw_init_eqinitall_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Reserve contexts for QP0 and QP1 */
if (status != DDI_SUCCESS) {
"hw_init_rsrv_sqp_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Initialize for multicast group handling */
if (status != DDI_SUCCESS) {
"hw_init_mcg_init_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Initialize the Hermon IB port(s) */
if (status != DDI_SUCCESS) {
"hw_init_hca_port_init_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
/* Determine NodeGUID and SystemImageGUID */
&nodeinfo);
if (status != HERMON_CMD_SUCCESS) {
"hw_init_getnodeinfo_cmd_fail");
/* This case is not the degraded one */
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 != HERMON_CMD_SUCCESS) {
"hw_init_getnodedesc_cmd_fail");
/* This case is not the degraded one */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_hw_fini()
*/
static void
{
int i, status;
/*
* JBDB - We might not want to run these returns in all cases of
* Bad News. We should still attempt to free all of the DMA memory
* resources... This needs to be worked last, after all allocations
* are implemented. For now, and possibly for later, this works.
*/
switch (cleanup) {
/*
* If we add more driver initialization steps that should be cleaned
* up here, we need to ensure that HERMON_DRV_CLEANUP_ALL is still the
* first entry (i.e. corresponds to the last init step).
*/
case HERMON_DRV_CLEANUP_ALL:
/* Shutdown the Hermon 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) {
return;
}
/* FALLTHROUGH */
/* Teardown Hermon interrupts */
/* FALLTHROUGH */
if (status != DDI_SUCCESS) {
return;
}
/* FALLTHROUGH */
/* Free the resources for the Hermon internal UAR pages */
/* FALLTHROUGH */
/*
* Free the PD that was used internally by Hermon 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) {
return;
}
/* FALLTHROUGH */
/* Cleanup all the phase2 resources first */
/* FALLTHROUGH */
/* LEVEL11 is after INIT_HCA */
/* FALLTHROUGH */
/*
* Unmap the ICM memory area with UNMAP_ICM command.
*/
if (status != DDI_SUCCESS) {
"hermon_hw_fini: failed to unmap ICM\n");
}
/* Free the initial ICM DMA handles */
/* Free the ICM table structures */
/* Free the ICM table handles */
sizeof (hermon_icm_table_t));
/* FALLTHROUGH */
/*
* Unmap the ICM Aux memory area with UNMAP_ICM_AUX command.
*/
if (status != HERMON_CMD_SUCCESS) {
"hermon_hw_fini: failed to unmap ICMA\n");
}
/* FALLTHROUGH */
/*
* Deallocate ICM Aux DMA memory.
*/
/* FALLTHROUGH */
if (state->hs_fm_uarhdl) {
}
if (state->hs_reg_uarhdl) {
}
}
for (i = 0; i < HERMON_MAX_PORTS; i++) {
sizeof (ib_pkey_t));
}
sizeof (ib_guid_t));
}
}
/* FALLTHROUGH */
/*
* Unmap the firmware memory area with UNMAP_FA command.
*/
if (status != HERMON_CMD_SUCCESS) {
"hermon_hw_fini: failed to unmap FW\n");
}
/*
* Deallocate firmware DMA memory.
*/
/* FALLTHROUGH */
/* stop the poll thread */
if (state->hs_fm_poll_thread) {
}
/* FALLTHROUGH */
/* Then cleanup the phase1 resources */
/* FALLTHROUGH */
/* Teardown any resources allocated for the config profile */
/* FALLTHROUGH */
#ifdef HERMON_SUPPORTS_MSIX_BAR
/*
* unmap 3rd BAR, MSIX BAR
*/
if (state->hs_reg_msihdl) {
}
/* FALLTHROUGH */
#endif
/*
* LEVEL1 and LEVEL0 resources are freed in
* hermon_drv_fini2().
*/
break;
default:
return;
}
}
/*
* hermon_soft_state_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
int max_send_wqe_bytes;
int max_recv_wqe_bytes;
/*
* 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 Hermon
* hardware abilities
*/
/* CQ interrupt moderation maximums - each limited to 16 bits */
/* CQ relocation to other EQs - change when multiple MSI-Xs are used */
/*
* 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, IBT_HCA_CURRENT_QP_STATE, IBT_HCA_PORT_UP,
* IBT_HCA_SRQ, IBT_HCA_RESIZE_SRQ and IBT_HCA_FMR are always
* supported
* All other features are conditionally supported, depending on the
* status return by the Hermon HCA in QUERY_DEV_LIM.
*/
}
}
}
}
}
}
}
}
}
}
}
}
/* More work needed in hermon_post_send for larger values */
}
/* We choose not to support "inline" unless it improves performance */
hca_attr->hca_max_inline_size = 0;
/*
* Set hca_attr's IDs
*/
/*
* 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);
}
/* we need to reduce this by the max space needed for headroom */
/*
* Determine max scatter-gather size in WQEs. The HCA has split
* the max sgl into rec'v Q and send Q values. Use the least.
*
* This is mainly useful for legacy clients. Smart clients
* such as IPoIB will use the IBT_HCA_WQE_SIZE_INFO sgl info.
*/
} else {
}
"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 Hermon
* hardware (from the QUERY_DEV_CAP 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 (NOT IN ARBEL or HERMON) */
hca_attr->hca_max_ah = 0;
/* 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->hs_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) {
"soft_state_init_kstatinit_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_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 */
}
/*
* hermon_icm_config_setup()
* Context: Only called from attach() path context
*/
static int
{
int status, i, j;
/* Bring in local devlims, cfg_profile and hs_icm table list */
/*
* Assign each ICM table's entry size from data in the devlims,
* except for RDB and MCG sizes, which are not returned in devlims
* but do have a fixed size, and the UAR context entry size, which
* we determine. For this, we use the "cp_num_pgs_per_uce" value
* from our hs_cfg_profile.
*/
/* Assign each ICM table's log2 number of entries */
/* Initialize the ICM tables */
/*
* ICM tables must be aligned on their size in the ICM address
* space. So, here we order the tables from largest total table
* size to the smallest. All tables are a power of 2 in size, so
* this will ensure that all tables are aligned on their own size
* without wasting space in the ICM.
*
* In order to easily set the ICM addresses without needing to
* worry about the ordering of our table indices as relates to
* the hermon_rsrc_type_t enum, we will use a list of pointers
* representing the tables for the sort, then assign ICM addresses
* below using it.
*/
for (i = 0; i < HERMON_NUM_ICM_RESOURCES; i++) {
}
for (i = HERMON_NUM_ICM_RESOURCES; i > 0; i--) {
switch (i) {
case HERMON_CMPT_QPC:
case HERMON_CMPT_SRQC:
case HERMON_CMPT_CQC:
case HERMON_CMPT_EQC:
continue;
}
for (j = 1; j < i; j++) {
}
}
}
/* Initialize the ICM address and ICM size */
/*
* Set the ICM base address of each table, using our sorted
* list of pointers from above.
*/
for (i = 0; i < HERMON_NUM_ICM_RESOURCES; i++) {
switch (j) {
case HERMON_CMPT_QPC:
case HERMON_CMPT_SRQC:
case HERMON_CMPT_CQC:
case HERMON_CMPT_EQC:
continue;
}
if (icm[j].table_size) {
/*
* Set the ICM base address in the table, save the
* ICM offset in the rsrc pool and increment the
* total ICM allocation.
*/
if (hermon_verbose) {
icm[j].table_size);
}
}
/* Verify that we don't exceed maximum ICM size */
/* free the ICM table memory resources */
"configuration: max (0x%lx) requested (0x%lx)\n",
"icm_config_toobig_fail");
return (DDI_FAILURE);
}
/* assign address to the 4 pieces of the CMPT */
if (j == HERMON_CMPT) {
}
/* Increment the ICM address for the next table */
}
/* Populate the structure for the INIT_HCA command */
/*
* Prior to invoking INIT_HCA, we must have ICM memory in place
* for the reserved objects in each table. We will allocate and map
* this initial ICM memory here. Note that given the assignment
* of span_size above, tables that are smaller or equal in total
* size to the default span_size will be mapped in full.
*/
if (status != DDI_SUCCESS) {
/* free the ICM table memory resources */
"icm_config_dma_init_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_inithca_set()
* Context: Only called from attach() path context
*/
static void
{
int i;
/* Populate the INIT_HCA structure */
/* set version */
/* set cacheline - log2 in 16-byte chunks */
/* we need to update the inithca info with thie UAR info too */
/* Set endianess */
#ifdef _LITTLE_ENDIAN
inithca->big_endian = 0;
#else
#endif
/* Port Checking is on by default */
/* Enable IPoIB checksum */
/* Set each ICM table's attributes */
for (i = 0; i < HERMON_NUM_ICM_RESOURCES; i++) {
case HERMON_CMPT:
break;
case HERMON_MTT:
break;
case HERMON_DMPT:
break;
case HERMON_QPC:
break;
case HERMON_CQC:
break;
case HERMON_SRQC:
break;
case HERMON_EQC:
break;
case HERMON_RDB:
icm[i].log_num_entries;
break;
case HERMON_MCG:
break;
case HERMON_ALTC:
break;
case HERMON_AUXC:
break;
default:
break;
}
}
}
/*
* hermon_icm_tables_init()
* Context: Only called from attach() path context
*
* Dynamic ICM breaks the various ICM tables into "span_size" chunks
* to enable allocation of backing memory on demand. Arbel used a
* fixed size ARBEL_ICM_SPAN_SIZE (initially was 512KB) as the
* span_size for all ICM chunks. Hermon has other considerations,
* so the span_size used differs from Arbel.
*
* The basic considerations for why Hermon differs are:
*
* 1) ICM memory is in units of HERMON pages.
*
* 2) The AUXC table is approximately 1 byte per QP.
*
* 3) ICM memory for AUXC, ALTC, and RDB is allocated when
* the ICM memory for the corresponding QPC is allocated.
*
* 4) ICM memory for the CMPT corresponding to the various primary
* resources (QPC, SRQC, CQC, and EQC) is allocated when the ICM
* memory for the primary resource is allocated.
*
* One HERMON page (4KB) would typically map 4K QPs worth of AUXC.
* So, the minimum chunk for the various QPC related ICM memory should
* all be allocated to support the 4K QPs. Currently, this means the
* amount of memory for the various QP chunks is:
*
* QPC 256*4K bytes
* RDB 128*4K bytes
* CMPT 64*4K bytes
* ALTC 64*4K bytes
* AUXC 1*4K bytes
*
* The span_size chosen for the QP resource is 4KB of AUXC entries,
* or 1 HERMON_PAGESIZE worth, which is the minimum ICM mapping size.
*
* Other ICM resources can have their span_size be more arbitrary.
* This is 4K (HERMON_ICM_SPAN), except for MTTs because they are tiny.
*/
/* macro to make the code below cleaner */
if (hermon_verbose) { \
"rsrc (0x%x) size (0x%lx) span (0x%x) " \
"span_shift (0x%x) split_shift (0x%x)", \
"span_mask (0x%x) rsrc_mask (0x%x)", \
}
static void
{
int i, k;
for (i = 0; i < HERMON_NUM_ICM_RESOURCES; i++) {
icm[i].log_object_size;
/* deal with "dependent" resource types */
switch (i) {
case HERMON_AUXC:
#ifdef HERMON_FW_WORKAROUND
/* FALLTHROUGH */
#endif
case HERMON_CMPT_QPC:
case HERMON_RDB:
case HERMON_ALTC:
init_dependent(HERMON_QPC, i);
continue;
case HERMON_CMPT_SRQC:
continue;
case HERMON_CMPT_CQC:
init_dependent(HERMON_CQC, i);
continue;
case HERMON_CMPT_EQC:
init_dependent(HERMON_EQC, i);
continue;
}
if (i == HERMON_MTT) /* Alloc enough MTTs to map 256MB */
per_split = 1;
} else {
if (per_split == 0) {
per_split = 1;
}
}
if (hermon_verbose)
/*
* Ensure a minimum table size of an ICM page, and a
* maximum span size of the ICM table size. This ensures
* that we don't have less than an ICM page to map, which is
* impossible, and that we will map an entire table at
* once if it's total size is less than the span size.
*/
icm[i].span_shift = 0;
icm[i].span_shift++;
icm[i].split_shift++;
/* Initialize the table lock */
if (hermon_verbose) {
"span (0x%x) num_spans (0x%x)",
"span_shift (0x%x) split_shift (0x%x)",
"span_mask (0x%x) rsrc_mask (0x%x)",
}
}
}
/*
* hermon_icm_tables_fini()
* Context: Only called from attach() path context
*
* Clean up all icm_tables. Free the bitmap and dma_info arrays.
*/
static void
{
int nspans;
int i, j;
for (i = 0; i < HERMON_NUM_ICM_RESOURCES; i++) {
for (j = 0; j < HERMON_ICM_SPLIT; j++) {
/* Free the ICM DMA slots */
nspans * sizeof (hermon_dma_info_t));
if (icm[i].icm_bitmap[j])
/* Free the table bitmap */
}
/* Destroy the table lock */
}
}
/*
* hermon_icm_dma_init()
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* This routine will allocate initial ICM DMA resources for ICM
* tables that have reserved ICM objects. This is the only routine
* where we should have to allocate ICM outside of hermon_rsrc_alloc().
* We need to allocate ICM here explicitly, rather than in
* hermon_rsrc_alloc(), because we've not yet completed the resource
* pool initialization. When the resource pools are initialized
* (in hermon_rsrc_init_phase2(), see hermon_rsrc.c for more
* information), resource preallocations will be invoked to match
* the ICM allocations seen here. We will then be able to use the
* normal allocation path. Note we don't need to set a refcnt on
* these initial allocations because that will be done in the calls
* to hermon_rsrc_alloc() from hermon_hw_entries_init() for the
* "prealloc" objects (see hermon_rsrc.c for more information).
*/
/* ICM for these is allocated within hermon_icm_alloc() */
switch (type) {
case HERMON_CMPT:
case HERMON_CMPT_QPC:
case HERMON_CMPT_SRQC:
case HERMON_CMPT_CQC:
case HERMON_CMPT_EQC:
case HERMON_AUXC:
case HERMON_ALTC:
case HERMON_RDB:
continue;
}
if (status != DDI_SUCCESS) {
while (type--) {
}
return (DDI_FAILURE);
}
if (hermon_verbose) {
"table (0x%x) index (0x%x) allocated", type, 0);
}
}
return (DDI_SUCCESS);
}
/*
* hermon_icm_dma_fini()
* Context: Only called from attach() path context
*
* ICM has been completely unmapped. We just free the memory here.
*/
static void
{
continue;
}
}
}
}
/*
* hermon_hca_port_init()
* Context: Only called from attach() path context
*/
static int
{
int i = 0, status;
/* Get number of HCA ports */
/* Allocate space for Hermon set port struct(s) */
sizeof (hermon_hw_set_port_t), KM_SLEEP);
/* Post commands to initialize each Hermon HCA port */
/*
* In Hermon, the process is different than in previous HCAs.
* Here, you have to:
* QUERY_PORT - to get basic information from the HCA
* set the fields accordingly
* INIT_PORT - to bring the port up
*
* Needs to be done for each port in turn
*/
for (i = 0; i < num_ports; i++) {
sizeof (hermon_hw_query_port_t), HERMON_CMD_NOSLEEP_SPIN);
if (status != HERMON_CMD_SUCCESS) {
goto init_ports_fail;
}
/*
* 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 */
goto init_ports_fail;
}
/* Validate the max port width */
goto init_ports_fail;
}
/* Validate max VL cap size */
goto init_ports_fail;
}
/* Validate max GID table size */
goto init_ports_fail;
}
/* Validate max PKey table size */
goto init_ports_fail;
}
/*
* Post the SET_PORT cmd to Hermon firmware. This sets
* the parameters of the port.
*/
if (status != HERMON_CMD_SUCCESS) {
goto init_ports_fail;
}
/* issue another SET_PORT cmd - performance fix/workaround */
/* XXX - need to discuss with Mellanox */
if (status != HERMON_CMD_SUCCESS) {
goto init_ports_fail;
}
}
/*
* Finally, do the INIT_PORT for each port in turn
* When this command completes, the corresponding Hermon port
* will be physically "Up" and initialized.
*/
for (i = 0; i < num_ports; i++) {
if (status != HERMON_CMD_SUCCESS) {
goto init_ports_fail;
}
}
/* Free up the memory for Hermon port init struct(s), return success */
return (DDI_SUCCESS);
/*
* Free up the memory for Hermon port init struct(s), shutdown any
* successfully initialized ports, and return failure
*/
(void) hermon_hca_ports_shutdown(state, i);
return (DDI_FAILURE);
}
/*
* hermon_hca_ports_shutdown()
*/
static int
{
int i, status;
/*
* Post commands to shutdown all init'd Hermon 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.
*/
for (i = 0; i < num_init; i++) {
if (status != HERMON_CMD_SUCCESS) {
return (status);
}
}
return (HERMON_CMD_SUCCESS);
}
/*
* hermon_internal_uarpg_init
* Context: Only called from attach() path context
*/
static int
{
int status;
/*
* Allocate the UAR page for kernel use. This UAR page is
* the privileged UAR page through which all kernel generated
* doorbells will be rung. There are a number of UAR pages
* reserved by hardware at the front of the UAR BAR, indicated
* by DEVCAP.num_rsvd_uar, which we have already allocated. So,
* the kernel page, or UAR page index num_rsvd_uar, will be
* allocated here for kernel use.
*/
&state->hs_uarkpg_rsrc);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Setup pointer to kernel UAR page */
/* need to set up DBr tracking as well */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_internal_uarpg_fini
*/
static void
{
/* Free up Hermon UAR page #1 (kernel driver doorbells) */
}
/*
* hermon_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.
*/
HERMON_SLEEP, &qp0_rsrc);
if (status != DDI_SUCCESS) {
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.
*/
HERMON_SLEEP, &qp1_rsrc);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
HERMON_SLEEP, &qp_resvd);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_special_qp_contexts_unreserve()
*/
static void
{
/* Unreserve contexts for spec_qp_unused */
/* Unreserve contexts for QP1 */
/* Unreserve contexts for QP0 */
/* Destroy the lock used for special QP rsrc management */
}
/*
* hermon_sw_reset()
* Context: Currently called only from attach() path context
*/
static int
{
int status, i;
int loopcnt;
/* initialize the FMA retry loop */
/*
* If the configured software reset delay is set to zero, then we
* will not attempt a software reset of the Hermon device.
*/
if (reset_delay == 0) {
return (DDI_SUCCESS);
}
/* the FMA retry loop starts. */
fm_test);
fm_test2);
/* Query the PCI capabilities of the HCA device */
/* but don't process the VPD until after reset */
if (status != DDI_SUCCESS) {
status);
return (DDI_FAILURE);
}
/*
* Read all PCI config info (reg0...reg63). Note: According to the
* Hermon software reset application note, we should not read or
* restore the values in reg22 and reg23.
* NOTE: For Hermon (and Arbel too) it says to restore the command
* register LAST, and technically, you need to restore the
* PCIE Capability "device control" and "link control" (word-sized,
* at offsets 0x08 and 0x10 from the capbility ID respectively).
* We hold off restoring the command register - offset 0x4 - till last
*/
/* 1st, wait for the semaphore assure accessibility - per PRM */
status = -1;
if (sem == 0) {
status = 0;
break;
}
drv_usecwait(1);
}
/* Check if timeout happens */
if (status == -1) {
/*
* Remove this acc handle from Hermon, then log
* the error.
*/
"failed to get the semaphore(0x%p)\n",
return (DDI_FAILURE);
}
for (i = 0; i < HERMON_SW_RESET_NUMREGS; i++) {
if ((i != HERMON_SW_RESET_REG22_RSVD) &&
(i != HERMON_SW_RESET_REG23_RSVD)) {
}
}
/*
* Perform the software reset (by writing 1 at offset 0xF0010)
*/
/*
* This delay is required so as not to cause a panic here. If the
* device is accessed too soon after reset it will not respond to
* config cycles, causing a Master Abort and panic.
*/
/*
* Poll waiting for the device to finish resetting.
*/
if (--loopcnt == 0)
break; /* just in case, break and go on */
}
if (loopcnt == 0)
pci_config_get32(hdl, 0));
/*
* Restore the config info
*/
for (i = 0; i < HERMON_SW_RESET_NUMREGS; i++) {
if ((i != HERMON_SW_RESET_REG22_RSVD) &&
(i != HERMON_SW_RESET_REG23_RSVD)) {
}
}
/*
* PCI Express Capability - we saved during capability list, and
* we'll restore them here.
*/
/* the FMA retry loop ends. */
fm_test2);
fm_test);
return (DDI_SUCCESS);
/* fall through */
return (DDI_FAILURE);
}
/*
* hermon_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);
}
/*
* hermon_mcg_fini()
*/
static void
{
/* Free up the space used for the MCG temporary copy buffer */
/* Destroy the multicast group mutex */
}
/*
* hermon_fw_version_check()
* Context: Only called from attach() path context
*/
static int
{
#ifdef FMA_TEST
if (hermon_test_num == -1) {
return (DDI_FAILURE);
}
#endif
/*
* Depending on which version of driver we have attached, and which
* HCA we've attached, the firmware version checks will be different.
* We set up the comparison values for both Arbel and Sinai HCAs.
*/
switch (state->hs_operational_mode) {
case HERMON_HCA_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);
}
/*
* hermon_device_info_report()
* Context: Only called from attach() path context
*/
static void
{
}
/*
* hermon_pci_capability_list()
* Context: Only called from attach() path context
*/
static int
{
/*
* Check for the "PCI Capabilities" bit in the "Status Register".
* Bit 4 in this register indicates the presence of a "PCI
* Capabilities" list.
*
* PCI-Express requires this bit to be set to 1.
*/
if ((data & 0x10) == 0) {
return (DDI_FAILURE);
}
/*
* 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. Hermon has the
* following:
* o Power Mgmt (0x02)
* o VPD Capability (0x03)
* o PCI-E Capability (0x10)
* o MSIX Capability (0x11)
*/
switch (data) {
case 0x01:
/* power mgmt handling */
break;
case 0x03:
/*
* Reading the PCIe VPD is inconsistent - that is, sometimes causes
* problems on (mostly) X64, though we've also seen problems w/ Sparc
* and Tavor --- so, for now until it's root caused, don't try and
* read it
*/
#ifdef HERMON_VPD_WORKS
#else
delay(100);
#endif
break;
case 0x10:
/*
* PCI Express Capability - save offset & contents
* for later in reset
*/
break;
case 0x11:
/*
* MSIX support - nothing to do, taken care of in the
*/
break;
default:
/* just go on to the next */
break;
}
/* Get offset of next entry in list */
}
return (DDI_SUCCESS);
}
/*
* hermon_pci_read_vpd()
* Context: Only called from attach() path context
* utility routine for hermon_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);
/* read of flag failed write one message but count the failures */
if (debug_vpd == 0)
"!Failed to see flag bit after VPD addr write\n");
debug_vpd++;
return (DDI_FAILURE);
}
/*
* hermon_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;
#ifdef _BIG_ENDIAN
#endif /* _BIG_ENDIAN */
union {
} vpd;
/*
* Read in the Vital Product Data (VPD) to the extend needed
* by the fwflash utility
*/
for (i = 0; i < HERMON_VPD_HDR_DWSIZE; i++) {
if (err != DDI_SUCCESS) {
goto out;
}
}
#ifdef _BIG_ENDIAN
/* Need to swap bytes for big endian. */
for (i = 0; i < HERMON_VPD_HDR_DWSIZE; i++) {
}
#endif /* _BIG_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->hs_hca_pn_len = 0;
}
/*
* hermon_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) {
return (DDI_FAILURE);
}
/*
* If Hermon supports MSI-X in this system (and, if it
* hasn't been overridden by a configuration variable), then
* the default behavior is to use a single MSI-X. Otherwise,
* fallback to using legacy interrupts. Also, if MSI-X is chosen,
* but fails for whatever reasons, then next try MSI
*/
if (status == DDI_SUCCESS) {
return (DDI_SUCCESS);
}
}
/*
* If Hermon supports MSI in this system (and, if it
* hasn't been overridden by a configuration variable), then
* the default behavior is to use a single MSIX. Otherwise,
* fallback to using legacy interrupts. Also, if MSI is 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);
}
}
/*
* None of MSI, MSI-X, nor legacy interrupts were successful.
* Return failure.
*/
return (DDI_FAILURE);
}
/*
* hermon_add_intrs()
* Context: Only called from attach() patch context
*/
static int
{
int status;
/* Get number of interrupts/MSI supported */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Get number of available interrupts/MSI */
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Ensure that we have at least one (1) usable MSI or interrupt */
return (DDI_FAILURE);
}
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* Ensure that we have allocated at least one (1) MSI or interrupt */
return (DDI_FAILURE);
}
/*
* will be used later when initializing certain mutexes.
*/
&state->hs_intrmsi_pri);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
&state->hs_intrmsi_cap);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* hermon_intr_or_msi_fini()
*/
static int
{
int status;
int intr;
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
void
{
int t_size; /* size in entries - each is 4 dwords */
/* come in with offset pointing at the capability structure */
offset += 4;
offset += 4;
#ifdef HERMON_SUPPORTS_MSIX_BAR
for (i = 0; i < 2; i++) {
+ offset));
i, j, msix_data);
}
}
#endif
}
/*
* X86 fastreboot support functions.
* to disable MSI-X interrupts in hermon_quiesce().
*/
/* Return the message control for MSI-X */
static ushort_t
{
if ((PCI_CAP_LOCATE(pci_cfg_hdl,
return (0);
}
return (msix_ctrl);
}
/* Return the MSI-X table size */
static size_t
{
}
/* Return the MSI-X PBA size */
static size_t
{
}
static void
{
/*
* MSI-X BIR Index Table:
* BAR indicator register (BIR) to Base Address register.
*/
0x20, 0x24, 0xff, 0xff};
/* Fastreboot data access attribute */
0, /* version */
DDI_STRICTORDER_ACC, /* attr access */
0
};
if ((PCI_CAP_LOCATE(pci_cfg_hdl,
return;
}
/* Get the BIR for MSI-X table */
/* Set the MSI-X table offset */
/* Set the MSI-X table size */
return;
}
reg_size = sizeof (pci_regspec_t) / sizeof (int);
/* Check the register number for MSI-X table */
(addr_space == PCI_ADDR_MEM64))) {
rnumber = i;
break;
}
}
/* Set device attribute version and access according to Hermon FM */
/* Map the entire MSI-X vector table */
return;
}
/* Get the BIR for MSI-X PBA */
/* Set the MSI-X PBA offset */
/* Set the MSI-X PBA size */
/* Check the register number for MSI-X PBA */
(addr_space == PCI_ADDR_MEM64))) {
rnumber = i;
break;
}
}
/* Map in the MSI-X Pending Bit Array */
return;
}
/* Set the MSI-X table save area */
KM_SLEEP);
/* Set the MSI-X PBA save area */
KM_SLEEP);
}
/* Disable Hermon interrupts */
static int
{
int i, j;
/*
* Check if MSI-X interrupts are used. If so, disable MSI-X interupts.
* If not, since Hermon doesn't support MSI interrupts, assuming the
* legacy interrupt is used instead, disable the legacy interrupt.
*/
if ((PCI_CAP_LOCATE(pci_cfg_hdl,
return (DDI_FAILURE);
}
if (!(msix_ctrl & PCI_MSIX_ENABLE_BIT))
return (DDI_SUCCESS);
/* Clear all inums in MSI-X table */
i += PCI_MSIX_VECTOR_SIZE) {
for (j = 0; j < PCI_MSIX_VECTOR_SIZE; j += 4) {
}
}
/* Disable MSI-X interrupts */
} else {
/* Disable the legacy interrupts */
}
return (DDI_SUCCESS);
}
/* Hermon quiesce(9F) entry */
static int
{
/* start fastreboot */
/* If it's in maintenance mode, do nothing but return with SUCCESS */
return (DDI_SUCCESS);
}
/* suppress Hermon FM ereports */
}
/* Shutdown HCA ports */
return (DDI_FAILURE);
}
/* Close HCA */
return (DDI_FAILURE);
}
/* Disable interrupts */
return (DDI_FAILURE);
}
/*
* Query the PCI capabilities of the HCA device, but don't process
* the VPD until after reset.
*/
return (DDI_FAILURE);
}
/*
* Read all PCI config info (reg0...reg63). Note: According to the
* Hermon software reset application note, we should not read or
* restore the values in reg22 and reg23.
* NOTE: For Hermon (and Arbel too) it says to restore the command
* register LAST, and technically, you need to restore the
* PCIE Capability "device control" and "link control" (word-sized,
* at offsets 0x08 and 0x10 from the capbility ID respectively).
* We hold off restoring the command register - offset 0x4 - till last
*/
/* 1st, wait for the semaphore assure accessibility - per PRM */
status = -1;
if (sem == 0) {
status = 0;
break;
}
drv_usecwait(1);
}
/* Check if timeout happens */
if (status == -1) {
return (DDI_FAILURE);
}
/* MSI-X interrupts are used, save the MSI-X table */
if (msix_tbl_hdl && msix_pba_hdl) {
/* save MSI-X table */
i += PCI_MSIX_VECTOR_SIZE) {
for (j = 0; j < PCI_MSIX_VECTOR_SIZE; j += 4) {
hs_msix_tbl_entries + i + j) = data32;
}
}
/* save MSI-X PBA */
hs_msix_pba_entries + i) = data64;
}
}
/* save PCI config space */
for (i = 0; i < HERMON_SW_RESET_NUMREGS; i++) {
if ((i != HERMON_SW_RESET_REG22_RSVD) &&
(i != HERMON_SW_RESET_REG23_RSVD)) {
state->hs_cfg_data[i] =
}
}
/* SW-reset HCA */
/*
* This delay is required so as not to cause a panic here. If the
* device is accessed too soon after reset it will not respond to
* config cycles, causing a Master Abort and panic.
*/
/* Poll waiting for the device to finish resetting */
if (--loopcnt == 0)
break; /* just in case, break and go on */
}
if (loopcnt == 0) {
return (DDI_FAILURE);
}
/* Restore the config info */
for (i = 0; i < HERMON_SW_RESET_NUMREGS; i++) {
if ((i != HERMON_SW_RESET_REG22_RSVD) &&
(i != HERMON_SW_RESET_REG23_RSVD)) {
}
}
/* If MSI-X interrupts are used, restore the MSI-X table */
if (msix_tbl_hdl && msix_pba_hdl) {
/* restore MSI-X PBA */
(state->hs_msix_pba_entries + i);
}
/* restore MSI-X table */
i += PCI_MSIX_VECTOR_SIZE) {
for (j = 0; j < PCI_MSIX_VECTOR_SIZE; j += 4) {
(state->hs_msix_tbl_entries + i + j);
}
}
}
/*
* PCI Express Capability - we saved during capability list, and
* we'll restore them here.
*/
/* restore the command register */
return (DDI_SUCCESS);
}