/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2014 QLogic Corporation
* The contents of this file are subject to the terms of the
* QLogic End User License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License at
* http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/
* QLogic_End_User_Software_License.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*/
/*
* Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#include "bnxe.h"
#ifndef STRINGIFY
#define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x)
#endif
#define BNXE_PRODUCT_BANNER "QLogic NetXtreme II 10 Gigabit Ethernet Driver v" STRINGIFY(MAJVERSION) "." STRINGIFY(MINVERSION) "." STRINGIFY(REVVERSION)
#define BNXE_PRODUCT_INFO "QLogic NXII 10 GbE v" STRINGIFY(MAJVERSION) "." STRINGIFY(MINVERSION) "." STRINGIFY(REVVERSION)
#define BNXE_REGISTER_BAR_NUM 1
#define BNXE_REGS_MAP_OFFSET 0
#define BNXE_L2_MEMORY_WINDOW_SIZE 0x40000 /* 256K for PCI Config Registers */
u32_t dbg_code_path = CP_ALL;
u8_t dbg_trace_level = LV_VERBOSE;
u32_t g_dbg_flags = 0;
kmutex_t bnxeLoaderMutex;
u32_t bnxeNumPlumbed;
extern ddi_dma_attr_t bnxeDmaPageAttrib;
extern ddi_dma_attr_t bnxeRxDmaAttrib;
extern ddi_dma_attr_t bnxeTxDmaAttrib;
extern ddi_dma_attr_t bnxeTxCbDmaAttrib;
u8_t BnxeInstance(void * pDev)
{
um_device_t * pUM = (um_device_t *)pDev;
return (pUM == NULL) ? 0xf : pUM->instance;
}
/* pass in pointer to either lm_device_t or um_device_t */
char * BnxeDevName(void * pDev)
{
um_device_t * pUM = (um_device_t *)pDev;
return ((pUM == NULL) || (*pUM->devName == 0)) ? "(bnxe)" : pUM->devName;
}
char * BnxeChipName(um_device_t * pUM)
{
switch (CHIP_NUM(&pUM->lm_dev) >> 16)
{
case 0x164e: return "BCM57710";
case 0x164f: return "BCM57711";
case 0x1650: return "BCM57711E";
case 0x1662: return "BCM57712";
case 0x1663: return "BCM57712NP";
case 0x16a1: return "BCM57840";
case 0x168d: return "BCM57840";
case 0x16a4: return "BCM57840NP";
case 0x16ab: return "BCM57840NP";
case 0x168e: return "BCM57810";
case 0x16ae: return "BCM57810NP";
case 0x168a: return "BCM57800";
case 0x16a5: return "BCM57800NP";
default: return "UNKNOWN";
}
}
boolean_t BnxeProtoSupport(um_device_t * pUM, int proto)
{
boolean_t do_eth;
boolean_t do_fcoe;
uint32_t port_feature_config_sf;
if (IS_MULTI_VNIC(&pUM->lm_dev))
{
do_eth = B_FALSE;
do_fcoe = B_FALSE;
if (pUM->lm_dev.hw_info.mcp_detected == 1)
{
if (pUM->lm_dev.params.mf_proto_support_flags &
LM_PROTO_SUPPORT_ETHERNET)
{
do_eth = B_TRUE;
}
if (pUM->lm_dev.params.mf_proto_support_flags &
LM_PROTO_SUPPORT_FCOE)
{
do_fcoe = B_TRUE;
}
}
else
{
/* mcp is not present so allow enumeration */
do_eth = B_TRUE;
do_fcoe = B_TRUE;
}
}
else /* SF */
{
do_eth = B_TRUE;
do_fcoe = B_FALSE;
/* check per port storage personality config from NVRAM */
port_feature_config_sf = (pUM->lm_dev.hw_info.port_feature_config &
PORT_FEAT_CFG_STORAGE_PERSONALITY_MASK);
switch (port_feature_config_sf)
{
case PORT_FEAT_CFG_STORAGE_PERSONALITY_ISCSI:
break;
case PORT_FEAT_CFG_STORAGE_PERSONALITY_FCOE:
case PORT_FEAT_CFG_STORAGE_PERSONALITY_BOTH:
case PORT_FEAT_CFG_STORAGE_PERSONALITY_DEFAULT:
default:
do_fcoe = B_TRUE;
break;
}
}
if (pUM->lm_dev.params.max_func_fcoe_cons == 0)
{
do_fcoe = B_FALSE;
}
return (((proto == LM_PROTO_SUPPORT_ETHERNET) && do_eth) ||
((proto == LM_PROTO_SUPPORT_FCOE) && do_fcoe));
}
boolean_t BnxeProtoFcoeAfex(um_device_t * pUM)
{
return ((pUM->lm_dev.params.mf_mode == MULTI_FUNCTION_AFEX) &&
BnxeProtoSupport(pUM, LM_PROTO_SUPPORT_FCOE)) ? B_TRUE : B_FALSE;
}
static boolean_t BnxePciInit(um_device_t * pUM)
{
/* setup resources needed for accessing the PCI configuration space */
if (pci_config_setup(pUM->pDev, &pUM->pPciCfg) != DDI_SUCCESS)
{
BnxeLogWarn(pUM, "Failed to setup PCI config");
return B_FALSE;
}
return B_TRUE;
}
static void BnxePciDestroy(um_device_t * pUM)
{
if (pUM->pPciCfg)
{
pci_config_teardown(&pUM->pPciCfg);
pUM->pPciCfg = NULL;
}
}
static void BnxeBarMemDestroy(um_device_t * pUM)
{
BnxeMemRegion * pMemRegion;
/* free the BAR mappings */
while (!d_list_is_empty(&pUM->memRegionList))
{
pMemRegion = (BnxeMemRegion *)d_list_peek_head(&pUM->memRegionList);
mm_unmap_io_space(&pUM->lm_dev,
pMemRegion->pRegAddr,
pMemRegion->size);
}
}
static void BnxeMutexInit(um_device_t * pUM)
{
lm_device_t * pLM = &pUM->lm_dev;
int idx;
for (idx = 0; idx < (MAX_RSS_CHAINS + 1); idx++)
{
mutex_init(&pUM->intrMutex[idx], NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->intrFlipMutex[idx], NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->sbMutex[idx], NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
}
for (idx = 0; idx < MAX_ETH_CONS; idx++)
{
mutex_init(&pUM->txq[idx].txMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->txq[idx].freeTxDescMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
pUM->txq[idx].pUM = pUM;
pUM->txq[idx].idx = idx;
}
for (idx = 0; idx < MAX_ETH_CONS; idx++)
{
mutex_init(&pUM->rxq[idx].rxMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->rxq[idx].doneRxMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
pUM->rxq[idx].pUM = pUM;
pUM->rxq[idx].idx = idx;
}
for (idx = 0; idx < USER_OPTION_RX_RING_GROUPS_MAX; idx++)
{
pUM->rxqGroup[idx].pUM = pUM;
pUM->rxqGroup[idx].idx = idx;
}
mutex_init(&pUM->ethConMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->mcpMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->phyMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->indMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->cidMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->spqMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->spReqMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->rrReqMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->islesCtrlMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->toeMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->memMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->offloadMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->hwInitMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->gldMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
rw_init(&pUM->gldTxMutex, NULL, RW_DRIVER, NULL);
mutex_init(&pUM->timerMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
mutex_init(&pUM->kstatMutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(pUM->intrPriority));
}
static void BnxeMutexDestroy(um_device_t * pUM)
{
lm_device_t * pLM = &pUM->lm_dev;
int idx;
for (idx = 0; idx < (MAX_RSS_CHAINS + 1); idx++)
{
mutex_destroy(&pUM->intrMutex[idx]);
mutex_destroy(&pUM->intrFlipMutex[idx]);
mutex_destroy(&pUM->sbMutex[idx]);
}
for (idx = 0; idx < MAX_ETH_CONS; idx++)
{
mutex_destroy(&pUM->txq[idx].txMutex);
mutex_destroy(&pUM->txq[idx].freeTxDescMutex);
}
for (idx = 0; idx < MAX_ETH_CONS; idx++)
{
mutex_destroy(&pUM->rxq[idx].rxMutex);
mutex_destroy(&pUM->rxq[idx].doneRxMutex);
}
mutex_destroy(&pUM->ethConMutex);
mutex_destroy(&pUM->mcpMutex);
mutex_destroy(&pUM->phyMutex);
mutex_destroy(&pUM->indMutex);
mutex_destroy(&pUM->cidMutex);
mutex_destroy(&pUM->spqMutex);
mutex_destroy(&pUM->spReqMutex);
mutex_destroy(&pUM->rrReqMutex);
mutex_destroy(&pUM->islesCtrlMutex);
mutex_destroy(&pUM->toeMutex);
mutex_destroy(&pUM->memMutex); /* not until all mem deleted */
mutex_destroy(&pUM->offloadMutex);
mutex_destroy(&pUM->hwInitMutex);
mutex_destroy(&pUM->gldMutex);
rw_destroy(&pUM->gldTxMutex);
mutex_destroy(&pUM->timerMutex);
mutex_destroy(&pUM->kstatMutex);
}
/* FMA support */
int BnxeCheckAccHandle(ddi_acc_handle_t handle)
{
ddi_fm_error_t de;
ddi_fm_acc_err_get(handle, &de, DDI_FME_VERSION);
ddi_fm_acc_err_clear(handle, DDI_FME_VERSION);
return (de.fme_status);
}
int BnxeCheckDmaHandle(ddi_dma_handle_t handle)
{
ddi_fm_error_t de;
ddi_fm_dma_err_get(handle, &de, DDI_FME_VERSION);
return (de.fme_status);
}
/* The IO fault service error handling callback function */
static int BnxeFmErrorCb(dev_info_t * pDev,
ddi_fm_error_t * err,
const void * impl_data)
{
/*
* As the driver can always deal with an error in any dma or
* access handle, we can just return the fme_status value.
*/
pci_ereport_post(pDev, err, NULL);
return (err->fme_status);
}
static void BnxeFmInit(um_device_t * pUM)
{
ddi_iblock_cookie_t iblk;
int fma_acc_flag;
int fma_dma_flag;
/* Only register with IO Fault Services if we have some capability */
if (pUM->fmCapabilities & DDI_FM_ACCCHK_CAPABLE)
{
bnxeAccessAttribBAR.devacc_attr_version = DDI_DEVICE_ATTR_V1;
bnxeAccessAttribBAR.devacc_attr_access = DDI_FLAGERR_ACC;
}
if (pUM->fmCapabilities & DDI_FM_DMACHK_CAPABLE)
{
bnxeDmaPageAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
bnxeRxDmaAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
bnxeTxDmaAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
bnxeTxCbDmaAttrib.dma_attr_flags = DDI_DMA_FLAGERR;
}
if (pUM->fmCapabilities)
{
/* Register capabilities with IO Fault Services */
ddi_fm_init(pUM->pDev, &pUM->fmCapabilities, &iblk);
/* Initialize pci ereport capabilities if ereport capable */
if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities) ||
DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
{
pci_ereport_setup(pUM->pDev);
}
/* Register error callback if error callback capable */
if (DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
{
ddi_fm_handler_register(pUM->pDev, BnxeFmErrorCb, (void *)pUM);
}
}
}
static void BnxeFmFini(um_device_t * pUM)
{
/* Only unregister FMA capabilities if we registered some */
if (pUM->fmCapabilities)
{
/* Release any resources allocated by pci_ereport_setup() */
if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities) ||
DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
{
pci_ereport_teardown(pUM->pDev);
}
/* Un-register error callback if error callback capable */
if (DDI_FM_ERRCB_CAP(pUM->fmCapabilities))
{
ddi_fm_handler_unregister(pUM->pDev);
}
/* Unregister from IO Fault Services */
ddi_fm_fini(pUM->pDev);
}
}
void BnxeFmErrorReport(um_device_t * pUM,
char * detail)
{
uint64_t ena;
char buf[FM_MAX_CLASS];
(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
ena = fm_ena_generate(0, FM_ENA_FMT1);
if (DDI_FM_EREPORT_CAP(pUM->fmCapabilities))
{
ddi_fm_ereport_post(pUM->pDev, buf, ena, DDI_NOSLEEP,
FM_VERSION, DATA_TYPE_UINT8,
FM_EREPORT_VERS0, NULL);
}
}
static boolean_t BnxeAttachDevice(um_device_t * pUM)
{
int rc;
int * props = NULL;
uint_t numProps;
u32_t vendor_id;
u32_t device_id;
/* fm-capable in bnxe.conf can be used to set fmCapabilities. */
pUM->fmCapabilities = ddi_prop_get_int(DDI_DEV_T_ANY,
pUM->pDev,
DDI_PROP_DONTPASS,
"fm-capable",
(DDI_FM_EREPORT_CAPABLE |
DDI_FM_ACCCHK_CAPABLE |
DDI_FM_DMACHK_CAPABLE |
DDI_FM_ERRCB_CAPABLE));
/* Register capabilities with IO Fault Services. */
BnxeFmInit(pUM);
if (!BnxePciInit(pUM))
{
BnxeFmFini(pUM);
return B_FALSE;
}
BnxeMutexInit(pUM);
if (!BnxeWorkQueueInit(pUM))
{
return B_FALSE;
}
rc = lm_get_dev_info(&pUM->lm_dev);
if (pUM->fmCapabilities &&
BnxeCheckAccHandle(pUM->pPciCfg) != DDI_FM_OK)
{
ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
if (pUM->fmCapabilities &&
BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK)
{
ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
if (rc != LM_STATUS_SUCCESS)
{
BnxeFmErrorReport(pUM, DDI_FM_DEVICE_INVAL_STATE);
ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_LOST);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
BnxeLogWarn(pUM, "Failed to get device information");
return B_FALSE;
}
#if 0
if (IS_PFDEV(&pUM->lm_dev) && lm_check_if_pf_assigned_to_vm(&pUM->lm_dev))
{
lm_set_virt_mode(&pUM->lm_dev, DEVICE_TYPE_PF, VT_ASSIGNED_TO_VM_PF);
}
#endif
/* check if FCoE is enabled on this function */
#if 0
pUM->do_fcoe =
((CHIP_IS_E2(&pUM->lm_dev) || CHIP_IS_E3(&pUM->lm_dev)) &&
BnxeProtoSupport(pUM, LM_PROTO_SUPPORT_FCOE)) ? B_TRUE :
B_FALSE;
#else
pUM->do_fcoe = B_FALSE;
#endif
lm_get_iscsi_boot_info_block(&pUM->lm_dev, &pUM->iscsiInfo);
if (pUM->iscsiInfo.signature != 0)
{
BnxeLogInfo(pUM, "MBA FCoE boot occurred on this interface.");
}
if (!BnxeIntrInit(pUM))
{
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
if (!BnxeKstatInit(pUM))
{
BnxeIntrFini(pUM);
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
if (BnxeProtoFcoeAfex(pUM))
{
/* No support for L2 on FCoE enabled AFEX function! */
BnxeLogInfo(pUM, "FCoE AFEX function, not registering with GLD.");
#if 0
/*
* The following is wonky. Doing a CLONE_DEV makes it visible to
* various L2 networking commands even though the instance was
* not registered with GLDv3 via mac_register().
*/
/* Create a style-2 DLPI device */
if (ddi_create_minor_node(pUM->pDev,
(char *)ddi_driver_name(pUM->pDev),
S_IFCHR,
0,
DDI_PSEUDO, //DDI_NT_NET,
CLONE_DEV) != DDI_SUCCESS)
{
BnxeLogWarn(pUM, "Failed to create device minor node.");
BnxeKstatFini(pUM);
BnxeIntrFini(pUM);
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
/* Create a style-1 DLPI device */
if (ddi_create_minor_node(pUM->pDev,
pUM->devName,
S_IFCHR,
pUM->instance,
DDI_PSEUDO, //DDI_NT_NET,
0) != DDI_SUCCESS)
{
BnxeLogWarn(pUM, "Failed to create device instance minor node.");
ddi_remove_minor_node(pUM->pDev, (char *)ddi_driver_name(pUM->pDev));
BnxeKstatFini(pUM);
BnxeIntrFini(pUM);
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
#endif
}
else
{
/* register with the GLDv3 MAC layer */
if (!BnxeGldInit(pUM))
{
BnxeKstatFini(pUM);
BnxeIntrFini(pUM);
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_FALSE;
}
}
snprintf(pUM->version,
sizeof(pUM->version),
"%d.%d.%d",
MAJVERSION,
MINVERSION,
REVVERSION);
snprintf(pUM->versionLM,
sizeof(pUM->versionLM),
"%d.%d.%d",
LM_DRIVER_MAJOR_VER,
LM_DRIVER_MINOR_VER,
LM_DRIVER_FIX_NUM);
snprintf(pUM->versionFW,
sizeof(pUM->versionFW),
"%d.%d.%d.%d",
BCM_5710_FW_MAJOR_VERSION,
BCM_5710_FW_MINOR_VERSION,
BCM_5710_FW_REVISION_VERSION,
BCM_5710_FW_ENGINEERING_VERSION);
snprintf(pUM->versionBC,
sizeof(pUM->versionBC),
"%d.%d.%d",
((pUM->lm_dev.hw_info.bc_rev >> 24) & 0xff),
((pUM->lm_dev.hw_info.bc_rev >> 16) & 0xff),
((pUM->lm_dev.hw_info.bc_rev >> 8) & 0xff));
snprintf(pUM->chipName,
sizeof(pUM->chipName),
"%s",
BnxeChipName(pUM));
snprintf(pUM->chipID,
sizeof(pUM->chipID),
"0x%x",
pUM->lm_dev.hw_info.chip_id);
*pUM->bus_dev_func = 0;
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
0, "reg", &props, &numProps);
if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
{
snprintf(pUM->bus_dev_func,
sizeof(pUM->bus_dev_func),
"%04x:%02x:%02x",
PCI_REG_BUS_G(props[0]),
PCI_REG_DEV_G(props[0]),
PCI_REG_FUNC_G(props[0]));
ddi_prop_free(props);
}
vendor_id = 0;
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
0, "vendor-id", &props, &numProps);
if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
{
vendor_id = props[0];
ddi_prop_free(props);
}
device_id = 0;
rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, pUM->pDev,
0, "device-id", &props, &numProps);
if ((rc == DDI_PROP_SUCCESS) && (numProps > 0))
{
device_id = props[0];
ddi_prop_free(props);
}
snprintf(pUM->vendor_device,
sizeof(pUM->vendor_device),
"%04x:%04x",
vendor_id,
device_id);
snprintf(pUM->intrAlloc,
sizeof(pUM->intrAlloc),
"%d %s",
(pUM->intrType == DDI_INTR_TYPE_FIXED) ? 1 : (pUM->defIntr.intrCount +
pUM->fcoeIntr.intrCount +
pUM->rssIntr.intrCount),
(pUM->intrType == DDI_INTR_TYPE_MSIX) ? "MSIX" :
(pUM->intrType == DDI_INTR_TYPE_MSI) ? "MSI" :
"Fixed");
BnxeLogInfo(pUM,
"(0x%p) %s %s - v%s - FW v%s - BC v%s - %s (%s)",
pUM,
pUM->chipName,
pUM->chipID,
pUM->version,
pUM->versionFW,
pUM->versionBC,
IS_MULTI_VNIC(&pUM->lm_dev) ? "MF" : "SF",
pUM->intrAlloc);
return B_TRUE;
}
static boolean_t BnxeDetachDevice(um_device_t * pUM)
{
int rc;
rc = BnxeFcoeFini(pUM);
if ((rc != 0) && (rc != ENOTSUP) && (rc != ENODEV))
{
return B_FALSE;
}
if (BnxeProtoFcoeAfex(pUM))
{
/* No support for L2 on FCoE enabled AFEX function! */
;
#if 0
ddi_remove_minor_node(pUM->pDev, pUM->devName);
ddi_remove_minor_node(pUM->pDev, (char *)ddi_driver_name(pUM->pDev));
#endif
}
else
{
if (!BnxeGldFini(pUM))
{
return B_FALSE;
}
}
BnxeKstatFini(pUM);
BnxeIntrFini(pUM);
BnxeBarMemDestroy(pUM);
BnxeWorkQueueWaitAndDestroy(pUM);
BnxeMutexDestroy(pUM);
BnxePciDestroy(pUM);
BnxeFmFini(pUM);
return B_TRUE;
}
static int BnxeAttach(dev_info_t * pDev, ddi_attach_cmd_t cmd)
{
um_device_t * pUM;
switch (cmd)
{
case DDI_ATTACH:
if ((pUM = kmem_zalloc(sizeof(um_device_t), KM_SLEEP)) == NULL)
{
BnxeLogWarn(NULL, "failed to allocate device structure");
return DDI_FAILURE;
}
ddi_set_driver_private(pDev, pUM);
/* set magic number for identification */
pUM->magic = BNXE_MAGIC;
/* default for debug logging is dump everything */
pUM->devParams.debug_level = (CP_ALL | LV_MASK);
/* save dev_info_t in the driver structure */
pUM->pDev = pDev;
d_list_clear(&pUM->memBlockList);
d_list_clear(&pUM->memDmaList);
d_list_clear(&pUM->memRegionList);
#ifdef BNXE_DEBUG_DMA_LIST
d_list_clear(&pUM->memDmaListSaved);
#endif
/* obtain a human-readable device name log messages with */
pUM->instance = ddi_get_instance(pDev);
snprintf(pUM->devName, sizeof(pUM->devName),
"bnxe%d", pUM->instance);
if (!BnxeAttachDevice(pUM))
{
kmem_free(pUM, sizeof(um_device_t));
return DDI_FAILURE;
}
if (BNXE_FCOE(pUM) && pUM->devParams.fcoeEnable)
{
BnxeFcoeStartStop(pUM);
}
return DDI_SUCCESS;
case DDI_RESUME:
#if !(defined(__S11) || defined(__S12))
case DDI_PM_RESUME:
#endif
pUM = (um_device_t *)ddi_get_driver_private(pDev);
/* sanity check */
if (pUM == NULL || pUM->pDev != pDev)
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
if (BnxeHwResume(pUM) != 0)
{
BnxeLogWarn(pUM, "Fail to resume this device!");
return DDI_FAILURE;
}
return DDI_SUCCESS;
default:
return DDI_FAILURE;
}
}
static int BnxeDetach(dev_info_t * pDev, ddi_detach_cmd_t cmd)
{
um_device_t * pUM;
switch (cmd)
{
case DDI_DETACH:
pUM = (um_device_t *)ddi_get_driver_private(pDev);
/* sanity check */
if (pUM == NULL || pUM->pDev != pDev)
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
if (pUM->intrEnabled != B_FALSE)
{
BnxeLogWarn(pUM, "Detaching a device that is currently running!");
return DDI_FAILURE;
}
if (!BnxeDetachDevice(pUM))
{
BnxeLogWarn(pUM, "Can't detach it now, please try again later!");
return DDI_FAILURE;
}
kmem_free(pUM, sizeof(um_device_t));
return DDI_SUCCESS;
case DDI_SUSPEND:
#if !(defined(__S11) || defined(__S12))
case DDI_PM_SUSPEND:
#endif
pUM = (um_device_t *)ddi_get_driver_private(pDev);
/* sanity check */
if (pUM == NULL || pUM->pDev != pDev)
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
if (BnxeHwSuspend(pUM) != 0)
{
BnxeLogWarn(pUM, "Fail to suspend this device!");
return DDI_FAILURE;
}
return DDI_SUCCESS;
default:
return DDI_FAILURE;
}
}
#if (DEVO_REV > 3)
static int BnxeQuiesce(dev_info_t * pDev)
{
um_device_t * pUM;
pUM = (um_device_t *)ddi_get_driver_private(pDev);
/* sanity check */
if (pUM == NULL || pUM->pDev != pDev)
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
if (!pUM->plumbed)
{
return DDI_SUCCESS;
}
if (BnxeHwQuiesce(pUM) != 0)
{
BnxeLogWarn(pUM, "Failed to quiesce the device!");
return DDI_FAILURE;
}
return DDI_SUCCESS;
}
#endif
void BnxeFcoeInitChild(dev_info_t * pDev,
dev_info_t * cDip)
{
um_device_t *pUM = (um_device_t *) ddi_get_driver_private(pDev);
if ((pUM == NULL) || (pUM->pDev != pDev))
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed ", __func__);
return;
}
ddi_set_name_addr(cDip, ddi_get_name_addr(pUM->pDev));
}
void BnxeFcoeUninitChild(dev_info_t * pDev,
dev_info_t * cDip)
{
ddi_set_name_addr(cDip, NULL);
}
static int BnxeBusCtl(dev_info_t * pDev,
dev_info_t * pRDev,
ddi_ctl_enum_t op,
void * pArg,
void * pResult)
{
um_device_t * pUM = (um_device_t *)ddi_get_driver_private(pDev);
/* sanity check */
if (pUM == NULL || pUM->pDev != pDev)
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
BnxeLogDbg(pUM, "BnxeBusCtl (%d)", op);
switch (op)
{
case DDI_CTLOPS_REPORTDEV:
case DDI_CTLOPS_IOMIN:
break;
case DDI_CTLOPS_INITCHILD:
BnxeFcoeInitChild(pDev, (dev_info_t *) pArg);
break;
case DDI_CTLOPS_UNINITCHILD:
BnxeFcoeUninitChild(pDev, (dev_info_t *) pArg);
break;
default:
return (ddi_ctlops(pDev, pRDev, op, pArg, pResult));
}
return DDI_SUCCESS;
}
static int BnxeCbIoctl(dev_t dev,
int cmd,
intptr_t arg,
int mode,
cred_t * credp,
int * rvalp)
{
BnxeBinding * pBinding = (BnxeBinding *)arg;
um_device_t * pUM;
(void)dev;
(void)mode;
(void)credp;
(void)rvalp;
if ((pBinding == NULL) ||
(pBinding->pCliDev == NULL) ||
(pBinding->pPrvDev == NULL))
{
BnxeLogWarn(NULL, "Invalid binding arg to ioctl %d", cmd);
return DDI_FAILURE;
}
pUM = (um_device_t *)ddi_get_driver_private(pBinding->pPrvDev);
/* sanity checks */
if (pBinding->version != BNXE_BINDING_VERSION)
{
BnxeLogWarn(NULL, "%s: Invalid binding version (0x%08x)",
__func__, pBinding->version);
return DDI_FAILURE;
}
if ((pUM == NULL) ||
(pUM->fcoe.pDev != pBinding->pCliDev) ||
(pUM->pDev != pBinding->pPrvDev))
{
BnxeLogWarn(NULL, "%s: dev_info_t match failed", __func__);
return DDI_FAILURE;
}
switch (cmd)
{
case BNXE_BIND_FCOE:
/* copy the binding struct and fill in the provider callback */
BnxeLogInfo(pUM, "FCoE BIND start");
if (!CLIENT_DEVI(pUM, LM_CLI_IDX_FCOE))
{
BnxeLogWarn(pUM, "FCoE BIND when DEVI is offline!");
return DDI_FAILURE;
}
if (CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE))
{
BnxeLogWarn(pUM, "FCoE BIND when alread bound!");
return DDI_FAILURE;
}
pUM->fcoe.bind = *pBinding;
pUM->fcoe.bind.prvCtl = pBinding->prvCtl = BnxeFcoePrvCtl;
pUM->fcoe.bind.prvTx = pBinding->prvTx = BnxeFcoePrvTx;
pUM->fcoe.bind.prvPoll = pBinding->prvPoll = BnxeFcoePrvPoll;
pUM->fcoe.bind.prvSendWqes = pBinding->prvSendWqes = BnxeFcoePrvSendWqes;
pUM->fcoe.bind.prvMapMailboxq = pBinding->prvMapMailboxq = BnxeFcoePrvMapMailboxq;
pUM->fcoe.bind.prvUnmapMailboxq = pBinding->prvUnmapMailboxq = BnxeFcoePrvUnmapMailboxq;
pUM->devParams.numRxDesc[LM_CLI_IDX_FCOE] = pBinding->numRxDescs;
pUM->devParams.numTxDesc[LM_CLI_IDX_FCOE] = pBinding->numTxDescs;
pUM->lm_dev.params.l2_rx_desc_cnt[LM_CLI_IDX_FCOE] = pBinding->numRxDescs;
BnxeInitBdCnts(pUM, LM_CLI_IDX_FCOE);
if (BnxeHwStartFCOE(pUM))
{
return DDI_FAILURE;
}
CLIENT_BIND_SET(pUM, LM_CLI_IDX_FCOE);
lm_mcp_indicate_client_bind(&pUM->lm_dev, LM_CLI_IDX_FCOE);
BnxeLogInfo(pUM, "FCoE BIND done");
return DDI_SUCCESS;
case BNXE_UNBIND_FCOE:
/* clear the binding struct and stats */
BnxeLogInfo(pUM, "FCoE UNBIND start");
if (CLIENT_DEVI(pUM, LM_CLI_IDX_FCOE))
{
BnxeLogWarn(pUM, "FCoE UNBIND when DEVI is online!");
return DDI_FAILURE;
}
if (!CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE))
{
BnxeLogWarn(pUM, "FCoE UNBIND when not bound!");
return DDI_FAILURE;
}
/* We must not detach until all packets held by fcoe are retrieved. */
if (!BnxeWaitForPacketsFromClient(pUM, LM_CLI_IDX_FCOE))
{
return DDI_FAILURE;
}
lm_mcp_indicate_client_unbind(&pUM->lm_dev, LM_CLI_IDX_FCOE);
CLIENT_BIND_RESET(pUM, LM_CLI_IDX_FCOE);
BnxeHwStopFCOE(pUM);
memset(&pUM->fcoe.bind, 0, sizeof(pUM->fcoe.bind));
memset(&pUM->fcoe.stats, 0, sizeof(pUM->fcoe.stats));
pBinding->prvCtl = NULL;
pBinding->prvTx = NULL;
pBinding->prvPoll = NULL;
pBinding->prvSendWqes = NULL;
pBinding->prvMapMailboxq = NULL;
pBinding->prvUnmapMailboxq = NULL;
pUM->fcoe.pDev = NULL; /* sketchy? */
BnxeLogInfo(pUM, "FCoE UNBIND done");
return DDI_SUCCESS;
default:
BnxeLogWarn(pUM, "Unknown ioctl %d", cmd);
return DDI_FAILURE;
}
}
#ifndef ILLUMOS
static struct bus_ops bnxe_bus_ops =
{
BUSO_REV,
nullbusmap, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
i_ddi_map_fault, /* bus_map_fault */
ddi_dma_map, /* bus_dma_map */
ddi_dma_allochdl, /* bus_dma_allochdl */
ddi_dma_freehdl, /* bus_dma_freehdl */
ddi_dma_bindhdl, /* bus_dma_bindhdl */
ddi_dma_unbindhdl, /* bus_unbindhdl */
ddi_dma_flush, /* bus_dma_flush */
ddi_dma_win, /* bus_dma_win */
ddi_dma_mctl, /* bus_dma_ctl */
BnxeBusCtl, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
NULL, /* bus_get_eventcookie */
NULL, /* bus_add_eventcall */
NULL, /* bus_remove_event */
NULL, /* bus_post_event */
NULL, /* bus_intr_ctl */
NULL, /* bus_config */
NULL, /* bus_unconfig */
NULL, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_power */
NULL
};
#endif /* ILLUMOS */
static struct cb_ops bnxe_cb_ops =
{
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
BnxeCbIoctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
(int)(D_MP | D_64BIT), /* cb_flag */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev, /* cb_awrite */
};
#if (DEVO_REV > 3)
static struct dev_ops bnxe_dev_ops =
{
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
BnxeAttach, /* devo_attach */
BnxeDetach, /* devo_detach */
nodev, /* devo_reset */
&bnxe_cb_ops, /* devo_cb_ops */
#ifndef ILLUMOS
&bnxe_bus_ops, /* devo_bus_ops */
#else
NULL, /* devo_bus_ops */
#endif
NULL, /* devo_power */
BnxeQuiesce /* devo_quiesce */
};
#else
static struct dev_ops bnxe_dev_ops =
{
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
BnxeAttach, /* devo_attach */
BnxeDetach, /* devo_detach */
nodev, /* devo_reset */
&bnxe_cb_ops, /* devo_cb_ops */
&bnxe_bus_ops, /* devo_bus_ops */
NULL /* devo_power */
};
#endif
static struct modldrv bnxe_modldrv =
{
&mod_driverops, /* drv_modops (must be mod_driverops for drivers) */
BNXE_PRODUCT_INFO, /* drv_linkinfo (string displayed by modinfo) */
&bnxe_dev_ops /* drv_dev_ops */
};
static struct modlinkage bnxe_modlinkage =
{
MODREV_1, /* ml_rev */
{
&bnxe_modldrv, /* ml_linkage */
NULL /* NULL termination */
}
};
int _init(void)
{
int rc;
mac_init_ops(&bnxe_dev_ops, "bnxe");
/* Install module information with O/S */
if ((rc = mod_install(&bnxe_modlinkage)) != DDI_SUCCESS)
{
BnxeLogWarn(NULL, "mod_install returned 0x%x", rc);
mac_fini_ops(&bnxe_dev_ops);
return rc;
}
mutex_init(&bnxeLoaderMutex, NULL, MUTEX_DRIVER, NULL);
bnxeNumPlumbed = 0;
BnxeLogInfo(NULL, "%s", BNXE_PRODUCT_BANNER);
return rc;
}
int _fini(void)
{
int rc;
if ((rc = mod_remove(&bnxe_modlinkage)) == DDI_SUCCESS)
{
mac_fini_ops(&bnxe_dev_ops);
mutex_destroy(&bnxeLoaderMutex);
if (bnxeNumPlumbed > 0)
{
/*
* This shouldn't be possible since modunload must only call _fini
* when no instances are currently plumbed.
*/
BnxeLogWarn(NULL, "%d instances have not been unplumbed", bnxeNumPlumbed);
}
}
return rc;
}
int _info(struct modinfo * pModinfo)
{
return mod_info(&bnxe_modlinkage, pModinfo);
}