#include "bnx.h"
#include "bnxgld.h"
#include "bnxhwi.h"
#include "bnxint.h"
#include "bnxtmr.h"
#include "bnxcfg.h"
#define BNX_PRODUCT_BANNER "QLogic 570x/571x Gigabit Ethernet Driver "\
BRCMVERSION
#define BNX_PRODUCT_INFO "QLogic 570x/571x GbE "\
BRCMVERSION
ddi_device_acc_attr_t bnxAccessAttribBAR = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC,
DDI_DEFAULT_ACC
};
ddi_device_acc_attr_t bnxAccessAttribBUF = {
DDI_DEVICE_ATTR_V0,
DDI_NEVERSWAP_ACC,
DDI_STRICTORDER_ACC,
DDI_DEFAULT_ACC
};
static int
bnx_free_system_resources(um_device_t * const umdevice)
{
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MINOR_NODE) {
umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MINOR_NODE;
#ifdef _USE_FRIENDLY_NAME
ddi_remove_minor_node(umdevice->os_param.dip,
(char *)ddi_driver_name(umdevice->os_param.dip));
#else
ddi_remove_minor_node(umdevice->os_param.dip,
ddi_get_name(umdevice->os_param.dip));
#endif
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_TIMER) {
umdevice->os_param.active_resc_flag &=
~DRV_RESOURCE_TIMER;
bnx_timer_fini(umdevice);
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_GLD_REGISTER) {
if (bnx_gld_fini(umdevice)) {
return (-1);
}
umdevice->os_param.active_resc_flag &=
~DRV_RESOURCE_GLD_REGISTER;
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_KSTAT) {
umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_KSTAT;
bnx_kstat_fini(umdevice);
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_HDWR_REGISTER) {
umdevice->os_param.active_resc_flag &=
~DRV_RESOURCE_HDWR_REGISTER;
bnx_hdwr_fini(umdevice);
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MUTEX) {
umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MUTEX;
mutex_destroy(&umdevice->os_param.ind_mutex);
mutex_destroy(&umdevice->os_param.phy_mutex);
mutex_destroy(&umdevice->os_param.rcv_mutex);
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_INTR_1) {
umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_INTR_1;
bnxIntrFini(umdevice);
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_MAP_REGS) {
umdevice->os_param.active_resc_flag &= ~DRV_RESOURCE_MAP_REGS;
ddi_regs_map_free(&umdevice->os_param.reg_acc_handle);
umdevice->lm_dev.vars.dmaRegAccHandle = NULL;
umdevice->os_param.reg_acc_handle = NULL;
}
if (umdevice->os_param.active_resc_flag & DRV_RESOURCE_PCICFG_MAPPED) {
umdevice->os_param.active_resc_flag &=
~DRV_RESOURCE_PCICFG_MAPPED;
pci_config_teardown(&umdevice->os_param.pci_cfg_handle);
}
return (0);
}
static int
bnx_attach_attach(um_device_t *umdevice)
{
int rc;
int instance;
unsigned int val;
int chip_id;
int device_id;
int subdevice_id;
off_t regSize;
dev_info_t *dip;
dip = umdevice->os_param.dip;
umdevice->os_param.active_resc_flag = 0;
rc = pci_config_setup(umdevice->os_param.dip,
&umdevice->os_param.pci_cfg_handle);
if (rc != DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s: Failed to setup PCI configuration space accesses.\n",
umdevice->dev_name);
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_PCICFG_MAPPED;
rc = ddi_dev_regsize(dip, 1, ®Size);
if (rc != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: failed to determine register set size.",
umdevice->dev_name);
}
rc = ddi_regs_map_setup(dip,
1,
&umdevice->os_param.regs_addr,
0,
regSize,
&bnxAccessAttribBAR,
&umdevice->os_param.reg_acc_handle);
if (rc != DDI_SUCCESS) {
cmn_err(CE_WARN,
"%s: Failed to memory map device.\n",
umdevice->dev_name);
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MAP_REGS;
bnx_cfg_msix(umdevice);
if (bnxIntrInit(umdevice) != 0) {
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_INTR_1;
mutex_init(&umdevice->os_param.rcv_mutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
mutex_init(&umdevice->os_param.phy_mutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
mutex_init(&umdevice->os_param.ind_mutex, NULL,
MUTEX_DRIVER, DDI_INTR_PRI(umdevice->intrPriority));
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MUTEX;
if (bnx_hdwr_init(umdevice)) {
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_HDWR_REGISTER;
if (!bnx_kstat_init(umdevice)) {
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_KSTAT;
if (bnx_gld_init(umdevice)) {
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_GLD_REGISTER;
bnx_timer_init(umdevice);
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_TIMER;
instance = ddi_get_instance(umdevice->os_param.dip);
#ifdef _USE_FRIENDLY_NAME
rc = ddi_create_minor_node(dip, (char *)ddi_driver_name(dip),
S_IFCHR, instance, DDI_PSEUDO, 0);
#else
rc = ddi_create_minor_node(dip, ddi_get_name(dip),
S_IFCHR, instance, DDI_PSEUDO, 0);
#endif
if (rc == DDI_FAILURE) {
cmn_err(CE_WARN, "%s: Failed to create device minor node.\n",
umdevice->dev_name);
goto error;
}
umdevice->os_param.active_resc_flag |= DRV_RESOURCE_MINOR_NODE;
ddi_report_dev(dip);
device_id = pci_config_get16(umdevice->os_param.pci_cfg_handle,
0x2);
subdevice_id = pci_config_get16(umdevice->os_param.pci_cfg_handle,
0x2e);
if ((device_id == 0x163b) && (subdevice_id == 0x163b)) {
chip_id = 0x5716;
} else {
chip_id = CHIP_NUM(&umdevice->lm_dev) >> 16;
}
(void) snprintf(umdevice->version, sizeof (umdevice->version), "%s",
BRCMVERSION);
REG_RD_IND(&umdevice->lm_dev,
umdevice->lm_dev.hw_info.shmem_base +
OFFSETOF(shmem_region_t, dev_info.bc_rev), &val);
umdevice->dev_var.fw_ver = (val & 0xFFFF0000) | ((val & 0xFF00) >> 8);
(void) snprintf(umdevice->versionFW, sizeof (umdevice->versionFW),
"0x%x", umdevice->dev_var.fw_ver);
(void) snprintf(umdevice->chipName, sizeof (umdevice->chipName),
"BCM%x", chip_id);
(void) snprintf(umdevice->intrAlloc, sizeof (umdevice->intrAlloc),
"1 %s", (umdevice->intrType == DDI_INTR_TYPE_MSIX) ? "MSIX" :
(umdevice->intrType == DDI_INTR_TYPE_MSI) ? "MSI" :
"Fixed");
cmn_err(CE_NOTE,
"!%s: (%s) BCM%x device with F/W Ver%x is initialized (%s)",
umdevice->dev_name, umdevice->version,
chip_id, umdevice->dev_var.fw_ver,
umdevice->intrAlloc);
return (0);
error:
(void) bnx_free_system_resources(umdevice);
return (-1);
}
static int
bnx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
um_device_t *umdevice;
int ret_val = DDI_SUCCESS;
switch (cmd) {
case DDI_ATTACH:
umdevice = kmem_zalloc(sizeof (um_device_t),
KM_NOSLEEP);
if (umdevice == NULL) {
cmn_err(CE_WARN, "%s: Failed to allocate "
"device memory.\n", __func__);
ret_val = DDI_FAILURE;
break;
}
umdevice->os_param.dip = dip;
umdevice->instance = ddi_get_instance(dip);
(void) snprintf(umdevice->dev_name,
sizeof (umdevice->dev_name), "%s%d", "bnx",
umdevice->instance);
ddi_set_driver_private(dip, (caddr_t)umdevice);
umdevice->magic = BNX_MAGIC;
if (bnx_attach_attach(umdevice)) {
ddi_set_driver_private(dip, (caddr_t)NULL);
kmem_free(umdevice, sizeof (um_device_t));
ret_val = DDI_FAILURE;
}
break;
case DDI_RESUME:
umdevice = ddi_get_driver_private(dip);
if (umdevice == NULL) {
ret_val = DDI_FAILURE;
break;
}
break;
default:
ret_val = DDI_FAILURE;
break;
}
return (ret_val);
}
static int
bnx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
um_device_t *umdevice;
int ret_val = DDI_SUCCESS;
switch (cmd) {
case DDI_DETACH:
umdevice = ddi_get_driver_private(dip);
if (umdevice == NULL) {
ret_val = DDI_SUCCESS;
break;
}
if (umdevice == NULL) {
cmn_err(CE_WARN,
"%s: Sanity check failed(1).", __func__);
ret_val = DDI_SUCCESS;
break;
}
if (umdevice->os_param.dip != dip) {
cmn_err(CE_WARN,
"%s: Sanity check failed(2).", __func__);
ret_val = DDI_SUCCESS;
break;
}
if (umdevice->intr_enabled != B_FALSE) {
cmn_err(CE_WARN, "%s: Detaching a device "
"that is currently running!!!\n",
umdevice->dev_name);
ret_val = DDI_FAILURE;
break;
}
if (bnx_free_system_resources(umdevice)) {
ret_val = DDI_FAILURE;
break;
}
ddi_set_driver_private(dip, (caddr_t)NULL);
kmem_free(umdevice, sizeof (um_device_t));
break;
case DDI_SUSPEND:
umdevice = ddi_get_driver_private(dip);
if (umdevice == NULL) {
ret_val = DDI_FAILURE;
break;
}
break;
default:
ret_val = DDI_FAILURE;
break;
}
return (ret_val);
}
static int
bnx_quiesce(dev_info_t *dip)
{
um_device_t *umdevice;
umdevice = ddi_get_driver_private(dip);
if (umdevice == NULL || umdevice->os_param.dip != dip) {
cmn_err(CE_WARN, "%s: Sanity check failed.", __func__);
return (DDI_FAILURE);
}
lm_disable_int(&(umdevice->lm_dev));
(void) lm_set_rx_mask(&(umdevice->lm_dev), RX_FILTER_USER_IDX0,
LM_RX_MASK_ACCEPT_NONE);
return (DDI_SUCCESS);
}
DDI_DEFINE_STREAM_OPS(bnx_dev_ops, nulldev, nulldev, bnx_attach, bnx_detach, \
nodev, NULL, (D_MP | D_64BIT), NULL, bnx_quiesce);
static struct modldrv bnx_modldrv = {
&mod_driverops,
BNX_PRODUCT_INFO,
&bnx_dev_ops
};
static struct modlinkage bnx_modlinkage = {
MODREV_1,
&bnx_modldrv,
NULL
};
int
_init(void)
{
int rc;
mac_init_ops(&bnx_dev_ops, "bnx");
rc = mod_install(&bnx_modlinkage);
if (rc != 0) {
cmn_err(CE_WARN, "%s:_init - mod_install returned 0x%x", "bnx",
rc);
return (rc);
}
cmn_err(CE_NOTE, "!%s", BNX_PRODUCT_BANNER);
return (rc);
}
int
_fini(void)
{
int rc;
rc = mod_remove(&bnx_modlinkage);
if (rc == 0) {
mac_fini_ops(&bnx_dev_ops);
}
return (rc);
}
int
_info(struct modinfo *modinfop)
{
int rc;
rc = mod_info(&bnx_modlinkage, modinfop);
return (rc);
}