aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-02-16 18:21:41 -0500
committerTom Rini <trini@konsulko.com>2022-02-16 18:21:41 -0500
commitb6277189398a166011ce46a63665423a20de0809 (patch)
tree86c2e9f571e8edc5aed84bc611801c54ec5051c9
parent6bb2557da29300bef341d8c56091724f71c1d70e (diff)
parentd5daa02d8d9e7c403a3339db1966e8413e64e408 (diff)
downloadu-boot-WIP/16Feb2022.zip
u-boot-WIP/16Feb2022.tar.gz
u-boot-WIP/16Feb2022.tar.bz2
Merge branch 'master' of git://source.denx.de/u-boot-usbWIP/16Feb2022
-rw-r--r--drivers/usb/dwc3/core.c9
-rw-r--r--drivers/usb/dwc3/dwc3-generic.c25
-rw-r--r--drivers/usb/host/xhci-ring.c31
3 files changed, 60 insertions, 5 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ce1c0e8..b592a48 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 8d53ba7..01bd0ca 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 0b3e7a2..eb6dfcd 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -482,6 +482,33 @@ union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected)
}
/*
+ * 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
* xhci_bulk_tx/xhci_ctrl_tx on this enpoint will add new transfers there and
@@ -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)