#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/1394/h1394.h>
#include <sys/1394/adapters/hci1394.h>
static uint_t hci1394_isr(caddr_t parm);
static void hci1394_isr_bus_reset(hci1394_state_t *soft_state);
static void hci1394_isr_self_id(hci1394_state_t *soft_state);
static void hci1394_isr_isoch_ir(hci1394_state_t *soft_state);
static void hci1394_isr_isoch_it(hci1394_state_t *soft_state);
static void hci1394_isr_atreq_complete(hci1394_state_t *soft_state);
static void hci1394_isr_arresp(hci1394_state_t *soft_state);
static void hci1394_isr_arreq(hci1394_state_t *soft_state);
static void hci1394_isr_atresp_complete(hci1394_state_t *soft_state);
int
hci1394_isr_init(hci1394_state_t *soft_state)
{
int status;
ASSERT(soft_state != NULL);
status = ddi_intr_hilevel(soft_state->drvinfo.di_dip, 0);
if (status != 0) {
return (DDI_FAILURE);
}
status = ddi_get_iblock_cookie(soft_state->drvinfo.di_dip, 0,
&soft_state->drvinfo.di_iblock_cookie);
if (status != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
hci1394_isr_fini(hci1394_state_t *soft_state)
{
ASSERT(soft_state != NULL);
}
int
hci1394_isr_handler_init(hci1394_state_t *soft_state)
{
int status;
ASSERT(soft_state != NULL);
status = ddi_add_intr(soft_state->drvinfo.di_dip, 0, NULL, NULL,
hci1394_isr, (caddr_t)soft_state);
return (status);
}
void
hci1394_isr_handler_fini(hci1394_state_t *soft_state)
{
ASSERT(soft_state != NULL);
ddi_remove_intr(soft_state->drvinfo.di_dip, 0,
soft_state->drvinfo.di_iblock_cookie);
}
void
hci1394_isr_mask_setup(hci1394_state_t *soft_state)
{
ASSERT(soft_state != NULL);
hci1394_ohci_ir_intr_disable(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_ir_intr_clear(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_it_intr_disable(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_it_intr_clear(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_intr_disable(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_intr_clear(soft_state->ohci, 0xFFFFFFFF);
hci1394_ohci_intr_enable(soft_state->ohci,
(OHCI_INTR_UNRECOVERABLE_ERR | OHCI_INTR_CYC_TOO_LONG |
OHCI_INTR_BUS_RESET | OHCI_INTR_SELFID_CMPLT |
OHCI_INTR_REQ_TX_CMPLT | OHCI_INTR_RESP_TX_CMPLT |
OHCI_INTR_RQPKT | OHCI_INTR_RSPKT | OHCI_INTR_ISOCH_TX |
OHCI_INTR_ISOCH_RX | OHCI_INTR_POST_WR_ERR | OHCI_INTR_PHY |
OHCI_INTR_LOCK_RESP_ERR));
}
static uint_t
hci1394_isr(caddr_t parm)
{
hci1394_state_t *soft_state;
h1394_posted_wr_err_t posted_wr_err;
uint32_t interrupt_event;
uint_t status;
status = DDI_INTR_UNCLAIMED;
soft_state = (hci1394_state_t *)parm;
ASSERT(soft_state != NULL);
if (hci1394_state(&soft_state->drvinfo) == HCI1394_SHUTDOWN)
return (DDI_INTR_UNCLAIMED);
interrupt_event = hci1394_ohci_intr_asserted(soft_state->ohci);
do {
if (interrupt_event & OHCI_INTR_BUS_RESET) {
hci1394_isr_bus_reset(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_SELFID_CMPLT) {
hci1394_isr_self_id(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_ISOCH_TX) {
hci1394_isr_isoch_it(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_ISOCH_RX) {
hci1394_isr_isoch_ir(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_REQ_TX_CMPLT) {
hci1394_isr_atreq_complete(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_RSPKT) {
hci1394_isr_arresp(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_RQPKT) {
hci1394_isr_arreq(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_RESP_TX_CMPLT) {
hci1394_isr_atresp_complete(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_CYC_64_SECS) {
hci1394_ohci_isr_cycle64seconds(soft_state->ohci);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_UNRECOVERABLE_ERR) {
h1394_error_detected(soft_state->drvinfo.di_sl_private,
H1394_SELF_INITIATED_SHUTDOWN, NULL);
cmn_err(CE_WARN, "hci1394(%d): driver shutdown: "
"unrecoverable error interrupt detected",
soft_state->drvinfo.di_instance);
hci1394_shutdown(soft_state->drvinfo.di_dip);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_CYC_LOST) {
hci1394_isoch_cycle_lost(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_CYC_INCONSISTENT) {
hci1394_isoch_cycle_inconsistent(soft_state);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_CYC_TOO_LONG) {
hci1394_ohci_intr_clear(soft_state->ohci,
OHCI_INTR_CYC_TOO_LONG);
hci1394_csr_state_bclr(soft_state->csr,
IEEE1394_CSR_STATE_CMSTR);
h1394_error_detected(soft_state->drvinfo.di_sl_private,
H1394_CYCLE_TOO_LONG, NULL);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_POST_WR_ERR) {
hci1394_ohci_postwr_addr(soft_state->ohci,
&posted_wr_err.addr);
h1394_error_detected(soft_state->drvinfo.di_sl_private,
H1394_POSTED_WR_ERR, &posted_wr_err);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_PHY) {
hci1394_ohci_isr_phy(soft_state->ohci);
status = DDI_INTR_CLAIMED;
}
if (interrupt_event & OHCI_INTR_LOCK_RESP_ERR) {
hci1394_ohci_intr_clear(soft_state->ohci,
OHCI_INTR_LOCK_RESP_ERR);
h1394_error_detected(soft_state->drvinfo.di_sl_private,
H1394_LOCK_RESP_ERR, NULL);
status = DDI_INTR_CLAIMED;
}
if ((status == DDI_INTR_UNCLAIMED) &&
(hci1394_state(&soft_state->drvinfo) ==
HCI1394_BUS_RESET)) {
if (soft_state->drvinfo.di_gencnt !=
hci1394_ohci_current_busgen(soft_state->ohci)) {
status = DDI_INTR_CLAIMED;
}
}
interrupt_event = hci1394_ohci_intr_asserted(
soft_state->ohci);
} while (interrupt_event != 0);
return (status);
}
static void
hci1394_isr_bus_reset(hci1394_state_t *soft_state)
{
int status;
ASSERT(soft_state != NULL);
status = hci1394_state_set(&soft_state->drvinfo, HCI1394_BUS_RESET);
if (status != DDI_SUCCESS) {
hci1394_ohci_intr_master_disable(soft_state->ohci);
return;
}
soft_state->drvinfo.di_gencnt =
hci1394_ohci_current_busgen(soft_state->ohci);
soft_state->drvinfo.di_stats.st_bus_reset_count++;
hci1394_ohci_intr_disable(soft_state->ohci, OHCI_INTR_BUS_RESET);
hci1394_async_atreq_reset(soft_state->async);
hci1394_async_atresp_reset(soft_state->async);
h1394_bus_reset(soft_state->drvinfo.di_sl_private,
(void **)&soft_state->sl_selfid_buf);
}
static void
hci1394_isr_self_id(hci1394_state_t *soft_state)
{
int status;
uint_t node_id;
uint_t selfid_size;
uint_t quadlet_count;
uint_t index;
uint32_t *selfid_buf_p;
boolean_t selfid_error;
boolean_t nodeid_error;
boolean_t saw_error = B_FALSE;
uint_t phy_status;
ASSERT(soft_state != NULL);
soft_state->drvinfo.di_stats.st_selfid_count++;
if (hci1394_state(&soft_state->drvinfo) != HCI1394_BUS_RESET) {
hci1394_isr_bus_reset(soft_state);
}
status = hci1394_ohci_phy_read(soft_state->ohci, 5, &phy_status);
if (status == DDI_SUCCESS) {
phy_status |= OHCI_PHY_LOOP_ERR | OHCI_PHY_PWRFAIL_ERR |
OHCI_PHY_TIMEOUT_ERR | OHCI_PHY_PORTEVT_ERR;
status = hci1394_ohci_phy_write(soft_state->ohci, 5,
phy_status);
if (status == DDI_SUCCESS) {
hci1394_ohci_intr_enable(soft_state->ohci,
OHCI_INTR_PHY);
}
}
if (hci1394_ohci_at_active(soft_state->ohci) == B_TRUE) {
saw_error = B_TRUE;
}
hci1394_ohci_intr_clear(soft_state->ohci, (OHCI_INTR_BUS_RESET |
OHCI_INTR_SELFID_CMPLT));
hci1394_ohci_nodeid_info(soft_state->ohci, &node_id, &nodeid_error);
if (nodeid_error == B_TRUE) {
saw_error = B_TRUE;
}
hci1394_ohci_selfid_sync(soft_state->ohci);
hci1394_ohci_selfid_info(soft_state->ohci,
&soft_state->drvinfo.di_gencnt, &selfid_size, &selfid_error);
if (selfid_error == B_TRUE) {
saw_error = B_TRUE;
}
if ((saw_error == B_FALSE) && (selfid_size == 0)) {
return;
}
if (hci1394_ohci_selfid_buf_current(soft_state->ohci) == B_FALSE) {
return;
}
selfid_size = selfid_size - IEEE1394_QUADLET;
quadlet_count = selfid_size >> 2;
for (index = 0; index < quadlet_count; index++) {
hci1394_ohci_selfid_read(soft_state->ohci, index + 1,
&soft_state->sl_selfid_buf[index]);
}
if (soft_state->halinfo.phy == H1394_PHY_1995) {
selfid_buf_p = (uint32_t *)(
(uintptr_t)soft_state->sl_selfid_buf +
(uintptr_t)selfid_size);
status = hci1394_ohci_phy_info(soft_state->ohci,
&selfid_buf_p[0]);
if (status != DDI_SUCCESS) {
selfid_buf_p[0] = 0xFFFFFFFF;
selfid_buf_p[1] = 0xFFFFFFFF;
} else {
selfid_buf_p[1] = ~selfid_buf_p[0];
}
selfid_size = selfid_size + 8;
}
hci1394_async_flush(soft_state->async);
if (soft_state->drvinfo.di_gencnt !=
hci1394_ohci_current_busgen(soft_state->ohci)) {
return;
}
hci1394_csr_bus_reset(soft_state->csr);
hci1394_isoch_error_ints_enable(soft_state);
if (saw_error == B_TRUE) {
h1394_self_ids(soft_state->drvinfo.di_sl_private,
soft_state->sl_selfid_buf, 0, node_id,
soft_state->drvinfo.di_gencnt);
status = hci1394_state_set(&soft_state->drvinfo,
HCI1394_NORMAL);
if (status != DDI_SUCCESS) {
hci1394_ohci_intr_master_disable(soft_state->ohci);
return;
}
} else if (IEEE1394_NODE_NUM(node_id) != 63) {
h1394_self_ids(soft_state->drvinfo.di_sl_private,
soft_state->sl_selfid_buf, selfid_size,
node_id, soft_state->drvinfo.di_gencnt);
status = hci1394_state_set(&soft_state->drvinfo,
HCI1394_NORMAL);
if (status != DDI_SUCCESS) {
hci1394_ohci_intr_master_disable(soft_state->ohci);
return;
}
} else {
cmn_err(CE_NOTE, "hci1394(%d): Too many devices on the 1394 "
"bus", soft_state->drvinfo.di_instance);
}
hci1394_ohci_intr_enable(soft_state->ohci, OHCI_INTR_BUS_RESET);
}
static void
hci1394_isr_isoch_ir(hci1394_state_t *soft_state)
{
uint32_t i;
uint32_t mask = 0x00000001;
uint32_t ev;
int num_ir_contexts;
hci1394_iso_ctxt_t *ctxtp;
ASSERT(soft_state != NULL);
num_ir_contexts = hci1394_isoch_recv_count_get(soft_state->isoch);
while ((ev = hci1394_ohci_ir_intr_asserted(soft_state->ohci)) != 0) {
hci1394_ohci_ir_intr_clear(soft_state->ohci, ev);
for (i = 0; i < num_ir_contexts; i++) {
if (ev & mask) {
ctxtp = hci1394_isoch_recv_ctxt_get(
soft_state->isoch, i);
hci1394_ixl_interrupt(soft_state, ctxtp,
B_FALSE);
}
mask <<= 1;
}
}
}
static void
hci1394_isr_isoch_it(hci1394_state_t *soft_state)
{
uint32_t i;
uint32_t mask = 0x00000001;
uint32_t ev;
int num_it_contexts;
hci1394_iso_ctxt_t *ctxtp;
ASSERT(soft_state != NULL);
num_it_contexts = hci1394_isoch_xmit_count_get(soft_state->isoch);
while ((ev = hci1394_ohci_it_intr_asserted(soft_state->ohci)) != 0) {
hci1394_ohci_it_intr_clear(soft_state->ohci, ev);
for (i = 0; i < num_it_contexts; i++) {
if (ev & mask) {
ctxtp = hci1394_isoch_xmit_ctxt_get(
soft_state->isoch, i);
hci1394_ixl_interrupt(soft_state, ctxtp,
B_FALSE);
}
mask <<= 1;
}
}
}
static void
hci1394_isr_atreq_complete(hci1394_state_t *soft_state)
{
boolean_t request_available;
ASSERT(soft_state != NULL);
hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_REQ_TX_CMPLT);
do {
(void) hci1394_async_atreq_process(soft_state->async, B_FALSE,
&request_available);
} while (request_available == B_TRUE);
}
static void
hci1394_isr_arresp(hci1394_state_t *soft_state)
{
boolean_t response_available;
ASSERT(soft_state != NULL);
hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RSPKT);
do {
(void) hci1394_async_arresp_process(soft_state->async,
&response_available);
} while (response_available == B_TRUE);
}
static void
hci1394_isr_arreq(hci1394_state_t *soft_state)
{
boolean_t request_available;
ASSERT(soft_state != NULL);
hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RQPKT);
do {
(void) hci1394_async_arreq_process(soft_state->async,
&request_available);
} while (request_available == B_TRUE);
}
static void
hci1394_isr_atresp_complete(hci1394_state_t *soft_state)
{
boolean_t response_available;
ASSERT(soft_state != NULL);
hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RESP_TX_CMPLT);
do {
(void) hci1394_async_atresp_process(soft_state->async,
B_FALSE, &response_available);
} while (response_available == B_TRUE);
}