diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ce1c0e88c2..b592a487e0 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -706,6 +706,14 @@ static void dwc3_gadget_run(struct dwc3 *dwc) mdelay(100); } +static void dwc3_core_stop(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + dwc3_writel(dwc->regs, DWC3_DCTL, reg & ~(DWC3_DCTL_RUN_STOP)); +} + static void dwc3_core_exit_mode(struct dwc3 *dwc) { switch (dwc->dr_mode) { @@ -1128,6 +1136,7 @@ void dwc3_remove(struct dwc3 *dwc) dwc3_core_exit_mode(dwc); dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); + dwc3_core_stop(dwc); dwc3_core_exit(dwc); kfree(dwc->mem); } diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index 8d53ba7790..01bd0ca190 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -110,7 +110,12 @@ static int dwc3_generic_of_to_plat(struct udevice *dev) struct dwc3_generic_plat *plat = dev_get_plat(dev); ofnode node = dev_ofnode(dev); - plat->base = dev_read_addr(dev); + if (!strncmp(dev->name, "port", 4) || !strncmp(dev->name, "hub", 3)) { + /* This is a leaf so check the parent */ + plat->base = dev_read_addr(dev->parent); + } else { + plat->base = dev_read_addr(dev); + } plat->maximum_speed = usb_get_maximum_speed(node); if (plat->maximum_speed == USB_SPEED_UNKNOWN) { @@ -120,8 +125,13 @@ static int dwc3_generic_of_to_plat(struct udevice *dev) plat->dr_mode = usb_get_dr_mode(node); if (plat->dr_mode == USB_DR_MODE_UNKNOWN) { - pr_err("Invalid usb mode setup\n"); - return -ENODEV; + /* might be a leaf so check the parent for mode */ + node = dev_ofnode(dev->parent); + plat->dr_mode = usb_get_dr_mode(node); + if (plat->dr_mode == USB_DR_MODE_UNKNOWN) { + pr_err("Invalid usb mode setup\n"); + return -ENODEV; + } } return 0; @@ -301,16 +311,20 @@ static int dwc3_glue_bind(struct udevice *parent) { ofnode node; int ret; + enum usb_dr_mode dr_mode; + + dr_mode = usb_get_dr_mode(dev_ofnode(parent)); ofnode_for_each_subnode(node, dev_ofnode(parent)) { const char *name = ofnode_get_name(node); - enum usb_dr_mode dr_mode; struct udevice *dev; const char *driver = NULL; debug("%s: subnode name: %s\n", __func__, name); - dr_mode = usb_get_dr_mode(node); + /* if the parent node doesn't have a mode check the leaf */ + if (!dr_mode) + dr_mode = usb_get_dr_mode(node); switch (dr_mode) { case USB_DR_MODE_PERIPHERAL: @@ -450,6 +464,7 @@ static const struct udevice_id dwc3_glue_ids[] = { { .compatible = "rockchip,rk3328-dwc3" }, { .compatible = "rockchip,rk3399-dwc3" }, { .compatible = "qcom,dwc3" }, + { .compatible = "fsl,imx8mq-dwc3" }, { .compatible = "intel,tangier-dwc3" }, { } }; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0b3e7a2f55..eb6dfcdb09 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -481,6 +481,33 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected) BUG(); } +/* + * Send reset endpoint command for given endpoint. This recovers from a + * halted endpoint (e.g. due to a stall error). + */ +static void reset_ep(struct usb_device *udev, int ep_index) +{ + struct xhci_ctrl *ctrl = xhci_get_ctrl(udev); + struct xhci_ring *ring = ctrl->devs[udev->slot_id]->eps[ep_index].ring; + union xhci_trb *event; + u32 field; + + printf("Resetting EP %d...\n", ep_index); + xhci_queue_command(ctrl, NULL, udev->slot_id, ep_index, TRB_RESET_EP); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + field = le32_to_cpu(event->trans_event.flags); + BUG_ON(TRB_TO_SLOT_ID(field) != udev->slot_id); + xhci_acknowledge_event(ctrl); + + xhci_queue_command(ctrl, (void *)((uintptr_t)ring->enqueue | + ring->cycle_state), udev->slot_id, ep_index, TRB_SET_DEQ); + event = xhci_wait_for_event(ctrl, TRB_COMPLETION); + BUG_ON(TRB_TO_SLOT_ID(le32_to_cpu(event->event_cmd.flags)) + != udev->slot_id || GET_COMP_CODE(le32_to_cpu( + event->event_cmd.status)) != COMP_SUCCESS); + xhci_acknowledge_event(ctrl); +} + /* * Stops transfer processing for an endpoint and throws away all unprocessed * TRBs by setting the xHC's dequeue pointer to our enqueue pointer. The next @@ -928,6 +955,10 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, record_transfer_result(udev, event, length); xhci_acknowledge_event(ctrl); + if (udev->status == USB_ST_STALLED) { + reset_ep(udev, ep_index); + return -EPIPE; + } /* Invalidate buffer to make it available to usb-core */ if (length > 0)