#include "bnxint.h"
#include "bnxsnd.h"
#include "bnxrcv.h"
#define BNX_INTR_NUMBER 0
static lm_interrupt_status_t
bnx_intr_priv(um_device_t *const umdevice)
{
u32_t idx;
lm_device_t *lmdevice;
lm_interrupt_status_t intrstat;
lmdevice = &(umdevice->lm_dev);
intrstat = lm_get_interrupt_status(lmdevice);
if (intrstat & LM_KNOCK_KNOCK_EVENT) {
um_send_driver_pulse(umdevice);
}
if (intrstat & LM_RX_EVENT_MASK) {
for (idx = RX_CHAIN_IDX0; idx < NUM_RX_CHAIN; idx++) {
if (intrstat & (LM_RX0_EVENT_ACTIVE << idx)) {
s_list_t *waitq;
waitq = &(_RX_QINFO(umdevice, idx).waitq);
mutex_enter(&umdevice->os_param.rcv_mutex);
(void) lm_get_packets_rcvd(lmdevice, idx, 0,
waitq);
mutex_exit(&umdevice->os_param.rcv_mutex);
}
}
}
if (intrstat & LM_TX_EVENT_MASK) {
for (idx = TX_CHAIN_IDX0; idx < NUM_TX_CHAIN; idx++) {
if (intrstat & (LM_TX0_EVENT_ACTIVE << idx)) {
bnx_xmit_ring_intr(umdevice, idx);
}
}
}
if (intrstat & LM_PHY_EVENT_ACTIVE) {
mutex_enter(&umdevice->os_param.phy_mutex);
lm_service_phy_int(lmdevice, FALSE);
mutex_exit(&umdevice->os_param.phy_mutex);
}
return (intrstat);
}
static void
bnx_intr_recv(um_device_t * const umdevice)
{
mutex_enter(&umdevice->os_param.rcv_mutex);
if (umdevice->intr_enabled == B_TRUE) {
bnx_rxpkts_intr(umdevice);
}
if (umdevice->intr_enabled == B_TRUE) {
bnx_rxpkts_post(umdevice);
}
mutex_exit(&umdevice->os_param.rcv_mutex);
}
static void
bnx_intr_xmit(um_device_t *const umdevice)
{
mutex_enter(&umdevice->os_param.xmit_mutex);
if (umdevice->intr_enabled == B_TRUE) {
bnx_txpkts_intr(umdevice);
}
mutex_exit(&umdevice->os_param.xmit_mutex);
}
static unsigned int
bnx_intr_1lvl(caddr_t arg1, caddr_t arg2)
{
lm_device_t *lmdevice;
um_device_t *umdevice;
lm_interrupt_status_t intrstat = 0;
u32_t value32;
umdevice = (um_device_t *)arg1;
lmdevice = &(umdevice->lm_dev);
mutex_enter(&umdevice->intr_mutex);
if (umdevice->intr_enabled != B_TRUE) {
mutex_exit(&umdevice->intr_mutex);
umdevice->intr_in_disabled++;
return (DDI_INTR_UNCLAIMED);
}
(void) ddi_dma_sync(*(umdevice->os_param.status_block_dma_hdl), 0,
STATUS_BLOCK_BUFFER_SIZE, DDI_DMA_SYNC_FORKERNEL);
if (lmdevice->vars.status_virt->deflt.status_idx ==
umdevice->dev_var.processed_status_idx) {
REG_RD(lmdevice, pci_config.pcicfg_misc_status, &value32);
if (value32 & PCICFG_MISC_STATUS_INTA_VALUE) {
umdevice->intr_no_change++;
mutex_exit(&umdevice->intr_mutex);
return (DDI_INTR_UNCLAIMED);
}
}
umdevice->intrFired++;
REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd,
(PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
PCICFG_INT_ACK_CMD_MASK_INT));
FLUSHPOSTEDWRITES(lmdevice);
umdevice->dev_var.processed_status_idx =
lmdevice->vars.status_virt->deflt.status_idx;
intrstat = bnx_intr_priv(umdevice);
value32 = umdevice->dev_var.processed_status_idx;
value32 |= PCICFG_INT_ACK_CMD_INDEX_VALID;
REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd, value32);
FLUSHPOSTEDWRITES(lmdevice);
umdevice->intr_count++;
if (intrstat & LM_RX_EVENT_MASK) {
bnx_intr_recv(umdevice);
}
if (intrstat & LM_TX_EVENT_MASK) {
bnx_intr_xmit(umdevice);
}
mutex_exit(&umdevice->intr_mutex);
return (DDI_INTR_CLAIMED);
}
void
bnx_intr_enable(um_device_t * const umdevice)
{
int rc;
umdevice->intr_count = 0;
umdevice->intr_enabled = B_TRUE;
if ((rc = ddi_intr_enable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: Failed to enable default isr block (%d)",
umdevice->dev_name, rc);
return;
}
lm_enable_int(&(umdevice->lm_dev));
FLUSHPOSTEDWRITES(&(umdevice->lm_dev));
drv_usecwait(2000000);
}
static void
bnx_intr_wait(um_device_t * const umdevice)
{
if (mutex_tryenter(&umdevice->intr_mutex)) {
mutex_enter(&umdevice->os_param.rcv_mutex);
mutex_exit(&umdevice->os_param.rcv_mutex);
} else {
mutex_enter(&umdevice->intr_mutex);
}
mutex_exit(&umdevice->intr_mutex);
}
void
bnx_intr_disable(um_device_t * const umdevice)
{
int rc;
umdevice->intr_enabled = B_FALSE;
bnx_intr_wait(umdevice);
lm_disable_int(&(umdevice->lm_dev));
FLUSHPOSTEDWRITES(&(umdevice->lm_dev));
if ((rc = ddi_intr_disable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: Failed to disable default isr (%d)",
umdevice->dev_name, rc);
}
}
int
bnxIntrInit(um_device_t *umdevice)
{
dev_info_t *pDev = umdevice->os_param.dip;
int intrActual, rc;
if ((umdevice->pIntrBlock = kmem_zalloc(sizeof (ddi_intr_handle_t),
KM_SLEEP)) == NULL) {
cmn_err(CE_WARN, "%s: Failed to allocate interrupt handle "
"block!", umdevice->dev_name);
return (-1);
}
umdevice->intrType = (umdevice->dev_var.disableMsix) ?
DDI_INTR_TYPE_FIXED : DDI_INTR_TYPE_MSIX;
while (1) {
if ((rc = ddi_intr_alloc(pDev, umdevice->pIntrBlock,
umdevice->intrType, 0, 1, &intrActual,
DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
cmn_err(CE_WARN, "!%s: Failed to initialize default "
"%s isr handle block (%d)", umdevice->dev_name,
(umdevice->intrType == DDI_INTR_TYPE_MSIX) ?
"MSIX" : "Fixed", rc);
if (umdevice->intrType == DDI_INTR_TYPE_MSIX) {
cmn_err(CE_WARN, "!%s: Reverting to Fixed "
"level interrupts", umdevice->dev_name);
umdevice->intrType = DDI_INTR_TYPE_FIXED;
continue;
} else {
kmem_free(umdevice->pIntrBlock,
sizeof (ddi_intr_handle_t));
return (-1);
}
}
break;
}
if (intrActual != 1) {
cmn_err(CE_WARN, "%s: Failed to alloc minimum default "
"isr handler!", umdevice->dev_name);
(void) ddi_intr_free(umdevice->pIntrBlock[0]);
kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
return (-1);
}
if ((rc = ddi_intr_get_pri(umdevice->pIntrBlock[0],
&umdevice->intrPriority)) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: Failed to get isr priority (%d)",
umdevice->dev_name, rc);
(void) ddi_intr_free(umdevice->pIntrBlock[0]);
kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
return (-1);
}
if (umdevice->intrPriority >= ddi_intr_get_hilevel_pri()) {
cmn_err(CE_WARN, "%s: Interrupt priority is too high",
umdevice->dev_name);
(void) ddi_intr_free(umdevice->pIntrBlock[0]);
kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
return (-1);
}
if ((rc = ddi_intr_add_handler(umdevice->pIntrBlock[0], bnx_intr_1lvl,
(caddr_t)umdevice, NULL)) != DDI_SUCCESS) {
cmn_err(CE_WARN, "%s: Failed to add the default isr "
"handler (%d)", umdevice->dev_name, rc);
(void) ddi_intr_free(umdevice->pIntrBlock[0]);
kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
return (-1);
}
mutex_init(&umdevice->intr_mutex, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(umdevice->intrPriority));
umdevice->lm_dev.vars.interrupt_mode =
(umdevice->intrType == DDI_INTR_TYPE_FIXED) ?
IRQ_MODE_LINE_BASED : IRQ_MODE_MSIX_BASED;
return (0);
}
void
bnxIntrFini(um_device_t *umdevice)
{
int ret;
if ((ret = ddi_intr_disable(umdevice->pIntrBlock[0])) != 0) {
dev_err(umdevice->os_param.dip, CE_WARN,
"failed to disable interrupt: %d", ret);
}
if ((ret = ddi_intr_remove_handler(umdevice->pIntrBlock[0])) != 0) {
dev_err(umdevice->os_param.dip, CE_WARN,
"failed to remove interrupt: %d", ret);
}
if ((ret = ddi_intr_free(umdevice->pIntrBlock[0])) != 0) {
dev_err(umdevice->os_param.dip, CE_WARN,
"failed to free interrupt: %d", ret);
}
kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
umdevice->pIntrBlock = NULL;
mutex_destroy(&umdevice->intr_mutex);
}