/* Handle unlinked interrupt QHs once they are gone from the hardware */ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) { bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); /* * Process all the QHs on the intr_unlink list that were added * before the current unlink cycle began. The list is in * temporal order, so stop when we reach the first entry in the * current cycle. But if the root hub isn't running then * process all the QHs on the list. */ ehci->intr_unlinking = true; while (!list_empty(&ehci->intr_unlink)) { struct ehci_qh *qh; qh = list_first_entry(&ehci->intr_unlink, struct ehci_qh, unlink_node); if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) break; list_del_init(&qh->unlink_node); end_unlink_intr(ehci, qh); } /* Handle remaining entries later */ if (!list_empty(&ehci->intr_unlink)) { ehci_enable_event(ehci, EHCI_HRTIMER_UNLINK_INTR, true); ++ehci->intr_unlink_cycle; } ehci->intr_unlinking = false; }
/* Start another free-iTDs/siTDs cycle */ static void start_free_itds(struct ehci_hcd *ehci) { if (!(ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_FREE_ITDS))) { ehci->last_itd_to_free = list_entry( ehci->cached_itd_list.prev, struct ehci_itd, itd_list); ehci->last_sitd_to_free = list_entry( ehci->cached_sitd_list.prev, struct ehci_sitd, sitd_list); ehci_enable_event(ehci, EHCI_HRTIMER_FREE_ITDS, true); }
/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ static void ehci_poll_ASS(struct ehci_hcd *ehci) { unsigned actual, want; /* Don't enable anything if the controller isn't running (e.g., died) */ if (ehci->rh_state != EHCI_RH_RUNNING) return; want = (ehci->command & CMD_ASE) ? STS_ASS : 0; actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS; if (want != actual) { /* Poll again later */ ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); ++ehci->ASS_poll_count; return; } if (ehci->ASS_poll_count > 20) ehci_dbg(ehci, "ASS poll count reached %d\n", ehci->ASS_poll_count); ehci->ASS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ if (ehci->async_count > 0) ehci_set_command_bit(ehci, CMD_ASE); } else { /* Running */ if (ehci->async_count == 0) { /* Turn off the schedule after a while */ ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC, true); } } }
/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ static void ehci_poll_PSS(struct ehci_hcd *ehci) { unsigned actual, want; /* Don't do anything if the controller isn't running (e.g., died) */ if (ehci->rh_state != EHCI_RH_RUNNING) return; want = (ehci->command & CMD_PSE) ? STS_PSS : 0; actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS; if (want != actual) { /* Poll again later, but give up after about 2-4 ms */ if (ehci->PSS_poll_count++ < 2) { ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); return; } ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n", want, actual); } ehci->PSS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ if (want == 0) { /* Stopped */ if (ehci->periodic_count > 0) ehci_set_command_bit(ehci, CMD_PSE); } else { /* Running */ if (ehci->periodic_count == 0) { /* Turn off the schedule after a while */ ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC, true); } } }
/* Poll the STS_HALT status bit; see when a dead controller stops */ static void ehci_handle_controller_death(struct ehci_hcd *ehci) { if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) { /* Give up after a few milliseconds */ if (ehci->died_poll_count++ < 5) { /* Try again later */ ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true); return; } ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n"); } /* Clean up the mess */ ehci->rh_state = EHCI_RH_HALTED; ehci_writel(ehci, 0, &ehci->regs->configured_flag); ehci_writel(ehci, 0, &ehci->regs->intr_enable); ehci_work(ehci); end_unlink_async(ehci); /* Not in process context, so don't try to reset the controller */ }