/* * hci1394_isr_arresp() * Process all responses that have come in off the bus and send then up to * the services layer. We send out a request on the bus (atreq) and some time * later a response comes in. We send this response up to the services * layer. */ static void hci1394_isr_arresp(hci1394_state_t *soft_state) { boolean_t response_available; int status; ASSERT(soft_state != NULL); TNF_PROBE_0_DEBUG(hci1394_isr_arresp_enter, HCI1394_TNF_HAL_STACK, ""); hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RSPKT); /* * Process all responses that have been received. If more responses * come in we will stay in interrupt handler and re-run this routine. * It is possible that we will call hci1394_async_arresp_process() * even though there are no more AR responses to process. This would * be because we have processed them earlier on. (i.e. we cleared * interrupt, then got another response and processed it. The interrupt * would still be pending. */ do { status = hci1394_async_arresp_process(soft_state->async, &response_available); if (status != DDI_SUCCESS) { TNF_PROBE_0(hci1394_isr_arresp_pr_fail, HCI1394_TNF_HAL_ERROR, ""); } } while (response_available == B_TRUE); TNF_PROBE_0_DEBUG(hci1394_isr_arresp_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr_arreq() * Process all requests that have come in off the bus and send then up to * the services layer. */ static void hci1394_isr_arreq(hci1394_state_t *soft_state) { boolean_t request_available; int status; ASSERT(soft_state != NULL); TNF_PROBE_0_DEBUG(hci1394_isr_arreq_enter, HCI1394_TNF_HAL_STACK, ""); hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RQPKT); /* * Process all requests that have been received. It is possible that we * will call hci1394_async_arreq_process() even though there are no * more requests to process. This would be because we have processed * them earlier on. (i.e. we cleared interrupt, got another request * and processed it. The interrupt would still be pending. */ do { status = hci1394_async_arreq_process(soft_state->async, &request_available); if (status != DDI_SUCCESS) { TNF_PROBE_0(hci1394_isr_arreq_pr_fail, HCI1394_TNF_HAL_ERROR, ""); } } while (request_available == B_TRUE); TNF_PROBE_0_DEBUG(hci1394_isr_arreq_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr_atreq_complete() * Process all completed requests that we have sent out (i.e. HW gave us * an ack). */ static void hci1394_isr_atreq_complete(hci1394_state_t *soft_state) { boolean_t request_available; int status; ASSERT(soft_state != NULL); TNF_PROBE_0_DEBUG(hci1394_isr_atreq_complete_enter, HCI1394_TNF_HAL_STACK, ""); hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_REQ_TX_CMPLT); /* * Processes all ack'd AT requests. If the request is pended, it is * considered complete relative the the atreq engine. AR response * processing will make sure we track the response. */ do { /* * Process a single request. Do not flush Q. That is only * done during bus reset processing. */ status = hci1394_async_atreq_process(soft_state->async, B_FALSE, &request_available); if (status != DDI_SUCCESS) { TNF_PROBE_0(hci1394_isr_atreq_complete_pr_fail, HCI1394_TNF_HAL_ERROR, ""); } } while (request_available == B_TRUE); TNF_PROBE_0_DEBUG(hci1394_isr_atreq_complete_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr_mask_setup() * Setup the initial interrupt mask for OpenHCI. These are the interrupts * that our interrupt handler is expected to handle. */ void hci1394_isr_mask_setup(hci1394_state_t *soft_state) { ASSERT(soft_state != NULL); TNF_PROBE_0_DEBUG(hci1394_isr_mask_setup_enter, HCI1394_TNF_HAL_STACK, ""); /* start off with all interrupts cleared/disabled */ 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); /* Setup Interrupt Mask Register */ 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)); TNF_PROBE_0_DEBUG(hci1394_isr_mask_setup_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr_atresp_complete() * Process all completed responses that we have sent out (i.e. HW gave us * an ack). We get in a request off the bus (arreq) and send it up to the * services layer, they send down a response to that request some time * later. This interrupt signifies that the HW is done with the response. * (i.e. it sent it out or failed it) */ static void hci1394_isr_atresp_complete(hci1394_state_t *soft_state) { boolean_t response_available; int status; ASSERT(soft_state != NULL); TNF_PROBE_0_DEBUG(hci1394_isr_atresp_complete_enter, HCI1394_TNF_HAL_STACK, ""); hci1394_ohci_intr_clear(soft_state->ohci, OHCI_INTR_RESP_TX_CMPLT); /* * Processes all ack'd AT responses It is possible that we will call * hci1394_async_atresp_process() even thought there are no more * responses to process. This would be because we have processed * them earlier on. (i.e. we cleared interrupt, then got another * response and processed it. The interrupt would still be pending. */ do { /* * Process a single response. Do not flush Q. That is only * done during bus reset processing. */ status = hci1394_async_atresp_process(soft_state->async, B_FALSE, &response_available); if (status != DDI_SUCCESS) { TNF_PROBE_0(hci1394_isr_atresp_complete_pr_fail, HCI1394_TNF_HAL_ERROR, ""); } } while (response_available == B_TRUE); TNF_PROBE_0_DEBUG(hci1394_isr_atresp_complete_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr_self_id() * Process the selfid complete interrupt. The bus reset has completed * and the 1394 HW has finished it's bus enumeration. The SW needs to * see what's changed and handle any hotplug conditions. */ 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); TNF_PROBE_0_DEBUG(hci1394_isr_self_id_enter, HCI1394_TNF_HAL_STACK, ""); soft_state->drvinfo.di_stats.st_selfid_count++; /* * check for the bizarre case that we got both a bus reset and self id * complete after checking for a bus reset */ if (hci1394_state(&soft_state->drvinfo) != HCI1394_BUS_RESET) { hci1394_isr_bus_reset(soft_state); } /* * Clear any set PHY error status bits set. The PHY status bits * may always be set (i.e. we removed cable power) so we do not want * to clear them when we handle the interrupt. We will clear them * every selfid complete interrupt so worst case we will get 1 PHY event * interrupt every bus reset. */ status = hci1394_ohci_phy_read(soft_state->ohci, 5, &phy_status); if (status != DDI_SUCCESS) { TNF_PROBE_0(hci1394_isr_self_id_pr_fail, HCI1394_TNF_HAL_ERROR, ""); } else { 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) { TNF_PROBE_0(hci1394_isr_self_id_pw_fail, HCI1394_TNF_HAL_ERROR, ""); } else { /* * Re-enable PHY interrupt. We disable the PHY interrupt * when we get one so that we do not get stuck in the * ISR. */ hci1394_ohci_intr_enable(soft_state->ohci, OHCI_INTR_PHY); } } /* See if either AT active bit is set */ if (hci1394_ohci_at_active(soft_state->ohci) == B_TRUE) { TNF_PROBE_1(hci1394_isr_self_id_as_fail, HCI1394_TNF_HAL_ERROR, "", tnf_string, errmsg, "AT ACTIVE still set"); saw_error = B_TRUE; } /* Clear busReset and selfIdComplete interrupts */ hci1394_ohci_intr_clear(soft_state->ohci, (OHCI_INTR_BUS_RESET | OHCI_INTR_SELFID_CMPLT)); /* Read node info and test for Invalid Node ID */ hci1394_ohci_nodeid_info(soft_state->ohci, &node_id, &nodeid_error); if (nodeid_error == B_TRUE) { TNF_PROBE_1(hci1394_isr_self_id_ni_fail, HCI1394_TNF_HAL_ERROR, "", tnf_string, errmsg, "saw invalid NodeID"); saw_error = B_TRUE; } /* Sync Selfid Buffer */ hci1394_ohci_selfid_sync(soft_state->ohci); /* store away selfid info */ hci1394_ohci_selfid_info(soft_state->ohci, &soft_state->drvinfo.di_gencnt, &selfid_size, &selfid_error); /* Test for selfid error */ if (selfid_error == B_TRUE) { TNF_PROBE_1(hci1394_isr_self_id_si_fail, HCI1394_TNF_HAL_ERROR, "", tnf_string, errmsg, "saw invalid SelfID"); saw_error = B_TRUE; } /* * selfid size could be 0 if a bus reset has occurred. If this occurs, * we should have another selfid int coming later. */ if ((saw_error == B_FALSE) && (selfid_size == 0)) { TNF_PROBE_0_DEBUG(hci1394_isr_self_id_exit, HCI1394_TNF_HAL_STACK, ""); return; } /* * make sure generation count in buffer matches generation * count in register. */ if (hci1394_ohci_selfid_buf_current(soft_state->ohci) == B_FALSE) { TNF_PROBE_0_DEBUG(hci1394_isr_self_id_exit, HCI1394_TNF_HAL_STACK, ""); return; } /* * Skip over first quadlet in selfid buffer, this is OpenHCI specific * data. */ selfid_size = selfid_size - IEEE1394_QUADLET; quadlet_count = selfid_size >> 2; /* Copy selfid buffer to Services Layer buffer */ for (index = 0; index < quadlet_count; index++) { hci1394_ohci_selfid_read(soft_state->ohci, index + 1, &soft_state->sl_selfid_buf[index]); } /* * Put our selfID info into the Services Layer's selfid buffer if we * have a 1394-1995 PHY. */ 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) { /* * If we fail reading from PHY, put invalid data into * the selfid buffer so the SL will reset the bus again. */ TNF_PROBE_0(hci1394_isr_self_id_pi_fail, HCI1394_TNF_HAL_ERROR, ""); selfid_buf_p[0] = 0xFFFFFFFF; selfid_buf_p[1] = 0xFFFFFFFF; } else { selfid_buf_p[1] = ~selfid_buf_p[0]; } selfid_size = selfid_size + 8; } /* Flush out async DMA Q's */ hci1394_async_flush(soft_state->async); /* * Make sure generation count is still valid. i.e. we have not gotten * another bus reset since the last time we checked. If we have gotten * another bus reset, we should have another selfid interrupt coming. */ if (soft_state->drvinfo.di_gencnt != hci1394_ohci_current_busgen(soft_state->ohci)) { TNF_PROBE_0_DEBUG(hci1394_isr_self_id_exit, HCI1394_TNF_HAL_STACK, ""); return; } /* * do whatever CSR register processing that needs to be done. */ hci1394_csr_bus_reset(soft_state->csr); /* * do whatever management may be necessary for the CYCLE_LOST and * CYCLE_INCONSISTENT interrupts. */ hci1394_isoch_error_ints_enable(soft_state); /* * See if we saw an error. If we did, tell the services layer that we * finished selfid processing and give them an illegal selfid buffer * size of 0. The Services Layer will try to reset the bus again to * see if we can recover from this problem. It will threshold after * a finite number of errors. */ 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); /* * Take ourself out of Bus Reset processing mode * * Set the driver state to normal. If we cannot, we have been * shutdown. The only way we can get in this code is if we have * a multi-processor machine and the HAL is shutdown by one * processor running in base context while this interrupt * handler runs in another processor. We will disable all * interrupts and just return. We shouldn't have to disable * the interrupts, but we will just in case. */ 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) { /* * Notify services layer about self-id-complete. Don't notify * the services layer if there are too many devices on the bus. */ h1394_self_ids(soft_state->drvinfo.di_sl_private, soft_state->sl_selfid_buf, selfid_size, node_id, soft_state->drvinfo.di_gencnt); /* * Take ourself out of Bus Reset processing mode * * Set the driver state to normal. If we cannot, we have been * shutdown. The only way we can get in this code is if we have * a multi-processor machine and the HAL is shutdown by one * processor running in base context while this interrupt * handler runs in another processor. We will disable all * interrupts and just return. We shouldn't have to disable * the interrupts, but we will just in case. */ 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); } /* enable bus reset interrupt */ hci1394_ohci_intr_enable(soft_state->ohci, OHCI_INTR_BUS_RESET); TNF_PROBE_0_DEBUG(hci1394_isr_self_id_exit, HCI1394_TNF_HAL_STACK, ""); }
/* * hci1394_isr() * Core interrupt handler. Every interrupt enabled in * hci1394_isr_mask_setup() should be covered here. There may be other * interrupts supported in here even if they are not initially enabled * (like OHCI_INTR_CYC_64_SECS) since they may be enabled later (i.e. due to * CSR register write) */ 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); TNF_PROBE_0_DEBUG(hci1394_isr_enter, HCI1394_TNF_HAL_STACK, ""); /* * Get all of the enabled 1394 interrupts which are currently * asserted. */ interrupt_event = hci1394_ohci_intr_asserted(soft_state->ohci); do { /* handle the asserted interrupts */ 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); /* clear cycle master bit in csr state register */ 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; } /* * Check for self-id-complete interrupt disappearing. There is * a chance in OpenHCI where it will assert the selfid * interrupt and then take it away. We will look for this case * and claim it just in case. We could possibly claim an * interrupt that's not ours. We would have to be in the * middle of a bus reset and a bunch of other weird stuff * would have to align. It should not hurt anything if we do. * * This will very very rarely happen, if ever. We still have * to handle the case, just in case. OpenHCI 1.1 should fix * this problem. */ 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)) { TNF_PROBE_0(hci1394_isr_busgen_claim, HCI1394_TNF_HAL, ""); status = DDI_INTR_CLAIMED; } } /* * See if any of the enabled 1394 interrupts have been asserted * since we first checked. */ interrupt_event = hci1394_ohci_intr_asserted( soft_state->ohci); } while (interrupt_event != 0); TNF_PROBE_0_DEBUG(hci1394_isr_exit, HCI1394_TNF_HAL_STACK, ""); return (status); }
/* * hci1394_isoch_cycle_inconsistent() * Called during interrupt notification to indicate that the cycle time * has changed unexpectedly. We need to take this opportunity to * update our tracking of each running transmit context's execution. * cycle_inconsistent only affects transmit, so recv contexts are left alone. */ void hci1394_isoch_cycle_inconsistent(hci1394_state_t *soft_statep) { int i, cnt_thresh; boolean_t note; hrtime_t current_time, last_time, delta, delta_thresh; hci1394_iso_ctxt_t *ctxtp; /* current context */ ASSERT(soft_statep); TNF_PROBE_0_DEBUG(hci1394_isoch_cycle_inconsistent_enter, HCI1394_TNF_HAL_STACK_ISOCH, ""); hci1394_ohci_intr_clear(soft_statep->ohci, OHCI_INTR_CYC_INCONSISTENT); /* grab the mutex before checking each context's INUSE and RUNNING */ mutex_enter(&soft_statep->isoch->ctxt_list_mutex); /* check for transmit contexts which are inuse and running */ for (i = 0; i < soft_statep->isoch->ctxt_xmit_count; i++) { ctxtp = &soft_statep->isoch->ctxt_xmit[i]; if ((ctxtp->ctxt_flags & (HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) { mutex_exit(&soft_statep->isoch->ctxt_list_mutex); hci1394_ixl_interrupt(soft_statep, ctxtp, B_FALSE); mutex_enter(&soft_statep->isoch->ctxt_list_mutex); } } /* * get the current time and calculate the delta between now and * when the last interrupt was processed. (NOTE: if the time * returned by gethrtime() rolls-over while we are counting these * interrupts, we will incorrectly restart the counting process. * However, because the probability of this happening is small and * not catching the roll-over will AT MOST double the time it takes * us to discover and correct from this condition, we can safely * ignore it.) */ current_time = gethrtime(); last_time = soft_statep->isoch->cycle_incon_thresh.last_intr_time; delta = current_time - last_time; /* * compare the calculated delta to the delta T threshold. If it * is less than the threshold, then increment the counter. If it * is not then reset the counter. */ delta_thresh = soft_statep->isoch->cycle_incon_thresh.delta_t_thresh; if (delta < delta_thresh) soft_statep->isoch->cycle_incon_thresh.delta_t_counter++; else soft_statep->isoch->cycle_incon_thresh.delta_t_counter = 0; /* * compare the counter to the counter threshold. If it is greater, * then disable the cycle inconsistent interrupt. */ cnt_thresh = soft_statep->isoch->cycle_incon_thresh.counter_thresh; note = B_FALSE; if (soft_statep->isoch->cycle_incon_thresh.delta_t_counter > cnt_thresh) { hci1394_ohci_intr_disable(soft_statep->ohci, OHCI_INTR_CYC_INCONSISTENT); note = B_TRUE; } /* save away the current time into the last_intr_time field */ soft_statep->isoch->cycle_incon_thresh.last_intr_time = current_time; mutex_exit(&soft_statep->isoch->ctxt_list_mutex); if (note == B_TRUE) { cmn_err(CE_NOTE, "!hci1394(%d): cycle_inconsistent interrupt " "disabled until next bus reset", soft_statep->drvinfo.di_instance); TNF_PROBE_1(hci1394_isoch_cycle_inconsistent_error, HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, msg, "CYCLE_INCONSISTENT intr disabled until next bus reset"); } TNF_PROBE_0_DEBUG(hci1394_isoch_cycle_inconsistent_exit, HCI1394_TNF_HAL_STACK_ISOCH, ""); }