Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 19f0048 in mainline


Ignore:
Timestamp:
2018-02-01T02:13:34Z (2 years ago)
Author:
Ondřej Hlavatý <aearsis@…>
Branches:
master
Children:
17d34a8
Parents:
53fdf8c
Message:

xhci: reinitialize in case of HC error

Location:
uspace
Files:
11 edited

Legend:

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

    r53fdf8c r19f0048  
    8686        list_initialize(&cr->cmd_list);
    8787
    88         cr->state = XHCI_CR_STATE_OPEN;
    89 
    9088        return EOK;
    9189}
     
    251249                fibril_condvar_wait(&cr->stopped_cv, &cr->guard);
    252250
     251        fibril_mutex_unlock(&cr->guard);
     252}
     253
     254/**
     255 * Mark the command ring as stopped. NAK new commands, abort running, do not
     256 * touch the HC as it's probably broken.
     257 */
     258void xhci_nuke_command_ring(xhci_hc_t *hc)
     259{
     260        xhci_cmd_ring_t *cr = get_cmd_ring(hc);
     261        fibril_mutex_lock(&cr->guard);
     262        // Prevent others from starting CR again.
     263        cr_set_state(cr, XHCI_CR_STATE_CLOSED);
     264        fibril_mutex_unlock(&cr->guard);
     265}
     266
     267/**
     268 * Mark the command ring as working again.
     269 */
     270void xhci_start_command_ring(xhci_hc_t *hc)
     271{
     272        xhci_cmd_ring_t *cr = get_cmd_ring(hc);
     273        fibril_mutex_lock(&cr->guard);
     274        // Prevent others from starting CR again.
     275        cr_set_state(cr, XHCI_CR_STATE_OPEN);
    253276        fibril_mutex_unlock(&cr->guard);
    254277}
  • uspace/drv/bus/usb/xhci/commands.h

    r53fdf8c r19f0048  
    128128extern void xhci_fini_commands(xhci_hc_t *);
    129129
     130extern void xhci_nuke_command_ring(xhci_hc_t *);
    130131extern void xhci_stop_command_ring(xhci_hc_t *);
    131132extern void xhci_abort_command_ring(xhci_hc_t *);
  • 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,
  • uspace/drv/bus/usb/xhci/hc.h

    r53fdf8c r19f0048  
    116116extern int hc_claim(xhci_hc_t *, ddf_dev_t *);
    117117extern int hc_irq_code_gen(irq_code_t *, xhci_hc_t *, const hw_res_list_parsed_t *, int *);
    118 extern int hc_start(xhci_hc_t *, bool);
     118extern int hc_start(xhci_hc_t *);
    119119extern void hc_fini(xhci_hc_t *);
    120120
  • uspace/drv/bus/usb/xhci/main.c

    r53fdf8c r19f0048  
    8383{
    8484        xhci_hc_t *hc = hcd_to_hc(hcd);
    85         return hc_start(hc, hcd->irq_cap >= 0);
     85        return hc_start(hc);
    8686}
    8787
  • uspace/drv/bus/usb/xhci/rh.c

    r53fdf8c r19f0048  
    113113        xhci_sw_ring_init(&rh->event_ring, rh->max_ports);
    114114
    115         joinable_fibril_start(rh->event_worker);
    116 
    117115        return EOK;
    118116}
     
    290288}
    291289
    292 void xhci_rh_startup(xhci_rh_t *rh)
    293 {
     290void xhci_rh_start(xhci_rh_t *rh)
     291{
     292        xhci_sw_ring_restart(&rh->event_ring);
     293        joinable_fibril_start(rh->event_worker);
     294
    294295        /* The reset changed status of all ports, and SW originated reason does
    295296         * not cause an interrupt.
     
    311312}
    312313
     314/**
     315 * Disconnect all devices on all ports. On contrary to ordinary disconnect, this
     316 * function waits until the disconnection routine is over.
     317 */
     318void xhci_rh_stop(xhci_rh_t *rh)
     319{
     320        xhci_sw_ring_stop(&rh->event_ring);
     321        joinable_fibril_join(rh->event_worker);
     322
     323        for (uint8_t i = 0; i < rh->max_ports; ++i) {
     324                rh_port_t * const port = &rh->ports[i];
     325                usb_port_disabled(&port->base, &rh_remove_device);
     326                usb_port_fini(&port->base);
     327        }
     328}
     329
    313330static int rh_worker(void *arg)
    314331{
  • uspace/drv/bus/usb/xhci/rh.h

    r53fdf8c r19f0048  
    8585
    8686extern void xhci_rh_set_ports_protocol(xhci_rh_t *, unsigned, unsigned, unsigned);
    87 extern void xhci_rh_startup(xhci_rh_t *);
     87extern void xhci_rh_start(xhci_rh_t *);
     88extern void xhci_rh_stop(xhci_rh_t *rh);
    8889
    8990#endif
  • uspace/drv/bus/usb/xhci/trb_ring.c

    r53fdf8c r19f0048  
    188188static uintptr_t trb_ring_enqueue_phys(xhci_trb_ring_t *ring)
    189189{
    190         uintptr_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
     190        size_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
    191191        return ring->enqueue_segment->phys + trb_id * sizeof(xhci_trb_t);
    192192}
     
    346346        }
    347347
     348        fibril_mutex_initialize(&ring->guard);
     349
     350        usb_log_debug("Initialized event ring.");
     351        return EOK;
     352}
     353
     354void xhci_event_ring_reset(xhci_event_ring_t *ring)
     355{
     356        list_foreach(ring->segments, segments_link, trb_segment_t, segment)
     357                memset(segment->trb_storage, 0, sizeof(segment->trb_storage));
     358
    348359        trb_segment_t * const segment = get_first_segment(&ring->segments);
    349360        ring->dequeue_segment = segment;
     
    351362        ring->dequeue_ptr = segment->phys;
    352363        ring->ccs = 1;
    353 
    354         fibril_mutex_initialize(&ring->guard);
    355 
    356         usb_log_debug("Initialized event ring.");
    357         return EOK;
    358364}
    359365
     
    432438        ring->end = ring->begin + size;
    433439
    434         ring->enqueue = ring->dequeue = ring->begin;
    435 
    436440        fibril_mutex_initialize(&ring->guard);
    437441        fibril_condvar_initialize(&ring->enqueued_cv);
    438442        fibril_condvar_initialize(&ring->dequeued_cv);
    439443
    440         ring->running = true;
     444        xhci_sw_ring_restart(ring);
    441445}
    442446
     
    486490}
    487491
     492void xhci_sw_ring_restart(xhci_sw_ring_t *ring)
     493{
     494        ring->enqueue = ring->dequeue = ring->begin;
     495        memset(ring->begin, 0, sizeof(xhci_trb_t) * (ring->end - ring->begin));
     496        ring->running = true;
     497}
     498
    488499void xhci_sw_ring_fini(xhci_sw_ring_t *ring)
    489500{
  • uspace/drv/bus/usb/xhci/trb_ring.h

    r53fdf8c r19f0048  
    109109extern int xhci_event_ring_init(xhci_event_ring_t *, size_t);
    110110extern void xhci_event_ring_fini(xhci_event_ring_t *);
     111extern void xhci_event_ring_reset(xhci_event_ring_t *);
    111112extern int xhci_event_ring_dequeue(xhci_event_ring_t *, xhci_trb_t *);
    112113
     
    130131extern int xhci_sw_ring_dequeue(xhci_sw_ring_t *, xhci_trb_t *);
    131132
     133extern void xhci_sw_ring_restart(xhci_sw_ring_t *);
    132134extern void xhci_sw_ring_stop(xhci_sw_ring_t *);
    133135extern void xhci_sw_ring_fini(xhci_sw_ring_t *);
  • uspace/lib/usbhost/include/usb/host/utility.h

    r53fdf8c r19f0048  
    6363void joinable_fibril_join(joinable_fibril_t *);
    6464void joinable_fibril_destroy(joinable_fibril_t *);
     65errno_t joinable_fibril_recreate(joinable_fibril_t *);
    6566
    6667
  • uspace/lib/usbhost/src/utility.c

    r53fdf8c r19f0048  
    297297{
    298298        joinable_fibril_t *jf = arg;
     299
    299300        jf->worker(jf->arg);
    300301
     
    315316                return NULL;
    316317
    317         jf->fid = fibril_create(joinable_fibril_worker, jf);
    318         if (!jf->fid) {
    319                 free(jf);
    320                 return NULL;
    321         }
    322 
    323318        jf->worker = worker;
    324319        jf->arg = arg;
     
    326321        fibril_condvar_initialize(&jf->dead_cv);
    327322
     323        if (joinable_fibril_recreate(jf)) {
     324                free(jf);
     325                return NULL;
     326        }
     327
    328328        return jf;
    329329}
    330 
    331330
    332331/**
     
    353352                fibril_condvar_wait(&jf->dead_cv, &jf->guard);
    354353        fibril_mutex_unlock(&jf->guard);
     354
     355        jf->fid = 0;
     356}
     357
     358/**
     359 * Reinitialize a joinable fibril.
     360 */
     361errno_t joinable_fibril_recreate(joinable_fibril_t *jf)
     362{
     363        assert(!jf->fid);
     364
     365        jf->fid = fibril_create(joinable_fibril_worker, jf);
     366        return jf->fid ? EOK : ENOMEM;
    355367}
    356368
Note: See TracChangeset for help on using the changeset viewer.