Changeset 19f0048 in mainline for uspace/drv/bus/usb/xhci/hc.c


Ignore:
Timestamp:
2018-02-01T02:13:34Z (6 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
17d34a8
Parents:
53fdf8c
Message:

xhci: reinitialize in case of HC error

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/drv/bus/usb/xhci/hc.c

    r53fdf8c r19f0048  
    259259int hc_init_memory(xhci_hc_t *hc, ddf_dev_t *device)
    260260{
    261         int err;
     261        int err = ENOMEM;
    262262
    263263        if (dma_buffer_alloc(&hc->dcbaa_dma, (1 + hc->max_slots) * sizeof(uint64_t)))
     
    265265        hc->dcbaa = hc->dcbaa_dma.virt;
    266266
     267        hc->event_worker = joinable_fibril_create(&event_worker, hc);
     268        if (!hc->event_worker)
     269                goto err_dcbaa;
     270
    267271        if ((err = xhci_event_ring_init(&hc->event_ring, 1)))
    268                 goto err_dcbaa;
     272                goto err_worker;
    269273
    270274        if ((err = xhci_scratchpad_alloc(hc)))
     
    277281                goto err_cmd;
    278282
    279         hc->event_worker = joinable_fibril_create(&event_worker, hc);
    280         if (!hc->event_worker)
    281                 goto err_bus;
    282 
    283283        xhci_sw_ring_init(&hc->sw_ring, PAGE_SIZE / sizeof(xhci_trb_t));
    284284
    285         joinable_fibril_start(hc->event_worker);
    286 
    287285        return EOK;
    288286
    289 err_bus:
    290         xhci_bus_fini(&hc->bus);
    291287err_cmd:
    292288        xhci_fini_commands(hc);
     
    295291err_event_ring:
    296292        xhci_event_ring_fini(&hc->event_ring);
     293err_worker:
     294        joinable_fibril_destroy(hc->event_worker);
    297295err_dcbaa:
    298296        hc->dcbaa = NULL;
     
    468466 * Initialize the HC: section 4.2
    469467 */
    470 int hc_start(xhci_hc_t *hc, bool irq)
     468int hc_start(xhci_hc_t *hc)
    471469{
    472470        int err;
     
    489487
    490488        XHCI_REG_SET(hc->op_regs, XHCI_OP_EWE, 1);
     489
     490        xhci_event_ring_reset(&hc->event_ring);
    491491
    492492        xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
     
    499499        XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
    500500
    501         if (irq) {
     501        if (hc->base.irq_cap > 0) {
    502502                XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
    503503                XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
     
    506506        XHCI_REG_SET(hc->op_regs, XHCI_OP_HSEE, 1);
    507507
     508        xhci_sw_ring_restart(&hc->sw_ring);
     509        joinable_fibril_start(hc->event_worker);
     510
     511        xhci_start_command_ring(hc);
     512
    508513        XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
    509514
    510         xhci_rh_startup(&hc->rh);
     515        /* RH needs to access port states on startup */
     516        xhci_rh_start(&hc->rh);
    511517
    512518        return EOK;
     519}
     520
     521static void hc_stop(xhci_hc_t *hc)
     522{
     523        /* Stop the HC in hardware. */
     524        XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
     525
     526        /*
     527         * Wait until the HC is halted - it shall take at most 16 ms.
     528         * Note that we ignore the return value here.
     529         */
     530        xhci_reg_wait(&hc->op_regs->usbsts, XHCI_REG_MASK(XHCI_OP_HCH),
     531            XHCI_REG_MASK(XHCI_OP_HCH));
     532
     533        /* Make sure commands will not block other fibrils. */
     534        xhci_nuke_command_ring(hc);
     535
     536        /* Stop the event worker fibril to restart it */
     537        xhci_sw_ring_stop(&hc->sw_ring);
     538        joinable_fibril_join(hc->event_worker);
     539
     540        /* Then, disconnect all roothub devices, which shall trigger
     541         * disconnection of everything */
     542        xhci_rh_stop(&hc->rh);
     543}
     544
     545static void hc_reinitialize(xhci_hc_t *hc)
     546{
     547        /* Stop everything. */
     548        hc_stop(hc);
     549
     550        usb_log_info("HC stopped. Starting again...");
     551
     552        /* The worker fibrils need to be started again */
     553        joinable_fibril_recreate(hc->event_worker);
     554        joinable_fibril_recreate(hc->rh.event_worker);
     555
     556        /* Now, the HC shall be stopped and software shall be clean. */
     557        hc_start(hc);
     558}
     559
     560static bool hc_is_broken(xhci_hc_t *hc)
     561{
     562        const uint32_t usbcmd = XHCI_REG_RD_FIELD(&hc->op_regs->usbcmd, 32);
     563        const uint32_t usbsts = XHCI_REG_RD_FIELD(&hc->op_regs->usbsts, 32);
     564
     565        return !(usbcmd & XHCI_REG_MASK(XHCI_OP_RS))
     566            ||  (usbsts & XHCI_REG_MASK(XHCI_OP_HCE))
     567            ||  (usbsts & XHCI_REG_MASK(XHCI_OP_HSE));
    513568}
    514569
     
    622677        hc->event_handler = 0;
    623678
    624         /* Update the ERDP to make room in the ring. */
    625679        uint64_t erdp = hc->event_ring.dequeue_ptr;
    626680        erdp |= XHCI_REG_MASK(XHCI_INTR_ERDP_EHB);
     
    646700
    647701        if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
    648                 usb_log_error("Host controller error occured. Bad things gonna happen...");
    649                 status &= ~XHCI_REG_MASK(XHCI_OP_HSE);
     702                usb_log_error("Host system error occured. Aren't we supposed to be dead already?");
     703                return;
     704        }
     705
     706        if (status & XHCI_REG_MASK(XHCI_OP_HCE)) {
     707                usb_log_error("Host controller error occured. Reinitializing...");
     708                hc_reinitialize(hc);
     709                return;
    650710        }
    651711
     
    676736void hc_fini(xhci_hc_t *hc)
    677737{
    678         xhci_sw_ring_stop(&hc->sw_ring);
    679         joinable_fibril_join(hc->event_worker);
     738        hc_stop(hc);
     739
    680740        xhci_sw_ring_fini(&hc->sw_ring);
    681 
     741        joinable_fibril_destroy(hc->event_worker);
    682742        xhci_bus_fini(&hc->bus);
    683743        xhci_event_ring_fini(&hc->event_ring);
     
    882942        xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
    883943
     944        if (hc_is_broken(hc))
     945                return EOK;
     946
    884947        /* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
    885948        return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT,
     
    930993{
    931994        xhci_device_t * const dev = xhci_ep_to_dev(ep);
     995        xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
    932996        const unsigned dci = endpoint_dci(ep);
     997
     998        if (hc_is_broken(hc))
     999                return EOK;
    9331000
    9341001        /* Issue configure endpoint command (sec 4.3.5). */
     
    9381005                return err;
    9391006
    940         xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
    9411007        xhci_input_ctx_t *ictx = ictx_dma_buf.virt;
    9421008        XHCI_INPUT_CTRL_CTX_DROP_SET(*XHCI_GET_CTRL_CTX(ictx, hc), dci);
     
    9921058        const unsigned dci = endpoint_dci(ep);
    9931059        xhci_hc_t * const hc = bus_to_hc(dev->base.bus);
     1060
     1061        if (hc_is_broken(hc))
     1062                return EOK;
     1063
    9941064        return xhci_cmd_sync_inline(hc, STOP_ENDPOINT,
    9951065                .slot_id = dev->slot_id,
Note: See TracChangeset for help on using the changeset viewer.