Ignore:
File:
1 edited

Legend:

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

    r9dfb034 r36795edf  
    11/*
    2  * Copyright (c) 2025 Jiri Svoboda
    32 * Copyright (c) 2011 Jan Vesely
    43 * Copyright (c) 2018 Ondrej Hlavaty
     
    213212}
    214213
    215 /** Quiesce host controller
    216  *
    217  * @param hcd Host controller device
    218  */
    219 int hc_quiesce(hc_device_t *hcd)
     214void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
     215{
     216        assert(instance);
     217        assert(ep);
     218        ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
     219        usb_log_debug("HC(%p) enqueue EP(%d:%d:%s:%s)", instance,
     220            ep->device->address, ep->endpoint,
     221            usb_str_transfer_type_short(ep->transfer_type),
     222            usb_str_direction(ep->direction));
     223        switch (ep->transfer_type) {
     224        case USB_TRANSFER_CONTROL:
     225        case USB_TRANSFER_BULK:
     226                endpoint_list_append_ep(&instance->async_list, ehci_ep);
     227                break;
     228        case USB_TRANSFER_INTERRUPT:
     229                endpoint_list_append_ep(&instance->int_list, ehci_ep);
     230                break;
     231        case USB_TRANSFER_ISOCHRONOUS:
     232                /* NOT SUPPORTED */
     233                break;
     234        }
     235}
     236
     237void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
     238{
     239        assert(instance);
     240        assert(ep);
     241        ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
     242        usb_log_debug("HC(%p) dequeue EP(%d:%d:%s:%s)", instance,
     243            ep->device->address, ep->endpoint,
     244            usb_str_transfer_type_short(ep->transfer_type),
     245            usb_str_direction(ep->direction));
     246        switch (ep->transfer_type) {
     247        case USB_TRANSFER_INTERRUPT:
     248                endpoint_list_remove_ep(&instance->int_list, ehci_ep);
     249                /* Fall through */
     250        case USB_TRANSFER_ISOCHRONOUS:
     251                /* NOT SUPPORTED */
     252                return;
     253        case USB_TRANSFER_CONTROL:
     254        case USB_TRANSFER_BULK:
     255                endpoint_list_remove_ep(&instance->async_list, ehci_ep);
     256                break;
     257        }
     258        fibril_mutex_lock(&instance->guard);
     259        usb_log_debug("HC(%p): Waiting for doorbell", instance);
     260        EHCI_SET(instance->registers->usbcmd, USB_CMD_IRQ_ASYNC_DOORBELL);
     261        fibril_condvar_wait(&instance->async_doorbell, &instance->guard);
     262        usb_log_debug2("HC(%p): Got doorbell", instance);
     263        fibril_mutex_unlock(&instance->guard);
     264}
     265
     266errno_t ehci_hc_status(bus_t *bus_base, uint32_t *status)
     267{
     268        assert(bus_base);
     269        assert(status);
     270
     271        ehci_bus_t *bus = (ehci_bus_t *) bus_base;
     272        hc_t *hc = bus->hc;
     273        assert(hc);
     274
     275        *status = 0;
     276        if (hc->registers) {
     277                *status = EHCI_RD(hc->registers->usbsts);
     278                EHCI_WR(hc->registers->usbsts, *status);
     279        }
     280        usb_log_debug2("HC(%p): Read status: %x", hc, *status);
     281        return EOK;
     282}
     283
     284/** Add USB transfer to the schedule.
     285 *
     286 * @param[in] hcd HCD driver structure.
     287 * @param[in] batch Batch representing the transfer.
     288 * @return Error code.
     289 */
     290errno_t ehci_hc_schedule(usb_transfer_batch_t *batch)
     291{
     292        assert(batch);
     293
     294        ehci_bus_t *bus = (ehci_bus_t *) endpoint_get_bus(batch->ep);
     295        hc_t *hc = bus->hc;
     296        assert(hc);
     297
     298        /* Check for root hub communication */
     299        if (batch->target.address == ehci_rh_get_address(&hc->rh)) {
     300                usb_log_debug("HC(%p): Scheduling BATCH(%p) for RH(%p)",
     301                    hc, batch, &hc->rh);
     302                return ehci_rh_schedule(&hc->rh, batch);
     303        }
     304
     305        endpoint_t *const ep = batch->ep;
     306        ehci_endpoint_t *const ehci_ep = ehci_endpoint_get(ep);
     307        ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
     308
     309        int err;
     310
     311        if ((err = ehci_transfer_batch_prepare(ehci_batch)))
     312                return err;
     313
     314        fibril_mutex_lock(&hc->guard);
     315
     316        if ((err = endpoint_activate_locked(ep, batch))) {
     317                fibril_mutex_unlock(&hc->guard);
     318                return err;
     319        }
     320
     321        usb_log_debug("HC(%p): Committing BATCH(%p)", hc, batch);
     322        ehci_transfer_batch_commit(ehci_batch);
     323
     324        /* Enqueue endpoint to the checked list */
     325        usb_log_debug2("HC(%p): Appending BATCH(%p)", hc, batch);
     326        list_append(&ehci_ep->pending_link, &hc->pending_endpoints);
     327
     328        fibril_mutex_unlock(&hc->guard);
     329        return EOK;
     330}
     331
     332/** Interrupt handling routine
     333 *
     334 * @param[in] hcd HCD driver structure.
     335 * @param[in] status Value of the status register at the time of interrupt.
     336 */
     337void ehci_hc_interrupt(bus_t *bus_base, uint32_t status)
     338{
     339        assert(bus_base);
     340
     341        ehci_bus_t *bus = (ehci_bus_t *) bus_base;
     342        hc_t *hc = bus->hc;
     343        assert(hc);
     344
     345        usb_log_debug2("HC(%p): Interrupt: %" PRIx32, hc, status);
     346        if (status & USB_STS_PORT_CHANGE_FLAG) {
     347                ehci_rh_interrupt(&hc->rh);
     348        }
     349
     350        if (status & USB_STS_IRQ_ASYNC_ADVANCE_FLAG) {
     351                fibril_mutex_lock(&hc->guard);
     352                usb_log_debug2("HC(%p): Signaling doorbell", hc);
     353                fibril_condvar_broadcast(&hc->async_doorbell);
     354                fibril_mutex_unlock(&hc->guard);
     355        }
     356
     357        if (status & (USB_STS_IRQ_FLAG | USB_STS_ERR_IRQ_FLAG)) {
     358                fibril_mutex_lock(&hc->guard);
     359
     360                usb_log_debug2("HC(%p): Scanning %zu pending endpoints", hc,
     361                    list_count(&hc->pending_endpoints));
     362                list_foreach_safe(hc->pending_endpoints, current, next) {
     363                        ehci_endpoint_t *ep =
     364                            list_get_instance(current, ehci_endpoint_t, pending_link);
     365
     366                        ehci_transfer_batch_t *batch =
     367                            ehci_transfer_batch_get(ep->base.active_batch);
     368                        assert(batch);
     369
     370                        if (ehci_transfer_batch_check_completed(batch)) {
     371                                endpoint_deactivate_locked(&ep->base);
     372                                list_remove(current);
     373                                hc_reset_toggles(&batch->base, &ehci_ep_toggle_reset);
     374                                usb_transfer_batch_finish(&batch->base);
     375                        }
     376                }
     377                fibril_mutex_unlock(&hc->guard);
     378
     379        }
     380
     381        if (status & USB_STS_HOST_ERROR_FLAG) {
     382                usb_log_fatal("HCD(%p): HOST SYSTEM ERROR!", hc);
     383                //TODO do something here
     384        }
     385}
     386
     387/** EHCI hw initialization routine.
     388 *
     389 * @param[in] instance EHCI hc driver structure.
     390 */
     391int hc_start(hc_device_t *hcd)
    220392{
    221393        hc_t *instance = hcd_to_hc(hcd);
     394        usb_log_debug("HC(%p): Starting HW.", instance);
    222395
    223396        /*
     
    249422        usb_log_debug("HC(%p): HW reset OK.", instance);
    250423
    251         return EOK;
    252 }
    253 
    254 void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
    255 {
    256         assert(instance);
    257         assert(ep);
    258         ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
    259         usb_log_debug("HC(%p) enqueue EP(%d:%d:%s:%s)", instance,
    260             ep->device->address, ep->endpoint,
    261             usb_str_transfer_type_short(ep->transfer_type),
    262             usb_str_direction(ep->direction));
    263         switch (ep->transfer_type) {
    264         case USB_TRANSFER_CONTROL:
    265         case USB_TRANSFER_BULK:
    266                 endpoint_list_append_ep(&instance->async_list, ehci_ep);
    267                 break;
    268         case USB_TRANSFER_INTERRUPT:
    269                 endpoint_list_append_ep(&instance->int_list, ehci_ep);
    270                 break;
    271         case USB_TRANSFER_ISOCHRONOUS:
    272                 /* NOT SUPPORTED */
    273                 break;
    274         }
    275 }
    276 
    277 void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep)
    278 {
    279         assert(instance);
    280         assert(ep);
    281         ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
    282         usb_log_debug("HC(%p) dequeue EP(%d:%d:%s:%s)", instance,
    283             ep->device->address, ep->endpoint,
    284             usb_str_transfer_type_short(ep->transfer_type),
    285             usb_str_direction(ep->direction));
    286         switch (ep->transfer_type) {
    287         case USB_TRANSFER_INTERRUPT:
    288                 endpoint_list_remove_ep(&instance->int_list, ehci_ep);
    289                 /* Fall through */
    290         case USB_TRANSFER_ISOCHRONOUS:
    291                 /* NOT SUPPORTED */
    292                 return;
    293         case USB_TRANSFER_CONTROL:
    294         case USB_TRANSFER_BULK:
    295                 endpoint_list_remove_ep(&instance->async_list, ehci_ep);
    296                 break;
    297         }
    298         fibril_mutex_lock(&instance->guard);
    299         usb_log_debug("HC(%p): Waiting for doorbell", instance);
    300         EHCI_SET(instance->registers->usbcmd, USB_CMD_IRQ_ASYNC_DOORBELL);
    301         fibril_condvar_wait(&instance->async_doorbell, &instance->guard);
    302         usb_log_debug2("HC(%p): Got doorbell", instance);
    303         fibril_mutex_unlock(&instance->guard);
    304 }
    305 
    306 errno_t ehci_hc_status(bus_t *bus_base, uint32_t *status)
    307 {
    308         assert(bus_base);
    309         assert(status);
    310 
    311         ehci_bus_t *bus = (ehci_bus_t *) bus_base;
    312         hc_t *hc = bus->hc;
    313         assert(hc);
    314 
    315         *status = 0;
    316         if (hc->registers) {
    317                 *status = EHCI_RD(hc->registers->usbsts);
    318                 EHCI_WR(hc->registers->usbsts, *status);
    319         }
    320         usb_log_debug2("HC(%p): Read status: %x", hc, *status);
    321         return EOK;
    322 }
    323 
    324 /** Add USB transfer to the schedule.
    325  *
    326  * @param[in] hcd HCD driver structure.
    327  * @param[in] batch Batch representing the transfer.
    328  * @return Error code.
    329  */
    330 errno_t ehci_hc_schedule(usb_transfer_batch_t *batch)
    331 {
    332         assert(batch);
    333 
    334         ehci_bus_t *bus = (ehci_bus_t *) endpoint_get_bus(batch->ep);
    335         hc_t *hc = bus->hc;
    336         assert(hc);
    337 
    338         /* Check for root hub communication */
    339         if (batch->target.address == ehci_rh_get_address(&hc->rh)) {
    340                 usb_log_debug("HC(%p): Scheduling BATCH(%p) for RH(%p)",
    341                     hc, batch, &hc->rh);
    342                 return ehci_rh_schedule(&hc->rh, batch);
    343         }
    344 
    345         endpoint_t *const ep = batch->ep;
    346         ehci_endpoint_t *const ehci_ep = ehci_endpoint_get(ep);
    347         ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
    348 
    349         int err;
    350 
    351         if ((err = ehci_transfer_batch_prepare(ehci_batch)))
    352                 return err;
    353 
    354         fibril_mutex_lock(&hc->guard);
    355 
    356         if ((err = endpoint_activate_locked(ep, batch))) {
    357                 fibril_mutex_unlock(&hc->guard);
    358                 return err;
    359         }
    360 
    361         usb_log_debug("HC(%p): Committing BATCH(%p)", hc, batch);
    362         ehci_transfer_batch_commit(ehci_batch);
    363 
    364         /* Enqueue endpoint to the checked list */
    365         usb_log_debug2("HC(%p): Appending BATCH(%p)", hc, batch);
    366         list_append(&ehci_ep->pending_link, &hc->pending_endpoints);
    367 
    368         fibril_mutex_unlock(&hc->guard);
    369         return EOK;
    370 }
    371 
    372 /** Interrupt handling routine
    373  *
    374  * @param[in] hcd HCD driver structure.
    375  * @param[in] status Value of the status register at the time of interrupt.
    376  */
    377 void ehci_hc_interrupt(bus_t *bus_base, uint32_t status)
    378 {
    379         assert(bus_base);
    380 
    381         ehci_bus_t *bus = (ehci_bus_t *) bus_base;
    382         hc_t *hc = bus->hc;
    383         assert(hc);
    384 
    385         usb_log_debug2("HC(%p): Interrupt: %" PRIx32, hc, status);
    386         if (status & USB_STS_PORT_CHANGE_FLAG) {
    387                 ehci_rh_interrupt(&hc->rh);
    388         }
    389 
    390         if (status & USB_STS_IRQ_ASYNC_ADVANCE_FLAG) {
    391                 fibril_mutex_lock(&hc->guard);
    392                 usb_log_debug2("HC(%p): Signaling doorbell", hc);
    393                 fibril_condvar_broadcast(&hc->async_doorbell);
    394                 fibril_mutex_unlock(&hc->guard);
    395         }
    396 
    397         if (status & (USB_STS_IRQ_FLAG | USB_STS_ERR_IRQ_FLAG)) {
    398                 fibril_mutex_lock(&hc->guard);
    399 
    400                 usb_log_debug2("HC(%p): Scanning %zu pending endpoints", hc,
    401                     list_count(&hc->pending_endpoints));
    402                 list_foreach_safe(hc->pending_endpoints, current, next) {
    403                         ehci_endpoint_t *ep =
    404                             list_get_instance(current, ehci_endpoint_t, pending_link);
    405 
    406                         ehci_transfer_batch_t *batch =
    407                             ehci_transfer_batch_get(ep->base.active_batch);
    408                         assert(batch);
    409 
    410                         if (ehci_transfer_batch_check_completed(batch)) {
    411                                 endpoint_deactivate_locked(&ep->base);
    412                                 list_remove(current);
    413                                 hc_reset_toggles(&batch->base, &ehci_ep_toggle_reset);
    414                                 usb_transfer_batch_finish(&batch->base);
    415                         }
    416                 }
    417                 fibril_mutex_unlock(&hc->guard);
    418 
    419         }
    420 
    421         if (status & USB_STS_HOST_ERROR_FLAG) {
    422                 usb_log_fatal("HCD(%p): HOST SYSTEM ERROR!", hc);
    423                 //TODO do something here
    424         }
    425 }
    426 
    427 /** EHCI hw initialization routine.
    428  *
    429  * @param[in] instance EHCI hc driver structure.
    430  */
    431 int hc_start(hc_device_t *hcd)
    432 {
    433         hc_t *instance = hcd_to_hc(hcd);
    434         usb_log_debug("HC(%p): Starting HW.", instance);
    435 
    436         /*
    437          * Turn off the HC if it's running, Reseting a running device is
    438          * undefined
    439          */
    440         if (!(EHCI_RD(instance->registers->usbsts) & USB_STS_HC_HALTED_FLAG)) {
    441                 /* disable all interrupts */
    442                 EHCI_WR(instance->registers->usbintr, 0);
    443                 /* ack all interrupts */
    444                 EHCI_WR(instance->registers->usbsts, 0x3f);
    445                 /* Stop HC hw */
    446                 EHCI_WR(instance->registers->usbcmd, 0);
    447                 /* Wait until hc is halted */
    448                 while ((EHCI_RD(instance->registers->usbsts) & USB_STS_HC_HALTED_FLAG) == 0) {
    449                         fibril_usleep(1);
    450                 }
    451                 usb_log_info("HC(%p): EHCI turned off.", instance);
    452         } else {
    453                 usb_log_info("HC(%p): EHCI was not running.", instance);
    454         }
    455 
    456         /* Hw initialization sequence, see page 53 (pdf 63) */
    457         EHCI_SET(instance->registers->usbcmd, USB_CMD_HC_RESET_FLAG);
    458         usb_log_info("HC(%p): Waiting for HW reset.", instance);
    459         while (EHCI_RD(instance->registers->usbcmd) & USB_CMD_HC_RESET_FLAG) {
    460                 fibril_usleep(1);
    461         }
    462         usb_log_debug("HC(%p): HW reset OK.", instance);
    463 
    464424        /* Use the lowest 4G segment */
    465425        EHCI_WR(instance->registers->ctrldssegment, 0);
Note: See TracChangeset for help on using the changeset viewer.