#include <sys/types.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/pci.h>
#include <sys/pci_cap.h>
#include <sys/bitmap.h>
#include <sys/policy.h>
#include <sys/ib/adapters/tavor/tavor.h>
#include <sys/pci.h>
void *tavor_statep;
tavor_umap_db_t tavor_userland_rsrc_db;
static int tavor_attach(dev_info_t *, ddi_attach_cmd_t);
static int tavor_detach(dev_info_t *, ddi_detach_cmd_t);
static int tavor_open(dev_t *, int, int, cred_t *);
static int tavor_close(dev_t, int, int, cred_t *);
static int tavor_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int tavor_drv_init(tavor_state_t *state, dev_info_t *dip, int instance);
static void tavor_drv_fini(tavor_state_t *state);
static void tavor_drv_fini2(tavor_state_t *state);
static int tavor_isr_init(tavor_state_t *state);
static void tavor_isr_fini(tavor_state_t *state);
static int tavor_hw_init(tavor_state_t *state);
static void tavor_hw_fini(tavor_state_t *state,
tavor_drv_cleanup_level_t cleanup);
static int tavor_soft_state_init(tavor_state_t *state);
static void tavor_soft_state_fini(tavor_state_t *state);
static int tavor_hca_port_init(tavor_state_t *state);
static int tavor_hca_ports_shutdown(tavor_state_t *state, uint_t num_init);
static void tavor_hca_config_setup(tavor_state_t *state,
tavor_hw_initqueryhca_t *inithca);
static int tavor_internal_uarpgs_init(tavor_state_t *state);
static void tavor_internal_uarpgs_fini(tavor_state_t *state);
static int tavor_special_qp_contexts_reserve(tavor_state_t *state);
static void tavor_special_qp_contexts_unreserve(tavor_state_t *state);
static int tavor_sw_reset(tavor_state_t *state);
static int tavor_mcg_init(tavor_state_t *state);
static void tavor_mcg_fini(tavor_state_t *state);
static int tavor_fw_version_check(tavor_state_t *state);
static void tavor_device_info_report(tavor_state_t *state);
static void tavor_pci_capability_list(tavor_state_t *state,
ddi_acc_handle_t hdl);
static void tavor_pci_capability_vpd(tavor_state_t *state,
ddi_acc_handle_t hdl, uint_t offset);
static int tavor_pci_read_vpd(ddi_acc_handle_t hdl, uint_t offset,
uint32_t addr, uint32_t *data);
static void tavor_pci_capability_pcix(tavor_state_t *state,
ddi_acc_handle_t hdl, uint_t offset);
static int tavor_intr_or_msi_init(tavor_state_t *state);
static int tavor_add_intrs(tavor_state_t *state, int intr_type);
static int tavor_intr_or_msi_fini(tavor_state_t *state);
static int tavor_intr_disable(tavor_state_t *);
static int tavor_quiesce(dev_info_t *);
static struct cb_ops tavor_cb_ops = {
tavor_open,
tavor_close,
nodev,
nodev,
nodev,
nodev,
nodev,
tavor_ioctl,
tavor_devmap,
NULL,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP |
D_64BIT | D_HOTPLUG |
D_DEVMAP,
CB_REV
};
static struct dev_ops tavor_ops = {
DEVO_REV,
0,
tavor_getinfo,
nulldev,
nulldev,
tavor_attach,
tavor_detach,
nodev,
&tavor_cb_ops,
NULL,
nodev,
tavor_quiesce,
};
static struct modldrv tavor_modldrv = {
&mod_driverops,
"Tavor InfiniBand HCA Driver",
&tavor_ops
};
static struct modlinkage tavor_modlinkage = {
MODREV_1,
&tavor_modldrv,
NULL
};
extern ibc_operations_t tavor_ibc_ops;
int
_init()
{
int status;
status = ddi_soft_state_init(&tavor_statep, sizeof (tavor_state_t),
(size_t)TAVOR_INITIAL_STATES);
if (status != 0) {
return (status);
}
status = ibc_init(&tavor_modlinkage);
if (status != 0) {
ddi_soft_state_fini(&tavor_statep);
return (status);
}
status = mod_install(&tavor_modlinkage);
if (status != 0) {
ibc_fini(&tavor_modlinkage);
ddi_soft_state_fini(&tavor_statep);
return (status);
}
tavor_umap_db_init();
return (status);
}
int
_info(struct modinfo *modinfop)
{
int status;
status = mod_info(&tavor_modlinkage, modinfop);
return (status);
}
int
_fini()
{
int status;
status = mod_remove(&tavor_modlinkage);
if (status != 0) {
return (status);
}
tavor_umap_db_fini();
ibc_fini(&tavor_modlinkage);
ddi_soft_state_fini(&tavor_statep);
return (status);
}
static int
tavor_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
dev_t dev;
tavor_state_t *state;
minor_t instance;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
dev = (dev_t)arg;
instance = TAVOR_DEV_INSTANCE(dev);
state = ddi_get_soft_state(tavor_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
*result = (void *)state->ts_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
dev = (dev_t)arg;
instance = TAVOR_DEV_INSTANCE(dev);
*result = (void *)(uintptr_t)instance;
return (DDI_SUCCESS);
default:
break;
}
return (DDI_FAILURE);
}
static int
tavor_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
tavor_state_t *state;
tavor_rsrc_t *rsrcp;
tavor_umap_db_entry_t *umapdb, *umapdb2;
minor_t instance;
uint64_t key, value;
uint_t tr_indx;
dev_t dev;
int status;
instance = TAVOR_DEV_INSTANCE(*devp);
state = ddi_get_soft_state(tavor_statep, instance);
if (state == NULL) {
return (ENXIO);
}
if ((otyp != OTYP_CHR) || ((flag & FEXCL) &&
secpolicy_excl_open(credp) != 0)) {
return (EINVAL);
}
key = ddi_get_pid();
mutex_enter(&tavor_userland_rsrc_db.tdl_umapdb_lock);
status = tavor_umap_db_find_nolock(instance, key,
MLNX_UMAP_UARPG_RSRC, &value, 0, NULL);
if (status != DDI_SUCCESS) {
if (!TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
rsrcp = NULL;
tr_indx = state->ts_open_tr_indx++;
} else {
status = tavor_rsrc_alloc(state, TAVOR_UARPG, 1,
TAVOR_NOSLEEP, &rsrcp);
if (status != DDI_SUCCESS) {
mutex_exit(
&tavor_userland_rsrc_db.tdl_umapdb_lock);
return (EAGAIN);
}
tr_indx = rsrcp->tr_indx;
}
umapdb = tavor_umap_db_alloc(instance, key,
MLNX_UMAP_UARPG_RSRC, (uint64_t)(uintptr_t)rsrcp);
if (umapdb == NULL) {
mutex_exit(&tavor_userland_rsrc_db.tdl_umapdb_lock);
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
tavor_rsrc_free(state, &rsrcp);
}
return (EAGAIN);
}
dev = makedevice(getmajor(*devp), (tr_indx <<
TAVOR_MINORNUM_SHIFT) | instance);
umapdb2 = tavor_umap_db_alloc(instance, dev,
MLNX_UMAP_PID_RSRC, (uint64_t)key);
if (umapdb2 == NULL) {
mutex_exit(&tavor_userland_rsrc_db.tdl_umapdb_lock);
tavor_umap_db_free(umapdb);
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
tavor_rsrc_free(state, &rsrcp);
}
return (EAGAIN);
}
tavor_umap_db_add_nolock(umapdb);
tavor_umap_db_add_nolock(umapdb2);
} else {
rsrcp = (tavor_rsrc_t *)(uintptr_t)value;
dev = makedevice(getmajor(*devp), (rsrcp->tr_indx <<
TAVOR_MINORNUM_SHIFT) | instance);
}
mutex_exit(&tavor_userland_rsrc_db.tdl_umapdb_lock);
*devp = dev;
return (0);
}
static int
tavor_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
tavor_state_t *state;
tavor_rsrc_t *rsrcp;
tavor_umap_db_entry_t *umapdb;
tavor_umap_db_priv_t *priv;
minor_t instance;
uint64_t key, value;
int status;
instance = TAVOR_DEV_INSTANCE(dev);
state = ddi_get_soft_state(tavor_statep, instance);
if (state == NULL) {
return (ENXIO);
}
key = dev;
mutex_enter(&tavor_userland_rsrc_db.tdl_umapdb_lock);
status = tavor_umap_db_find_nolock(instance, key, MLNX_UMAP_PID_RSRC,
&value, TAVOR_UMAP_DB_REMOVE, &umapdb);
if (status == DDI_SUCCESS) {
priv = (tavor_umap_db_priv_t *)umapdb->tdbe_common.tdb_priv;
if (priv != NULL) {
tavor_umap_db_handle_onclose_cb(priv);
kmem_free(priv, sizeof (tavor_umap_db_priv_t));
umapdb->tdbe_common.tdb_priv = (void *)NULL;
}
tavor_umap_db_free(umapdb);
key = value;
status = tavor_umap_db_find_nolock(instance, key,
MLNX_UMAP_UARPG_RSRC, &value, TAVOR_UMAP_DB_REMOVE,
&umapdb);
if (status == DDI_SUCCESS) {
tavor_umap_db_free(umapdb);
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
rsrcp = (tavor_rsrc_t *)(uintptr_t)value;
tavor_rsrc_free(state, &rsrcp);
}
}
}
mutex_exit(&tavor_userland_rsrc_db.tdl_umapdb_lock);
return (0);
}
static int
tavor_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
tavor_state_t *state;
ibc_clnt_hdl_t tmp_ibtfpriv;
ibc_status_t ibc_status;
int instance;
int status;
#ifdef __lock_lint
(void) tavor_quiesce(dip);
#endif
switch (cmd) {
case DDI_ATTACH:
instance = ddi_get_instance(dip);
status = ddi_soft_state_zalloc(tavor_statep, instance);
if (status != DDI_SUCCESS) {
cmn_err(CE_NOTE, "tavor%d: driver failed to attach: "
"attach_ssz_fail", instance);
goto fail_attach_nomsg;
}
state = ddi_get_soft_state(tavor_statep, instance);
if (state == NULL) {
ddi_soft_state_free(tavor_statep, instance);
cmn_err(CE_NOTE, "tavor%d: driver failed to attach: "
"attach_gss_fail", instance);
goto fail_attach_nomsg;
}
TAVOR_ATTACH_MSG_INIT(state->ts_attach_buf);
status = tavor_drv_init(state, dip, instance);
if ((status != DDI_SUCCESS) &&
(TAVOR_IS_OPERATIONAL(state->ts_operational_mode))) {
goto fail_attach;
}
status = ddi_create_minor_node(dip, "devctl", S_IFCHR, instance,
DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
tavor_drv_fini(state);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"attach_create_mn_fail");
goto fail_attach;
}
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
ibc_status = ibc_attach(&tmp_ibtfpriv,
&state->ts_ibtfinfo);
if (ibc_status != IBC_SUCCESS) {
ddi_remove_minor_node(dip, "devctl");
tavor_drv_fini(state);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"attach_ibcattach_fail");
goto fail_attach;
}
TAVOR_ENABLE_IBTF_CALLB(state, tmp_ibtfpriv);
ibc_post_attach(state->ts_ibtfpriv);
status = tavor_agent_handlers_init(state);
if (status != DDI_SUCCESS) {
(void) ibc_pre_detach(tmp_ibtfpriv, DDI_DETACH);
TAVOR_QUIESCE_IBTF_CALLB(state);
if (state->ts_in_evcallb != 0) {
TAVOR_WARNING(state, "unable to "
"quiesce Tavor IBTF callbacks");
}
ibc_detach(tmp_ibtfpriv);
ddi_remove_minor_node(dip, "devctl");
tavor_drv_fini(state);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"attach_agentinit_fail");
goto fail_attach;
}
}
ddi_report_dev(dip);
tavor_device_info_report(state);
if (!(TAVOR_IS_OPERATIONAL(state->ts_operational_mode))) {
cmn_err(CE_NOTE, "tavor%d: driver attached "
"(for maintenance mode only)", state->ts_instance);
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_FAILURE);
default:
break;
}
fail_attach:
cmn_err(CE_NOTE, "tavor%d: driver failed to attach: %s", instance,
state->ts_attach_buf);
tavor_drv_fini2(state);
ddi_soft_state_free(tavor_statep, instance);
fail_attach_nomsg:
return (DDI_FAILURE);
}
static int
tavor_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
tavor_state_t *state;
ibc_clnt_hdl_t tmp_ibtfpriv;
ibc_status_t ibc_status;
int instance, status;
instance = ddi_get_instance(dip);
state = ddi_get_soft_state(tavor_statep, instance);
if (state == NULL) {
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
status = tavor_agent_handlers_fini(state);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ibc_status = ibc_pre_detach(state->ts_ibtfpriv, cmd);
if (ibc_status != IBC_SUCCESS) {
status = tavor_agent_handlers_init(state);
if (status != DDI_SUCCESS) {
TAVOR_WARNING(state, "failed to "
"restart Tavor agents");
}
return (DDI_FAILURE);
}
tmp_ibtfpriv = state->ts_ibtfpriv;
TAVOR_QUIESCE_IBTF_CALLB(state);
if (state->ts_in_evcallb != 0) {
TAVOR_WARNING(state, "unable to quiesce Tavor "
"IBTF callbacks");
}
ibc_detach(tmp_ibtfpriv);
}
ddi_remove_minor_node(dip, "devctl");
if (TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
tavor_drv_fini(state);
cmn_err(CE_CONT, "Tavor driver successfully "
"detached\n");
}
tavor_drv_fini2(state);
ddi_soft_state_free(tavor_statep, instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_FAILURE);
default:
break;
}
return (DDI_FAILURE);
}
static int
tavor_drv_init(tavor_state_t *state, dev_info_t *dip, int instance)
{
int status;
state->ts_dip = dip;
state->ts_instance = instance;
if (TAVOR_IS_HCA_MODE(state->ts_dip)) {
state->ts_operational_mode = TAVOR_HCA_MODE;
} else if (TAVOR_IS_COMPAT_MODE(state->ts_dip)) {
state->ts_operational_mode = TAVOR_COMPAT_MODE;
} else if (TAVOR_IS_MAINTENANCE_MODE(state->ts_dip)) {
state->ts_operational_mode = TAVOR_MAINTENANCE_MODE;
return (DDI_FAILURE);
} else {
state->ts_operational_mode = 0;
TAVOR_WARNING(state, "unexpected device type detected");
return (DDI_FAILURE);
}
status = tavor_hw_init(state);
if (status != DDI_SUCCESS) {
state->ts_operational_mode = TAVOR_MAINTENANCE_MODE;
cmn_err(CE_NOTE, "tavor%d: error during attach: %s", instance,
state->ts_attach_buf);
return (DDI_FAILURE);
}
status = tavor_isr_init(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, TAVOR_DRV_CLEANUP_ALL);
return (DDI_FAILURE);
}
status = tavor_soft_state_init(state);
if (status != DDI_SUCCESS) {
tavor_isr_fini(state);
tavor_hw_fini(state, TAVOR_DRV_CLEANUP_ALL);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
tavor_drv_fini(tavor_state_t *state)
{
tavor_soft_state_fini(state);
tavor_isr_fini(state);
tavor_hw_fini(state, TAVOR_DRV_CLEANUP_ALL);
}
static void
tavor_drv_fini2(tavor_state_t *state)
{
if (state->ts_reg_cmdhdl) {
ddi_regs_map_free(&state->ts_reg_cmdhdl);
state->ts_reg_cmdhdl = NULL;
}
if (state->ts_pci_cfghdl) {
pci_config_teardown(&state->ts_pci_cfghdl);
state->ts_pci_cfghdl = NULL;
}
}
static int
tavor_isr_init(tavor_state_t *state)
{
int status;
status = ddi_intr_add_handler(state->ts_intrmsi_hdl, tavor_isr,
(caddr_t)state, NULL);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (state->ts_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
status = ddi_intr_block_enable(&state->ts_intrmsi_hdl, 1);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
} else {
status = ddi_intr_enable(state->ts_intrmsi_hdl);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
}
tavor_eq_arm_all(state);
return (DDI_SUCCESS);
}
static void
tavor_isr_fini(tavor_state_t *state)
{
if (state->ts_intrmsi_cap & DDI_INTR_FLAG_BLOCK) {
(void) ddi_intr_block_disable(&state->ts_intrmsi_hdl, 1);
} else {
(void) ddi_intr_disable(state->ts_intrmsi_hdl);
}
(void) ddi_intr_remove_handler(state->ts_intrmsi_hdl);
}
static int
tavor_fix_error_buf(tavor_state_t *state)
{
int assigned_addr_len;
pci_regspec_t *assigned_addr;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, state->ts_dip,
DDI_PROP_DONTPASS, "assigned-addresses", (int **)&assigned_addr,
(uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
state->ts_fw.error_buf_addr -= assigned_addr[0].pci_phys_low +
((uint64_t)(assigned_addr[0].pci_phys_mid) << 32);
ddi_prop_free(assigned_addr);
return (DDI_SUCCESS);
}
static int
tavor_hw_init(tavor_state_t *state)
{
tavor_drv_cleanup_level_t cleanup;
sm_nodeinfo_t nodeinfo;
uint64_t errorcode;
off_t ddr_size;
int status;
int retries;
cleanup = TAVOR_DRV_CLEANUP_LEVEL0;
state->ts_reg_accattr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
state->ts_reg_accattr.devacc_attr_endian_flags = DDI_STRUCTURE_BE_ACC;
state->ts_reg_accattr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
status = pci_config_setup(state->ts_dip, &state->ts_pci_cfghdl);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_PCI_config_space_regmap_fail");
return (DDI_FAILURE);
}
status = ddi_regs_map_setup(state->ts_dip, TAVOR_CMD_BAR,
&state->ts_reg_cmd_baseaddr, 0, 0, &state->ts_reg_accattr,
&state->ts_reg_cmdhdl);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_CMD_ddirms_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL1;
status = ddi_regs_map_setup(state->ts_dip, TAVOR_UAR_BAR,
&state->ts_reg_uar_baseaddr, 0, 0, &state->ts_reg_accattr,
&state->ts_reg_uarhdl);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_UAR_ddirms_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL2;
status = ddi_dev_regsize(state->ts_dip, TAVOR_DDR_BAR, &ddr_size);
if (status != DDI_SUCCESS) {
cmn_err(CE_CONT, "Tavor: ddi_dev_regsize() failed "
"(check HCA-attached DIMM memory?)\n");
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_DDR_ddi_regsize_fail");
return (DDI_FAILURE);
}
#if !defined(_ELF64) && !defined(__sparc)
ddr_size = TAVOR_DDR_SIZE_MIN;
#endif
state->ts_cfg_profile_setting = ddr_size;
status = ddi_regs_map_setup(state->ts_dip, TAVOR_DDR_BAR,
&state->ts_reg_ddr_baseaddr, 0, ddr_size, &state->ts_reg_accattr,
&state->ts_reg_ddrhdl);
if (status == DDI_ME_NORESOURCES && ddr_size > TAVOR_DDR_SIZE_128) {
status = ddi_regs_map_setup(state->ts_dip, TAVOR_DDR_BAR,
&state->ts_reg_ddr_baseaddr, 0, TAVOR_DDR_SIZE_128,
&state->ts_reg_accattr, &state->ts_reg_ddrhdl);
if (status == DDI_SUCCESS) {
state->ts_cfg_profile_setting = TAVOR_DDR_SIZE_128;
}
}
if (status != DDI_SUCCESS) {
if (status == DDI_ME_RNUMBER_RANGE) {
cmn_err(CE_CONT, "Tavor: ddi_regs_map_setup() failed "
"(check HCA-attached DIMM memory?)\n");
}
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_DDR_ddirms_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL3;
state->ts_cmd_regs.hcr = (tavor_hw_hcr_t *)
((uintptr_t)state->ts_reg_cmd_baseaddr + TAVOR_CMD_HCR_OFFSET);
state->ts_cmd_regs.ecr = (uint64_t *)
((uintptr_t)state->ts_reg_cmd_baseaddr + TAVOR_CMD_ECR_OFFSET);
state->ts_cmd_regs.clr_ecr = (uint64_t *)
((uintptr_t)state->ts_reg_cmd_baseaddr + TAVOR_CMD_CLR_ECR_OFFSET);
state->ts_cmd_regs.sw_reset = (uint32_t *)
((uintptr_t)state->ts_reg_cmd_baseaddr + TAVOR_CMD_SW_RESET_OFFSET);
state->ts_cmd_regs.clr_int = (uint64_t *)
((uintptr_t)state->ts_reg_cmd_baseaddr + TAVOR_CMD_CLR_INT_OFFSET);
status = tavor_cfg_profile_init_phase1(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf, "hw_init_cfginit_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL4;
status = tavor_sw_reset(state);
if (status != TAVOR_CMD_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf, "hw_init_sw_reset_fail");
return (DDI_FAILURE);
}
status = tavor_sys_en_cmd_post(state, TAVOR_CMD_SYS_EN_NORMAL,
&errorcode, TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
if ((status == TAVOR_CMD_BAD_NVMEM) ||
(status == TAVOR_CMD_DDR_MEM_ERR)) {
cmn_err(CE_CONT, "Tavor: SYS_EN command failed: 0x%x "
"0x%" PRIx64 " (invalid firmware image?)\n",
status, errorcode);
} else {
cmn_err(CE_CONT, "Tavor: SYS_EN command failed: 0x%x "
"0x%" PRIx64 "\n", status, errorcode);
}
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_sys_en_cmd_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL5;
status = tavor_rsrc_init_phase1(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_rsrcinit1_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL6;
status = tavor_cmn_query_cmd_post(state, QUERY_DDR, 0,
&state->ts_ddr, sizeof (tavor_hw_queryddr_t),
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: QUERY_DDR command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_query_ddr_cmd_fail");
return (DDI_FAILURE);
}
status = tavor_cmn_query_cmd_post(state, QUERY_FW, 0, &state->ts_fw,
sizeof (tavor_hw_queryfw_t), TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: QUERY_FW command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_query_fw_cmd_fail");
return (DDI_FAILURE);
}
if (tavor_fix_error_buf(state) != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_fixerrorbuf_fail");
return (DDI_FAILURE);
}
status = tavor_fw_version_check(state);
if (status != DDI_SUCCESS) {
if (state->ts_operational_mode == TAVOR_HCA_MODE) {
cmn_err(CE_CONT, "Unsupported Tavor FW version: "
"expected: %04d.%04d.%04d, "
"actual: %04d.%04d.%04d\n",
TAVOR_FW_VER_MAJOR,
TAVOR_FW_VER_MINOR,
TAVOR_FW_VER_SUBMINOR,
state->ts_fw.fw_rev_major,
state->ts_fw.fw_rev_minor,
state->ts_fw.fw_rev_subminor);
} else if (state->ts_operational_mode == TAVOR_COMPAT_MODE) {
cmn_err(CE_CONT, "Unsupported Tavor Compat FW version: "
"expected: %04d.%04d.%04d, "
"actual: %04d.%04d.%04d\n",
TAVOR_COMPAT_FW_VER_MAJOR,
TAVOR_COMPAT_FW_VER_MINOR,
TAVOR_COMPAT_FW_VER_SUBMINOR,
state->ts_fw.fw_rev_major,
state->ts_fw.fw_rev_minor,
state->ts_fw.fw_rev_subminor);
} else {
cmn_err(CE_CONT, "Unsupported FW version: "
"%04d.%04d.%04d\n",
state->ts_fw.fw_rev_major,
state->ts_fw.fw_rev_minor,
state->ts_fw.fw_rev_subminor);
}
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_checkfwver_fail");
return (DDI_FAILURE);
}
drv_usecwait(10);
retries = 1000;
retry:
status = tavor_mod_stat_cfg_cmd_post(state);
if (status != DDI_SUCCESS) {
if (retries > 0) {
drv_usecwait(1000);
retries--;
goto retry;
}
cmn_err(CE_CONT, "Tavor: MOD_STAT_CFG command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_mod_stat_cfg_cmd_fail");
return (DDI_FAILURE);
}
status = tavor_cmn_query_cmd_post(state, QUERY_DEV_LIM, 0,
&state->ts_devlim, sizeof (tavor_hw_querydevlim_t),
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: QUERY_DEV_LIM command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_query_devlim_cmd_fail");
return (DDI_FAILURE);
}
status = tavor_cfg_profile_init_phase2(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf, "hw_init_cfginit2_fail");
return (DDI_FAILURE);
}
status = tavor_rsrc_init_phase2(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_rsrcinit2_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL7;
status = tavor_cmn_query_cmd_post(state, QUERY_ADAPTER, 0,
&state->ts_adapter, sizeof (tavor_hw_queryadapter_t),
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: QUERY_ADAPTER command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_query_adapter_cmd_fail");
return (DDI_FAILURE);
}
tavor_hca_config_setup(state, &state->ts_hcaparams);
status = tavor_init_hca_cmd_post(state, &state->ts_hcaparams,
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: INIT_HCA command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_init_hca_cmd_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL8;
status = tavor_pd_alloc(state, &state->ts_pdhdl_internal, TAVOR_SLEEP);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_internal_pd_alloc_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL9;
status = tavor_internal_uarpgs_init(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_internal_uarpgs_alloc_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL10;
status = tavor_intr_or_msi_init(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"intr_or_msi_init_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL11;
status = tavor_eq_init_all(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_eqinitall_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL12;
status = tavor_special_qp_contexts_reserve(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_reserve_special_qp_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL13;
status = tavor_mcg_init(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf, "hw_init_mcg_init_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_LEVEL14;
status = tavor_hca_port_init(state);
if (status != DDI_SUCCESS) {
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_hca_port_init_fail");
return (DDI_FAILURE);
}
cleanup = TAVOR_DRV_CLEANUP_ALL;
status = tavor_getnodeinfo_cmd_post(state, TAVOR_CMD_NOSLEEP_SPIN,
&nodeinfo);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: GetNodeInfo command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_getnodeinfo_cmd_fail");
return (DDI_FAILURE);
}
if (state->ts_cfg_profile->cp_nodeguid) {
state->ts_nodeguid = state->ts_cfg_profile->cp_nodeguid;
} else {
state->ts_nodeguid = nodeinfo.NodeGUID;
}
if (state->ts_nodeguid != nodeinfo.NodeGUID) {
cmn_err(CE_NOTE, "!NodeGUID value queried from firmware "
"does not match value set by device property");
}
if (state->ts_cfg_profile->cp_sysimgguid) {
state->ts_sysimgguid = state->ts_cfg_profile->cp_sysimgguid;
} else {
state->ts_sysimgguid = nodeinfo.SystemImageGUID;
}
if (state->ts_sysimgguid != nodeinfo.SystemImageGUID) {
cmn_err(CE_NOTE, "!SystemImageGUID value queried from firmware "
"does not match value set by device property");
}
status = tavor_getnodedesc_cmd_post(state, TAVOR_CMD_NOSLEEP_SPIN,
(sm_nodedesc_t *)&state->ts_nodedesc);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: GetNodeDesc command failed: %08x\n",
status);
tavor_hw_fini(state, cleanup);
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"hw_init_getnodedesc_cmd_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
tavor_hw_fini(tavor_state_t *state, tavor_drv_cleanup_level_t cleanup)
{
uint_t num_ports;
int status;
switch (cleanup) {
case TAVOR_DRV_CLEANUP_ALL:
num_ports = state->ts_cfg_profile->cp_num_ports;
(void) tavor_hca_ports_shutdown(state, num_ports);
case TAVOR_DRV_CLEANUP_LEVEL14:
tavor_mcg_fini(state);
case TAVOR_DRV_CLEANUP_LEVEL13:
tavor_special_qp_contexts_unreserve(state);
case TAVOR_DRV_CLEANUP_LEVEL12:
status = tavor_eq_fini_all(state);
if (status != DDI_SUCCESS) {
TAVOR_WARNING(state, "failed to teardown EQs");
return;
}
case TAVOR_DRV_CLEANUP_LEVEL11:
status = tavor_intr_or_msi_fini(state);
if (status != DDI_SUCCESS) {
TAVOR_WARNING(state, "failed to free intr/MSI");
return;
}
case TAVOR_DRV_CLEANUP_LEVEL10:
tavor_internal_uarpgs_fini(state);
case TAVOR_DRV_CLEANUP_LEVEL9:
status = tavor_pd_free(state, &state->ts_pdhdl_internal);
if (status != DDI_SUCCESS) {
TAVOR_WARNING(state, "failed to free internal PD");
return;
}
case TAVOR_DRV_CLEANUP_LEVEL8:
status = tavor_close_hca_cmd_post(state,
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
TAVOR_WARNING(state, "failed to shutdown HCA");
return;
}
case TAVOR_DRV_CLEANUP_LEVEL7:
tavor_rsrc_fini(state, TAVOR_RSRC_CLEANUP_ALL);
case TAVOR_DRV_CLEANUP_LEVEL6:
tavor_rsrc_fini(state, TAVOR_RSRC_CLEANUP_PHASE1_COMPLETE);
case TAVOR_DRV_CLEANUP_LEVEL5:
status = tavor_sys_dis_cmd_post(state, TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
TAVOR_WARNING(state, "failed to shutdown hardware");
return;
}
case TAVOR_DRV_CLEANUP_LEVEL4:
tavor_cfg_profile_fini(state);
case TAVOR_DRV_CLEANUP_LEVEL3:
ddi_regs_map_free(&state->ts_reg_ddrhdl);
case TAVOR_DRV_CLEANUP_LEVEL2:
ddi_regs_map_free(&state->ts_reg_uarhdl);
case TAVOR_DRV_CLEANUP_LEVEL1:
case TAVOR_DRV_CLEANUP_LEVEL0:
break;
default:
TAVOR_WARNING(state, "unexpected driver cleanup level");
return;
}
}
static int
tavor_soft_state_init(tavor_state_t *state)
{
ibt_hca_attr_t *hca_attr;
uint64_t maxval, val;
ibt_hca_flags_t caps = IBT_HCA_NO_FLAGS;
int status;
state->ts_ibtfinfo.hca_ci_vers = IBCI_V4;
state->ts_ibtfinfo.hca_handle = (ibc_hca_hdl_t)state;
state->ts_ibtfinfo.hca_ops = &tavor_ibc_ops;
hca_attr = kmem_zalloc(sizeof (ibt_hca_attr_t), KM_SLEEP);
state->ts_ibtfinfo.hca_attr = hca_attr;
hca_attr->hca_dip = state->ts_dip;
hca_attr->hca_fw_major_version = state->ts_fw.fw_rev_major;
hca_attr->hca_fw_minor_version = state->ts_fw.fw_rev_minor;
hca_attr->hca_fw_micro_version = state->ts_fw.fw_rev_subminor;
if (state->ts_devlim.ud_multi) {
caps |= IBT_HCA_UD_MULTICAST;
}
if (state->ts_devlim.atomic) {
caps |= IBT_HCA_ATOMICS_HCA;
}
if (state->ts_devlim.apm) {
caps |= IBT_HCA_AUTO_PATH_MIG;
}
if (state->ts_devlim.pkey_v) {
caps |= IBT_HCA_PKEY_CNTR;
}
if (state->ts_devlim.qkey_v) {
caps |= IBT_HCA_QKEY_CNTR;
}
if (state->ts_cfg_profile->cp_srq_enable) {
caps |= IBT_HCA_SRQ | IBT_HCA_RESIZE_SRQ;
}
caps |= (IBT_HCA_AH_PORT_CHECK | IBT_HCA_SQD_SQD_PORT |
IBT_HCA_SI_GUID | IBT_HCA_RNR_NAK | IBT_HCA_CURRENT_QP_STATE |
IBT_HCA_PORT_UP | IBT_HCA_SQD_STATE);
hca_attr->hca_flags = caps;
hca_attr->hca_flags2 = IBT_HCA2_DMA_MR;
hca_attr->hca_vendor_id = state->ts_adapter.vendor_id;
hca_attr->hca_device_id = state->ts_adapter.device_id;
hca_attr->hca_version_id = state->ts_adapter.rev_id;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_qp);
hca_attr->hca_max_qp = val - ((uint64_t)1 <<
state->ts_devlim.log_rsvd_qp);
maxval = ((uint64_t)1 << state->ts_devlim.log_max_qp_sz);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_max_qp_sz);
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_maxqpsz_toobig_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_qp_sz = val;
maxval = state->ts_devlim.max_sg;
val = state->ts_cfg_profile->cp_wqe_max_sgl;
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanysgl_fail");
return (DDI_FAILURE);
}
if (state->ts_cfg_profile->cp_wqe_real_max_sgl > maxval) {
state->ts_cfg_profile->cp_wqe_real_max_sgl = maxval;
val = maxval;
} else {
val = state->ts_cfg_profile->cp_wqe_real_max_sgl;
}
hca_attr->hca_max_sgl = val;
hca_attr->hca_max_rd_sgl = 0;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_cq);
hca_attr->hca_max_cq = val - ((uint64_t)1 <<
state->ts_devlim.log_rsvd_cq);
maxval = ((uint64_t)1 << state->ts_devlim.log_max_cq_sz);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_max_cq_sz) - 1;
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_maxcqsz_toobig_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_cq_sz = val;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_srq);
hca_attr->hca_max_srqs = val - ((uint64_t)1 <<
state->ts_devlim.log_rsvd_srq);
maxval = ((uint64_t)1 << state->ts_devlim.log_max_srq_sz);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_max_srq_sz);
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_maxsrqsz_toobig_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_srqs_sz = val;
val = state->ts_cfg_profile->cp_srq_max_sgl;
maxval = state->ts_devlim.max_sg;
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanysrqsgl_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_srq_sgl = val;
hca_attr->hca_page_sz = ((PAGESIZE == (1 << 13)) ? IBT_PAGE_8K :
IBT_PAGE_4K);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_mpt);
hca_attr->hca_max_memr = val - ((uint64_t)1 <<
state->ts_devlim.log_rsvd_mpt);
hca_attr->hca_max_mem_win = val - ((uint64_t)1 <<
state->ts_devlim.log_rsvd_mpt);
maxval = state->ts_devlim.log_max_mrw_sz;
val = state->ts_cfg_profile->cp_log_max_mrw_sz;
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_maxmrwsz_toobig_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_memr_len = ((uint64_t)1 << val);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_rdb);
hca_attr->hca_max_rsc = val;
val = state->ts_cfg_profile->cp_hca_max_rdma_in_qp;
hca_attr->hca_max_rdma_in_qp = val;
val = state->ts_cfg_profile->cp_hca_max_rdma_out_qp;
hca_attr->hca_max_rdma_out_qp = val;
hca_attr->hca_max_rdma_in_ee = 0;
hca_attr->hca_max_rdma_out_ee = 0;
hca_attr->hca_max_ipv6_qp = 0;
hca_attr->hca_max_ether_qp = 0;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_qp);
hca_attr->hca_max_mcg_qps = val;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_mcg);
hca_attr->hca_max_mcg = val;
val = state->ts_cfg_profile->cp_num_qp_per_mcg;
hca_attr->hca_max_qp_per_mcg = val;
maxval = ((uint64_t)1 << state->ts_devlim.log_max_pkey);
val = ((uint64_t)state->ts_cfg_profile->cp_num_ports <<
state->ts_cfg_profile->cp_log_max_pkeytbl);
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanypkey_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_partitions = val;
maxval = state->ts_devlim.num_ports;
val = state->ts_cfg_profile->cp_num_ports;
if ((val > maxval) || (val == 0)) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanyports_fail");
return (DDI_FAILURE);
}
hca_attr->hca_nports = val;
hca_attr->hca_node_guid = state->ts_nodeguid;
hca_attr->hca_si_guid = state->ts_sysimgguid;
hca_attr->hca_local_ack_delay = state->ts_devlim.ca_ack_delay;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_max_gidtbl);
hca_attr->hca_max_port_sgid_tbl_sz = val;
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_max_pkeytbl);
hca_attr->hca_max_port_pkey_tbl_sz = val;
maxval = ((uint64_t)1 << state->ts_devlim.log_max_pd);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_pd);
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanypd_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_pd = val;
maxval = ((uint64_t)1 << state->ts_devlim.log_max_av);
val = ((uint64_t)1 << state->ts_cfg_profile->cp_log_num_ah);
if (val > maxval) {
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_toomanyah_fail");
return (DDI_FAILURE);
}
hca_attr->hca_max_ah = val;
hca_attr->hca_max_rdd = 0;
hca_attr->hca_max_eec = 0;
mutex_init(&state->ts_uar_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->ts_intrmsi_pri));
state->ts_fw_flashstarted = 0;
mutex_init(&state->ts_fw_flashlock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->ts_intrmsi_pri));
mutex_init(&state->ts_info_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->ts_intrmsi_pri));
tavor_qpn_avl_init(state);
status = tavor_kstat_init(state);
if (status != DDI_SUCCESS) {
tavor_qpn_avl_fini(state);
mutex_destroy(&state->ts_info_lock);
mutex_destroy(&state->ts_fw_flashlock);
mutex_destroy(&state->ts_uar_lock);
kmem_free(hca_attr, sizeof (ibt_hca_attr_t));
TAVOR_ATTACH_MSG(state->ts_attach_buf,
"soft_state_init_kstatinit_fail");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
tavor_soft_state_fini(tavor_state_t *state)
{
tavor_kstat_fini(state);
tavor_qpn_avl_fini(state);
mutex_destroy(&state->ts_info_lock);
mutex_destroy(&state->ts_fw_flashlock);
mutex_destroy(&state->ts_uar_lock);
kmem_free(state->ts_ibtfinfo.hca_attr, sizeof (ibt_hca_attr_t));
}
static void
tavor_hca_config_setup(tavor_state_t *state,
tavor_hw_initqueryhca_t *inithca)
{
tavor_rsrc_pool_info_t *rsrc_pool;
uint64_t ddr_baseaddr, ddr_base_map_addr;
uint64_t offset, addr;
uint_t mcg_size;
#ifdef _LITTLE_ENDIAN
inithca->big_endian = 0;
#else
inithca->big_endian = 1;
#endif
inithca->udav_chk = TAVOR_UDAV_PROTECT_DISABLED;
inithca->udav_port_chk = TAVOR_UDAV_PORTCHK_ENABLED;
ddr_baseaddr = (uint64_t)(uintptr_t)state->ts_reg_ddr_baseaddr;
ddr_base_map_addr = (uint64_t)state->ts_ddr.ddr_baseaddr;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_QPC];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.qpc_baseaddr_h = (addr >> 32);
inithca->context.qpc_baseaddr_l = (addr & 0xFFFFFFFF) >> 7;
inithca->context.log_num_qp = state->ts_cfg_profile->cp_log_num_qp;
inithca->context.eec_baseaddr_h = 0;
inithca->context.eec_baseaddr_l = 0;
inithca->context.log_num_ee = 0;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_CQC];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.cqc_baseaddr_h = (addr >> 32);
inithca->context.cqc_baseaddr_l = (addr & 0xFFFFFFFF) >> 6;
inithca->context.log_num_cq = state->ts_cfg_profile->cp_log_num_cq;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_SRQC];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.srqc_baseaddr_h = (addr >> 32);
inithca->context.srqc_baseaddr_l = (addr & 0xFFFFFFFF) >> 6;
inithca->context.log_num_srq =
state->ts_cfg_profile->cp_log_num_srq;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_EQPC];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.eqpc_baseaddr = addr;
inithca->context.eeec_baseaddr = 0;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_EQC];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.eqc_baseaddr_h = (addr >> 32);
inithca->context.eqc_baseaddr_l = (addr & 0xFFFFFFFF) >> 6;
inithca->context.log_num_eq = TAVOR_NUM_EQ_SHIFT;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_RDB];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->context.rdb_baseaddr_h = (addr >> 32);
inithca->context.rdb_baseaddr_l = 0;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_MCG];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->multi.mc_baseaddr = addr;
mcg_size = TAVOR_MCGMEM_SZ(state);
inithca->multi.log_mc_tbl_ent = highbit(mcg_size) - 1;
inithca->multi.mc_tbl_hash_sz =
(1 << state->ts_cfg_profile->cp_log_num_mcg_hash);
inithca->multi.mc_hash_fn = TAVOR_MCG_DEFAULT_HASH_FN;
inithca->multi.log_mc_tbl_sz = state->ts_cfg_profile->cp_log_num_mcg;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_MPT];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->tpt.mpt_baseaddr = addr;
inithca->tpt.mttseg_sz = TAVOR_MTTSEG_SIZE_SHIFT;
inithca->tpt.log_mpt_sz = state->ts_cfg_profile->cp_log_num_mpt;
inithca->tpt.mtt_version = TAVOR_MTT_PG_WALK_VER;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_MTT];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->tpt.mtt_baseaddr = addr;
rsrc_pool = &state->ts_rsrc_hdl[TAVOR_UAR_SCR];
offset = (uint64_t)(uintptr_t)rsrc_pool->rsrc_start - ddr_baseaddr;
addr = ddr_base_map_addr + offset;
inithca->uar.uarscr_baseaddr = addr;
inithca->uar.uar_pg_sz = PAGESHIFT - 0xC;
}
static int
tavor_hca_port_init(tavor_state_t *state)
{
tavor_hw_initib_t *portinits, *initib;
tavor_cfg_profile_t *cfgprof;
uint_t num_ports;
int i, status;
uint64_t maxval, val;
uint64_t sysimgguid, nodeguid, portguid;
cfgprof = state->ts_cfg_profile;
num_ports = cfgprof->cp_num_ports;
portinits = (tavor_hw_initib_t *)kmem_zalloc(num_ports *
sizeof (tavor_hw_initib_t), KM_SLEEP);
for (i = 0; i < num_ports; i++) {
initib = &portinits[i];
sysimgguid = cfgprof->cp_sysimgguid;
if (sysimgguid != 0) {
initib->set_sysimg_guid = 1;
initib->sysimg_guid = sysimgguid;
}
nodeguid = cfgprof->cp_nodeguid;
if (nodeguid != 0) {
initib->set_node_guid = 1;
initib->node_guid = nodeguid;
}
portguid = cfgprof->cp_portguid[i];
if (portguid != 0) {
initib->set_port_guid0 = 1;
initib->guid0 = portguid;
}
maxval = state->ts_devlim.max_mtu;
val = cfgprof->cp_max_mtu;
if (val > maxval) {
goto init_ports_fail;
}
initib->mtu_cap = val;
maxval = state->ts_devlim.max_port_width;
val = cfgprof->cp_max_port_width;
if (val > maxval) {
goto init_ports_fail;
}
initib->port_width_cap = val;
maxval = state->ts_devlim.max_vl;
val = cfgprof->cp_max_vlcap;
if (val > maxval) {
goto init_ports_fail;
}
initib->vl_cap = val;
maxval = ((uint64_t)1 << state->ts_devlim.log_max_gid);
val = ((uint64_t)1 << cfgprof->cp_log_max_gidtbl);
if (val > maxval) {
goto init_ports_fail;
}
initib->max_gid = val;
maxval = ((uint64_t)1 << state->ts_devlim.log_max_pkey);
val = ((uint64_t)1 << cfgprof->cp_log_max_pkeytbl);
if (val > maxval) {
goto init_ports_fail;
}
initib->max_pkey = val;
status = tavor_init_ib_cmd_post(state, initib, i + 1,
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
cmn_err(CE_CONT, "Tavor: INIT_IB (port %02d) command "
"failed: %08x\n", i + 1, status);
goto init_ports_fail;
}
}
kmem_free(portinits, num_ports * sizeof (tavor_hw_initib_t));
return (DDI_SUCCESS);
init_ports_fail:
kmem_free(portinits, num_ports * sizeof (tavor_hw_initib_t));
(void) tavor_hca_ports_shutdown(state, i);
return (DDI_FAILURE);
}
static int
tavor_hca_ports_shutdown(tavor_state_t *state, uint_t num_init)
{
int i, status;
for (i = 0; i < num_init; i++) {
status = tavor_close_ib_cmd_post(state, i + 1,
TAVOR_CMD_NOSLEEP_SPIN);
if (status != TAVOR_CMD_SUCCESS) {
TAVOR_WARNING(state, "failed to shutdown HCA port");
return (status);
}
}
return (TAVOR_CMD_SUCCESS);
}
static int
tavor_internal_uarpgs_init(tavor_state_t *state)
{
int status;
status = tavor_rsrc_alloc(state, TAVOR_UARPG, 1, TAVOR_SLEEP,
&state->ts_uarpg0_rsrc_rsrvd);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
status = tavor_rsrc_alloc(state, TAVOR_UARPG, 1, TAVOR_SLEEP,
&state->ts_uarpg1_rsrc);
if (status != DDI_SUCCESS) {
tavor_rsrc_free(state, &state->ts_uarpg0_rsrc_rsrvd);
return (DDI_FAILURE);
}
state->ts_uar = (tavor_hw_uar_t *)state->ts_uarpg1_rsrc->tr_addr;
return (DDI_SUCCESS);
}
static void
tavor_internal_uarpgs_fini(tavor_state_t *state)
{
tavor_rsrc_free(state, &state->ts_uarpg1_rsrc);
tavor_rsrc_free(state, &state->ts_uarpg0_rsrc_rsrvd);
}
static int
tavor_special_qp_contexts_reserve(tavor_state_t *state)
{
tavor_rsrc_t *qp0_rsrc, *qp1_rsrc;
int status;
mutex_init(&state->ts_spec_qplock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->ts_intrmsi_pri));
status = tavor_rsrc_alloc(state, TAVOR_QPC, 2, TAVOR_SLEEP, &qp0_rsrc);
if (status != DDI_SUCCESS) {
mutex_destroy(&state->ts_spec_qplock);
return (DDI_FAILURE);
}
state->ts_spec_qp0 = qp0_rsrc;
status = tavor_rsrc_alloc(state, TAVOR_QPC, 2, TAVOR_SLEEP, &qp1_rsrc);
if (status != DDI_SUCCESS) {
tavor_rsrc_free(state, &qp0_rsrc);
mutex_destroy(&state->ts_spec_qplock);
return (DDI_FAILURE);
}
state->ts_spec_qp1 = qp1_rsrc;
return (DDI_SUCCESS);
}
static void
tavor_special_qp_contexts_unreserve(tavor_state_t *state)
{
tavor_rsrc_free(state, &state->ts_spec_qp1);
tavor_rsrc_free(state, &state->ts_spec_qp0);
mutex_destroy(&state->ts_spec_qplock);
}
static int
tavor_sw_reset(tavor_state_t *state)
{
dev_info_t *dip, *pdip;
ddi_acc_handle_t hdl = state->ts_pci_cfghdl, phdl;
uint32_t reset_delay;
int status, i;
reset_delay = state->ts_cfg_profile->cp_sw_reset_delay;
if (reset_delay == 0) {
return (DDI_SUCCESS);
}
dip = state->ts_dip;
pdip = ddi_get_parent(dip);
tavor_pci_capability_list(state, hdl);
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
state->ts_cfg_data[i] = pci_config_get32(hdl, i << 2);
}
}
if (TAVOR_PARENT_IS_BRIDGE(pdip)) {
status = pci_config_setup(pdip, &phdl);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
state->ts_cfg_pdata[i] =
pci_config_get32(phdl, i << 2);
}
}
}
ddi_put32(state->ts_reg_cmdhdl, state->ts_cmd_regs.sw_reset,
TAVOR_SW_RESET_START);
drv_usecwait(reset_delay);
if (TAVOR_PARENT_IS_BRIDGE(pdip)) {
i = 0;
while (pci_config_get32(phdl, 0) == TAVOR_SW_RESET_NOTDONE) {
drv_usecwait(TAVOR_SW_RESET_POLL_DELAY);
}
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
pci_config_put32(phdl, i << 2,
state->ts_cfg_pdata[i]);
}
}
pci_config_teardown(&phdl);
} else {
i = 0;
while (pci_config_get32(hdl, 0) == TAVOR_SW_RESET_NOTDONE) {
drv_usecwait(TAVOR_SW_RESET_POLL_DELAY);
}
}
for (i = 0; i < TAVOR_SW_RESET_NUMREGS; i++) {
if ((i != TAVOR_SW_RESET_REG22_RSVD) &&
(i != TAVOR_SW_RESET_REG23_RSVD)) {
pci_config_put32(hdl, i << 2, state->ts_cfg_data[i]);
}
}
return (DDI_SUCCESS);
}
static int
tavor_mcg_init(tavor_state_t *state)
{
uint_t mcg_tmp_sz;
mcg_tmp_sz = TAVOR_MCGMEM_SZ(state);
state->ts_mcgtmp = kmem_zalloc(mcg_tmp_sz, KM_SLEEP);
mutex_init(&state->ts_mcglock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(state->ts_intrmsi_pri));
return (DDI_SUCCESS);
}
static void
tavor_mcg_fini(tavor_state_t *state)
{
uint_t mcg_tmp_sz;
mcg_tmp_sz = TAVOR_MCGMEM_SZ(state);
kmem_free(state->ts_mcgtmp, mcg_tmp_sz);
mutex_destroy(&state->ts_mcglock);
}
static int
tavor_fw_version_check(tavor_state_t *state)
{
uint_t tavor_fw_ver_major;
uint_t tavor_fw_ver_minor;
uint_t tavor_fw_ver_subminor;
switch (state->ts_operational_mode) {
case TAVOR_HCA_MODE:
tavor_fw_ver_major = TAVOR_FW_VER_MAJOR;
tavor_fw_ver_minor = TAVOR_FW_VER_MINOR;
tavor_fw_ver_subminor = TAVOR_FW_VER_SUBMINOR;
break;
case TAVOR_COMPAT_MODE:
tavor_fw_ver_major = TAVOR_COMPAT_FW_VER_MAJOR;
tavor_fw_ver_minor = TAVOR_COMPAT_FW_VER_MINOR;
tavor_fw_ver_subminor = TAVOR_COMPAT_FW_VER_SUBMINOR;
break;
default:
return (DDI_FAILURE);
}
if (state->ts_fw.fw_rev_major < tavor_fw_ver_major) {
return (DDI_FAILURE);
} else if (state->ts_fw.fw_rev_major > tavor_fw_ver_major) {
return (DDI_SUCCESS);
}
if (state->ts_fw.fw_rev_minor < tavor_fw_ver_minor) {
return (DDI_FAILURE);
} else if (state->ts_fw.fw_rev_minor > tavor_fw_ver_minor) {
return (DDI_SUCCESS);
}
if (state->ts_fw.fw_rev_subminor < tavor_fw_ver_subminor) {
return (DDI_FAILURE);
} else if (state->ts_fw.fw_rev_subminor > tavor_fw_ver_subminor) {
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
static void
tavor_device_info_report(tavor_state_t *state)
{
cmn_err(CE_CONT, "?tavor%d: FW ver: %04d.%04d.%04d, "
"HW rev: %02x\n", state->ts_instance, state->ts_fw.fw_rev_major,
state->ts_fw.fw_rev_minor, state->ts_fw.fw_rev_subminor,
state->ts_adapter.rev_id);
cmn_err(CE_CONT, "?tavor%d: %64s (0x%016" PRIx64 ")\n",
state->ts_instance, state->ts_nodedesc, state->ts_nodeguid);
}
static void
tavor_pci_capability_list(tavor_state_t *state, ddi_acc_handle_t hdl)
{
uint_t offset, data;
data = pci_config_get16(hdl, 0x6);
if ((data & 0x10) == 0) {
return;
}
offset = pci_config_get8(hdl, 0x34);
while (offset != 0x0) {
data = pci_config_get8(hdl, offset);
switch (data) {
case 0x03:
tavor_pci_capability_vpd(state, hdl, offset);
break;
case 0x07:
tavor_pci_capability_pcix(state, hdl, offset);
break;
case 0x05:
break;
default:
break;
}
offset = pci_config_get8(hdl, offset + 1);
}
}
static int
tavor_pci_read_vpd(ddi_acc_handle_t hdl, uint_t offset, uint32_t addr,
uint32_t *data)
{
int retry = 4;
uint32_t val;
int vpd_addr = offset + 2;
int vpd_data = offset + 4;
(void) pci_config_put32(hdl, offset, addr << 16);
do {
drv_usecwait(1000);
val = pci_config_get16(hdl, vpd_addr);
if ((val >> 15) & 0x01) {
*data = pci_config_get32(hdl, vpd_data);
return (DDI_SUCCESS);
}
} while (--retry);
return (DDI_FAILURE);
}
static void
tavor_pci_capability_vpd(tavor_state_t *state, ddi_acc_handle_t hdl,
uint_t offset)
{
uint8_t name_length;
uint8_t pn_length;
int i, err = 0;
int vpd_str_id = 0;
int vpd_ro_desc;
int vpd_ro_pn_desc;
#ifndef _LITTLE_ENDIAN
uint32_t data32;
#endif
union {
uint32_t vpd_int[TAVOR_VPD_HDR_DWSIZE];
uchar_t vpd_char[TAVOR_VPD_HDR_BSIZE];
} vpd;
for (i = 0; i < TAVOR_VPD_HDR_DWSIZE; i++) {
err = tavor_pci_read_vpd(hdl, offset, i << 2, &vpd.vpd_int[i]);
if (err != DDI_SUCCESS) {
cmn_err(CE_NOTE, "!VPD read failed\n");
goto out;
}
}
#ifndef _LITTLE_ENDIAN
for (i = 0; i < TAVOR_VPD_HDR_DWSIZE; i++) {
data32 = vpd.vpd_int[i];
vpd.vpd_char[(i << 2) + 3] =
(uchar_t)((data32 & 0xFF000000) >> 24);
vpd.vpd_char[(i << 2) + 2] =
(uchar_t)((data32 & 0x00FF0000) >> 16);
vpd.vpd_char[(i << 2) + 1] =
(uchar_t)((data32 & 0x0000FF00) >> 8);
vpd.vpd_char[i << 2] = (uchar_t)(data32 & 0x000000FF);
}
#endif
if (vpd.vpd_char[vpd_str_id] == 0x82) {
name_length = (uint8_t)vpd.vpd_char[vpd_str_id + 1];
if (name_length > sizeof (state->ts_hca_name)) {
cmn_err(CE_NOTE, "!VPD name too large (0x%x)\n",
name_length);
goto out;
}
(void) memcpy(state->ts_hca_name, &vpd.vpd_char[vpd_str_id + 3],
name_length);
state->ts_hca_name[name_length] = 0;
vpd_ro_desc = name_length + 3;
vpd_ro_pn_desc = vpd_ro_desc + 3;
if (vpd.vpd_char[vpd_ro_desc] != 0x90 ||
(vpd.vpd_char[vpd_ro_pn_desc] != 'P' &&
vpd.vpd_char[vpd_ro_pn_desc + 1] != 'N')) {
cmn_err(CE_NOTE, "!VPD Part Number not found\n");
goto out;
}
pn_length = (uint8_t)vpd.vpd_char[vpd_ro_pn_desc + 2];
if (pn_length > sizeof (state->ts_hca_pn)) {
cmn_err(CE_NOTE, "!VPD part number too large (0x%x)\n",
name_length);
goto out;
}
(void) memcpy(state->ts_hca_pn,
&vpd.vpd_char[vpd_ro_pn_desc + 3],
pn_length);
state->ts_hca_pn[pn_length] = 0;
state->ts_hca_pn_len = pn_length;
} else {
cmn_err(CE_NOTE, "!VPD String ID Tag not found, tag: %02x\n",
vpd.vpd_char[0]);
goto out;
}
return;
out:
state->ts_hca_pn_len = 0;
}
static void
tavor_pci_capability_pcix(tavor_state_t *state, ddi_acc_handle_t hdl,
uint_t offset)
{
uint_t command, status;
int max_out_splt_trans, max_mem_rd_byte_cnt;
int designed_max_out_splt_trans, designed_max_mem_rd_byte_cnt;
command = pci_config_get16(hdl, offset + 2);
status = pci_config_get32(hdl, offset + 4);
designed_max_out_splt_trans = ((status >> 23) & 7);
max_out_splt_trans = ddi_prop_get_int(DDI_DEV_T_ANY, state->ts_dip,
DDI_PROP_DONTPASS, "pcix-max-outstanding-split-trans", -1);
if ((max_out_splt_trans != -1) &&
((max_out_splt_trans < 0) ||
(max_out_splt_trans > designed_max_out_splt_trans))) {
cmn_err(CE_NOTE, "!tavor%d: property \"pcix-max-outstanding-"
"split-trans\" (%d) invalid or exceeds device maximum"
" (%d), using default value (%d)\n", state->ts_instance,
max_out_splt_trans, designed_max_out_splt_trans,
state->ts_cfg_profile->cp_max_out_splt_trans);
max_out_splt_trans =
state->ts_cfg_profile->cp_max_out_splt_trans;
} else if (max_out_splt_trans == -1) {
max_out_splt_trans =
state->ts_cfg_profile->cp_max_out_splt_trans;
}
if (max_out_splt_trans >= 0) {
command = ((command & 0xFF8F) | max_out_splt_trans << 4);
}
designed_max_mem_rd_byte_cnt = ((status >> 21) & 3);
max_mem_rd_byte_cnt = ddi_prop_get_int(DDI_DEV_T_ANY, state->ts_dip,
DDI_PROP_DONTPASS, "pcix-max-read-byte-count", -1);
if ((max_mem_rd_byte_cnt != -1) &&
((max_mem_rd_byte_cnt < 0) ||
(max_mem_rd_byte_cnt > designed_max_mem_rd_byte_cnt))) {
cmn_err(CE_NOTE, "!tavor%d: property \"pcix-max-read-byte-"
"count\" (%d) invalid or exceeds device maximum"
" (%d), using default value (%d)\n", state->ts_instance,
max_mem_rd_byte_cnt, designed_max_mem_rd_byte_cnt,
state->ts_cfg_profile->cp_max_mem_rd_byte_cnt);
max_mem_rd_byte_cnt =
state->ts_cfg_profile->cp_max_mem_rd_byte_cnt;
} else if (max_mem_rd_byte_cnt == -1) {
max_mem_rd_byte_cnt =
state->ts_cfg_profile->cp_max_mem_rd_byte_cnt;
}
if (max_mem_rd_byte_cnt >= 0) {
command = ((command & 0xFFF3) | max_mem_rd_byte_cnt << 2);
}
pci_config_put16(hdl, offset + 2, command);
}
static int
tavor_intr_or_msi_init(tavor_state_t *state)
{
int status;
status = ddi_intr_get_supported_types(state->ts_dip,
&state->ts_intr_types_avail);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if ((state->ts_cfg_profile->cp_use_msi_if_avail != 0) &&
(state->ts_intr_types_avail & DDI_INTR_TYPE_MSI)) {
status = tavor_add_intrs(state, DDI_INTR_TYPE_MSI);
if (status == DDI_SUCCESS) {
state->ts_intr_type_chosen = DDI_INTR_TYPE_MSI;
return (DDI_SUCCESS);
}
}
if (state->ts_intr_types_avail & DDI_INTR_TYPE_FIXED) {
status = tavor_add_intrs(state, DDI_INTR_TYPE_FIXED);
if (status == DDI_SUCCESS) {
state->ts_intr_type_chosen = DDI_INTR_TYPE_FIXED;
return (DDI_SUCCESS);
}
}
return (DDI_FAILURE);
}
static int
tavor_add_intrs(tavor_state_t *state, int intr_type)
{
int status;
status = ddi_intr_get_nintrs(state->ts_dip, intr_type,
&state->ts_intrmsi_count);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
status = ddi_intr_get_navail(state->ts_dip, intr_type,
&state->ts_intrmsi_avail);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if ((state->ts_intrmsi_avail < 1) || (state->ts_intrmsi_count < 1)) {
return (DDI_FAILURE);
}
status = ddi_intr_alloc(state->ts_dip, &state->ts_intrmsi_hdl,
intr_type, 0, 1, &state->ts_intrmsi_allocd,
DDI_INTR_ALLOC_STRICT);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (state->ts_intrmsi_allocd < 1) {
return (DDI_FAILURE);
}
status = ddi_intr_get_pri(state->ts_intrmsi_hdl,
&state->ts_intrmsi_pri);
if (status != DDI_SUCCESS) {
(void) ddi_intr_free(state->ts_intrmsi_hdl);
return (DDI_FAILURE);
}
if (state->ts_intrmsi_pri >= ddi_intr_get_hilevel_pri()) {
(void) ddi_intr_free(state->ts_intrmsi_hdl);
return (DDI_FAILURE);
}
status = ddi_intr_get_cap(state->ts_intrmsi_hdl,
&state->ts_intrmsi_cap);
if (status != DDI_SUCCESS) {
(void) ddi_intr_free(state->ts_intrmsi_hdl);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
tavor_intr_or_msi_fini(tavor_state_t *state)
{
int status;
status = ddi_intr_free(state->ts_intrmsi_hdl);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
tavor_intr_disable(tavor_state_t *state)
{
ushort_t msi_ctrl = 0, caps_ctrl = 0;
ddi_acc_handle_t pci_cfg_hdl = state->ts_pci_cfghdl;
ASSERT(pci_cfg_hdl != NULL);
ASSERT(state->ts_intr_types_avail &
(DDI_INTR_TYPE_FIXED | DDI_INTR_TYPE_MSI));
if ((state->ts_cfg_profile->cp_use_msi_if_avail != 0) &&
(state->ts_intr_types_avail & DDI_INTR_TYPE_MSI)) {
if ((PCI_CAP_LOCATE(pci_cfg_hdl, PCI_CAP_ID_MSI,
&caps_ctrl) == DDI_SUCCESS)) {
if ((msi_ctrl = PCI_CAP_GET16(pci_cfg_hdl, 0,
caps_ctrl, PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
return (DDI_FAILURE);
}
ASSERT(msi_ctrl != 0);
if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
return (DDI_SUCCESS);
if (msi_ctrl & PCI_MSI_PVM_MASK) {
int offset = (msi_ctrl & PCI_MSI_64BIT_MASK) ?
PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
PCI_CAP_PUT32(pci_cfg_hdl, 0, caps_ctrl, offset, 0);
}
msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
PCI_CAP_PUT16(pci_cfg_hdl, 0, caps_ctrl, PCI_MSI_CTRL,
msi_ctrl);
} else {
uint16_t cmdreg = pci_config_get16(pci_cfg_hdl, PCI_CONF_COMM);
ASSERT(state->ts_intr_types_avail & DDI_INTR_TYPE_FIXED);
cmdreg |= PCI_COMM_INTX_DISABLE;
pci_config_put16(pci_cfg_hdl, PCI_CONF_COMM, cmdreg);
}
return (DDI_SUCCESS);
}
static int
tavor_quiesce(dev_info_t *dip)
{
tavor_state_t *state = ddi_get_soft_state(tavor_statep,
DEVI(dip)->devi_instance);
ASSERT(state != NULL);
state->ts_quiescing = B_TRUE;
if (!TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
return (DDI_SUCCESS);
}
if (tavor_hca_ports_shutdown(state,
state->ts_cfg_profile->cp_num_ports) != TAVOR_CMD_SUCCESS) {
state->ts_quiescing = B_FALSE;
return (DDI_FAILURE);
}
if (tavor_close_hca_cmd_post(state, TAVOR_CMD_NOSLEEP_SPIN) !=
TAVOR_CMD_SUCCESS) {
state->ts_quiescing = B_FALSE;
return (DDI_FAILURE);
}
if (tavor_sys_dis_cmd_post(state, TAVOR_CMD_NOSLEEP_SPIN) !=
TAVOR_CMD_SUCCESS) {
state->ts_quiescing = B_FALSE;
return (DDI_FAILURE);
}
if (tavor_intr_disable(state) != DDI_SUCCESS) {
state->ts_quiescing = B_FALSE;
return (DDI_FAILURE);
}
if (tavor_sw_reset(state) != DDI_SUCCESS) {
state->ts_quiescing = B_FALSE;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}